Sat, 04 Jan 2025 16:38:48 +0100
merge
--- a/.hgignore Sun May 23 09:44:43 2021 +0200 +++ b/.hgignore Sat Jan 04 16:38:48 2025 +0100 @@ -1,10 +1,8 @@ relre:^build/.*$ relre:^config.mk$ relre:^core$ -relre:^ui/wpf/UIcore/obj -relre:^ui/wpf/UIwrapper/.vs -relre:^ui/wpf/UIwrapper/UIwrapper.VC -relre:^ui/wpf/UIwrapper/UIwrapper/Debug -relre:^ui/wpf/UIwrapper/UIwrapper/Release -relre:^ui/wpf/UIwrapper/UIwrapper/x64 -relre:^ui/wpf/UIwrapper/ipch \ No newline at end of file +relre:^make/vs/.vs/.* +relre:^make/vs/packages/.* +relre:^make/vs/.*vcxproj\.user +relre:^make/xcode/toolkit/toolkit.xcodeproj/xcuserdata/.* +relre:^make/xcode/toolkit/toolkit.xcodeproj/project.xcworkspace/xcuserdata/.* \ No newline at end of file
--- a/application/Makefile Sun May 23 09:44:43 2021 +0200 +++ b/application/Makefile Sat Jan 04 16:38:48 2025 +0100 @@ -26,20 +26,20 @@ # POSSIBILITY OF SUCH DAMAGE. # -BUILD_ROOT = ../ +BUILD_ROOT = .. include ../config.mk CFLAGS += -I../ui/ -I../ucx SRC = main.c -OBJ = $(SRC:%.c=../build/application/%.$(OBJ_EXT)) +OBJ = $(SRC:%.c=../build/application/%$(OBJ_EXT)) all: ../build/bin/mk12 ../build/bin/mk12: $(OBJ) $(BUILD_ROOT)/build/lib/libuitk.a - $(LD) -o ../build/bin/mk12$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx $(LDFLAGS) $(TK_LDFLAGS) + $(CC) -o ../build/bin/mk12$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx $(LDFLAGS) $(TK_LDFLAGS) -../build/application/%.$(OBJ_EXT): %.c +../build/application/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) $(TK_CFLAGS) -o $@ -c $<
--- a/application/main.c Sun May 23 09:44:43 2021 +0200 +++ b/application/main.c Sat Jan 04 16:38:48 2025 +0100 @@ -28,62 +28,433 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <ui/ui.h> -#include <ucx/buffer.h> -#include <ucx/utils.h> +#include <cx/buffer.h> +#include <cx/utils.h> + +#if !defined(UI_COCOA) && !defined(UI_MOTIF) typedef struct { + UiString *str1; + UiString *str2; + UiString *path; UiText *text; + UiDouble *progress; + UiList *list; + UiList *list2; + UiList *menulist; + UiInteger *radio; + UiInteger *tabview; + UiGeneric *image; + UiList *srclist1; + UiList *srclist2; + UiList *items; } MyDocument; MyDocument *doc1; MyDocument *doc2; +UIWIDGET tabview; + +static UiCondVar *cond; +static int thr_end = 0; +static int thr_started = 0; + +int threadfunc(void *data) { + printf("thr wait for data...\n"); + ui_condvar_wait(cond); + printf("thr data received: {%s} [%d]\n", cond->data, cond->intdata); + ui_condvar_destroy(cond); + cond = NULL; + + return 0; +} + +void action_start_thread(UiEvent *event, void *data) { + if(!thr_started) { + cond = ui_condvar_create(); + ui_job(event->obj, threadfunc, NULL, NULL, NULL); + thr_started = 1; + } +} + +void action_notify_thread(UiEvent *event, void *data) { + if(!thr_end) { + ui_condvar_signal(cond, "hello thread", 123); + thr_end = 1; + } +} void action_menu(UiEvent *event, void *userdata) { - printf("action_menu: %s\n", (char*)userdata); + +} + +void action_file_selected(UiEvent *event, void *userdata) { + UiFileList *files = event->eventdata; + MyDocument *doc = event->document; + printf("files: %d\n", (int)files->nfiles); + if(files->nfiles > 0) { + printf("selected file: %s\n", files->files[0]); + ui_image_load_file(doc->image, files->files[0]); + } } void action_button(UiEvent *event, void *userdata) { - printf("button test\n"); - MyDocument *doc = event->document; - if(!doc) { - printf("no document\n"); - return; - } - - char *text = doc->text->get(doc->text); - printf("text: {\n%s\n}\n", text); + ui_openfiledialog(event->obj, UI_FILEDIALOG_SELECT_SINGLE, action_file_selected, NULL); } void action_switch(UiEvent *event, void *userdata) { - if(event->document == doc1) { - ui_set_document(event->obj, doc2); - } else { - ui_set_document(event->obj, doc1); - } + +} + +void action_toolbar_button(UiEvent *event, void *userdata) { + printf("toolbar button\n"); + + ui_dialog(event->obj, .title = "Dialog Title", .content = "Content Label", .button1_label = "btn1", .button2_label = "btn2", .input = TRUE, .closebutton_label = "Cancel"); +} + +void action_dialog_button(UiEvent *event, void *userdata) { + ui_close(event->obj); +} + +void action_dialog_onactivate(UiEvent *event, void *userdata) { + printf("textfield activate\n"); + ui_close(event->obj); } +void action_toolbar_dialog(UiEvent *event, void *userdata) { + + UiObject *dialog = ui_dialog_window(event->obj, .title = "Dialog Window", .lbutton1 = "Cancel 1", .lbutton2 = "Btn2", .rbutton3 = "Btn3", .rbutton4 = "Login 4", .onclick = action_dialog_button, .default_button = 4, .show_closebutton = UI_OFF); + + ui_vbox(dialog, .margin = 10, .spacing = 10) { + ui_label(dialog, .label = "Enter password:"); + ui_passwordfield(dialog, .varname = "password", .onactivate = action_dialog_onactivate); + } + + ui_show(dialog); +} + +void action_toolbar_newwindow(UiEvent *event, void *userdata) { + UiObject *obj = ui_simple_window("New Window", NULL); + + ui_headerbar0(obj) { + ui_headerbar_start(obj) { + ui_button(obj, .label = "Open"); + } + ui_headerbar_end(obj) { + ui_button(obj, .label = "Test"); + } + } + + ui_textarea(obj, .varname="text"); + + ui_show(obj); +} MyDocument* create_doc(void) { MyDocument *doc = ui_document_new(sizeof(MyDocument)); UiContext *docctx = ui_document_context(doc); - doc->text = ui_text_new(docctx, "text"); + doc->str1 = ui_string_new(docctx, "str1"); + doc->str1 = ui_string_new(docctx, "str2"); + doc->path = ui_string_new(docctx, "path"); + doc->progress = ui_double_new(docctx, "progress"); + doc->list = ui_list_new(docctx, "list"); + ui_list_append(doc->list, "test1"); + ui_list_append(doc->list, "test2"); + ui_list_append(doc->list, "test3"); + doc->list2 = ui_list_new(docctx, "list2"); + ui_list_append(doc->list2, "test1"); + ui_list_append(doc->list2, "test2"); + ui_list_append(doc->list2, "test3"); + doc->radio = ui_int_new(docctx, "radio"); + doc->tabview = ui_int_new(docctx, "tabview"); + doc->image = ui_generic_new(docctx, "image"); + + doc->srclist1 = ui_list_new(docctx, "srclist1"); + doc->srclist2 = ui_list_new(docctx, "srclist2"); + ui_list_append(doc->srclist1, "test1"); + ui_list_append(doc->srclist1, "test2"); + ui_list_append(doc->srclist1, "test3"); + ui_list_append(doc->srclist2, "x1"); + ui_list_append(doc->srclist2, "x2"); + + doc->items = ui_list_new(docctx, "items"); + ui_list_append(doc->items, "Item 1"); + ui_list_append(doc->items, "Item 2"); + ui_list_append(doc->items, "Item 3"); + + //doc->text = ui_text_new(docctx, "text"); return doc; } +UiIcon *icon = NULL; + +static void* list_getvalue(void *elm, int col) { + /* + if(col == 0) { + if(!icon) { + icon = ui_icon("folder", 24); + } + return icon; + } + */ + + char *str = elm; + return col == 0 ? str : "x"; +} + +static UiList *menu_list; +int new_item_count = 0; + +void action_add_menu_item(UiEvent *event, void *userdata) { + char str[64]; + snprintf(str, 64, "new item %d", new_item_count++); + + ui_list_append(menu_list, strdup(str)); + ui_list_notify(menu_list); +} + +void action_menu_list(UiEvent *event, void *userdata) { + printf("menu list item: %d\n", event->intval); +} + +static int tab_x = 0; +void action_tab2_button(UiEvent *event, void *userdata) { + MyDocument *doc = event->document; + printf("current page: %d\n", (int)ui_get(doc->tabview)); + ui_set(doc->tabview, 0); +} + + +void action_group1(UiEvent *event, void *userdata) { + UiContext *ctx = event->obj->ctx; + if(userdata) { + ui_unset_group(ctx, 1); + } else { + ui_set_group(ctx, 1); + } +} + +void action_group2(UiEvent *event, void *userdata) { + UiContext *ctx = event->obj->ctx; + if(userdata) { + ui_unset_group(ctx, 2); + } else { + ui_set_group(ctx, 2); + } +} + +static UiObject *ref_window; + +void action_button_ref(UiEvent *event, void *userdata) { + UiObject *obj = event->obj; + printf("action_button_ref: %u\n", obj->ref); + ui_object_ref(obj); + ref_window = obj; +} + +void action_button_unref(UiEvent *event, void *userdata) { + UiObject *obj = userdata; + printf("action_button_unref: %u\n", obj->ref); + ui_object_unref(obj); +} + +void action_toolbar_unrefwindow(UiEvent *event, void *userdata) { + UiObject *obj = ui_simple_window("Unref", NULL); + ui_grid(obj, .margin = 20) { + ui_button(obj, .label = "Unref", .onclick = action_button_unref, .onclickdata = ref_window); + } + ui_show(obj); +} + +void action_sourcelist_activate(UiEvent *event, void *userdata) { + printf("sourcelist %s index %d\n", event->eventdata, event->intval); +} + +UiMenuBuilder *menubuilder; + +void* table_getvalue(void *row, int col) { + return row; +} + +void sourcelist_getvalue(void *sublistdata, void *rowdata, int index, UiSubListItem *item) { + item->label = strdup(rowdata); + item->eventdata = sublistdata; +} + +typedef struct Item { + UiObject *obj; + MyDocument *doc; + void *elm; +} Item; + +void item_remove(UiEvent *event, void *userdata) { + Item *item = userdata; + int index = 0; + void *elm = ui_list_first(item->doc->items); + while(elm) { + if(elm == item->elm) { + break; + } + elm = ui_list_next(item->doc->items); + index++; + } + + ui_list_remove(item->doc->items, index); + ui_list_update(item->doc->items); +} + +void create_item(UiObject *obj, int index, void *elm, void *userdata) { + Item *i = ui_malloc(obj->ctx, sizeof(Item)); + i->obj = obj; + i->elm = elm; + i->doc = userdata; + + char *item = elm; + ui_button(obj, .label = item); + ui_checkbox(obj, .label = "Check"); + ui_label(obj, .fill = UI_ON); + ui_button(obj, .label = "X", .onclick = item_remove, .onclickdata = i); + + + +} + void application_startup(UiEvent *event, void *data) { + // global list + UiContext *global = ui_global_context(); + menu_list = ui_list_new(global, "menulist"); + ui_list_append(menu_list, "menu list item 1"); + ui_list_append(menu_list, "menu list item 2"); + ui_list_append(menu_list, "menu list item 3"); - UiObject *obj = ui_window("Test", NULL); - ui_textarea_nv(obj, "text"); - ui_button(obj, "Test", action_button, NULL); - ui_button(obj, "Switch Document", action_switch, NULL); + + + UiObject *obj = ui_sidebar_window("Test", NULL); + + MyDocument *doc = create_doc(); + ui_attach_document(obj->ctx, doc); + + ui_sidebar(obj, .margin = 0, .spacing = 0) { + ui_sourcelist(obj, .fill = UI_ON, + .getvalue = sourcelist_getvalue, + .sublists = UI_SUBLISTS(UI_SUBLIST(.varname = "srclist1", .header = "Header 1", .userdata = "Sublist1"), UI_SUBLIST(.varname = "srclist2", .header = "Header 2", .userdata = "Sublist2")), + .onactivate = action_sourcelist_activate); + } - doc1 = create_doc(); - doc2 = create_doc(); + ui_tabview(obj, .spacing=10, .margin=10, .tabview = UI_TABVIEW_NAVIGATION_SIDE, .varname="tabview") { + ui_tab(obj, "Tab 1") { + ui_vbox(obj, .fill = UI_OFF, .margin = 15, .spacing = 15) { + ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button); + ui_togglebutton(obj, .label = "Toggle"); + ui_checkbox(obj, .label = "My Checkbox"); + } + ui_grid(obj, .fill = UI_OFF, .columnspacing = 15, .rowspacing = 15, .margin = 15) { + ui_button(obj, .label = "Activate Group 1", .hexpand = TRUE, .onclick = action_group1); + ui_button(obj, .label = "Disable Group 1", .onclick = action_group1, .onclickdata = "disable"); + ui_newline(obj); + ui_button(obj, .label = "Activate Group 2", .hexpand = TRUE, .onclick = action_group2); + ui_button(obj, .label = "Disable Group 2", .onclick = action_group2, .onclickdata = "disable"); + ui_newline(obj); + + ui_button(obj, .label = "Groups 1,2", .colspan = 2, .groups = UI_GROUPS(1, 2)); + ui_newline(obj); + + ui_label(obj, .label = "Label Col 1", .align = UI_ALIGN_LEFT); + ui_label(obj, .label = "Label Col 2", .style = UI_LABEL_STYLE_TITLE, .align = UI_ALIGN_RIGHT); + ui_newline(obj); + + ui_spinner(obj, .step = 5); + ui_newline(obj); + + ui_progressbar(obj, .colspan = 2, .varname = "progress"); + ui_set(doc->progress, 0.75); + ui_newline(obj); + + ui_textfield(obj, .value = doc->str1); + ui_newline(obj); + + //ui_button(obj, .label="Test"); + ui_path_textfield(obj, .varname = "path", .hfill = TRUE, .hexpand = TRUE); + ui_set(doc->path, "/test/path/longdirectoryname/123"); + ui_newline(obj); + + //UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "Col 1", UI_STRING, "Col 2", -1); + //model->getvalue = list_getvalue; + ui_combobox(obj, .hexpand = true, .vexpand = false, .colspan = 2, .varname = "list", .getvalue = list_getvalue); + ui_newline(obj); + + ui_hbox0(obj) { + ui_radiobutton(obj, .label = "Radio 1", .varname = "radio"); + ui_radiobutton(obj, .label = "Radio 2", .varname = "radio"); + ui_radiobutton(obj, .label = "Radio 3", .varname = "radio"); + } + ui_newline(obj); + + UiModel *model = ui_model(obj->ctx, UI_STRING, "col1", -1); + model->getvalue = table_getvalue; + ui_table(obj, .model = model, .list = doc->list2, .colspan = 2, .hexpand = TRUE, .contextmenu = menubuilder); + } + } + ui_tab(obj, "Tab 2") { + ui_button(obj, .label = "Button 1 Start Thread", .onclick=action_start_thread); + ui_button(obj, .label = "Button 2 Notify Thread", .onclick=action_notify_thread); + ui_button(obj, .label = "Obj Ref", .onclick=action_button_ref); + ui_button(obj, .label = "Obj Unref", .onclick=action_button_unref, .onclickdata = obj); + ui_button(obj, .label = "Button 5", .onclick=action_tab2_button); + ui_button(obj, .label = "Button 6", .onclick=action_tab2_button); + } + ui_tab(obj, "Tab 3") { + UiTabViewArgs args = {0}; + UI_CTN(obj, tabview=ui_tabview_create(obj, args)) { + UiObject *tab1 = ui_tabview_add(tabview, "Sub 1", -1); + ui_button(tab1, .label = "Button 1"); + + + UiObject *tab2 = ui_tabview_add(tabview, "Sub 2", -1); + ui_button(tab2, .label = "Button 2"); + } + } + ui_tab(obj, "Tab 4") { + ui_textarea(obj, .varname = "text"); + } + ui_tab(obj, "Tab 5") { + ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button); + ui_imageviewer(obj, .varname = "image", .style_class = "imageviewer", .contextmenu = menubuilder); + } + + ui_tab(obj, "Tab 6") { + ui_scrolledwindow(obj, .fill = UI_ON) { + ui_expander(obj, .label = "Expander", .margin = 10, .spacing = 10) { + ui_label(obj, .label = "Test"); + ui_button(obj, .label = "Button"); + } + + ui_frame(obj, .label = "Frame", .margin = 10, .spacing = 10) { + ui_label(obj, .label = "Title", .style = UI_LABEL_STYLE_TITLE); + ui_label(obj, .label = "Sub-Title", .style = UI_LABEL_STYLE_SUBTITLE); + ui_label(obj, .label = "Dim Label", .style = UI_LABEL_STYLE_DIM); + ui_label(obj, .label = "No Style"); + } + + for(int i=0;i<100;i++) { + char labelstr[32]; + snprintf(labelstr, 32, "button %d", i); + ui_button(obj, .label = labelstr); + } + } + } + + ui_tab(obj, "Tab 7") { + ui_itemlist(obj, .create_ui = create_item, .varname = "items", .subcontainer = UI_CONTAINER_HBOX, .sub_spacing = 10, .margin = 10, .spacing = 4, .userdata = doc); + } + } - ui_attach_document(obj->ctx, doc1); + /* + + */ ui_show(obj); } @@ -93,18 +464,176 @@ ui_onstartup(application_startup, NULL); // menu - ui_menu("_File"); - ui_menuitem("_Hello", action_menu, NULL); - ui_submenu("Submenu1"); - ui_submenu("Submenu2"); - ui_menuitem("item2", action_menu, NULL); - ui_submenu_end(); - ui_menuitem("item3", action_menu, NULL); - ui_submenu_end(); - ui_menuitem("item4", action_menu, NULL); - + ui_menu("File") { + ui_menuitem(.label = "Test"); + } + + ui_contextmenu(&menubuilder) { + ui_menuitem(.label = "Context Item 1"); + ui_menuitem(.label = "Context Item 2"); + ui_menu("Context Submenu") { + ui_menuitem(.label = "Context Sub Item"); + } + } + + ui_menu("Edit") { + ui_menuitem(.label = "Undo"); + ui_menuseparator(); + ui_menu("Submenu") { + ui_menuitem(.label = "Subitem"); + } + } + + ui_toolbar_item("Test", .label = "Test", .onclick = action_toolbar_button); + ui_toolbar_item("Test2", .label = "New Window", .onclick = action_toolbar_newwindow); + ui_toolbar_item("Test3", .label = "Dialog", .onclick = action_toolbar_dialog); + ui_toolbar_item("Test4", .label = "Unref Window", .onclick = action_toolbar_unrefwindow); + ui_toolbar_item("Test5", .label = "Test 5", .onclick = action_toolbar_button); + ui_toolbar_item("Test6", .label = "Test 6", .onclick = action_toolbar_button); + ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button); + ui_toolbar_menu("Menu", .label = "Menu") { + ui_menuitem("Secondary Test", .onclick = action_toolbar_button, NULL); + ui_menu("Secondary Sub") { + ui_menuitem("Secondary subitem", NULL, NULL); + } + ui_menuseparator(); + ui_menu_itemlist(.varname = "menulist", .onselect=action_menu_list); + ui_menuseparator(); + ui_menuitem("last", .onclick = action_add_menu_item); + } + + ui_toolbar_appmenu() { + ui_menuitem("New"); + ui_menuitem("Open"); + ui_menuitem("Save"); + + ui_menuseparator(); + + ui_menuitem("Close"); + } + + ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Test6", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Toggle", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Menu", UI_TOOLBAR_LEFT); + + ui_toolbar_add_default("Test2", UI_TOOLBAR_CENTER); + ui_toolbar_add_default("Test3", UI_TOOLBAR_CENTER); + + ui_toolbar_add_default("Test4", UI_TOOLBAR_RIGHT); + ui_toolbar_add_default("Test5", UI_TOOLBAR_RIGHT); ui_main(); return (EXIT_SUCCESS); } + +#endif + +#if defined(UI_COCOA) || defined(UI_MOTIF) + +static UiList *menulist; +int items = 4; + +void action_button(UiEvent *event, void *data) { + printf("action_button\n"); + + char *newitem = malloc(32); + snprintf(newitem, 32, "Item %d", ++items); + ui_list_append(menulist, newitem); + ui_list_notify(menulist); +} + +typedef struct WData { + UiString *path; + UiList *list; +} WData; + + +int lsitems = 4; + +void action_button2(UiEvent *event, void *data) { + WData *wdata = event->window; + char *newitem = malloc(32); + snprintf(newitem, 32, "List Item %d", ++lsitems); + ui_list_append(wdata->list, newitem); + ui_list_update(wdata->list); + UiListSelection sel; + int index = lsitems-1; + sel.count = 1; + sel.rows = &index; + wdata->list->setselection(wdata->list, sel); +} + +void action_listevent(UiEvent *event, void *data) { + printf("%s: %d\n", data, event->intval); + UiListSelection *sel = event->eventdata; + for(int i=0;i<sel->count;i++) { + printf("sel: %d\n", sel->rows[i]); + } + printf("\n"); +} + +void application_startup(UiEvent *event, void *data) { + + menulist = ui_list_new(ui_global_context(), "menulist"); + ui_list_append(menulist, "Item 1"); + ui_list_append(menulist, "Item 2"); + ui_list_append(menulist, "Item 3"); + ui_list_append(menulist, "Item 4"); + + UiObject *obj = ui_window("Test", NULL); + + WData *wdata = ui_malloc(obj->ctx, sizeof(WData)); + wdata->path = ui_string_new(obj->ctx, NULL); + wdata->list = ui_list_new(obj->ctx, NULL); + obj->window = wdata; + + ui_list_append(wdata->list, "List Item 1"); + ui_list_append(wdata->list, "List Item 2"); + ui_list_append(wdata->list, "List Item 3"); + ui_list_append(wdata->list, "List Item 4"); + + ui_button(obj, .label = "Add Menu Item", .onclick = action_button, .name = "mybutton1"); + ui_button(obj, .label = "Add List Item", .onclick = action_button2); + ui_progressbar(obj, .name = "pb"); + ui_listview(obj, .list = wdata->list, .fill = UI_ON, .multiselection = TRUE, + .onactivate = action_listevent, .onactivatedata = "activate", + .onselection = action_listevent, .onselectiondata = "selection"); + + + ui_show(obj); +} + +void action_test(UiEvent *event, void *data) { + printf("action test\n"); +} + +int main(int argc, char** argv) { + ui_init("app1", argc, argv); + ui_onstartup(application_startup, NULL); + + // menu + ui_menu("File") { + ui_menuitem(.label = "Test 1", .onclick = action_test); + ui_menuitem(.label = "Test 2", .onclick = action_test); + ui_menuitem(.label = "Test 3", .onclick = action_test); + ui_menuseparator(); + ui_menu_toggleitem(.label = "Toggle 1"); + ui_menu_toggleitem(.label = "Toggle 2"); + ui_menuseparator(); + ui_menu_radioitem(.label = "Radio 1", .varname = "menu_radio"); + ui_menu_radioitem(.label = "Radio 2", .varname = "menu_radio"); + ui_menu_radioitem(.label = "Radio 3", .varname = "menu_radio"); + ui_menu_radioitem(.label = "Radio 4", .varname = "menu_radio"); + ui_menuseparator(); + ui_menu_itemlist(.varname = "menulist"); + ui_menuseparator(); + ui_menuitem(.label = "Quit"); + } + + ui_main(); + return (EXIT_SUCCESS); +} + +#endif
--- a/configure Sun May 23 09:44:43 2021 +0200 +++ b/configure Sat Jan 04 16:38:48 2025 +0100 @@ -1,40 +1,53 @@ #!/bin/sh - -PREFIX=/usr -EPREFIX=$PREFIX +# create temporary directory +TEMP_DIR=".tmp-`uname -n`" +rm -Rf "$TEMP_DIR" +if mkdir -p "$TEMP_DIR"; then + : +else + echo "Cannot create tmp dir $TEMP_DIR" + echo "Abort" + exit 1 +fi +touch "$TEMP_DIR/options" +touch "$TEMP_DIR/features" -BINDIR= -SBINDIR= -LIBDIR= -LIBEXECDIR= -DATADIR= -SYSCONFDIR= -SHAREDSTATEDIR= -LOCALSTATEDIR= -INCLUDEDIR= -INFODIR= -MANDIR= +# define standard variables +# also define standard prefix (this is where we will search for config.site) +prefix=/usr +exec_prefix= +bindir= +sbindir= +libdir= +libexecdir= +datarootdir= +datadir= +sysconfdir= +sharedstatedir= +localstatedir= +runstatedir= +includedir= +infodir= +localedir= +mandir= -OS=`uname -s` -OS_VERSION=`uname -r` - -TEMP_DIR=".tmp-`uname -n`" -mkdir -p $TEMP_DIR -if [ $? -ne 0 ]; then - echo "Cannot create tmp dir" - echo "Abort" -fi -touch $TEMP_DIR/options -touch $TEMP_DIR/features +# custom variables # features +# clean abort +abort_configure() +{ + rm -Rf "$TEMP_DIR" + exit 1 +} + # help text printhelp() { - echo "Usage: $0 [OPTIONS]..." - cat << __EOF__ + echo "Usage: $0 [OPTIONS]..." + cat << __EOF__ Installation directories: --prefix=PREFIX path prefix for architecture-independent files [/usr] @@ -47,697 +60,846 @@ --sysconfdir=DIR system configuration files [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR run-time variable data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --mandir=DIR man documentation [DATAROOTDIR/man] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] Options: - --toolkit=(gtk3|gtk2|gtk2legacy|qt5|qt4|motif) + --debug add extra compile flags for debug builds + --release add extra compile flags for release builds + --toolkit=(libadwaita|gtk4|gtk3|gtk2|gtk2legacy|qt5|qt4|cocoa|motif) __EOF__ } # -# parse arguments +# parse arguments # -for ARG in $@ +BUILD_TYPE="default" +for ARG in "$@" do case "$ARG" in - "--prefix="*) PREFIX=${ARG#--prefix=} ;; - "--exec-prefix="*) EPREFIX=${ARG#--exec-prefix=} ;; - "--bindir="*) BINDIR=${ARG#----bindir=} ;; - "--sbindir="*) SBINDIR=${ARG#--sbindir=} ;; - "--libdir="*) LIBDIR=${ARG#--libdir=} ;; - "--libexecdir="*) LIBEXECDIR=${ARG#--libexecdir=} ;; - "--datadir="*) DATADIR=${ARG#--datadir=} ;; - "--sysconfdir="*) SYSCONFDIR=${ARG#--sysconfdir=} ;; - "--sharedstatedir="*) SHAREDSTATEDIR=${ARG#--sharedstatedir=} ;; - "--localstatedir="*) LOCALSTATEDIR=${ARG#--localstatedir=} ;; - "--includedir="*) INCLUDEDIR=${ARG#--includedir=} ;; - "--infodir="*) INFODIR=${ARG#--infodir=} ;; - "--mandir"*) MANDIR=${ARG#--mandir} ;; - "--help"*) printhelp; exit 1 ;; - "--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;; - "-"*) echo "unknown option: $ARG"; exit 1 ;; - esac + "--prefix="*) prefix=${ARG#--prefix=} ;; + "--exec-prefix="*) exec_prefix=${ARG#--exec-prefix=} ;; + "--bindir="*) bindir=${ARG#----bindir=} ;; + "--sbindir="*) sbindir=${ARG#--sbindir=} ;; + "--libdir="*) libdir=${ARG#--libdir=} ;; + "--libexecdir="*) libexecdir=${ARG#--libexecdir=} ;; + "--datarootdir="*) datarootdir=${ARG#--datarootdir=} ;; + "--datadir="*) datadir=${ARG#--datadir=} ;; + "--sysconfdir="*) sysconfdir=${ARG#--sysconfdir=} ;; + "--sharedstatedir="*) sharedstatedir=${ARG#--sharedstatedir=} ;; + "--localstatedir="*) localstatedir=${ARG#--localstatedir=} ;; + "--includedir="*) includedir=${ARG#--includedir=} ;; + "--infodir="*) infodir=${ARG#--infodir=} ;; + "--mandir"*) mandir=${ARG#--mandir} ;; + "--localedir"*) localedir=${ARG#--localedir} ;; + "--help"*) printhelp; abort_configure ;; + "--debug") BUILD_TYPE="debug" ;; + "--release") BUILD_TYPE="release" ;; + "--toolkit="*) OPT_TOOLKIT=${ARG#--toolkit=} ;; + "-"*) echo "unknown option: $ARG"; abort_configure ;; + esac done -# set dir variables -if [ -z "$BINDIR" ]; then - BINDIR=$EPREFIX/bin -fi -if [ -z "$SBINDIR" ]; then - SBINDIR=$EPREFIX/sbin -fi -if [ -z "$LIBDIR" ]; then - LIBDIR=$EPREFIX/lib -fi -if [ -z "$LIBEXEC" ]; then - LIBEXECDIR=$EPREFIX/libexec -fi -if [ -z "$DATADIR" ]; then - DATADIR=$PREFIX/share -fi -if [ -z "$SYSCONFDIR" ]; then - SYSCONFDIR=$PREFIX/etc -fi -if [ -z "$SHAREDSTATEDIR" ]; then - SHAREDSTATEDIR=$PREFIX/com -fi -if [ -z "$LOCALSTATEDIR" ]; then - LOCALSTATEDIR=$PREFIX/var -fi -if [ -z "$INCLUDEDIR" ]; then - INCLUDEDIR=$PREFIX/include -fi -if [ -z "$INFODIR" ]; then - INFODIR=$PREFIX/info -fi -if [ -z "$MANDIR" ]; then - MANDIR=$PREFIX/man + + +# set defaults for dir variables +: ${exec_prefix:="$prefix"} +: ${bindir:='${exec_prefix}/bin'} +: ${sbindir:='${exec_prefix}/sbin'} +: ${libdir:='${exec_prefix}/lib'} +: ${libexecdir:='${exec_prefix}/libexec'} +: ${datarootdir:='${prefix}/share'} +: ${datadir:='${datarootdir}'} +: ${sysconfdir:='${prefix}/etc'} +: ${sharedstatedir:='${prefix}/com'} +: ${localstatedir:='${prefix}/var'} +: ${runstatedir:='${localstatedir}/run'} +: ${includedir:='${prefix}/include'} +: ${infodir:='${datarootdir}/info'} +: ${mandir:='${datarootdir}/man'} +: ${localedir:='${datarootdir}/locale'} + +# check if a config.site exists and load it +if [ -n "$CONFIG_SITE" ]; then + # CONFIG_SITE may contain space separated file names + for cs in $CONFIG_SITE; do + printf "loading defaults from $cs... " + . "$cs" + echo ok + done +elif [ -f "$prefix/share/config.site" ]; then + printf "loading site defaults... " + . "$prefix/share/config.site" + echo ok +elif [ -f "$prefix/etc/config.site" ]; then + printf "loading site defaults... " + . "$prefix/etc/config.site" + echo ok fi -which pkg-config > /dev/null -if [ $? -eq 0 ]; then - PKG_CONFIG=pkg-config -else - PKG_CONFIG=false -fi +# Test for availability of pkg-config +PKG_CONFIG=`command -v pkg-config` +: ${PKG_CONFIG:="false"} # Simple uname based platform detection # $PLATFORM is used for platform dependent dependency selection +OS=`uname -s` +OS_VERSION=`uname -r` printf "detect platform... " -if [ $OS = SunOS ]; then +if [ "$OS" = "SunOS" ]; then PLATFORM="solaris sunos unix svr4" -fi -if [ $OS = Linux ]; then +elif [ "$OS" = "Linux" ]; then PLATFORM="linux unix" -fi -if [ $OS = FreeBSD ]; then +elif [ "$OS" = "FreeBSD" ]; then PLATFORM="freebsd bsd unix" -fi -if [ $OS = Darwin ]; then +elif [ "$OS" = "OpenBSD" ]; then + PLATFORM="openbsd bsd unix" +elif [ "$OS" = "NetBSD" ]; then + PLATFORM="netbsd bsd unix" +elif [ "$OS" = "Darwin" ]; then PLATFORM="macos osx bsd unix" -fi -echo $OS | grep "MINGW" > /dev/null -if [ $? -eq 0 ]; then +elif echo "$OS" | grep -i "MINGW" > /dev/null; then PLATFORM="windows mingw" fi - -if [ -z "$PLATFORM" ]; then - PLATFORM="unix" -fi +: ${PLATFORM:="unix"} -for p in $PLATFORM -do - PLATFORM_NAME=$p - break -done -echo $PLATFORM_NAME +PLATFORM_NAME=`echo "$PLATFORM" | cut -f1 -d' ' -` +echo "$PLATFORM_NAME" isplatform() { for p in $PLATFORM do - if [ $p = $1 ]; then + if [ "$p" = "$1" ]; then return 0 fi done return 1 } -isnotplatform() +notisplatform() { for p in $PLATFORM do - if [ $p = $1 ]; then + if [ "$p" = "$1" ]; then + return 1 + fi + done + return 0 +} +istoolchain() +{ + for t in $TOOLCHAIN + do + if [ "$t" = "$1" ]; then + return 0 + fi + done + return 1 +} +notistoolchain() +{ + for t in $TOOLCHAIN + do + if [ "$t" = "$1" ]; then return 1 fi done return 0 } -# generate config.mk and config.h -cat > $TEMP_DIR/config.mk << __EOF__ -# -# config.mk generated by configure -# -# general vars - -PREFIX=$PREFIX -EPREFIX=$EPREFIX - -BINDIR=$BINDIR -SBINDIR=$SBINDIR -LIBDIR=$LIBDIR -LIBEXECDIR=$LIBEXECDIR -DATADIR=$DATADIR -SYSCONFDIR=$SYSCONFDIR -SHAREDSTATEDIR=$SHAREDSTATEDIR -LOCALSTATEDIR=$LOCALSTATEDIR -INCLUDEDIR=$INCLUDEDIR -INFODIR=$INFODIR -MANDIR=$MANDIR - +# generate vars.mk +cat > "$TEMP_DIR/vars.mk" << __EOF__ +prefix=$prefix +exec_prefix=$exec_prefix +bindir=$bindir +sbindir=$sbindir +libdir=$libdir +libexecdir=$libexecdir +datarootdir=$datarootdir +datadir=$datadir +sysconfdir=$sysconfdir +sharedstatedir=$sharedstatedir +localstatedir=$localstatedir +runstatedir=$runstatedir +includedir=$includedir +infodir=$infodir +mandir=$mandir +localedir=$localedir __EOF__ -echo > $TEMP_DIR/make.mk - -ENV_CFLAGS=$CFLAGS -ENV_LDFLAGS=$LDFLAGS -ENV_CXXFLAGS=$CXXFLAGS - -# Toolchain detection -# this will insert make vars to config.mk +# toolchain detection utilities . make/toolchain.sh -# add user specified flags to config.mk -echo >> $TEMP_DIR/config.mk -if [ ! -z "${ENV_CFLAGS}" ]; then - echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk -fi -if [ ! -z "${ENV_CXXFLAGS}" ]; then - echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk -fi -if [ ! -z "${ENV_LDFLAGS}" ]; then - echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk -fi - # # DEPENDENCIES # -dependency_qt4() +# check languages +lang_c= +lang_cpp= +if detect_c_compiler ; then + lang_c=1 +fi + +# create buffer for make variables required by dependencies +echo > "$TEMP_DIR/make.mk" + +test_pkg_config() { - printf "checking for qt4... " - # dependency qt4 - while true - do - qmake-qt4 -o - /dev/null | grep DEFINES\ > /dev/null - if [ $? -eq 0 ]; then - CFLAGS="$CFLAGS `qmake-qt4 -o - /dev/null | grep DEFINES\ `" - else - break - fi - qmake-qt4 -o - /dev/null | grep INCPATH\ > /dev/null - if [ $? -eq 0 ]; then - CFLAGS="$CFLAGS `qmake-qt4 -o - /dev/null | grep INCPATH\ `" - else - break - fi - > /dev/null - if [ $? -eq 0 ]; then - LDFLAGS="$LDFLAGS ``" - else - break - fi - which qmake-qt4 > /dev/null - if [ $? -ne 0 ]; then - break - fi - echo yes - return 0 - done - - echo no - return 1 + if "$PKG_CONFIG" --exists "$1" ; then : + else return 1 ; fi + if [ -z "$2" ] || "$PKG_CONFIG" --atleast-version="$2" "$1" ; then : + else return 1 ; fi + if [ -z "$3" ] || "$PKG_CONFIG" --exact-version="$3" "$1" ; then : + else return 1 ; fi + if [ -z "$4" ] || "$PKG_CONFIG" --max-version="$4" "$1" ; then : + else return 1 ; fi + return 0 } -dependency_gtk2legacy() + +print_check_msg() { - printf "checking for gtk2legacy... " - # dependency gtk2legacy + if [ -z "$1" ]; then + shift + printf "$@" + fi +} + +dependency_error_gtk2legacy() +{ + print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... " + # dependency gtk2legacy while true do if [ -z "$PKG_CONFIG" ]; then - break + break fi - $PKG_CONFIG gtk+-2.0 - if [ $? -ne 0 ] ; then + if test_pkg_config "gtk+-2.0" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`" + else break fi - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-2.0`" - LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-2.0`" - CFLAGS="$CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY" - LDFLAGS="$LDFLAGS -lpthread" - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_gtk2legacy" "yes\n" + dep_checked_gtk2legacy=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_gtk2legacy" "no\n" + dep_checked_gtk2legacy=1 + return 0 } -dependency_qt5() +dependency_error_gtk2() { - printf "checking for qt5... " - # dependency qt5 + print_check_msg "$dep_checked_gtk2" "checking for gtk2... " + # dependency gtk2 while true do - qmake-qt5 -o - /dev/null | grep DEFINES\ > /dev/null - if [ $? -eq 0 ]; then - CFLAGS="$CFLAGS `qmake-qt5 -o - /dev/null | grep DEFINES\ `" + if [ -z "$PKG_CONFIG" ]; then + break + fi + if pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null ; then + : + else + break + fi + if test_pkg_config "gtk+-2.0" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`" else break fi - qmake-qt5 -o - /dev/null | grep INCPATH\ > /dev/null - if [ $? -eq 0 ]; then - CFLAGS="$CFLAGS `qmake-qt5 -o - /dev/null | grep INCPATH\ `" - else + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_gtk2" "yes\n" + dep_checked_gtk2=1 + return 1 + done + + print_check_msg "$dep_checked_gtk2" "no\n" + dep_checked_gtk2=1 + return 0 +} +dependency_error_gtk3() +{ + print_check_msg "$dep_checked_gtk3" "checking for gtk3... " + # dependency gtk3 + while true + do + if [ -z "$PKG_CONFIG" ]; then break fi - > /dev/null - if [ $? -eq 0 ]; then - LDFLAGS="$LDFLAGS ``" + if test_pkg_config "gtk+-3.0" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-3.0`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-3.0`" else break fi - which qmake-qt5 > /dev/null - if [ $? -ne 0 ]; then - break - fi - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK3" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_gtk3" "yes\n" + dep_checked_gtk3=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_gtk3" "no\n" + dep_checked_gtk3=1 + return 0 } -dependency_gtk2() +dependency_error_gtk4() { - printf "checking for gtk2... " - # dependency gtk2 + print_check_msg "$dep_checked_gtk4" "checking for gtk4... " + # dependency gtk4 while true do if [ -z "$PKG_CONFIG" ]; then - break + break fi - $PKG_CONFIG gtk+-2.0 - if [ $? -ne 0 ] ; then + if test_pkg_config "gtk4" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk4`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk4`" + else break fi - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-2.0`" - LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-2.0`" - CFLAGS="$CFLAGS -DUI_GTK2" - LDFLAGS="$LDFLAGS -lpthread" - pkg-config --atleast-version=2.20 gtk+-2.0 > /dev/null - if [ $? -ne 0 ]; then - break - fi - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_gtk4" "yes\n" + dep_checked_gtk4=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_gtk4" "no\n" + dep_checked_gtk4=1 + return 0 } -dependency_gtk3() +dependency_error_libadwaita() { - printf "checking for gtk3... " - # dependency gtk3 + print_check_msg "$dep_checked_libadwaita" "checking for libadwaita... " + # dependency libadwaita while true do if [ -z "$PKG_CONFIG" ]; then - break + break fi - $PKG_CONFIG gtk+-3.0 - if [ $? -ne 0 ] ; then + if test_pkg_config "libadwaita-1" "" "" "" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libadwaita-1`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libadwaita-1`" + else break fi - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags gtk+-3.0`" - LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs gtk+-3.0`" - CFLAGS="$CFLAGS -DUI_GTK3" - LDFLAGS="$LDFLAGS -lpthread" - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4 -DUI_LIBADWAITA" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread" + print_check_msg "$dep_checked_libadwaita" "yes\n" + dep_checked_libadwaita=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_libadwaita" "no\n" + dep_checked_libadwaita=1 + return 0 } -dependency_motif() +dependency_error_motif() { - printf "checking for motif... " - # dependency motif + print_check_msg "$dep_checked_motif" "checking for motif... " + # dependency motif platform="bsd" while true do - CFLAGS="$CFLAGS -DUI_MOTIF" - LDFLAGS="$LDFLAGS -lXm -lXt -lX11 -lpthread" - echo yes - return 0 + if notisplatform "bsd"; then + break + fi + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF -I/usr/local/include/X11" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread" + print_check_msg "$dep_checked_motif" "yes\n" + dep_checked_motif=1 + return 1 done - - echo no - return 1 -} -dependency_wpf() -{ - printf "checking for wpf... " - # dependency wpf platform="windows" + + # dependency motif while true do - if isnotplatform "windows"; then - break - fi - CFLAGS="$CFLAGS -DUI_WPF" - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread" + print_check_msg "$dep_checked_motif" "yes\n" + dep_checked_motif=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_motif" "no\n" + dep_checked_motif=1 + return 0 } -dependency_cocoa() +dependency_error_cocoa() { - printf "checking for cocoa... " + print_check_msg "$dep_checked_cocoa" "checking for cocoa... " # dependency cocoa platform="macos" while true do - if isnotplatform "macos"; then + if notisplatform "macos"; then break fi - CFLAGS="$CFLAGS -DUI_COCOA" - LDFLAGS="$LDFLAGS -lobjc -framework Cocoa" - echo yes - return 0 + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_COCOA" + TEMP_LDFLAGS="$TEMP_LDFLAGS -lobjc -framework Cocoa" + print_check_msg "$dep_checked_cocoa" "yes\n" + dep_checked_cocoa=1 + return 1 done - - echo no - return 1 + + print_check_msg "$dep_checked_cocoa" "no\n" + dep_checked_cocoa=1 + return 0 } +dependency_error_winui() +{ + print_check_msg "$dep_checked_winui" "checking for winui... " + # dependency winui platform="windows" + while true + do + if notisplatform "windows"; then + break + fi + TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WINUI" + print_check_msg "$dep_checked_winui" "yes\n" + dep_checked_winui=1 + return 1 + done + + print_check_msg "$dep_checked_winui" "no\n" + dep_checked_winui=1 + return 0 +} + +# start collecting dependency information +echo > "$TEMP_DIR/flags.mk" DEPENDENCIES_FAILED= ERROR=0 -# general dependencies -CFLAGS= -LDFLAGS= +# unnamed dependencies +TEMP_CFLAGS= +TEMP_CXXFLAGS= +TEMP_LDFLAGS= while true do - if isnotplatform "macos"; then + while true + do + if [ -z "$lang_c" ] ; then + ERROR=1 + break + fi + + break + done + break +done +while true +do + if notisplatform "macos"; then break fi while true do - - cat >> $TEMP_DIR/make.mk << __EOF__ -OBJ_EXT = o -LIB_EXT = a + + cat >> "$TEMP_DIR/make.mk" << __EOF__ +OBJ_EXT = .o +LIB_EXT = .a PACKAGE_SCRIPT = package_osx.sh - __EOF__ - break done - break done while true do - if isnotplatform "unix"; then + if notisplatform "unix"; then break fi - if isplatform "macos"; then + if isplatform "macos" || istoolchain "macos"; then break fi while true do - - cat >> $TEMP_DIR/make.mk << __EOF__ -OBJ_EXT = o -LIB_EXT = a + + cat >> "$TEMP_DIR/make.mk" << __EOF__ +OBJ_EXT = .o +LIB_EXT = .a PACKAGE_SCRIPT = package_unix.sh - __EOF__ - break done - + break +done +while true +do + if notisplatform "bsd"; then + break + fi + while true + do + + TEMP_CFLAGS="$TEMP_CFLAGS -I/usr/local/include" + TEMP_LDFLAGS="$TEMP_LDFLAGS -L/usr/local/lib" + break + done break done -# add general dependency flags to config.mk -echo >> $TEMP_DIR/config.mk -if [ ! -z "${CFLAGS}" ]; then - echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk +# add general dependency flags to flags.mk +echo "# general flags" >> "$TEMP_DIR/flags.mk" +if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then + echo "CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${CXXFLAGS}" ]; then - echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then + echo "CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${LDFLAGS}" ]; then - echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_LDFLAGS}" ]; then + echo "LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk" fi # # OPTION VALUES # +checkopt_toolkit_libadwaita() +{ + VERR=0 + if dependency_error_libadwaita ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ +TOOLKIT = gtk +GTKOBJ = draw_cairo.o +__EOF__ + return 0 +} +checkopt_toolkit_gtk4() +{ + VERR=0 + if dependency_error_gtk4 ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ +TOOLKIT = gtk +GTKOBJ = draw_cairo.o +__EOF__ + return 0 +} checkopt_toolkit_gtk3() { - VERR=0 - dependency_gtk3 - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_gtk3 ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = gtk GTKOBJ = draw_cairo.o - __EOF__ - return 0 + return 0 } checkopt_toolkit_gtk2() { - VERR=0 - dependency_gtk2 - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_gtk2 ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = gtk GTKOBJ = draw_cairo.o - __EOF__ - return 0 + return 0 } checkopt_toolkit_gtk2legacy() { - VERR=0 - dependency_gtk2legacy - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_gtk2legacy ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = gtk GTKOBJ = draw_gdk.o - __EOF__ - return 0 + return 0 } checkopt_toolkit_qt5() { - VERR=0 - dependency_qt5 - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_qt5 ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = qt LD = $(CXX) - __EOF__ - return 0 + return 0 } checkopt_toolkit_qt4() { - VERR=0 - dependency_qt4 - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_qt4 ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = qt LD = $(CXX) - __EOF__ - return 0 + return 0 +} +checkopt_toolkit_cocoa() +{ + VERR=0 + if dependency_error_cocoa ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ +TOOLKIT = cocoa +__EOF__ + return 0 } checkopt_toolkit_motif() { - VERR=0 - dependency_motif - if [ $? -ne 0 ]; then - VERR=1 - fi - if [ $VERR -ne 0 ]; then - return 1 - fi - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + if dependency_error_motif ; then + VERR=1 + fi + if [ $VERR -ne 0 ]; then + return 1 + fi + cat >> "$TEMP_DIR/make.mk" << __EOF__ TOOLKIT = motif - __EOF__ - return 0 + return 0 } # # TARGETS # -CFLAGS= -CXXFLAGS= -LDFLAGS= -# Target: tk -CFLAGS= -LDFLAGS= -CXXFLAGS= +echo >> "$TEMP_DIR/flags.mk" +echo "configuring target: tk" +echo "# flags for target tk" >> "$TEMP_DIR/flags.mk" +TEMP_CFLAGS= +TEMP_CXXFLAGS= +TEMP_LDFLAGS= # Features # Option: --toolkit -if [ -z $OPT_TOOLKIT ]; then - SAVED_ERROR=$ERROR - SAVED_DEPENDENCIES_FAILED=$DEPENDENCIES_FAILED - ERROR=0 - while true - do - if isplatform "windows"; then - checkopt_toolkit_wpf - if [ $? -eq 0 ]; then - echo " toolkit: wpf" >> $TEMP_DIR/options - ERROR=0 - break - fi - fi - if isplatform "macos"; then - checkopt_toolkit_cocoa - if [ $? -eq 0 ]; then - echo " toolkit: cocoa" >> $TEMP_DIR/options - ERROR=0 - break - fi - fi - checkopt_toolkit_gtk3 - if [ $? -eq 0 ]; then - echo " toolkit: gtk3" >> $TEMP_DIR/options - ERROR=0 - break - fi - checkopt_toolkit_qt5 - if [ $? -eq 0 ]; then - echo " toolkit: qt5" >> $TEMP_DIR/options - ERROR=0 - break - fi - checkopt_toolkit_gtk2 - if [ $? -eq 0 ]; then - echo " toolkit: gtk2" >> $TEMP_DIR/options - ERROR=0 - break - fi - checkopt_toolkit_qt4 - if [ $? -eq 0 ]; then - echo " toolkit: qt4" >> $TEMP_DIR/options - ERROR=0 - break - fi - checkopt_toolkit_motif - if [ $? -eq 0 ]; then - echo " toolkit: motif" >> $TEMP_DIR/options - ERROR=0 - break - fi - break - done - if [ $ERROR -ne 0 ]; then - SAVED_ERROR=1 - fi - ERROR=$SAVED_ERROR - DEPENDENCIES_FAILED=$SAVED_DEPENDENCIES_FAILED= +if [ -z "$OPT_TOOLKIT" ]; then + echo "auto-detecting option 'toolkit'" + SAVED_ERROR="$ERROR" + SAVED_DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED" + ERROR=1 + while true + do + if isplatform "windows"; then + if checkopt_toolkit_winui ; then + echo " toolkit: winui" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + fi + if isplatform "macos"; then + if checkopt_toolkit_cocoa ; then + echo " toolkit: cocoa" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + fi + if checkopt_toolkit_gtk4 ; then + echo " toolkit: gtk4" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + if checkopt_toolkit_gtk3 ; then + echo " toolkit: gtk3" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + if checkopt_toolkit_gtk2 ; then + echo " toolkit: gtk2" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + if checkopt_toolkit_qt4 ; then + echo " toolkit: qt4" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + if checkopt_toolkit_motif ; then + echo " toolkit: motif" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + break + done + if [ $ERROR -ne 0 ]; then + SAVED_ERROR=1 + SAVED_DEPENDENCIES_FAILED="option 'toolkit' $SAVED_DEPENDENCIES_FAILED" + fi + ERROR="$SAVED_ERROR" + DEPENDENCIES_FAILED="$SAVED_DEPENDENCIES_FAILED" else - if false; then - false - elif [ $OPT_TOOLKIT = "gtk3" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_gtk3 - if [ $? -ne 0 ]; then - ERROR=1 - fi - elif [ $OPT_TOOLKIT = "gtk2" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_gtk2 - if [ $? -ne 0 ]; then - ERROR=1 - fi - elif [ $OPT_TOOLKIT = "gtk2legacy" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_gtk2legacy - if [ $? -ne 0 ]; then - ERROR=1 - fi - elif [ $OPT_TOOLKIT = "qt5" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_qt5 - if [ $? -ne 0 ]; then - ERROR=1 - fi - elif [ $OPT_TOOLKIT = "qt4" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_qt4 - if [ $? -ne 0 ]; then - ERROR=1 - fi - elif [ $OPT_TOOLKIT = "motif" ]; then - echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options - checkopt_toolkit_motif - if [ $? -ne 0 ]; then - ERROR=1 - fi - fi + echo "checking option toolkit = $OPT_TOOLKIT" + if false; then + false + elif [ "$OPT_TOOLKIT" = "libadwaita" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_libadwaita ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "gtk4" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_gtk4 ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "gtk3" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_gtk3 ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "gtk2" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_gtk2 ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "gtk2legacy" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_gtk2legacy ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "qt5" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_qt5 ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "qt4" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_qt4 ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "cocoa" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_cocoa ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + elif [ "$OPT_TOOLKIT" = "motif" ]; then + echo " toolkit: $OPT_TOOLKIT" >> $TEMP_DIR/options + if checkopt_toolkit_motif ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option 'toolkit' $DEPENDENCIES_FAILED" + fi + fi fi -echo >> $TEMP_DIR/config.mk -if [ ! -z "${CFLAGS}" ]; then - echo "TK_CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then + echo "TK_CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk" +fi +if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then + echo "TK_CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${CXXFLAGS}" ]; then - echo "TK_CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk +if [ "$BUILD_TYPE" = "debug" ]; then + if [ -n "$lang_c" ]; then + echo 'TK_CFLAGS += ${DEBUG_CC_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi + if [ -n "$lang_cpp" ]; then + echo 'TK_CXXFLAGS += ${DEBUG_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi fi -if [ ! -z "${LDFLAGS}" ]; then - echo "TK_LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk +if [ "$BUILD_TYPE" = "release" ]; then + if [ -n "$lang_c" ]; then + echo 'TK_CFLAGS += ${RELEASE_CC_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi + if [ -n "$lang_cpp" ]; then + echo 'TK_CXXFLAGS += ${RELEASE_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi +fi +if [ -n "${TEMP_LDFLAGS}" ]; then + echo "TK_LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk" fi + +# final result if [ $ERROR -ne 0 ]; then - echo - echo "Error: Unresolved dependencies" - echo $DEPENDENCIES_FAILED - rm -Rf $TEMP_DIR - exit 1 + echo + echo "Error: Unresolved dependencies" + echo "$DEPENDENCIES_FAILED" + abort_configure fi echo "configure finished" echo echo "Build Config:" -echo " PREFIX: $PREFIX" -echo " TOOLCHAIN: $TOOLCHAIN_NAME" +echo " PREFIX: $prefix" +echo " TOOLCHAIN: $TOOLCHAIN_NAME" echo "Options:" -cat $TEMP_DIR/options +cat "$TEMP_DIR/options" echo -cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk -rm -Rf $TEMP_DIR +# generate the config.mk file +cat > "$TEMP_DIR/config.mk" << __EOF__ +# +# config.mk generated by configure +# +__EOF__ +write_toolchain_defaults "$TEMP_DIR/toolchain.mk" +cat "$TEMP_DIR/vars.mk" "$TEMP_DIR/toolchain.mk" "$TEMP_DIR/flags.mk" "$TEMP_DIR/make.mk" > config.mk +rm -Rf "$TEMP_DIR"
--- a/make/Makefile.mk Sun May 23 09:44:43 2021 +0200 +++ b/make/Makefile.mk Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. # -# Copyright 2013 Olaf Wintermann. All rights reserved. +# Copyright 2023 Olaf Wintermann. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -32,7 +32,7 @@ include config.mk BUILD_DIRS = build/bin build/lib -BUILD_DIRS += build/application +BUILD_DIRS += build/application build/ucx BUILD_DIRS += build/ui/common build/ui/$(TOOLKIT) all: $(BUILD_DIRS) ucx ui application @@ -41,11 +41,11 @@ $(BUILD_DIRS): mkdir -p $@ -ucx: FORCE - cd ucx; $(MAKE) +ui: ucx FORCE + cd ui; $(MAKE) all -ui: FORCE - cd ui; $(MAKE) all +ucx: FORCE + cd ucx; $(MAKE) all application: ui FORCE cd application; $(MAKE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/cc.mk Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,14 @@ +# +# cc toolchain config +# + +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = + +SHLIB_CFLAGS = -fPIC +SHLIB_LDFLAGS = -shared \ No newline at end of file
--- a/make/clang.mk Sun May 23 09:44:43 2021 +0200 +++ b/make/clang.mk Sat Jan 04 16:38:48 2025 +0100 @@ -2,8 +2,13 @@ # clang toolchain config # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -fPIC SHLIB_LDFLAGS = -shared
--- a/make/configure.vm Sun May 23 09:44:43 2021 +0200 +++ b/make/configure.vm Sat Jan 04 16:38:48 2025 +0100 @@ -1,78 +1,65 @@ #!/bin/sh +# create temporary directory +TEMP_DIR=".tmp-`uname -n`" +rm -Rf "$TEMP_DIR" +if mkdir -p "$TEMP_DIR"; then + : +else + echo "Cannot create tmp dir $TEMP_DIR" + echo "Abort" + exit 1 +fi +touch "$TEMP_DIR/options" +touch "$TEMP_DIR/features" + +# define standard variables +# also define standard prefix (this is where we will search for config.site) +prefix=/usr +exec_prefix= +bindir= +sbindir= +libdir= +libexecdir= +datarootdir= +datadir= +sysconfdir= +sharedstatedir= +localstatedir= +runstatedir= +includedir= +infodir= +localedir= +mandir= + +# custom variables #foreach( $var in $vars ) #if( $var.exec ) -${var.name}=`${var.value}` +${var.varName}=`${var.value}` #else -${var.name}=${var.value} +${var.varName}="${var.value}" #end #end -#if ( ! $project.hasVar("PREFIX") ) -PREFIX=/usr +# features +#foreach( $feature in $features ) +#if( ${feature.auto} ) +${feature.varName}=auto #end -#if ( ! $project.hasVar("EPREFIX") ) -EPREFIX=$PREFIX #end -#if ( ! $project.hasVar("BINDIR") ) -BINDIR= -#end -#if ( ! $project.hasVar("SBINDIR") ) -SBINDIR= -#end -#if ( ! $project.hasVar("LIBDIR") ) -LIBDIR= -#end -#if ( ! $project.hasVar("LIBEXECDIR") ) -LIBEXECDIR= -#end -#if ( ! $project.hasVar("DATADIR") ) -DATADIR= -#end -#if ( ! $project.hasVar("SYSCONFDIR") ) -SYSCONFDIR= -#end -#if ( ! $project.hasVar("SHAREDSTATEDIR") ) -SHAREDSTATEDIR= -#end -#if ( ! $project.hasVar("LOCALSTATEDIR") ) -LOCALSTATEDIR= -#end -#if ( ! $project.hasVar("INCLUDEDIR") ) -INCLUDEDIR= -#end -#if ( ! $project.hasVar("INFODIR") ) -INFODIR= -#end -#if ( ! $project.hasVar("MANDIR") ) -MANDIR= -#end - -OS=`uname -s` -OS_VERSION=`uname -r` - -TEMP_DIR=".tmp-`uname -n`" -mkdir -p $TEMP_DIR -if [ $? -ne 0 ]; then - echo "Cannot create tmp dir" - echo "Abort" -fi -touch $TEMP_DIR/options -touch $TEMP_DIR/features - -# features -#foreach( $feature in $features ) -#if( ${feature.isDefault()} ) -${feature.getVarName()}=on -#end -#end +# clean abort +abort_configure() +{ + rm -Rf "$TEMP_DIR" + exit 1 +} # help text printhelp() { - echo "Usage: $0 [OPTIONS]..." - cat << __EOF__ + echo "Usage: $0 [OPTIONS]..." + cat << __EOF__ Installation directories: --prefix=PREFIX path prefix for architecture-independent files [/usr] @@ -85,28 +72,28 @@ --sysconfdir=DIR system configuration files [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR run-time variable data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --mandir=DIR man documentation [DATAROOTDIR/man] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] #if( $options.size() > 0 ) Options: + --debug add extra compile flags for debug builds + --release add extra compile flags for release builds #foreach( $opt in $options ) - --${opt.getArgument()}=${opt.getValuesString()} + --${opt.argument}=${opt.valuesString} #end #end #if( $features.size() > 0 ) Optional Features: #foreach( $feature in $features ) -#if( $feature.default ) - --disable-${feature.arg} -#else - --enable-${feature.arg} -#end +${feature.helpText} #end #end @@ -114,297 +101,328 @@ } # -# parse arguments +# parse arguments # +BUILD_TYPE="default" #set( $D = '$' ) -for ARG in $@ +for ARG in "$@" do case "$ARG" in - "--prefix="*) PREFIX=${D}{ARG#--prefix=} ;; - "--exec-prefix="*) EPREFIX=${D}{ARG#--exec-prefix=} ;; - "--bindir="*) BINDIR=${D}{ARG#----bindir=} ;; - "--sbindir="*) SBINDIR=${D}{ARG#--sbindir=} ;; - "--libdir="*) LIBDIR=${D}{ARG#--libdir=} ;; - "--libexecdir="*) LIBEXECDIR=${D}{ARG#--libexecdir=} ;; - "--datadir="*) DATADIR=${D}{ARG#--datadir=} ;; - "--sysconfdir="*) SYSCONFDIR=${D}{ARG#--sysconfdir=} ;; - "--sharedstatedir="*) SHAREDSTATEDIR=${D}{ARG#--sharedstatedir=} ;; - "--localstatedir="*) LOCALSTATEDIR=${D}{ARG#--localstatedir=} ;; - "--includedir="*) INCLUDEDIR=${D}{ARG#--includedir=} ;; - "--infodir="*) INFODIR=${D}{ARG#--infodir=} ;; - "--mandir"*) MANDIR=${D}{ARG#--mandir} ;; - "--help"*) printhelp; exit 1 ;; - #foreach( $opt in $options ) - "--${opt.getArgument()}="*) ${opt.getVarName()}=${D}{ARG#--${opt.getArgument()}=} ;; + "--prefix="*) prefix=${D}{ARG#--prefix=} ;; + "--exec-prefix="*) exec_prefix=${D}{ARG#--exec-prefix=} ;; + "--bindir="*) bindir=${D}{ARG#----bindir=} ;; + "--sbindir="*) sbindir=${D}{ARG#--sbindir=} ;; + "--libdir="*) libdir=${D}{ARG#--libdir=} ;; + "--libexecdir="*) libexecdir=${D}{ARG#--libexecdir=} ;; + "--datarootdir="*) datarootdir=${D}{ARG#--datarootdir=} ;; + "--datadir="*) datadir=${D}{ARG#--datadir=} ;; + "--sysconfdir="*) sysconfdir=${D}{ARG#--sysconfdir=} ;; + "--sharedstatedir="*) sharedstatedir=${D}{ARG#--sharedstatedir=} ;; + "--localstatedir="*) localstatedir=${D}{ARG#--localstatedir=} ;; + "--includedir="*) includedir=${D}{ARG#--includedir=} ;; + "--infodir="*) infodir=${D}{ARG#--infodir=} ;; + "--mandir"*) mandir=${D}{ARG#--mandir} ;; + "--localedir"*) localedir=${D}{ARG#--localedir} ;; + "--help"*) printhelp; abort_configure ;; + "--debug") BUILD_TYPE="debug" ;; + "--release") BUILD_TYPE="release" ;; + #foreach( $opt in $options ) + "--${opt.argument}="*) ${opt.varName}=${D}{ARG#--${opt.argument}=} ;; #end - #foreach( $feature in $features ) - "--enable-${feature.arg}") ${feature.getVarName()}=on ;; - "--disable-${feature.arg}") unset ${feature.getVarName()} ;; - #end - "-"*) echo "unknown option: $ARG"; exit 1 ;; - esac + #foreach( $feature in $features ) + "--enable-${feature.arg}") ${feature.varName}=on ;; + "--disable-${feature.arg}") unset ${feature.varName} ;; + #end + "-"*) echo "unknown option: $ARG"; abort_configure ;; + esac done -# set dir variables -if [ -z "$BINDIR" ]; then - BINDIR=$EPREFIX/bin -fi -if [ -z "$SBINDIR" ]; then - SBINDIR=$EPREFIX/sbin -fi -if [ -z "$LIBDIR" ]; then - LIBDIR=$EPREFIX/lib -fi -if [ -z "$LIBEXEC" ]; then - LIBEXECDIR=$EPREFIX/libexec -fi -if [ -z "$DATADIR" ]; then - DATADIR=$PREFIX/share -fi -if [ -z "$SYSCONFDIR" ]; then - SYSCONFDIR=$PREFIX/etc -fi -if [ -z "$SHAREDSTATEDIR" ]; then - SHAREDSTATEDIR=$PREFIX/com -fi -if [ -z "$LOCALSTATEDIR" ]; then - LOCALSTATEDIR=$PREFIX/var -fi -if [ -z "$INCLUDEDIR" ]; then - INCLUDEDIR=$PREFIX/include -fi -if [ -z "$INFODIR" ]; then - INFODIR=$PREFIX/info -fi -if [ -z "$MANDIR" ]; then - MANDIR=$PREFIX/man +## Begin unparsed content. ** +#[[ + +# set defaults for dir variables +: ${exec_prefix:="$prefix"} +: ${bindir:='${exec_prefix}/bin'} +: ${sbindir:='${exec_prefix}/sbin'} +: ${libdir:='${exec_prefix}/lib'} +: ${libexecdir:='${exec_prefix}/libexec'} +: ${datarootdir:='${prefix}/share'} +: ${datadir:='${datarootdir}'} +: ${sysconfdir:='${prefix}/etc'} +: ${sharedstatedir:='${prefix}/com'} +: ${localstatedir:='${prefix}/var'} +: ${runstatedir:='${localstatedir}/run'} +: ${includedir:='${prefix}/include'} +: ${infodir:='${datarootdir}/info'} +: ${mandir:='${datarootdir}/man'} +: ${localedir:='${datarootdir}/locale'} + +# check if a config.site exists and load it +if [ -n "$CONFIG_SITE" ]; then + # CONFIG_SITE may contain space separated file names + for cs in $CONFIG_SITE; do + printf "loading defaults from $cs... " + . "$cs" + echo ok + done +elif [ -f "$prefix/share/config.site" ]; then + printf "loading site defaults... " + . "$prefix/share/config.site" + echo ok +elif [ -f "$prefix/etc/config.site" ]; then + printf "loading site defaults... " + . "$prefix/etc/config.site" + echo ok fi -which pkg-config > /dev/null -if [ $? -eq 0 ]; then - PKG_CONFIG=pkg-config -else - PKG_CONFIG=false -fi +# Test for availability of pkg-config +PKG_CONFIG=`command -v pkg-config` +: ${PKG_CONFIG:="false"} # Simple uname based platform detection # $PLATFORM is used for platform dependent dependency selection +OS=`uname -s` +OS_VERSION=`uname -r` printf "detect platform... " -if [ $OS = SunOS ]; then +if [ "$OS" = "SunOS" ]; then PLATFORM="solaris sunos unix svr4" -fi -if [ $OS = Linux ]; then +elif [ "$OS" = "Linux" ]; then PLATFORM="linux unix" -fi -if [ $OS = FreeBSD ]; then +elif [ "$OS" = "FreeBSD" ]; then PLATFORM="freebsd bsd unix" -fi -if [ $OS = Darwin ]; then +elif [ "$OS" = "OpenBSD" ]; then + PLATFORM="openbsd bsd unix" +elif [ "$OS" = "NetBSD" ]; then + PLATFORM="netbsd bsd unix" +elif [ "$OS" = "Darwin" ]; then PLATFORM="macos osx bsd unix" -fi -echo $OS | grep "MINGW" > /dev/null -if [ $? -eq 0 ]; then +elif echo "$OS" | grep -i "MINGW" > /dev/null; then PLATFORM="windows mingw" fi - -if [ -z "$PLATFORM" ]; then - PLATFORM="unix" -fi +: ${PLATFORM:="unix"} -for p in $PLATFORM -do - PLATFORM_NAME=$p - break -done -echo $PLATFORM_NAME +PLATFORM_NAME=`echo "$PLATFORM" | cut -f1 -d' ' -` +echo "$PLATFORM_NAME" isplatform() { for p in $PLATFORM do - if [ $p = $1 ]; then + if [ "$p" = "$1" ]; then return 0 fi done return 1 } -isnotplatform() +notisplatform() { for p in $PLATFORM do - if [ $p = $1 ]; then + if [ "$p" = "$1" ]; then + return 1 + fi + done + return 0 +} +istoolchain() +{ + for t in $TOOLCHAIN + do + if [ "$t" = "$1" ]; then + return 0 + fi + done + return 1 +} +notistoolchain() +{ + for t in $TOOLCHAIN + do + if [ "$t" = "$1" ]; then return 1 fi done return 0 } - -# generate config.mk and config.h -cat > $TEMP_DIR/config.mk << __EOF__ -# -# config.mk generated by configure -# - -# general vars -#foreach( $var in $vars ) -${var.name}=$${var.name} -#end +]]# +## End of unparsed content ** -#if ( ! $project.hasVar("PREFIX") ) -PREFIX=$PREFIX -#end -#if ( ! $project.hasVar("EPREFIX") ) -EPREFIX=$EPREFIX -#end - -#if ( ! $project.hasVar("BINDIR") ) -BINDIR=$BINDIR -#end -#if ( ! $project.hasVar("SBINDIR") ) -SBINDIR=$SBINDIR +# generate vars.mk +cat > "$TEMP_DIR/vars.mk" << __EOF__ +prefix=$prefix +exec_prefix=$exec_prefix +bindir=$bindir +sbindir=$sbindir +libdir=$libdir +libexecdir=$libexecdir +datarootdir=$datarootdir +datadir=$datadir +sysconfdir=$sysconfdir +sharedstatedir=$sharedstatedir +localstatedir=$localstatedir +runstatedir=$runstatedir +includedir=$includedir +infodir=$infodir +mandir=$mandir +localedir=$localedir +#foreach( $var in $vars ) +${var.varName}=${D}${var.varName} #end -#if ( ! $project.hasVar("LIBDIR") ) -LIBDIR=$LIBDIR -#end -#if ( ! $project.hasVar("LIBEXECDIR") ) -LIBEXECDIR=$LIBEXECDIR -#end -#if ( ! $project.hasVar("DATADIR") ) -DATADIR=$DATADIR -#end -#if ( ! $project.hasVar("SYSCONFDIR") ) -SYSCONFDIR=$SYSCONFDIR -#end -#if ( ! $project.hasVar("SHAREDSTATEDIR") ) -SHAREDSTATEDIR=$SHAREDSTATEDIR -#end -#if ( ! $project.hasVar("LOCALSTATEDIR") ) -LOCALSTATEDIR=$LOCALSTATEDIR -#end -#if ( ! $project.hasVar("INCLUDEDIR") ) -INCLUDEDIR=$INCLUDEDIR -#end -#if ( ! $project.hasVar("INFODIR") ) -INFODIR=$INFODIR -#end -#if ( ! $project.hasVar("MANDIR") ) -MANDIR=$MANDIR -#end - __EOF__ -echo > $TEMP_DIR/make.mk - -ENV_CFLAGS=$CFLAGS -ENV_LDFLAGS=$LDFLAGS -ENV_CXXFLAGS=$CXXFLAGS - -# Toolchain detection -# this will insert make vars to config.mk +# toolchain detection utilities . make/toolchain.sh -# add user specified flags to config.mk -echo >> $TEMP_DIR/config.mk -if [ ! -z "${ENV_CFLAGS}" ]; then - echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk -fi -if [ ! -z "${ENV_CXXFLAGS}" ]; then - echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk -fi -if [ ! -z "${ENV_LDFLAGS}" ]; then - echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk -fi - # # DEPENDENCIES # -#foreach( $dependency in $namedDependencies ) -dependency_${dependency.name}() +# check languages +lang_c= +lang_cpp= +#foreach( $lang in $languages ) +if detect_${lang}_compiler ; then + lang_${lang}=1 +fi +#end + +# create buffer for make variables required by dependencies +echo > "$TEMP_DIR/make.mk" + +test_pkg_config() { - printf "checking for ${dependency.name}... " - #foreach( $sub in $dependency.getSubdependencies() ) - # dependency $sub.name $sub.getPlatformString() + if "$PKG_CONFIG" --exists "$1" ; then : + else return 1 ; fi + if [ -z "$2" ] || "$PKG_CONFIG" --atleast-version="$2" "$1" ; then : + else return 1 ; fi + if [ -z "$3" ] || "$PKG_CONFIG" --exact-version="$3" "$1" ; then : + else return 1 ; fi + if [ -z "$4" ] || "$PKG_CONFIG" --max-version="$4" "$1" ; then : + else return 1 ; fi + return 0 +} + +print_check_msg() +{ + if [ -z "$1" ]; then + shift + printf "$@" + fi +} + +#foreach( $dependency in $namedDependencies ) +dependency_error_${dependency.id}() +{ + print_check_msg "${D}dep_checked_${dependency.id}" "checking for ${dependency.name}... " + #foreach( $sub in $dependency.subdependencies ) + # dependency $sub.fullName while true do - #if( $sub.platform ) - if isnotplatform "${sub.platform}"; then + #if( $sub.platform ) + if notisplatform "${sub.platform}"; then + break + fi + #end + #if( $sub.toolchain ) + if notistoolchain "${sub.toolchain}"; then + break + fi + #end + #foreach( $np in $sub.notList ) + if isplatform "${np}" || istoolchain "${np}"; then break fi - #end - #foreach( $not in $sub.getNotList() ) - if isplatform "${not}"; then + #end + #foreach( $lang in $sub.lang ) + if [ -z "$lang_${lang}" ] ; then break fi - #end + #end #if( $sub.pkgconfig.size() > 0 ) if [ -z "$PKG_CONFIG" ]; then - break + break + fi + #end + #foreach( $test in $sub.tests ) + if $test > /dev/null ; then + : + else + break fi #end #foreach( $pkg in $sub.pkgconfig ) - $PKG_CONFIG $pkg.getPkgConfigParam() - if [ $? -ne 0 ] ; then + if test_pkg_config "$pkg.name" "$pkg.atleast" "$pkg.exact" "$pkg.max" ; then + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags $pkg.name`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs $pkg.name`" + else break fi - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`" - LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`" #end #foreach( $flags in $sub.flags ) #if( $flags.exec ) - $flags.value > /dev/null - if [ $? -eq 0 ]; then - $flags.varName="$$flags.varName `$flags.value`" + if tmp_flags=`$flags.value` ; then + TEMP_$flags.varName="$TEMP_$flags.varName $tmp_flags" else break fi #else - $flags.varName="$$flags.varName $flags.value" + TEMP_$flags.varName="$TEMP_$flags.varName $flags.value" #end #end - #foreach( $test in $sub.tests ) - $test > /dev/null - if [ $? -ne 0 ]; then - break - fi - #end - #if ( $sub.make.length() > 0 ) - cat >> $TEMP_DIR/make.mk << __EOF__ -# Dependency: $dependency.name + #if ( $sub.make.length() > 0 ) + cat >> $TEMP_DIR/make.mk << __EOF__ +# Dependency: $dependency.name $sub.make __EOF__ #end - echo yes - return 0 + print_check_msg "${D}dep_checked_${dependency.id}" "yes\n" + dep_checked_${dependency.id}=1 + return 1 done - - #end - echo no - return 1 + + #end + print_check_msg "${D}dep_checked_${dependency.id}" "no\n" + dep_checked_${dependency.id}=1 + return 0 } #end +# start collecting dependency information +echo > "$TEMP_DIR/flags.mk" + DEPENDENCIES_FAILED= ERROR=0 #if( $dependencies.size() > 0 ) -# general dependencies -CFLAGS= -LDFLAGS= +# unnamed dependencies +TEMP_CFLAGS= +TEMP_CXXFLAGS= +TEMP_LDFLAGS= #foreach( $dependency in $dependencies ) while true do - #if( $dependency.platform ) - if isnotplatform "${dependency.platform}"; then + #if( $dependency.platform ) + if notisplatform "${dependency.platform}"; then + break + fi + #end + #if( $dependency.toolchain ) + if notistoolchain "${dependency.toolchain}"; then break fi #end - #foreach( $not in $dependency.getNotList() ) - if isplatform "${not}"; then + #foreach( $np in $dependency.notList ) + if isplatform "${np}" || istoolchain "${np}"; then break fi - #end + #end while true do + #foreach( $lang in $dependency.lang ) + if [ -z "$lang_${lang}" ] ; then + ERROR=1 + break + fi + #end #if( $dependency.pkgconfig.size() > 0 ) if [ -z "$PKG_CONFIG" ]; then ERROR=1 @@ -412,54 +430,54 @@ fi #end #foreach( $pkg in $dependency.pkgconfig ) - printf "checking for pkg-config package $pkg.getPkgConfigParam()... " - $PKG_CONFIG $pkg.getPkgConfigParam() - if [ $? -ne 0 ]; then - echo no + print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "checking for pkg-config package $pkg.name... " + if test_pkg_config "$pkg.name" "$pkg.atleast" "$pkg.exact" "$pkg.max" ; then + print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "yes\n" + dep_pkgconfig_checked_${pkg.id}=1 + TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags $pkg.name`" + TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs $pkg.name`" + else + print_check_msg "${D}dep_pkgconfig_checked_${pkg.id}" "no\n" + dep_pkgconfig_checked_${pkg.id}=1 ERROR=1 break fi - echo yes - CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`" - LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`" #end - + #foreach( $flags in $dependency.flags ) #if( $flags.exec ) $flags.value > /dev/null - if [ $? -ne 0 ]; then - $flags.varName="$$flags.varName `$flags.value`" + if tmp_flags=`$flags.value` ; then + TEMP_$flags.varName="$TEMP_$flags.varName $tmp_flags" else ERROR=1 break fi #else - $flags.varName="$$flags.varName $flags.value" + TEMP_$flags.varName="$TEMP_$flags.varName $flags.value" #end #end - #if ( $dependency.make.length() > 0 ) - cat >> $TEMP_DIR/make.mk << __EOF__ + #if ( $dependency.make.length() > 0 ) + cat >> "$TEMP_DIR/make.mk" << __EOF__ $dependency.make __EOF__ #end - break done - break done #end -# add general dependency flags to config.mk -echo >> $TEMP_DIR/config.mk -if [ ! -z "${CFLAGS}" ]; then - echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk +# add general dependency flags to flags.mk +echo "# general flags" >> "$TEMP_DIR/flags.mk" +if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then + echo "CFLAGS += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${CXXFLAGS}" ]; then - echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then + echo "CXXFLAGS += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${LDFLAGS}" ]; then - echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_LDFLAGS}" ]; then + echo "LDFLAGS += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk" fi #end @@ -470,25 +488,25 @@ #foreach( $val in $opt.values ) ${val.func}() { - VERR=0 - #foreach( $dep in $val.dependencies ) - dependency_$dep - if [ $? -ne 0 ]; then - VERR=1 - fi - #end - if [ $VERR -ne 0 ]; then - return 1 - fi - #foreach( $def in $val.defines ) - CFLAGS="$CFLAGS ${def.toFlags()}" - #end - #if( $val.hasMake() ) - cat >> $TEMP_DIR/make.mk << __EOF__ + VERR=0 + #foreach( $dep in $val.dependencies ) + if dependency_error_$dep ; then + VERR=1 + fi + #end + if [ $VERR -ne 0 ]; then + return 1 + fi + #foreach( $def in $val.defines ) + TEMP_CFLAGS="$TEMP_CFLAGS ${def.toFlags()}" + TEMP_CXXFLAGS="$TEMP_CXXFLAGS ${def.toFlags()}" + #end + #if( $val.hasMake() ) + cat >> "$TEMP_DIR/make.mk" << __EOF__ $val.make __EOF__ - #end - return 0 + #end + return 0 } #end #end @@ -496,120 +514,160 @@ # # TARGETS # -CFLAGS= -CXXFLAGS= -LDFLAGS= #foreach( $target in $targets ) +echo >> "$TEMP_DIR/flags.mk" #if ( $target.name ) -# Target: $target.name +echo "configuring target: $target.name" +echo "# flags for target $target.name" >> "$TEMP_DIR/flags.mk" #else -# Target +echo "configuring global target" +echo "# flags for unnamed target" >> "$TEMP_DIR/flags.mk" #end -CFLAGS= -LDFLAGS= -CXXFLAGS= +TEMP_CFLAGS= +TEMP_CXXFLAGS= +TEMP_LDFLAGS= #foreach( $dependency in $target.dependencies ) -dependency_$dependency -if [ $? -ne 0 ]; then - DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} " - ERROR=1 +if dependency_error_$dependency; then + DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} " + ERROR=1 fi #end # Features #foreach( $feature in $target.features ) -if [ ! -z "$${feature.getVarName()}" ]; then +if [ -n "${D}${feature.varName}" ]; then #foreach( $dependency in $feature.dependencies ) - # check dependency - dependency_$dependency - if [ $? -ne 0 ]; then - # "auto" features can fail and are just disabled in this case - if [ $${feature.getVarName()} != "auto" ]; then - DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} " - ERROR=1 - fi - fi + # check dependency + if dependency_error_$dependency ; then + # "auto" features can fail and are just disabled in this case + if [ "${D}${feature.varName}" = "auto" ]; then + DISABLE_${feature.varName}=1 + else + DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} " + ERROR=1 + fi + fi #end + if [ -n "$DISABLE_${feature.varName}" ]; then + unset ${feature.varName} + fi fi #end #foreach( $opt in $target.options ) # Option: --${opt.argument} -if [ -z ${D}${opt.getVarName()} ]; then - SAVED_ERROR=$ERROR - SAVED_DEPENDENCIES_FAILED=$DEPENDENCIES_FAILED - ERROR=0 - while true - do - #foreach( $optdef in $opt.defaults ) - #if( $optdef.platform ) - if isplatform "$optdef.platform"; then - #end - $optdef.func - if [ $? -eq 0 ]; then - echo " ${opt.argument}: ${optdef.valueName}" >> $TEMP_DIR/options - ERROR=0 - break - fi - #if( $optdef.platform ) - fi - #end - #end - break - done - if [ $ERROR -ne 0 ]; then - SAVED_ERROR=1 - fi - ERROR=$SAVED_ERROR - DEPENDENCIES_FAILED=$SAVED_DEPENDENCIES_FAILED= +if [ -z "${D}${opt.varName}" ]; then + echo "auto-detecting option '${opt.argument}'" + SAVED_ERROR="$ERROR" + SAVED_DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED" + ERROR=1 + while true + do + #foreach( $optdef in $opt.defaults ) + #if( $optdef.platform ) + if isplatform "$optdef.platform"; then + #end + if $optdef.func ; then + echo " ${opt.argument}: ${optdef.valueName}" >> "$TEMP_DIR/options" + ERROR=0 + break + fi + #if( $optdef.platform ) + fi + #end + #end + break + done + if [ $ERROR -ne 0 ]; then + SAVED_ERROR=1 + SAVED_DEPENDENCIES_FAILED="option '${opt.argument}' $SAVED_DEPENDENCIES_FAILED" + fi + ERROR="$SAVED_ERROR" + DEPENDENCIES_FAILED="$SAVED_DEPENDENCIES_FAILED" else - if false; then - false - #foreach( $optval in $opt.values ) - elif [ ${D}${opt.getVarName()} = "${optval.value}" ]; then - echo " ${opt.argument}: ${D}${opt.getVarName()}" >> $TEMP_DIR/options - $optval.func - if [ $? -ne 0 ]; then - ERROR=1 - fi - #end - fi + echo "checking option ${opt.argument} = ${D}${opt.varName}" + if false; then + false + #foreach( $optval in $opt.values ) + elif [ "${D}${opt.varName}" = "${optval.value}" ]; then + echo " ${opt.argument}: ${D}${opt.varName}" >> $TEMP_DIR/options + if $optval.func ; then + : + else + ERROR=1 + DEPENDENCIES_FAILED="option '${opt.argument}' $DEPENDENCIES_FAILED" + fi + #end + fi fi #end -echo >> $TEMP_DIR/config.mk -if [ ! -z "${CFLAGS}" ]; then - echo "${target.getCFlags()} += $CFLAGS" >> $TEMP_DIR/config.mk +if [ -n "${TEMP_CFLAGS}" ] && [ -n "$lang_c" ]; then + echo "${target.cFlags} += $TEMP_CFLAGS" >> "$TEMP_DIR/flags.mk" +fi +if [ -n "${TEMP_CXXFLAGS}" ] && [ -n "$lang_cpp" ]; then + echo "${target.cxxFlags} += $TEMP_CXXFLAGS" >> "$TEMP_DIR/flags.mk" fi -if [ ! -z "${CXXFLAGS}" ]; then - echo "${target.getCXXFlags()} += $CXXFLAGS" >> $TEMP_DIR/config.mk +if [ "$BUILD_TYPE" = "debug" ]; then + if [ -n "$lang_c" ]; then + echo '${target.cFlags} += ${DEBUG_CC_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi + if [ -n "$lang_cpp" ]; then + echo '${target.cxxFlags} += ${DEBUG_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi fi -if [ ! -z "${LDFLAGS}" ]; then - echo "${target.getLDFlags()} += $LDFLAGS" >> $TEMP_DIR/config.mk +if [ "$BUILD_TYPE" = "release" ]; then + if [ -n "$lang_c" ]; then + echo '${target.cFlags} += ${RELEASE_CC_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi + if [ -n "$lang_cpp" ]; then + echo '${target.cxxFlags} += ${RELEASE_CXX_FLAGS}' >> "$TEMP_DIR/flags.mk" + fi +fi +if [ -n "${TEMP_LDFLAGS}" ]; then + echo "${target.ldFlags} += $TEMP_LDFLAGS" >> "$TEMP_DIR/flags.mk" fi #end + +# final result if [ $ERROR -ne 0 ]; then - echo - echo "Error: Unresolved dependencies" - echo $DEPENDENCIES_FAILED - rm -Rf $TEMP_DIR - exit 1 + echo + echo "Error: Unresolved dependencies" + echo "$DEPENDENCIES_FAILED" + abort_configure fi echo "configure finished" echo echo "Build Config:" -echo " PREFIX: $PREFIX" -echo " TOOLCHAIN: $TOOLCHAIN_NAME" +echo " PREFIX: $prefix" +echo " TOOLCHAIN: $TOOLCHAIN_NAME" #if ( $options.size() > 0 ) echo "Options:" -cat $TEMP_DIR/options +cat "$TEMP_DIR/options" +#end +#if ( $features.size() > 0 ) +echo "Features:" +#foreach( $feature in $features ) +if [ -n "${D}${feature.varName}" ]; then +echo " $feature.name: on" +else +echo " $feature.name: off" +fi +#end #end echo -cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk -rm -Rf $TEMP_DIR +# generate the config.mk file +cat > "$TEMP_DIR/config.mk" << __EOF__ +# +# config.mk generated by configure +# +__EOF__ +write_toolchain_defaults "$TEMP_DIR/toolchain.mk" +cat "$TEMP_DIR/vars.mk" "$TEMP_DIR/toolchain.mk" "$TEMP_DIR/flags.mk" "$TEMP_DIR/make.mk" > config.mk +rm -Rf "$TEMP_DIR"
--- a/make/gcc.mk Sun May 23 09:44:43 2021 +0200 +++ b/make/gcc.mk Sat Jan 04 16:38:48 2025 +0100 @@ -2,8 +2,13 @@ # gcc toolchain config # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -fPIC SHLIB_LDFLAGS = -shared
--- a/make/project.xml Sun May 23 09:44:43 2021 +0200 +++ b/make/project.xml Sat Jan 04 16:38:48 2025 +0100 @@ -1,12 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> -<project> - <!-- - <dependency name="gtk4"> - <pkgconfig>gtk+-4.0</pkgconfig> - <cflags>-DUI_GTK3</cflags> +<project version="0.3" xmlns="http://unixwork.de/uwproj"> + <dependency> + <lang>c</lang> + </dependency> + + <dependency name="libadwaita"> + <pkgconfig>libadwaita-1</pkgconfig> + <cflags>-DUI_GTK4 -DUI_LIBADWAITA</cflags> <ldflags>-lpthread</ldflags> </dependency> - --> + <dependency name="gtk4"> + <pkgconfig>gtk4</pkgconfig> + <cflags>-DUI_GTK4</cflags> + <ldflags>-lpthread</ldflags> + </dependency> <dependency name="gtk3"> <pkgconfig>gtk+-3.0</pkgconfig> <cflags>-DUI_GTK3</cflags> @@ -23,50 +30,66 @@ <cflags>-DUI_GTK2 -DUI_GTK2LEGACY</cflags> <ldflags>-lpthread</ldflags> </dependency> - <dependency name="wpf" platform="windows"> - <cflags>-DUI_WPF</cflags> + <dependency name="winui" platform="windows"> + <cflags>-DUI_WINUI</cflags> </dependency> + <!-- <dependency name="qt4"> <test>which qmake-qt4</test> - <cflags type="exec">qmake-qt4 -o - /dev/null | grep DEFINES\ </cflags> - <cflags type="exec">qmake-qt4 -o - /dev/null | grep INCPATH\ </cflags> - <ldflags type="exec"><cflags type="exec">qmake-qt4 -o - /dev/null | grep LIBS\ </cflags></ldflags> + <cflags exec="true">qmake-qt4 -o - /dev/null | grep DEFINES\ </cflags> + <cflags exec="true">qmake-qt4 -o - /dev/null | grep INCPATH\ </cflags> + <ldflags exec="true">qmake-qt4 -o - /dev/null | grep LIBS\ </ldflags> </dependency> <dependency name="qt5"> <test>which qmake-qt5</test> - <cflags type="exec">qmake-qt5 -o - /dev/null | grep DEFINES\ </cflags> - <cflags type="exec">qmake-qt5 -o - /dev/null | grep INCPATH\ </cflags> - <ldflags type="exec"><cflags type="exec">qmake-qt5 -o - /dev/null | grep LIBS\ </cflags></ldflags> + <cflags exec="true">qmake-qt5 -o - /dev/null | grep DEFINES\ </cflags> + <cflags exec="true">qmake-qt5 -o - /dev/null | grep INCPATH\ </cflags> + <ldflags exec="true">qmake-qt5 -o - /dev/null | grep LIBS\ </ldflags> </dependency> + --> <dependency name="cocoa" platform="macos"> <cflags>-DUI_COCOA</cflags> <ldflags>-lobjc -framework Cocoa</ldflags> </dependency> + + <dependency name="motif" platform="bsd"> + <cflags>-DUI_MOTIF -I/usr/local/include/X11</cflags> + <ldflags>-lXm -lXt -lX11 -lpthread</ldflags> + </dependency> + <dependency name="motif"> <cflags>-DUI_MOTIF</cflags> <ldflags>-lXm -lXt -lX11 -lpthread</ldflags> </dependency> <dependency platform="macos"> - <make>OBJ_EXT = o</make> - <make>LIB_EXT = a</make> + <make>OBJ_EXT = .o</make> + <make>LIB_EXT = .a</make> <make>PACKAGE_SCRIPT = package_osx.sh</make> </dependency> <dependency platform="unix" not="macos"> - <make>OBJ_EXT = o</make> - <make>LIB_EXT = a</make> + <make>OBJ_EXT = .o</make> + <make>LIB_EXT = .a</make> <make>PACKAGE_SCRIPT = package_unix.sh</make> </dependency> + <dependency platform="bsd"> + <cflags>-I/usr/local/include</cflags> + <ldflags>-L/usr/local/lib</ldflags> + </dependency> + <target name="tk"> <option arg="toolkit"> - <!-- + <value str="libadwaita"> + <dependencies>libadwaita</dependencies> + <make>TOOLKIT = gtk</make> + <make>GTKOBJ = draw_cairo.o</make> + </value> <value str="gtk4"> <dependencies>gtk4</dependencies> <make>TOOLKIT = gtk</make> <make>GTKOBJ = draw_cairo.o</make> </value> - --> <value str="gtk3"> <dependencies>gtk3</dependencies> <make>TOOLKIT = gtk</make> @@ -92,14 +115,19 @@ <make>TOOLKIT = qt</make> <make>LD = $(CXX)</make> </value> + <value str="cocoa"> + <dependencies>cocoa</dependencies> + <make>TOOLKIT = cocoa</make> + </value> <value str="motif"> <dependencies>motif</dependencies> <make>TOOLKIT = motif</make> </value> - <default value="wpf" platform="windows" /> + <default value="winui" platform="windows" /> <default value="cocoa" platform="macos" /> + <default value="gtk4" /> <default value="gtk3" /> - <default value="qt5" /> + <!--<default value="qt5" />--> <default value="gtk2" /> <default value="qt4" /> <default value="motif" />
--- a/make/suncc.mk Sun May 23 09:44:43 2021 +0200 +++ b/make/suncc.mk Sat Jan 04 16:38:48 2025 +0100 @@ -2,8 +2,13 @@ # suncc toolchain # -CFLAGS = -LDFLAGS = +CFLAGS = +CXXFLAGS = +DEBUG_CC_FLAGS = -g +DEBUG_CXX_FLAGS = -g +RELEASE_CC_FLAGS = -O3 -DNDEBUG +RELEASE_CXX_FLAGS = -O3 -DNDEBUG +LDFLAGS = SHLIB_CFLAGS = -Kpic SHLIB_LDFLAGS = -G
--- a/make/toolchain.sh Sun May 23 09:44:43 2021 +0200 +++ b/make/toolchain.sh Sat Jan 04 16:38:48 2025 +0100 @@ -3,179 +3,198 @@ # toolchain detection # -C_COMPILERS="cc gcc clang suncc" -CPP_COMPILERS="CC g++ clang++ sunCC" -unset CC_ARG_CHECKED -unset TOOLCHAIN_DETECTION_ERROR +if isplatform "bsd" && notisplatform "openbsd"; then + C_COMPILERS="clang gcc cc" + CPP_COMPILERS="clang++ g++ CC" +else + C_COMPILERS="gcc clang suncc cc" + CPP_COMPILERS="g++ clang++ sunCC CC" +fi +unset TOOLCHAIN unset TOOLCHAIN_NAME +unset TOOLCHAIN_CC +unset TOOLCHAIN_CXX check_c_compiler() { - cat > $TEMP_DIR/test.c << __EOF__ + cat > "$TEMP_DIR/test.c" << __EOF__ /* test file */ #include <stdio.h> int main(int argc, char **argv) { -#if defined(__clang__) - printf("clang\n"); +#if defined(_MSC_VER) + printf("msc\n"); +#elif defined(__clang__) + printf("clang gnuc\n"); #elif defined(__GNUC__) - printf("gcc\n"); + printf("gcc gnuc\n"); #elif defined(__sun) - printf("suncc\n"); + printf("suncc\n"); #else - printf("unknown\n"); + printf("unknown\n"); #endif - return 0; + return 0; } __EOF__ - rm -f $TEMP_DIR/checkcc - $1 -o $TEMP_DIR/checkcc $CFLAGS $LDFLAGS $TEMP_DIR/test.c 2> /dev/null - - if [ $? -ne 0 ]; then - return 1 - fi - return 0 + rm -f "$TEMP_DIR/checkcc" + $1 -o "$TEMP_DIR/checkcc" $CFLAGS $LDFLAGS "$TEMP_DIR/test.c" 2> /dev/null } check_cpp_compiler() { - cat > $TEMP_DIR/test.cpp << __EOF__ + cat > "$TEMP_DIR/test.cpp" << __EOF__ /* test file */ #include <iostream> int main(int argc, char **argv) { -#if defined(__clang__) - std::cout << "clang" << std::endl; +#if defined(_MSC_VER) + std::cout << "msc" << std::endl; +#elif defined(__clang__) + std::cout << "clang gnuc" << std::endl; #elif defined(__GNUC__) - std::cout << "gcc" << std::endl; + std::cout << "gcc gnuc" << std::endl; #elif defined(__sun) - std::cout << "suncc" << std::endl; + std::cout << "suncc" << std::endl; #else - std::cout << "unknown" << std::endl; + std::cout << "cc" << std::endl; #endif - return 0; + return 0; +} +__EOF__ + rm -f "$TEMP_DIR/checkcc" + $1 -o "$TEMP_DIR/checkcc" $CXXFLAGS $LDFLAGS "$TEMP_DIR/test.cpp" 2> /dev/null +} + +create_libtest_source() +{ + # $1: filename + # $2: optional include + cat > "$TEMP_DIR/$1" << __EOF__ +/* libtest file */ +int main(int argc, char **argv) { + return 0; } __EOF__ - rm -f $TEMP_DIR/checkcc - $1 -o $TEMP_DIR/checkcc $CXXFLAGS $LDFLAGS $TEMP_DIR/test.cpp 2> /dev/null - - if [ $? -ne 0 ]; then - return 1 - fi - return 0 + if [ -n "$2" ]; then + echo "#include <$2>" >> "$TEMP_DIR/$1" + fi +} + +check_c_lib() +{ + # $1: libname + # $2: optional include + if [ -z "$TOOLCHAIN_CC" ]; then + return 1 + fi + create_libtest_source "test.c" "$2" + rm -f "$TEMP_DIR/checklib" + $TOOLCHAIN_CC -o "$TEMP_DIR/checklib" $CFLAGS $LDFLAGS "-l$1" "$TEMP_DIR/test.c" 2> /dev/null +} + +check_cpp_lib() +{ + # $1: libname + # $2: optional include + if [ -z "$TOOLCHAIN_CXX" ]; then + return 1 + fi + create_libtest_source "test.cpp" "$2" + rm -f "$TEMP_DIR/checklib" + $TOOLCHAIN_CXX -o "$TEMP_DIR/checklib" $CXXFLAGS $LDFLAGS "-l$1" "$TEMP_DIR/test.cpp" 2> /dev/null +} + +check_lib() +{ + # $1: libname + # $2: optional include + if [ -n "$TOOLCHAIN_CC" ]; then + check_c_lib "$1" "$2" + elif [ -n "$TOOLCHAIN_CXX" ]; then + check_cpp_lib "$1" "$2" + fi } -printf "detect C compiler... " +detect_c_compiler() +{ + if [ -n "$TOOLCHAIN_CC" ]; then + return 0 + fi + printf "detect C compiler... " + if [ -n "$CC" ]; then + if check_c_compiler "$CC"; then + TOOLCHAIN_CC=$CC + TOOLCHAIN=`"$TEMP_DIR/checkcc"` + TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -` + echo "$CC" + return 0 + else + echo "$CC is not a working C compiler" + return 1 + fi + else + for COMP in $C_COMPILERS + do + if check_c_compiler "$COMP"; then + TOOLCHAIN_CC=$COMP + TOOLCHAIN=`"$TEMP_DIR/checkcc"` + TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -` + echo "$COMP" + return 0 + fi + done + echo "not found" + return 1 + fi +} -for COMP in $C_COMPILERS -do - check_c_compiler $COMP - if [ $? -ne 0 ]; then - if [ ! -z "$CC" ]; then - if [ $COMP = $CC ]; then - echo "$CC is not a working C Compiler" - TOOLCHAIN_DETECTION_ERROR="error" - break - fi - fi - else - TOOLCHAIN_NAME=`$TEMP_DIR/checkcc` - USE_TOOLCHAIN=$TOOLCHAIN_NAME - if [ $COMP = "cc" ]; then - # we have found a working compiler, but in case - # the compiler is gcc or clang, we try to use - # these commands and not 'cc' - TOOLCHAIN_NAME=`$TEMP_DIR/checkcc` - if [ $TOOLCHAIN_NAME = "gcc" ]; then - check_c_compiler "gcc" - if [ $? -eq 0 ]; then - COMP=gcc - USE_TOOLCHAIN="gcc" - fi - fi - if [ $TOOLCHAIN_NAME = "clang" ]; then - check_c_compiler "clang" - if [ $? -eq 0 ]; then - COMP=clang - USE_TOOLCHAIN="clang" - fi - fi - fi - - TOOLCHAIN_NAME=$USE_TOOLCHAIN - TOOLCHAIN_CC=$COMP - echo $COMP - break - fi -done -if [ -z $TOOLCHAIN_CC ]; then - echo "not found" -fi - -printf "detect C++ compiler... " +detect_cpp_compiler() +{ + if [ -n "$TOOLCHAIN_CXX" ]; then + return 0 + fi + printf "detect C++ compiler... " -for COMP in $CPP_COMPILERS -do - check_cpp_compiler $COMP - if [ $? -ne 0 ]; then - if [ ! -z "$CXX" ]; then - if [ $COMP = $CXX ]; then - echo "$CC is not a working C++ Compiler" - TOOLCHAIN_DETECTION_ERROR="error" - break - fi - fi - else - if [ $COMP = "CC" ]; then - # we have found a working compiler, but in case - # the compiler is gcc or clang, we try to use - # these commands and not 'cc' - TOOLCHAIN_NAME=`$TEMP_DIR/checkcc` - USE_TOOLCHAIN=$TOOLCHAIN_NAME - if [ $TOOLCHAIN_NAME = "gcc" ]; then - check_cpp_compiler "g++" - if [ $? -eq 0 ]; then - COMP=g++ - USE_TOOLCHAIN="gcc" - fi - fi - if [ $TOOLCHAIN_NAME = "clang" ]; then - check_cpp_compiler "clang++" - if [ $? -eq 0 ]; then - COMP=clang++ - USE_TOOLCHAIN="clang" - fi - fi - fi - - TOOLCHAIN_NAME=$USE_TOOLCHAIN - TOOLCHAIN_CXX=$COMP - echo $COMP - break - fi -done -if [ -z $TOOLCHAIN_CXX ]; then - echo "not found" -fi + if [ -n "$CXX" ]; then + if check_cpp_compiler "$CXX"; then + TOOLCHAIN_CXX=$CXX + TOOLCHAIN=`"$TEMP_DIR/checkcc"` + TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -` + echo "$CXX" + return 0 + else + echo "$CXX is not a working C++ compiler" + return 1 + fi + else + for COMP in $CPP_COMPILERS + do + if check_cpp_compiler "$COMP"; then + TOOLCHAIN_CXX=$COMP + TOOLCHAIN=`"$TEMP_DIR/checkcc"` + TOOLCHAIN_NAME=`echo "$TOOLCHAIN" | cut -f1 -d' ' -` + echo "$COMP" + return 0 + fi + done + echo "${TOOLCHAIN_CXX:-"not found"}" + return 1 + fi +} -TOOLCHAIN_LD=$TOOLCHAIN_CC - -if [ -z "$TOOLCHAIN_NAME" ]; then - TOOLCHAIN_DETECTION_ERROR="error" -else - cat >> $TEMP_DIR/config.mk << __EOF__ -# toolchain -__EOF__ - echo "CC = ${TOOLCHAIN_CC}" >> $TEMP_DIR/config.mk - if [ ! -z "$TOOLCHAIN_CXX" ]; then - echo "CXX = ${TOOLCHAIN_CXX}" >> $TEMP_DIR/config.mk - fi - echo "LD = ${TOOLCHAIN_LD}" >> $TEMP_DIR/config.mk - echo >> $TEMP_DIR/config.mk - - cat "make/${TOOLCHAIN_NAME}.mk" > /dev/null 2>&1 - if [ $? -eq 0 ]; then - echo "include \$(BUILD_ROOT)/make/${TOOLCHAIN_NAME}.mk" >> $TEMP_DIR/config.mk - else - echo "SHLIB_CFLAGS = -fPIC" >> $TEMP_DIR/config.mk - echo "SHLIB_LDFLAGS = -shared" >> $TEMP_DIR/config.mk - fi -fi +write_toolchain_defaults() +{ + echo "# toolchain" >> "$1" + if [ -n "$TOOLCHAIN_CC" ]; then + echo "CC = ${TOOLCHAIN_CC}" >> "$1" + fi + if [ -n "$TOOLCHAIN_CXX" ]; then + echo "CXX = ${TOOLCHAIN_CXX}" >> "$1" + fi + echo >> "$1" + if [ -f "make/${TOOLCHAIN_NAME}.mk" ]; then + cat "make/${TOOLCHAIN_NAME}.mk" >> "$1" + elif [ -f "make/cc.mk" ]; then + cat "make/cc.mk" >> "$1" + else + echo "!!! WARNING !!! Default toolchain flags not found. Configuration might be incomplete." + fi +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/uwproj.xsd Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,287 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns="http://unixwork.de/uwproj" + targetNamespace="http://unixwork.de/uwproj" + elementFormDefault="qualified" + version="0.2" +> + <xs:element name="project" type="ProjectType"/> + + <xs:complexType name="ProjectType"> + <xs:annotation> + <xs:documentation> + The root element of an uwproj project. + Consists of an optional <code>config</code> element + and an arbitrary number of <code>dependency</code> + and <code>target</code> elements. + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="config" type="ConfigType" minOccurs="0"/> + <xs:element name="dependency" type="DependencyType" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="target" type="TargetType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="ConfigType"> + <xs:annotation> + <xs:documentation> + The configuration section. + Consists of an arbitrary number of <code>var</code> elements. + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="var" type="ConfigVarType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="ConfigVarType"> + <xs:annotation> + <xs:documentation> + The definition of a configuration variable. + <p> + Configuration variables are supposed to be used in the configure script and are also + written to the resulting config file (in contrast to make variables, which are only + written to the config file). + The <code>name</code> attribute is mandatory, the value is defined by the text body of the element. + The optional Boolean <code>exec</code> attribute (false by default) controls, whether the entire + definition is automatically executed under command substitution. + </p> + </xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="exec" type="xs:boolean" default="false"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="PkgConfigType"> + <xs:annotation> + <xs:documentation> + Instructs configure to invoke <code>pkg-config</code>, if present on the system, to determine + compiler and linker flags. The text body of this element defines the package name to search. + To constrain the allowed versions, use the attributes <code>atleast, exact, max</code>. + </xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="atleast" type="xs:string"/> + <xs:attribute name="exact" type="xs:string"/> + <xs:attribute name="max" type="xs:string"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:simpleType name="LangType"> + <xs:annotation> + <xs:documentation> + Requests a compiler for the specified language. Allowed values are + c, cpp. + </xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"> + <xs:enumeration value="c"/> + <xs:enumeration value="cpp"/> + </xs:restriction> + </xs:simpleType> + + <xs:complexType name="DependencyType"> + <xs:annotation> + <xs:documentation> + Declares a dependency. + <p> + If the optional <code>name</code> attribute is omitted, the dependency is global + and must be satisfied, otherwise configuration shall fail. + A <em>named dependency</em> can be referenced by a target (or is implicitly referenced + by the default target, if no targets are specified). + Multiple declarations for the same named dependency may exist, in which case each declaration + is checked one after another, until one block is satisfied. The result of the first satisfied + dependency declaration is supposed to be applied to the config file. + </p> + <p> + The optional <code>platform</code> attribute may specify a <em>single</em> platform identifier and + the optional <code>toolchain</code> attribute may specify a <em>single</em> toolchain. + The optional <code>not</code> attribute may specify a comma-separated list of platform and/or + toolchain identifiers. + The configure script shall skip this dependency declaration if the detected platform and toolchain + is not matching the filter specification of these attributes. + </p> + </xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="lang" type="LangType"/> + <xs:element name="cflags" type="FlagsType"/> + <xs:element name="cxxflags" type="FlagsType"/> + <xs:element name="ldflags" type="FlagsType"/> + <xs:element name="pkgconfig" type="PkgConfigType"/> + <xs:element name="test" type="xs:string"> + <xs:annotation> + <xs:documentation> + Specifies a custom command that shall be executed to test whether this dependency is satisfied. + </xs:documentation> + </xs:annotation> + </xs:element> + <xs:element name="make" type="MakeVarType"/> + </xs:choice> + <xs:attribute name="name" type="xs:string"/> + <xs:attribute name="platform" type="xs:string"/> + <xs:attribute name="toolchain" type="xs:string"/> + <xs:attribute name="not" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="FlagsType"> + <xs:annotation> + <xs:documentation> + Instructs configure to append the contents of the element's body to the respective flags variable. + If the optional <code>exec</code> flag is set to <code>true</code>, the contents are supposed to be + executed under command substitution <em>at configuration time</em> before they are applied. + </xs:documentation> + </xs:annotation> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute name="exec" type="xs:boolean" default="false"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:complexType name="TargetType"> + <xs:annotation> + <xs:documentation> + Declares a build target that is supposed to be configured. + <p> + If no build target is declared explicitly, an implicit default + target is generated, which has the <code>alldependencies</code> + flag set. + </p> + <p> + The optional <code>name</code> attribute is also used to generate a prefix + for the compiler and linker flags variables. + Furthermore, a target may consist of an arbitrary number of <code>feature</code>, + <code>option</code>, and <code>define</code> elements. + Named dependencies can be listed (separated by comma) in the <code>dependencies</code> + element. If this target shall use <em>all</em> available named dependencies, the empty + element <code>alldependencies</code> can be used as a shortcut. + </p> + </xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="feature" type="FeatureType"/> + <xs:element name="option" type="OptionType"/> + <xs:element name="define" type="DefineType"/> + <xs:element name="dependencies" type="DependenciesType"/> + <xs:element name="alldependencies"> + <xs:complexType/> + </xs:element> + </xs:choice> + <xs:attribute name="name" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="FeatureType"> + <xs:annotation> + <xs:documentation> + Declares an optional feature, that can be enabled during configuration, if all + <code>dependencies</code> are satisfied. + If a feature is enabled, all <code>define</code> and <code>make</code> definitions are + supposed to be applied to the config file. + In case the optional <code>default</code> attribute is set to true, the feature is enabled by default + and is supposed to be automatically disabled (without error) when the dependencies are not satisfied. + The name that is supposed to be used for the --enable and --disable arguments can be optionally + specified with the <code>arg</code> attribute. Otherwise, the <code>name</code> is used by default. + Optionally, a description for the help text of the resulting configure script can be specified by + adding a <code>desc</code> element. + </xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:group ref="TargetDataGroup"/> + </xs:choice> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="arg" type="xs:string"/> + <xs:attribute name="default" type="xs:boolean" default="false"/> + <xs:element name="desc" type="xs:string"/> + </xs:complexType> + + <xs:complexType name="OptionType"> + <xs:annotation> + <xs:documentation> + Declares a configuration option. + The option argument name is specified with the <code>arg</code> attribute. + Then, the children of this element specify possible <code>values</code> by defining the conditions + (in terms of dependencies) and effects (in terms of defines and make variables) of each value. + Finally, a set of <code>default</code>s is specified which supposed to automagically select the most + appropriate value for a specific platform under the available dependencies (in case the option is not + explicitly specified by using the command line argument). + </xs:documentation> + </xs:annotation> + <xs:sequence> + <xs:element name="value" type="OptionValueType" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="default" type="OptionDefaultType" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="arg" type="xs:string" use="required"/> + </xs:complexType> + + <xs:complexType name="OptionValueType"> + <xs:annotation> + <xs:documentation> + Declares a possible value for the option (in the <code>str</code> attribute) and + the conditions (<code>dependencies</code>) and effects, the value has. + </xs:documentation> + </xs:annotation> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:group ref="TargetDataGroup"/> + </xs:choice> + <xs:attribute name="str" type="xs:string" use="required"/> + </xs:complexType> + + <xs:complexType name="OptionDefaultType"> + <xs:annotation> + <xs:documentation> + Specifies a default value for this option. Multiple default values can be specified, in which case + they are checked one after another for availability. With the optional <code>platform</code> attribute, + the default value can be constrained to a <em>single</em> specific platform and is supposed to be + skipped by configure, when this platform is not detected. + </xs:documentation> + </xs:annotation> + <xs:attribute name="value" type="xs:string" use="required"/> + <xs:attribute name="platform" type="xs:string"/> + </xs:complexType> + + <xs:group name="TargetDataGroup"> + <xs:choice> + <xs:element name="define" type="DefineType" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="dependencies" type="DependenciesType" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="make" type="MakeVarType" minOccurs="0" maxOccurs="unbounded"/> + </xs:choice> + </xs:group> + + <xs:complexType name="DefineType"> + <xs:annotation> + <xs:documentation> + Specifies C/C++ pre-processor definitions that are supposed to + be appended to the compiler flags, if supported. + (Note: for example, Fortran also supports C/C++ style pre-processor definitions under + certain circumstances) + </xs:documentation> + </xs:annotation> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="value" type="xs:string"/> + </xs:complexType> + + <xs:simpleType name="DependenciesType"> + <xs:annotation> + <xs:documentation>A comma-separated list of named dependencies.</xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"/> + </xs:simpleType> + + <xs:simpleType name="MakeVarType"> + <xs:annotation> + <xs:documentation> + The text contents in the body of this element are supposed to be appended literally + to the config file without prior processing. + </xs:documentation> + </xs:annotation> + <xs:restriction base="xs:string"/> + </xs:simpleType> +</xs:schema>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/testapp/app.manifest Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="test.app"/> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8. + For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 + + It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.--> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + </windowsSettings> + </application> +</assembly> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/testapp/main.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,791 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <Windows.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include <ui/ui.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <ui/ui.h> + + +typedef struct { + UiString *str1; + UiString *str2; + UiString *path; + UiText *text; + UiDouble *progress; + UiList *list; + UiList *menulist; + UiInteger *radio; + UiInteger *tabview; + UiGeneric *image; +} MyDocument; + +MyDocument *doc1; +MyDocument *doc2; + +UIWIDGET tabview; + +static UiCondVar *cond; +static int thr_end = 0; +static int thr_started = 0; + +int threadfunc(void *data) { + printf("thr wait for data...\n"); + ui_condvar_wait(cond); + printf("thr data received: {%s} [%d]\n", cond->data, cond->intdata); + ui_condvar_destroy(cond); + cond = NULL; + + return 0; +} + +void action_start_thread(UiEvent *event, void *data) { + if(!thr_started) { + cond = ui_condvar_create(); + ui_job(event->obj, threadfunc, NULL, NULL, NULL); + thr_started = 1; + } +} + +void action_notify_thread(UiEvent *event, void *data) { + if(!thr_end) { + ui_condvar_signal(cond, "hello thread", 123); + thr_end = 1; + } +} + +void action_menu(UiEvent *event, void *userdata) { + +} + +void action_file_selected(UiEvent *event, void *userdata) { + UiFileList *files = event->eventdata; + MyDocument *doc = event->document; + printf("files: %d\n", (int)files->nfiles); + if(files->nfiles > 0) { + printf("selected file: %s\n", files->files[0]); + ui_image_load_file(doc->image, files->files[0]); + } +} + +void action_button(UiEvent *event, void *userdata) { + ui_openfiledialog(event->obj, UI_FILEDIALOG_SELECT_SINGLE, action_file_selected, NULL); +} + +void action_switch(UiEvent *event, void *userdata) { + +} + +void action_toolbar_button(UiEvent *event, void *userdata) { + printf("toolbar button\n"); + + ui_dialog(event->obj, .title = "Dialog Title", .content = "Content Label", .button1_label = "btn1", .button2_label = "btn2", .input = TRUE, .closebutton_label = "Cancel"); +} + +void action_dialog_button(UiEvent *event, void *userdata) { + ui_close(event->obj); +} + +void action_toolbar_dialog(UiEvent *event, void *userdata) { + + UiObject *dialog = ui_dialog_window(event->obj, .title = "Dialog Window", .lbutton1 = "Cancel 1", .lbutton2 = "Btn 2", .rbutton3 = "Btn3", .rbutton4 = "Login 4", .onclick = action_dialog_button, .default_button = 4, .show_closebutton = UI_OFF); + + ui_vbox(dialog, .margin = 10, .spacing = 10) { + ui_label(dialog, .label = "Enter password:"); + ui_passwordfield(dialog, .varname = "password"); + } + + ui_show(dialog); +} + +void action_toolbar_newwindow(UiEvent *event, void *userdata) { + UiObject *obj = ui_simple_window("New Window", NULL); + + ui_headerbar0(obj) { + ui_headerbar_start(obj) { + ui_button(obj, .label = "Open"); + } + ui_headerbar_end(obj) { + ui_button(obj, .label = "Test"); + } + } + + ui_textarea(obj, .varname="text"); + + ui_show(obj); +} + +MyDocument* create_doc(void) { + MyDocument *doc = ui_document_new(sizeof(MyDocument)); + UiContext *docctx = ui_document_context(doc); + doc->str1 = ui_string_new(docctx, "str1"); + doc->str1 = ui_string_new(docctx, "str2"); + doc->path = ui_string_new(docctx, "path"); + doc->progress = ui_double_new(docctx, "progress"); + doc->list = ui_list_new(docctx, "list"); + ui_list_append(doc->list, "test1"); + ui_list_append(doc->list, "test2"); + ui_list_append(doc->list, "test3"); + doc->radio = ui_int_new(docctx, "radio"); + doc->tabview = ui_int_new(docctx, "tabview"); + doc->image = ui_generic_new(docctx, "image"); + //doc->text = ui_text_new(docctx, "text"); + return doc; +} + +UiIcon *icon = NULL; + +static void* list_getvalue(void *elm, int col) { + /* + if(col == 0) { + if(!icon) { + icon = ui_icon("folder", 24); + } + return icon; + } + */ + + char *str = elm; + return col == 0 ? str : "x"; +} + +static UiList *menu_list; +int new_item_count = 0; + +void action_add_menu_item(UiEvent *event, void *userdata) { + char str[64]; + snprintf(str, 64, "new item %d", new_item_count++); + + ui_list_append(menu_list, strdup(str)); + ui_list_notify(menu_list); +} + +void action_menu_list(UiEvent *event, void *userdata) { + printf("menu list item: %d\n", event->intval); +} + +static int tab_x = 0; +void action_tab2_button(UiEvent *event, void *userdata) { + MyDocument *doc = event->document; + printf("current page: %d\n", (int)ui_get(doc->tabview)); + ui_set(doc->tabview, 0); +} + + +void action_group1(UiEvent *event, void *userdata) { + UiContext *ctx = event->obj->ctx; + if(userdata) { + ui_unset_group(ctx, 1); + } else { + ui_set_group(ctx, 1); + } +} + +void action_group2(UiEvent *event, void *userdata) { + UiContext *ctx = event->obj->ctx; + if(userdata) { + ui_unset_group(ctx, 2); + } else { + ui_set_group(ctx, 2); + } +} + +void application_startup(UiEvent *event, void *data) { + // global list + UiContext *global = ui_global_context(); + menu_list = ui_list_new(global, "menulist"); + ui_list_append(menu_list, "menu list item 1"); + ui_list_append(menu_list, "menu list item 2"); + ui_list_append(menu_list, "menu list item 3"); + + + + UiObject *obj = ui_window("Test", NULL); + + MyDocument *doc = create_doc(); + ui_attach_document(obj->ctx, doc); + + ui_tabview(obj, .spacing=10, .margin=10, .tabview = UI_TABVIEW_NAVIGATION_SIDE, .varname="tabview") { + ui_tab(obj, "Tab 1") { + ui_vbox(obj, .fill = UI_OFF, .margin = 15, .spacing = 15) { + ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button); + ui_togglebutton(obj, .label = "Toggle"); + ui_checkbox(obj, .label = "My Checkbox"); + } + ui_grid(obj, .fill = UI_OFF, .columnspacing = 15, .rowspacing = 15, .margin = 15) { + ui_button(obj, .label = "Activate Group 1", .hexpand = TRUE, .onclick = action_group1); + ui_button(obj, .label = "Disable Group 1", .onclick = action_group1, .onclickdata = "disable"); + ui_newline(obj); + ui_button(obj, .label = "Activate Group 2", .hexpand = TRUE, .onclick = action_group2); + ui_button(obj, .label = "Disable Group 2", .onclick = action_group2, .onclickdata = "disable"); + ui_newline(obj); + + ui_button(obj, .label = "Groups 1,2", .colspan = 2, .groups = UI_GROUPS(1, 2)); + ui_newline(obj); + + ui_label(obj, .label = "Label Col 1", .align = UI_ALIGN_LEFT); + ui_label(obj, .label = "Label Col 2", .style = UI_LABEL_STYLE_TITLE, .align = UI_ALIGN_RIGHT); + ui_newline(obj); + + //ui_spinner(obj, .step = 5); + //ui_newline(obj); + + ui_progressbar(obj, .colspan = 2, .varname = "progress"); + ui_set(doc->progress, 0.75); + ui_newline(obj); + + ui_textfield(obj, .value = doc->str1); + ui_newline(obj); + + //ui_button(obj, .label="Test"); + ui_path_textfield(obj, .varname = "path"); + ui_set(doc->path, "/test/path/longdirectoryname/123"); + ui_newline(obj); + + //UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "Col 1", UI_STRING, "Col 2", -1); + //model->getvalue = list_getvalue; + ui_combobox(obj, .hexpand = true, .vexpand = false, .colspan = 2, .varname = "list", .getvalue = list_getvalue); + ui_newline(obj); + + ui_hbox0(obj) { + ui_radiobutton(obj, .label = "Radio 1", .varname = "radio"); + ui_radiobutton(obj, .label = "Radio 2", .varname = "radio"); + ui_radiobutton(obj, .label = "Radio 3", .varname = "radio"); + } + } + } + ui_tab(obj, "Tab 2") { + ui_button(obj, .label = "Button 1 Start Thread", .onclick=action_start_thread); + ui_button(obj, .label = "Button 2 Notify Thread", .onclick=action_notify_thread); + ui_button(obj, .label = "Button 3", .onclick=action_tab2_button); + ui_button(obj, .label = "Button 4", .onclick=action_tab2_button); + ui_button(obj, .label = "Button 5", .onclick=action_tab2_button); + ui_button(obj, .label = "Button 6", .onclick=action_tab2_button); + } + ui_tab(obj, "Tab 3") { + UiTabViewArgs args = {0}; + UI_CTN(obj, tabview=ui_tabview_create(obj, args)) { + UiObject *tab1 = ui_tabview_add(tabview, "Sub 1", -1); + ui_button(tab1, .label = "Button 1"); + + + UiObject *tab2 = ui_tabview_add(tabview, "Sub 2", -1); + ui_button(tab2, .label = "Button 2"); + } + } + ui_tab(obj, "Tab 4") { + ui_grid0(obj) { + ui_button(obj, .label = "test1"); + ui_newline(obj); + ui_textarea(obj, .varname = "text", .vexpand = TRUE, .hexpand = TRUE); + } + } + ui_tab(obj, "Tab 5") { + ui_button(obj, .label = "Test Button", .icon = "application-x-generic", .onclick = action_button); + ui_imageviewer(obj, .varname = "image", .style_class = "imageviewer"); + } + + ui_tab(obj, "Tab 6") { + ui_scrolledwindow(obj, .fill = UI_ON) { + ui_expander(obj, .label = "Expander", .margin = 10, .spacing = 10) { + ui_label(obj, .label = "Test"); + ui_button(obj, .label = "Button"); + } + + ui_frame(obj, .label = "Frame", .margin = 10, .spacing = 10) { + ui_label(obj, .label = "Title", .style = UI_LABEL_STYLE_TITLE); + ui_label(obj, .label = "Sub-Title", .style = UI_LABEL_STYLE_SUBTITLE); + ui_label(obj, .label = "Dim Label", .style = UI_LABEL_STYLE_DIM); + ui_label(obj, .label = "No Style"); + } + + for(int i=0;i<100;i++) { + char labelstr[32]; + snprintf(labelstr, 32, "button %d", i); + ui_button(obj, .label = labelstr); + } + } + } + } + + /* + + */ + + ui_show(obj); +} + +/* +typedef struct WindowData { + UiInteger* check; + UiInteger* toggle; + UiInteger* radio; + UiString* text; + UiString* password; + UiList* list; + UiString* t1; + UiString* t2; + UiString* t3; + UiString* path; + UiList* list2; + UiList* list3; + UiDouble* progress; + UiInteger* spinner; +} WindowData; + +static UiIcon* folder_icon; + +UiList* menuList; + +void event_mt(UiEvent* event, void* data) { + char* mt_str = data; + + printf("%s\n", mt_str); +} + +int test_threadfunc(void *data) { + char* str = data; + + return 0; +} + +void action_thread_test(UiEvent* event, void* data) { + ui_job(event->obj, test_threadfunc, "testdata", event_mt, "testdata2"); +} + +void action1(UiEvent* event, void* data) { + char* action = data; + + WindowData* wdata = event->window; + int64_t is_checked = ui_get(wdata->check); + int64_t radio = ui_get(wdata->radio); + + printf("data: %s %d\n", data, is_checked); + + double d = ui_get(wdata->progress); + ui_set(wdata->progress, d + 1); + + int spinner_active = ui_get(wdata->spinner); + ui_set(wdata->spinner, !spinner_active); + + ui_list_append(menuList, "List Item X"); + ui_list_append(menuList, "List Item X"); + ui_notify(menuList->observers, NULL); +} + +void action_set_checkbox(UiEvent* event, void* data) { + char* action = data; + + WindowData* wdata = event->window; + wdata->check->set(wdata->check, 1); +} + +void action_onchange(UiEvent* event, void* data) { + printf("onchange: %d\n", event->intval); +} + +void action_switch(UiEvent* event, void* data) { + printf("onchange: %d\n", event->intval); +} + +void action_toolbar_button(UiEvent* event, void *data) { + printf("toolbar action\n"); +} + + +void action_listselection_changed(UiEvent* event, void* data) { + printf("selection changed\n"); + UiListSelection* sel = event->eventdata; + for (int i = 0; i < sel->count; i++) { + int row = sel->rows[i]; + printf("row: %d\n", row); + } +} + +void action_onactivate(UiEvent* event, void* Data) { + printf("activate\n"); + UiListSelection* sel = event->eventdata; + for (int i = 0; i < sel->count; i++) { + int row = sel->rows[i]; + printf("row: %d\n", row); + } +} + +typedef struct TableData { + char* col1; + char* col2; + char* col3; +} TableData; + +void* table_getvalue(void* data, int i) { + TableData* t = data; + switch (i) { + case 0: return folder_icon; + case 1: return t->col1; + case 2: return t->col2; + case 3: return t->col3; + } + return NULL; +} + +void action_add(UiEvent* event, void* data) { + WindowData* wdata = event->window; + char* t1 = wdata->t1->get(wdata->t1); + char* t2 = wdata->t2->get(wdata->t2); + char* t3 = wdata->t3->get(wdata->t3); + + TableData* tdat = malloc(sizeof(TableData)); + tdat->col1 = _strdup(t1); + tdat->col2 = _strdup(t2); + tdat->col3 = _strdup(t3); + ui_list_append(wdata->list2, tdat); + wdata->list2->update(wdata->list2, 0); + +} + +void action_breadcrumb(UiEvent* event, void* data) { + int i = event->intval; + char* c = event->eventdata; + printf("index: %d\n", i); +} + +void dragstart(UiEvent* event, void* data) { + UiListDnd* ldnd = event->eventdata; + ui_selection_settext(ldnd->dnd, "Hello World!", -1); +} + +void dragcomplete(UiEvent* event, void* data) { + +} + +void dragover(UiEvent* event, void* data) { + +} + +void drop(UiEvent* event, void* data) { + +} + +void dialog_result(UiEvent *evt, void *data) { + char *str = evt->eventdata; + printf("dialog: %d\n", (int)evt->intval); +} + +void btn_dialog(UiEvent *evt, void *data) { + ui_dialog(evt->obj, .title = "Title", .input = TRUE, .content = "Hello World", .button1_label = "Yes", .button2_label = "No", .closebutton_label = "Close", .result = dialog_result); +} + + + + +void application_startup(UiEvent* event, void* data) { + UiContext* gctx = ui_global_context(); + menuList = ui_list_new(gctx, "menulist"); + ui_list_append(menuList, "List Item 1"); + ui_list_append(menuList, "List Item 2"); + ui_list_append(menuList, "List Item 3"); + ui_list_append(menuList, "List Item 4"); + ui_list_append(menuList, "List Item 5"); + ui_list_append(menuList, "List Item 6"); + + UiObject* obj = ui_window("Test", NULL); + WindowData* wdata = ui_malloc(obj->ctx, sizeof(WindowData)); + obj->window = wdata; + wdata->check = ui_int_new(obj->ctx, "check"); + wdata->toggle = ui_int_new(obj->ctx, "toggle"); + wdata->radio = ui_int_new(obj->ctx, "radio"); + wdata->text = ui_string_new(obj->ctx, "text"); + wdata->password = ui_string_new(obj->ctx, "password"); + wdata->list = ui_list_new(obj->ctx, "list"); + wdata->list2 = ui_list_new(obj->ctx, "list2"); + wdata->list3 = ui_list_new(obj->ctx, "list3"); + wdata->t1 = ui_string_new(obj->ctx, "t1"); + wdata->t2 = ui_string_new(obj->ctx, "t2"); + wdata->t3 = ui_string_new(obj->ctx, "t3"); + wdata->path = ui_string_new(obj->ctx, "path"); + wdata->progress = ui_double_new(obj->ctx, "progress"); + wdata->spinner = ui_int_new(obj->ctx, "spinner"); + + ui_list_append(wdata->list, "Hello"); + ui_list_append(wdata->list, "World"); + ui_list_append(wdata->list, "Item3"); + ui_list_append(wdata->list, "Item4"); + ui_list_append(wdata->list, "Item5"); + ui_list_append(wdata->list, "Item6"); + + ui_list_append(wdata->list3, "usr"); + ui_list_append(wdata->list3, "share"); + ui_list_append(wdata->list3, "test"); + ui_list_append(wdata->list3, "dir"); + + //folder_icon = ui_icon("Folder", 32); + folder_icon = ui_foldericon(16); + + TableData* td1 = malloc(sizeof(TableData)); + TableData* td2 = malloc(sizeof(TableData)); + TableData* td3 = malloc(sizeof(TableData)); + TableData* td4 = malloc(sizeof(TableData)); + TableData* td5 = malloc(sizeof(TableData)); + TableData* td6 = malloc(sizeof(TableData)); + td1->col1 = "a1"; + td1->col2 = "b1"; + td1->col3 = "c1"; + td2->col1 = "a2"; + td2->col2 = "b2"; + td2->col3 = "b3"; + td3->col1 = "a3"; + td3->col2 = "b3"; + td3->col3 = "c3"; + td4->col1 = "a3"; + td4->col2 = "b3"; + td4->col3 = "c3"; + td5->col1 = "a3"; + td5->col2 = "b3"; + td5->col3 = "c3"; + td6->col1 = "a3"; + td6->col2 = "b3"; + td6->col3 = "c3"; + + ui_list_append(wdata->list2, td1); + ui_list_append(wdata->list2, td2); + ui_list_append(wdata->list2, td3); + ui_list_append(wdata->list2, td4); + ui_list_append(wdata->list2, td5); + ui_list_append(wdata->list2, td6); + + ui_scrolledwindow0(obj) { + ui_grid(obj, .margin = 10, .columnspacing = 5, .rowspacing = 20) { + ui_button(obj, .label = "Thread Test", .onclick = action_thread_test, .onclickdata = "action1"); + ui_button(obj, .label = "Button2", .icon = "Back", .onclick = action1, .onclickdata = "action2"); + ui_button(obj, .icon = "Forward", .onclick = action1, .onclickdata = "action3", .hexpand = true); + ui_newline(obj); + + ui_button(obj, .label = "Dialog Test", .onclick = btn_dialog, .onclickdata = "action4"); + ui_button(obj, .label = "Button5", .onclick = action1, .onclickdata = "action5", .colspan = 2); + ui_newline(obj); + + ui_button(obj, .label = "Very Long Button Label Text ____________ Test", .onclick = action_set_checkbox); + ui_newline(obj); + + ui_checkbox(obj, .label = "Option 1", .value = wdata->check, .onchange = action_onchange); + ui_togglebutton(obj, .label = "Option 2", .value = wdata->toggle); + ui_newline(obj); + + ui_label(obj, .label = "Progress"); + ui_progressspinner(obj, .value = wdata->spinner); + ui_newline(obj); + + ui_hbox(obj, .colspan = 3) { + ui_radiobutton(obj, .label = "Radio 1", .value = wdata->radio); + ui_radiobutton(obj, .label = "Radio 2", .value = wdata->radio); + ui_radiobutton(obj, .label = "Radio 3", .value = wdata->radio); + } + ui_newline(obj); + ui_radiobutton(obj, .label = "Radio 4", .value = wdata->radio); + ui_switch(obj, .label = "test", .onchange = action_switch); + ui_newline(obj); + + //ui_breadcrumbbar(obj, .list = wdata->list3, .onactivate=action_breadcrumb); + ui_textfield(obj, .varname = "newtext"); + ui_path_textfield(obj, .colspan = 2, .value=wdata->path, .onactivate = action_breadcrumb); + ui_newline(obj); + wdata->path->set(wdata->path, "/usr/path/test"); + + ui_textfield(obj, .value = wdata->text); + ui_passwordfield(obj, .value = wdata->password); + ui_newline(obj); + + ui_frame(obj, .label = "Test", .colspan = 3) { + ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); + } + ui_newline(obj); + + ui_expander(obj, .label = "Expand", .colspan = 3, .margin = 10, .spacing = 5, .isexpanded = false) { + ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); + ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); + ui_button(obj, .label = "Button1", .onclick = action1, .onclickdata = "action1"); + } + ui_newline(obj); + + ui_combobox(obj, .list = wdata->list, .onselection= action_listselection_changed, .onactivate= action_onactivate); + ui_newline(obj); + + ui_tabview(obj, .colspan = 3, .vexpand = true, .hexpand = true, .tabview = UI_TABVIEW_NAVIGATION_SIDE) { + ui_tab(obj, "Tab 1") { + ui_button(obj, .label = "Tab 1 Button"); + } + ui_tab(obj, "Tab 2") { + ui_button(obj, .label = "Tab 2 Button"); + } + ui_tab(obj, "Tab 3") { + + } + } + ui_newline(obj); + + ui_label(obj, .label = "Test Label"); + ui_progressbar(obj, .value = wdata->progress, .colspan = 2); + ui_newline(obj); + + ui_newline(obj); + ui_textfield(obj, .value = wdata->t1); + ui_textfield(obj, .value = wdata->t2); + ui_textfield(obj, .value = wdata->t3); + ui_newline(obj); + ui_button(obj, .label = "Add", .onclick = action_add); + ui_newline(obj); + + + ui_newline(obj); + + UiModel* model = ui_model(obj->ctx, UI_ICON_TEXT, "Col 1", UI_STRING, "Col 2", UI_STRING, "Col 3", -1); + model->getvalue = table_getvalue; + ui_table(obj, .colspan = 3, .model = model, .list = wdata->list2, .onactivate = action_onactivate, + .onselection = action_listselection_changed, + .ondragstart = dragstart, .ondragcomplete = dragcomplete, .ondrop = drop); + ui_model_free(obj->ctx, model); + } + } + + ui_show(obj); +} + +*/ + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) +{ + ui_init("app1", NULL, 0); + ui_onstartup(application_startup, NULL); + + // menu + ui_menu("File") { + ui_menuitem(.label = "Test"); + } + + ui_toolbar_item("Test", .label = "Test", .onclick = action_toolbar_button); + ui_toolbar_item("Test2", .label = "New Window", .onclick = action_toolbar_newwindow); + ui_toolbar_item("Test3", .label = "Dialog", .onclick = action_toolbar_dialog); + ui_toolbar_item("Test4", .label = "Test 4", .onclick = action_toolbar_button); + ui_toolbar_item("Test5", .label = "Test 5", .onclick = action_toolbar_button); + ui_toolbar_item("Test6", .label = "Test 6", .onclick = action_toolbar_button); + ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button); + ui_toolbar_menu("Menu", .label = "Menu") { + ui_menuitem("Secondary Test", .onclick = action_toolbar_button, NULL); + ui_menu("Secondary Sub") { + ui_menuitem("Secondary subitem", NULL, NULL); + } + ui_menuseparator(); + ui_menu_itemlist(.varname = "menulist", .onselect=action_menu_list); + ui_menuseparator(); + ui_menuitem("last", .onclick = action_add_menu_item); + } + + ui_toolbar_appmenu() { + ui_menuitem("New"); + ui_menuitem("Open"); + ui_menuitem("Save"); + + ui_menuseparator(); + + ui_menuitem("Close"); + } + + ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Test6", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Toggle", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Menu", UI_TOOLBAR_LEFT); + + ui_toolbar_add_default("Test2", UI_TOOLBAR_CENTER); + ui_toolbar_add_default("Test3", UI_TOOLBAR_CENTER); + + ui_toolbar_add_default("Test4", UI_TOOLBAR_RIGHT); + ui_toolbar_add_default("Test5", UI_TOOLBAR_RIGHT); + + ui_main(); + + return (EXIT_SUCCESS); + + /* + ui_init("app1", 0, NULL); + ui_onstartup(application_startup, NULL); + + ui_menu("File") { + ui_menuitem(.label = "Item 1"); + ui_menuitem(.label = "Item 2"); + ui_menuseparator(); + ui_menu("File Sub") { + ui_menuitem(.label = "Sub Item"); + } + + ui_menuitem(.label = "Exit"); + } + + ui_toolbar_item("Test", .label = "Home", .icon = "Home", .onclick = action_toolbar_button); + ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button); + ui_toolbar_toggleitem("Toggle2", .label = "Toggle2", .onchange = action_toolbar_button); + ui_toolbar_toggleitem("Toggle3", .label = "Toggle3", .onchange = action_toolbar_button); + + ui_toolbar_menu("Menu", .label = "Menu") { + + ui_menuitem(.label = "x", NULL, NULL); + ui_menuitem(.label = "x", NULL, NULL); + ui_menu_itemlist(.varname = "menulist"); + ui_menuitem(.label = "x", NULL, NULL); + ui_menuitem(.label = "x", NULL, NULL); + ui_menuitem(.label = "x", NULL, NULL); + ui_menu("TB Sub") { + ui_menuitem("TB subitem", NULL, NULL); + } + } + + ui_toolbar_menu(NULL, .label = "Menu") { + ui_menuitem("Secondary Test", NULL, NULL); + ui_menu("Secondary Sub") { + ui_menuitem("Secondary subitem", NULL, NULL); + } + } + + ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Toggle", UI_TOOLBAR_LEFT); + ui_toolbar_add_default("Toggle2", UI_TOOLBAR_CENTER); + ui_toolbar_add_default("Toggle3", UI_TOOLBAR_CENTER); + ui_toolbar_add_default("Menu", UI_TOOLBAR_RIGHT); + + ui_main(); + + return (EXIT_SUCCESS); + */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/testapp/packages.config Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" /> + <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240122.1" targetFramework="native" /> + <package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.3233" targetFramework="native" /> + <package id="Microsoft.WindowsAppSDK" version="1.5.241001000" targetFramework="native" /> +</packages> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/testapp/testapp.vcxproj Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" /> + <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" /> + <Import Project="..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props')" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{3541f08b-e6cc-4c23-a0d3-51983aab33c6}</ProjectGuid> + <RootNamespace>testapp</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> + <WindowsPackageType>None</WindowsPackageType> + <AppxPackage>false</AppxPackage> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> + <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>false</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <AdditionalIncludeDirectories>C:\Users\Olaf\Projekte\toolkit\ui;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard_C>stdc17</LanguageStandard_C> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.c" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\ui\winui\winui.vcxproj"> + <Project>{59f97886-bf49-4b3f-9ef6-fa7a84f3ab56}</Project> + </ProjectReference> + <ProjectReference Include="..\ucx\ucx.vcxproj"> + <Project>{27da0164-3475-43e2-a1a4-a5d07d305749}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Manifest Include="app.manifest" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets')" /> + <Import Project="..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> + <Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" /> + <Import Project="..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.props'))" /> + <Error Condition="!Exists('..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.3233\build\Microsoft.Windows.SDK.BuildTools.targets'))" /> + <Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.240122.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> + <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" /> + <Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" /> + <Error Condition="!Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props'))" /> + <Error Condition="!Exists('..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets'))" /> + </Target> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/testapp/testapp.vcxproj.filters Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Quelldateien"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Headerdateien"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Ressourcendateien"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" /> + </ItemGroup> + <ItemGroup> + <Manifest Include="app.manifest" /> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/toolkit.sln Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,71 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33530.505 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testapp", "testapp\testapp.vcxproj", "{3541F08B-E6CC-4C23-A0D3-51983AAB33C6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ucx", "ucx\ucx.vcxproj", "{27DA0164-3475-43E2-A1A4-A5D07D305749}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winui", "..\..\ui\winui\winui.vcxproj", "{59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|ARM64.ActiveCfg = Debug|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|ARM64.Build.0 = Debug|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x64.ActiveCfg = Debug|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x64.Build.0 = Debug|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x86.ActiveCfg = Debug|Win32 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Debug|x86.Build.0 = Debug|Win32 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|ARM64.ActiveCfg = Release|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|ARM64.Build.0 = Release|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x64.ActiveCfg = Release|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x64.Build.0 = Release|x64 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x86.ActiveCfg = Release|Win32 + {3541F08B-E6CC-4C23-A0D3-51983AAB33C6}.Release|x86.Build.0 = Release|Win32 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|ARM64.ActiveCfg = Debug|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|ARM64.Build.0 = Debug|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x64.ActiveCfg = Debug|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x64.Build.0 = Debug|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x86.ActiveCfg = Debug|Win32 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Debug|x86.Build.0 = Debug|Win32 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|ARM64.ActiveCfg = Release|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|ARM64.Build.0 = Release|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x64.ActiveCfg = Release|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x64.Build.0 = Release|x64 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x86.ActiveCfg = Release|Win32 + {27DA0164-3475-43E2-A1A4-A5D07D305749}.Release|x86.Build.0 = Release|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.Build.0 = Debug|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.ActiveCfg = Debug|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.Build.0 = Debug|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x64.Deploy.0 = Debug|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.ActiveCfg = Debug|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.Build.0 = Debug|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Debug|x86.Deploy.0 = Debug|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.ActiveCfg = Release|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.Build.0 = Release|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|ARM64.Deploy.0 = Release|ARM64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.ActiveCfg = Release|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.Build.0 = Release|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x64.Deploy.0 = Release|x64 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.ActiveCfg = Release|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.Build.0 = Release|Win32 + {59F97886-BF49-4B3F-9EF6-FA7A84F3AB56}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {141CA624-F556-4BE7-9218-8D6EEAB95C95} + EndGlobalSection +EndGlobal
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/ucx/ucx.vcxproj Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{27da0164-3475-43e2-a1a4-a5d07d305749}</ProjectGuid> + <RootNamespace>ucx</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> + <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>false</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <LanguageStandard_C>stdc17</LanguageStandard_C> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\..\ucx\allocator.c" /> + <ClCompile Include="..\..\..\ucx\array_list.c" /> + <ClCompile Include="..\..\..\ucx\buffer.c" /> + <ClCompile Include="..\..\..\ucx\compare.c" /> + <ClCompile Include="..\..\..\ucx\hash_key.c" /> + <ClCompile Include="..\..\..\ucx\hash_map.c" /> + <ClCompile Include="..\..\..\ucx\iterator.c" /> + <ClCompile Include="..\..\..\ucx\linked_list.c" /> + <ClCompile Include="..\..\..\ucx\list.c" /> + <ClCompile Include="..\..\..\ucx\map.c" /> + <ClCompile Include="..\..\..\ucx\mempool.c" /> + <ClCompile Include="..\..\..\ucx\printf.c" /> + <ClCompile Include="..\..\..\ucx\string.c" /> + <ClCompile Include="..\..\..\ucx\szmul.c" /> + <ClCompile Include="..\..\..\ucx\tree.c" /> + <ClCompile Include="..\..\..\ucx\utils.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\ucx\cx\allocator.h" /> + <ClInclude Include="..\..\..\ucx\cx\array_list.h" /> + <ClInclude Include="..\..\..\ucx\cx\buffer.h" /> + <ClInclude Include="..\..\..\ucx\cx\collection.h" /> + <ClInclude Include="..\..\..\ucx\cx\common.h" /> + <ClInclude Include="..\..\..\ucx\cx\compare.h" /> + <ClInclude Include="..\..\..\ucx\cx\hash_key.h" /> + <ClInclude Include="..\..\..\ucx\cx\hash_map.h" /> + <ClInclude Include="..\..\..\ucx\cx\iterator.h" /> + <ClInclude Include="..\..\..\ucx\cx\linked_list.h" /> + <ClInclude Include="..\..\..\ucx\cx\list.h" /> + <ClInclude Include="..\..\..\ucx\cx\map.h" /> + <ClInclude Include="..\..\..\ucx\cx\mempool.h" /> + <ClInclude Include="..\..\..\ucx\cx\printf.h" /> + <ClInclude Include="..\..\..\ucx\cx\string.h" /> + <ClInclude Include="..\..\..\ucx\cx\test.h" /> + <ClInclude Include="..\..\..\ucx\cx\tree.h" /> + <ClInclude Include="..\..\..\ucx\cx\utils.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/ucx/ucx.vcxproj.filters Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Quelldateien"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Headerdateien"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Ressourcendateien"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\ucx\allocator.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\array_list.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\buffer.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\compare.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\hash_key.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\hash_map.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\linked_list.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\list.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\map.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\printf.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\string.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\utils.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\mempool.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\iterator.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\szmul.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ucx\tree.c"> + <Filter>Quelldateien</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\ucx\cx\allocator.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\array_list.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\buffer.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\collection.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\common.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\compare.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\hash_key.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\hash_map.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\iterator.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\linked_list.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\list.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\map.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\mempool.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\printf.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\string.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\utils.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\test.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ucx\cx\tree.h"> + <Filter>Headerdateien</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/uicommon/uicommon.vcxproj Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>17.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{8b88698e-c185-4383-99fe-0c34d6deed2e}</ProjectGuid> + <RootNamespace>uicommon</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v143</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> + <IntDir>..\..\..\build\vs\testapp\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>false</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;UI_WINUI;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + <LanguageStandard_C>stdc17</LanguageStandard_C> + <AdditionalIncludeDirectories>$(SolutionDir)..\..\ucx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\..\ui\common\context.c" /> + <ClCompile Include="..\..\..\ui\common\document.c" /> + <ClCompile Include="..\..\..\ui\common\menu.c" /> + <ClCompile Include="..\..\..\ui\common\object.c" /> + <ClCompile Include="..\..\..\ui\common\properties.c" /> + <ClCompile Include="..\..\..\ui\common\toolbar.c" /> + <ClCompile Include="..\..\..\ui\common\types.c" /> + <ClCompile Include="..\..\..\ui\common\ucx_properties.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\ui\common\context.h" /> + <ClInclude Include="..\..\..\ui\common\document.h" /> + <ClInclude Include="..\..\..\ui\common\menu.h" /> + <ClInclude Include="..\..\..\ui\common\object.h" /> + <ClInclude Include="..\..\..\ui\common\properties.h" /> + <ClInclude Include="..\..\..\ui\common\toolbar.h" /> + <ClInclude Include="..\..\..\ui\common\types.h" /> + <ClInclude Include="..\..\..\ui\common\ucx_properties.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/uicommon/uicommon.vcxproj.filters Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Ressourcendateien"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Ressourcendateien\Source"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\ui\common\context.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\document.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\menu.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\object.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\properties.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\toolbar.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\types.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + <ClCompile Include="..\..\..\ui\common\ucx_properties.c"> + <Filter>Ressourcendateien\Source</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\ui\common\context.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\document.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\menu.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\object.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\properties.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\toolbar.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\types.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + <ClInclude Include="..\..\..\ui\common\ucx_properties.h"> + <Filter>Ressourcendateien\Source</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/vs/uicommon/uicommon.vcxproj.user Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup /> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,630 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + ED6580EE2CFF19F900F5402F /* context.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DD2CFF19F900F5402F /* context.c */; }; + ED6580EF2CFF19F900F5402F /* menu.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E12CFF19F900F5402F /* menu.c */; }; + ED6580F02CFF19F900F5402F /* threadpool.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E72CFF19F900F5402F /* threadpool.c */; }; + ED6580F12CFF19F900F5402F /* condvar.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DB2CFF19F900F5402F /* condvar.c */; }; + ED6580F22CFF19F900F5402F /* document.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580DF2CFF19F900F5402F /* document.c */; }; + ED6580F32CFF19F900F5402F /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E32CFF19F900F5402F /* object.c */; }; + ED6580F42CFF19F900F5402F /* toolbar.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E92CFF19F900F5402F /* toolbar.c */; }; + ED6580F52CFF19F900F5402F /* types.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580EB2CFF19F900F5402F /* types.c */; }; + ED6580F62CFF19F900F5402F /* properties.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580E52CFF19F900F5402F /* properties.c */; }; + ED6580F72CFF19F900F5402F /* ucx_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580ED2CFF19F900F5402F /* ucx_properties.c */; }; + ED65811D2CFF1A3000F5402F /* linked_list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581142CFF1A3000F5402F /* linked_list.c */; }; + ED65811E2CFF1A3000F5402F /* tree.c in Sources */ = {isa = PBXBuildFile; fileRef = ED65811B2CFF1A3000F5402F /* tree.c */; }; + ED6581202CFF1A3000F5402F /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581172CFF1A3000F5402F /* mempool.c */; }; + ED6581212CFF1A3000F5402F /* map.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581162CFF1A3000F5402F /* map.c */; }; + ED6581222CFF1A3000F5402F /* hash_map.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581122CFF1A3000F5402F /* hash_map.c */; }; + ED6581232CFF1A3000F5402F /* array_list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FA2CFF1A3000F5402F /* array_list.c */; }; + ED6581242CFF1A3000F5402F /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581152CFF1A3000F5402F /* list.c */; }; + ED6581252CFF1A3000F5402F /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FC2CFF1A3000F5402F /* compare.c */; }; + ED6581262CFF1A3000F5402F /* hash_key.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581112CFF1A3000F5402F /* hash_key.c */; }; + ED6581272CFF1A3000F5402F /* iterator.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581132CFF1A3000F5402F /* iterator.c */; }; + ED6581282CFF1A3000F5402F /* string.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581192CFF1A3000F5402F /* string.c */; }; + ED6581292CFF1A3000F5402F /* allocator.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580F92CFF1A3000F5402F /* allocator.c */; }; + ED65812A2CFF1A3000F5402F /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = ED65811C2CFF1A3000F5402F /* utils.c */; }; + ED65812B2CFF1A3000F5402F /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6580FB2CFF1A3000F5402F /* buffer.c */; }; + ED65812C2CFF1A3000F5402F /* printf.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6581182CFF1A3000F5402F /* printf.c */; }; + ED65812D2CFF1A3000F5402F /* common.h.orig in Resources */ = {isa = PBXBuildFile; fileRef = ED6581022CFF1A3000F5402F /* common.h.orig */; }; + ED6581312CFF1A8800F5402F /* toolkit.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581302CFF1A8800F5402F /* toolkit.m */; }; + ED6581342CFF1F1900F5402F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581332CFF1F1900F5402F /* AppDelegate.m */; }; + ED6581392CFF287300F5402F /* EventData.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581362CFF287300F5402F /* EventData.m */; }; + ED65813A2CFF287300F5402F /* UiJob.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581382CFF287300F5402F /* UiJob.m */; }; + ED6581432CFF3BCE00F5402F /* window.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581422CFF3BCE00F5402F /* window.m */; }; + ED6581442CFF3BCE00F5402F /* button.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65813C2CFF3BCE00F5402F /* button.m */; }; + ED6581452CFF3BCE00F5402F /* Container.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65813E2CFF3BCE00F5402F /* Container.m */; }; + ED6581462CFF3BCE00F5402F /* GridLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = ED6581402CFF3BCE00F5402F /* GridLayout.m */; }; + ED65815C2CFF3EE900F5402F /* MainWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65815B2CFF3EE900F5402F /* MainWindow.m */; }; + ED65815F2CFF4BF200F5402F /* WindowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = ED65815E2CFF4BF200F5402F /* WindowManager.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + ED6580AC2CFF122700F5402F /* toolkit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = toolkit.app; sourceTree = BUILT_PRODUCTS_DIR; }; + ED6580DA2CFF19F900F5402F /* condvar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = condvar.h; path = /Users/olaf/Projekte/toolkit/ui/common/condvar.h; sourceTree = "<absolute>"; }; + ED6580DB2CFF19F900F5402F /* condvar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = condvar.c; path = /Users/olaf/Projekte/toolkit/ui/common/condvar.c; sourceTree = "<absolute>"; }; + ED6580DC2CFF19F900F5402F /* context.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = context.h; path = /Users/olaf/Projekte/toolkit/ui/common/context.h; sourceTree = "<absolute>"; }; + ED6580DD2CFF19F900F5402F /* context.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = context.c; path = /Users/olaf/Projekte/toolkit/ui/common/context.c; sourceTree = "<absolute>"; }; + ED6580DE2CFF19F900F5402F /* document.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = document.h; path = /Users/olaf/Projekte/toolkit/ui/common/document.h; sourceTree = "<absolute>"; }; + ED6580DF2CFF19F900F5402F /* document.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = document.c; path = /Users/olaf/Projekte/toolkit/ui/common/document.c; sourceTree = "<absolute>"; }; + ED6580E02CFF19F900F5402F /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = /Users/olaf/Projekte/toolkit/ui/common/menu.h; sourceTree = "<absolute>"; }; + ED6580E12CFF19F900F5402F /* menu.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = menu.c; path = /Users/olaf/Projekte/toolkit/ui/common/menu.c; sourceTree = "<absolute>"; }; + ED6580E22CFF19F900F5402F /* object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = object.h; path = /Users/olaf/Projekte/toolkit/ui/common/object.h; sourceTree = "<absolute>"; }; + ED6580E32CFF19F900F5402F /* object.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = object.c; path = /Users/olaf/Projekte/toolkit/ui/common/object.c; sourceTree = "<absolute>"; }; + ED6580E42CFF19F900F5402F /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ui/common/properties.h; sourceTree = "<absolute>"; }; + ED6580E52CFF19F900F5402F /* properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = properties.c; path = /Users/olaf/Projekte/toolkit/ui/common/properties.c; sourceTree = "<absolute>"; }; + ED6580E62CFF19F900F5402F /* threadpool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = threadpool.h; path = /Users/olaf/Projekte/toolkit/ui/common/threadpool.h; sourceTree = "<absolute>"; }; + ED6580E72CFF19F900F5402F /* threadpool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = threadpool.c; path = /Users/olaf/Projekte/toolkit/ui/common/threadpool.c; sourceTree = "<absolute>"; }; + ED6580E82CFF19F900F5402F /* toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolbar.h; path = /Users/olaf/Projekte/toolkit/ui/common/toolbar.h; sourceTree = "<absolute>"; }; + ED6580E92CFF19F900F5402F /* toolbar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = toolbar.c; path = /Users/olaf/Projekte/toolkit/ui/common/toolbar.c; sourceTree = "<absolute>"; }; + ED6580EA2CFF19F900F5402F /* types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = types.h; path = /Users/olaf/Projekte/toolkit/ui/common/types.h; sourceTree = "<absolute>"; }; + ED6580EB2CFF19F900F5402F /* types.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = types.c; path = /Users/olaf/Projekte/toolkit/ui/common/types.c; sourceTree = "<absolute>"; }; + ED6580EC2CFF19F900F5402F /* ucx_properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ucx_properties.h; path = /Users/olaf/Projekte/toolkit/ui/common/ucx_properties.h; sourceTree = "<absolute>"; }; + ED6580ED2CFF19F900F5402F /* ucx_properties.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = ucx_properties.c; path = /Users/olaf/Projekte/toolkit/ui/common/ucx_properties.c; sourceTree = "<absolute>"; }; + ED6580F92CFF1A3000F5402F /* allocator.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = allocator.c; path = /Users/olaf/Projekte/toolkit/ucx/allocator.c; sourceTree = "<absolute>"; }; + ED6580FA2CFF1A3000F5402F /* array_list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = array_list.c; path = /Users/olaf/Projekte/toolkit/ucx/array_list.c; sourceTree = "<absolute>"; }; + ED6580FB2CFF1A3000F5402F /* buffer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = /Users/olaf/Projekte/toolkit/ucx/buffer.c; sourceTree = "<absolute>"; }; + ED6580FC2CFF1A3000F5402F /* compare.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = compare.c; path = /Users/olaf/Projekte/toolkit/ucx/compare.c; sourceTree = "<absolute>"; }; + ED6580FD2CFF1A3000F5402F /* allocator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = allocator.h; sourceTree = "<group>"; }; + ED6580FE2CFF1A3000F5402F /* array_list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = array_list.h; sourceTree = "<group>"; }; + ED6580FF2CFF1A3000F5402F /* buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = buffer.h; sourceTree = "<group>"; }; + ED6581002CFF1A3000F5402F /* collection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = collection.h; sourceTree = "<group>"; }; + ED6581012CFF1A3000F5402F /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; }; + ED6581022CFF1A3000F5402F /* common.h.orig */ = {isa = PBXFileReference; lastKnownFileType = text; path = common.h.orig; sourceTree = "<group>"; }; + ED6581032CFF1A3000F5402F /* compare.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = compare.h; sourceTree = "<group>"; }; + ED6581042CFF1A3000F5402F /* hash_key.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hash_key.h; sourceTree = "<group>"; }; + ED6581052CFF1A3000F5402F /* hash_map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hash_map.h; sourceTree = "<group>"; }; + ED6581062CFF1A3000F5402F /* iterator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iterator.h; sourceTree = "<group>"; }; + ED6581072CFF1A3000F5402F /* linked_list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = linked_list.h; sourceTree = "<group>"; }; + ED6581082CFF1A3000F5402F /* list.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = list.h; sourceTree = "<group>"; }; + ED6581092CFF1A3000F5402F /* map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = map.h; sourceTree = "<group>"; }; + ED65810A2CFF1A3000F5402F /* mempool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mempool.h; sourceTree = "<group>"; }; + ED65810B2CFF1A3000F5402F /* printf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = printf.h; sourceTree = "<group>"; }; + ED65810C2CFF1A3000F5402F /* string.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = string.h; sourceTree = "<group>"; }; + ED65810D2CFF1A3000F5402F /* test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = test.h; sourceTree = "<group>"; }; + ED65810E2CFF1A3000F5402F /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tree.h; sourceTree = "<group>"; }; + ED65810F2CFF1A3000F5402F /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; }; + ED6581112CFF1A3000F5402F /* hash_key.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hash_key.c; path = /Users/olaf/Projekte/toolkit/ucx/hash_key.c; sourceTree = "<absolute>"; }; + ED6581122CFF1A3000F5402F /* hash_map.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = hash_map.c; path = /Users/olaf/Projekte/toolkit/ucx/hash_map.c; sourceTree = "<absolute>"; }; + ED6581132CFF1A3000F5402F /* iterator.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = iterator.c; path = /Users/olaf/Projekte/toolkit/ucx/iterator.c; sourceTree = "<absolute>"; }; + ED6581142CFF1A3000F5402F /* linked_list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = linked_list.c; path = /Users/olaf/Projekte/toolkit/ucx/linked_list.c; sourceTree = "<absolute>"; }; + ED6581152CFF1A3000F5402F /* list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = list.c; path = /Users/olaf/Projekte/toolkit/ucx/list.c; sourceTree = "<absolute>"; }; + ED6581162CFF1A3000F5402F /* map.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = map.c; path = /Users/olaf/Projekte/toolkit/ucx/map.c; sourceTree = "<absolute>"; }; + ED6581172CFF1A3000F5402F /* mempool.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mempool.c; path = /Users/olaf/Projekte/toolkit/ucx/mempool.c; sourceTree = "<absolute>"; }; + ED6581182CFF1A3000F5402F /* printf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = printf.c; path = /Users/olaf/Projekte/toolkit/ucx/printf.c; sourceTree = "<absolute>"; }; + ED6581192CFF1A3000F5402F /* string.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = string.c; path = /Users/olaf/Projekte/toolkit/ucx/string.c; sourceTree = "<absolute>"; }; + ED65811B2CFF1A3000F5402F /* tree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = tree.c; path = /Users/olaf/Projekte/toolkit/ucx/tree.c; sourceTree = "<absolute>"; }; + ED65811C2CFF1A3000F5402F /* utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = utils.c; path = /Users/olaf/Projekte/toolkit/ucx/utils.c; sourceTree = "<absolute>"; }; + ED65812F2CFF1A8800F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/toolkit.h; sourceTree = "<absolute>"; }; + ED6581302CFF1A8800F5402F /* toolkit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = toolkit.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/toolkit.m; sourceTree = "<absolute>"; }; + ED6581322CFF1F1900F5402F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/AppDelegate.h; sourceTree = "<absolute>"; }; + ED6581332CFF1F1900F5402F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/AppDelegate.m; sourceTree = "<absolute>"; }; + ED6581352CFF287300F5402F /* EventData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = EventData.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/EventData.h; sourceTree = "<absolute>"; }; + ED6581362CFF287300F5402F /* EventData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = EventData.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/EventData.m; sourceTree = "<absolute>"; }; + ED6581372CFF287300F5402F /* UiJob.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = UiJob.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/UiJob.h; sourceTree = "<absolute>"; }; + ED6581382CFF287300F5402F /* UiJob.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = UiJob.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/UiJob.m; sourceTree = "<absolute>"; }; + ED65813B2CFF3BCE00F5402F /* button.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = button.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/button.h; sourceTree = "<absolute>"; }; + ED65813C2CFF3BCE00F5402F /* button.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = button.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/button.m; sourceTree = "<absolute>"; }; + ED65813D2CFF3BCE00F5402F /* Container.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Container.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Container.h; sourceTree = "<absolute>"; }; + ED65813E2CFF3BCE00F5402F /* Container.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Container.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/Container.m; sourceTree = "<absolute>"; }; + ED65813F2CFF3BCE00F5402F /* GridLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GridLayout.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/GridLayout.h; sourceTree = "<absolute>"; }; + ED6581402CFF3BCE00F5402F /* GridLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = GridLayout.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/GridLayout.m; sourceTree = "<absolute>"; }; + ED6581412CFF3BCE00F5402F /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = window.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/window.h; sourceTree = "<absolute>"; }; + ED6581422CFF3BCE00F5402F /* window.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = window.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/window.m; sourceTree = "<absolute>"; }; + ED6581482CFF3CA000F5402F /* button.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = button.h; path = /Users/olaf/Projekte/toolkit/ui/ui/button.h; sourceTree = "<absolute>"; }; + ED6581492CFF3CA000F5402F /* container.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = container.h; path = /Users/olaf/Projekte/toolkit/ui/ui/container.h; sourceTree = "<absolute>"; }; + ED65814A2CFF3CA000F5402F /* display.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = display.h; path = /Users/olaf/Projekte/toolkit/ui/ui/display.h; sourceTree = "<absolute>"; }; + ED65814B2CFF3CA000F5402F /* dnd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dnd.h; path = /Users/olaf/Projekte/toolkit/ui/ui/dnd.h; sourceTree = "<absolute>"; }; + ED65814C2CFF3CA000F5402F /* entry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = entry.h; path = /Users/olaf/Projekte/toolkit/ui/ui/entry.h; sourceTree = "<absolute>"; }; + ED65814D2CFF3CA000F5402F /* graphics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = graphics.h; path = /Users/olaf/Projekte/toolkit/ui/ui/graphics.h; sourceTree = "<absolute>"; }; + ED65814E2CFF3CA000F5402F /* icons.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = icons.h; path = /Users/olaf/Projekte/toolkit/ui/ui/icons.h; sourceTree = "<absolute>"; }; + ED65814F2CFF3CA000F5402F /* image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = image.h; path = /Users/olaf/Projekte/toolkit/ui/ui/image.h; sourceTree = "<absolute>"; }; + ED6581502CFF3CA000F5402F /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = /Users/olaf/Projekte/toolkit/ui/ui/menu.h; sourceTree = "<absolute>"; }; + ED6581512CFF3CA000F5402F /* properties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = properties.h; path = /Users/olaf/Projekte/toolkit/ui/ui/properties.h; sourceTree = "<absolute>"; }; + ED6581522CFF3CA000F5402F /* range.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = range.h; path = /Users/olaf/Projekte/toolkit/ui/ui/range.h; sourceTree = "<absolute>"; }; + ED6581532CFF3CA000F5402F /* stock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = stock.h; path = /Users/olaf/Projekte/toolkit/ui/ui/stock.h; sourceTree = "<absolute>"; }; + ED6581542CFF3CA000F5402F /* text.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = text.h; path = /Users/olaf/Projekte/toolkit/ui/ui/text.h; sourceTree = "<absolute>"; }; + ED6581552CFF3CA000F5402F /* toolbar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolbar.h; path = /Users/olaf/Projekte/toolkit/ui/ui/toolbar.h; sourceTree = "<absolute>"; }; + ED6581562CFF3CA000F5402F /* toolkit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = toolkit.h; path = /Users/olaf/Projekte/toolkit/ui/ui/toolkit.h; sourceTree = "<absolute>"; }; + ED6581572CFF3CA000F5402F /* tree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = tree.h; path = /Users/olaf/Projekte/toolkit/ui/ui/tree.h; sourceTree = "<absolute>"; }; + ED6581582CFF3CA000F5402F /* ui.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ui.h; path = /Users/olaf/Projekte/toolkit/ui/ui/ui.h; sourceTree = "<absolute>"; }; + ED6581592CFF3CA000F5402F /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = window.h; path = /Users/olaf/Projekte/toolkit/ui/ui/window.h; sourceTree = "<absolute>"; }; + ED65815A2CFF3EE900F5402F /* MainWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainWindow.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/MainWindow.h; sourceTree = "<absolute>"; }; + ED65815B2CFF3EE900F5402F /* MainWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MainWindow.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/MainWindow.m; sourceTree = "<absolute>"; }; + ED65815D2CFF4BF200F5402F /* WindowManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WindowManager.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/WindowManager.h; sourceTree = "<absolute>"; }; + ED65815E2CFF4BF200F5402F /* WindowManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WindowManager.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/WindowManager.m; sourceTree = "<absolute>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + ED6580AE2CFF122700F5402F /* toolkit */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = toolkit; + sourceTree = "<group>"; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + ED6580A92CFF122700F5402F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + ED6580A32CFF122700F5402F = { + isa = PBXGroup; + children = ( + ED6581472CFF3C8300F5402F /* public */, + ED65812E2CFF1A7200F5402F /* cocoa */, + ED6580F82CFF1A1200F5402F /* ucx */, + ED6580D92CFF19DB00F5402F /* common */, + ED6580AE2CFF122700F5402F /* toolkit */, + ED6580AD2CFF122700F5402F /* Products */, + ); + sourceTree = "<group>"; + }; + ED6580AD2CFF122700F5402F /* Products */ = { + isa = PBXGroup; + children = ( + ED6580AC2CFF122700F5402F /* toolkit.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + ED6580D92CFF19DB00F5402F /* common */ = { + isa = PBXGroup; + children = ( + ED6580DA2CFF19F900F5402F /* condvar.h */, + ED6580DB2CFF19F900F5402F /* condvar.c */, + ED6580DC2CFF19F900F5402F /* context.h */, + ED6580DD2CFF19F900F5402F /* context.c */, + ED6580DE2CFF19F900F5402F /* document.h */, + ED6580DF2CFF19F900F5402F /* document.c */, + ED6580E02CFF19F900F5402F /* menu.h */, + ED6580E12CFF19F900F5402F /* menu.c */, + ED6580E22CFF19F900F5402F /* object.h */, + ED6580E32CFF19F900F5402F /* object.c */, + ED6580E42CFF19F900F5402F /* properties.h */, + ED6580E52CFF19F900F5402F /* properties.c */, + ED6580E62CFF19F900F5402F /* threadpool.h */, + ED6580E72CFF19F900F5402F /* threadpool.c */, + ED6580E82CFF19F900F5402F /* toolbar.h */, + ED6580E92CFF19F900F5402F /* toolbar.c */, + ED6580EA2CFF19F900F5402F /* types.h */, + ED6580EB2CFF19F900F5402F /* types.c */, + ED6580EC2CFF19F900F5402F /* ucx_properties.h */, + ED6580ED2CFF19F900F5402F /* ucx_properties.c */, + ); + path = common; + sourceTree = "<group>"; + }; + ED6580F82CFF1A1200F5402F /* ucx */ = { + isa = PBXGroup; + children = ( + ED6580F92CFF1A3000F5402F /* allocator.c */, + ED6580FA2CFF1A3000F5402F /* array_list.c */, + ED6580FB2CFF1A3000F5402F /* buffer.c */, + ED6580FC2CFF1A3000F5402F /* compare.c */, + ED6581102CFF1A3000F5402F /* cx */, + ED6581112CFF1A3000F5402F /* hash_key.c */, + ED6581122CFF1A3000F5402F /* hash_map.c */, + ED6581132CFF1A3000F5402F /* iterator.c */, + ED6581142CFF1A3000F5402F /* linked_list.c */, + ED6581152CFF1A3000F5402F /* list.c */, + ED6581162CFF1A3000F5402F /* map.c */, + ED6581172CFF1A3000F5402F /* mempool.c */, + ED6581182CFF1A3000F5402F /* printf.c */, + ED6581192CFF1A3000F5402F /* string.c */, + ED65811B2CFF1A3000F5402F /* tree.c */, + ED65811C2CFF1A3000F5402F /* utils.c */, + ); + path = ucx; + sourceTree = "<group>"; + }; + ED6581102CFF1A3000F5402F /* cx */ = { + isa = PBXGroup; + children = ( + ED6580FD2CFF1A3000F5402F /* allocator.h */, + ED6580FE2CFF1A3000F5402F /* array_list.h */, + ED6580FF2CFF1A3000F5402F /* buffer.h */, + ED6581002CFF1A3000F5402F /* collection.h */, + ED6581012CFF1A3000F5402F /* common.h */, + ED6581022CFF1A3000F5402F /* common.h.orig */, + ED6581032CFF1A3000F5402F /* compare.h */, + ED6581042CFF1A3000F5402F /* hash_key.h */, + ED6581052CFF1A3000F5402F /* hash_map.h */, + ED6581062CFF1A3000F5402F /* iterator.h */, + ED6581072CFF1A3000F5402F /* linked_list.h */, + ED6581082CFF1A3000F5402F /* list.h */, + ED6581092CFF1A3000F5402F /* map.h */, + ED65810A2CFF1A3000F5402F /* mempool.h */, + ED65810B2CFF1A3000F5402F /* printf.h */, + ED65810C2CFF1A3000F5402F /* string.h */, + ED65810D2CFF1A3000F5402F /* test.h */, + ED65810E2CFF1A3000F5402F /* tree.h */, + ED65810F2CFF1A3000F5402F /* utils.h */, + ); + name = cx; + path = /Users/olaf/Projekte/toolkit/ucx/cx; + sourceTree = "<absolute>"; + }; + ED65812E2CFF1A7200F5402F /* cocoa */ = { + isa = PBXGroup; + children = ( + ED65815D2CFF4BF200F5402F /* WindowManager.h */, + ED65815E2CFF4BF200F5402F /* WindowManager.m */, + ED65815A2CFF3EE900F5402F /* MainWindow.h */, + ED65815B2CFF3EE900F5402F /* MainWindow.m */, + ED65813B2CFF3BCE00F5402F /* button.h */, + ED65813C2CFF3BCE00F5402F /* button.m */, + ED65813D2CFF3BCE00F5402F /* Container.h */, + ED65813E2CFF3BCE00F5402F /* Container.m */, + ED65813F2CFF3BCE00F5402F /* GridLayout.h */, + ED6581402CFF3BCE00F5402F /* GridLayout.m */, + ED6581412CFF3BCE00F5402F /* window.h */, + ED6581422CFF3BCE00F5402F /* window.m */, + ED6581352CFF287300F5402F /* EventData.h */, + ED6581362CFF287300F5402F /* EventData.m */, + ED6581372CFF287300F5402F /* UiJob.h */, + ED6581382CFF287300F5402F /* UiJob.m */, + ED6581322CFF1F1900F5402F /* AppDelegate.h */, + ED6581332CFF1F1900F5402F /* AppDelegate.m */, + ED65812F2CFF1A8800F5402F /* toolkit.h */, + ED6581302CFF1A8800F5402F /* toolkit.m */, + ); + path = cocoa; + sourceTree = "<group>"; + }; + ED6581472CFF3C8300F5402F /* public */ = { + isa = PBXGroup; + children = ( + ED6581482CFF3CA000F5402F /* button.h */, + ED6581492CFF3CA000F5402F /* container.h */, + ED65814A2CFF3CA000F5402F /* display.h */, + ED65814B2CFF3CA000F5402F /* dnd.h */, + ED65814C2CFF3CA000F5402F /* entry.h */, + ED65814D2CFF3CA000F5402F /* graphics.h */, + ED65814E2CFF3CA000F5402F /* icons.h */, + ED65814F2CFF3CA000F5402F /* image.h */, + ED6581502CFF3CA000F5402F /* menu.h */, + ED6581512CFF3CA000F5402F /* properties.h */, + ED6581522CFF3CA000F5402F /* range.h */, + ED6581532CFF3CA000F5402F /* stock.h */, + ED6581542CFF3CA000F5402F /* text.h */, + ED6581552CFF3CA000F5402F /* toolbar.h */, + ED6581562CFF3CA000F5402F /* toolkit.h */, + ED6581572CFF3CA000F5402F /* tree.h */, + ED6581582CFF3CA000F5402F /* ui.h */, + ED6581592CFF3CA000F5402F /* window.h */, + ); + path = public; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + ED6580AB2CFF122700F5402F /* toolkit */ = { + isa = PBXNativeTarget; + buildConfigurationList = ED6580BC2CFF122800F5402F /* Build configuration list for PBXNativeTarget "toolkit" */; + buildPhases = ( + ED6580A82CFF122700F5402F /* Sources */, + ED6580A92CFF122700F5402F /* Frameworks */, + ED6580AA2CFF122700F5402F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + ED6580AE2CFF122700F5402F /* toolkit */, + ); + name = toolkit; + packageProductDependencies = ( + ); + productName = toolkit; + productReference = ED6580AC2CFF122700F5402F /* toolkit.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + ED6580A42CFF122700F5402F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1610; + TargetAttributes = { + ED6580AB2CFF122700F5402F = { + CreatedOnToolsVersion = 16.1; + }; + }; + }; + buildConfigurationList = ED6580A72CFF122700F5402F /* Build configuration list for PBXProject "toolkit" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = ED6580A32CFF122700F5402F; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = ED6580AD2CFF122700F5402F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ED6580AB2CFF122700F5402F /* toolkit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + ED6580AA2CFF122700F5402F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ED65812D2CFF1A3000F5402F /* common.h.orig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + ED6580A82CFF122700F5402F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ED6580EE2CFF19F900F5402F /* context.c in Sources */, + ED6580EF2CFF19F900F5402F /* menu.c in Sources */, + ED6580F02CFF19F900F5402F /* threadpool.c in Sources */, + ED6580F12CFF19F900F5402F /* condvar.c in Sources */, + ED65815F2CFF4BF200F5402F /* WindowManager.m in Sources */, + ED6580F22CFF19F900F5402F /* document.c in Sources */, + ED65811D2CFF1A3000F5402F /* linked_list.c in Sources */, + ED65811E2CFF1A3000F5402F /* tree.c in Sources */, + ED6581202CFF1A3000F5402F /* mempool.c in Sources */, + ED6581212CFF1A3000F5402F /* map.c in Sources */, + ED6581222CFF1A3000F5402F /* hash_map.c in Sources */, + ED6581232CFF1A3000F5402F /* array_list.c in Sources */, + ED6581242CFF1A3000F5402F /* list.c in Sources */, + ED65815C2CFF3EE900F5402F /* MainWindow.m in Sources */, + ED6581252CFF1A3000F5402F /* compare.c in Sources */, + ED6581262CFF1A3000F5402F /* hash_key.c in Sources */, + ED6581272CFF1A3000F5402F /* iterator.c in Sources */, + ED6581282CFF1A3000F5402F /* string.c in Sources */, + ED6581312CFF1A8800F5402F /* toolkit.m in Sources */, + ED6581342CFF1F1900F5402F /* AppDelegate.m in Sources */, + ED6581292CFF1A3000F5402F /* allocator.c in Sources */, + ED6581432CFF3BCE00F5402F /* window.m in Sources */, + ED6581442CFF3BCE00F5402F /* button.m in Sources */, + ED6581452CFF3BCE00F5402F /* Container.m in Sources */, + ED6581462CFF3BCE00F5402F /* GridLayout.m in Sources */, + ED65812A2CFF1A3000F5402F /* utils.c in Sources */, + ED6581392CFF287300F5402F /* EventData.m in Sources */, + ED65813A2CFF287300F5402F /* UiJob.m in Sources */, + ED65812B2CFF1A3000F5402F /* buffer.c in Sources */, + ED65812C2CFF1A3000F5402F /* printf.c in Sources */, + ED6580F32CFF19F900F5402F /* object.c in Sources */, + ED6580F42CFF19F900F5402F /* toolbar.c in Sources */, + ED6580F52CFF19F900F5402F /* types.c in Sources */, + ED6580F62CFF19F900F5402F /* properties.c in Sources */, + ED6580F72CFF19F900F5402F /* ucx_properties.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + ED6580BA2CFF122800F5402F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ../../../ucx; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + ED6580BB2CFF122800F5402F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ../../../ucx; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 15.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + ED6580BD2CFF122800F5402F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = toolkit/toolkit.entitlements; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + ../../../ucx, + ../../../ui, + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainNibFile = MainMenu; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_CFLAGS = "-DUI_COCOA"; + PRODUCT_BUNDLE_IDENTIFIER = de.unixwork.toolkit; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Debug; + }; + ED6580BE2CFF122800F5402F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = toolkit/toolkit.entitlements; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + HEADER_SEARCH_PATHS = ( + ../../../ucx, + ../../../ui, + ); + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_NSMainNibFile = MainMenu; + INFOPLIST_KEY_NSPrincipalClass = NSApplication; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_CFLAGS = "-DUI_COCOA"; + PRODUCT_BUNDLE_IDENTIFIER = de.unixwork.toolkit; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_EMIT_LOC_STRINGS = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + ED6580A72CFF122700F5402F /* Build configuration list for PBXProject "toolkit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ED6580BA2CFF122800F5402F /* Debug */, + ED6580BB2CFF122800F5402F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + ED6580BC2CFF122800F5402F /* Build configuration list for PBXNativeTarget "toolkit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + ED6580BD2CFF122800F5402F /* Debug */, + ED6580BE2CFF122800F5402F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = ED6580A42CFF122700F5402F /* Project object */; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit.xcodeproj/project.xcworkspace/contents.xcworkspacedata Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:"> + </FileRef> +</Workspace>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit.xcodeproj/xcshareddata/xcschemes/toolkit.xcscheme Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "1610" + version = "1.7"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ED6580AB2CFF122700F5402F" + BuildableName = "toolkit.app" + BlueprintName = "toolkit" + ReferencedContainer = "container:toolkit.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ED6580AB2CFF122700F5402F" + BuildableName = "toolkit.app" + BlueprintName = "toolkit" + ReferencedContainer = "container:toolkit.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "ED6580AB2CFF122700F5402F" + BuildableName = "toolkit.app" + BlueprintName = "toolkit" + ReferencedContainer = "container:toolkit.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/Assets.xcassets/AccentColor.colorset/Contents.json Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/Assets.xcassets/AppIcon.appiconset/Contents.json Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/Assets.xcassets/Contents.json Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/Base.lproj/MainMenu.xib Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct"> + <dependencies> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/> + </dependencies> + <objects> + <customObject id="-2" userLabel="File's Owner" customClass="NSApplication"> + <connections> + <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/> + </connections> + </customObject> + <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> + <customObject id="-3" userLabel="Application" customClass="NSObject"/> + <customObject id="Voe-Tx-rLC" customClass="AppDelegate"/> + <customObject id="YLy-65-1bz" customClass="NSFontManager"/> + <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6"> + <items> + <menuItem title="toolkit" id="1Xt-HY-uBw"> + <modifierMask key="keyEquivalentModifierMask"/> + <menu key="submenu" title="toolkit" systemMenu="apple" id="uQy-DD-JDr"> + <items> + <menuItem title="About toolkit" id="5kV-Vb-QxS"> + <modifierMask key="keyEquivalentModifierMask"/> + <connections> + <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/> + </connections> + </menuItem> + <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/> + <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/> + <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/> + <menuItem title="Services" id="NMo-om-nkz"> + <modifierMask key="keyEquivalentModifierMask"/> + <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/> + </menuItem> + <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/> + <menuItem title="Hide toolkit" keyEquivalent="h" id="Olw-nP-bQN"> + <connections> + <action selector="hide:" target="-1" id="PnN-Uc-m68"/> + </connections> + </menuItem> + <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO"> + <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/> + <connections> + <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/> + </connections> + </menuItem> + <menuItem title="Show All" id="Kd2-mp-pUS"> + <modifierMask key="keyEquivalentModifierMask"/> + <connections> + <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/> + </connections> + </menuItem> + <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/> + <menuItem title="Quit toolkit" keyEquivalent="q" id="4sb-4s-VLi"> + <connections> + <action selector="terminate:" target="-1" id="Te7-pn-YzF"/> + </connections> + </menuItem> + </items> + </menu> + </menuItem> + <menuItem title="File" id="dMs-cI-mzQ"> + <modifierMask key="keyEquivalentModifierMask"/> + </menuItem> + <menuItem title="Edit" id="5QF-Oa-p0T"> + <modifierMask key="keyEquivalentModifierMask"/> + </menuItem> + <menuItem title="Format" id="jxT-CU-nIS"> + <modifierMask key="keyEquivalentModifierMask"/> + </menuItem> + <menuItem title="View" id="H8h-7b-M4v"> + <modifierMask key="keyEquivalentModifierMask"/> + </menuItem> + <menuItem title="Window" id="aUF-d1-5bR"> + <modifierMask key="keyEquivalentModifierMask"/> + </menuItem> + <menuItem title="Help" id="wpr-3q-Mcd"> + <modifierMask key="keyEquivalentModifierMask"/> + <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ"> + <items> + <menuItem title="toolkit Help" keyEquivalent="?" id="FKE-Sm-Kum"> + <connections> + <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/> + </connections> + </menuItem> + </items> + </menu> + </menuItem> + </items> + <point key="canvasLocation" x="200" y="121"/> + </menu> + </objects> +</document>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/main.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +#include <ui/ui.h> + +static void action_button(UiEvent *event, void *userdata) { + printf("button click\n"); +} + +void application_startup(UiEvent *event, void *data) { + UiObject *obj = ui_window("My Window", NULL); + + + ui_button(obj, .label = "Button X1 -------------------------------------------------- END", .onclick = action_button); + ui_newline(obj); + ui_button(obj, .label = "Button X2"); + ui_button(obj, .label = "Button X3", .hexpand = TRUE); + ui_button(obj, .label = "Button X4"); + ui_newline(obj); + ui_button(obj, .label = "Button X5"); + + + ui_show(obj); +} + +int main(int argc, char * argv[]) { + ui_init("app1", argc, argv); + ui_onstartup(application_startup, NULL); + ui_main(); + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/xcode/toolkit/toolkit/toolkit.entitlements Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.security.app-sandbox</key> + <false/> + <key>com.apple.security.files.user-selected.read-only</key> + <true/> +</dict> +</plist>
--- a/resource/template.app/Contents/Info.plist Sun May 23 09:44:43 2021 +0200 +++ b/resource/template.app/Contents/Info.plist Sat Jan 04 16:38:48 2025 +0100 @@ -3,13 +3,13 @@ <plist version="1.0"> <dict> <key>BuildMachineOSBuild</key> - <string>10K549</string> + <string>24B91</string> <key>CFBundleDevelopmentRegion</key> - <string>de_DE</string> + <string>en</string> <key>CFBundleExecutable</key> <string>mk12</string> <key>CFBundleIdentifier</key> - <string>com.yourcompany.toolkit</string> + <string>de.unixwork.toolkit</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> @@ -18,53 +18,32 @@ <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> - <key>CFBundleSignature</key> - <string>????</string> + <key>CFBundleSupportedPlatforms</key> + <array> + <string>MacOSX</string> + </array> <key>CFBundleVersion</key> <string>1</string> <key>DTCompiler</key> - <string></string> + <string>com.apple.compilers.llvm.clang.1_0</string> <key>DTPlatformBuild</key> - <string>10M2518</string> + <string>24B75</string> + <key>DTPlatformName</key> + <string>macosx</string> <key>DTPlatformVersion</key> - <string>PG</string> + <string>15.1</string> <key>DTSDKBuild</key> - <string>10M2518</string> + <string>24B75</string> <key>DTSDKName</key> - <string>macosx10.6</string> + <string>macosx15.1</string> <key>DTXcode</key> - <string>0400</string> + <string>1610</string> <key>DTXcodeBuild</key> - <string>10M2518</string> + <string>16B40</string> <key>LSMinimumSystemVersion</key> - <string>10.7</string> + <string>15.1</string> <key>NSMainNibFile</key> <string>MainMenu</string> - <key>CFBundleDisplayName</key> - <string></string> - <key>CFBundleGetInfoString</key> - <string></string> - <key>LSApplicationCategoryType</key> - <string></string> - <key>CFBundleDocumentTypes</key> - <array> - <dict> - <key>LSItemContentTypes</key> - <array> - <string>public.data</string> - </array> - <key>CFBundleTypeIconFile</key> - <string></string> - <key>CFBundleTypeName</key> - <string>DocumentType</string> - <key>CFBundleTypeRole</key> - <string>Editor</string> -<!-- - <key>NSDocumentClass</key> - <string>Document</string> ---> - </dict> - </array> <key>NSPrincipalClass</key> <string>NSApplication</string> </dict>
--- a/ucx/Makefile Sun May 23 09:44:43 2021 +0200 +++ b/ucx/Makefile Sat Jan 04 16:38:48 2025 +0100 @@ -30,24 +30,25 @@ include ../config.mk # list of source files -SRC = utils.c +SRC = allocator.c +SRC += array_list.c +SRC += mempool.c +SRC += buffer.c +SRC += compare.c +SRC += hash_key.c +SRC += hash_map.c +SRC += linked_list.c SRC += list.c SRC += map.c -SRC += avl.c -SRC += properties.c -SRC += mempool.c +SRC += printf.c SRC += string.c -SRC += test.c -SRC += allocator.c -SRC += logging.c -SRC += buffer.c -SRC += stack.c -SRC += ucx.c -SRC += array.c +SRC += utils.c +SRC += tree.c +SRC += iterator.c -OBJ = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT)) +OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT)) -UCX_LIB = ../build/lib/libucx.$(LIB_EXT) +UCX_LIB = ../build/lib/libucx$(LIB_EXT) all: ../build/ucx $(UCX_LIB) @@ -57,6 +58,6 @@ ../build/ucx: mkdir -p ../build/ucx -../build/ucx/%.$(OBJ_EXT): %.c +../build/ucx/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) -o $@ -c $<
--- a/ucx/README Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -UCX is a library for common data structures, algorithms and string functions. - -More informations at: https://develop.uap-core.de/ucx/ -
--- a/ucx/allocator.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/allocator.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,35 +26,111 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/allocator.h" - -#include <stdlib.h> +#include "cx/allocator.h" -static UcxAllocator default_allocator = { - NULL, - ucx_default_malloc, - ucx_default_calloc, - ucx_default_realloc, - ucx_default_free -}; - -UcxAllocator *ucx_default_allocator() { - UcxAllocator *allocator = &default_allocator; - return allocator; -} - -void *ucx_default_malloc(void *ignore, size_t n) { +__attribute__((__malloc__, __alloc_size__(2))) +static void *cx_malloc_stdlib( + __attribute__((__unused__)) void *d, + size_t n +) { return malloc(n); } -void *ucx_default_calloc(void *ignore, size_t n, size_t size) { - return calloc(n, size); +__attribute__((__warn_unused_result__, __alloc_size__(3))) +static void *cx_realloc_stdlib( + __attribute__((__unused__)) void *d, + void *mem, + size_t n +) { + return realloc(mem, n); +} + +__attribute__((__malloc__, __alloc_size__(2, 3))) +static void *cx_calloc_stdlib( + __attribute__((__unused__)) void *d, + size_t nelem, + size_t n +) { + return calloc(nelem, n); +} + +__attribute__((__nonnull__)) +static void cx_free_stdlib( + __attribute__((__unused__)) void *d, + void *mem +) { + free(mem); } -void *ucx_default_realloc(void *ignore, void *data, size_t n) { - return realloc(data, n); +static cx_allocator_class cx_default_allocator_class = { + cx_malloc_stdlib, + cx_realloc_stdlib, + cx_calloc_stdlib, + cx_free_stdlib +}; + +struct cx_allocator_s cx_default_allocator = { + &cx_default_allocator_class, + NULL +}; +CxAllocator *cxDefaultAllocator = &cx_default_allocator; + + +int cx_reallocate( + void **mem, + size_t n +) { + void *nmem = realloc(*mem, n); + if (nmem == NULL) { + return 1; + } else { + *mem = nmem; + return 0; + } +} + +// IMPLEMENTATION OF HIGH LEVEL API + +void *cxMalloc( + const CxAllocator *allocator, + size_t n +) { + return allocator->cl->malloc(allocator->data, n); } -void ucx_default_free(void *ignore, void *data) { - free(data); +void *cxRealloc( + const CxAllocator *allocator, + void *mem, + size_t n +) { + return allocator->cl->realloc(allocator->data, mem, n); } + +int cxReallocate( + const CxAllocator *allocator, + void **mem, + size_t n +) { + void *nmem = allocator->cl->realloc(allocator->data, *mem, n); + if (nmem == NULL) { + return 1; + } else { + *mem = nmem; + return 0; + } +} + +void *cxCalloc( + const CxAllocator *allocator, + size_t nelem, + size_t n +) { + return allocator->cl->calloc(allocator->data, nelem, n); +} + +void cxFree( + const CxAllocator *allocator, + void *mem +) { + allocator->cl->free(allocator->data, mem); +}
--- a/ucx/array.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,467 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#define _GNU_SOURCE /* we want to use qsort_r(), if available */ -#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */ - - -#include "ucx/array.h" -#include "ucx/utils.h" - -#include <string.h> -#include <stdlib.h> -#include <errno.h> - -#ifndef UCX_ARRAY_DISABLE_QSORT -#ifdef __GLIBC__ -#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) -#define ucx_array_sort_impl qsort_r -#endif /* glibc version >= 2.8 */ -#elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__) -#define ucx_array_sort_impl ucx_qsort_r -#define USE_UCX_QSORT_R -#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun) -#if __STDC_VERSION__ >= 201112L -#define ucx_array_sort_impl qsort_s -#endif -#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */ -#endif /* UCX_ARRAY_DISABLE_QSORT */ - -#ifndef ucx_array_sort_impl -#define ucx_array_sort_impl ucx_mergesort -#endif - -static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) { - size_t required_capacity = array->capacity; - while (reqcap > required_capacity) { - if (required_capacity * 2 < required_capacity) - return 1; - required_capacity <<= 1; - } - if (ucx_array_reserve(array, required_capacity)) { - return 1; - } - return 0; -} - -int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t elmsize, size_t index, void* data) { - - if(!alloc || !capacity || !array) { - errno = EINVAL; - return 1; - } - - size_t newcapacity = *capacity; - while(index >= newcapacity) { - if(ucx_szmul(newcapacity, 2, &newcapacity)) { - errno = EOVERFLOW; - return 1; - } - } - - size_t memlen, offset; - if(ucx_szmul(newcapacity, elmsize, &memlen)) { - errno = EOVERFLOW; - return 1; - } - /* we don't need to check index*elmsize - it is smaller than memlen */ - - - void* newptr = alrealloc(alloc, *array, memlen); - if(newptr == NULL) { - errno = ENOMEM; /* we cannot assume that every allocator sets this */ - return 1; - } - *array = newptr; - *capacity = newcapacity; - - - char* dest = *array; - dest += elmsize*index; - memcpy(dest, data, elmsize); - - return 0; -} - -int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t index, void* data) { - - return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*), - index, &data); -} - -UcxArray* ucx_array_new(size_t capacity, size_t elemsize) { - return ucx_array_new_a(capacity, elemsize, ucx_default_allocator()); -} - -UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - UcxArray* array = almalloc(allocator, sizeof(UcxArray)); - if(array) { - ucx_array_init_a(array, capacity, elemsize, allocator); - } - return array; -} - -void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) { - ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator()); -} - -void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - - array->allocator = allocator; - array->elemsize = elemsize; - array->size = 0; - array->data = alcalloc(allocator, capacity, elemsize); - - if (array->data) { - array->capacity = capacity; - } else { - array->capacity = 0; - } -} - -int ucx_array_clone(UcxArray* dest, UcxArray const* src) { - if (ucx_array_ensurecap(dest, src->capacity)) { - return 1; - } - - dest->elemsize = src->elemsize; - dest->size = src->size; - - if (dest->data) { - memcpy(dest->data, src->data, src->size*src->elemsize); - } - - return 0; -} - -int ucx_array_equals(UcxArray const *array1, UcxArray const *array2, - cmp_func cmpfnc, void* data) { - - if (array1->size != array2->size || array1->elemsize != array2->elemsize) { - return 0; - } else { - if (array1->size == 0) - return 1; - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array1->elemsize; - data = &elemsize; - } - - for (size_t i = 0 ; i < array1->size ; i++) { - int r = cmpfnc( - ucx_array_at(array1, i), - ucx_array_at(array2, i), - data); - if (r != 0) - return 0; - } - return 1; - } -} - -void ucx_array_destroy(UcxArray *array) { - if(array->data) - alfree(array->allocator, array->data); - array->data = NULL; - array->capacity = array->size = 0; -} - -void ucx_array_free(UcxArray *array) { - ucx_array_destroy(array); - alfree(array->allocator, array); -} - -int ucx_array_append_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - void* dest = ucx_array_at(array, array->size); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - if (array->size > 0) { - void *dest = ucx_array_at(array, count); - memmove(dest, array->data, array->elemsize*array->size); - } - - if (data) { - memcpy(array->data, data, array->elemsize*count); - } else { - memset(array->data, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_set_from(UcxArray *array, size_t index, - void *data, size_t count) { - if (ucx_array_ensurecap(array, index + count)) - return 1; - - if (index+count > array->size) { - array->size = index+count; - } - - void *dest = ucx_array_at(array, index); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - - return 0; -} - -int ucx_array_concat(UcxArray *array1, const UcxArray *array2) { - - if (array1->elemsize != array2->elemsize) - return 1; - - size_t capacity = array1->capacity+array2->capacity; - - if (array1->capacity < capacity) { - if (ucx_array_reserve(array1, capacity)) { - return 1; - } - } - - void* dest = ucx_array_at(array1, array1->size); - memcpy(dest, array2->data, array2->size*array2->elemsize); - - array1->size += array2->size; - - return 0; -} - -void *ucx_array_at(UcxArray const *array, size_t index) { - char* memory = array->data; - char* loc = memory + index*array->elemsize; - return loc; -} - -size_t ucx_array_find(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array->elemsize; - data = &elemsize; - } - - if (array->size > 0) { - for (size_t i = 0 ; i < array->size ; i++) { - void* ptr = ucx_array_at(array, i); - if (cmpfnc(ptr, elem, data) == 0) { - return i; - } - } - return array->size; - } else { - return 0; - } -} - -int ucx_array_contains(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - return ucx_array_find(array, elem, cmpfnc, data) != array->size; -} - -static void ucx_mergesort_merge(void *arrdata,size_t elemsize, - cmp_func cmpfnc, void *data, - size_t start, size_t mid, size_t end) { - - char* array = arrdata; - - size_t rightstart = mid + 1; - - if (cmpfnc(array + mid*elemsize, - array + rightstart*elemsize, data) <= 0) { - /* already sorted */ - return; - } - - /* we need memory for one element */ - void *value = malloc(elemsize); - - while (start <= mid && rightstart <= end) { - if (cmpfnc(array + start*elemsize, - array + rightstart*elemsize, data) <= 0) { - start++; - } else { - /* save the value from the right */ - memcpy(value, array + rightstart*elemsize, elemsize); - - /* shift all left elements one element to the right */ - size_t shiftcount = rightstart-start; - void *startptr = array + start*elemsize; - void *dest = array + (start+1)*elemsize; - memmove(dest, startptr, shiftcount*elemsize); - - /* bring the first value from the right to the left */ - memcpy(startptr, value, elemsize); - - start++; - mid++; - rightstart++; - } - } - - /* free the temporary memory */ - free(value); -} - -static void ucx_mergesort_impl(void *arrdata, size_t elemsize, - cmp_func cmpfnc, void *data, size_t l, size_t r) { - if (l < r) { - size_t m = l + (r - l) / 2; - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); - ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r); - } -} - -static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1); -} - -#ifdef USE_UCX_QSORT_R -struct cmpfnc_swapargs_info { - cmp_func func; - void *data; -}; - -static int cmp_func_swap_args(void *data, const void *x, const void *y) { - struct cmpfnc_swapargs_info* info = data; - return info->func(x, y, info->data); -} - -static void ucx_qsort_r(void *array, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - struct cmpfnc_swapargs_info info; - info.func = cmpfnc; - info.data = data; - qsort_r(array, count, elemsize, &info, cmp_func_swap_args); -} -#endif /* USE_UCX_QSORT_R */ - -void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) { - ucx_array_sort_impl(array->data, array->size, array->elemsize, - cmpfnc, data); -} - -void ucx_array_remove(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, index+1); - memmove(dest, src, (array->size - index)*array->elemsize); - } -} - -void ucx_array_remove_fast(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, array->size); - memcpy(dest, src, array->elemsize); - } -} - -int ucx_array_shrink(UcxArray* array) { - void* newptr = alrealloc(array->allocator, array->data, - array->size*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = array->size; - return 0; - } else { - return 1; - } -} - -int ucx_array_resize(UcxArray* array, size_t capacity) { - if (array->capacity >= capacity) { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - if (array->size > array->capacity) { - array->size = array->capacity; - } - return 0; - } else { - return 1; - } - } else { - return ucx_array_reserve(array, capacity); - } -} - -int ucx_array_reserve(UcxArray* array, size_t capacity) { - if (array->capacity > capacity) { - return 0; - } else { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - return 0; - } else { - return 1; - } - } -} - -int ucx_array_grow(UcxArray* array, size_t count) { - return ucx_array_reserve(array, array->size+count); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/array_list.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,767 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/array_list.h" +#include "cx/compare.h" +#include <assert.h> +#include <string.h> + +// Default array reallocator + +static void *cx_array_default_realloc( + void *array, + size_t capacity, + size_t elem_size, + __attribute__((__unused__)) struct cx_array_reallocator_s *alloc +) { + return realloc(array, capacity * elem_size); +} + +struct cx_array_reallocator_s cx_array_default_reallocator_impl = { + cx_array_default_realloc, NULL, NULL, 0, 0 +}; + +struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl; + +// LOW LEVEL ARRAY LIST FUNCTIONS + +enum cx_array_result cx_array_copy( + void **target, + size_t *size, + size_t *capacity, + size_t index, + const void *src, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +) { + // assert pointers + assert(target != NULL); + assert(size != NULL); + assert(src != NULL); + + // determine capacity + size_t cap = capacity == NULL ? *size : *capacity; + + // check if resize is required + size_t minsize = index + elem_count; + size_t newsize = *size < minsize ? minsize : *size; + bool needrealloc = newsize > cap; + + // reallocate if possible + if (needrealloc) { + // a reallocator and a capacity variable must be available + if (reallocator == NULL || capacity == NULL) { + return CX_ARRAY_REALLOC_NOT_SUPPORTED; + } + + // check, if we need to repair the src pointer + uintptr_t targetaddr = (uintptr_t) *target; + uintptr_t srcaddr = (uintptr_t) src; + bool repairsrc = targetaddr <= srcaddr + && srcaddr < targetaddr + cap * elem_size; + + // calculate new capacity (next number divisible by 16) + cap = newsize - (newsize % 16) + 16; + assert(cap > newsize); + + // perform reallocation + void *newmem = reallocator->realloc( + *target, cap, elem_size, reallocator + ); + if (newmem == NULL) { + return CX_ARRAY_REALLOC_FAILED; + } + + // repair src pointer, if necessary + if (repairsrc) { + src = ((char *) newmem) + (srcaddr - targetaddr); + } + + // store new pointer and capacity + *target = newmem; + *capacity = cap; + } + + // determine target pointer + char *start = *target; + start += index * elem_size; + + // copy elements and set new size + memmove(start, src, elem_count * elem_size); + *size = newsize; + + // return successfully + return CX_ARRAY_SUCCESS; +} + +enum cx_array_result cx_array_insert_sorted( + void **target, + size_t *size, + size_t *capacity, + cx_compare_func cmp_func, + const void *sorted_data, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +) { + // assert pointers + assert(target != NULL); + assert(size != NULL); + assert(capacity != NULL); + assert(cmp_func != NULL); + assert(sorted_data != NULL); + assert(reallocator != NULL); + + // corner case + if (elem_count == 0) return 0; + + // store some counts + size_t old_size = *size; + size_t needed_capacity = old_size + elem_count; + + // if we need more than we have, try a reallocation + if (needed_capacity > *capacity) { + size_t new_capacity = needed_capacity - (needed_capacity % 16) + 16; + void *new_mem = reallocator->realloc( + *target, new_capacity, elem_size, reallocator + ); + if (new_mem == NULL) { + // give it up right away, there is no contract + // that requires us to insert as much as we can + return CX_ARRAY_REALLOC_FAILED; + } + *target = new_mem; + *capacity = new_capacity; + } + + // now we have guaranteed that we can insert everything + size_t new_size = old_size + elem_count; + *size = new_size; + + // declare the source and destination indices/pointers + size_t si = 0, di = 0; + const char *src = sorted_data; + char *dest = *target; + + // find the first insertion point + di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func); + dest += di * elem_size; + + // move the remaining elements in the array completely to the right + // we will call it the "buffer" for parked elements + size_t buf_size = old_size - di; + size_t bi = new_size - buf_size; + char *bptr = ((char *) *target) + bi * elem_size; + memmove(bptr, dest, buf_size * elem_size); + + // while there are both source and buffered elements left, + // copy them interleaving + while (si < elem_count && bi < new_size) { + // determine how many source elements can be inserted + size_t copy_len, bytes_copied; + copy_len = cx_array_binary_search_sup( + src, + elem_count - si, + elem_size, + bptr, + cmp_func + ); + + // copy the source elements + bytes_copied = copy_len * elem_size; + memcpy(dest, src, bytes_copied); + dest += bytes_copied; + src += bytes_copied; + si += copy_len; + + // when all source elements are in place, we are done + if (si >= elem_count) break; + + // determine how many buffered elements need to be restored + copy_len = cx_array_binary_search_sup( + bptr, + new_size - bi, + elem_size, + src, + cmp_func + ); + + // restore the buffered elements + bytes_copied = copy_len * elem_size; + memmove(dest, bptr, bytes_copied); + dest += bytes_copied; + bptr += bytes_copied; + bi += copy_len; + } + + // still source elements left? simply append them + if (si < elem_count) { + memcpy(dest, src, elem_size * (elem_count - si)); + } + + // still buffer elements left? + // don't worry, we already moved them to the correct place + + return CX_ARRAY_SUCCESS; +} + +size_t cx_array_binary_search_inf( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func cmp_func +) { + // special case: empty array + if (size == 0) return 0; + + // declare a variable that will contain the compare results + int result; + + // cast the array pointer to something we can use offsets with + const char *array = arr; + + // check the first array element + result = cmp_func(elem, array); + if (result < 0) { + return size; + } else if (result == 0) { + return 0; + } + + // check the last array element + result = cmp_func(elem, array + elem_size * (size - 1)); + if (result >= 0) { + return size - 1; + } + + // the element is now guaranteed to be somewhere in the list + // so start the binary search + size_t left_index = 1; + size_t right_index = size - 1; + size_t pivot_index; + + while (left_index <= right_index) { + pivot_index = left_index + (right_index - left_index) / 2; + const char *arr_elem = array + pivot_index * elem_size; + result = cmp_func(elem, arr_elem); + if (result == 0) { + // found it! + return pivot_index; + } else if (result < 0) { + // element is smaller than pivot, continue search left + right_index = pivot_index - 1; + } else { + // element is larger than pivot, continue search right + left_index = pivot_index + 1; + } + } + + // report the largest upper bound + return result < 0 ? (pivot_index - 1) : pivot_index; +} + +#ifndef CX_ARRAY_SWAP_SBO_SIZE +#define CX_ARRAY_SWAP_SBO_SIZE 128 +#endif +unsigned cx_array_swap_sbo_size = CX_ARRAY_SWAP_SBO_SIZE; + +void cx_array_swap( + void *arr, + size_t elem_size, + size_t idx1, + size_t idx2 +) { + assert(arr != NULL); + + // short circuit + if (idx1 == idx2) return; + + char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE]; + void *tmp; + + // decide if we can use the local buffer + if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) { + tmp = malloc(elem_size); + // we don't want to enforce error handling + if (tmp == NULL) abort(); + } else { + tmp = sbo_mem; + } + + // calculate memory locations + char *left = arr, *right = arr; + left += idx1 * elem_size; + right += idx2 * elem_size; + + // three-way swap + memcpy(tmp, left, elem_size); + memcpy(left, right, elem_size); + memcpy(right, tmp, elem_size); + + // free dynamic memory, if it was needed + if (tmp != sbo_mem) { + free(tmp); + } +} + +// HIGH LEVEL ARRAY LIST FUNCTIONS + +typedef struct { + struct cx_list_s base; + void *data; + size_t capacity; + struct cx_array_reallocator_s reallocator; +} cx_array_list; + +static void *cx_arl_realloc( + void *array, + size_t capacity, + size_t elem_size, + struct cx_array_reallocator_s *alloc +) { + // retrieve the pointer to the list allocator + const CxAllocator *al = alloc->ptr1; + + // use the list allocator to reallocate the memory + return cxRealloc(al, array, capacity * elem_size); +} + +static void cx_arl_destructor(struct cx_list_s *list) { + cx_array_list *arl = (cx_array_list *) list; + + char *ptr = arl->data; + + if (list->collection.simple_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { + cx_invoke_simple_destructor(list, ptr); + ptr += list->collection.elem_size; + } + } + if (list->collection.advanced_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { + cx_invoke_advanced_destructor(list, ptr); + ptr += list->collection.elem_size; + } + } + + cxFree(list->collection.allocator, arl->data); + cxFree(list->collection.allocator, list); +} + +static size_t cx_arl_insert_array( + struct cx_list_s *list, + size_t index, + const void *array, + size_t n +) { + // out of bounds and special case check + if (index > list->collection.size || n == 0) return 0; + + // get a correctly typed pointer to the list + cx_array_list *arl = (cx_array_list *) list; + + // do we need to move some elements? + if (index < list->collection.size) { + const char *first_to_move = (const char *) arl->data; + first_to_move += index * list->collection.elem_size; + size_t elems_to_move = list->collection.size - index; + size_t start_of_moved = index + n; + + if (CX_ARRAY_SUCCESS != cx_array_copy( + &arl->data, + &list->collection.size, + &arl->capacity, + start_of_moved, + first_to_move, + list->collection.elem_size, + elems_to_move, + &arl->reallocator + )) { + // if moving existing elems is unsuccessful, abort + return 0; + } + } + + // note that if we had to move the elements, the following operation + // is guaranteed to succeed, because we have the memory already allocated + // therefore, it is impossible to leave this function with an invalid array + + // place the new elements + if (CX_ARRAY_SUCCESS == cx_array_copy( + &arl->data, + &list->collection.size, + &arl->capacity, + index, + array, + list->collection.elem_size, + n, + &arl->reallocator + )) { + return n; + } else { + // array list implementation is "all or nothing" + return 0; + } +} + +static size_t cx_arl_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + // get a correctly typed pointer to the list + cx_array_list *arl = (cx_array_list *) list; + + if (CX_ARRAY_SUCCESS == cx_array_insert_sorted( + &arl->data, + &list->collection.size, + &arl->capacity, + list->collection.cmpfunc, + sorted_data, + list->collection.elem_size, + n, + &arl->reallocator + )) { + return n; + } else { + // array list implementation is "all or nothing" + return 0; + } +} + +static int cx_arl_insert_element( + struct cx_list_s *list, + size_t index, + const void *element +) { + return 1 != cx_arl_insert_array(list, index, element, 1); +} + +static int cx_arl_insert_iter( + struct cx_iterator_s *iter, + const void *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle.m; + if (iter->index < list->collection.size) { + int result = cx_arl_insert_element( + list, + iter->index + 1 - prepend, + elem + ); + if (result == 0) { + iter->elem_count++; + if (prepend != 0) { + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size; + } + } + return result; + } else { + int result = cx_arl_insert_element(list, list->collection.size, elem); + if (result == 0) { + iter->elem_count++; + iter->index = list->collection.size; + } + return result; + } +} + +static int cx_arl_remove( + struct cx_list_s *list, + size_t index +) { + cx_array_list *arl = (cx_array_list *) list; + + // out-of-bounds check + if (index >= list->collection.size) { + return 1; + } + + // content destruction + cx_invoke_destructor(list, ((char *) arl->data) + index * list->collection.elem_size); + + // short-circuit removal of last element + if (index == list->collection.size - 1) { + list->collection.size--; + return 0; + } + + // just move the elements starting at index to the left + int result = cx_array_copy( + &arl->data, + &list->collection.size, + &arl->capacity, + index, + ((char *) arl->data) + (index + 1) * list->collection.elem_size, + list->collection.elem_size, + list->collection.size - index - 1, + &arl->reallocator + ); + + // cx_array_copy cannot fail, array cannot grow + assert(result == 0); + + // decrease the size + list->collection.size--; + + return 0; +} + +static void cx_arl_clear(struct cx_list_s *list) { + if (list->collection.size == 0) return; + + cx_array_list *arl = (cx_array_list *) list; + char *ptr = arl->data; + + if (list->collection.simple_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { + cx_invoke_simple_destructor(list, ptr); + ptr += list->collection.elem_size; + } + } + if (list->collection.advanced_destructor) { + for (size_t i = 0; i < list->collection.size; i++) { + cx_invoke_advanced_destructor(list, ptr); + ptr += list->collection.elem_size; + } + } + + memset(arl->data, 0, list->collection.size * list->collection.elem_size); + list->collection.size = 0; +} + +static int cx_arl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + if (i >= list->collection.size || j >= list->collection.size) return 1; + cx_array_list *arl = (cx_array_list *) list; + cx_array_swap(arl->data, list->collection.elem_size, i, j); + return 0; +} + +static void *cx_arl_at( + const struct cx_list_s *list, + size_t index +) { + if (index < list->collection.size) { + const cx_array_list *arl = (const cx_array_list *) list; + char *space = arl->data; + return space + index * list->collection.elem_size; + } else { + return NULL; + } +} + +static ssize_t cx_arl_find_remove( + struct cx_list_s *list, + const void *elem, + bool remove +) { + assert(list->collection.cmpfunc != NULL); + assert(list->collection.size < SIZE_MAX / 2); + char *cur = ((const cx_array_list *) list)->data; + + for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) { + if (0 == list->collection.cmpfunc(elem, cur)) { + if (remove) { + if (0 == cx_arl_remove(list, i)) { + return i; + } else { + return -1; + } + } else { + return i; + } + } + cur += list->collection.elem_size; + } + + return -1; +} + +static void cx_arl_sort(struct cx_list_s *list) { + assert(list->collection.cmpfunc != NULL); + qsort(((cx_array_list *) list)->data, + list->collection.size, + list->collection.elem_size, + list->collection.cmpfunc + ); +} + +static int cx_arl_compare( + const struct cx_list_s *list, + const struct cx_list_s *other +) { + assert(list->collection.cmpfunc != NULL); + if (list->collection.size == other->collection.size) { + const char *left = ((const cx_array_list *) list)->data; + const char *right = ((const cx_array_list *) other)->data; + for (size_t i = 0; i < list->collection.size; i++) { + int d = list->collection.cmpfunc(left, right); + if (d != 0) { + return d; + } + left += list->collection.elem_size; + right += other->collection.elem_size; + } + return 0; + } else { + return list->collection.size < other->collection.size ? -1 : 1; + } +} + +static void cx_arl_reverse(struct cx_list_s *list) { + if (list->collection.size < 2) return; + void *data = ((const cx_array_list *) list)->data; + size_t half = list->collection.size / 2; + for (size_t i = 0; i < half; i++) { + cx_array_swap(data, list->collection.elem_size, i, list->collection.size - 1 - i); + } +} + +static bool cx_arl_iter_valid(const void *it) { + const struct cx_iterator_s *iter = it; + const struct cx_list_s *list = iter->src_handle.c; + return iter->index < list->collection.size; +} + +static void *cx_arl_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; + return iter->elem_handle; +} + +static void cx_arl_iter_next(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + cx_arl_remove(iter->src_handle.m, iter->index); + } else { + iter->index++; + iter->elem_handle = + ((char *) iter->elem_handle) + + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; + } +} + +static void cx_arl_iter_prev(void *it) { + struct cx_iterator_s *iter = it; + const cx_array_list *list = iter->src_handle.c; + if (iter->base.remove) { + iter->base.remove = false; + cx_arl_remove(iter->src_handle.m, iter->index); + } + iter->index--; + if (iter->index < list->base.collection.size) { + iter->elem_handle = ((char *) list->data) + + iter->index * list->base.collection.elem_size; + } +} + + +static struct cx_iterator_s cx_arl_iterator( + const struct cx_list_s *list, + size_t index, + bool backwards +) { + struct cx_iterator_s iter; + + iter.index = index; + iter.src_handle.c = list; + iter.elem_handle = cx_arl_at(list, index); + iter.elem_size = list->collection.elem_size; + iter.elem_count = list->collection.size; + iter.base.valid = cx_arl_iter_valid; + iter.base.current = cx_arl_iter_current; + iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; + iter.base.remove = false; + iter.base.mutating = false; + + return iter; +} + +static cx_list_class cx_array_list_class = { + cx_arl_destructor, + cx_arl_insert_element, + cx_arl_insert_array, + cx_arl_insert_sorted, + cx_arl_insert_iter, + cx_arl_remove, + cx_arl_clear, + cx_arl_swap, + cx_arl_at, + cx_arl_find_remove, + cx_arl_sort, + cx_arl_compare, + cx_arl_reverse, + cx_arl_iterator, +}; + +CxList *cxArrayListCreate( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size, + size_t initial_capacity +) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + + cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list)); + if (list == NULL) return NULL; + + list->base.cl = &cx_array_list_class; + list->base.collection.allocator = allocator; + list->capacity = initial_capacity; + + if (elem_size > 0) { + list->base.collection.elem_size = elem_size; + list->base.collection.cmpfunc = comparator; + } else { + elem_size = sizeof(void *); + list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; + cxListStorePointers((CxList *) list); + } + + // allocate the array after the real elem_size is known + list->data = cxCalloc(allocator, initial_capacity, elem_size); + if (list->data == NULL) { + cxFree(allocator, list); + return NULL; + } + + // configure the reallocator + list->reallocator.realloc = cx_arl_realloc; + list->reallocator.ptr1 = (void *) allocator; + + return (CxList *) list; +}
--- a/ucx/avl.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,373 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/avl.h" - -#include <limits.h> - -#define ptrcast(ptr) ((void*)(ptr)) -#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree)) -#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode)) - -static void ucx_avl_connect(UcxAVLTree *tree, - UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) { - if (child) { - child->parent = node; - } - // if child is NULL, nullkey decides if left or right pointer is cleared - if (tree->cmpfunc( - ptrcast(child ? child->key : nullkey), - ptrcast(node->key), tree->userdata) > 0) { - node->right = child; - } else { - node->left = child; - } - size_t lh = node->left ? node->left->height : 0; - size_t rh = node->right ? node->right->height : 0; - node->height = 1 + (lh > rh ? lh : rh); -} - -#define avlheight(node) ((node) ? (node)->height : 0) - -static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->left; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->right, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->right; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->left, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) { - int lh = avlheight(n->left); - int rh = avlheight(n->right); - n->height = 1 + (lh > rh ? lh : rh); - - if (lh - rh == 2) { - UcxAVLNode *c = n->left; - if (avlheight(c->right) - avlheight(c->left) == 1) { - avl_rotleft(tree, c); - } - n = avl_rotright(tree, n); - } else if (rh - lh == 2) { - UcxAVLNode *c = n->right; - if (avlheight(c->left) - avlheight(c->right) == 1) { - avl_rotright(tree, c); - } - n = avl_rotleft(tree, n); - } - - if (n->parent) { - ucx_avl_balance(tree, n->parent); - } else { - tree->root = n; - } -} - -UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) { - return ucx_avl_new_a(cmpfunc, ucx_default_allocator()); -} - -UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) { - UcxAVLTree* tree = alloc_tree(allocator); - if (tree) { - tree->allocator = allocator; - tree->cmpfunc = cmpfunc; - tree->root = NULL; - tree->userdata = NULL; - } - - return tree; -} - -static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) { - if (node) { - ucx_avl_free_node(al, node->left); - ucx_avl_free_node(al, node->right); - alfree(al, node); - } -} - -void ucx_avl_free(UcxAVLTree *tree) { - UcxAllocator *al = tree->allocator; - ucx_avl_free_node(al, tree->root); - alfree(al, tree); -} - -static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node, - ucx_destructor destr) { - if (node) { - ucx_avl_free_content_node(al, node->left, destr); - ucx_avl_free_content_node(al, node->right, destr); - if (destr) { - destr(node->value); - } else { - alfree(al, node->value); - } - } -} - -void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) { - ucx_avl_free_content_node(tree->allocator, tree->root, destr); -} - -UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - return n; -} - -void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = ucx_avl_get_node(tree, key); - return n ? n->value : NULL; -} - -UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = tree->root; - UcxAVLNode *closest = NULL; - - intmax_t cmpresult; - intmax_t closest_dist; - closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX; - - while (n && (cmpresult = dfnc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - if (mode == UCX_AVL_FIND_CLOSEST) { - intmax_t dist = cmpresult; - if (dist < 0) dist *= -1; - if (dist < closest_dist) { - closest_dist = dist; - closest = n; - } - } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) { - if (cmpresult > closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) { - if (cmpresult < closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } - n = cmpresult > 0 ? n->right : n->left; - } - return n ? n : closest; -} - -void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode); - return n ? n->value : NULL; -} - -int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) { - return ucx_avl_put_s(tree, key, value, NULL); -} - -int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, - void **oldvalue) { - if (tree->root) { - UcxAVLNode *n = tree->root; - int cmpresult; - while ((cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - UcxAVLNode *m = cmpresult > 0 ? n->right : n->left; - if (m) { - n = m; - } else { - break; - } - } - - if (cmpresult) { - UcxAVLNode* e = alloc_node(tree->allocator); - if (e) { - e->key = key; e->value = value; e->height = 1; - e->parent = e->left = e->right = NULL; - ucx_avl_connect(tree, n, e, 0); - ucx_avl_balance(tree, n); - return 0; - } else { - return 1; - } - } else { - if (oldvalue) { - *oldvalue = n->value; - } - n->value = value; - return 0; - } - } else { - tree->root = alloc_node(tree->allocator); - if (tree->root) { - tree->root->key = key; tree->root->value = value; - tree->root->height = 1; - tree->root->parent = tree->root->left = tree->root->right = NULL; - - if (oldvalue) { - *oldvalue = NULL; - } - - return 0; - } else { - return 1; - } - } -} - -int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) { - return ucx_avl_remove_s(tree, key, NULL, NULL); -} - -int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) { - return ucx_avl_remove_s(tree, node->key, NULL, NULL); -} - -int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, - intptr_t *oldkey, void **oldvalue) { - - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - if (n) { - if (oldkey) { - *oldkey = n->key; - } - if (oldvalue) { - *oldvalue = n->value; - } - - UcxAVLNode *p = n->parent; - if (n->left && n->right) { - UcxAVLNode *s = n->right; - while (s->left) { - s = s->left; - } - ucx_avl_connect(tree, s->parent, s->right, s->key); - n->key = s->key; n->value = s->value; - p = s->parent; - alfree(tree->allocator, s); - } else { - if (p) { - ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key); - } else { - tree->root = n->right ? n->right : n->left; - if (tree->root) { - tree->root->parent = NULL; - } - } - alfree(tree->allocator, n); - } - - if (p) { - ucx_avl_balance(tree, p); - } - - return 0; - } else { - return 1; - } -} - -static size_t ucx_avl_countn(UcxAVLNode *node) { - if (node) { - return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right); - } else { - return 0; - } -} - -size_t ucx_avl_count(UcxAVLTree *tree) { - return ucx_avl_countn(tree->root); -} - -UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) { - if (node->left) { - UcxAVLNode* n = node->left; - while (n->right) { - n = n->right; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->right == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -} - -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) { - if (node->right) { - UcxAVLNode* n = node->right; - while (n->left) { - n = n->left; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->left == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -}
--- a/ucx/buffer.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/buffer.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,90 +26,100 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/buffer.h" +#include "cx/buffer.h" +#include "cx/utils.h" -#include <stdarg.h> -#include <stdlib.h> +#include <stdio.h> #include <string.h> -UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) { - UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer)); - if (buffer) { - buffer->flags = flags; - if (!space) { - buffer->space = (char*)malloc(capacity); - if (!buffer->space) { - free(buffer); - return NULL; - } - memset(buffer->space, 0, capacity); - buffer->flags |= UCX_BUFFER_AUTOFREE; - } else { - buffer->space = (char*)space; +int cxBufferInit( + CxBuffer *buffer, + void *space, + size_t capacity, + const CxAllocator *allocator, + int flags +) { + if (allocator == NULL) allocator = cxDefaultAllocator; + buffer->allocator = allocator; + buffer->flags = flags; + if (!space) { + buffer->bytes = cxMalloc(allocator, capacity); + if (buffer->bytes == NULL) { + return 1; } - buffer->capacity = capacity; - buffer->size = 0; - - buffer->pos = 0; + buffer->flags |= CX_BUFFER_FREE_CONTENTS; + } else { + buffer->bytes = space; } + buffer->capacity = capacity; + buffer->size = 0; + buffer->pos = 0; - return buffer; -} + buffer->flush_func = NULL; + buffer->flush_target = NULL; + buffer->flush_blkmax = 0; + buffer->flush_blksize = 4096; + buffer->flush_threshold = SIZE_MAX; -void ucx_buffer_free(UcxBuffer *buffer) { - if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) { - free(buffer->space); - } - free(buffer); + return 0; } -UcxBuffer* ucx_buffer_extract( - UcxBuffer *src, size_t start, size_t length, int flags) { - if (src->size == 0 || length == 0 || - ((size_t)-1) - start < length || start+length > src->capacity) - { +void cxBufferDestroy(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); + } +} + +CxBuffer *cxBufferCreate( + void *space, + size_t capacity, + const CxAllocator *allocator, + int flags +) { + CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); + if (buf == NULL) return NULL; + if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { + return buf; + } else { + cxFree(allocator, buf); return NULL; } +} - UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer)); - if (dst) { - dst->space = (char*)malloc(length); - if (!dst->space) { - free(dst); - return NULL; - } - dst->capacity = length; - dst->size = length; - dst->flags = flags | UCX_BUFFER_AUTOFREE; - dst->pos = 0; - memcpy(dst->space, src->space+start, length); +void cxBufferFree(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); } - return dst; + cxFree(buffer->allocator, buffer); } -int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) { +int cxBufferSeek( + CxBuffer *buffer, + off_t offset, + int whence +) { size_t npos; switch (whence) { - case SEEK_CUR: - npos = buffer->pos; - break; - case SEEK_END: - npos = buffer->size; - break; - case SEEK_SET: - npos = 0; - break; - default: - return -1; + case SEEK_CUR: + npos = buffer->pos; + break; + case SEEK_END: + npos = buffer->size; + break; + case SEEK_SET: + npos = 0; + break; + default: + return -1; } size_t opos = npos; npos += offset; - + if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { return -1; } - + if (npos >= buffer->size) { return -1; } else { @@ -119,135 +129,242 @@ } -int ucx_buffer_eof(UcxBuffer *buffer) { +void cxBufferClear(CxBuffer *buffer) { + memset(buffer->bytes, 0, buffer->size); + buffer->size = 0; + buffer->pos = 0; +} + +void cxBufferReset(CxBuffer *buffer) { + buffer->size = 0; + buffer->pos = 0; +} + +int cxBufferEof(const CxBuffer *buffer) { return buffer->pos >= buffer->size; } -int ucx_buffer_extend(UcxBuffer *buffer, size_t len) { - size_t newcap = buffer->capacity; - - if (buffer->capacity + len < buffer->capacity) { - return -1; +int cxBufferMinimumCapacity( + CxBuffer *buffer, + size_t newcap +) { + if (newcap <= buffer->capacity) { + return 0; } - - while (buffer->capacity + len > newcap) { - newcap <<= 1; - if (newcap < buffer->capacity) { - return -1; - } - } - - char *newspace = (char*)realloc(buffer->space, newcap); - if (newspace) { - memset(newspace+buffer->size, 0, newcap-buffer->size); - buffer->space = newspace; + + if (cxReallocate(buffer->allocator, + (void **) &buffer->bytes, newcap) == 0) { buffer->capacity = newcap; + return 0; } else { return -1; } - - return 0; } -size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer) { +/** + * Helps flushing data to the flush target of a buffer. + * + * @param buffer the buffer containing the config + * @param space the data to flush + * @param size the element size + * @param nitems the number of items + * @return the number of items flushed + */ +static size_t cx_buffer_write_flush_helper( + CxBuffer *buffer, + const unsigned char *space, + size_t size, + size_t nitems +) { + size_t pos = 0; + size_t remaining = nitems; + size_t max_items = buffer->flush_blksize / size; + while (remaining > 0) { + size_t items = remaining > max_items ? max_items : remaining; + size_t flushed = buffer->flush_func( + space + pos, + size, items, + buffer->flush_target); + if (flushed > 0) { + pos += (flushed * size); + remaining -= flushed; + } else { + // if no bytes can be flushed out anymore, we give up + break; + } + } + return nitems - remaining; +} + +size_t cxBufferWrite( + const void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { + // optimize for easy case + if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { + memcpy(buffer->bytes + buffer->pos, ptr, nitems); + buffer->pos += nitems; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + return nitems; + } + size_t len; - if(ucx_szmul(size, nitems, &len)) { + size_t nitems_out = nitems; + if (cx_szmul(size, nitems, &len)) { return 0; } size_t required = buffer->pos + len; if (buffer->pos > required) { return 0; } - + + bool perform_flush = false; if (required > buffer->capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if (ucx_buffer_extend(buffer, required - buffer->capacity)) { - return 0; + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { + if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { + perform_flush = true; + } else { + if (cxBufferMinimumCapacity(buffer, required)) { + return 0; + } } } else { - len = buffer->capacity - buffer->pos; - if (size > 1) { - len -= len%size; + if (buffer->flush_blkmax > 0) { + perform_flush = true; + } else { + // truncate data to be written, if we can neither extend nor flush + len = buffer->capacity - buffer->pos; + if (size > 1) { + len -= len % size; + } + nitems_out = len / size; } } } - + if (len == 0) { return len; } - - memcpy(buffer->space + buffer->pos, ptr, len); - buffer->pos += len; - if(buffer->pos > buffer->size) { - buffer->size = buffer->pos; + + if (perform_flush) { + size_t flush_max; + if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { + return 0; + } + size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL + ? buffer->pos + : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); + if (flush_pos == buffer->pos) { + // entire buffer has been flushed, we can reset + buffer->size = buffer->pos = 0; + + size_t items_flush; // how many items can also be directly flushed + size_t items_keep; // how many items have to be written to the buffer + + items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size; + if (items_flush > 0) { + items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size); + // in case we could not flush everything, keep the rest + } + items_keep = nitems - items_flush; + if (items_keep > 0) { + // try again with the remaining stuff + const unsigned char *new_ptr = ptr; + new_ptr += items_flush * size; + // report the directly flushed items as written plus the remaining stuff + return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); + } else { + // all items have been flushed - report them as written + return nitems; + } + } else if (flush_pos == 0) { + // nothing could be flushed at all, we immediately give up without writing any data + return 0; + } else { + // we were partially successful, we shift left and try again + cxBufferShiftLeft(buffer, flush_pos); + return cxBufferWrite(ptr, size, nitems, buffer); + } + } else { + memcpy(buffer->bytes + buffer->pos, ptr, len); + buffer->pos += len; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + return nitems_out; } - - return len / size; + } -size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer) { +int cxBufferPut( + CxBuffer *buffer, + int c +) { + c &= 0xFF; + unsigned char const ch = c; + if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { + return c; + } else { + return EOF; + } +} + +size_t cxBufferPutString( + CxBuffer *buffer, + const char *str +) { + return cxBufferWrite(str, 1, strlen(str), buffer); +} + +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { size_t len; - if(ucx_szmul(size, nitems, &len)) { + if (cx_szmul(size, nitems, &len)) { return 0; } if (buffer->pos + len > buffer->size) { len = buffer->size - buffer->pos; - if (size > 1) len -= len%size; + if (size > 1) len -= len % size; } - + if (len <= 0) { return len; } - - memcpy(ptr, buffer->space + buffer->pos, len); + + memcpy(ptr, buffer->bytes + buffer->pos, len); buffer->pos += len; - + return len / size; } -int ucx_buffer_putc(UcxBuffer *buffer, int c) { - if(buffer->pos >= buffer->capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if(ucx_buffer_extend(buffer, 1)) { - return EOF; - } - } else { - return EOF; - } - } - - c &= 0xFF; - buffer->space[buffer->pos] = (char) c; - buffer->pos++; - if(buffer->pos > buffer->size) { - buffer->size = buffer->pos; - } - return c; -} - -int ucx_buffer_getc(UcxBuffer *buffer) { - if (ucx_buffer_eof(buffer)) { +int cxBufferGet(CxBuffer *buffer) { + if (cxBufferEof(buffer)) { return EOF; } else { - int c = ((unsigned char*)buffer->space)[buffer->pos]; + int c = buffer->bytes[buffer->pos]; buffer->pos++; return c; } } -size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) { - return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); -} - -int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) { +int cxBufferShiftLeft( + CxBuffer *buffer, + size_t shift +) { if (shift >= buffer->size) { buffer->pos = buffer->size = 0; } else { - memmove(buffer->space, buffer->space + shift, buffer->size - shift); + memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); buffer->size -= shift; - + if (buffer->pos >= shift) { buffer->pos -= shift; } else { @@ -257,14 +374,17 @@ return 0; } -int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) { +int cxBufferShiftRight( + CxBuffer *buffer, + size_t shift +) { size_t req_capacity = buffer->size + shift; size_t movebytes; - + // auto extend buffer, if required and enabled if (buffer->capacity < req_capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) { + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, req_capacity)) { return 1; } movebytes = buffer->size; @@ -274,23 +394,26 @@ } else { movebytes = buffer->size; } - - memmove(buffer->space + shift, buffer->space, movebytes); - buffer->size = shift+movebytes; - + + memmove(buffer->bytes + shift, buffer->bytes, movebytes); + buffer->size = shift + movebytes; + buffer->pos += shift; if (buffer->pos > buffer->size) { buffer->pos = buffer->size; } - + return 0; } -int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) { +int cxBufferShift( + CxBuffer *buffer, + off_t shift +) { if (shift < 0) { - return ucx_buffer_shift_left(buffer, (size_t) (-shift)); + return cxBufferShiftLeft(buffer, (size_t) (-shift)); } else if (shift > 0) { - return ucx_buffer_shift_right(buffer, (size_t) shift); + return cxBufferShiftRight(buffer, (size_t) shift); } else { return 0; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/compare.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,213 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/compare.h" + +#include <math.h> + +int cx_cmp_int(const void *i1, const void *i2) { + int a = *((const int *) i1); + int b = *((const int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_longint(const void *i1, const void *i2) { + long int a = *((const long int *) i1); + long int b = *((const long int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_longlong(const void *i1, const void *i2) { + long long a = *((const long long *) i1); + long long b = *((const long long *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int16(const void *i1, const void *i2) { + int16_t a = *((const int16_t *) i1); + int16_t b = *((const int16_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int32(const void *i1, const void *i2) { + int32_t a = *((const int32_t *) i1); + int32_t b = *((const int32_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int64(const void *i1, const void *i2) { + int64_t a = *((const int64_t *) i1); + int64_t b = *((const int64_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint(const void *i1, const void *i2) { + unsigned int a = *((const unsigned int *) i1); + unsigned int b = *((const unsigned int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_ulongint(const void *i1, const void *i2) { + unsigned long int a = *((const unsigned long int *) i1); + unsigned long int b = *((const unsigned long int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_ulonglong(const void *i1, const void *i2) { + unsigned long long a = *((const unsigned long long *) i1); + unsigned long long b = *((const unsigned long long *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint16(const void *i1, const void *i2) { + uint16_t a = *((const uint16_t *) i1); + uint16_t b = *((const uint16_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint32(const void *i1, const void *i2) { + uint32_t a = *((const uint32_t *) i1); + uint32_t b = *((const uint32_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint64(const void *i1, const void *i2) { + uint64_t a = *((const uint64_t *) i1); + uint64_t b = *((const uint64_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_float(const void *f1, const void *f2) { + float a = *((const float *) f1); + float b = *((const float *) f2); + if (fabsf(a - b) < 1e-6f) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_double( + const void *d1, + const void *d2 +) { + double a = *((const double *) d1); + double b = *((const double *) d2); + if (fabs(a - b) < 1e-14) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_intptr( + const void *ptr1, + const void *ptr2 +) { + intptr_t p1 = *(const intptr_t *) ptr1; + intptr_t p2 = *(const intptr_t *) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +} + +int cx_cmp_uintptr( + const void *ptr1, + const void *ptr2 +) { + uintptr_t p1 = *(const uintptr_t *) ptr1; + uintptr_t p2 = *(const uintptr_t *) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +} + +int cx_cmp_ptr( + const void *ptr1, + const void *ptr2 +) { + uintptr_t p1 = (uintptr_t) ptr1; + uintptr_t p2 = (uintptr_t) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/allocator.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,242 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file allocator.h + * Interface for custom allocators. + */ + +#ifndef UCX_ALLOCATOR_H +#define UCX_ALLOCATOR_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The class definition for an allocator. + */ +typedef struct { + /** + * The allocator's malloc() implementation. + */ + void *(*malloc)( + void *data, + size_t n + ); + + /** + * The allocator's realloc() implementation. + */ + __attribute__((__warn_unused_result__)) + void *(*realloc)( + void *data, + void *mem, + size_t n + ); + + /** + * The allocator's calloc() implementation. + */ + void *(*calloc)( + void *data, + size_t nelem, + size_t n + ); + + /** + * The allocator's free() implementation. + */ + __attribute__((__nonnull__)) + void (*free)( + void *data, + void *mem + ); +} cx_allocator_class; + +/** + * Structure holding the data for an allocator. + */ +struct cx_allocator_s { + /** + * A pointer to the instance of the allocator class. + */ + cx_allocator_class *cl; + /** + * A pointer to the data this allocator uses. + */ + void *data; +}; + +/** + * High-Level type alias for the allocator type. + */ +typedef struct cx_allocator_s CxAllocator; + +/** + * A default allocator using standard library malloc() etc. + */ +extern CxAllocator *cxDefaultAllocator; + +/** + * Function pointer type for destructor functions. + * + * A destructor function deallocates possible contents and MAY free the memory + * pointed to by \p memory. Read the documentation of the respective function + * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that + * particular implementation. + * + * @param memory a pointer to the object to destruct + */ +__attribute__((__nonnull__)) +typedef void (*cx_destructor_func)(void *memory); + +/** + * Function pointer type for destructor functions. + * + * A destructor function deallocates possible contents and MAY free the memory + * pointed to by \p memory. Read the documentation of the respective function + * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that + * particular implementation. + * + * @param data an optional pointer to custom data + * @param memory a pointer to the object to destruct + */ +__attribute__((__nonnull__(2))) +typedef void (*cx_destructor_func2)( + void *data, + void *memory +); + +/** + * Re-allocate a previously allocated block and changes the pointer in-place, if necessary. + * + * \par Error handling + * \c errno will be set by realloc() on failure. + * + * @param mem pointer to the pointer to allocated block + * @param n the new size in bytes + * @return zero on success, non-zero on failure + */ +__attribute__((__nonnull__)) +int cx_reallocate( + void **mem, + size_t n +); + +/** + * Allocate \p n bytes of memory. + * + * @param allocator the allocator + * @param n the number of bytes + * @return a pointer to the allocated memory + */ +__attribute__((__malloc__)) +__attribute__((__alloc_size__(2))) +void *cxMalloc( + const CxAllocator *allocator, + size_t n +); + +/** + * Re-allocate the previously allocated block in \p mem, making the new block \p n bytes long. + * This function may return the same pointer that was passed to it, if moving the memory + * was not necessary. + * + * \note Re-allocating a block allocated by a different allocator is undefined. + * + * @param allocator the allocator + * @param mem pointer to the previously allocated block + * @param n the new size in bytes + * @return a pointer to the re-allocated memory + */ +__attribute__((__warn_unused_result__)) +__attribute__((__alloc_size__(3))) +void *cxRealloc( + const CxAllocator *allocator, + void *mem, + size_t n +); + +/** + * Re-allocate a previously allocated block and changes the pointer in-place, if necessary. + * This function acts like cxRealloc() using the pointer pointed to by \p mem. + * + * \note Re-allocating a block allocated by a different allocator is undefined. + * + * \par Error handling + * \c errno will be set, if the underlying realloc function does so. + * + * @param allocator the allocator + * @param mem pointer to the pointer to allocated block + * @param n the new size in bytes + * @return zero on success, non-zero on failure + */ +__attribute__((__nonnull__)) +int cxReallocate( + const CxAllocator *allocator, + void **mem, + size_t n +); + +/** + * Allocate \p nelem elements of \p n bytes each, all initialized to zero. + * + * @param allocator the allocator + * @param nelem the number of elements + * @param n the size of each element in bytes + * @return a pointer to the allocated memory + */ +__attribute__((__malloc__)) +__attribute__((__alloc_size__(2, 3))) +void *cxCalloc( + const CxAllocator *allocator, + size_t nelem, + size_t n +); + +/** + * Free a block allocated by this allocator. + * + * \note Freeing a block of a different allocator is undefined. + * + * @param allocator the allocator + * @param mem a pointer to the block to free + */ +__attribute__((__nonnull__)) +void cxFree( + const CxAllocator *allocator, + void *mem +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_ALLOCATOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/array_list.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,461 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file array_list.h + * \brief Array list implementation. + * \details Also provides several low-level functions for custom array list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + + +#ifndef UCX_ARRAY_LIST_H +#define UCX_ARRAY_LIST_H + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The maximum item size in an array list that fits into stack buffer when swapped. + */ +extern unsigned cx_array_swap_sbo_size; + +/** + * Declares variables for an array that can be used with the convenience macros. + * + * @see cx_array_simple_add() + * @see cx_array_simple_copy() + * @see cx_array_initialize() + * @see cx_array_simple_add_sorted() + * @see cx_array_simple_insert_sorted() + */ +#define CX_ARRAY_DECLARE(type, name) \ + type * name; \ + size_t name##_size; \ + size_t name##_capacity + +/** + * Initializes an array declared with CX_ARRAY_DECLARE(). + * + * The memory for the array is allocated with stdlib malloc(). + * @param array the array + * @param capacity the initial capacity + */ +#define cx_array_initialize(array, capacity) \ + array##_capacity = capacity; \ + array##_size = 0; \ + array = malloc(sizeof(array[0]) * capacity) + +/** + * Defines a reallocation mechanism for arrays. + */ +struct cx_array_reallocator_s { + /** + * Reallocates space for the given array. + * + * Implementations are not required to free the original array. + * This allows reallocation of static memory by allocating heap memory + * and copying the array contents. The information in the custom fields of + * the referenced allocator can be used to track the state of the memory + * or to transport other additional data. + * + * @param array the array to reallocate + * @param capacity the new capacity (number of elements) + * @param elem_size the size of each element + * @param alloc a reference to this allocator + * @return a pointer to the reallocated memory or \c NULL on failure + */ + void *(*realloc)( + void *array, + size_t capacity, + size_t elem_size, + struct cx_array_reallocator_s *alloc + ); + + /** + * Custom data pointer. + */ + void *ptr1; + /** + * Custom data pointer. + */ + void *ptr2; + /** + * Custom data integer. + */ + size_t int1; + /** + * Custom data integer. + */ + size_t int2; +}; + +/** + * A default stdlib-based array reallocator. + */ +extern struct cx_array_reallocator_s *cx_array_default_reallocator; + +/** + * Return codes for array functions. + */ +enum cx_array_result { + CX_ARRAY_SUCCESS, + CX_ARRAY_REALLOC_NOT_SUPPORTED, + CX_ARRAY_REALLOC_FAILED, +}; + +/** + * Copies elements from one array to another. + * + * The elements are copied to the \p target array at the specified \p index, + * overwriting possible elements. The \p index does not need to be in range of + * the current array \p size. If the new index plus the number of elements added + * would extend the array's size, and \p capacity is not \c NULL, the remaining + * capacity is used. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made, unless the \p reallocator is set to \c NULL, in which case + * this function ultimately returns a failure. + * + * @param target a pointer to the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity - + * \c NULL if only the size shall be used to bound the array (reallocations + * will NOT be supported in that case) + * @param index the index where the copied elements shall be placed + * @param src the source array + * @param elem_size the size of one element + * @param elem_count the number of elements to copy + * @param reallocator the array reallocator to use, or \c NULL + * if reallocation shall not happen + * @return zero on success, non-zero error code on failure + */ +__attribute__((__nonnull__(1, 2, 5))) +enum cx_array_result cx_array_copy( + void **target, + size_t *size, + size_t *capacity, + size_t index, + const void *src, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +); + +/** + * Convenience macro that uses cx_array_copy() with a default layout and the default reallocator. + * + * @param array the name of the array (NOT a pointer to the array) + * @param index the index where the copied elements shall be placed + * @param src the source array + * @param count the number of elements to copy + * @see CX_ARRAY_DECLARE() + */ +#define cx_array_simple_copy(array, index, src, count) \ + cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \ + index, src, sizeof((array)[0]), count, cx_array_default_reallocator) + +/** + * Adds an element to an array with the possibility of allocating more space. + * + * The element \p elem is added to the end of the \p target array which containing + * \p size elements, already. The \p capacity must not be \c NULL and point a + * variable holding the current maximum number of elements the array can hold. + * + * If the capacity is insufficient to hold the new element, and the optional + * \p reallocator is not \c NULL, an attempt increase the \p capacity is made + * and the new capacity is written back. + * + * @param target a pointer to the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity - must not be \c NULL + * @param elem_size the size of one element + * @param elem a pointer to the element to add + * @param reallocator the array reallocator to use, or \c NULL if reallocation shall not happen + * @return zero on success, non-zero error code on failure + */ +#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \ + cx_array_copy((void**)(target), size, capacity, *(size), elem, elem_size, 1, reallocator) + +/** + * Convenience macro that uses cx_array_add() with a default layout and + * the default reallocator. + * + * @param array the name of the array (NOT a pointer to the array) + * @param elem the element to add (NOT a pointer, address is automatically taken) + * @see CX_ARRAY_DECLARE() + */ +#define cx_array_simple_add(array, elem) \ + cx_array_simple_copy(array, array##_size, &(elem), 1) + + +/** + * Inserts a sorted array into another sorted array. + * + * If either the target or the source array is not already sorted with respect + * to the specified \p cmp_func, the behavior is undefined. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made. + * + * @param target a pointer to the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity + * @param cmp_func the compare function for the elements + * @param src the source array + * @param elem_size the size of one element + * @param elem_count the number of elements to insert + * @param reallocator the array reallocator to use + * @return zero on success, non-zero error code on failure + */ +__attribute__((__nonnull__)) +enum cx_array_result cx_array_insert_sorted( + void **target, + size_t *size, + size_t *capacity, + cx_compare_func cmp_func, + const void *src, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +); + +/** + * Inserts an element into a sorted array. + * + * If the target array is not already sorted with respect + * to the specified \p cmp_func, the behavior is undefined. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made. + * + * @param target a pointer to the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity + * @param elem_size the size of one element + * @param elem a pointer to the element to add + * @param reallocator the array reallocator to use + * @return zero on success, non-zero error code on failure + */ +#define cx_array_add_sorted(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ + cx_array_insert_sorted((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) + +/** + * Convenience macro for cx_array_add_sorted() with a default + * layout and the default reallocator. + * + * @param array the name of the array (NOT a pointer to the array) + * @param elem the element to add (NOT a pointer, address is automatically taken) + * @param cmp_func the compare function for the elements + * @see CX_ARRAY_DECLARE() + */ +#define cx_array_simple_add_sorted(array, elem, cmp_func) \ + cx_array_add_sorted(&array, &(array##_size), &(array##_capacity), \ + sizeof((array)[0]), &(elem), cmp_func, cx_array_default_reallocator) + +/** + * Convenience macro for cx_array_insert_sorted() with a default + * layout and the default reallocator. + * + * @param array the name of the array (NOT a pointer to the array) + * @param src pointer to the source array + * @param n number of elements in the source array + * @param cmp_func the compare function for the elements + * @see CX_ARRAY_DECLARE() + */ +#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \ + cx_array_insert_sorted((void**)(&array), &(array##_size), &(array##_capacity), \ + cmp_func, src, sizeof((array)[0]), n, cx_array_default_reallocator) + + +/** + * Searches the largest lower bound in a sorted array. + * + * In other words, this function returns the index of the largest element + * in \p arr that is less or equal to \p elem with respect to \p cmp_func. + * When no such element exists, \p size is returned. + * + * If \p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the \p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @return the index of the largest lower bound, or \p size + */ +__attribute__((__nonnull__)) +size_t cx_array_binary_search_inf( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func cmp_func +); + +/** + * Searches an item in a sorted array. + * + * If the array is not sorted with respect to the \p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @return the index of the element in the array, or \p size if the element + * cannot be found + */ +__attribute__((__nonnull__)) +static inline size_t cx_array_binary_search( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func cmp_func +) { + size_t index = cx_array_binary_search_inf( + arr, size, elem_size, elem, cmp_func + ); + if (index < size && cmp_func(((const char *) arr) + index * elem_size, elem) == 0) { + return index; + } else { + return size; + } +} + +/** + * Searches the smallest upper bound in a sorted array. + * + * In other words, this function returns the index of the smallest element + * in \p arr that is greater or equal to \p elem with respect to \p cmp_func. + * When no such element exists, \p size is returned. + * + * If \p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the \p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @return the index of the smallest upper bound, or \p size + */ +__attribute__((__nonnull__)) +static inline size_t cx_array_binary_search_sup( + const void *arr, + size_t size, + size_t elem_size, + const void *elem, + cx_compare_func cmp_func +) { + size_t inf = cx_array_binary_search_inf(arr, size, elem_size, elem, cmp_func); + if (inf == size) { + // no infimum means, first element is supremum + return 0; + } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) { + return inf; + } else { + return inf + 1; + } +} + +/** + * Swaps two array elements. + * + * @param arr the array + * @param elem_size the element size + * @param idx1 index of first element + * @param idx2 index of second element + */ +__attribute__((__nonnull__)) +void cx_array_swap( + void *arr, + size_t elem_size, + size_t idx1, + size_t idx2 +); + +/** + * Allocates an array list for storing elements with \p elem_size bytes each. + * + * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(), if none is given. + * + * @param allocator the allocator for allocating the list memory + * (if \c NULL the cxDefaultAllocator will be used) + * @param comparator the comparator for the elements + * (if \c NULL, and the list is not storing pointers, sort and find + * functions will not work) + * @param elem_size the size of each element in bytes + * @param initial_capacity the initial number of elements the array can store + * @return the created list + */ +CxList *cxArrayListCreate( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size, + size_t initial_capacity +); + +/** + * Allocates an array list for storing elements with \p elem_size bytes each. + * + * The list will use the cxDefaultAllocator and \em NO compare function. + * If you want to call functions that need a compare function, you have to + * set it immediately after creation or use cxArrayListCreate(). + * + * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(). + * + * @param elem_size the size of each element in bytes + * @param initial_capacity the initial number of elements the array can store + * @return the created list + */ +#define cxArrayListCreateSimple(elem_size, initial_capacity) \ + cxArrayListCreate(NULL, NULL, elem_size, initial_capacity) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_ARRAY_LIST_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/buffer.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,464 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file buffer.h + * + * \brief Advanced buffer implementation. + * + * Instances of CxBuffer can be used to read from or to write to like one + * would do with a stream. + * + * Some features for convenient use of the buffer + * can be enabled. See the documentation of the macro constants for more + * information. + * + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_BUFFER_H +#define UCX_BUFFER_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * No buffer features enabled (all flags cleared). + */ +#define CX_BUFFER_DEFAULT 0x00 + +/** + * If this flag is enabled, the buffer will automatically free its contents when destroyed. + */ +#define CX_BUFFER_FREE_CONTENTS 0x01 + +/** + * If this flag is enabled, the buffer will automatically extends its capacity. + */ +#define CX_BUFFER_AUTO_EXTEND 0x02 + +/** Structure for the UCX buffer data. */ +typedef struct { + /** A pointer to the buffer contents. */ + union { + /** + * Data is interpreted as text. + */ + char *space; + /** + * Data is interpreted as binary. + */ + unsigned char *bytes; + }; + /** The allocator to use for automatic memory management. */ + const CxAllocator *allocator; + /** Current position of the buffer. */ + size_t pos; + /** Current capacity (i.e. maximum size) of the buffer. */ + size_t capacity; + /** Current size of the buffer content. */ + size_t size; + /** + * The buffer may not extend beyond this threshold before starting to flush. + * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled). + */ + size_t flush_threshold; + /** + * The block size for the elements to flush. + * Default is 4096 bytes. + */ + size_t flush_blksize; + /** + * The maximum number of blocks to flush in one cycle. + * Zero disables flushing entirely (this is the default). + * Set this to \c SIZE_MAX to flush the entire buffer. + * + * @attention if the maximum number of blocks multiplied with the block size + * is smaller than the expected contents written to this buffer within one write + * operation, multiple flush cycles are performed after that write. + * That means the total number of blocks flushed after one write to this buffer may + * be larger than \c flush_blkmax. + */ + size_t flush_blkmax; + + /** + * The write function used for flushing. + * If NULL, the flushed content gets discarded. + */ + cx_write_func flush_func; + + /** + * The target for \c flush_func. + */ + void *flush_target; + + /** + * Flag register for buffer features. + * @see #CX_BUFFER_DEFAULT + * @see #CX_BUFFER_FREE_CONTENTS + * @see #CX_BUFFER_AUTO_EXTEND + */ + int flags; +} cx_buffer_s; + +/** + * UCX buffer. + */ +typedef cx_buffer_s CxBuffer; + +/** + * Initializes a fresh buffer. + * + * \note You may provide \c NULL as argument for \p space. + * Then this function will allocate the space and enforce + * the #CX_BUFFER_FREE_CONTENTS flag. + * + * @param buffer the buffer to initialize + * @param space pointer to the memory area, or \c NULL to allocate + * new memory + * @param capacity the capacity of the buffer + * @param allocator the allocator this buffer shall use for automatic + * memory management. If \c NULL, the default heap allocator will be used. + * @param flags buffer features (see cx_buffer_s.flags) + * @return zero on success, non-zero if a required allocation failed + */ +__attribute__((__nonnull__(1))) +int cxBufferInit( + CxBuffer *buffer, + void *space, + size_t capacity, + const CxAllocator *allocator, + int flags +); + +/** + * Allocates and initializes a fresh buffer. + * + * \note You may provide \c NULL as argument for \p space. + * Then this function will allocate the space and enforce + * the #CX_BUFFER_FREE_CONTENTS flag. + * + * @param space pointer to the memory area, or \c NULL to allocate + * new memory + * @param capacity the capacity of the buffer + * @param allocator the allocator to use for allocating the structure and the automatic + * memory management within the buffer. If \c NULL, the default heap allocator will be used. + * @param flags buffer features (see cx_buffer_s.flags) + * @return a pointer to the buffer on success, \c NULL if a required allocation failed + */ +CxBuffer *cxBufferCreate( + void *space, + size_t capacity, + const CxAllocator *allocator, + int flags +); + +/** + * Destroys the buffer contents. + * + * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. + * If you want to free the memory of the entire buffer, use cxBufferFree(). + * + * @param buffer the buffer which contents shall be destroyed + * @see cxBufferInit() + */ +__attribute__((__nonnull__)) +void cxBufferDestroy(CxBuffer *buffer); + +/** + * Deallocates the buffer. + * + * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys + * the contents. If you \em only want to destroy the contents, use cxBufferDestroy(). + * + * @param buffer the buffer to deallocate + * @see cxBufferCreate() + */ +__attribute__((__nonnull__)) +void cxBufferFree(CxBuffer *buffer); + +/** + * Shifts the contents of the buffer by the given offset. + * + * If the offset is positive, the contents are shifted to the right. + * If auto extension is enabled, the buffer grows, if necessary. + * In case the auto extension fails, this function returns a non-zero value and + * no contents are changed. + * If auto extension is disabled, the contents that do not fit into the buffer + * are discarded. + * + * If the offset is negative, the contents are shifted to the left where the + * first \p shift bytes are discarded. + * The new size of the buffer is the old size minus the absolute shift value. + * If this value is larger than the buffer size, the buffer is emptied (but + * not cleared, see the security note below). + * + * The buffer position gets shifted alongside with the content but is kept + * within the boundaries of the buffer. + * + * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and + * cxBufferShiftRight() functions using a \c size_t as parameter type. + * + * \attention + * Security Note: The shifting operation does \em not erase the previously occupied memory cells. + * But you can easily do that manually, e.g. by calling + * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or + * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code> + * for a left shift. + * + * @param buffer the buffer + * @param shift the shift offset (negative means left shift) + * @return 0 on success, non-zero if a required auto-extension fails + */ +__attribute__((__nonnull__)) +int cxBufferShift( + CxBuffer *buffer, + off_t shift +); + +/** + * Shifts the buffer to the right. + * See cxBufferShift() for details. + * + * @param buffer the buffer + * @param shift the shift offset + * @return 0 on success, non-zero if a required auto-extension fails + * @see cxBufferShift() + */ +__attribute__((__nonnull__)) +int cxBufferShiftRight( + CxBuffer *buffer, + size_t shift +); + +/** + * Shifts the buffer to the left. + * See cxBufferShift() for details. + * + * \note Since a left shift cannot fail due to memory allocation problems, this + * function always returns zero. + * + * @param buffer the buffer + * @param shift the positive shift offset + * @return always zero + * @see cxBufferShift() + */ +__attribute__((__nonnull__)) +int cxBufferShiftLeft( + CxBuffer *buffer, + size_t shift +); + + +/** + * Moves the position of the buffer. + * + * The new position is relative to the \p whence argument. + * + * \li \c SEEK_SET marks the start of the buffer. + * \li \c SEEK_CUR marks the current position. + * \li \c SEEK_END marks the end of the buffer. + * + * With an offset of zero, this function sets the buffer position to zero + * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position + * unchanged (\c SEEK_CUR). + * + * @param buffer the buffer + * @param offset position offset relative to \p whence + * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * @return 0 on success, non-zero if the position is invalid + * + */ +__attribute__((__nonnull__)) +int cxBufferSeek( + CxBuffer *buffer, + off_t offset, + int whence +); + +/** + * Clears the buffer by resetting the position and deleting the data. + * + * The data is deleted by zeroing it with a call to memset(). + * If you do not need that, you can use the faster cxBufferReset(). + * + * @param buffer the buffer to be cleared + * @see cxBufferReset() + */ +__attribute__((__nonnull__)) +void cxBufferClear(CxBuffer *buffer); + +/** + * Resets the buffer by resetting the position and size to zero. + * + * The data in the buffer is not deleted. If you need a safe + * reset of the buffer, use cxBufferClear(). + * + * @param buffer the buffer to be cleared + * @see cxBufferClear() + */ +__attribute__((__nonnull__)) +void cxBufferReset(CxBuffer *buffer); + +/** + * Tests, if the buffer position has exceeded the buffer size. + * + * @param buffer the buffer to test + * @return non-zero, if the current buffer position has exceeded the last + * byte of the buffer's contents. + */ +__attribute__((__nonnull__)) +int cxBufferEof(const CxBuffer *buffer); + + +/** + * Ensures that the buffer has a minimum capacity. + * + * If the current capacity is not sufficient, the buffer will be extended. + * + * @param buffer the buffer + * @param capacity the minimum required capacity for this buffer + * @return 0 on success or a non-zero value on failure + */ +__attribute__((__nonnull__)) +int cxBufferMinimumCapacity( + CxBuffer *buffer, + size_t capacity +); + +/** + * Writes data to a CxBuffer. + * + * If flushing is enabled and the buffer needs to flush, the data is flushed to + * the target until the target signals that it cannot take more data by + * returning zero via the respective write function. In that case, the remaining + * data in this buffer is shifted to the beginning of this buffer so that the + * newly available space can be used to append as much data as possible. This + * function only stops writing more elements, when the flush target and this + * buffer are both incapable of taking more data or all data has been written. + * The number returned by this function is the total number of elements that + * could be written during the process. It does not necessarily mean that those + * elements are still in this buffer, because some of them could have also be + * flushed already. + * + * If automatic flushing is not enabled, the position of the buffer is increased + * by the number of bytes written. + * + * \note The signature is compatible with the fwrite() family of functions. + * + * @param ptr a pointer to the memory area containing the bytes to be written + * @param size the length of one element + * @param nitems the element count + * @param buffer the CxBuffer to write to + * @return the total count of elements written + */ +__attribute__((__nonnull__)) +size_t cxBufferWrite( + const void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +); + +/** + * Reads data from a CxBuffer. + * + * The position of the buffer is increased by the number of bytes read. + * + * \note The signature is compatible with the fread() family of functions. + * + * @param ptr a pointer to the memory area where to store the read data + * @param size the length of one element + * @param nitems the element count + * @param buffer the CxBuffer to read from + * @return the total number of elements read + */ +__attribute__((__nonnull__)) +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +); + +/** + * Writes a character to a buffer. + * + * The least significant byte of the argument is written to the buffer. If the + * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, + * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature is + * disabled or buffer extension fails, \c EOF is returned. + * + * On successful write, the position of the buffer is increased. + * + * @param buffer the buffer to write to + * @param c the character to write + * @return the byte that has bean written or \c EOF when the end of the stream is + * reached and automatic extension is not enabled or not possible + */ +__attribute__((__nonnull__)) +int cxBufferPut( + CxBuffer *buffer, + int c +); + +/** + * Writes a string to a buffer. + * + * @param buffer the buffer + * @param str the zero-terminated string + * @return the number of bytes written + */ +__attribute__((__nonnull__)) +size_t cxBufferPutString( + CxBuffer *buffer, + const char *str +); + +/** + * Gets a character from a buffer. + * + * The current position of the buffer is increased after a successful read. + * + * @param buffer the buffer to read from + * @return the character or \c EOF, if the end of the buffer is reached + */ +__attribute__((__nonnull__)) +int cxBufferGet(CxBuffer *buffer); + +#ifdef __cplusplus +} +#endif + +#endif // UCX_BUFFER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/collection.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,164 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file collection.h + * \brief Common definitions for various collection implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COLLECTION_H +#define UCX_COLLECTION_H + +#include "allocator.h" +#include "iterator.h" +#include "compare.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Special constant used for creating collections that are storing pointers. + */ +#define CX_STORE_POINTERS 0 + +/** + * Base attributes of a collection. + */ +struct cx_collection_s { + /** + * The allocator to use. + */ + const CxAllocator *allocator; + /** + * The comparator function for the elements. + */ + cx_compare_func cmpfunc; + /** + * The size of each element. + */ + size_t elem_size; + /** + * The number of currently stored elements. + */ + size_t size; + /** + * An optional simple destructor for the collection's elements. + * + * @attention Read the documentation of the particular collection implementation + * whether this destructor shall only destroy the contents or also free the memory. + */ + cx_destructor_func simple_destructor; + /** + * An optional advanced destructor for the collection's elements. + * + * @attention Read the documentation of the particular collection implementation + * whether this destructor shall only destroy the contents or also free the memory. + */ + cx_destructor_func2 advanced_destructor; + /** + * The pointer to additional data that is passed to the advanced destructor. + */ + void *destructor_data; + /** + * Indicates if this list is supposed to store pointers + * instead of copies of the actual objects. + */ + bool store_pointer; +}; + +/** + * Use this macro to declare common members for a collection structure. + */ +#define CX_COLLECTION_BASE struct cx_collection_s collection + +/** + * Sets a simple destructor function for this collection. + * + * @param c the collection + * @param destr the destructor function + */ +#define cxDefineDestructor(c, destr) \ + (c)->collection.simple_destructor = (cx_destructor_func) destr + +/** + * Sets a simple destructor function for this collection. + * + * @param c the collection + * @param destr the destructor function + */ +#define cxDefineAdvancedDestructor(c, destr, data) \ + (c)->collection.advanced_destructor = (cx_destructor_func2) destr; \ + (c)->collection.destructor_data = data + +/** + * Invokes the simple destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_simple_destructor(c, e) \ + (c)->collection.simple_destructor((c)->collection.store_pointer ? (*((void **) (e))) : (e)) + +/** + * Invokes the advanced destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_advanced_destructor(c, e) \ + (c)->collection.advanced_destructor((c)->collection.destructor_data, \ + (c)->collection.store_pointer ? (*((void **) (e))) : (e)) + + +/** + * Invokes all available destructor functions for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_destructor(c, e) \ + if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \ + if ((c)->collection.advanced_destructor) cx_invoke_advanced_destructor(c,e) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_COLLECTION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/common.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file common.h + * + * \brief Common definitions and feature checks. + * + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + * + * \mainpage UAP Common Extensions + * Library with common and useful functions, macros and data structures. + * <p> + * Latest available source:<br> + * <a href="https://sourceforge.net/projects/ucx/files/">https://sourceforge.net/projects/ucx/files/</a> + * </p> + * + * <p> + * Repositories:<br> + * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a> + * - or - + * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a> + * </p> + * + * <h2>LICENCE</h2> + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UCX_COMMON_H +#define UCX_COMMON_H + +/** Major UCX version as integer constant. */ +#define UCX_VERSION_MAJOR 3 + +/** Minor UCX version as integer constant. */ +#define UCX_VERSION_MINOR 1 + +/** Version constant which ensures to increase monotonically. */ +#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) + +// Common Includes + +#include <stdlib.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + +#ifndef UCX_TEST_H +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + const void *, + size_t, + size_t, + void * +); +#endif // UCX_TEST_H + +/** + * Function pointer compatible with fread-like functions. + */ +typedef size_t (*cx_read_func)( + void *, + size_t, + size_t, + void * +); + + +// Compiler specific stuff + +#ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ +#define __attribute__(x) +#endif + +#ifdef _MSC_VER + +// fix missing ssize_t definition +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; + +// fix missing _Thread_local support +#define _Thread_local __declspec(thread) + +#endif + +#endif // UCX_COMMON_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/common.h.orig Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file common.h + * + * \brief Common definitions and feature checks. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + * + * \mainpage UAP Common Extensions + * Library with common and useful functions, macros and data structures. + * <p> + * Latest available source:<br> + * <a href="https://sourceforge.net/projects/ucx/files/">https://sourceforge.net/projects/ucx/files/</a> + * </p> + * + * <p> + * Repositories:<br> + * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a> + * - or - + * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a> + * </p> + * + * <h2>LICENCE</h2> + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UCX_COMMON_H +#define UCX_COMMON_H + +/** Major UCX version as integer constant. */ +#define UCX_VERSION_MAJOR 3 + +/** Minor UCX version as integer constant. */ +#define UCX_VERSION_MINOR 0 + +/** Version constant which ensures to increase monotonically. */ +#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) + +#define __attribute__(...) + +#include <stdlib.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> + +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + void const *, + size_t, + size_t, + void * +); + +/** + * Function pointer compatible with fread-like functions. + */ +typedef size_t (*cx_read_func)( + void *, + size_t, + size_t, + void * +); + +#ifdef _WIN32 + +#ifdef __MINGW32__ +#include <sys/types.h> +#endif // __MINGW32__ + +#else // !_WIN32 + +#include <sys/types.h> + +#endif // _WIN32 + +#ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ +#define __attribute__(x) +#endif + +#endif // UCX_COMMON_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/compare.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,243 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file compare.h + * \brief A collection of simple compare functions. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COMPARE_H +#define UCX_COMPARE_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CX_COMPARE_FUNC_DEFINED +#define CX_COMPARE_FUNC_DEFINED +/** + * A comparator function comparing two collection elements. + */ +typedef int(*cx_compare_func)( + const void *left, + const void *right +); +#endif // CX_COMPARE_FUNC_DEFINED + +/** + * Compares two integers of type int. + * + * @param i1 pointer to integer one + * @param i2 pointer to integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int(const void *i1, const void *i2); + +/** + * Compares two integers of type long int. + * + * @param i1 pointer to long integer one + * @param i2 pointer to long integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_longint(const void *i1, const void *i2); + +/** + * Compares two integers of type long long. + * + * @param i1 pointer to long long one + * @param i2 pointer to long long two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_longlong(const void *i1, const void *i2); + +/** + * Compares two integers of type int16_t. + * + * @param i1 pointer to int16_t one + * @param i2 pointer to int16_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int16(const void *i1, const void *i2); + +/** + * Compares two integers of type int32_t. + * + * @param i1 pointer to int32_t one + * @param i2 pointer to int32_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int32(const void *i1, const void *i2); + +/** + * Compares two integers of type int64_t. + * + * @param i1 pointer to int64_t one + * @param i2 pointer to int64_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int64(const void *i1, const void *i2); + +/** + * Compares two integers of type unsigned int. + * + * @param i1 pointer to unsigned integer one + * @param i2 pointer to unsigned integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint(const void *i1, const void *i2); + +/** + * Compares two integers of type unsigned long int. + * + * @param i1 pointer to unsigned long integer one + * @param i2 pointer to unsigned long integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_ulongint(const void *i1, const void *i2); + +/** + * Compares two integers of type unsigned long long. + * + * @param i1 pointer to unsigned long long one + * @param i2 pointer to unsigned long long two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_ulonglong(const void *i1, const void *i2); + +/** + * Compares two integers of type uint16_t. + * + * @param i1 pointer to uint16_t one + * @param i2 pointer to uint16_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint16(const void *i1, const void *i2); + +/** + * Compares two integers of type uint32_t. + * + * @param i1 pointer to uint32_t one + * @param i2 pointer to uint32_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint32(const void *i1, const void *i2); + +/** + * Compares two integers of type uint64_t. + * + * @param i1 pointer to uint64_t one + * @param i2 pointer to uint64_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint64(const void *i1, const void *i2); + +/** + * Compares two real numbers of type float with precision 1e-6f. + * + * @param f1 pointer to float one + * @param f2 pointer to float two + * @return -1, if *f1 is less than *f2, 0 if both are equal, + * 1 if *f1 is greater than *f2 + */ + +int cx_cmp_float(const void *f1, const void *f2); + +/** + * Compares two real numbers of type double with precision 1e-14. + * + * @param d1 pointer to double one + * @param d2 pointer to double two + * @return -1, if *d1 is less than *d2, 0 if both are equal, + * 1 if *d1 is greater than *d2 + */ +int cx_cmp_double( + const void *d1, + const void *d2 +); + +/** + * Compares the integer representation of two pointers. + * + * @param ptr1 pointer to pointer one (const intptr_t*) + * @param ptr2 pointer to pointer two (const intptr_t*) + * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, + * 1 if *ptr1 is greater than *ptr2 + */ +int cx_cmp_intptr( + const void *ptr1, + const void *ptr2 +); + +/** + * Compares the unsigned integer representation of two pointers. + * + * @param ptr1 pointer to pointer one (const uintptr_t*) + * @param ptr2 pointer to pointer two (const uintptr_t*) + * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, + * 1 if *ptr1 is greater than *ptr2 + */ +int cx_cmp_uintptr( + const void *ptr1, + const void *ptr2 +); + +/** + * Compares the pointers specified in the arguments without de-referencing. + * + * @param ptr1 pointer one + * @param ptr2 pointer two + * @return -1 if ptr1 is less than ptr2, 0 if both are equal, + * 1 if ptr1 is greater than ptr2 + */ +int cx_cmp_ptr( + const void *ptr1, + const void *ptr2 +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_COMPARE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_key.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,128 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file hash_key.h + * \brief Interface for map implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + + +#ifndef UCX_HASH_KEY_H +#define UCX_HASH_KEY_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for a key within a hash map. */ +struct cx_hash_key_s { + /** The key data. */ + const void *data; + /** + * The key data length. + */ + size_t len; + /** The hash value of the key data. */ + unsigned hash; +}; + +/** + * Type for a hash key. + */ +typedef struct cx_hash_key_s CxHashKey; + +/** + * Computes a murmur2 32 bit hash. + * + * You need to initialize \c data and \c len in the key struct. + * The hash is then directly written to that struct. + * + * \note If \c data is \c NULL, the hash is defined as 1574210520. + * + * @param key the key, the hash shall be computed for + */ +void cx_hash_murmur(CxHashKey *key); + +/** + * Computes a hash key from a string. + * + * The string needs to be zero-terminated. + * + * @param str the string + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key_str(const char *str); + +/** + * Computes a hash key from a byte array. + * + * @param bytes the array + * @param len the length + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key_bytes( + const unsigned char *bytes, + size_t len +); + +/** + * Computes a hash key for an arbitrary object. + * + * The computation uses the in-memory representation that might not be + * the same on different platforms. Therefore, this hash should not be + * used for data exchange with different machines. + * + * @param obj a pointer to an arbitrary object + * @param len the length of object in memory + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key( + const void *obj, + size_t len +); + +/** + * Computes a hash key from a UCX string. + * + * @param str the string + * @return the hash key + */ +#define cx_hash_key_cxstr(str) cx_hash_key((void*)(str).ptr, (str).length) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_HASH_KEY_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_map.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,133 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file hash_map.h + * \brief Hash map implementation. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_HASH_MAP_H +#define UCX_HASH_MAP_H + +#include "map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for an element of a hash map. */ +struct cx_hash_map_element_s; + +/** + * Internal structure for a hash map. + */ +struct cx_hash_map_s { + /** + * Base structure for maps. + */ + struct cx_map_s base; + /** + * The buckets of this map, each containing a linked list of elements. + */ + struct cx_hash_map_element_s **buckets; + /** + * The number of buckets. + */ + size_t bucket_count; +}; + + +/** + * Creates a new hash map with the specified number of buckets. + * + * If \p buckets is zero, an implementation defined default will be used. + * + * If \p elem_size is CX_STORE_POINTERS, the created map will be created as if + * cxMapStorePointers() was called immediately after creation. + * + * @note Iterators provided by this hash map implementation provide the remove operation. + * The index value of an iterator is incremented when the iterator advanced without removal. + * In other words, when the iterator is finished, \c index==size . + * + * @param allocator the allocator to use + * @param itemsize the size of one element + * @param buckets the initial number of buckets in this hash map + * @return a pointer to the new hash map + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxMap *cxHashMapCreate( + const CxAllocator *allocator, + size_t itemsize, + size_t buckets +); + +/** + * Creates a new hash map with a default number of buckets. + * + * If \p elem_size is CX_STORE_POINTERS, the created map will be created as if + * cxMapStorePointers() was called immediately after creation. + * + * @note Iterators provided by this hash map implementation provide the remove operation. + * The index value of an iterator is incremented when the iterator advanced without removal. + * In other words, when the iterator is finished, \c index==size . + * + * @param itemsize the size of one element + * @return a pointer to the new hash map + */ +#define cxHashMapCreateSimple(itemsize) \ + cxHashMapCreate(cxDefaultAllocator, itemsize, 0) + +/** + * Increases the number of buckets, if necessary. + * + * The load threshold is \c 0.75*buckets. If the element count exceeds the load + * threshold, the map will be rehashed. Otherwise, no action is performed and + * this function simply returns 0. + * + * The rehashing process ensures, that the number of buckets is at least + * 2.5 times the element count. So there is enough room for additional + * elements without the need of another soon rehashing. + * + * You can use this function after filling a map to increase access performance. + * + * @note If the specified map is not a hash map, the behavior is undefined. + * + * @param map the map to rehash + * @return zero on success, non-zero if a memory allocation error occurred + */ +__attribute__((__nonnull__)) +int cxMapRehash(CxMap *map); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_HASH_MAP_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/iterator.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,265 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file iterator.h + * \brief Interface for iterator implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_ITERATOR_H +#define UCX_ITERATOR_H + +#include "common.h" + +struct cx_iterator_base_s { + /** + * True iff the iterator points to valid data. + */ + __attribute__ ((__nonnull__)) + bool (*valid)(const void *); + + /** + * Returns a pointer to the current element. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + void *(*current)(const void *); + + /** + * Original implementation in case the function needs to be wrapped. + */ + __attribute__ ((__nonnull__)) + void *(*current_impl)(const void *); + + /** + * Advances the iterator. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + void (*next)(void *); + /** + * Indicates whether this iterator may remove elements. + */ + bool mutating; + /** + * Internal flag for removing the current element when advancing. + */ + bool remove; +}; + +/** + * Declares base attributes for an iterator. + * Must be the first member of an iterator structure. + */ +#define CX_ITERATOR_BASE struct cx_iterator_base_s base + +/** + * Internal iterator struct - use CxIterator. + */ +struct cx_iterator_s { + CX_ITERATOR_BASE; + + /** + * Handle for the current element. + */ + void *elem_handle; + + /** + * Handle for the source collection, if any. + */ + union { + /** + * Access for mutating iterators. + */ + void *m; + /** + * Access for normal iterators. + */ + const void *c; + } src_handle; + + /** + * Field for storing a key-value pair. + * May be used by iterators that iterate over k/v-collections. + */ + struct { + /** + * A pointer to the key. + */ + const void *key; + /** + * A pointer to the value. + */ + void *value; + } kv_data; + + /** + * Field for storing a slot number. + * May be used by iterators that iterate over multi-bucket collections. + */ + size_t slot; + + /** + * If the iterator is position-aware, contains the index of the element in the underlying collection. + * Otherwise, this field is usually uninitialized. + */ + size_t index; + + /** + * The size of an individual element. + */ + size_t elem_size; + + /** + * May contain the total number of elements, if known. + * Shall be set to \c SIZE_MAX when the total number is unknown during iteration. + */ + size_t elem_count; +}; + +/** + * Iterator type. + * + * An iterator points to a certain element in a (possibly unbounded) chain of elements. + * Iterators that are based on collections (which have a defined "first" element), are supposed + * to be "position-aware", which means that they keep track of the current index within the collection. + * + * @note Objects that are pointed to by an iterator are always mutable through that iterator. However, + * any concurrent mutation of the collection other than by this iterator makes this iterator invalid + * and it must not be used anymore. + */ +typedef struct cx_iterator_s CxIterator; + +/** + * Checks if the iterator points to valid data. + * + * This is especially false for past-the-end iterators. + * + * @param iter the iterator + * @return true iff the iterator points to valid data + */ +#define cxIteratorValid(iter) (iter).base.valid(&(iter)) + +/** + * Returns a pointer to the current element. + * + * The behavior is undefined if this iterator is invalid. + * + * @param iter the iterator + * @return a pointer to the current element + */ +#define cxIteratorCurrent(iter) (iter).base.current(&iter) + +/** + * Advances the iterator to the next element. + * + * @param iter the iterator + */ +#define cxIteratorNext(iter) (iter).base.next(&iter) + +/** + * Flags the current element for removal, if this iterator is mutating. + * + * @param iter the iterator + */ +#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating + +/** + * Obtains a reference to an arbitrary iterator. + * + * This is useful for APIs that expect some iterator as an argument. + * + * @param iter the iterator + */ +#define cxIteratorRef(iter) &((iter).base) + +/** + * Loops over an iterator. + * @param type the type of the elements + * @param elem the name of the iteration variable + * @param iter the iterator + */ +#define cx_foreach(type, elem, iter) \ +for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) != NULL ; cxIteratorNext(iter)) + + +/** + * Creates an iterator for the specified plain array. + * + * The \p array can be \c NULL in which case the iterator will be immediately + * initialized such that #cxIteratorValid() returns \c false. + * + * + * @param array a pointer to the array (can be \c NULL) + * @param elem_size the size of one array element + * @param elem_count the number of elements in the array + * @return an iterator for the specified array + */ +__attribute__((__warn_unused_result__)) +CxIterator cxIterator( + const void *array, + size_t elem_size, + size_t elem_count +); + +/** + * Creates a mutating iterator for the specified plain array. + * + * While the iterator is in use, the array may only be altered by removing + * elements through #cxIteratorFlagRemoval(). Every other change to the array + * will bring this iterator to an undefined state. + * + * When \p remove_keeps_order is set to \c false, removing an element will only + * move the last element to the position of the removed element, instead of + * moving all subsequent elements by one. Usually, when the order of elements is + * not important, this parameter should be set to \c false. + * + * The \p array can be \c NULL in which case the iterator will be immediately + * initialized such that #cxIteratorValid() returns \c false. + * + * + * @param array a pointer to the array (can be \c NULL) + * @param elem_size the size of one array element + * @param elem_count the number of elements in the array + * @param remove_keeps_order \c true if the order of elements must be preserved + * when removing an element + * @return an iterator for the specified array + */ +__attribute__((__warn_unused_result__)) +CxIterator cxMutIterator( + void *array, + size_t elem_size, + size_t elem_count, + bool remove_keeps_order +); + +#endif // UCX_ITERATOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/linked_list.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,506 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file linked_list.h + * \brief Linked list implementation. + * \details Also provides several low-level functions for custom linked list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_LINKED_LIST_H +#define UCX_LINKED_LIST_H + +#include "common.h" +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The maximum item size that uses SBO swap instead of relinking. + */ +extern unsigned cx_linked_list_swap_sbo_size; + +/** + * Allocates a linked list for storing elements with \p elem_size bytes each. + * + * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(), if none is given. + * + * @param allocator the allocator for allocating the list nodes + * (if \c NULL the cxDefaultAllocator will be used) + * @param comparator the comparator for the elements + * (if \c NULL, and the list is not storing pointers, sort and find + * functions will not work) + * @param elem_size the size of each element in bytes + * @return the created list + */ +CxList *cxLinkedListCreate( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size +); + +/** + * Allocates a linked list for storing elements with \p elem_size bytes each. + * + * The list will use cxDefaultAllocator and no comparator function. If you want + * to call functions that need a comparator, you must either set one immediately + * after list creation or use cxLinkedListCreate(). + * + * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation and the compare + * function will be automatically set to cx_cmp_ptr(). + * + * @param elem_size the size of each element in bytes + * @return the created list + */ +#define cxLinkedListCreateSimple(elem_size) \ + cxLinkedListCreate(NULL, NULL, elem_size) + +/** + * Finds the node at a certain index. + * + * This function can be used to start at an arbitrary position within the list. + * If the search index is large than the start index, \p loc_advance must denote + * the location of some sort of \c next pointer (i.e. a pointer to the next node). + * But it is also possible that the search index is smaller than the start index + * (e.g. in cases where traversing a list backwards is faster) in which case + * \p loc_advance must denote the location of some sort of \c prev pointer + * (i.e. a pointer to the previous node). + * + * @param start a pointer to the start node + * @param start_index the start index + * @param loc_advance the location of the pointer to advance + * @param index the search index + * @return the node found at the specified index + */ +__attribute__((__nonnull__)) +void *cx_linked_list_at( + const void *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +); + +/** + * Finds the index of an element within a linked list. + * + * @param start a pointer to the start node + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func a compare function to compare \p elem against the node data + * @param elem a pointer to the element to find + * @return the index of the element or a negative value if it could not be found + */ +__attribute__((__nonnull__)) +ssize_t cx_linked_list_find( + const void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + const void *elem +); + +/** + * Finds the node containing an element within a linked list. + * + * @param result a pointer to the memory where the node pointer (or \c NULL if the element + * could not be found) shall be stored to + * @param start a pointer to the start node + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func a compare function to compare \p elem against the node data + * @param elem a pointer to the element to find + * @return the index of the element or a negative value if it could not be found + */ +__attribute__((__nonnull__)) +ssize_t cx_linked_list_find_node( + void **result, + const void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + const void *elem +); + +/** + * Finds the first node in a linked list. + * + * The function starts with the pointer denoted by \p node and traverses the list + * along a prev pointer whose location within the node struct is + * denoted by \p loc_prev. + * + * @param node a pointer to a node in the list + * @param loc_prev the location of the \c prev pointer + * @return a pointer to the first node + */ +__attribute__((__nonnull__)) +void *cx_linked_list_first( + const void *node, + ptrdiff_t loc_prev +); + +/** + * Finds the last node in a linked list. + * + * The function starts with the pointer denoted by \p node and traverses the list + * along a next pointer whose location within the node struct is + * denoted by \p loc_next. + * + * @param node a pointer to a node in the list + * @param loc_next the location of the \c next pointer + * @return a pointer to the last node + */ +__attribute__((__nonnull__)) +void *cx_linked_list_last( + const void *node, + ptrdiff_t loc_next +); + +/** + * Finds the predecessor of a node in case it is not linked. + * + * \remark If \p node is not contained in the list starting with \p begin, the behavior is undefined. + * + * @param begin the node where to start the search + * @param loc_next the location of the \c next pointer + * @param node the successor of the node to find + * @return the node or \c NULL if \p node has no predecessor + */ +__attribute__((__nonnull__)) +void *cx_linked_list_prev( + const void *begin, + ptrdiff_t loc_next, + const void *node +); + +/** + * Adds a new node to a linked list. + * The node must not be part of any list already. + * + * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be appended + */ +__attribute__((__nonnull__(5))) +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +); + +/** + * Prepends a new node to a linked list. + * The node must not be part of any list already. + * + * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be prepended + */ +__attribute__((__nonnull__(5))) +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +); + +/** + * Links two nodes. + * + * @param left the new predecessor of \p right + * @param right the new successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +__attribute__((__nonnull__)) +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Unlinks two nodes. + * + * If right is not the successor of left, the behavior is undefined. + * + * @param left the predecessor of \p right + * @param right the successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +__attribute__((__nonnull__)) +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Inserts a new node after a given node of a linked list. + * The new node must not be part of any list already. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL if you want to prepend the node to the list) + * @param new_node a pointer to the node that shall be inserted + */ +__attribute__((__nonnull__(6))) +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +); + +/** + * Inserts a chain of nodes after a given node of a linked list. + * The chain must not be part of any list already. + * + * If you do not explicitly specify the end of the chain, it will be determined by traversing + * the \c next pointer. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need + * to provide a valid \p loc_prev location. + * Then the chain will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL to prepend the chain to the list) + * @param insert_begin a pointer to the first node of the chain that shall be inserted + * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined) + */ +__attribute__((__nonnull__(6))) +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +); + +/** + * Inserts a node into a sorted linked list. + * The new node must not be part of any list already. + * + * If the list starting with the node pointed to by \p begin is not sorted + * already, the behavior is undefined. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be inserted + * @param cmp_func a compare function that will receive the node pointers + */ +__attribute__((__nonnull__(1, 5, 6))) +void cx_linked_list_insert_sorted( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node, + cx_compare_func cmp_func +); + +/** + * Inserts a chain of nodes into a sorted linked list. + * The chain must not be part of any list already. + * + * If either the list starting with the node pointed to by \p begin or the list + * starting with \p insert_begin is not sorted, the behavior is undefined. + * + * \attention In contrast to cx_linked_list_insert_chain(), the source chain + * will be broken and inserted into the target list so that the resulting list + * will be sorted according to \p cmp_func. That means, each node in the source + * chain may be re-linked with nodes from the target list. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param insert_begin a pointer to the first node of the chain that shall be inserted + * @param cmp_func a compare function that will receive the node pointers + */ +__attribute__((__nonnull__(1, 5, 6))) +void cx_linked_list_insert_sorted_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *insert_begin, + cx_compare_func cmp_func +); + +/** + * Removes a node from the linked list. + * + * If the node to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end) + * addresses are provided, the pointers are adjusted accordingly. + * + * The following combinations of arguments are valid (more arguments are optional): + * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) + * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance) + * + * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used + * to traverse to a former adjacent node in the list. + * + * @param begin a pointer to the begin node pointer (optional) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node to remove + */ +__attribute__((__nonnull__(5))) +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +); + + +/** + * Determines the size of a linked list starting with \p node. + * @param node the first node + * @param loc_next the location of the \c next pointer within the node struct + * @return the size of the list or zero if \p node is \c NULL + */ +size_t cx_linked_list_size( + const void *node, + ptrdiff_t loc_next +); + +/** + * Sorts a linked list based on a comparison function. + * + * This function can work with linked lists of the following structure: + * \code + * typedef struct node node; + * struct node { + * node* prev; + * node* next; + * my_payload data; + * } + * \endcode + * + * @note This is a recursive function with at most logarithmic recursion depth. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if not present) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func the compare function defining the sort order + */ +__attribute__((__nonnull__(1, 6))) +void cx_linked_list_sort( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + cx_compare_func cmp_func +); + + +/** + * Compares two lists element wise. + * + * \note Both list must have the same structure. + * + * @param begin_left the begin of the left list (\c NULL denotes an empty list) + * @param begin_right the begin of the right list (\c NULL denotes an empty list) + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func the function to compare the elements + * @return the first non-zero result of invoking \p cmp_func or: negative if the left list is smaller than the + * right list, positive if the left list is larger than the right list, zero if both lists are equal. + */ +__attribute__((__nonnull__(5))) +int cx_linked_list_compare( + const void *begin_left, + const void *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func +); + +/** + * Reverses the order of the nodes in a linked list. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +__attribute__((__nonnull__(1))) +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_LINKED_LIST_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/list.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,800 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file list.h + * \brief Interface for list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_LIST_H +#define UCX_LIST_H + +#include "common.h" +#include "collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * List class type. + */ +typedef struct cx_list_class_s cx_list_class; + +/** + * Structure for holding the base data of a list. + */ +struct cx_list_s { + CX_COLLECTION_BASE; + /** + * The list class definition. + */ + const cx_list_class *cl; + /** + * The actual implementation in case the list class is delegating. + */ + const cx_list_class *climpl; +}; + +/** + * The class definition for arbitrary lists. + */ +struct cx_list_class_s { + /** + * Destructor function. + * + * Implementations SHALL invoke the content destructor functions if provided + * and SHALL deallocate the list memory. + */ + void (*destructor)(struct cx_list_s *list); + + /** + * Member function for inserting a single element. + * Implementors SHOULD see to performant implementations for corner cases. + */ + int (*insert_element)( + struct cx_list_s *list, + size_t index, + const void *data + ); + + /** + * Member function for inserting multiple elements. + * Implementors SHOULD see to performant implementations for corner cases. + * @see cx_list_default_insert_array() + */ + size_t (*insert_array)( + struct cx_list_s *list, + size_t index, + const void *data, + size_t n + ); + + /** + * Member function for inserting sorted elements into a sorted list. + * + * @see cx_list_default_insert_sorted() + */ + size_t (*insert_sorted)( + struct cx_list_s *list, + const void *sorted_data, + size_t n + ); + + /** + * Member function for inserting an element relative to an iterator position. + */ + int (*insert_iter)( + struct cx_iterator_s *iter, + const void *elem, + int prepend + ); + + /** + * Member function for removing an element. + */ + int (*remove)( + struct cx_list_s *list, + size_t index + ); + + /** + * Member function for removing all elements. + */ + void (*clear)(struct cx_list_s *list); + + /** + * Member function for swapping two elements. + * @see cx_list_default_swap() + */ + int (*swap)( + struct cx_list_s *list, + size_t i, + size_t j + ); + + /** + * Member function for element lookup. + */ + void *(*at)( + const struct cx_list_s *list, + size_t index + ); + + /** + * Member function for finding and optionally removing an element. + */ + ssize_t (*find_remove)( + struct cx_list_s *list, + const void *elem, + bool remove + ); + + /** + * Member function for sorting the list in-place. + * @see cx_list_default_sort() + */ + void (*sort)(struct cx_list_s *list); + + /** + * Optional member function for comparing this list + * to another list of the same type. + * If set to \c NULL, comparison won't be optimized. + */ + int (*compare)( + const struct cx_list_s *list, + const struct cx_list_s *other + ); + + /** + * Member function for reversing the order of the items. + */ + void (*reverse)(struct cx_list_s *list); + + /** + * Member function for returning an iterator pointing to the specified index. + */ + struct cx_iterator_s (*iterator)( + const struct cx_list_s *list, + size_t index, + bool backward + ); +}; + +/** + * Default implementation of an array insert. + * + * This function uses the element insert function for each element of the array. + * + * Use this in your own list class if you do not want to implement an optimized + * version for your list. + * + * @param list the list + * @param index the index where to insert the data + * @param data a pointer to the array of data to insert + * @param n the number of elements to insert + * @return the number of elements actually inserted + */ +__attribute__((__nonnull__)) +size_t cx_list_default_insert_array( + struct cx_list_s *list, + size_t index, + const void *data, + size_t n +); + +/** + * Default implementation of a sorted insert. + * + * This function uses the array insert function to insert consecutive groups + * of sorted data. + * + * The source data \em must already be sorted wrt. the list's compare function. + * + * Use this in your own list class if you do not want to implement an optimized + * version for your list. + * + * @param list the list + * @param sorted_data a pointer to the array of pre-sorted data to insert + * @param n the number of elements to insert + * @return the number of elements actually inserted + */ +__attribute__((__nonnull__)) +size_t cx_list_default_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +); + +/** + * Default unoptimized sort implementation. + * + * This function will copy all data to an array, sort the array with standard + * qsort, and then copy the data back to the list memory. + * + * Use this in your own list class if you do not want to implement an optimized + * version for your list. + * + * @param list the list that shall be sorted + */ +__attribute__((__nonnull__)) +void cx_list_default_sort(struct cx_list_s *list); + +/** + * Default unoptimized swap implementation. + * + * Use this in your own list class if you do not want to implement an optimized + * version for your list. + * + * @param list the list in which to swap + * @param i index of one element + * @param j index of the other element + * @return zero on success, non-zero when indices are out of bounds or memory + * allocation for the temporary buffer fails + */ +__attribute__((__nonnull__)) +int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); + +/** + * Common type for all list implementations. + */ +typedef struct cx_list_s CxList; + +/** + * Advises the list to store copies of the objects (default mode of operation). + * + * Retrieving objects from this list will yield pointers to the copies stored + * within this list. + * + * @param list the list + * @see cxListStorePointers() + */ +__attribute__((__nonnull__)) +void cxListStoreObjects(CxList *list); + +/** + * Advises the list to only store pointers to the objects. + * + * Retrieving objects from this list will yield the original pointers stored. + * + * @note This function forcibly sets the element size to the size of a pointer. + * Invoking this function on a non-empty list that already stores copies of + * objects is undefined. + * + * @param list the list + * @see cxListStoreObjects() + */ +__attribute__((__nonnull__)) +void cxListStorePointers(CxList *list); + +/** + * Returns true, if this list is storing pointers instead of the actual data. + * + * @param list + * @return true, if this list is storing pointers + * @see cxListStorePointers() + */ +__attribute__((__nonnull__)) +static inline bool cxListIsStoringPointers(const CxList *list) { + return list->collection.store_pointer; +} + +/** + * Returns the number of elements currently stored in the list. + * + * @param list the list + * @return the number of currently stored elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListSize(const CxList *list) { + return list->collection.size; +} + +/** + * Adds an item to the end of the list. + * + * @param list the list + * @param elem a pointer to the element to add + * @return zero on success, non-zero on memory allocation failure + * @see cxListAddArray() + */ +__attribute__((__nonnull__)) +static inline int cxListAdd( + CxList *list, + const void *elem +) { + return list->cl->insert_element(list, list->collection.size, elem); +} + +/** + * Adds multiple items to the end of the list. + * + * This method is more efficient than invoking cxListAdd() multiple times. + * + * If there is not enough memory to add all elements, the returned value is + * less than \p n. + * + * If this list is storing pointers instead of objects \p array is expected to + * be an array of pointers. + * + * @param list the list + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListAddArray( + CxList *list, + const void *array, + size_t n +) { + return list->cl->insert_array(list, list->collection.size, array, n); +} + +/** + * Inserts an item at the specified index. + * + * If \p index equals the list \c size, this is effectively cxListAdd(). + * + * @param list the list + * @param index the index the element shall have + * @param elem a pointer to the element to add + * @return zero on success, non-zero on memory allocation failure + * or when the index is out of bounds + * @see cxListInsertAfter() + * @see cxListInsertBefore() + */ +__attribute__((__nonnull__)) +static inline int cxListInsert( + CxList *list, + size_t index, + const void *elem +) { + return list->cl->insert_element(list, index, elem); +} + +/** + * Inserts an item into a sorted list. + * + * @param list the list + * @param elem a pointer to the element to add + * @return zero on success, non-zero on memory allocation failure + */ +__attribute__((__nonnull__)) +static inline int cxListInsertSorted( + CxList *list, + const void *elem +) { + const void *data = list->collection.store_pointer ? &elem : elem; + return list->cl->insert_sorted(list, data, 1) == 0; +} + +/** + * Inserts multiple items to the list at the specified index. + * If \p index equals the list size, this is effectively cxListAddArray(). + * + * This method is usually more efficient than invoking cxListInsert() + * multiple times. + * + * If there is not enough memory to add all elements, the returned value is + * less than \p n. + * + * If this list is storing pointers instead of objects \p array is expected to + * be an array of pointers. + * + * @param list the list + * @param index the index where to add the elements + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListInsertArray( + CxList *list, + size_t index, + const void *array, + size_t n +) { + return list->cl->insert_array(list, index, array, n); +} + +/** + * Inserts a sorted array into a sorted list. + * + * This method is usually more efficient than inserting each element separately, + * because consecutive chunks of sorted data are inserted in one pass. + * + * If there is not enough memory to add all elements, the returned value is + * less than \p n. + * + * If this list is storing pointers instead of objects \p array is expected to + * be an array of pointers. + * + * @param list the list + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListInsertSortedArray( + CxList *list, + const void *array, + size_t n +) { + return list->cl->insert_sorted(list, array, n); +} + +/** + * Inserts an element after the current location of the specified iterator. + * + * The used iterator remains operational, but all other active iterators should + * be considered invalidated. + * + * If \p iter is not a list iterator, the behavior is undefined. + * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * + * @param iter an iterator + * @param elem the element to insert + * @return zero on success, non-zero on memory allocation failure + * @see cxListInsert() + * @see cxListInsertBefore() + */ +__attribute__((__nonnull__)) +static inline int cxListInsertAfter( + CxIterator *iter, + const void *elem +) { + return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 0); +} + +/** + * Inserts an element before the current location of the specified iterator. + * + * The used iterator remains operational, but all other active iterators should + * be considered invalidated. + * + * If \p iter is not a list iterator, the behavior is undefined. + * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * + * @param iter an iterator + * @param elem the element to insert + * @return zero on success, non-zero on memory allocation failure + * @see cxListInsert() + * @see cxListInsertAfter() + */ +__attribute__((__nonnull__)) +static inline int cxListInsertBefore( + CxIterator *iter, + const void *elem +) { + return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 1); +} + +/** + * Removes the element at the specified index. + * + * If an element destructor function is specified, it is called before + * removing the element. + * + * @param list the list + * @param index the index of the element + * @return zero on success, non-zero if the index is out of bounds + */ +__attribute__((__nonnull__)) +static inline int cxListRemove( + CxList *list, + size_t index +) { + return list->cl->remove(list, index); +} + +/** + * Removes all elements from this list. + * + * If an element destructor function is specified, it is called for each + * element before removing them. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListClear(CxList *list) { + list->cl->clear(list); +} + +/** + * Swaps two items in the list. + * + * Implementations should only allocate temporary memory for the swap, if + * it is necessary. + * + * @param list the list + * @param i the index of the first element + * @param j the index of the second element + * @return zero on success, non-zero if one of the indices is out of bounds + */ +__attribute__((__nonnull__)) +static inline int cxListSwap( + CxList *list, + size_t i, + size_t j +) { + return list->cl->swap(list, i, j); +} + +/** + * Returns a pointer to the element at the specified index. + * + * @param list the list + * @param index the index of the element + * @return a pointer to the element or \c NULL if the index is out of bounds + */ +__attribute__((__nonnull__)) +static inline void *cxListAt( + CxList *list, + size_t index +) { + return list->cl->at(list, index); +} + +/** + * Returns an iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListIteratorAt( + const CxList *list, + size_t index +) { + return list->cl->iterator(list, index, false); +} + +/** + * Returns a backwards iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListBackwardsIteratorAt( + const CxList *list, + size_t index +) { + return list->cl->iterator(list, index, true); +} + +/** + * Returns a mutating iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxIterator cxListMutIteratorAt( + CxList *list, + size_t index +); + +/** + * Returns a mutating backwards iterator pointing to the item at the + * specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxIterator cxListMutBackwardsIteratorAt( + CxList *list, + size_t index +); + +/** + * Returns an iterator pointing to the first item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListIterator(const CxList *list) { + return list->cl->iterator(list, 0, false); +} + +/** + * Returns a mutating iterator pointing to the first item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListMutIterator(CxList *list) { + return cxListMutIteratorAt(list, 0); +} + + +/** + * Returns a backwards iterator pointing to the last item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListBackwardsIterator(const CxList *list) { + return list->cl->iterator(list, list->collection.size - 1, true); +} + +/** + * Returns a mutating backwards iterator pointing to the last item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListMutBackwardsIterator(CxList *list) { + return cxListMutBackwardsIteratorAt(list, list->collection.size - 1); +} + +/** + * Returns the index of the first element that equals \p elem. + * + * Determining equality is performed by the list's comparator function. + * + * @param list the list + * @param elem the element to find + * @return the index of the element or a negative + * value when the element is not found + */ +__attribute__((__nonnull__)) +static inline ssize_t cxListFind( + const CxList *list, + const void *elem +) { + return list->cl->find_remove((CxList*)list, elem, false); +} + +/** + * Removes and returns the index of the first element that equals \p elem. + * + * Determining equality is performed by the list's comparator function. + * + * @param list the list + * @param elem the element to find and remove + * @return the index of the now removed element or a negative + * value when the element is not found or could not be removed + */ +__attribute__((__nonnull__)) +static inline ssize_t cxListFindRemove( + CxList *list, + const void *elem +) { + return list->cl->find_remove(list, elem, true); +} + +/** + * Sorts the list in-place. + * + * \remark The underlying sort algorithm is implementation defined. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListSort(CxList *list) { + list->cl->sort(list); +} + +/** + * Reverses the order of the items. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListReverse(CxList *list) { + list->cl->reverse(list); +} + +/** + * Compares a list to another list of the same type. + * + * First, the list sizes are compared. + * If they match, the lists are compared element-wise. + * + * @param list the list + * @param other the list to compare to + * @return zero, if both lists are equal element wise, + * negative if the first list is smaller, positive if the first list is larger + */ +__attribute__((__nonnull__)) +int cxListCompare( + const CxList *list, + const CxList *other +); + +/** + * Deallocates the memory of the specified list structure. + * + * Also calls content a destructor function, depending on the configuration + * in CxList.content_destructor_type. + * + * This function itself is a destructor function for the CxList. + * + * @param list the list which shall be destroyed + */ +__attribute__((__nonnull__)) +void cxListDestroy(CxList *list); + +/** + * A shared instance of an empty list. + * + * Writing to that list is undefined. + */ +extern CxList * const cxEmptyList; + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_LIST_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/map.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,1158 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file map.h + * \brief Interface for map implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_MAP_H +#define UCX_MAP_H + +#include "common.h" +#include "collection.h" +#include "string.h" +#include "hash_key.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Type for the UCX map. */ +typedef struct cx_map_s CxMap; + +/** Type for a map entry. */ +typedef struct cx_map_entry_s CxMapEntry; + +/** Type for map class definitions. */ +typedef struct cx_map_class_s cx_map_class; + +/** Structure for the UCX map. */ +struct cx_map_s { + /** + * Base attributes. + */ + CX_COLLECTION_BASE; + /** The map class definition. */ + cx_map_class *cl; +}; + +/** + * The type of iterator for a map. + */ +enum cx_map_iterator_type { + /** + * Iterates over key/value pairs. + */ + CX_MAP_ITERATOR_PAIRS, + /** + * Iterates over keys only. + */ + CX_MAP_ITERATOR_KEYS, + /** + * Iterates over values only. + */ + CX_MAP_ITERATOR_VALUES +}; + +/** + * The class definition for arbitrary maps. + */ +struct cx_map_class_s { + /** + * Deallocates the entire memory. + */ + __attribute__((__nonnull__)) + void (*destructor)(struct cx_map_s *map); + + /** + * Removes all elements. + */ + __attribute__((__nonnull__)) + void (*clear)(struct cx_map_s *map); + + /** + * Add or overwrite an element. + */ + __attribute__((__nonnull__)) + int (*put)( + CxMap *map, + CxHashKey key, + void *value + ); + + /** + * Returns an element. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + void *(*get)( + const CxMap *map, + CxHashKey key + ); + + /** + * Removes an element. + */ + __attribute__((__nonnull__)) + void *(*remove)( + CxMap *map, + CxHashKey key, + bool destroy + ); + + /** + * Creates an iterator for this map. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type); +}; + +/** + * A map entry. + */ +struct cx_map_entry_s { + /** + * A pointer to the key. + */ + const CxHashKey *key; + /** + * A pointer to the value. + */ + void *value; +}; + +/** + * A shared instance of an empty map. + * + * Writing to that map is undefined. + */ +extern CxMap *const cxEmptyMap; + +/** + * Advises the map to store copies of the objects (default mode of operation). + * + * Retrieving objects from this map will yield pointers to the copies stored + * within this list. + * + * @param map the map + * @see cxMapStorePointers() + */ +__attribute__((__nonnull__)) +static inline void cxMapStoreObjects(CxMap *map) { + map->collection.store_pointer = false; +} + +/** + * Advises the map to only store pointers to the objects. + * + * Retrieving objects from this list will yield the original pointers stored. + * + * @note This function forcibly sets the element size to the size of a pointer. + * Invoking this function on a non-empty map that already stores copies of + * objects is undefined. + * + * @param map the map + * @see cxMapStoreObjects() + */ +__attribute__((__nonnull__)) +static inline void cxMapStorePointers(CxMap *map) { + map->collection.store_pointer = true; + map->collection.elem_size = sizeof(void *); +} + +/** + * Returns true, if this map is storing pointers instead of the actual data. + * + * @param map + * @return true, if this map is storing pointers + * @see cxMapStorePointers() + */ +__attribute__((__nonnull__)) +static inline bool cxMapIsStoringPointers(const CxMap *map) { + return map->collection.store_pointer; +} + +/** + * Deallocates the memory of the specified map. + * + * @param map the map to be destroyed + */ +__attribute__((__nonnull__)) +static inline void cxMapDestroy(CxMap *map) { + map->cl->destructor(map); +} + + +/** + * Clears a map by removing all elements. + * + * @param map the map to be cleared + */ +__attribute__((__nonnull__)) +static inline void cxMapClear(CxMap *map) { + map->cl->clear(map); +} + +/** + * Returns the number of elements in this map. + * + * @param map the map + * @return the number of stored elements + */ +__attribute__((__nonnull__)) +static inline size_t cxMapSize(const CxMap *map) { + return map->collection.size; +} + + +// TODO: set-like map operations (union, intersect, difference) + +/** + * Creates a value iterator for a map. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored values + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIteratorValues(const CxMap *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); +} + +/** + * Creates a key iterator for a map. + * + * The elements of the iterator are keys of type CxHashKey. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored keys + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIteratorKeys(const CxMap *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); +} + +/** + * Creates an iterator for a map. + * + * The elements of the iterator are key/value pairs of type CxMapEntry. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored entries + * @see cxMapIteratorKeys() + * @see cxMapIteratorValues() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIterator(const CxMap *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); +} + + +/** + * Creates a mutating iterator over the values of a map. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored values + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxIterator cxMapMutIteratorValues(CxMap *map); + +/** + * Creates a mutating iterator over the keys of a map. + * + * The elements of the iterator are keys of type CxHashKey. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored keys + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxIterator cxMapMutIteratorKeys(CxMap *map); + +/** + * Creates a mutating iterator for a map. + * + * The elements of the iterator are key/value pairs of type CxMapEntry. + * + * \note An iterator iterates over all elements successively. Therefore the order + * highly depends on the map implementation and may change arbitrarily when the contents change. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored entries + * @see cxMapMutIteratorKeys() + * @see cxMapMutIteratorValues() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxIterator cxMapMutIterator(CxMap *map); + +#ifdef __cplusplus +} // end the extern "C" block here, because we want to start overloading + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + CxHashKey const &key, + void *value +) { + return map->cl->put(map, key, value); +} + + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + cxstring const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + cxmutstr const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + const char *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(key), value); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + const CxMap *map, + CxHashKey const &key +) { + return map->cl->get(map, key); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + const CxMap *map, + cxstring const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + const CxMap *map, + cxmutstr const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + const CxMap *map, + const char *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + const char *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + const char *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + const char *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->collection.store_pointer); +} + +#else // __cplusplus + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put( + CxMap *map, + CxHashKey key, + void *value +) { + return map->cl->put(map, key, value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_cxstr( + CxMap *map, + cxstring key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_mustr( + CxMap *map, + cxmutstr key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_str( + CxMap *map, + const char *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +#define cxMapPut(map, key, value) _Generic((key), \ + CxHashKey: cx_map_put, \ + cxstring: cx_map_put_cxstr, \ + cxmutstr: cx_map_put_mustr, \ + char*: cx_map_put_str, \ + const char*: cx_map_put_str) \ + (map, key, value) + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get( + const CxMap *map, + CxHashKey key +) { + return map->cl->get(map, key); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_cxstr( + const CxMap *map, + cxstring key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_mustr( + const CxMap *map, + cxmutstr key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_str( + const CxMap *map, + const char *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +#define cxMapGet(map, key) _Generic((key), \ + CxHashKey: cx_map_get, \ + cxstring: cx_map_get_cxstr, \ + cxmutstr: cx_map_get_mustr, \ + char*: cx_map_get_str, \ + const char*: cx_map_get_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_str( + CxMap *map, + const char *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +#define cxMapRemove(map, key) _Generic((key), \ + CxHashKey: cx_map_remove, \ + cxstring: cx_map_remove_cxstr, \ + cxmutstr: cx_map_remove_mustr, \ + char*: cx_map_remove_str, \ + const char*: cx_map_remove_str) \ + (map, key) + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_str( + CxMap *map, + const char *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +#define cxMapDetach(map, key) _Generic((key), \ + CxHashKey: cx_map_detach, \ + cxstring: cx_map_detach_cxstr, \ + cxmutstr: cx_map_detach_mustr, \ + char*: cx_map_detach_str, \ + const char*: cx_map_detach_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_cxstr( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_mustr( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_str( + CxMap *map, + const char *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->collection.store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +#define cxMapRemoveAndGet(map, key) _Generic((key), \ + CxHashKey: cx_map_remove_and_get, \ + cxstring: cx_map_remove_and_get_cxstr, \ + cxmutstr: cx_map_remove_and_get_mustr, \ + char*: cx_map_remove_and_get_str, \ + const char*: cx_map_remove_and_get_str) \ + (map, key) + +#endif // __cplusplus + +#endif // UCX_MAP_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/mempool.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,148 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file mempool.h + * \brief Interface for memory pool implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_MEMPOOL_H +#define UCX_MEMPOOL_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for pooled memory. */ +struct cx_mempool_memory_s; + +/** + * The basic structure of a memory pool. + * Should be the first member of an actual memory pool implementation. + */ +struct cx_mempool_s { + /** The provided allocator. */ + const CxAllocator *allocator; + + /** + * A destructor that shall be automatically registered for newly allocated memory. + * This destructor MUST NOT free the memory. + */ + cx_destructor_func auto_destr; + + /** Array of pooled memory. */ + struct cx_mempool_memory_s **data; + + /** Number of pooled memory items. */ + size_t size; + + /** Memory pool capacity. */ + size_t capacity; +}; + +/** + * Common type for all memory pool implementations. + */ +typedef struct cx_mempool_s CxMempool; + +/** + * Creates an array-based memory pool with a shared destructor function. + * + * This destructor MUST NOT free the memory. + * + * @param capacity the initial capacity of the pool + * @param destr the destructor function to use for allocated memory + * @return the created memory pool or \c NULL if allocation failed + */ +__attribute__((__warn_unused_result__)) +CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr); + +/** + * Creates a basic array-based memory pool. + * + * @param capacity the initial capacity of the pool + * @return the created memory pool or \c NULL if allocation failed + */ +__attribute__((__warn_unused_result__)) +static inline CxMempool *cxBasicMempoolCreate(size_t capacity) { + return cxMempoolCreate(capacity, NULL); +} + +/** + * Destroys a memory pool and frees the managed memory. + * + * @param pool the memory pool to destroy + */ +__attribute__((__nonnull__)) +void cxMempoolDestroy(CxMempool *pool); + +/** + * Sets the destructor function for a specific allocated memory object. + * + * If the memory is not managed by a UCX memory pool, the behavior is undefined. + * The destructor MUST NOT free the memory. + * + * @param memory the object allocated in the pool + * @param fnc the destructor function + */ +__attribute__((__nonnull__)) +void cxMempoolSetDestructor( + void *memory, + cx_destructor_func fnc +); + +/** + * Registers foreign memory with this pool. + * + * The destructor, in contrast to memory allocated by the pool, MUST free the memory. + * + * A small portion of memory will be allocated to register the information in the pool. + * If that allocation fails, this function will return non-zero. + * + * @param pool the pool + * @param memory the object allocated in the pool + * @param destr the destructor function + * @return zero on success, non-zero on failure + */ +__attribute__((__nonnull__)) +int cxMempoolRegister( + CxMempool *pool, + void *memory, + cx_destructor_func destr +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_MEMPOOL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/printf.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,335 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file printf.h + * \brief Wrapper for write functions with a printf-like interface. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_PRINTF_H +#define UCX_PRINTF_H + +#include "common.h" +#include "string.h" +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * The maximum string length that fits into stack memory. + */ +extern unsigned const cx_printf_sbo_size; + +/** + * A \c fprintf like function which writes the output to a stream by + * using a write_func. + * + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ... additional arguments + * @return the total number of bytes written + */ +__attribute__((__nonnull__(1, 2, 3), __format__(printf, 3, 4))) +int cx_fprintf( + void *stream, + cx_write_func wfc, + const char *fmt, + ... +); + +/** + * A \c vfprintf like function which writes the output to a stream by + * using a write_func. + * + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ap argument list + * @return the total number of bytes written + * @see cx_fprintf() + */ +__attribute__((__nonnull__)) +int cx_vfprintf( + void *stream, + cx_write_func wfc, + const char *fmt, + va_list ap +); + +/** + * A \c asprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param allocator the CxAllocator used for allocating the string + * @param fmt format string + * @param ... additional arguments + * @return the formatted string + * @see cx_strfree_a() + */ +__attribute__((__nonnull__(1, 2), __format__(printf, 2, 3))) +cxmutstr cx_asprintf_a( + const CxAllocator *allocator, + const char *fmt, + ... +); + +/** + * A \c asprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param fmt format string + * @param ... additional arguments + * @return the formatted string + * @see cx_strfree() + */ +#define cx_asprintf(fmt, ...) \ + cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) + +/** +* A \c vasprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param allocator the CxAllocator used for allocating the string + * @param fmt format string + * @param ap argument list + * @return the formatted string + * @see cx_asprintf_a() + */ +__attribute__((__nonnull__)) +cxmutstr cx_vasprintf_a( + const CxAllocator *allocator, + const char *fmt, + va_list ap +); + +/** +* A \c vasprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param fmt format string + * @param ap argument list + * @return the formatted string + * @see cx_asprintf() + */ +#define cx_vasprintf(fmt, ap) cx_vasprintf_a(cxDefaultAllocator, fmt, ap) + +/** + * A \c printf like function which writes the output to a CxBuffer. + * + * @param buffer a pointer to the buffer the data is written to + * @param fmt the format string + * @param ... additional arguments + * @return the total number of bytes written + * @see ucx_fprintf() + */ +#define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \ + (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__) + + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param str a pointer to the string buffer + * @param len a pointer to the length of the buffer + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +#define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__) + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \attention The original buffer MUST have been allocated with the same allocator! + * + * @param alloc the allocator to use + * @param str a pointer to the string buffer + * @param len a pointer to the length of the buffer + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +__attribute__((__nonnull__(1, 2, 3, 4), __format__(printf, 4, 5))) +int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, ... ); + + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param str a pointer to the string buffer + * @param len a pointer to the length of the buffer + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +#define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap) + +/** + * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \attention The original buffer MUST have been allocated with the same allocator! + * + * @param alloc the allocator to use + * @param str a pointer to the string buffer + * @param len a pointer to the length of the buffer + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +__attribute__((__nonnull__)) +int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap); + + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param buf a pointer to the buffer + * @param len a pointer to the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +#define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__) + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param alloc the allocator to use + * @param buf a pointer to the buffer + * @param len a pointer to the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ... additional arguments + * @return the length of produced string + */ +__attribute__((__nonnull__(1, 2, 4, 5), __format__(printf, 5, 6))) +int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ... ); + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param buf a pointer to the buffer + * @param len a pointer to the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +#define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap) + +/** + * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * + * The size of the buffer will be updated in \p len when necessary. + * + * The location of the resulting string will \em always be stored to \p str. When the buffer + * was sufficiently large, \p buf itself will be stored to the location of \p str. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * \remark When a new string needed to be allocated, the contents of \p buf will be + * poisoned after the call, because this function tries to produce the string in \p buf, first. + * + * @param alloc the allocator to use + * @param buf a pointer to the buffer + * @param len a pointer to the length of the buffer + * @param str a pointer to the location + * @param fmt the format string + * @param ap argument list + * @return the length of produced string + */ +__attribute__((__nonnull__)) +int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_PRINTF_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/string.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,1082 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file string.h + * \brief Strings that know their length. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_STRING_H +#define UCX_STRING_H + +#include "common.h" +#include "allocator.h" + +/** + * The maximum length of the "needle" in cx_strstr() that can use SBO. + */ +extern unsigned const cx_strstr_sbo_size; + +/** + * The UCX string structure. + */ +struct cx_mutstr_s { + /** + * A pointer to the string. + * \note The string is not necessarily \c NULL terminated. + * Always use the length. + */ + char *ptr; + /** The length of the string */ + size_t length; +}; + +/** + * A mutable string. + */ +typedef struct cx_mutstr_s cxmutstr; + +/** + * The UCX string structure for immutable (constant) strings. + */ +struct cx_string_s { + /** + * A pointer to the immutable string. + * \note The string is not necessarily \c NULL terminated. + * Always use the length. + */ + const char *ptr; + /** The length of the string */ + size_t length; +}; + +/** + * An immutable string. + */ +typedef struct cx_string_s cxstring; + +/** + * Context for string tokenizing. + */ +struct cx_strtok_ctx_s { + /** + * The string to tokenize. + */ + cxstring str; + /** + * The primary delimiter. + */ + cxstring delim; + /** + * Optional array of more delimiters. + */ + const cxstring *delim_more; + /** + * Length of the array containing more delimiters. + */ + size_t delim_more_count; + /** + * Position of the currently active token in the source string. + */ + size_t pos; + /** + * Position of next delimiter in the source string. + * + * If the tokenizer has not yet returned a token, the content of this field + * is undefined. If the tokenizer reached the end of the string, this field + * contains the length of the source string. + */ + size_t delim_pos; + /** + * The position of the next token in the source string. + */ + size_t next_pos; + /** + * The number of already found tokens. + */ + size_t found; + /** + * The maximum number of tokens that shall be returned. + */ + size_t limit; +}; + +/** + * A string tokenizing context. + */ +typedef struct cx_strtok_ctx_s CxStrtokCtx; + +#ifdef __cplusplus +extern "C" { + +/** + * A literal initializer for an UCX string structure. + * + * @param literal the string literal + */ +#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1} + +#else // __cplusplus + +/** + * A literal initializer for an UCX string structure. + * + * The argument MUST be a string (const char*) \em literal. + * + * @param literal the string literal + */ +#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1} + +#endif + + +/** + * Wraps a mutable string that must be zero-terminated. + * + * The length is implicitly inferred by using a call to \c strlen(). + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a constant string, use cx_str(). + * + * @param cstring the string to wrap, must be zero-terminated + * @return the wrapped string + * + * @see cx_mutstrn() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_mutstr(char *cstring); + +/** + * Wraps a string that does not need to be zero-terminated. + * + * The argument may be \c NULL if the length is zero. + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a constant string, use cx_strn(). + * + * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param length the length of the string + * @return the wrapped string + * + * @see cx_mutstr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_mutstrn( + char *cstring, + size_t length +); + +/** + * Wraps a string that must be zero-terminated. + * + * The length is implicitly inferred by using a call to \c strlen(). + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a non-constant string, use cx_mutstr(). + * + * @param cstring the string to wrap, must be zero-terminated + * @return the wrapped string + * + * @see cx_strn() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxstring cx_str(const char *cstring); + + +/** + * Wraps a string that does not need to be zero-terminated. + * + * The argument may be \c NULL if the length is zero. + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a non-constant string, use cx_mutstrn(). + * + * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param length the length of the string + * @return the wrapped string + * + * @see cx_str() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strn( + const char *cstring, + size_t length +); + +/** +* Casts a mutable string to an immutable string. +* +* \note This is not seriously a cast. Instead you get a copy +* of the struct with the desired pointer type. Both structs still +* point to the same location, though! +* +* @param str the mutable string to cast +* @return an immutable copy of the string pointer +*/ +__attribute__((__warn_unused_result__)) +cxstring cx_strcast(cxmutstr str); + +/** + * Passes the pointer in this string to \c free(). + * + * The pointer in the struct is set to \c NULL and the length is set to zero. + * + * \note There is no implementation for cxstring, because it is unlikely that + * you ever have a <code>const char*</code> you are really supposed to free. + * If you encounter such situation, you should double-check your code. + * + * @param str the string to free + */ +__attribute__((__nonnull__)) +void cx_strfree(cxmutstr *str); + +/** + * Passes the pointer in this string to the allocators free function. + * + * The pointer in the struct is set to \c NULL and the length is set to zero. + * + * \note There is no implementation for cxstring, because it is unlikely that + * you ever have a <code>const char*</code> you are really supposed to free. + * If you encounter such situation, you should double-check your code. + * + * @param alloc the allocator + * @param str the string to free + */ +__attribute__((__nonnull__)) +void cx_strfree_a( + const CxAllocator *alloc, + cxmutstr *str +); + +/** + * Returns the accumulated length of all specified strings. + * + * \attention if the count argument is larger than the number of the + * specified strings, the behavior is undefined. + * + * @param count the total number of specified strings + * @param ... all strings + * @return the accumulated length of all strings + */ +__attribute__((__warn_unused_result__)) +size_t cx_strlen( + size_t count, + ... +); + +/** + * Concatenates strings. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree_a() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param alloc the allocator to use + * @param str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strcat_ma( + const CxAllocator *alloc, + cxmutstr str, + size_t count, + ... +); + +/** + * Concatenates strings and returns a new string. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree_a() eventually. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param alloc the allocator to use + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_a(alloc, count, ...) \ +cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings and returns a new string. + * + * The resulting string will be allocated by standard \c malloc(). + * So developers \em must pass the return value to cx_strfree() eventually. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat(count, ...) \ +cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings. + * + * The resulting string will be allocated by standard \c malloc(). + * So developers \em must pass the return value to cx_strfree() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_m(str, count, ...) \ +cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) + +/** + * Returns a substring starting at the specified location. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @return a substring of \p string starting at \p start + * + * @see cx_strsubsl() + * @see cx_strsubs_m() + * @see cx_strsubsl_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strsubs( + cxstring string, + size_t start +); + +/** + * Returns a substring starting at the specified location. + * + * The returned string will be limited to \p length bytes or the number + * of bytes available in \p string, whichever is smaller. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @param length the maximum length of the returned string + * @return a substring of \p string starting at \p start + * + * @see cx_strsubs() + * @see cx_strsubs_m() + * @see cx_strsubsl_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strsubsl( + cxstring string, + size_t start, + size_t length +); + +/** + * Returns a substring starting at the specified location. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @return a substring of \p string starting at \p start + * + * @see cx_strsubsl_m() + * @see cx_strsubs() + * @see cx_strsubsl() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strsubs_m( + cxmutstr string, + size_t start +); + +/** + * Returns a substring starting at the specified location. + * + * The returned string will be limited to \p length bytes or the number + * of bytes available in \p string, whichever is smaller. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @param length the maximum length of the returned string + * @return a substring of \p string starting at \p start + * + * @see cx_strsubs_m() + * @see cx_strsubs() + * @see cx_strsubsl() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strsubsl_m( + cxmutstr string, + size_t start, + size_t length +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the first location of \p chr + * + * @see cx_strchr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strchr( + cxstring string, + int chr +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the first location of \p chr + * + * @see cx_strchr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strchr_m( + cxmutstr string, + int chr +); + +/** + * Returns a substring starting at the location of the last occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the last location of \p chr + * + * @see cx_strrchr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strrchr( + cxstring string, + int chr +); + +/** + * Returns a substring starting at the location of the last occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the last location of \p chr + * + * @see cx_strrchr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strrchr_m( + cxmutstr string, + int chr +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified string. + * + * If \p haystack does not contain \p needle, an empty string is returned. + * + * If \p needle is an empty string, the complete \p haystack is + * returned. + * + * @param haystack the string to be scanned + * @param needle string containing the sequence of characters to match + * @return a substring starting at the first occurrence of + * \p needle, or an empty string, if the sequence is not + * contained + * @see cx_strstr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strstr( + cxstring haystack, + cxstring needle +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified string. + * + * If \p haystack does not contain \p needle, an empty string is returned. + * + * If \p needle is an empty string, the complete \p haystack is + * returned. + * + * @param haystack the string to be scanned + * @param needle string containing the sequence of characters to match + * @return a substring starting at the first occurrence of + * \p needle, or an empty string, if the sequence is not + * contained + * @see cx_strstr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strstr_m( + cxmutstr haystack, + cxstring needle +); + +/** + * Splits a given string using a delimiter string. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pre-allocated array of at least \p limit length + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit( + cxstring string, + cxstring delim, + size_t limit, + cxstring *output +); + +/** + * Splits a given string using a delimiter string. + * + * The array pointed to by \p output will be allocated by \p allocator. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * \attention If allocation fails, the \c NULL pointer will be written to + * \p output and the number returned will be zero. + * + * @param allocator the allocator to use for allocating the resulting array + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pointer where the address of the allocated array shall be + * written to + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_a( + const CxAllocator *allocator, + cxstring string, + cxstring delim, + size_t limit, + cxstring **output +); + + +/** + * Splits a given string using a delimiter string. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pre-allocated array of at least \p limit length + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_m( + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr *output +); + +/** + * Splits a given string using a delimiter string. + * + * The array pointed to by \p output will be allocated by \p allocator. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * \attention If allocation fails, the \c NULL pointer will be written to + * \p output and the number returned will be zero. + * + * @param allocator the allocator to use for allocating the resulting array + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pointer where the address of the allocated array shall be + * written to + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_ma( + const CxAllocator *allocator, + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr **output +); + +/** + * Compares two strings. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal + */ +__attribute__((__warn_unused_result__)) +int cx_strcmp( + cxstring s1, + cxstring s2 +); + +/** + * Compares two strings ignoring case. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal ignoring case + */ +__attribute__((__warn_unused_result__)) +int cx_strcasecmp( + cxstring s1, + cxstring s2 +); + +/** + * Compares two strings. + * + * This function has a compatible signature for the use as a cx_compare_func. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal + */ +__attribute__((__warn_unused_result__, __nonnull__)) +int cx_strcmp_p( + const void *s1, + const void *s2 +); + +/** + * Compares two strings ignoring case. + * + * This function has a compatible signature for the use as a cx_compare_func. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal ignoring case + */ +__attribute__((__warn_unused_result__, __nonnull__)) +int cx_strcasecmp_p( + const void *s1, + const void *s2 +); + + +/** + * Creates a duplicate of the specified string. + * + * The new string will contain a copy allocated by \p allocator. + * + * \note The returned string is guaranteed to be zero-terminated. + * + * @param allocator the allocator to use + * @param string the string to duplicate + * @return a duplicate of the string + * @see cx_strdup() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strdup_a( + const CxAllocator *allocator, + cxstring string +); + +/** + * Creates a duplicate of the specified string. + * + * The new string will contain a copy allocated by standard + * \c malloc(). So developers \em must pass the return value to cx_strfree(). + * + * \note The returned string is guaranteed to be zero-terminated. + * + * @param string the string to duplicate + * @return a duplicate of the string + * @see cx_strdup_a() + */ +#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string) + + +/** + * Creates a duplicate of the specified string. + * + * The new string will contain a copy allocated by \p allocator. + * + * \note The returned string is guaranteed to be zero-terminated. + * + * @param allocator the allocator to use + * @param string the string to duplicate + * @return a duplicate of the string + * @see cx_strdup_m() + */ +#define cx_strdup_ma(allocator, string) cx_strdup_a(allocator, cx_strcast(string)) + +/** + * Creates a duplicate of the specified string. + * + * The new string will contain a copy allocated by standard + * \c malloc(). So developers \em must pass the return value to cx_strfree(). + * + * \note The returned string is guaranteed to be zero-terminated. + * + * @param string the string to duplicate + * @return a duplicate of the string + * @see cx_strdup_ma() + */ +#define cx_strdup_m(string) cx_strdup_a(cxDefaultAllocator, cx_strcast(string)) + +/** + * Omits leading and trailing spaces. + * + * \note the returned string references the same memory, thus you + * must \em not free the returned memory. + * + * @param string the string that shall be trimmed + * @return the trimmed string + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strtrim(cxstring string); + +/** + * Omits leading and trailing spaces. + * + * \note the returned string references the same memory, thus you + * must \em not free the returned memory. + * + * @param string the string that shall be trimmed + * @return the trimmed string + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strtrim_m(cxmutstr string); + +/** + * Checks, if a string has a specific prefix. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return \c true, if and only if the string has the specified prefix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strprefix( + cxstring string, + cxstring prefix +); + +/** + * Checks, if a string has a specific suffix. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return \c true, if and only if the string has the specified suffix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strsuffix( + cxstring string, + cxstring suffix +); + +/** + * Checks, if a string has a specific prefix, ignoring the case. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return \c true, if and only if the string has the specified prefix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strcaseprefix( + cxstring string, + cxstring prefix +); + +/** + * Checks, if a string has a specific suffix, ignoring the case. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return \c true, if and only if the string has the specified suffix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strcasesuffix( + cxstring string, + cxstring suffix +); + +/** + * Converts the string to lower case. + * + * The change is made in-place. If you want a copy, use cx_strdup(), first. + * + * @param string the string to modify + * @see cx_strdup() + */ +void cx_strlower(cxmutstr string); + +/** + * Converts the string to upper case. + * + * The change is made in-place. If you want a copy, use cx_strdup(), first. + * + * @param string the string to modify + * @see cx_strdup() + */ +void cx_strupper(cxmutstr string); + +/** + * Replaces a pattern in a string with another string. + * + * The pattern is taken literally and is no regular expression. + * Replaces at most \p replmax occurrences. + * + * The returned string will be allocated by \p allocator and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param allocator the allocator to use + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @param replmax maximum number of replacements + * @return the resulting string after applying the replacements + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strreplacen_a( + const CxAllocator *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +); + +/** + * Replaces a pattern in a string with another string. + * + * The pattern is taken literally and is no regular expression. + * Replaces at most \p replmax occurrences. + * + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @param replmax maximum number of replacements + * @return the resulting string after applying the replacements + */ +#define cx_strreplacen(str, pattern, replacement, replmax) \ +cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax) + +/** + * Replaces a pattern in a string with another string. + * + * The pattern is taken literally and is no regular expression. + * + * The returned string will be allocated by \p allocator and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param allocator the allocator to use + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @return the resulting string after applying the replacements + */ +#define cx_strreplace_a(allocator, str, pattern, replacement) \ +cx_strreplacen_a(allocator, str, pattern, replacement, SIZE_MAX) + +/** + * Replaces a pattern in a string with another string. + * + * The pattern is taken literally and is no regular expression. + * Replaces at most \p replmax occurrences. + * + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @return the resulting string after applying the replacements + */ +#define cx_strreplace(str, pattern, replacement) \ +cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX) + +/** + * Creates a string tokenization context. + * + * @param str the string to tokenize + * @param delim the delimiter (must not be empty) + * @param limit the maximum number of tokens that shall be returned + * @return a new string tokenization context + */ +__attribute__((__warn_unused_result__)) +CxStrtokCtx cx_strtok( + cxstring str, + cxstring delim, + size_t limit +); + +/** +* Creates a string tokenization context for a mutable string. +* +* @param str the string to tokenize +* @param delim the delimiter (must not be empty) +* @param limit the maximum number of tokens that shall be returned +* @return a new string tokenization context +*/ +__attribute__((__warn_unused_result__)) +CxStrtokCtx cx_strtok_m( + cxmutstr str, + cxstring delim, + size_t limit +); + +/** + * Returns the next token. + * + * The token will point to the source string. + * + * @param ctx the tokenization context + * @param token a pointer to memory where the next token shall be stored + * @return true if successful, false if the limit or the end of the string + * has been reached + */ +__attribute__((__warn_unused_result__, __nonnull__)) +bool cx_strtok_next( + CxStrtokCtx *ctx, + cxstring *token +); + +/** + * Returns the next token of a mutable string. + * + * The token will point to the source string. + * If the context was not initialized over a mutable string, modifying + * the data of the returned token is undefined behavior. + * + * @param ctx the tokenization context + * @param token a pointer to memory where the next token shall be stored + * @return true if successful, false if the limit or the end of the string + * has been reached + */ +__attribute__((__warn_unused_result__, __nonnull__)) +bool cx_strtok_next_m( + CxStrtokCtx *ctx, + cxmutstr *token +); + +/** + * Defines an array of more delimiters for the specified tokenization context. + * + * @param ctx the tokenization context + * @param delim array of more delimiters + * @param count number of elements in the array + */ +__attribute__((__nonnull__)) +void cx_strtok_delim( + CxStrtokCtx *ctx, + const cxstring *delim, + size_t count +); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_STRING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/test.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,330 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file: test.h + * + * UCX Test Framework. + * + * Usage of this test framework: + * + * **** IN HEADER FILE: **** + * + * <pre> + * CX_TEST(function_name); + * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional + * </pre> + * + * **** IN SOURCE FILE: **** + * <pre> + * CX_TEST_SUBROUTINE(subroutine_name, paramlist) { + * // tests with CX_TEST_ASSERT() + * } + * + * CX_TEST(function_name) { + * // memory allocation and other stuff here + * #CX_TEST_DO { + * // tests with CX_TEST_ASSERT() and/or + * // calls with CX_TEST_CALL_SUBROUTINE() here + * } + * // cleanup of memory here + * } + * </pre> + * + * @attention Do not call own functions within a test, that use + * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE(). + * + * @author Mike Becker + * @author Olaf Wintermann + * + */ + +#ifndef UCX_TEST_H +#define UCX_TEST_H + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <setjmp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FUNCTION__ +/** + * Alias for the <code>__func__</code> preprocessor macro. + * Some compilers use <code>__func__</code> and others use __FUNCTION__. + * We use __FUNCTION__ so we define it for those compilers which use + * <code>__func__</code>. + */ +#define __FUNCTION__ __func__ +#endif + +// +#if !defined(__clang__) && __GNUC__ > 3 +#pragma GCC diagnostic ignored "-Wclobbered" +#endif + +#ifndef UCX_COMMON_H +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + const void *, + size_t, + size_t, + void * +); +#endif // UCX_COMMON_H + +/** Type for the CxTestSuite. */ +typedef struct CxTestSuite CxTestSuite; + +/** Pointer to a test function. */ +typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func); + +/** Type for the internal list of test cases. */ +typedef struct CxTestSet CxTestSet; + +/** Structure for the internal list of test cases. */ +struct CxTestSet { + + /** Test case. */ + CxTest test; + + /** Pointer to the next list element. */ + CxTestSet *next; +}; + +/** + * A test suite containing multiple test cases. + */ +struct CxTestSuite { + + /** The number of successful tests after the suite has been run. */ + unsigned int success; + + /** The number of failed tests after the suite has been run. */ + unsigned int failure; + + /** The optional name of this test suite. */ + const char *name; + + /** + * Internal list of test cases. + * Use cx_test_register() to add tests to this list. + */ + CxTestSet *tests; +}; + +/** + * Creates a new test suite. + * @param name optional name of the suite + * @return a new test suite + */ +static inline CxTestSuite* cx_test_suite_new(const char *name) { + CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite)); + if (suite != NULL) { + suite->name = name; + suite->success = 0; + suite->failure = 0; + suite->tests = NULL; + } + + return suite; +} + +/** + * Destroys a test suite. + * @param suite the test suite to destroy + */ +static inline void cx_test_suite_free(CxTestSuite* suite) { + CxTestSet *l = suite->tests; + while (l != NULL) { + CxTestSet *e = l; + l = l->next; + free(e); + } + free(suite); +} + +/** + * Registers a test function with the specified test suite. + * + * @param suite the suite, the test function shall be added to + * @param test the test function to register + * @return zero on success or non-zero on failure + */ +static inline int cx_test_register(CxTestSuite* suite, CxTest test) { + CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet)); + if (t) { + t->test = test; + t->next = NULL; + if (suite->tests == NULL) { + suite->tests = t; + } else { + CxTestSet *last = suite->tests; + while (last->next) { + last = last->next; + } + last->next = t; + } + return 0; + } else { + return 1; + } +} + +/** + * Runs a test suite and writes the test log to the specified stream. + * @param suite the test suite to run + * @param out_target the target buffer or file to write the output to + * @param out_writer the write function writing to \p out_target + */ +static inline void cx_test_run(CxTestSuite *suite, + void *out_target, cx_write_func out_writer) { + if (suite->name == NULL) { + out_writer("*** Test Suite ***\n", 1, 19, out_target); + } else { + out_writer("*** Test Suite : ", 1, 17, out_target); + out_writer(suite->name, 1, strlen(suite->name), out_target); + out_writer(" ***\n", 1, 5, out_target); + } + suite->success = 0; + suite->failure = 0; + for (CxTestSet *elem = suite->tests; elem; elem = elem->next) { + elem->test(suite, out_target, out_writer); + } + out_writer("\nAll test completed.\n", 1, 21, out_target); + char total[80]; + int len = snprintf( + total, 80, + " Total: %u\n Success: %u\n Failure: %u\n\n", + suite->success + suite->failure, suite->success, suite->failure + ); + out_writer(total, 1, len, out_target); +} + +/** + * Runs a test suite and writes the test log to the specified FILE stream. + * @param suite the test suite to run + * @param file the target file to write the output to + */ +#define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite) + +/** + * Runs a test suite and writes the test log to stdout. + * @param suite the test suite to run + */ +#define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout) + +/** + * Macro for a #CxTest function header. + * + * Use this macro to declare and/or define a #CxTest function. + * + * @param name the name of the test function + */ +#define CX_TEST(name) void name(CxTestSuite* _suite_,void *_output_, cx_write_func _writefnc_) + +/** + * Defines the scope of a test. + * @attention Any CX_TEST_ASSERT() calls must be performed in scope of + * #CX_TEST_DO. + */ +#define CX_TEST_DO _writefnc_("Running ", 1, 8, _output_);\ + _writefnc_(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ + _writefnc_("... ", 1, 4, _output_);\ + jmp_buf _env_;\ + for (unsigned int _cx_test_loop_ = 0 ;\ + _cx_test_loop_ == 0 && !setjmp(_env_);\ + _writefnc_("success.\n", 1, 9, _output_),\ + _suite_->success++, _cx_test_loop_++) + +/** + * Checks a test assertion. + * If the assertion is correct, the test carries on. If the assertion is not + * correct, the specified message (terminated by a dot and a line break) is + * written to the test suites output stream. + * @param condition the condition to check + * @param message the message that shall be printed out on failure + */ +#define CX_TEST_ASSERTM(condition,message) if (!(condition)) { \ + const char *_assert_msg_ = message; \ + _writefnc_(_assert_msg_, 1, strlen(_assert_msg_), _output_); \ + _writefnc_(".\n", 1, 2, _output_); \ + _suite_->failure++; \ + longjmp(_env_, 1);\ + } (void) 0 + +/** + * Checks a test assertion. + * If the assertion is correct, the test carries on. If the assertion is not + * correct, the specified message (terminated by a dot and a line break) is + * written to the test suites output stream. + * @param condition the condition to check + */ +#define CX_TEST_ASSERT(condition) CX_TEST_ASSERTM(condition, #condition " failed") + +/** + * Macro for a test subroutine function header. + * + * Use this to declare and/or define a subroutine that can be called by using + * CX_TEST_CALL_SUBROUTINE(). + * + * @param name the name of the subroutine + * @param ... the parameter list + * + * @see CX_TEST_CALL_SUBROUTINE() + */ +#define CX_TEST_SUBROUTINE(name,...) void name(CxTestSuite* _suite_,\ + void *_output_, cx_write_func _writefnc_, jmp_buf _env_, __VA_ARGS__) + +/** + * Macro for calling a test subroutine. + * + * Subroutines declared with CX_TEST_SUBROUTINE() can be called by using this + * macro. + * + * @remark You may <b>only</b> call subroutines within a #CX_TEST_DO block. + * + * @param name the name of the subroutine + * @param ... the argument list + * + * @see CX_TEST_SUBROUTINE() + */ +#define CX_TEST_CALL_SUBROUTINE(name,...) \ + name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_TEST_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/tree.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,1254 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file tree.h + * \brief Interface for tree implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_TREE_H +#define UCX_TREE_H + +#include "common.h" + +#include "collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A depth-first tree iterator. + * + * This iterator is not position-aware in a strict sense, as it does not assume + * a particular order of elements in the tree. However, the iterator keeps track + * of the number of nodes it has passed in a counter variable. + * Each node, regardless of the number of passes, is counted only once. + * + * @note Objects that are pointed to by an iterator are mutable through that + * iterator. However, if the + * underlying data structure is mutated by other means than this iterator (e.g. + * elements added or removed), the iterator becomes invalid (regardless of what + * cxIteratorValid() returns). + * + * @see CxIterator + */ +typedef struct cx_tree_iterator_s { + /** + * Base members. + */ + CX_ITERATOR_BASE; + /** + * Indicates whether the subtree below the current node shall be skipped. + */ + bool skip; + /** + * Set to true, when the iterator shall visit a node again + * when all it's children have been processed. + */ + bool visit_on_exit; + /** + * True, if this iterator is currently leaving the node. + */ + bool exiting; + /** + * Offset in the node struct for the children linked list. + */ + ptrdiff_t loc_children; + /** + * Offset in the node struct for the next pointer. + */ + ptrdiff_t loc_next; + /** + * The total number of distinct nodes that have been passed so far. + */ + size_t counter; + /** + * The currently observed node. + * + * This is the same what cxIteratorCurrent() would return. + */ + void *node; + /** + * Stores a copy of the next pointer of the visited node. + * Allows freeing a node on exit without corrupting the iteration. + */ + void *node_next; + /** + * Internal stack. + * Will be automatically freed once the iterator becomes invalid. + * + * If you want to discard the iterator before, you need to manually + * call cxTreeIteratorDispose(). + */ + void **stack; + /** + * Internal capacity of the stack. + */ + size_t stack_capacity; + union { + /** + * Internal stack size. + */ + size_t stack_size; + /** + * The current depth in the tree. + */ + size_t depth; + }; +} CxTreeIterator; + +/** + * An element in a visitor queue. + */ +struct cx_tree_visitor_queue_s { + /** + * The tree node to visit. + */ + void *node; + /** + * The depth of the node. + */ + size_t depth; + /** + * The next element in the queue or \c NULL. + */ + struct cx_tree_visitor_queue_s *next; +}; + +/** + * A breadth-first tree iterator. + * + * This iterator needs to maintain a visitor queue that will be automatically + * freed once the iterator becomes invalid. + * If you want to discard the iterator before, you MUST manually call + * cxTreeVisitorDispose(). + * + * This iterator is not position-aware in a strict sense, as it does not assume + * a particular order of elements in the tree. However, the iterator keeps track + * of the number of nodes it has passed in a counter variable. + * Each node, regardless of the number of passes, is counted only once. + * + * @note Objects that are pointed to by an iterator are mutable through that + * iterator. However, if the + * underlying data structure is mutated by other means than this iterator (e.g. + * elements added or removed), the iterator becomes invalid (regardless of what + * cxIteratorValid() returns). + * + * @see CxIterator + */ +typedef struct cx_tree_visitor_s { + /** + * Base members. + */ + CX_ITERATOR_BASE; + /** + * Indicates whether the subtree below the current node shall be skipped. + */ + bool skip; + /** + * Offset in the node struct for the children linked list. + */ + ptrdiff_t loc_children; + /** + * Offset in the node struct for the next pointer. + */ + ptrdiff_t loc_next; + /** + * The total number of distinct nodes that have been passed so far. + */ + size_t counter; + /** + * The currently observed node. + * + * This is the same what cxIteratorCurrent() would return. + */ + void *node; + /** + * The current depth in the tree. + */ + size_t depth; + /** + * The next element in the visitor queue. + */ + struct cx_tree_visitor_queue_s *queue_next; + /** + * The last element in the visitor queue. + */ + struct cx_tree_visitor_queue_s *queue_last; +} CxTreeVisitor; + +/** + * Releases internal memory of the given tree iterator. + * @param iter the iterator + */ + __attribute__((__nonnull__)) +static inline void cxTreeIteratorDispose(CxTreeIterator *iter) { + free(iter->stack); + iter->stack = NULL; +} + +/** + * Releases internal memory of the given tree visitor. + * @param visitor the visitor + */ +__attribute__((__nonnull__)) +static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) { + struct cx_tree_visitor_queue_s *q = visitor->queue_next; + while (q != NULL) { + struct cx_tree_visitor_queue_s *next = q->next; + free(q); + q = next; + } +} + +/** + * Advises the iterator to skip the subtree below the current node and + * also continues the current loop. + * + * @param iterator the iterator + */ +#define cxTreeIteratorContinue(iterator) (iterator).skip = true; continue + +/** + * Advises the visitor to skip the subtree below the current node and + * also continues the current loop. + * + * @param visitor the visitor + */ +#define cxTreeVisitorContinue(visitor) cxTreeIteratorContinue(visitor) + +/** + * Links a node to a (new) parent. + * + * If the node has already a parent, it is unlinked, first. + * If the parent has children already, the node is \em appended to the list + * of all currently existing children. + * + * @param parent the parent node + * @param node the node that shall be linked + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @see cx_tree_unlink() + */ +__attribute__((__nonnull__)) +void cx_tree_link( + void *restrict parent, + void *restrict node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Unlinks a node from its parent. + * + * If the node has no parent, this function does nothing. + * + * @param node the node that shall be unlinked from its parent + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @see cx_tree_link() + */ +__attribute__((__nonnull__)) +void cx_tree_unlink( + void *node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Function pointer for a search function. + * + * A function of this kind shall check if the specified \p node + * contains the given \p data or if one of the children might contain + * the data. + * + * The function should use the returned integer to indicate how close the + * match is, where a negative number means that it does not match at all. + * + * For example if a tree stores file path information, a node that is + * describing a parent directory of a filename that is searched, shall + * return a positive number to indicate that a child node might contain the + * searched item. On the other hand, if the node denotes a path that is not a + * prefix of the searched filename, the function would return -1 to indicate + * that the search does not need to be continued in that branch. + * + * @param node the node that is currently investigated + * @param data the data that is searched for + * + * @return 0 if the node contains the data, + * positive if one of the children might contain the data, + * negative if neither the node, nor the children contains the data + */ +typedef int (*cx_tree_search_data_func)(const void *node, const void *data); + + +/** + * Function pointer for a search function. + * + * A function of this kind shall check if the specified \p node + * contains the same \p data as \p new_node or if one of the children might + * contain the data. + * + * The function should use the returned integer to indicate how close the + * match is, where a negative number means that it does not match at all. + * + * For example if a tree stores file path information, a node that is + * describing a parent directory of a filename that is searched, shall + * return a positive number to indicate that a child node might contain the + * searched item. On the other hand, if the node denotes a path that is not a + * prefix of the searched filename, the function would return -1 to indicate + * that the search does not need to be continued in that branch. + * + * @param node the node that is currently investigated + * @param new_node a new node with the information which is searched + * + * @return 0 if \p node contains the same data as \p new_node, + * positive if one of the children might contain the data, + * negative if neither the node, nor the children contains the data + */ +typedef int (*cx_tree_search_func)(const void *node, const void *new_node); + +/** + * Searches for data in a tree. + * + * When the data cannot be found exactly, the search function might return a + * closest result which might be a good starting point for adding a new node + * to the tree (see also #cx_tree_add()). + * + * Depending on the tree structure it is not necessarily guaranteed that the + * "closest" match is uniquely defined. This function will search for a node + * with the best match according to the \p sfunc (meaning: the return value of + * \p sfunc which is closest to zero). If that is also ambiguous, an arbitrary + * node matching the criteria is returned. + * + * @param root the root node + * @param data the data to search for + * @param sfunc the search function + * @param result where the result shall be stored + * @param loc_children offset in the node struct for the children linked list + * @param loc_next offset in the node struct for the next pointer + * @return zero if the node was found exactly, positive if a node was found that + * could contain the node (but doesn't right now), negative if the tree does not + * contain any node that might be related to the searched data + */ +__attribute__((__nonnull__)) +int cx_tree_search_data( + const void *root, + const void *data, + cx_tree_search_data_func sfunc, + void **result, + ptrdiff_t loc_children, + ptrdiff_t loc_next +); + +/** + * Searches for a node in a tree. + * + * When no node with the same data can be found, the search function might + * return a closest result which might be a good starting point for adding the + * new node to the tree (see also #cx_tree_add()). + * + * Depending on the tree structure it is not necessarily guaranteed that the + * "closest" match is uniquely defined. This function will search for a node + * with the best match according to the \p sfunc (meaning: the return value of + * \p sfunc which is closest to zero). If that is also ambiguous, an arbitrary + * node matching the criteria is returned. + * + * @param root the root node + * @param node the node to search for + * @param sfunc the search function + * @param result where the result shall be stored + * @param loc_children offset in the node struct for the children linked list + * @param loc_next offset in the node struct for the next pointer + * @return zero if the node was found exactly, positive if a node was found that + * could contain the node (but doesn't right now), negative if the tree does not + * contain any node that might be related to the searched data + */ +__attribute__((__nonnull__)) +int cx_tree_search( + const void *root, + const void *node, + cx_tree_search_func sfunc, + void **result, + ptrdiff_t loc_children, + ptrdiff_t loc_next +); + +/** + * Creates a depth-first iterator for a tree with the specified root node. + * + * @note A tree iterator needs to maintain a stack of visited nodes, which is + * allocated using stdlib malloc(). + * When the iterator becomes invalid, this memory is automatically released. + * However, if you wish to cancel the iteration before the iterator becomes + * invalid by itself, you MUST call cxTreeIteratorDispose() manually to release + * the memory. + * + * @remark The returned iterator does not support cxIteratorFlagRemoval(). + * + * @param root the root node + * @param visit_on_exit set to true, when the iterator shall visit a node again + * after processing all children + * @param loc_children offset in the node struct for the children linked list + * @param loc_next offset in the node struct for the next pointer + * @return the new tree iterator + * @see cxTreeIteratorDispose() + */ +CxTreeIterator cx_tree_iterator( + void *root, + bool visit_on_exit, + ptrdiff_t loc_children, + ptrdiff_t loc_next +); + +/** + * Creates a breadth-first iterator for a tree with the specified root node. + * + * @note A tree visitor needs to maintain a queue of to be visited nodes, which + * is allocated using stdlib malloc(). + * When the visitor becomes invalid, this memory is automatically released. + * However, if you wish to cancel the iteration before the visitor becomes + * invalid by itself, you MUST call cxTreeVisitorDispose() manually to release + * the memory. + * + * @remark The returned iterator does not support cxIteratorFlagRemoval(). + * + * @param root the root node + * @param loc_children offset in the node struct for the children linked list + * @param loc_next offset in the node struct for the next pointer + * @return the new tree visitor + * @see cxTreeVisitorDispose() + */ +CxTreeVisitor cx_tree_visitor( + void *root, + ptrdiff_t loc_children, + ptrdiff_t loc_next +); + +/** + * Describes a function that creates a tree node from the specified data. + * The first argument points to the data the node shall contain and + * the second argument may be used for additional data (e.g. an allocator). + * Functions of this type shall either return a new pointer to a newly + * created node or \c NULL when allocation fails. + * + * \note the function may leave the node pointers in the struct uninitialized. + * The caller is responsible to set them according to the intended use case. + */ +typedef void *(*cx_tree_node_create_func)(const void *, void *); + +/** + * The local search depth for a new subtree when adding multiple elements. + * The default value is 3. + * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to + * implement optimized insertion of multiple elements into a tree. + */ +extern unsigned int cx_tree_add_look_around_depth; + +/** + * Adds multiple elements efficiently to a tree. + * + * Once an element cannot be added to the tree, this function returns, leaving + * the iterator in a valid state pointing to the element that could not be + * added. + * Also, the pointer of the created node will be stored to \p failed. + * The integer returned by this function denotes the number of elements obtained + * from the \p iter that have been successfully processed. + * When all elements could be processed, a \c NULL pointer will be written to + * \p failed. + * + * The advantage of this function compared to multiple invocations of + * #cx_tree_add() is that the search for the insert locations is not always + * started from the root node. + * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes + * of the current insert location before starting from the root node again. + * When the variable is set to zero, only the last found location is checked + * again. + * + * Refer to the documentation of #cx_tree_add() for more details. + * + * @param iter a pointer to an arbitrary iterator + * @param num the maximum number of elements to obtain from the iterator + * @param sfunc a search function + * @param cfunc a node creation function + * @param cdata optional additional data + * @param root the root node of the tree + * @param failed location where the pointer to a failed node shall be stored + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @return the number of nodes created and added + * @see cx_tree_add() + */ +__attribute__((__nonnull__(1, 3, 4, 6, 7))) +size_t cx_tree_add_iter( + struct cx_iterator_base_s *iter, + size_t num, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **failed, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Adds multiple elements efficiently to a tree. + * + * Once an element cannot be added to the tree, this function returns, storing + * the pointer of the created node to \p failed. + * The integer returned by this function denotes the number of elements from + * the \p src array that have been successfully processed. + * When all elements could be processed, a \c NULL pointer will be written to + * \p failed. + * + * The advantage of this function compared to multiple invocations of + * #cx_tree_add() is that the search for the insert locations is not always + * started from the root node. + * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes + * of the current insert location before starting from the root node again. + * When the variable is set to zero, only the last found location is checked + * again. + * + * Refer to the documentation of #cx_tree_add() for more details. + * + * @param src a pointer to the source data array + * @param num the number of elements in the \p src array + * @param elem_size the size of each element in the \p src array + * @param sfunc a search function + * @param cfunc a node creation function + * @param cdata optional additional data + * @param failed location where the pointer to a failed node shall be stored + * @param root the root node of the tree + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @return the number of array elements successfully processed + * @see cx_tree_add() + */ +__attribute__((__nonnull__(1, 4, 5, 7, 8))) +size_t cx_tree_add_array( + const void *src, + size_t num, + size_t elem_size, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **failed, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Adds data to a tree. + * + * An adequate location where to add the new tree node is searched with the + * specified \p sfunc. + * + * When a location is found, the \p cfunc will be invoked with \p cdata. + * + * The node returned by \p cfunc will be linked into the tree. + * When \p sfunc returned a positive integer, the new node will be linked as a + * child. The other children (now siblings of the new node) are then checked + * with \p sfunc, whether they could be children of the new node and re-linked + * accordingly. + * + * When \p sfunc returned zero and the found node has a parent, the new + * node will be added as sibling - otherwise, the new node will be added + * as a child. + * + * When \p sfunc returned a negative value, the new node will not be added to + * the tree and this function returns a non-zero value. + * The caller should check if \p cnode contains a node pointer and deal with the + * node that could not be added. + * + * This function also returns a non-zero value when \p cfunc tries to allocate + * a new node but fails to do so. In that case, the pointer stored to \p cnode + * will be \c NULL. + * + * Multiple elements can be added more efficiently with + * #cx_tree_add_array() or #cx_tree_add_iter(). + * + * @param src a pointer to the data + * @param sfunc a search function + * @param cfunc a node creation function + * @param cdata optional additional data + * @param cnode the location where a pointer to the new node is stored + * @param root the root node of the tree + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @return zero when a new node was created and added to the tree, + * non-zero otherwise + */ +__attribute__((__nonnull__(1, 2, 3, 5, 6))) +int cx_tree_add( + const void *src, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **cnode, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + + +/** + * Tree class type. + */ +typedef struct cx_tree_class_s cx_tree_class; + +/** + * Base structure that can be used for tree nodes in a #CxTree. + */ +struct cx_tree_node_base_s { + /** + * Pointer to the parent. + */ + struct cx_tree_node_base_s *parent; + /** + * Pointer to the first child. + */ + struct cx_tree_node_base_s *children; + /** + * Pointer to the last child. + */ + struct cx_tree_node_base_s *last_child; + /** + * Pointer to the previous sibling. + */ + struct cx_tree_node_base_s *prev; + /** + * Pointer to the next sibling. + */ + struct cx_tree_node_base_s *next; +}; + +/** + * Structure for holding the base data of a tree. + */ +struct cx_tree_s { + /** + * The tree class definition. + */ + const cx_tree_class *cl; + + /** + * Allocator to allocate new nodes. + */ + const CxAllocator *allocator; + + /** + * A pointer to the root node. + * + * Will be \c NULL when \c size is 0. + */ + void *root; + + /** + * A function to create new nodes. + * + * Invocations to this function will receive a pointer to this tree + * structure as second argument. + * + * Nodes MAY use #cx_tree_node_base_s as base layout, but do not need to. + */ + cx_tree_node_create_func node_create; + + /** + * An optional simple destructor for the tree nodes. + */ + cx_destructor_func simple_destructor; + + /** + * An optional advanced destructor for the tree nodes. + */ + cx_destructor_func2 advanced_destructor; + + /** + * The pointer to additional data that is passed to the advanced destructor. + */ + void *destructor_data; + + /** + * A function to compare two nodes. + */ + cx_tree_search_func search; + + /** + * A function to compare a node with data. + */ + cx_tree_search_data_func search_data; + + /** + * The number of currently stored elements. + */ + size_t size; + + /** + * Offset in the node struct for the parent pointer. + */ + ptrdiff_t loc_parent; + + /** + * Offset in the node struct for the children linked list. + */ + ptrdiff_t loc_children; + + /** + * Optional offset in the node struct for the pointer to the last child + * in the linked list (negative if there is no such pointer). + */ + ptrdiff_t loc_last_child; + + /** + * Offset in the node struct for the previous sibling pointer. + */ + ptrdiff_t loc_prev; + + /** + * Offset in the node struct for the next sibling pointer. + */ + ptrdiff_t loc_next; +}; + +/** + * Macro to roll out the #cx_tree_node_base_s structure with a custom + * node type. + */ +#define CX_TREE_NODE_BASE(type) \ + type *parent; \ + type *children;\ + type *last_child;\ + type *prev;\ + type *next + +/** + * Macro for specifying the layout of a base node tree. + */ +#define cx_tree_node_base_layout \ + offsetof(struct cx_tree_node_base_s, parent),\ + offsetof(struct cx_tree_node_base_s, children),\ + offsetof(struct cx_tree_node_base_s, last_child),\ + offsetof(struct cx_tree_node_base_s, prev), \ + offsetof(struct cx_tree_node_base_s, next) + +/** + * Macro for obtaining the node pointer layout for a specific tree. + */ +#define cx_tree_node_layout(tree) \ + (tree)->loc_parent,\ + (tree)->loc_children,\ + (tree)->loc_last_child,\ + (tree)->loc_prev, \ + (tree)->loc_next + +/** + * The class definition for arbitrary trees. + */ +struct cx_tree_class_s { + /** + * Destructor function. + * + * Implementations SHALL invoke the node destructor functions if provided + * and SHALL deallocate the tree memory. + */ + void (*destructor)(struct cx_tree_s *); + + /** + * Member function for inserting a single element. + * + * Implementations SHALL NOT simply invoke \p insert_many as this comes + * with too much overhead. + */ + int (*insert_element)( + struct cx_tree_s *tree, + const void *data + ); + + /** + * Member function for inserting multiple elements. + * + * Implementations SHALL avoid to perform a full search in the tree for + * every element even though the source data MAY be unsorted. + */ + size_t (*insert_many)( + struct cx_tree_s *tree, + struct cx_iterator_base_s *iter, + size_t n + ); + + /** + * Member function for finding a node. + */ + void *(*find)( + struct cx_tree_s *tree, + const void *subtree, + const void *data + ); + + /** + * Member function for creating an iterator for the tree. + */ + CxTreeIterator (*iterator)( + struct cx_tree_s *tree, + bool visit_on_exit + ); + + /** + * Member function for creating a visitor for the tree. + */ + CxTreeVisitor (*visitor)(struct cx_tree_s *tree); +}; + +/** + * Common type for all tree implementations. + */ +typedef struct cx_tree_s CxTree; + +/** + * Creates a new tree structure based on the specified layout. + * + * The specified \p allocator will be used for creating the tree struct + * and SHALL be used by \p create_func to allocate memory for the nodes. + * + * \note This function will also register an advanced destructor which + * will free the nodes with the allocator's free() method. + * + * @param allocator the allocator that shall be used + * @param create_func a function that creates new nodes + * @param search_func a function that compares two nodes + * @param search_data_func a function that compares a node with data + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @return the new tree + * @see cxTreeCreateSimple() + * @see cxTreeCreateWrapped() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxTree *cxTreeCreate( + const CxAllocator *allocator, + cx_tree_node_create_func create_func, + cx_tree_search_func search_func, + cx_tree_search_data_func search_data_func, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Creates a new tree structure based on a default layout. + * + * Nodes created by \p create_func MUST contain #cx_tree_node_base_s as first + * member (or at least respect the default offsets specified in the tree + * struct) and they MUST be allocated with the specified allocator. + * + * \note This function will also register an advanced destructor which + * will free the nodes with the allocator's free() method. + * + * @param allocator the allocator that shall be used + * @param create_func a function that creates new nodes + * @param search_func a function that compares two nodes + * @param search_data_func a function that compares a node with data + * @return the new tree + * @see cxTreeCreate() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxTree *cxTreeCreateSimple( + const CxAllocator *allocator, + cx_tree_node_create_func create_func, + cx_tree_search_func search_func, + cx_tree_search_data_func search_data_func +) { + return cxTreeCreate( + allocator, + create_func, + search_func, + search_data_func, + cx_tree_node_base_layout + ); +} + +/** + * Creates a new tree structure based on an existing tree. + * + * The specified \p allocator will be used for creating the tree struct. + * + * \attention This function will create an incompletely defined tree structure + * where neither the create function, the search function, nor a destructor + * will be set. If you wish to use any of this functionality for the wrapped + * tree, you need to specify those functions afterwards. + * + * @param root the root node of the tree that shall be wrapped + * @param loc_parent offset in the node struct for the parent pointer + * @param loc_children offset in the node struct for the children linked list + * @param loc_last_child optional offset in the node struct for the pointer to + * the last child in the linked list (negative if there is no such pointer) + * @param loc_prev offset in the node struct for the prev pointer + * @param loc_next offset in the node struct for the next pointer + * @return the new tree + * @see cxTreeCreate() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxTree *cxTreeCreateWrapped( + const CxAllocator *allocator, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +); + +/** + * Destroys the tree structure. + * + * \attention This function will only invoke the destructor functions + * on the nodes, if specified. + * It will NOT additionally free the nodes with the tree's allocator, because + * that would cause a double-free in most scenarios. + * + * @param tree the tree to destroy + */ +__attribute__((__nonnull__)) +static inline void cxTreeDestroy(CxTree *tree) { + tree->cl->destructor(tree); +} + +/** + * Inserts data into the tree. + * + * \remark For this function to work, the tree needs specified search and + * create functions, which might not be available for wrapped trees + * (see #cxTreeCreateWrapped()). + * + * @param tree the tree + * @param data the data to insert + * @return zero on success, non-zero on failure + */ +__attribute__((__nonnull__)) +static inline int cxTreeInsert( + CxTree *tree, + const void *data +) { + return tree->cl->insert_element(tree, data); +} + +/** + * Inserts elements provided by an iterator efficiently into the tree. + * + * \remark For this function to work, the tree needs specified search and + * create functions, which might not be available for wrapped trees + * (see #cxTreeCreateWrapped()). + * + * @param tree the tree + * @param iter the iterator providing the elements + * @param n the maximum number of elements to insert + * @return the number of elements that could be successfully inserted + */ +__attribute__((__nonnull__)) +static inline size_t cxTreeInsertIter( + CxTree *tree, + struct cx_iterator_base_s *iter, + size_t n +) { + return tree->cl->insert_many(tree, iter, n); +} + +/** + * Inserts an array of data efficiently into the tree. + * + * \remark For this function to work, the tree needs specified search and + * create functions, which might not be available for wrapped trees + * (see #cxTreeCreateWrapped()). + * + * @param tree the tree + * @param data the array of data to insert + * @param elem_size the size of each element in the array + * @param n the number of elements in the array + * @return the number of elements that could be successfully inserted + */ +__attribute__((__nonnull__)) +static inline size_t cxTreeInsertArray( + CxTree *tree, + const void *data, + size_t elem_size, + size_t n +) { + if (n == 0) return 0; + if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; + CxIterator iter = cxIterator(data, elem_size, n); + return cxTreeInsertIter(tree, cxIteratorRef(iter), n); +} + +/** + * Searches the data in the specified tree. + * + * \remark For this function to work, the tree needs a specified \c search_data + * function, which might not be available wrapped trees + * (see #cxTreeCreateWrapped()). + * + * @param tree the tree + * @param data the data to search for + * @return the first matching node, or \c NULL when the data cannot be found + */ +__attribute__((__nonnull__)) +static inline void *cxTreeFind( + CxTree *tree, + const void *data +) { + return tree->cl->find(tree, tree->root, data); +} + +/** + * Searches the data in the specified subtree. + * + * \note When \p subtree_root is not part of the \p tree, the behavior is + * undefined. + * + * \remark For this function to work, the tree needs a specified \c search_data + * function, which might not be the case for wrapped trees + * (see #cxTreeCreateWrapped()). + * + * @param tree the tree + * @param data the data to search for + * @param subtree_root the node where to start + * @return the first matching node, or \c NULL when the data cannot be found + */ +__attribute__((__nonnull__)) +static inline void *cxTreeFindInSubtree( + CxTree *tree, + const void *data, + void *subtree_root +) { + return tree->cl->find(tree, subtree_root, data); +} + +/** + * Determines the size of the specified subtree. + * + * @param tree the tree + * @param subtree_root the root node of the subtree + * @return the number of nodes in the specified subtree + */ +__attribute__((__nonnull__)) +size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); + +/** + * Determines the depth of the specified subtree. + * + * @param tree the tree + * @param subtree_root the root node of the subtree + * @return the tree depth including the \p subtree_root + */ +__attribute__((__nonnull__)) +size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); + +/** + * Determines the depth of the entire tree. + * + * @param tree the tree + * @return the tree depth, counting the root as one + */ +__attribute__((__nonnull__)) +size_t cxTreeDepth(CxTree *tree); + +/** + * Creates a depth-first iterator for the specified tree. + * + * @param tree the tree to iterate + * @param visit_on_exit true, if the iterator shall visit a node again when + * leaving the sub-tree + * @return a tree iterator (depth-first) + * @see cxTreeVisitor() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxTreeIterator cxTreeIterator( + CxTree *tree, + bool visit_on_exit +) { + return tree->cl->iterator(tree, visit_on_exit); +} + +/** + * Creates a breadth-first iterator for the specified tree. + * + * @param tree the tree to iterate + * @return a tree visitor (a.k.a. breadth-first iterator) + * @see cxTreeIterator() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxTreeVisitor cxTreeVisitor(CxTree *tree) { + return tree->cl->visitor(tree); +} + +/** + * Adds a new node to the tree. + * + * \attention The node may be externally created, but MUST obey the same rules + * as if it was created by the tree itself with #cxTreeAddChild() (e.g. use + * the same allocator). + * + * @param tree the tree + * @param parent the parent of the node to add + * @param child the node to add + */ +__attribute__((__nonnull__)) +static inline void cxTreeAddChildNode( + CxTree *tree, + void *parent, + void *child) { + cx_tree_link(parent, child, cx_tree_node_layout(tree)); + tree->size++; +} + +/** + * Creates a new node and adds it to the tree. + * + * With this function you can decide where exactly the new node shall be added. + * If you specified an appropriate search function, you may want to consider + * leaving this task to the tree by using #cxTreeInsert(). + * + * Be aware that adding nodes at arbitrary locations in the tree might cause + * wrong or undesired results when subsequently invoking #cxTreeInsert() and + * the invariant imposed by the search function does not hold any longer. + * + * @param tree the tree + * @param parent the parent node of the new node + * @param data the data that will be submitted to the create function + * @return zero when the new node was created, non-zero on allocation failure + * @see cxTreeInsert() + */ +__attribute__((__nonnull__)) +int cxTreeAddChild( + CxTree *tree, + void *parent, + const void *data +); + +/** + * A function that is invoked when a node needs to be re-linked to a new parent. + * + * When a node is re-linked, sometimes the contents need to be updated. + * This callback is invoked by #cxTreeRemove() so that those updates can be + * applied when re-linking the children of the removed node. + * + * @param node the affected node + * @param old_parent the old parent of the node + * @param new_parent the new parent of the node + */ +typedef void (*cx_tree_relink_func)( + void *node, + const void *old_parent, + const void *new_parent +); + +/** + * Removes a node and re-links its children to its former parent. + * + * If the node is not part of the tree, the behavior is undefined. + * + * \note The destructor function, if any, will \em not be invoked. That means + * you will need to free the removed node by yourself, eventually. + * + * @param tree the tree + * @param node the node to remove (must not be the root node) + * @param relink_func optional callback to update the content of each re-linked + * node + * @return zero on success, non-zero if \p node is the root node of the tree + */ +__attribute__((__nonnull__(1,2))) +int cxTreeRemove( + CxTree *tree, + void *node, + cx_tree_relink_func relink_func +); + +/** + * Removes a node and it's subtree from the tree. + * + * If the node is not part of the tree, the behavior is undefined. + * + * \note The destructor function, if any, will \em not be invoked. That means + * you will need to free the removed subtree by yourself, eventually. + * + * @param tree the tree + * @param node the node to remove + */ +__attribute__((__nonnull__)) +void cxTreeRemoveSubtree(CxTree *tree, void *node); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_TREE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/utils.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,194 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file utils.h + * + * \brief General purpose utility functions. + * + * \author Mike Becker + * \author Olaf Wintermann + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_UTILS_H +#define UCX_UTILS_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Convenience macro for a for loop that counts from zero to n-1. + */ +#define cx_for_n(varname, n) for (size_t varname = 0 ; (varname) < (n) ; (varname)++) + +/** + * Convenience macro for swapping two pointers. + */ +#ifdef __cplusplus +#define cx_swap_ptr(left, right) do {auto cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) +#else +#define cx_swap_ptr(left, right) do {void *cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) +#endif + +// cx_szmul() definition + +#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN) +#define CX_SZMUL_BUILTIN + +/** + * Alias for \c __builtin_mul_overflow. + * + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) __builtin_mul_overflow(a, b, result) + +#else // no GNUC or clang bultin + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result) + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * This is a custom implementation in case there is no compiler builtin + * available. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t where the result should be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +int cx_szmul_impl(size_t a, size_t b, size_t *result); + +#endif // cx_szmul + + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @param n the maximum number of bytes that shall be copied. + * If this is larger than \p bufsize, the content is copied over multiple + * iterations. + * @return the total number of bytes copied + */ +__attribute__((__nonnull__(1, 2, 3, 4))) +size_t cx_stream_bncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + char *buf, + size_t bufsize, + size_t n +); + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @return total number of bytes copied + */ +#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ + cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX) + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n the maximum number of bytes that shall be copied. + * @return total number of bytes copied + */ +__attribute__((__nonnull__)) +size_t cx_stream_ncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + size_t n +); + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @return total number of bytes copied + */ +#define cx_stream_copy(src, dest, rfnc, wfnc) \ + cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX) + +#ifdef __cplusplus +} +#endif + +#endif // UCX_UTILS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_key.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/hash_key.h" +#include <string.h> + +void cx_hash_murmur(CxHashKey *key) { + const unsigned char *data = key->data; + if (data == NULL) { + // extension: special value for NULL + key->hash = 1574210520u; + return; + } + size_t len = key->len; + + unsigned m = 0x5bd1e995; + unsigned r = 24; + unsigned h = 25 ^ len; + unsigned i = 0; + while (len >= 4) { + unsigned k = data[i + 0] & 0xFF; + k |= (data[i + 1] & 0xFF) << 8; + k |= (data[i + 2] & 0xFF) << 16; + k |= (data[i + 3] & 0xFF) << 24; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + i += 4; + len -= 4; + } + + switch (len) { + case 3: + h ^= (data[i + 2] & 0xFF) << 16; + __attribute__((__fallthrough__)); + case 2: + h ^= (data[i + 1] & 0xFF) << 8; + __attribute__((__fallthrough__)); + case 1: + h ^= (data[i + 0] & 0xFF); + h *= m; + __attribute__((__fallthrough__)); + default: // do nothing + ; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + key->hash = h; +} + +CxHashKey cx_hash_key_str(const char *str) { + CxHashKey key; + key.data = str; + key.len = str == NULL ? 0 : strlen(str); + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key_bytes( + const unsigned char *bytes, + size_t len +) { + CxHashKey key; + key.data = bytes; + key.len = len; + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key( + const void *obj, + size_t len +) { + CxHashKey key; + key.data = obj; + key.len = len; + cx_hash_murmur(&key); + return key; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_map.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,479 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/hash_map.h" +#include "cx/utils.h" + +#include <string.h> +#include <assert.h> + +struct cx_hash_map_element_s { + /** A pointer to the next element in the current bucket. */ + struct cx_hash_map_element_s *next; + + /** The corresponding key. */ + CxHashKey key; + + /** The value data. */ + char data[]; +}; + +static void cx_hash_map_clear(struct cx_map_s *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + cx_for_n(i, hash_map->bucket_count) { + struct cx_hash_map_element_s *elem = hash_map->buckets[i]; + if (elem != NULL) { + do { + struct cx_hash_map_element_s *next = elem->next; + // invoke the destructor + cx_invoke_destructor(map, elem->data); + // free the key data + cxFree(map->collection.allocator, (void *) elem->key.data); + // free the node + cxFree(map->collection.allocator, elem); + // proceed + elem = next; + } while (elem != NULL); + + // do not leave a dangling pointer + hash_map->buckets[i] = NULL; + } + } + map->collection.size = 0; +} + +static void cx_hash_map_destructor(struct cx_map_s *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + + // free the buckets + cx_hash_map_clear(map); + cxFree(map->collection.allocator, hash_map->buckets); + + // free the map structure + cxFree(map->collection.allocator, map); +} + +static int cx_hash_map_put( + CxMap *map, + CxHashKey key, + void *value +) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + const CxAllocator *allocator = map->collection.allocator; + + unsigned hash = key.hash; + if (hash == 0) { + cx_hash_murmur(&key); + hash = key.hash; + } + + size_t slot = hash % hash_map->bucket_count; + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + struct cx_hash_map_element_s *prev = NULL; + + while (elm != NULL && elm->key.hash < hash) { + prev = elm; + elm = elm->next; + } + + if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && + memcmp(elm->key.data, key.data, key.len) == 0) { + // overwrite existing element + if (map->collection.store_pointer) { + memcpy(elm->data, &value, sizeof(void *)); + } else { + memcpy(elm->data, value, map->collection.elem_size); + } + } else { + // allocate new element + struct cx_hash_map_element_s *e = cxMalloc( + allocator, + sizeof(struct cx_hash_map_element_s) + map->collection.elem_size + ); + if (e == NULL) { + return -1; + } + + // write the value + if (map->collection.store_pointer) { + memcpy(e->data, &value, sizeof(void *)); + } else { + memcpy(e->data, value, map->collection.elem_size); + } + + // copy the key + void *kd = cxMalloc(allocator, key.len); + if (kd == NULL) { + return -1; + } + memcpy(kd, key.data, key.len); + e->key.data = kd; + e->key.len = key.len; + e->key.hash = hash; + + // insert the element into the linked list + if (prev == NULL) { + hash_map->buckets[slot] = e; + } else { + prev->next = e; + } + e->next = elm; + + // increase the size + map->collection.size++; + } + + return 0; +} + +static void cx_hash_map_unlink( + struct cx_hash_map_s *hash_map, + size_t slot, + struct cx_hash_map_element_s *prev, + struct cx_hash_map_element_s *elm +) { + // unlink + if (prev == NULL) { + hash_map->buckets[slot] = elm->next; + } else { + prev->next = elm->next; + } + // free element + cxFree(hash_map->base.collection.allocator, (void *) elm->key.data); + cxFree(hash_map->base.collection.allocator, elm); + // decrease size + hash_map->base.collection.size--; +} + +/** + * Helper function to avoid code duplication. + * + * @param map the map + * @param key the key to look up + * @param remove flag indicating whether the looked up entry shall be removed + * @param destroy flag indicating whether the destructor shall be invoked + * @return a pointer to the value corresponding to the key or \c NULL + */ +static void *cx_hash_map_get_remove( + CxMap *map, + CxHashKey key, + bool remove, + bool destroy +) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + + unsigned hash = key.hash; + if (hash == 0) { + cx_hash_murmur(&key); + hash = key.hash; + } + + size_t slot = hash % hash_map->bucket_count; + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + struct cx_hash_map_element_s *prev = NULL; + while (elm && elm->key.hash <= hash) { + if (elm->key.hash == hash && elm->key.len == key.len) { + if (memcmp(elm->key.data, key.data, key.len) == 0) { + void *data = NULL; + if (destroy) { + cx_invoke_destructor(map, elm->data); + } else { + if (map->collection.store_pointer) { + data = *(void **) elm->data; + } else { + data = elm->data; + } + } + if (remove) { + cx_hash_map_unlink(hash_map, slot, prev, elm); + } + return data; + } + } + prev = elm; + elm = prev->next; + } + + return NULL; +} + +static void *cx_hash_map_get( + const CxMap *map, + CxHashKey key +) { + // we can safely cast, because we know the map stays untouched + return cx_hash_map_get_remove((CxMap *) map, key, false, false); +} + +static void *cx_hash_map_remove( + CxMap *map, + CxHashKey key, + bool destroy +) { + return cx_hash_map_get_remove(map, key, true, destroy); +} + +static void *cx_hash_map_iter_current_entry(const void *it) { + const struct cx_iterator_s *iter = it; + // struct has to have a compatible signature + return (struct cx_map_entry_s *) &(iter->kv_data); +} + +static void *cx_hash_map_iter_current_key(const void *it) { + const struct cx_iterator_s *iter = it; + struct cx_hash_map_element_s *elm = iter->elem_handle; + return &elm->key; +} + +static void *cx_hash_map_iter_current_value(const void *it) { + const struct cx_iterator_s *iter = it; + const struct cx_hash_map_s *map = iter->src_handle.c; + struct cx_hash_map_element_s *elm = iter->elem_handle; + if (map->base.collection.store_pointer) { + return *(void **) elm->data; + } else { + return elm->data; + } +} + +static bool cx_hash_map_iter_valid(const void *it) { + const struct cx_iterator_s *iter = it; + return iter->elem_handle != NULL; +} + +static void cx_hash_map_iter_next(void *it) { + struct cx_iterator_s *iter = it; + struct cx_hash_map_element_s *elm = iter->elem_handle; + struct cx_hash_map_s *map = iter->src_handle.m; + + // remove current element, if asked + if (iter->base.remove) { + + // clear the flag + iter->base.remove = false; + + // determine the next element + struct cx_hash_map_element_s *next = elm->next; + + // search the previous element + struct cx_hash_map_element_s *prev = NULL; + if (map->buckets[iter->slot] != elm) { + prev = map->buckets[iter->slot]; + while (prev->next != elm) { + prev = prev->next; + } + } + + // destroy + cx_invoke_destructor((struct cx_map_s *) map, elm->data); + + // unlink + cx_hash_map_unlink(map, iter->slot, prev, elm); + + // advance + elm = next; + } else { + // just advance + elm = elm->next; + iter->index++; + } + + // search the next bucket, if required + while (elm == NULL && ++iter->slot < map->bucket_count) { + elm = map->buckets[iter->slot]; + } + + // fill the struct with the next element + iter->elem_handle = elm; + if (elm == NULL) { + iter->kv_data.key = NULL; + iter->kv_data.value = NULL; + } else { + iter->kv_data.key = &elm->key; + if (map->base.collection.store_pointer) { + iter->kv_data.value = *(void **) elm->data; + } else { + iter->kv_data.value = elm->data; + } + } +} + +static CxIterator cx_hash_map_iterator( + const CxMap *map, + enum cx_map_iterator_type type +) { + CxIterator iter; + + iter.src_handle.c = map; + iter.elem_count = map->collection.size; + + switch (type) { + case CX_MAP_ITERATOR_PAIRS: + iter.elem_size = sizeof(CxMapEntry); + iter.base.current = cx_hash_map_iter_current_entry; + break; + case CX_MAP_ITERATOR_KEYS: + iter.elem_size = sizeof(CxHashKey); + iter.base.current = cx_hash_map_iter_current_key; + break; + case CX_MAP_ITERATOR_VALUES: + iter.elem_size = map->collection.elem_size; + iter.base.current = cx_hash_map_iter_current_value; + break; + default: + assert(false); + } + + iter.base.valid = cx_hash_map_iter_valid; + iter.base.next = cx_hash_map_iter_next; + iter.base.remove = false; + iter.base.mutating = false; + + iter.slot = 0; + iter.index = 0; + + if (map->collection.size > 0) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + struct cx_hash_map_element_s *elm = hash_map->buckets[0]; + while (elm == NULL) { + elm = hash_map->buckets[++iter.slot]; + } + iter.elem_handle = elm; + iter.kv_data.key = &elm->key; + if (map->collection.store_pointer) { + iter.kv_data.value = *(void **) elm->data; + } else { + iter.kv_data.value = elm->data; + } + } else { + iter.elem_handle = NULL; + iter.kv_data.key = NULL; + iter.kv_data.value = NULL; + } + + return iter; +} + +static cx_map_class cx_hash_map_class = { + cx_hash_map_destructor, + cx_hash_map_clear, + cx_hash_map_put, + cx_hash_map_get, + cx_hash_map_remove, + cx_hash_map_iterator, +}; + +CxMap *cxHashMapCreate( + const CxAllocator *allocator, + size_t itemsize, + size_t buckets +) { + if (buckets == 0) { + // implementation defined default + buckets = 16; + } + + struct cx_hash_map_s *map = cxCalloc(allocator, 1, + sizeof(struct cx_hash_map_s)); + if (map == NULL) return NULL; + + // initialize hash map members + map->bucket_count = buckets; + map->buckets = cxCalloc(allocator, buckets, + sizeof(struct cx_hash_map_element_s *)); + if (map->buckets == NULL) { + cxFree(allocator, map); + return NULL; + } + + // initialize base members + map->base.cl = &cx_hash_map_class; + map->base.collection.allocator = allocator; + + if (itemsize > 0) { + map->base.collection.store_pointer = false; + map->base.collection.elem_size = itemsize; + } else { + map->base.collection.store_pointer = true; + map->base.collection.elem_size = sizeof(void *); + } + + return (CxMap *) map; +} + +int cxMapRehash(CxMap *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + if (map->collection.size > ((hash_map->bucket_count * 3) >> 2)) { + + size_t new_bucket_count = (map->collection.size * 5) >> 1; + struct cx_hash_map_element_s **new_buckets = cxCalloc( + map->collection.allocator, + new_bucket_count, sizeof(struct cx_hash_map_element_s *) + ); + + if (new_buckets == NULL) { + return 1; + } + + // iterate through the elements and assign them to their new slots + cx_for_n(slot, hash_map->bucket_count) { + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + while (elm != NULL) { + struct cx_hash_map_element_s *next = elm->next; + size_t new_slot = elm->key.hash % new_bucket_count; + + // find position where to insert + struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot]; + struct cx_hash_map_element_s *bucket_prev = NULL; + while (bucket_next != NULL && + bucket_next->key.hash < elm->key.hash) { + bucket_prev = bucket_next; + bucket_next = bucket_next->next; + } + + // insert + if (bucket_prev == NULL) { + elm->next = new_buckets[new_slot]; + new_buckets[new_slot] = elm; + } else { + bucket_prev->next = elm; + elm->next = bucket_next; + } + + // advance + elm = next; + } + } + + // assign result to the map + hash_map->bucket_count = new_bucket_count; + cxFree(map->collection.allocator, hash_map->buckets); + hash_map->buckets = new_buckets; + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/iterator.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/iterator.h" + +#include <string.h> + +static bool cx_iter_valid(const void *it) { + const struct cx_iterator_s *iter = it; + return iter->index < iter->elem_count; +} + +static void *cx_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; + return iter->elem_handle; +} + +static void cx_iter_next_fast(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + iter->elem_count--; + // only move the last element when we are not currently aiming + // at the last element already + if (iter->index < iter->elem_count) { + void *last = ((char *) iter->src_handle.m) + + iter->elem_count * iter->elem_size; + memcpy(iter->elem_handle, last, iter->elem_size); + } + } else { + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size; + } +} + +static void cx_iter_next_slow(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + iter->elem_count--; + + // number of elements to move + size_t remaining = iter->elem_count - iter->index; + if (remaining > 0) { + memmove( + iter->elem_handle, + ((char *) iter->elem_handle) + iter->elem_size, + remaining * iter->elem_size + ); + } + } else { + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size; + } +} + +CxIterator cxMutIterator( + void *array, + size_t elem_size, + size_t elem_count, + bool remove_keeps_order +) { + CxIterator iter; + + iter.index = 0; + iter.src_handle.m = array; + iter.elem_handle = array; + iter.elem_size = elem_size; + iter.elem_count = array == NULL ? 0 : elem_count; + iter.base.valid = cx_iter_valid; + iter.base.current = cx_iter_current; + iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; + iter.base.remove = false; + iter.base.mutating = true; + + return iter; +} + +CxIterator cxIterator( + const void *array, + size_t elem_size, + size_t elem_count +) { + CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false); + iter.base.mutating = false; + return iter; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/linked_list.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,1117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/linked_list.h" +#include "cx/utils.h" +#include "cx/compare.h" +#include <string.h> +#include <assert.h> + +// LOW LEVEL LINKED LIST FUNCTIONS + +#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) +#define ll_prev(node) CX_LL_PTR(node, loc_prev) +#define ll_next(node) CX_LL_PTR(node, loc_next) +#define ll_advance(node) CX_LL_PTR(node, loc_advance) +#define ll_data(node) (((char*)(node))+loc_data) + +void *cx_linked_list_at( + const void *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +) { + assert(start != NULL); + assert(loc_advance >= 0); + size_t i = start_index; + const void *cur = start; + while (i != index && cur != NULL) { + cur = ll_advance(cur); + i < index ? i++ : i--; + } + return (void *) cur; +} + +ssize_t cx_linked_list_find( + const void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + const void *elem +) { + void *dummy; + return cx_linked_list_find_node( + &dummy, start, + loc_advance, loc_data, + cmp_func, elem + ); +} + +ssize_t cx_linked_list_find_node( + void **result, + const void *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + const void *elem +) { + assert(result != NULL); + assert(start != NULL); + assert(loc_advance >= 0); + assert(loc_data >= 0); + assert(cmp_func); + + const void *node = start; + ssize_t index = 0; + do { + void *current = ll_data(node); + if (cmp_func(current, elem) == 0) { + *result = (void*) node; + return index; + } + node = ll_advance(node); + index++; + } while (node != NULL); + *result = NULL; + return -1; +} + +void *cx_linked_list_first( + const void *node, + ptrdiff_t loc_prev +) { + return cx_linked_list_last(node, loc_prev); +} + +void *cx_linked_list_last( + const void *node, + ptrdiff_t loc_next +) { + assert(node != NULL); + assert(loc_next >= 0); + + const void *cur = node; + const void *last; + do { + last = cur; + } while ((cur = ll_next(cur)) != NULL); + + return (void *) last; +} + +void *cx_linked_list_prev( + const void *begin, + ptrdiff_t loc_next, + const void *node +) { + assert(begin != NULL); + assert(node != NULL); + assert(loc_next >= 0); + if (begin == node) return NULL; + const void *cur = begin; + const void *next; + while (1) { + next = ll_next(cur); + if (next == node) return (void *) cur; + cur = next; + } +} + +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert(loc_next >= 0); + ll_next(left) = right; + if (loc_prev >= 0) { + ll_prev(right) = left; + } +} + +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert (loc_next >= 0); + assert(ll_next(left) == right); + ll_next(left) = NULL; + if (loc_prev >= 0) { + assert(ll_prev(right) == left); + ll_prev(right) = NULL; + } +} + +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { + void *last; + if (end == NULL) { + assert(begin != NULL); + last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next); + } else { + last = *end; + } + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node); +} + +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node); +} + +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node); +} + +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +) { + // find the end of the chain, if not specified + if (insert_end == NULL) { + insert_end = cx_linked_list_last(insert_begin, loc_next); + } + + // determine the successor + void *successor; + if (node == NULL) { + assert(begin != NULL || (end != NULL && loc_prev >= 0)); + if (begin != NULL) { + successor = *begin; + *begin = insert_begin; + } else { + successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); + } + } else { + successor = ll_next(node); + cx_linked_list_link(node, insert_begin, loc_prev, loc_next); + } + + if (successor == NULL) { + // the list ends with the new chain + if (end != NULL) { + *end = insert_end; + } + } else { + cx_linked_list_link(insert_end, successor, loc_prev, loc_next); + } +} + +void cx_linked_list_insert_sorted( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node, + cx_compare_func cmp_func +) { + assert(ll_next(new_node) == NULL); + cx_linked_list_insert_sorted_chain( + begin, end, loc_prev, loc_next, new_node, cmp_func); +} + +void cx_linked_list_insert_sorted_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *insert_begin, + cx_compare_func cmp_func +) { + assert(begin != NULL); + assert(loc_next >= 0); + assert(insert_begin != NULL); + + // track currently observed nodes + void *dest_prev = NULL; + void *dest = *begin; + void *src = insert_begin; + + // special case: list is empty + if (dest == NULL) { + *begin = src; + if (end != NULL) { + *end = cx_linked_list_last(src, loc_next); + } + return; + } + + // search the list for insertion points + while (dest != NULL && src != NULL) { + // compare current list node with source node + // if less or equal, skip + if (cmp_func(dest, src) <= 0) { + dest_prev = dest; + dest = ll_next(dest); + continue; + } + + // determine chain of elements that can be inserted + void *end_of_chain = src; + void *next_in_chain = ll_next(src); + while (next_in_chain != NULL) { + // once we become larger than the list elem, break + if (cmp_func(dest, next_in_chain) <= 0) { + break; + } + // otherwise, we can insert one more + end_of_chain = next_in_chain; + next_in_chain = ll_next(next_in_chain); + } + + // insert the elements + if (dest_prev == NULL) { + // new begin + *begin = src; + } else { + cx_linked_list_link(dest_prev, src, loc_prev, loc_next); + } + cx_linked_list_link(end_of_chain, dest, loc_prev, loc_next); + + // continue with next + src = next_in_chain; + dest_prev = dest; + dest = ll_next(dest); + } + + // insert remaining items + if (src != NULL) { + cx_linked_list_link(dest_prev, src, loc_prev, loc_next); + } + + // determine new end of list, if requested + if (end != NULL) { + *end = cx_linked_list_last( + dest != NULL ? dest : dest_prev, loc_next); + } +} + +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) { + assert(node != NULL); + assert(loc_next >= 0); + assert(loc_prev >= 0 || begin != NULL); + + // find adjacent nodes + void *next = ll_next(node); + void *prev; + if (loc_prev >= 0) { + prev = ll_prev(node); + } else { + prev = cx_linked_list_prev(*begin, loc_next, node); + } + + // update next pointer of prev node, or set begin + if (prev == NULL) { + if (begin != NULL) { + *begin = next; + } + } else { + ll_next(prev) = next; + } + + // update prev pointer of next node, or set end + if (next == NULL) { + if (end != NULL) { + *end = prev; + } + } else if (loc_prev >= 0) { + ll_prev(next) = prev; + } +} + +size_t cx_linked_list_size( + const void *node, + ptrdiff_t loc_next +) { + assert(loc_next >= 0); + size_t size = 0; + while (node != NULL) { + node = ll_next(node); + size++; + } + return size; +} + +#ifndef CX_LINKED_LIST_SORT_SBO_SIZE +#define CX_LINKED_LIST_SORT_SBO_SIZE 1024 +#endif + +static void cx_linked_list_sort_merge( + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + size_t length, + void *ls, + void *le, + void *re, + cx_compare_func cmp_func, + void **begin, + void **end +) { + void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; + void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? + malloc(sizeof(void *) * length) : sbo; + if (sorted == NULL) abort(); + void *rc, *lc; + + lc = ls; + rc = le; + size_t n = 0; + while (lc && lc != le && rc != re) { + if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { + sorted[n] = lc; + lc = ll_next(lc); + } else { + sorted[n] = rc; + rc = ll_next(rc); + } + n++; + } + while (lc && lc != le) { + sorted[n] = lc; + lc = ll_next(lc); + n++; + } + while (rc && rc != re) { + sorted[n] = rc; + rc = ll_next(rc); + n++; + } + + // Update pointer + if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; + cx_for_n (i, length - 1) { + cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); + } + ll_next(sorted[length - 1]) = NULL; + + *begin = sorted[0]; + *end = sorted[length-1]; + if (sorted != sbo) { + free(sorted); + } +} + +void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) { + assert(begin != NULL); + assert(loc_next >= 0); + assert(loc_data >= 0); + assert(cmp_func); + + void *lc, *ls, *le, *re; + + // set start node + ls = *begin; + + // early exit when this list is empty + if (ls == NULL) return; + + // check how many elements are already sorted + lc = ls; + size_t ln = 1; + while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { + lc = ll_next(lc); + ln++; + } + le = ll_next(lc); + + // if first unsorted node is NULL, the list is already completely sorted + if (le != NULL) { + void *rc; + size_t rn = 1; + rc = le; + // skip already sorted elements + while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { + rc = ll_next(rc); + rn++; + } + re = ll_next(rc); + + // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them + void *sorted_begin, *sorted_end; + cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn, ls, le, re, cmp_func, + &sorted_begin, &sorted_end); + + // Something left? Sort it! + size_t remainder_length = cx_linked_list_size(re, loc_next); + if (remainder_length > 0) { + void *remainder = re; + cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); + + // merge sorted list with (also sorted) remainder + cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn + remainder_length, + sorted_begin, remainder, NULL, cmp_func, + &sorted_begin, &sorted_end); + } + *begin = sorted_begin; + if (end) *end = sorted_end; + } +} + +int cx_linked_list_compare( + const void *begin_left, + const void *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) { + const void *left = begin_left, *right = begin_right; + + while (left != NULL && right != NULL) { + const void *left_data = ll_data(left); + const void *right_data = ll_data(right); + int result = cmp_func(left_data, right_data); + if (result != 0) return result; + left = ll_advance(left); + right = ll_advance(right); + } + + if (left != NULL) { return 1; } + else if (right != NULL) { return -1; } + else { return 0; } +} + +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert(begin != NULL); + assert(loc_next >= 0); + + // swap all links + void *prev = NULL; + void *cur = *begin; + while (cur != NULL) { + void *next = ll_next(cur); + + ll_next(cur) = prev; + if (loc_prev >= 0) { + ll_prev(cur) = next; + } + + prev = cur; + cur = next; + } + + // update begin and end + if (end != NULL) { + *end = *begin; + } + *begin = prev; +} + +// HIGH LEVEL LINKED LIST IMPLEMENTATION + +typedef struct cx_linked_list_node cx_linked_list_node; +struct cx_linked_list_node { + cx_linked_list_node *prev; + cx_linked_list_node *next; + char payload[]; +}; + +#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) +#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) +#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) + +typedef struct { + struct cx_list_s base; + cx_linked_list_node *begin; + cx_linked_list_node *end; +} cx_linked_list; + +static cx_linked_list_node *cx_ll_node_at( + const cx_linked_list *list, + size_t index +) { + if (index >= list->base.collection.size) { + return NULL; + } else if (index > list->base.collection.size / 2) { + return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); + } else { + return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); + } +} + +static cx_linked_list_node *cx_ll_malloc_node(const struct cx_list_s *list) { + return cxMalloc(list->collection.allocator, + sizeof(cx_linked_list_node) + list->collection.elem_size); +} + +static int cx_ll_insert_at( + struct cx_list_s *list, + cx_linked_list_node *node, + const void *elem +) { + + // create the new new_node + cx_linked_list_node *new_node = cx_ll_malloc_node(list); + + // sortir if failed + if (new_node == NULL) return 1; + + // initialize new new_node + new_node->prev = new_node->next = NULL; + memcpy(new_node->payload, elem, list->collection.elem_size); + + // insert + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_insert_chain( + (void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, + node, new_node, new_node + ); + + // increase the size and return + list->collection.size++; + return 0; +} + +static size_t cx_ll_insert_array( + struct cx_list_s *list, + size_t index, + const void *array, + size_t n +) { + // out-of bounds and corner case check + if (index > list->collection.size || n == 0) return 0; + + // find position efficiently + cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); + + // perform first insert + if (0 != cx_ll_insert_at(list, node, array)) { + return 1; + } + + // is there more? + if (n == 1) return 1; + + // we now know exactly where we are + node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; + + // we can add the remaining nodes and immediately advance to the inserted node + const char *source = array; + for (size_t i = 1; i < n; i++) { + source += list->collection.elem_size; + if (0 != cx_ll_insert_at(list, node, source)) { + return i; + } + node = node->next; + } + return n; +} + +static int cx_ll_insert_element( + struct cx_list_s *list, + size_t index, + const void *element +) { + return 1 != cx_ll_insert_array(list, index, element, 1); +} + +static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func; + +static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) { + const cx_linked_list_node *left = l; + const cx_linked_list_node *right = r; + return cx_ll_insert_sorted_cmp_func(left->payload, right->payload); +} + +static size_t cx_ll_insert_sorted( + struct cx_list_s *list, + const void *array, + size_t n +) { + // special case + if (n == 0) return 0; + + // create a new chain of nodes + cx_linked_list_node *chain = cx_ll_malloc_node(list); + if (chain == NULL) return 0; + + memcpy(chain->payload, array, list->collection.elem_size); + chain->prev = NULL; + chain->next = NULL; + + // add all elements from the array to that chain + cx_linked_list_node *prev = chain; + const char *src = array; + size_t inserted = 1; + for (; inserted < n; inserted++) { + cx_linked_list_node *next = cx_ll_malloc_node(list); + if (next == NULL) break; + src += list->collection.elem_size; + memcpy(next->payload, src, list->collection.elem_size); + prev->next = next; + next->prev = prev; + prev = next; + } + prev->next = NULL; + + // invoke the low level function + cx_linked_list *ll = (cx_linked_list *) list; + cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; + cx_linked_list_insert_sorted_chain( + (void **) &ll->begin, + (void **) &ll->end, + CX_LL_LOC_PREV, + CX_LL_LOC_NEXT, + chain, + cx_ll_insert_sorted_cmp_helper + ); + + // adjust the list metadata + list->collection.size += inserted; + + return inserted; +} + +static int cx_ll_remove( + struct cx_list_s *list, + size_t index +) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = cx_ll_node_at(ll, index); + + // out-of-bounds check + if (node == NULL) return 1; + + // element destruction + cx_invoke_destructor(list, node->payload); + + // remove + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + + // adjust size + list->collection.size--; + + // free and return + cxFree(list->collection.allocator, node); + + return 0; +} + +static void cx_ll_clear(struct cx_list_s *list) { + if (list->collection.size == 0) return; + + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = ll->begin; + while (node != NULL) { + cx_invoke_destructor(list, node->payload); + cx_linked_list_node *next = node->next; + cxFree(list->collection.allocator, node); + node = next; + } + ll->begin = ll->end = NULL; + list->collection.size = 0; +} + +#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE +#define CX_LINKED_LIST_SWAP_SBO_SIZE 128 +#endif +unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE; + +static int cx_ll_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + if (i >= list->collection.size || j >= list->collection.size) return 1; + if (i == j) return 0; + + // perform an optimized search that finds both elements in one run + cx_linked_list *ll = (cx_linked_list *) list; + size_t mid = list->collection.size / 2; + size_t left, right; + if (i < j) { + left = i; + right = j; + } else { + left = j; + right = i; + } + cx_linked_list_node *nleft, *nright; + if (left < mid && right < mid) { + // case 1: both items left from mid + nleft = cx_ll_node_at(ll, left); + assert(nleft != NULL); + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else if (left >= mid && right >= mid) { + // case 2: both items right from mid + nright = cx_ll_node_at(ll, right); + assert(nright != NULL); + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } else { + // case 3: one item left, one item right + + // chose the closest to begin / end + size_t closest; + size_t other; + size_t diff2boundary = list->collection.size - right - 1; + if (left <= diff2boundary) { + closest = left; + other = right; + nleft = cx_ll_node_at(ll, left); + } else { + closest = right; + other = left; + diff2boundary = left; + nright = cx_ll_node_at(ll, right); + } + + // is other element closer to us or closer to boundary? + if (right - left <= diff2boundary) { + // search other element starting from already found element + if (closest == left) { + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else { + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } + } else { + // search other element starting at the boundary + if (closest == left) { + nright = cx_ll_node_at(ll, other); + } else { + nleft = cx_ll_node_at(ll, other); + } + } + } + + if (list->collection.elem_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { + cx_linked_list_node *prev = nleft->prev; + cx_linked_list_node *next = nright->next; + cx_linked_list_node *midstart = nleft->next; + cx_linked_list_node *midend = nright->prev; + + if (prev == NULL) { + ll->begin = nright; + } else { + prev->next = nright; + } + nright->prev = prev; + if (midstart == nright) { + // special case: both nodes are adjacent + nright->next = nleft; + nleft->prev = nright; + } else { + // likely case: a chain is between the two nodes + nright->next = midstart; + midstart->prev = nright; + midend->next = nleft; + nleft->prev = midend; + } + nleft->next = next; + if (next == NULL) { + ll->end = nleft; + } else { + next->prev = nleft; + } + } else { + // swap payloads to avoid relinking + char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; + memcpy(buf, nleft->payload, list->collection.elem_size); + memcpy(nleft->payload, nright->payload, list->collection.elem_size); + memcpy(nright->payload, buf, list->collection.elem_size); + } + + return 0; +} + +static void *cx_ll_at( + const struct cx_list_s *list, + size_t index +) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = cx_ll_node_at(ll, index); + return node == NULL ? NULL : node->payload; +} + +static ssize_t cx_ll_find_remove( + struct cx_list_s *list, + const void *elem, + bool remove +) { + if (remove) { + cx_linked_list *ll = ((cx_linked_list *) list); + cx_linked_list_node *node; + ssize_t index = cx_linked_list_find_node( + (void **) &node, + ll->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->collection.cmpfunc, elem + ); + if (node != NULL) { + cx_invoke_destructor(list, node->payload); + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + list->collection.size--; + cxFree(list->collection.allocator, node); + } + return index; + } else { + return cx_linked_list_find( + ((cx_linked_list *) list)->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->collection.cmpfunc, elem + ); + } +} + +static void cx_ll_sort(struct cx_list_s *list) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->collection.cmpfunc); +} + +static void cx_ll_reverse(struct cx_list_s *list) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); +} + +static int cx_ll_compare( + const struct cx_list_s *list, + const struct cx_list_s *other +) { + cx_linked_list *left = (cx_linked_list *) list; + cx_linked_list *right = (cx_linked_list *) other; + return cx_linked_list_compare(left->begin, right->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->collection.cmpfunc); +} + +static bool cx_ll_iter_valid(const void *it) { + const struct cx_iterator_s *iter = it; + return iter->elem_handle != NULL; +} + +static void cx_ll_iter_next(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + struct cx_list_s *list = iter->src_handle.m; + cx_linked_list *ll = iter->src_handle.m; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + cx_invoke_destructor(list, node->payload); + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + list->collection.size--; + cxFree(list->collection.allocator, node); + } else { + iter->index++; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + } +} + +static void cx_ll_iter_prev(void *it) { + struct cx_iterator_s *iter = it; + if (iter->base.remove) { + iter->base.remove = false; + struct cx_list_s *list = iter->src_handle.m; + cx_linked_list *ll = iter->src_handle.m; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->prev; + iter->index--; + cx_invoke_destructor(list, node->payload); + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + list->collection.size--; + cxFree(list->collection.allocator, node); + } else { + iter->index--; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->prev; + } +} + +static void *cx_ll_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; + cx_linked_list_node *node = iter->elem_handle; + return node->payload; +} + +static CxIterator cx_ll_iterator( + const struct cx_list_s *list, + size_t index, + bool backwards +) { + CxIterator iter; + iter.index = index; + iter.src_handle.c = list; + iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); + iter.elem_size = list->collection.elem_size; + iter.elem_count = list->collection.size; + iter.base.valid = cx_ll_iter_valid; + iter.base.current = cx_ll_iter_current; + iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; + iter.base.mutating = false; + iter.base.remove = false; + return iter; +} + +static int cx_ll_insert_iter( + CxIterator *iter, + const void *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle.m; + cx_linked_list_node *node = iter->elem_handle; + if (node != NULL) { + assert(prepend >= 0 && prepend <= 1); + cx_linked_list_node *choice[2] = {node, node->prev}; + int result = cx_ll_insert_at(list, choice[prepend], elem); + if (result == 0) { + iter->elem_count++; + if (prepend) { + iter->index++; + } + } + return result; + } else { + int result = cx_ll_insert_element(list, list->collection.size, elem); + if (result == 0) { + iter->elem_count++; + iter->index = list->collection.size; + } + return result; + } +} + +static void cx_ll_destructor(CxList *list) { + cx_linked_list *ll = (cx_linked_list *) list; + + cx_linked_list_node *node = ll->begin; + while (node) { + cx_invoke_destructor(list, node->payload); + void *next = node->next; + cxFree(list->collection.allocator, node); + node = next; + } + + cxFree(list->collection.allocator, list); +} + +static cx_list_class cx_linked_list_class = { + cx_ll_destructor, + cx_ll_insert_element, + cx_ll_insert_array, + cx_ll_insert_sorted, + cx_ll_insert_iter, + cx_ll_remove, + cx_ll_clear, + cx_ll_swap, + cx_ll_at, + cx_ll_find_remove, + cx_ll_sort, + cx_ll_compare, + cx_ll_reverse, + cx_ll_iterator, +}; + +CxList *cxLinkedListCreate( + const CxAllocator *allocator, + cx_compare_func comparator, + size_t elem_size +) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + + cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); + if (list == NULL) return NULL; + + list->base.cl = &cx_linked_list_class; + list->base.collection.allocator = allocator; + + if (elem_size > 0) { + list->base.collection.elem_size = elem_size; + list->base.collection.cmpfunc = comparator; + } else { + list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; + cxListStorePointers((CxList *) list); + } + + return (CxList *) list; +}
--- a/ucx/list.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/list.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,403 +26,462 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/list.h" +#include "cx/list.h" + +#include <string.h> + +// <editor-fold desc="Store Pointers Functionality"> + +static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; + +static int cx_pl_cmpfunc( + const void *l, + const void *r +) { + void *const *lptr = l; + void *const *rptr = r; + const void *left = lptr == NULL ? NULL : *lptr; + const void *right = rptr == NULL ? NULL : *rptr; + return cx_pl_cmpfunc_impl(left, right); +} + +static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) { + // cast away const - this is the hacky thing + struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; + cx_pl_cmpfunc_impl = l->cmpfunc; + l->cmpfunc = cx_pl_cmpfunc; +} -UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) { - return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data); +static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) { + // cast away const - this is the hacky thing + struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; + l->cmpfunc = cx_pl_cmpfunc_impl; +} + +static void cx_pl_destructor(struct cx_list_s *list) { + list->climpl->destructor(list); +} + +static int cx_pl_insert_element( + struct cx_list_s *list, + size_t index, + const void *element +) { + return list->climpl->insert_element(list, index, &element); +} + +static size_t cx_pl_insert_array( + struct cx_list_s *list, + size_t index, + const void *array, + size_t n +) { + return list->climpl->insert_array(list, index, array, n); } -UcxList *ucx_list_clone_a(UcxAllocator *alloc, const UcxList *l, - copy_func fnc, void *data) { - UcxList *ret = NULL; - while (l) { - if (fnc) { - ret = ucx_list_append_a(alloc, ret, fnc(l->data, data)); - } else { - ret = ucx_list_append_a(alloc, ret, l->data); - } - l = l->next; - } +static size_t cx_pl_insert_sorted( + struct cx_list_s *list, + const void *array, + size_t n +) { + cx_pl_hack_cmpfunc(list); + size_t result = list->climpl->insert_sorted(list, array, n); + cx_pl_unhack_cmpfunc(list); + return result; +} + +static int cx_pl_insert_iter( + struct cx_iterator_s *iter, + const void *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle.m; + return list->climpl->insert_iter(iter, &elem, prepend); +} + +static int cx_pl_remove( + struct cx_list_s *list, + size_t index +) { + return list->climpl->remove(list, index); +} + +static void cx_pl_clear(struct cx_list_s *list) { + list->climpl->clear(list); +} + +static int cx_pl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + return list->climpl->swap(list, i, j); +} + +static void *cx_pl_at( + const struct cx_list_s *list, + size_t index +) { + void **ptr = list->climpl->at(list, index); + return ptr == NULL ? NULL : *ptr; +} + +static ssize_t cx_pl_find_remove( + struct cx_list_s *list, + const void *elem, + bool remove +) { + cx_pl_hack_cmpfunc(list); + ssize_t ret = list->climpl->find_remove(list, &elem, remove); + cx_pl_unhack_cmpfunc(list); return ret; } -int ucx_list_equals(const UcxList *l1, const UcxList *l2, - cmp_func fnc, void* data) { - if (l1 == l2) return 1; - - while (l1 != NULL && l2 != NULL) { - if (fnc == NULL) { - if (l1->data != l2->data) return 0; - } else { - if (fnc(l1->data, l2->data, data) != 0) return 0; - } - l1 = l1->next; - l2 = l2->next; - } - - return (l1 == NULL && l2 == NULL); +static void cx_pl_sort(struct cx_list_s *list) { + cx_pl_hack_cmpfunc(list); + list->climpl->sort(list); + cx_pl_unhack_cmpfunc(list); } -void ucx_list_free(UcxList *l) { - ucx_list_free_a(ucx_default_allocator(), l); +static int cx_pl_compare( + const struct cx_list_s *list, + const struct cx_list_s *other +) { + cx_pl_hack_cmpfunc(list); + int ret = list->climpl->compare(list, other); + cx_pl_unhack_cmpfunc(list); + return ret; +} + +static void cx_pl_reverse(struct cx_list_s *list) { + list->climpl->reverse(list); +} + +static void *cx_pl_iter_current(const void *it) { + const struct cx_iterator_s *iter = it; + void **ptr = iter->base.current_impl(it); + return ptr == NULL ? NULL : *ptr; } -void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) { - UcxList *e = l, *f; - while (e != NULL) { - f = e; - e = e->next; - alfree(alloc, f); - } -} - -void ucx_list_free_content(UcxList* list, ucx_destructor destr) { - if (!destr) destr = free; - while (list != NULL) { - destr(list->data); - list = list->next; - } +static struct cx_iterator_s cx_pl_iterator( + const struct cx_list_s *list, + size_t index, + bool backwards +) { + struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); + iter.base.current_impl = iter.base.current; + iter.base.current = cx_pl_iter_current; + return iter; } -UcxList *ucx_list_append(UcxList *l, void *data) { - return ucx_list_append_a(ucx_default_allocator(), l, data); -} +static cx_list_class cx_pointer_list_class = { + cx_pl_destructor, + cx_pl_insert_element, + cx_pl_insert_array, + cx_pl_insert_sorted, + cx_pl_insert_iter, + cx_pl_remove, + cx_pl_clear, + cx_pl_swap, + cx_pl_at, + cx_pl_find_remove, + cx_pl_sort, + cx_pl_compare, + cx_pl_reverse, + cx_pl_iterator, +}; -UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList)); - if (!nl) { - return NULL; - } - - nl->data = data; - nl->next = NULL; - if (l) { - UcxList *t = ucx_list_last(l); - t->next = nl; - nl->prev = t; - return l; - } else { - nl->prev = NULL; - return nl; +void cxListStoreObjects(CxList *list) { + list->collection.store_pointer = false; + if (list->climpl != NULL) { + list->cl = list->climpl; + list->climpl = NULL; } } -UcxList *ucx_list_prepend(UcxList *l, void *data) { - return ucx_list_prepend_a(ucx_default_allocator(), l, data); -} - -UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = ucx_list_append_a(alloc, NULL, data); - if (!nl) { - return NULL; - } - l = ucx_list_first(l); - - if (l) { - nl->next = l; - l->prev = nl; - } - return nl; -} - -UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) { - if (l1) { - UcxList *last = ucx_list_last(l1); - last->next = l2; - if (l2) { - l2->prev = last; - } - return l1; - } else { - return l2; - } +void cxListStorePointers(CxList *list) { + list->collection.elem_size = sizeof(void *); + list->collection.store_pointer = true; + list->climpl = list->cl; + list->cl = &cx_pointer_list_class; } -UcxList *ucx_list_last(const UcxList *l) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next != NULL) { - e = e->next; - } - return (UcxList*)e; -} +// </editor-fold> -ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) { - ssize_t index = 0; - while (list) { - if (list == elem) { - return index; - } - list = list->next; - index++; - } - return -1; +// <editor-fold desc="empty list implementation"> + +static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) { + // this is a noop, but MUST be implemented } -UcxList *ucx_list_get(const UcxList *l, size_t index) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next && index > 0) { - e = e->next; - index--; - } - - return (UcxList*)(index == 0 ? e : NULL); +static void *cx_emptyl_at( + __attribute__((__unused__)) const struct cx_list_s *list, + __attribute__((__unused__)) size_t index +) { + return NULL; } -ssize_t ucx_list_find(const UcxList *l, void *elem, - cmp_func fnc, void *cmpdata) { - ssize_t index = 0; - UCX_FOREACH(e, l) { - if (fnc) { - if (fnc(elem, e->data, cmpdata) == 0) { - return index; - } - } else { - if (elem == e->data) { - return index; - } - } - index++; - } +static ssize_t cx_emptyl_find_remove( + __attribute__((__unused__)) struct cx_list_s *list, + __attribute__((__unused__)) const void *elem, + __attribute__((__unused__)) bool remove +) { return -1; } -int ucx_list_contains(const UcxList *l, void *elem, - cmp_func fnc, void *cmpdata) { - return ucx_list_find(l, elem, fnc, cmpdata) > -1; +static bool cx_emptyl_iter_valid(__attribute__((__unused__)) const void *iter) { + return false; } -size_t ucx_list_size(const UcxList *l) { - if (l == NULL) return 0; - - const UcxList *e = l; - size_t s = 1; - while (e->next != NULL) { - e = e->next; - s++; - } - - return s; +static CxIterator cx_emptyl_iterator( + const struct cx_list_s *list, + size_t index, + __attribute__((__unused__)) bool backwards +) { + CxIterator iter = {0}; + iter.src_handle.c = list; + iter.index = index; + iter.base.valid = cx_emptyl_iter_valid; + return iter; } -static UcxList *ucx_list_sort_merge(size_t length, - UcxList* ls, UcxList* le, UcxList* re, - cmp_func fnc, void* data) { +static cx_list_class cx_empty_list_class = { + cx_emptyl_noop, + NULL, + NULL, + NULL, + NULL, + NULL, + cx_emptyl_noop, + NULL, + cx_emptyl_at, + cx_emptyl_find_remove, + cx_emptyl_noop, + NULL, + cx_emptyl_noop, + cx_emptyl_iterator, +}; - UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length); - UcxList *rc, *lc; +CxList cx_empty_list = { + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false + }, + &cx_empty_list_class, + NULL +}; + +CxList *const cxEmptyList = &cx_empty_list; + +// </editor-fold> + +#define invoke_list_func(name, list, ...) \ + ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \ + (list, __VA_ARGS__) - lc = ls; rc = le; - size_t n = 0; - while (lc && lc != le && rc != re) { - if (fnc(lc->data, rc->data, data) <= 0) { - sorted[n] = lc; - lc = lc->next; +size_t cx_list_default_insert_array( + struct cx_list_s *list, + size_t index, + const void *data, + size_t n +) { + size_t elem_size = list->collection.elem_size; + const char *src = data; + size_t i = 0; + for (; i < n; i++) { + if (0 != invoke_list_func(insert_element, + list, index + i, src + (i * elem_size))) { + return i; + } + } + return i; +} + +size_t cx_list_default_insert_sorted( + struct cx_list_s *list, + const void *sorted_data, + size_t n +) { + // corner case + if (n == 0) return 0; + + size_t elem_size = list->collection.elem_size; + cx_compare_func cmp = list->collection.cmpfunc; + const char *src = sorted_data; + + // track indices and number of inserted items + size_t di = 0, si = 0, inserted = 0; + + // search the list for insertion points + for (; di < list->collection.size; di++) { + const void *list_elm = invoke_list_func(at, list, di); + + // compare current list element with first source element + // if less or equal, skip + if (cmp(list_elm, src) <= 0) { + continue; + } + + // determine number of consecutive elements that can be inserted + size_t ins = 1; + const char *next = src; + while (++si < n) { + next += elem_size; + // once we become larger than the list elem, break + if (cmp(list_elm, next) <= 0) { + break; + } + // otherwise, we can insert one more + ins++; + } + + // insert the elements at location si + if (ins == 1) { + if (0 != invoke_list_func(insert_element, + list, di, src)) + return inserted; } else { - sorted[n] = rc; - rc = rc->next; + size_t r = invoke_list_func(insert_array, list, di, src, ins); + if (r < ins) return inserted + r; } - n++; - } - while (lc && lc != le) { - sorted[n] = lc; - lc = lc->next; - n++; - } - while (rc && rc != re) { - sorted[n] = rc; - rc = rc->next; - n++; + inserted += ins; + di += ins; + + // everything inserted? + if (inserted == n) return inserted; + src = next; } - // Update pointer - sorted[0]->prev = NULL; - for (int i = 0 ; i < length-1 ; i++) { - sorted[i]->next = sorted[i+1]; - sorted[i+1]->prev = sorted[i]; + // insert remaining items + if (si < n) { + inserted += invoke_list_func(insert_array, list, di, src, n - si); } - sorted[length-1]->next = NULL; - UcxList *ret = sorted[0]; - free(sorted); - return ret; + return inserted; } -UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) { - if (l == NULL) { - return NULL; +void cx_list_default_sort(struct cx_list_s *list) { + size_t elem_size = list->collection.elem_size; + size_t list_size = list->collection.size; + void *tmp = malloc(elem_size * list_size); + if (tmp == NULL) abort(); + + // copy elements from source array + char *loc = tmp; + for (size_t i = 0; i < list_size; i++) { + void *src = invoke_list_func(at, list, i); + memcpy(loc, src, elem_size); + loc += elem_size; + } + + // qsort + qsort(tmp, list_size, elem_size, + list->collection.cmpfunc); + + // copy elements back + loc = tmp; + for (size_t i = 0; i < list_size; i++) { + void *dest = invoke_list_func(at, list, i); + memcpy(dest, loc, elem_size); + loc += elem_size; } - UcxList *lc; - size_t ln = 1; + free(tmp); +} + +int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) { + if (i == j) return 0; + if (i >= list->collection.size) return 1; + if (j >= list->collection.size) return 1; + + size_t elem_size = list->collection.elem_size; + + void *tmp = malloc(elem_size); + if (tmp == NULL) return 1; + + void *ip = invoke_list_func(at, list, i); + void *jp = invoke_list_func(at, list, j); + + memcpy(tmp, ip, elem_size); + memcpy(ip, jp, elem_size); + memcpy(jp, tmp, elem_size); + + free(tmp); + + return 0; +} + +void cxListDestroy(CxList *list) { + list->cl->destructor(list); +} + +int cxListCompare( + const CxList *list, + const CxList *other +) { + bool cannot_optimize = false; - UcxList *ls = l, *le, *re; - - // check how many elements are already sorted - lc = ls; - while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) { - lc = lc->next; - ln++; + // if one is storing pointers but the other is not + cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer; + + // if one class is wrapped but the other is not + cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL); + + // if the compare functions do not match or both are NULL + if (!cannot_optimize) { + cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ? + list->climpl->compare : list->cl->compare); + cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ? + other->climpl->compare : other->cl->compare); + cannot_optimize |= list_cmp != other_cmp; + cannot_optimize |= list_cmp == NULL; } - le = lc->next; - if (le == NULL) { - return l; // this list is already sorted :) + if (cannot_optimize) { + // lists are definitely different - cannot use internal compare function + if (list->collection.size == other->collection.size) { + CxIterator left = list->cl->iterator(list, 0, false); + CxIterator right = other->cl->iterator(other, 0, false); + for (size_t i = 0; i < list->collection.size; i++) { + void *leftValue = cxIteratorCurrent(left); + void *rightValue = cxIteratorCurrent(right); + int d = list->collection.cmpfunc(leftValue, rightValue); + if (d != 0) { + return d; + } + cxIteratorNext(left); + cxIteratorNext(right); + } + return 0; + } else { + return list->collection.size < other->collection.size ? -1 : 1; + } } else { - UcxList *rc; - size_t rn = 1; - rc = le; - // skip already sorted elements - while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) { - rc = rc->next; - rn++; - } - re = rc->next; - - // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them - UcxList *sorted = ucx_list_sort_merge(ln+rn, - ls, le, re, - fnc, data); - - // Something left? Sort it! - size_t remainder_length = ucx_list_size(re); - if (remainder_length > 0) { - UcxList *remainder = ucx_list_sort(re, fnc, data); - - // merge sorted list with (also sorted) remainder - l = ucx_list_sort_merge(ln+rn+remainder_length, - sorted, remainder, NULL, fnc, data); - } else { - // no remainder - we've got our sorted list - l = sorted; - } - - return l; + // lists are compatible + return list->cl->compare(list, other); } } -UcxList *ucx_list_first(const UcxList *l) { - if (!l) { - return NULL; - } - - const UcxList *e = l; - while (e->prev) { - e = e->prev; - } - return (UcxList *)e; -} - -UcxList *ucx_list_remove(UcxList *l, UcxList *e) { - return ucx_list_remove_a(ucx_default_allocator(), l, e); -} - -UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) { - if (l == e) { - l = e->next; - } - - if (e->next) { - e->next->prev = e->prev; - } - - if (e->prev) { - e->prev->next = e->next; - } - - alfree(alloc, e); - return l; +CxIterator cxListMutIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, false); + it.base.mutating = true; + return it; } - -static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata, - int op) { - - UcxList *res = NULL; - UcxList *cur = NULL; - const UcxList *src = left; - - do { - UCX_FOREACH(node, src) { - void* elem = node->data; - if ( - (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) || - (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) || - (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) { - UcxList *nl = almalloc(allocator, sizeof(UcxList)); - nl->prev = cur; - nl->next = NULL; - if (cpfnc) { - nl->data = cpfnc(elem, cpdata); - } else { - nl->data = elem; - } - if (cur != NULL) - cur->next = nl; - cur = nl; - if (res == NULL) - res = cur; - } - } - if (op == 0 && src == left) - src = right; - else - src = NULL; - } while (src != NULL); - - return res; +CxIterator cxListMutBackwardsIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, true); + it.base.mutating = true; + return it; } - -UcxList* ucx_list_union(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_union_a(ucx_default_allocator(), - left, right, cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_union_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 0); -} - -UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_intersection_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_intersection_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 1); -} - -UcxList* ucx_list_difference(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_difference_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_difference_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 2); -}
--- a/ucx/logging.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/logging.h" - -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <time.h> - -UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) { - UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger)); - if (logger != NULL) { - logger->stream = stream; - logger->writer = (write_func)fwrite; - logger->dateformat = (char*) "%F %T %z "; - logger->level = level; - logger->mask = mask; - logger->levels = ucx_map_new(8); - - unsigned int l; - l = UCX_LOGGER_ERROR; - ucx_map_int_put(logger->levels, l, (void*) "[ERROR]"); - l = UCX_LOGGER_WARN; - ucx_map_int_put(logger->levels, l, (void*) "[WARNING]"); - l = UCX_LOGGER_INFO; - ucx_map_int_put(logger->levels, l, (void*) "[INFO]"); - l = UCX_LOGGER_DEBUG; - ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]"); - l = UCX_LOGGER_TRACE; - ucx_map_int_put(logger->levels, l, (void*) "[TRACE]"); - } - - return logger; -} - -void ucx_logger_free(UcxLogger *logger) { - ucx_map_free(logger->levels); - free(logger); -} - -// estimated max. message length (documented) -#define UCX_LOGGER_MSGMAX 4096 - -void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, - const unsigned int line, const char *format, ...) { - if (level <= logger->level) { - char msg[UCX_LOGGER_MSGMAX]; - const char *text; - size_t k = 0; - size_t n; - - if ((logger->mask & UCX_LOGGER_LEVEL) > 0) { - text = (const char*) ucx_map_int_get(logger->levels, level); - if (!text) { - text = "[UNKNOWN]"; - } - n = strlen(text); - n = n > 256 ? 256 : n; - memcpy(msg+k, text, n); - k += n; - msg[k++] = ' '; - } - if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) { - time_t now = time(NULL); - k += strftime(msg+k, 128, logger->dateformat, localtime(&now)); - } - if ((logger->mask & UCX_LOGGER_SOURCE) > 0) { - char *fpart = strrchr(file, '/'); - if (fpart) file = fpart+1; - fpart = strrchr(file, '\\'); - if (fpart) file = fpart+1; - n = strlen(file); - memcpy(msg+k, file, n); - k += n; - k += sprintf(msg+k, ":%u ", line); - } - - if (k > 0) { - msg[k++] = '-'; msg[k++] = ' '; - } - - va_list args; - va_start (args, format); - k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args); - va_end (args); - - msg[k++] = '\n'; - - logger->writer(msg, 1, k, logger->stream); - } -}
--- a/ucx/map.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/map.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,402 +1,102 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/map.h" - -#include <stdlib.h> -#include <string.h> - -UcxMap *ucx_map_new(size_t size) { - return ucx_map_new_a(NULL, size); -} - -UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) { - if(size == 0) { - size = 16; - } - - if(!allocator) { - allocator = ucx_default_allocator(); - } - - UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap)); - if (!map) { - return NULL; - } - - map->allocator = allocator; - map->map = (UcxMapElement**)alcalloc( - allocator, size, sizeof(UcxMapElement*)); - if(map->map == NULL) { - alfree(allocator, map); - return NULL; - } - map->size = size; - map->count = 0; - - return map; -} - -static void ucx_map_free_elmlist_contents(UcxMap *map) { - for (size_t n = 0 ; n < map->size ; n++) { - UcxMapElement *elem = map->map[n]; - if (elem != NULL) { - do { - UcxMapElement *next = elem->next; - alfree(map->allocator, elem->key.data); - alfree(map->allocator, elem); - elem = next; - } while (elem != NULL); - } - } -} - -void ucx_map_free(UcxMap *map) { - ucx_map_free_elmlist_contents(map); - alfree(map->allocator, map->map); - alfree(map->allocator, map); -} - -void ucx_map_free_content(UcxMap *map, ucx_destructor destr) { - UcxMapIterator iter = ucx_map_iterator(map); - void *val; - UCX_MAP_FOREACH(key, val, iter) { - if (destr) { - destr(val); - } else { - alfree(map->allocator, val); - } - } -} - -void ucx_map_clear(UcxMap *map) { - if (map->count == 0) { - return; // nothing to do - } - ucx_map_free_elmlist_contents(map); - memset(map->map, 0, map->size*sizeof(UcxMapElement*)); - map->count = 0; -} - -int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data) { - UcxMapIterator i = ucx_map_iterator(from); - void *value; - UCX_MAP_FOREACH(key, value, i) { - if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) { - return 1; - } - } - return 0; -} - -UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data) { - return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data); -} - -UcxMap *ucx_map_clone_a(UcxAllocator *allocator, - UcxMap const *map, copy_func fnc, void *data) { - size_t bs = (map->count * 5) >> 1; - UcxMap *newmap = ucx_map_new_a(allocator, bs > map->size ? bs : map->size); - if (!newmap) { - return NULL; - } - ucx_map_copy(map, newmap, fnc, data); - return newmap; -} - -int ucx_map_rehash(UcxMap *map) { - size_t load = (map->size * 3) >> 2; - if (map->count > load) { - UcxMap oldmap; - oldmap.map = map->map; - oldmap.size = map->size; - oldmap.count = map->count; - oldmap.allocator = map->allocator; - - map->size = (map->count * 5) >> 1; - map->map = (UcxMapElement**)alcalloc( - map->allocator, map->size, sizeof(UcxMapElement*)); - if (!map->map) { - *map = oldmap; - return 1; - } - map->count = 0; - ucx_map_copy(&oldmap, map, NULL, NULL); - - /* free the UcxMapElement list of oldmap */ - ucx_map_free_elmlist_contents(&oldmap); - alfree(map->allocator, oldmap.map); - } - return 0; -} - -int ucx_map_put(UcxMap *map, UcxKey key, void *data) { - UcxAllocator *allocator = map->allocator; - - if (key.hash == 0) { - key.hash = ucx_hash((const char*)key.data, key.len); - } - - struct UcxMapKey mapkey; - mapkey.hash = key.hash; - - size_t slot = mapkey.hash%map->size; - UcxMapElement *elm = map->map[slot]; - UcxMapElement *prev = NULL; - - while (elm && elm->key.hash < mapkey.hash) { - prev = elm; - elm = elm->next; - } - - if (!elm || elm->key.hash != mapkey.hash) { - UcxMapElement *e = (UcxMapElement*)almalloc( - allocator, sizeof(UcxMapElement)); - if (!e) { - return -1; - } - e->key.data = NULL; - if (prev) { - prev->next = e; - } else { - map->map[slot] = e; - } - e->next = elm; - elm = e; - } - - if (!elm->key.data) { - void *kd = almalloc(allocator, key.len); - if (!kd) { - return -1; - } - memcpy(kd, key.data, key.len); - mapkey.data = kd; - mapkey.len = key.len; - elm->key = mapkey; - map->count++; - } - elm->data = data; - - return 0; -} - -static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) { - if(key.hash == 0) { - key.hash = ucx_hash((const char*)key.data, key.len); - } - - size_t slot = key.hash%map->size; - UcxMapElement *elm = map->map[slot]; - UcxMapElement *pelm = NULL; - while (elm && elm->key.hash <= key.hash) { - if(elm->key.hash == key.hash) { - int n = (key.len > elm->key.len) ? elm->key.len : key.len; - if (memcmp(elm->key.data, key.data, n) == 0) { - void *data = elm->data; - if (remove) { - if (pelm) { - pelm->next = elm->next; - } else { - map->map[slot] = elm->next; - } - alfree(map->allocator, elm->key.data); - alfree(map->allocator, elm); - map->count--; - } - - return data; - } - } - pelm = elm; - elm = pelm->next; - } - - return NULL; -} - -void *ucx_map_get(UcxMap const *map, UcxKey key) { - return ucx_map_get_and_remove((UcxMap *)map, key, 0); -} - -void *ucx_map_remove(UcxMap *map, UcxKey key) { - return ucx_map_get_and_remove(map, key, 1); -} - -UcxKey ucx_key(const void *data, size_t len) { - UcxKey key; - key.data = data; - key.len = len; - key.hash = ucx_hash((const char*)data, len); - return key; -} - - -int ucx_hash(const char *data, size_t len) { - /* murmur hash 2 */ - - int m = 0x5bd1e995; - int r = 24; - - int h = 25 ^ len; - - int i = 0; - while (len >= 4) { - int k = data[i + 0] & 0xFF; - k |= (data[i + 1] & 0xFF) << 8; - k |= (data[i + 2] & 0xFF) << 16; - k |= (data[i + 3] & 0xFF) << 24; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - i += 4; - len -= 4; - } - - switch (len) { - case 3: h ^= (data[i + 2] & 0xFF) << 16; - /* no break */ - case 2: h ^= (data[i + 1] & 0xFF) << 8; - /* no break */ - case 1: h ^= (data[i + 0] & 0xFF); h *= m; - /* no break */ - } - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -UcxMapIterator ucx_map_iterator(UcxMap const *map) { - UcxMapIterator i; - i.map = map; - i.cur = NULL; - i.index = 0; - return i; -} - -int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) { - UcxMapElement *e = i->cur; - - if (e) { - e = e->next; - } else { - e = i->map->map[0]; - } - - while (i->index < i->map->size) { - if (e) { - if (e->data) { - i->cur = e; - *elm = e->data; - key->data = e->key.data; - key->hash = e->key.hash; - key->len = e->key.len; - return 1; - } - - e = e->next; - } else { - i->index++; - - if (i->index < i->map->size) { - e = i->map->map[i->index]; - } - } - } - - return 0; -} - -UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_union_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_union_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata); - ucx_map_copy(second, result, cpfnc, cpdata); - return result; -} - -UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_intersection_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_intersection_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ? - first->size : second->size); - - UcxMapIterator iter = ucx_map_iterator(first); - void* value; - UCX_MAP_FOREACH(key, value, iter) { - if (ucx_map_get(second, key)) { - ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value); - } - } - - return result; -} - -UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_difference_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_difference_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - - UcxMap *result = ucx_map_new_a(allocator, first->size - second->count); - - UcxMapIterator iter = ucx_map_iterator(first); - void* value; - UCX_MAP_FOREACH(key, value, iter) { - if (!ucx_map_get(second, key)) { - ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value); - } - } - - ucx_map_rehash(result); - return result; -} \ No newline at end of file +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/map.h" +#include <string.h> + +// <editor-fold desc="empty map implementation"> + +static void cx_empty_map_noop(__attribute__((__unused__)) CxMap *map) { + // this is a noop, but MUST be implemented +} + +static void *cx_empty_map_get( + __attribute__((__unused__)) const CxMap *map, + __attribute__((__unused__)) CxHashKey key +) { + return NULL; +} + +static bool cx_empty_map_iter_valid(__attribute__((__unused__)) const void *iter) { + return false; +} + +static CxIterator cx_empty_map_iterator( + const struct cx_map_s *map, + __attribute__((__unused__)) enum cx_map_iterator_type type +) { + CxIterator iter = {0}; + iter.src_handle.c = map; + iter.base.valid = cx_empty_map_iter_valid; + return iter; +} + +static struct cx_map_class_s cx_empty_map_class = { + cx_empty_map_noop, + cx_empty_map_noop, + NULL, + cx_empty_map_get, + NULL, + cx_empty_map_iterator +}; + +CxMap cx_empty_map = { + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false + }, + &cx_empty_map_class +}; + +CxMap *const cxEmptyMap = &cx_empty_map; + +// </editor-fold> + +CxIterator cxMapMutIteratorValues(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); + it.base.mutating = true; + return it; +} + +CxIterator cxMapMutIteratorKeys(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); + it.base.mutating = true; + return it; +} + +CxIterator cxMapMutIterator(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); + it.base.mutating = true; + return it; +}
--- a/ucx/mempool.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/mempool.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,212 +26,207 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/mempool.h" - -#include <stdlib.h> +#include "cx/mempool.h" +#include "cx/utils.h" #include <string.h> -#include <stdio.h> -#ifdef __cplusplus -#define __STDC_FORMAT_MACROS -#endif -#include <inttypes.h> -/** Capsule for destructible memory chunks. */ -typedef struct { - /** The destructor for the memory chunk. */ - ucx_destructor destructor; - /** - * First byte of the memory chunk. - * Note, that the address <code>&c</code> is also the address - * of the whole memory chunk. - */ - char c; -} ucx_memchunk; - -/** Capsule for data and its destructor. */ -typedef struct { - /** The destructor for the data. */ - ucx_destructor destructor; - /** A pointer to the data. */ - void *ptr; -} ucx_regdestr; - -#ifdef __cplusplus -extern "C" -#endif -void ucx_mempool_shared_destr(void* ptr) { - ucx_regdestr *rd = (ucx_regdestr*)ptr; - rd->destructor(rd->ptr); -} +struct cx_mempool_memory_s { + /** The destructor. */ + cx_destructor_func destructor; + /** The actual memory. */ + char c[]; +}; -UcxMempool *ucx_mempool_new(size_t n) { - size_t poolsz; - if(ucx_szmul(n, sizeof(void*), &poolsz)) { - return NULL; - } - - UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool)); - if (!pool) { - return NULL; - } - - pool->data = (void**) malloc(poolsz); - if (pool->data == NULL) { - free(pool); - return NULL; - } - - pool->ndata = 0; - pool->size = n; - - UcxAllocator *allocator = (UcxAllocator*)malloc(sizeof(UcxAllocator)); - if(!allocator) { - free(pool->data); - free(pool); - return NULL; - } - allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc; - allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc; - allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc; - allocator->free = (ucx_allocator_free)ucx_mempool_free; - allocator->pool = pool; - pool->allocator = allocator; - - return pool; -} +static void *cx_mempool_malloc( + void *p, + size_t n +) { + struct cx_mempool_s *pool = p; -int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) { - if (newcap < pool->ndata) { - return 1; - } - - size_t newcapsz; - if(ucx_szmul(newcap, sizeof(void*), &newcapsz)) { - return 1; - } - - void **data = (void**) realloc(pool->data, newcapsz); - if (data) { - pool->data = data; - pool->size = newcap; - return 0; - } else { - return 1; - } -} - -void *ucx_mempool_malloc(UcxMempool *pool, size_t n) { - if(((size_t)-1) - sizeof(ucx_destructor) < n) { - return NULL; - } - - if (pool->ndata >= pool->size) { - size_t newcap = pool->size*2; - if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) { + if (pool->size >= pool->capacity) { + size_t newcap = pool->capacity - (pool->capacity % 16) + 16; + struct cx_mempool_memory_s **newdata = realloc(pool->data, newcap*sizeof(struct cx_mempool_memory_s*)); + if (newdata == NULL) { return NULL; } + pool->data = newdata; + pool->capacity = newcap; } - void *p = malloc(sizeof(ucx_destructor) + n); - ucx_memchunk *mem = (ucx_memchunk*)p; - if (!mem) { + struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n); + if (mem == NULL) { return NULL; } - mem->destructor = NULL; - pool->data[pool->ndata] = mem; - pool->ndata++; + mem->destructor = pool->auto_destr; + pool->data[pool->size] = mem; + pool->size++; - return &(mem->c); + return mem->c; } -void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) { +static void *cx_mempool_calloc( + void *p, + size_t nelem, + size_t elsize +) { size_t msz; - if(ucx_szmul(nelem, elsize, &msz)) { + if (cx_szmul(nelem, elsize, &msz)) { return NULL; } - - void *ptr = ucx_mempool_malloc(pool, msz); - if (!ptr) { + void *ptr = cx_mempool_malloc(p, msz); + if (ptr == NULL) { return NULL; } memset(ptr, 0, nelem * elsize); return ptr; } -void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) { - if(((size_t)-1) - sizeof(ucx_destructor) < n) { - return NULL; - } - - char *mem = ((char*)ptr) - sizeof(ucx_destructor); - char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor)); - if (!newm) { +static void *cx_mempool_realloc( + void *p, + void *ptr, + size_t n +) { + struct cx_mempool_s *pool = p; + + struct cx_mempool_memory_s *mem, *newm; + mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); + newm = realloc(mem, n + sizeof(cx_destructor_func)); + + if (newm == NULL) { return NULL; } if (mem != newm) { - for(size_t i=0 ; i < pool->ndata ; i++) { - if(pool->data[i] == mem) { + cx_for_n(i, pool->size) { + if (pool->data[i] == mem) { pool->data[i] = newm; - return newm + sizeof(ucx_destructor); + return ((char*)newm) + sizeof(cx_destructor_func); } } - fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", - (intptr_t)ptr, (intptr_t)pool); abort(); } else { - return newm + sizeof(ucx_destructor); + return ptr; } } -void ucx_mempool_free(UcxMempool *pool, void *ptr) { - ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor)); - for(size_t i=0 ; i<pool->ndata ; i++) { - if(chunk == pool->data[i]) { - if(chunk->destructor != NULL) { - chunk->destructor(&(chunk->c)); +static void cx_mempool_free( + void *p, + void *ptr +) { + struct cx_mempool_s *pool = p; + + struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) + ((char *) ptr - sizeof(cx_destructor_func)); + + cx_for_n(i, pool->size) { + if (mem == pool->data[i]) { + if (mem->destructor) { + mem->destructor(mem->c); } - free(chunk); - size_t last_index = pool->ndata - 1; - if(i != last_index) { + free(mem); + size_t last_index = pool->size - 1; + if (i != last_index) { pool->data[i] = pool->data[last_index]; pool->data[last_index] = NULL; } - pool->ndata--; + pool->size--; return; } } - fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", - (intptr_t)ptr, (intptr_t)pool); abort(); } -void ucx_mempool_destroy(UcxMempool *pool) { - ucx_memchunk *chunk; - for(size_t i=0 ; i<pool->ndata ; i++) { - chunk = (ucx_memchunk*) pool->data[i]; - if(chunk) { - if(chunk->destructor) { - chunk->destructor(&(chunk->c)); - } - free(chunk); +void cxMempoolDestroy(CxMempool *pool) { + struct cx_mempool_memory_s *mem; + cx_for_n(i, pool->size) { + mem = pool->data[i]; + if (mem->destructor) { + mem->destructor(mem->c); } + free(mem); } free(pool->data); - free(pool->allocator); + free((void*) pool->allocator); free(pool); } -void ucx_mempool_set_destr(void *ptr, ucx_destructor func) { - *(ucx_destructor*)((char*)ptr-sizeof(ucx_destructor)) = func; +void cxMempoolSetDestructor( + void *ptr, + cx_destructor_func func +) { + *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; +} + +struct cx_mempool_foreign_mem_s { + cx_destructor_func destr; + void* mem; +}; + +static void cx_mempool_destr_foreign_mem(void* ptr) { + struct cx_mempool_foreign_mem_s *fm = ptr; + fm->destr(fm->mem); +} + +int cxMempoolRegister( + CxMempool *pool, + void *memory, + cx_destructor_func destr +) { + struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( + pool, + sizeof(struct cx_mempool_foreign_mem_s) + ); + if (fm == NULL) return 1; + + fm->mem = memory; + fm->destr = destr; + *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; + + return 0; } -void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr) { - ucx_regdestr *rd = (ucx_regdestr*)ucx_mempool_malloc( - pool, - sizeof(ucx_regdestr)); - rd->destructor = destr; - rd->ptr = ptr; - ucx_mempool_set_destr(rd, ucx_mempool_shared_destr); +static cx_allocator_class cx_mempool_allocator_class = { + cx_mempool_malloc, + cx_mempool_realloc, + cx_mempool_calloc, + cx_mempool_free +}; + +CxMempool *cxMempoolCreate( + size_t capacity, + cx_destructor_func destr +) { + size_t poolsize; + if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { + return NULL; + } + + struct cx_mempool_s *pool = + malloc(sizeof(struct cx_mempool_s)); + if (pool == NULL) { + return NULL; + } + + CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); + if (provided_allocator == NULL) { + free(pool); + return NULL; + } + provided_allocator->cl = &cx_mempool_allocator_class; + provided_allocator->data = pool; + + pool->allocator = provided_allocator; + + pool->data = malloc(poolsize); + if (pool->data == NULL) { + free(provided_allocator); + free(pool); + return NULL; + } + + pool->size = 0; + pool->capacity = capacity; + pool->auto_destr = destr; + + return (CxMempool *) pool; } -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/printf.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,194 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/printf.h" + +#include <stdio.h> +#include <string.h> + +#ifndef CX_PRINTF_SBO_SIZE +#define CX_PRINTF_SBO_SIZE 512 +#endif +unsigned const cx_printf_sbo_size = CX_PRINTF_SBO_SIZE; + +int cx_fprintf( + void *stream, + cx_write_func wfc, + const char *fmt, + ... +) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = cx_vfprintf(stream, wfc, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vfprintf( + void *stream, + cx_write_func wfc, + const char *fmt, + va_list ap +) { + char buf[CX_PRINTF_SBO_SIZE]; + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); + if (ret < 0) { + va_end(ap2); + return ret; + } else if (ret < CX_PRINTF_SBO_SIZE) { + va_end(ap2); + return (int) wfc(buf, 1, ret, stream); + } else { + int len = ret + 1; + char *newbuf = malloc(len); + if (!newbuf) { + va_end(ap2); + return -1; + } + + ret = vsnprintf(newbuf, len, fmt, ap2); + va_end(ap2); + if (ret > 0) { + ret = (int) wfc(newbuf, 1, ret, stream); + } + free(newbuf); + } + return ret; +} + +cxmutstr cx_asprintf_a( + const CxAllocator *allocator, + const char *fmt, + ... +) { + va_list ap; + va_start(ap, fmt); + cxmutstr ret = cx_vasprintf_a(allocator, fmt, ap); + va_end(ap); + return ret; +} + +cxmutstr cx_vasprintf_a( + const CxAllocator *a, + const char *fmt, + va_list ap +) { + cxmutstr s; + s.ptr = NULL; + s.length = 0; + char buf[CX_PRINTF_SBO_SIZE]; + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); + if (ret >= 0 && ret < CX_PRINTF_SBO_SIZE) { + s.ptr = cxMalloc(a, ret + 1); + if (s.ptr) { + s.length = (size_t) ret; + memcpy(s.ptr, buf, ret); + s.ptr[s.length] = '\0'; + } + } else { + int len = ret + 1; + s.ptr = cxMalloc(a, len); + if (s.ptr) { + ret = vsnprintf(s.ptr, len, fmt, ap2); + if (ret < 0) { + free(s.ptr); + s.ptr = NULL; + } else { + s.length = (size_t) ret; + } + } + } + va_end(ap2); + return s; +} + +int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + int ret = cx_vsprintf_a(alloc, str, len, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(*str, *len, fmt, ap); + if ((unsigned) ret >= *len) { + unsigned newlen = ret + 1; + char *ptr = cxRealloc(alloc, *str, newlen); + if (ptr) { + int newret = vsnprintf(ptr, newlen, fmt, ap2); + if (newret < 0) { + cxFree(alloc, ptr); + } else { + *len = newlen; + *str = ptr; + ret = newret; + } + } + } + va_end(ap2); + return ret; +} + +int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, *len, fmt, ap); + *str = buf; + if ((unsigned) ret >= *len) { + unsigned newlen = ret + 1; + char *ptr = cxMalloc(alloc, newlen); + if (ptr) { + int newret = vsnprintf(ptr, newlen, fmt, ap2); + if (newret < 0) { + cxFree(alloc, ptr); + } else { + *len = newlen; + *str = ptr; + ret = newret; + } + } + } + va_end(ap2); + return ret; +}
--- a/ucx/properties.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/properties.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -UcxProperties *ucx_properties_new() { - UcxProperties *parser = (UcxProperties*)malloc( - sizeof(UcxProperties)); - if(!parser) { - return NULL; - } - - parser->buffer = NULL; - parser->buflen = 0; - parser->pos = 0; - parser->tmp = NULL; - parser->tmplen = 0; - parser->tmpcap = 0; - parser->error = 0; - parser->delimiter = '='; - parser->comment1 = '#'; - parser->comment2 = 0; - parser->comment3 = 0; - - return parser; -} - -void ucx_properties_free(UcxProperties *parser) { - if(parser->tmp) { - free(parser->tmp); - } - free(parser); -} - -void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) { - parser->buffer = buf; - parser->buflen = len; - parser->pos = 0; -} - -static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) { - if(parser->tmpcap - parser->tmplen < len) { - size_t newcap = parser->tmpcap + len + 64; - parser->tmp = (char*)realloc(parser->tmp, newcap); - parser->tmpcap = newcap; - } - memcpy(parser->tmp + parser->tmplen, buf, len); - parser->tmplen += len; -} - -int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value) { - if(parser->tmplen > 0) { - char *buf = parser->buffer + parser->pos; - size_t len = parser->buflen - parser->pos; - sstr_t str = sstrn(buf, len); - sstr_t nl = sstrchr(str, '\n'); - if(nl.ptr) { - size_t newlen = (size_t)(nl.ptr - buf) + 1; - parser_tmp_append(parser, buf, newlen); - // the tmp buffer contains exactly one line now - - char *orig_buf = parser->buffer; - size_t orig_len = parser->buflen; - - parser->buffer = parser->tmp; - parser->buflen = parser->tmplen; - parser->pos = 0; - parser->tmp = NULL; - parser->tmpcap = 0; - parser->tmplen = 0; - // run ucx_properties_next with the tmp buffer as main buffer - int ret = ucx_properties_next(parser, name, value); - - // restore original buffer - parser->tmp = parser->buffer; - parser->buffer = orig_buf; - parser->buflen = orig_len; - parser->pos = newlen; - - /* - * if ret == 0 the tmp buffer contained just space or a comment - * we parse again with the original buffer to get a name/value - * or a new tmp buffer - */ - return ret ? ret : ucx_properties_next(parser, name, value); - } else { - parser_tmp_append(parser, buf, len); - return 0; - } - } else if(parser->tmp) { - free(parser->tmp); - parser->tmp = NULL; - } - - char comment1 = parser->comment1; - char comment2 = parser->comment2; - char comment3 = parser->comment3; - char delimiter = parser->delimiter; - - // get one line and parse it - while(parser->pos < parser->buflen) { - char *buf = parser->buffer + parser->pos; - size_t len = parser->buflen - parser->pos; - - /* - * First we check if we have at least one line. We also get indices of - * delimiter and comment chars - */ - size_t delimiter_index = 0; - size_t comment_index = 0; - int has_comment = 0; - - size_t i = 0; - char c = 0; - for(;i<len;i++) { - c = buf[i]; - if(c == comment1 || c == comment2 || c == comment3) { - if(comment_index == 0) { - comment_index = i; - has_comment = 1; - } - } else if(c == delimiter) { - if(delimiter_index == 0 && !has_comment) { - delimiter_index = i; - } - } else if(c == '\n') { - break; - } - } - - if(c != '\n') { - // we don't have enough data for a line - // store remaining bytes in temporary buffer for next round - parser->tmpcap = len + 128; - parser->tmp = (char*)malloc(parser->tmpcap); - parser->tmplen = len; - memcpy(parser->tmp, buf, len); - return 0; - } - - sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i); - // check line - if(delimiter_index == 0) { - line = sstrtrim(line); - if(line.length != 0) { - parser->error = 1; - } - } else { - sstr_t n = sstrn(buf, delimiter_index); - sstr_t v = sstrn( - buf + delimiter_index + 1, - line.length - delimiter_index - 1); - n = sstrtrim(n); - v = sstrtrim(v); - if(n.length != 0 || v.length != 0) { - *name = n; - *value = v; - parser->pos += i + 1; - return 1; - } else { - parser->error = 1; - } - } - - parser->pos += i + 1; - } - - return 0; -} - -int ucx_properties2map(UcxProperties *parser, UcxMap *map) { - sstr_t name; - sstr_t value; - while(ucx_properties_next(parser, &name, &value)) { - value = sstrdup_a(map->allocator, value); - if(!value.ptr) { - return 1; - } - if(ucx_map_sstr_put(map, name, value.ptr)) { - alfree(map->allocator, value.ptr); - return 1; - } - } - if (parser->error) { - return parser->error; - } else { - return 0; - } -} - -// buffer size is documented - change doc, when you change bufsize! -#define UCX_PROPLOAD_BUFSIZE 1024 -int ucx_properties_load(UcxMap *map, FILE *file) { - UcxProperties *parser = ucx_properties_new(); - if(!(parser && map && file)) { - return 1; - } - - int error = 0; - size_t r; - char buf[UCX_PROPLOAD_BUFSIZE]; - while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) { - ucx_properties_fill(parser, buf, r); - error = ucx_properties2map(parser, map); - if (error) { - break; - } - } - ucx_properties_free(parser); - return error; -} - -int ucx_properties_store(UcxMap *map, FILE *file) { - UcxMapIterator iter = ucx_map_iterator(map); - void *v; - sstr_t value; - size_t written; - - UCX_MAP_FOREACH(k, v, iter) { - value = sstr((char*)v); - - written = 0; - written += fwrite(k.data, 1, k.len, file); - written += fwrite(" = ", 1, 3, file); - written += fwrite(value.ptr, 1, value.length, file); - written += fwrite("\n", 1, 1, file); - - if (written != k.len + value.length + 4) { - return 1; - } - } - - return 0; -} -
--- a/ucx/stack.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/stack.h" - -#include <string.h> - -static size_t ucx_stack_align(size_t n) { - int align = n % sizeof(void*); - if (align) { - n += sizeof(void*) - align; - } - return n; -} - -void ucx_stack_init(UcxStack *stack, char* space, size_t size) { - stack->size = size - size % sizeof(void*); - stack->space = space; - stack->top = NULL; - - stack->allocator.pool = stack; - stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc; - stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc; - stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc; - stack->allocator.free = (ucx_allocator_free) ucx_stack_free; -} - -void *ucx_stack_malloc(UcxStack *stack, size_t n) { - - if (ucx_stack_avail(stack) < ucx_stack_align(n)) { - return NULL; - } else { - char *prev = stack->top; - if (stack->top) { - stack->top += ucx_stack_align(ucx_stack_topsize(stack)); - } else { - stack->top = stack->space; - } - - ((struct ucx_stack_metadata*)stack->top)->prev = prev; - ((struct ucx_stack_metadata*)stack->top)->size = n; - stack->top += sizeof(struct ucx_stack_metadata); - - return stack->top; - } -} - -void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) { - void *mem = ucx_stack_malloc(stack, nelem*elsize); - memset(mem, 0, nelem*elsize); - return mem; -} - -void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) { - if (ptr == stack->top) { - if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) { - return NULL; - } else { - ((struct ucx_stack_metadata*)stack->top - 1)->size = n; - return ptr; - } - } else { - if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) < - ucx_stack_align(n)) { - void *nptr = ucx_stack_malloc(stack, n); - if (nptr) { - memcpy(nptr, ptr, n); - ucx_stack_free(stack, ptr); - - return nptr; - } else { - return NULL; - } - } else { - ((struct ucx_stack_metadata*)ptr - 1)->size = n; - return ptr; - } - } -} - -void ucx_stack_free(UcxStack *stack, void *ptr) { - if (ptr == stack->top) { - stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev; - } else { - struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)( - (char*)ptr + - ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size) - ); - next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev; - } -} - -void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) { - if (ucx_stack_empty(stack)) { - return; - } - - if (dest) { - size_t len = ucx_stack_topsize(stack); - if (len > n) { - len = n; - } - - memcpy(dest, stack->top, len); - } - - ucx_stack_free(stack, stack->top); -} - -size_t ucx_stack_avail(UcxStack *stack) { - size_t avail = ((stack->top ? (stack->size - - (stack->top - stack->space) - - ucx_stack_align(ucx_stack_topsize(stack))) - : stack->size)); - - if (avail > sizeof(struct ucx_stack_metadata)) { - return avail - sizeof(struct ucx_stack_metadata); - } else { - return 0; - } -} - -void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) { - void *space = ucx_stack_malloc(stack, n); - if (space) { - memcpy(space, data, n); - } - return space; -} - -void *ucx_stack_pusharr(UcxStack *stack, - size_t nelem, size_t elsize, const void *data) { - - // skip the memset by using malloc - void *space = ucx_stack_malloc(stack, nelem*elsize); - if (space) { - memcpy(space, data, nelem*elsize); - } - return space; -}
--- a/ucx/string.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/string.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,63 +26,71 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/string.h" +#include "cx/string.h" +#include "cx/utils.h" -#include "ucx/allocator.h" - -#include <stdlib.h> #include <string.h> #include <stdarg.h> -#include <stdint.h> #include <ctype.h> #ifndef _WIN32 -#include <strings.h> /* for strncasecmp() */ -#endif /* _WIN32 */ + +#include <strings.h> // for strncasecmp() -sstr_t sstr(char *cstring) { - sstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +#endif // _WIN32 + +cxmutstr cx_mutstr(char *cstring) { + return (cxmutstr) {cstring, strlen(cstring)}; } -sstr_t sstrn(char *cstring, size_t length) { - sstr_t string; - string.ptr = cstring; - string.length = length; - return string; +cxmutstr cx_mutstrn( + char *cstring, + size_t length +) { + return (cxmutstr) {cstring, length}; +} + +cxstring cx_str(const char *cstring) { + return (cxstring) {cstring, strlen(cstring)}; +} + +cxstring cx_strn( + const char *cstring, + size_t length +) { + return (cxstring) {cstring, length}; } -scstr_t scstr(const char *cstring) { - scstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +cxstring cx_strcast(cxmutstr str) { + return (cxstring) {str.ptr, str.length}; } -scstr_t scstrn(const char *cstring, size_t length) { - scstr_t string; - string.ptr = cstring; - string.length = length; - return string; +void cx_strfree(cxmutstr *str) { + free(str->ptr); + str->ptr = NULL; + str->length = 0; } +void cx_strfree_a( + const CxAllocator *alloc, + cxmutstr *str +) { + cxFree(alloc, str->ptr); + str->ptr = NULL; + str->length = 0; +} -size_t scstrnlen(size_t n, ...) { - if (n == 0) return 0; - +size_t cx_strlen( + size_t count, + ... +) { + if (count == 0) return 0; + va_list ap; - va_start(ap, n); - + va_start(ap, count); size_t size = 0; - - for (size_t i = 0 ; i < n ; i++) { - scstr_t str = va_arg(ap, scstr_t); - if(SIZE_MAX - str.length < size) { - size = SIZE_MAX; - break; - } + cx_for_n(i, count) { + cxstring str = va_arg(ap, cxstring); size += str.length; } va_end(ap); @@ -90,410 +98,341 @@ return size; } -static sstr_t sstrvcat_a( - UcxAllocator *a, +cxmutstr cx_strcat_ma( + const CxAllocator *alloc, + cxmutstr str, size_t count, - scstr_t s1, - va_list ap) { - sstr_t str; - str.ptr = NULL; - str.length = 0; - if(count < 2) { - return str; - } - - scstr_t s2 = va_arg (ap, scstr_t); - - if(((size_t)-1) - s1.length < s2.length) { - return str; - } - - scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t)); - if(!strings) { - return str; - } - + ... +) { + if (count == 0) return str; + + cxstring *strings = calloc(count, sizeof(cxstring)); + if (!strings) abort(); + + va_list ap; + va_start(ap, count); + // get all args and overall length - strings[0] = s1; - strings[1] = s2; - size_t slen = s1.length + s2.length; - int error = 0; - for (size_t i=2;i<count;i++) { - scstr_t s = va_arg (ap, scstr_t); + size_t slen = str.length; + cx_for_n(i, count) { + cxstring s = va_arg (ap, cxstring); strings[i] = s; - if(((size_t)-1) - s.length < slen) { - error = 1; - break; - } slen += s.length; } - if(error) { - free(strings); - return str; + va_end(ap); + + // reallocate or create new string + if (str.ptr == NULL) { + str.ptr = cxMalloc(alloc, slen + 1); + } else { + str.ptr = cxRealloc(alloc, str.ptr, slen + 1); } - - // create new string - str.ptr = (char*) almalloc(a, slen + 1); + if (str.ptr == NULL) abort(); + + // concatenate strings + size_t pos = str.length; str.length = slen; - if(!str.ptr) { - free(strings); - str.length = 0; - return str; - } - - // concatenate strings - size_t pos = 0; - for (size_t i=0;i<count;i++) { - scstr_t s = strings[i]; + cx_for_n(i, count) { + cxstring s = strings[i]; memcpy(str.ptr + pos, s.ptr, s.length); pos += s.length; } - + + // terminate string str.ptr[str.length] = '\0'; - + + // free temporary array free(strings); - + return str; } -sstr_t scstrcat(size_t count, scstr_t s1, ...) { - va_list ap; - va_start(ap, s1); - sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap); - va_end(ap); - return s; -} - -sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...) { - va_list ap; - va_start(ap, s1); - sstr_t s = sstrvcat_a(a, count, s1, ap); - va_end(ap); - return s; +cxstring cx_strsubs( + cxstring string, + size_t start +) { + return cx_strsubsl(string, start, string.length - start); } -static int ucx_substring( - size_t str_length, - size_t start, - size_t length, - size_t *newlen, - size_t *newpos) -{ - *newlen = 0; - *newpos = 0; - - if(start > str_length) { - return 0; - } - - if(length > str_length - start) { - length = str_length - start; - } - *newlen = length; - *newpos = start; - return 1; -} - -sstr_t sstrsubs(sstr_t s, size_t start) { - return sstrsubsl (s, start, s.length-start); +cxmutstr cx_strsubs_m( + cxmutstr string, + size_t start +) { + return cx_strsubsl_m(string, start, string.length - start); } -sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) { - size_t pos; - sstr_t ret = { NULL, 0 }; - if(ucx_substring(s.length, start, length, &ret.length, &pos)) { - ret.ptr = s.ptr + pos; +cxstring cx_strsubsl( + cxstring string, + size_t start, + size_t length +) { + if (start > string.length) { + return (cxstring) {NULL, 0}; } - return ret; -} -scstr_t scstrsubs(scstr_t string, size_t start) { - return scstrsubsl(string, start, string.length-start); -} + size_t rem_len = string.length - start; + if (length > rem_len) { + length = rem_len; + } -scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) { - size_t pos; - scstr_t ret = { NULL, 0 }; - if(ucx_substring(s.length, start, length, &ret.length, &pos)) { - ret.ptr = s.ptr + pos; - } - return ret; + return (cxstring) {string.ptr + start, length}; } - -static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) { - for(size_t i=0;i<length;i++) { - if(str[i] == chr) { - *pos = i; - return 1; - } - } - return 0; +cxmutstr cx_strsubsl_m( + cxmutstr string, + size_t start, + size_t length +) { + cxstring result = cx_strsubsl(cx_strcast(string), start, length); + return (cxmutstr) {(char *) result.ptr, result.length}; } -static int ucx_strrchr(const char *str, size_t length, int chr, size_t *pos) { - if(length > 0) { - for(size_t i=length ; i>0 ; i--) { - if(str[i-1] == chr) { - *pos = i-1; - return 1; - } +cxstring cx_strchr( + cxstring string, + int chr +) { + chr = 0xFF & chr; + // TODO: improve by comparing multiple bytes at once + cx_for_n(i, string.length) { + if (string.ptr[i] == chr) { + return cx_strsubs(string, i); } } - return 0; + return (cxstring) {NULL, 0}; +} + +cxmutstr cx_strchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; } -sstr_t sstrchr(sstr_t s, int c) { - size_t pos = 0; - if(ucx_strchr(s.ptr, s.length, c, &pos)) { - return sstrsubs(s, pos); +cxstring cx_strrchr( + cxstring string, + int chr +) { + chr = 0xFF & chr; + size_t i = string.length; + while (i > 0) { + i--; + // TODO: improve by comparing multiple bytes at once + if (string.ptr[i] == chr) { + return cx_strsubs(string, i); + } } - return sstrn(NULL, 0); + return (cxstring) {NULL, 0}; } -sstr_t sstrrchr(sstr_t s, int c) { - size_t pos = 0; - if(ucx_strrchr(s.ptr, s.length, c, &pos)) { - return sstrsubs(s, pos); - } - return sstrn(NULL, 0); -} - -scstr_t scstrchr(scstr_t s, int c) { - size_t pos = 0; - if(ucx_strchr(s.ptr, s.length, c, &pos)) { - return scstrsubs(s, pos); - } - return scstrn(NULL, 0); -} - -scstr_t scstrrchr(scstr_t s, int c) { - size_t pos = 0; - if(ucx_strrchr(s.ptr, s.length, c, &pos)) { - return scstrsubs(s, pos); - } - return scstrn(NULL, 0); +cxmutstr cx_strrchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strrchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; } -#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \ - ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index]) - -#define ptable_w(useheap, ptable, index, src) do {\ - if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\ - else ((size_t*)ptable)[index] = src;\ - } while (0); - +#ifndef CX_STRSTR_SBO_SIZE +#define CX_STRSTR_SBO_SIZE 512 +#endif +unsigned const cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; -static const char* ucx_strstr( - const char *str, - size_t length, - const char *match, - size_t matchlen, - size_t *newlen) -{ - *newlen = length; - if (matchlen == 0) { - return str; +cxstring cx_strstr( + cxstring haystack, + cxstring needle +) { + if (needle.length == 0) { + return haystack; } - - const char *result = NULL; - size_t resultlen = 0; - + + // optimize for single-char needles + if (needle.length == 1) { + return cx_strchr(haystack, *needle.ptr); + } + /* * IMPORTANT: - * our prefix table contains the prefix length PLUS ONE - * this is our decision, because we want to use the full range of size_t - * the original algorithm needs a (-1) at one single place - * and we want to avoid that + * Our prefix table contains the prefix length PLUS ONE + * this is our decision, because we want to use the full range of size_t. + * The original algorithm needs a (-1) at one single place, + * and we want to avoid that. */ - - /* static prefix table */ - static uint8_t s_prefix_table[256]; - - /* check pattern length and use appropriate prefix table */ - /* if the pattern exceeds static prefix table, allocate on the heap */ - register int useheap = matchlen > 255; - register void* ptable = useheap ? - calloc(matchlen+1, sizeof(size_t)): s_prefix_table; - - /* keep counter in registers */ + + // local prefix table + size_t s_prefix_table[CX_STRSTR_SBO_SIZE]; + + // check needle length and use appropriate prefix table + // if the pattern exceeds static prefix table, allocate on the heap + bool useheap = needle.length >= CX_STRSTR_SBO_SIZE; + register size_t *ptable = useheap ? calloc(needle.length + 1, + sizeof(size_t)) : s_prefix_table; + + // keep counter in registers register size_t i, j; - - /* fill prefix table */ - i = 0; j = 0; - ptable_w(useheap, ptable, i, j); - while (i < matchlen) { - while (j >= 1 && match[j-1] != match[i]) { - ptable_r(j, useheap, ptable, j-1); + + // fill prefix table + i = 0; + j = 0; + ptable[i] = j; + while (i < needle.length) { + while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) { + j = ptable[j - 1]; } - i++; j++; - ptable_w(useheap, ptable, i, j); + i++; + j++; + ptable[i] = j; } - /* search */ - i = 0; j = 1; - while (i < length) { - while (j >= 1 && str[i] != match[j-1]) { - ptable_r(j, useheap, ptable, j-1); + // search + cxstring result = {NULL, 0}; + i = 0; + j = 1; + while (i < haystack.length) { + while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) { + j = ptable[j - 1]; } - i++; j++; - if (j-1 == matchlen) { - size_t start = i - matchlen; - result = str + start; - resultlen = length - start; + i++; + j++; + if (j - 1 == needle.length) { + size_t start = i - needle.length; + result.ptr = haystack.ptr + start; + result.length = haystack.length - start; break; } } - /* if prefix table was allocated on the heap, free it */ + // if prefix table was allocated on the heap, free it if (ptable != s_prefix_table) { free(ptable); } - - *newlen = resultlen; - return result; -} - -sstr_t scstrsstr(sstr_t string, scstr_t match) { - sstr_t result; - - size_t reslen; - const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); - if(!resstr) { - result.ptr = NULL; - result.length = 0; - return result; - } - - size_t pos = resstr - string.ptr; - result.ptr = string.ptr + pos; - result.length = reslen; - - return result; -} - -scstr_t scstrscstr(scstr_t string, scstr_t match) { - scstr_t result; - - size_t reslen; - const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); - if(!resstr) { - result.ptr = NULL; - result.length = 0; - return result; - } - - size_t pos = resstr - string.ptr; - result.ptr = string.ptr + pos; - result.length = reslen; - - return result; -} - -#undef ptable_r -#undef ptable_w - -sstr_t* scstrsplit(scstr_t s, scstr_t d, ssize_t *n) { - return scstrsplit_a(ucx_default_allocator(), s, d, n); -} - -sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) { - if (s.length == 0 || d.length == 0) { - *n = -1; - return NULL; - } - - /* special cases: delimiter is at least as large as the string */ - if (d.length >= s.length) { - /* exact match */ - if (sstrcmp(s, d) == 0) { - *n = 0; - return NULL; - } else /* no match possible */ { - *n = 1; - sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t)); - if(result) { - *result = sstrdup_a(allocator, s); - } else { - *n = -2; - } - return result; - } - } - - ssize_t nmax = *n; - size_t arrlen = 16; - sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t)); - - if (result) { - scstr_t curpos = s; - ssize_t j = 1; - while (1) { - scstr_t match; - /* optimize for one byte delimiters */ - if (d.length == 1) { - match = curpos; - for (size_t i = 0 ; i < curpos.length ; i++) { - if (curpos.ptr[i] == *(d.ptr)) { - match.ptr = curpos.ptr + i; - break; - } - match.length--; - } - } else { - match = scstrscstr(curpos, d); - } - if (match.length > 0) { - /* is this our last try? */ - if (nmax == 0 || j < nmax) { - /* copy the current string to the array */ - scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr); - result[j-1] = sstrdup_a(allocator, item); - size_t processed = item.length + d.length; - curpos.ptr += processed; - curpos.length -= processed; - - /* allocate memory for the next string */ - j++; - if (j > arrlen) { - arrlen *= 2; - size_t reallocsz; - sstr_t* reallocated = NULL; - if(!ucx_szmul(arrlen, sizeof(sstr_t), &reallocsz)) { - reallocated = (sstr_t*) alrealloc( - allocator, result, reallocsz); - } - if (reallocated) { - result = reallocated; - } else { - for (ssize_t i = 0 ; i < j-1 ; i++) { - alfree(allocator, result[i].ptr); - } - alfree(allocator, result); - *n = -2; - return NULL; - } - } - } else { - /* nmax reached, copy the _full_ remaining string */ - result[j-1] = sstrdup_a(allocator, curpos); - break; - } - } else { - /* no more matches, copy last string */ - result[j-1] = sstrdup_a(allocator, curpos); - break; - } - } - *n = j; - } else { - *n = -2; - } return result; } -int scstrcmp(scstr_t s1, scstr_t s2) { +cxmutstr cx_strstr_m( + cxmutstr haystack, + cxstring needle +) { + cxstring result = cx_strstr(cx_strcast(haystack), needle); + return (cxmutstr) {(char *) result.ptr, result.length}; +} + +size_t cx_strsplit( + cxstring string, + cxstring delim, + size_t limit, + cxstring *output +) { + // special case: output limit is zero + if (limit == 0) return 0; + + // special case: delimiter is empty + if (delim.length == 0) { + output[0] = string; + return 1; + } + + // special cases: delimiter is at least as large as the string + if (delim.length >= string.length) { + // exact match + if (cx_strcmp(string, delim) == 0) { + output[0] = cx_strn(string.ptr, 0); + output[1] = cx_strn(string.ptr + string.length, 0); + return 2; + } else { + // no match possible + output[0] = string; + return 1; + } + } + + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + // copy the current string to the array + cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr); + output[n - 1] = item; + size_t processed = item.length + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached, copy the _full_ remaining string + output[n - 1] = curpos; + break; + } + } else { + // no more matches, copy last string + output[n - 1] = curpos; + break; + } + } + + return n; +} + +size_t cx_strsplit_a( + const CxAllocator *allocator, + cxstring string, + cxstring delim, + size_t limit, + cxstring **output +) { + // find out how many splits we're going to make and allocate memory + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + size_t processed = match.ptr - curpos.ptr + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached + break; + } + } else { + // no more matches + break; + } + } + *output = cxCalloc(allocator, n, sizeof(cxstring)); + return cx_strsplit(string, delim, n, *output); +} + +size_t cx_strsplit_m( + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr *output +) { + return cx_strsplit(cx_strcast(string), + delim, limit, (cxstring *) output); +} + +size_t cx_strsplit_ma( + const CxAllocator *allocator, + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr **output +) { + return cx_strsplit_a(allocator, cx_strcast(string), + delim, limit, (cxstring **) output); +} + +int cx_strcmp( + cxstring s1, + cxstring s2 +) { if (s1.length == s2.length) { return memcmp(s1.ptr, s2.ptr, s1.length); } else if (s1.length > s2.length) { @@ -503,7 +442,10 @@ } } -int scstrcasecmp(scstr_t s1, scstr_t s2) { +int cx_strcasecmp( + cxstring s1, + cxstring s2 +) { if (s1.length == s2.length) { #ifdef _WIN32 return _strnicmp(s1.ptr, s2.ptr, s1.length); @@ -517,216 +459,186 @@ } } -sstr_t scstrdup(scstr_t s) { - return sstrdup_a(ucx_default_allocator(), s); +int cx_strcmp_p( + const void *s1, + const void *s2 +) { + const cxstring *left = s1; + const cxstring *right = s2; + return cx_strcmp(*left, *right); +} + +int cx_strcasecmp_p( + const void *s1, + const void *s2 +) { + const cxstring *left = s1; + const cxstring *right = s2; + return cx_strcasecmp(*left, *right); } -sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) { - sstr_t newstring; - newstring.ptr = (char*)almalloc(allocator, s.length + 1); - if (newstring.ptr) { - newstring.length = s.length; - newstring.ptr[newstring.length] = 0; - - memcpy(newstring.ptr, s.ptr, s.length); - } else { - newstring.length = 0; +cxmutstr cx_strdup_a( + const CxAllocator *allocator, + cxstring string +) { + cxmutstr result = { + cxMalloc(allocator, string.length + 1), + string.length + }; + if (result.ptr == NULL) { + result.length = 0; + return result; } - - return newstring; + memcpy(result.ptr, string.ptr, string.length); + result.ptr[string.length] = '\0'; + return result; +} + +cxstring cx_strtrim(cxstring string) { + cxstring result = string; + // TODO: optimize by comparing multiple bytes at once + while (result.length > 0 && isspace(*result.ptr)) { + result.ptr++; + result.length--; + } + while (result.length > 0 && isspace(result.ptr[result.length - 1])) { + result.length--; + } + return result; } +cxmutstr cx_strtrim_m(cxmutstr string) { + cxstring result = cx_strtrim(cx_strcast(string)); + return (cxmutstr) {(char *) result.ptr, result.length}; +} -static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) { - const char *newptr = s; - size_t length = len; - - while(length > 0 && isspace(*newptr)) { - newptr++; - length--; - } - while(length > 0 && isspace(newptr[length-1])) { - length--; - } - - *newlen = length; - return newptr - s; +bool cx_strprefix( + cxstring string, + cxstring prefix +) { + if (string.length < prefix.length) return false; + return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; +} + +bool cx_strsuffix( + cxstring string, + cxstring suffix +) { + if (string.length < suffix.length) return false; + return memcmp(string.ptr + string.length - suffix.length, + suffix.ptr, suffix.length) == 0; } -sstr_t sstrtrim(sstr_t string) { - sstr_t newstr; - newstr.ptr = string.ptr - + ucx_strtrim(string.ptr, string.length, &newstr.length); - return newstr; +bool cx_strcaseprefix( + cxstring string, + cxstring prefix +) { + if (string.length < prefix.length) return false; +#ifdef _WIN32 + return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0; +#else + return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0; +#endif } -scstr_t scstrtrim(scstr_t string) { - scstr_t newstr; - newstr.ptr = string.ptr - + ucx_strtrim(string.ptr, string.length, &newstr.length); - return newstr; +bool cx_strcasesuffix( + cxstring string, + cxstring suffix +) { + if (string.length < suffix.length) return false; +#ifdef _WIN32 + return _strnicmp(string.ptr+string.length-suffix.length, + suffix.ptr, suffix.length) == 0; +#else + return strncasecmp(string.ptr + string.length - suffix.length, + suffix.ptr, suffix.length) == 0; +#endif } -int scstrprefix(scstr_t string, scstr_t prefix) { - if (string.length == 0) { - return prefix.length == 0; - } - if (prefix.length == 0) { - return 1; - } - - if (prefix.length > string.length) { - return 0; - } else { - return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; +void cx_strlower(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) tolower(string.ptr[i]); } } -int scstrsuffix(scstr_t string, scstr_t suffix) { - if (string.length == 0) { - return suffix.length == 0; - } - if (suffix.length == 0) { - return 1; - } - - if (suffix.length > string.length) { - return 0; - } else { - return memcmp(string.ptr+string.length-suffix.length, - suffix.ptr, suffix.length) == 0; - } -} - -int scstrcaseprefix(scstr_t string, scstr_t prefix) { - if (string.length == 0) { - return prefix.length == 0; - } - if (prefix.length == 0) { - return 1; - } - - if (prefix.length > string.length) { - return 0; - } else { - scstr_t subs = scstrsubsl(string, 0, prefix.length); - return scstrcasecmp(subs, prefix) == 0; +void cx_strupper(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) toupper(string.ptr[i]); } } -int scstrcasesuffix(scstr_t string, scstr_t suffix) { - if (string.length == 0) { - return suffix.length == 0; - } - if (suffix.length == 0) { - return 1; - } - - if (suffix.length > string.length) { - return 0; - } else { - scstr_t subs = scstrsubs(string, string.length-suffix.length); - return scstrcasecmp(subs, suffix) == 0; - } -} - -sstr_t scstrlower(scstr_t string) { - sstr_t ret = sstrdup(string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = tolower(ret.ptr[i]); - } - return ret; -} +#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE +#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64 +#endif -sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string) { - sstr_t ret = sstrdup_a(allocator, string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = tolower(ret.ptr[i]); - } - return ret; -} - -sstr_t scstrupper(scstr_t string) { - sstr_t ret = sstrdup(string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = toupper(ret.ptr[i]); - } - return ret; -} - -sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string) { - sstr_t ret = sstrdup_a(allocator, string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = toupper(ret.ptr[i]); - } - return ret; -} - -#define REPLACE_INDEX_BUFFER_MAX 100 - -struct scstrreplace_ibuf { - size_t* buf; - unsigned int len; /* small indices */ - struct scstrreplace_ibuf* next; +struct cx_strreplace_ibuf { + size_t *buf; + struct cx_strreplace_ibuf *next; + unsigned int len; }; -static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) { +static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { while (buf) { - struct scstrreplace_ibuf *next = buf->next; + struct cx_strreplace_ibuf *next = buf->next; free(buf->buf); free(buf); buf = next; } } -sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str, - scstr_t pattern, scstr_t replacement, size_t replmax) { +cxmutstr cx_strreplacen_a( + const CxAllocator *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +) { if (pattern.length == 0 || pattern.length > str.length || replmax == 0) - return sstrdup(str); + return cx_strdup_a(allocator, str); - /* Compute expected buffer length */ + // Compute expected buffer length size_t ibufmax = str.length / pattern.length; size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; - if (ibuflen > REPLACE_INDEX_BUFFER_MAX) { - ibuflen = REPLACE_INDEX_BUFFER_MAX; + if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { + ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; } - /* Allocate first index buffer */ - struct scstrreplace_ibuf *firstbuf, *curbuf; - firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf)); - if (!firstbuf) return sstrn(NULL, 0); + // Allocate first index buffer + struct cx_strreplace_ibuf *firstbuf, *curbuf; + firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf)); + if (!firstbuf) return cx_mutstrn(NULL, 0); firstbuf->buf = calloc(ibuflen, sizeof(size_t)); if (!firstbuf->buf) { free(firstbuf); - return sstrn(NULL, 0); + return cx_mutstrn(NULL, 0); } - /* Search occurrences */ - scstr_t searchstr = str; + // Search occurrences + cxstring searchstr = str; size_t found = 0; do { - scstr_t match = scstrscstr(searchstr, pattern); + cxstring match = cx_strstr(searchstr, pattern); if (match.length > 0) { - /* Allocate next buffer in chain, if required */ + // Allocate next buffer in chain, if required if (curbuf->len == ibuflen) { - struct scstrreplace_ibuf *nextbuf = - calloc(1, sizeof(struct scstrreplace_ibuf)); + struct cx_strreplace_ibuf *nextbuf = + calloc(1, sizeof(struct cx_strreplace_ibuf)); if (!nextbuf) { - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } nextbuf->buf = calloc(ibuflen, sizeof(size_t)); if (!nextbuf->buf) { free(nextbuf); - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } curbuf->next = nextbuf; curbuf = nextbuf; } - /* Record match index */ + // Record match index found++; size_t idx = match.ptr - str.ptr; curbuf->buf[curbuf->len++] = idx; @@ -737,8 +649,8 @@ } } while (searchstr.length > 0 && found < replmax); - /* Allocate result string */ - sstr_t result; + // Allocate result string + cxmutstr result; { ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; size_t rcount = 0; @@ -748,60 +660,127 @@ curbuf = curbuf->next; } while (curbuf); result.length = str.length + rcount * adjlen; - result.ptr = almalloc(allocator, result.length); + result.ptr = cxMalloc(allocator, result.length + 1); if (!result.ptr) { - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } } - /* Build result string */ + // Build result string curbuf = firstbuf; size_t srcidx = 0; - char* destptr = result.ptr; + char *destptr = result.ptr; do { for (size_t i = 0; i < curbuf->len; i++) { - /* Copy source part up to next match*/ + // Copy source part up to next match size_t idx = curbuf->buf[i]; size_t srclen = idx - srcidx; if (srclen > 0) { - memcpy(destptr, str.ptr+srcidx, srclen); + memcpy(destptr, str.ptr + srcidx, srclen); destptr += srclen; srcidx += srclen; } - /* Copy the replacement and skip the source pattern */ + // Copy the replacement and skip the source pattern srcidx += pattern.length; memcpy(destptr, replacement.ptr, replacement.length); destptr += replacement.length; } curbuf = curbuf->next; } while (curbuf); - memcpy(destptr, str.ptr+srcidx, str.length-srcidx); + memcpy(destptr, str.ptr + srcidx, str.length - srcidx); - /* Free index buffer */ - scstrrepl_free_ibuf(firstbuf); + // Result is guaranteed to be zero-terminated + result.ptr[result.length] = '\0'; + + // Free index buffer + cx_strrepl_free_ibuf(firstbuf); return result; } -sstr_t scstrreplacen(scstr_t str, scstr_t pattern, - scstr_t replacement, size_t replmax) { - return scstrreplacen_a(ucx_default_allocator(), - str, pattern, replacement, replmax); +CxStrtokCtx cx_strtok( + cxstring str, + cxstring delim, + size_t limit +) { + CxStrtokCtx ctx; + ctx.str = str; + ctx.delim = delim; + ctx.limit = limit; + ctx.pos = 0; + ctx.next_pos = 0; + ctx.delim_pos = 0; + ctx.found = 0; + ctx.delim_more = NULL; + ctx.delim_more_count = 0; + return ctx; +} + +CxStrtokCtx cx_strtok_m( + cxmutstr str, + cxstring delim, + size_t limit +) { + return cx_strtok(cx_strcast(str), delim, limit); } +bool cx_strtok_next( + CxStrtokCtx *ctx, + cxstring *token +) { + // abortion criteria + if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) { + return false; + } -// type adjustment functions -scstr_t ucx_sc2sc(scstr_t str) { - return str; + // determine the search start + cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos); + + // search the next delimiter + cxstring delim = cx_strstr(haystack, ctx->delim); + + // if found, make delim capture exactly the delimiter + if (delim.length > 0) { + delim.length = ctx->delim.length; + } + + // if more delimiters are specified, check them now + if (ctx->delim_more_count > 0) { + cx_for_n(i, ctx->delim_more_count) { + cxstring d = cx_strstr(haystack, ctx->delim_more[i]); + if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) { + delim.ptr = d.ptr; + delim.length = ctx->delim_more[i].length; + } + } + } + + // store the token information and adjust the context + ctx->found++; + ctx->pos = ctx->next_pos; + token->ptr = &ctx->str.ptr[ctx->pos]; + ctx->delim_pos = delim.length == 0 ? + ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr); + token->length = ctx->delim_pos - ctx->pos; + ctx->next_pos = ctx->delim_pos + delim.length; + + return true; } -scstr_t ucx_ss2sc(sstr_t str) { - scstr_t cs; - cs.ptr = str.ptr; - cs.length = str.length; - return cs; + +bool cx_strtok_next_m( + CxStrtokCtx *ctx, + cxmutstr *token +) { + return cx_strtok_next(ctx, (cxstring *) token); } -scstr_t ucx_ss2c_s(scstr_t c) { - return c; + +void cx_strtok_delim( + CxStrtokCtx *ctx, + const cxstring *delim, + size_t count +) { + ctx->delim_more = delim; + ctx->delim_more_count = count; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/szmul.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +int cx_szmul_impl( + size_t a, + size_t b, + size_t *result +) { + if (a == 0 || b == 0) { + *result = 0; + return 0; + } + size_t r = a * b; + if (r / b == a) { + *result = r; + return 0; + } else { + *result = 0; + return 1; + } +}
--- a/ucx/test.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/test.h" - -UcxTestSuite* ucx_test_suite_new() { - UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); - if (suite != NULL) { - suite->success = 0; - suite->failure = 0; - suite->tests = NULL; - } - - return suite; -} - -void ucx_test_suite_free(UcxTestSuite* suite) { - UcxTestList *l = suite->tests; - while (l != NULL) { - UcxTestList *e = l; - l = l->next; - free(e); - } - free(suite); -} - -int ucx_test_register(UcxTestSuite* suite, UcxTest test) { - if (suite->tests) { - UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (newelem) { - newelem->test = test; - newelem->next = NULL; - - UcxTestList *last = suite->tests; - while (last->next) { - last = last->next; - } - last->next = newelem; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } else { - suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (suite->tests) { - suite->tests->test = test; - suite->tests->next = NULL; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } -} - -void ucx_test_run(UcxTestSuite* suite, FILE* output) { - suite->success = 0; - suite->failure = 0; - for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) { - elem->test(suite, output); - } - fwrite("\nAll test completed.\n", 1, 21, output); - fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n", - suite->success+suite->failure, suite->success, suite->failure); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/tree.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,958 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/tree.h" + +#include "cx/array_list.h" + +#include <assert.h> + +#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) +#define tree_parent(node) CX_TREE_PTR(node, loc_parent) +#define tree_children(node) CX_TREE_PTR(node, loc_children) +#define tree_last_child(node) CX_TREE_PTR(node, loc_last_child) +#define tree_prev(node) CX_TREE_PTR(node, loc_prev) +#define tree_next(node) CX_TREE_PTR(node, loc_next) + +#define cx_tree_ptr_locations \ + loc_parent, loc_children, loc_last_child, loc_prev, loc_next + +static void cx_tree_zero_pointers( + void *node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + tree_parent(node) = NULL; + tree_prev(node) = NULL; + tree_next(node) = NULL; + tree_children(node) = NULL; + if (loc_last_child >= 0) { + tree_last_child(node) = NULL; + } +} + +void cx_tree_link( + void *restrict parent, + void *restrict node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + void *current_parent = tree_parent(node); + if (current_parent == parent) return; + if (current_parent != NULL) { + cx_tree_unlink(node, cx_tree_ptr_locations); + } + + if (tree_children(parent) == NULL) { + tree_children(parent) = node; + if (loc_last_child >= 0) { + tree_last_child(parent) = node; + } + } else { + if (loc_last_child >= 0) { + void *child = tree_last_child(parent); + tree_prev(node) = child; + tree_next(child) = node; + tree_last_child(parent) = node; + } else { + void *child = tree_children(parent); + void *next; + while ((next = tree_next(child)) != NULL) { + child = next; + } + tree_prev(node) = child; + tree_next(child) = node; + } + } + tree_parent(node) = parent; +} + +void cx_tree_unlink( + void *node, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + if (tree_parent(node) == NULL) return; + + void *left = tree_prev(node); + void *right = tree_next(node); + void *parent = tree_parent(node); + assert(left == NULL || tree_children(parent) != node); + assert(right == NULL || loc_last_child < 0 || + tree_last_child(parent) != node); + + if (left == NULL) { + tree_children(parent) = right; + } else { + tree_next(left) = right; + } + if (right == NULL) { + if (loc_last_child >= 0) { + tree_last_child(parent) = left; + } + } else { + tree_prev(right) = left; + } + + tree_parent(node) = NULL; + tree_prev(node) = NULL; + tree_next(node) = NULL; +} + +int cx_tree_search( + const void *root, + const void *node, + cx_tree_search_func sfunc, + void **result, + ptrdiff_t loc_children, + ptrdiff_t loc_next +) { + int ret; + *result = NULL; + + // shortcut: compare root before doing anything else + ret = sfunc(root, node); + if (ret < 0) { + return ret; + } else if (ret == 0 || tree_children(root) == NULL) { + *result = (void*)root; + return ret; + } + + // create a working stack + CX_ARRAY_DECLARE(const void *, work); + cx_array_initialize(work, 32); + + // add the children of root to the working stack + { + void *c = tree_children(root); + while (c != NULL) { + cx_array_simple_add(work, c); + c = tree_next(c); + } + } + + // remember a candidate for adding the data + // also remember the exact return code from sfunc + void *candidate = (void *) root; + int ret_candidate = ret; + + // process the working stack + while (work_size > 0) { + // pop element + const void *elem = work[--work_size]; + + // apply the search function + ret = sfunc(elem, node); + + if (ret == 0) { + // if found, exit the search + *result = (void *) elem; + work_size = 0; + break; + } else if (ret > 0) { + // if children might contain the data, add them to the stack + void *c = tree_children(elem); + while (c != NULL) { + cx_array_simple_add(work, c); + c = tree_next(c); + } + + // remember this node in case no child is suitable + if (ret < ret_candidate) { + candidate = (void *) elem; + ret_candidate = ret; + } + } + } + + // not found, but was there a candidate? + if (ret != 0 && candidate != NULL) { + ret = ret_candidate; + *result = candidate; + } + + // free the working queue and return + free(work); + return ret; +} + +int cx_tree_search_data( + const void *root, + const void *data, + cx_tree_search_data_func sfunc, + void **result, + ptrdiff_t loc_children, + ptrdiff_t loc_next +) { + // it is basically the same implementation + return cx_tree_search( + root, data, + (cx_tree_search_func) sfunc, + result, + loc_children, loc_next); +} + +static bool cx_tree_iter_valid(const void *it) { + const struct cx_tree_iterator_s *iter = it; + return iter->node != NULL; +} + +static void *cx_tree_iter_current(const void *it) { + const struct cx_tree_iterator_s *iter = it; + return iter->node; +} + +static void cx_tree_iter_next(void *it) { + struct cx_tree_iterator_s *iter = it; + ptrdiff_t const loc_next = iter->loc_next; + ptrdiff_t const loc_children = iter->loc_children; + // protect us from misuse + if (!iter->base.valid(iter)) return; + + void *children; + + // check if we are currently exiting or entering nodes + if (iter->exiting) { + children = NULL; + // skipping on exit is pointless, just clear the flag + iter->skip = false; + } else { + if (iter->skip) { + // skip flag is set, pretend that there are no children + iter->skip = false; + children = NULL; + } else { + // try to enter the children (if any) + children = tree_children(iter->node); + } + } + + if (children == NULL) { + // search for the next node + void *next; + cx_tree_iter_search_next: + // check if there is a sibling + if (iter->exiting) { + next = iter->node_next; + } else { + next = tree_next(iter->node); + iter->node_next = next; + } + if (next == NULL) { + // no sibling, we are done with this node and exit + if (iter->visit_on_exit && !iter->exiting) { + // iter is supposed to visit the node again + iter->exiting = true; + } else { + iter->exiting = false; + if (iter->depth == 1) { + // there is no parent - we have iterated the entire tree + // invalidate the iterator and free the node stack + iter->node = iter->node_next = NULL; + iter->stack_capacity = iter->depth = 0; + free(iter->stack); + iter->stack = NULL; + } else { + // the parent node can be obtained from the top of stack + // this way we can avoid the loc_parent in the iterator + iter->depth--; + iter->node = iter->stack[iter->depth - 1]; + // retry with the parent node to find a sibling + goto cx_tree_iter_search_next; + } + } + } else { + if (iter->visit_on_exit && !iter->exiting) { + // iter is supposed to visit the node again + iter->exiting = true; + } else { + iter->exiting = false; + // move to the sibling + iter->counter++; + iter->node = next; + // new top of stack is the sibling + iter->stack[iter->depth - 1] = next; + } + } + } else { + // node has children, push the first child onto the stack and enter it + cx_array_simple_add(iter->stack, children); + iter->node = children; + iter->counter++; + } +} + +CxTreeIterator cx_tree_iterator( + void *root, + bool visit_on_exit, + ptrdiff_t loc_children, + ptrdiff_t loc_next +) { + CxTreeIterator iter; + iter.loc_children = loc_children; + iter.loc_next = loc_next; + iter.visit_on_exit = visit_on_exit; + + // initialize members + iter.node_next = NULL; + iter.exiting = false; + iter.skip = false; + + // assign base iterator functions + iter.base.mutating = false; + iter.base.remove = false; + iter.base.current_impl = NULL; + iter.base.valid = cx_tree_iter_valid; + iter.base.next = cx_tree_iter_next; + iter.base.current = cx_tree_iter_current; + + // visit the root node + iter.node = root; + if (root != NULL) { + iter.stack_capacity = 16; + iter.stack = malloc(sizeof(void *) * 16); + iter.stack[0] = root; + iter.counter = 1; + iter.depth = 1; + } else { + iter.stack_capacity = 0; + iter.stack = NULL; + iter.counter = 0; + iter.depth = 0; + } + + return iter; +} + +static bool cx_tree_visitor_valid(const void *it) { + const struct cx_tree_visitor_s *iter = it; + return iter->node != NULL; +} + +static void *cx_tree_visitor_current(const void *it) { + const struct cx_tree_visitor_s *iter = it; + return iter->node; +} + +__attribute__((__nonnull__)) +static void cx_tree_visitor_enqueue_siblings( + struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) { + node = tree_next(node); + while (node != NULL) { + struct cx_tree_visitor_queue_s *q; + q = malloc(sizeof(struct cx_tree_visitor_queue_s)); + q->depth = iter->queue_last->depth; + q->node = node; + iter->queue_last->next = q; + iter->queue_last = q; + node = tree_next(node); + } + iter->queue_last->next = NULL; +} + +static void cx_tree_visitor_next(void *it) { + struct cx_tree_visitor_s *iter = it; + // protect us from misuse + if (!iter->base.valid(iter)) return; + + ptrdiff_t const loc_next = iter->loc_next; + ptrdiff_t const loc_children = iter->loc_children; + + // add the children of the current node to the queue + // unless the skip flag is set + void *children; + if (iter->skip) { + iter->skip = false; + children = NULL; + } else { + children = tree_children(iter->node); + } + if (children != NULL) { + struct cx_tree_visitor_queue_s *q; + q = malloc(sizeof(struct cx_tree_visitor_queue_s)); + q->depth = iter->depth + 1; + q->node = children; + if (iter->queue_last == NULL) { + assert(iter->queue_next == NULL); + iter->queue_next = q; + } else { + iter->queue_last->next = q; + } + iter->queue_last = q; + cx_tree_visitor_enqueue_siblings(iter, children, loc_next); + } + + // check if there is a next node + if (iter->queue_next == NULL) { + iter->node = NULL; + return; + } + + // dequeue the next node + iter->node = iter->queue_next->node; + iter->depth = iter->queue_next->depth; + { + struct cx_tree_visitor_queue_s *q = iter->queue_next; + iter->queue_next = q->next; + if (iter->queue_next == NULL) { + assert(iter->queue_last == q); + iter->queue_last = NULL; + } + free(q); + } + + // increment the node counter + iter->counter++; +} + +CxTreeVisitor cx_tree_visitor( + void *root, + ptrdiff_t loc_children, + ptrdiff_t loc_next +) { + CxTreeVisitor iter; + iter.loc_children = loc_children; + iter.loc_next = loc_next; + + // initialize members + iter.skip = false; + iter.queue_next = NULL; + iter.queue_last = NULL; + + // assign base iterator functions + iter.base.mutating = false; + iter.base.remove = false; + iter.base.current_impl = NULL; + iter.base.valid = cx_tree_visitor_valid; + iter.base.next = cx_tree_visitor_next; + iter.base.current = cx_tree_visitor_current; + + // visit the root node + iter.node = root; + if (root != NULL) { + iter.counter = 1; + iter.depth = 1; + } else { + iter.counter = 0; + iter.depth = 0; + } + + return iter; +} + +static void cx_tree_add_link_duplicate( + void *original, void *duplicate, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next +) { + void *shared_parent = tree_parent(original); + if (shared_parent == NULL) { + cx_tree_link(original, duplicate, cx_tree_ptr_locations); + } else { + cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations); + } +} + +static void cx_tree_add_link_new( + void *parent, void *node, cx_tree_search_func sfunc, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next +) { + // check the current children one by one, + // if they could be children of the new node + void *child = tree_children(parent); + while (child != NULL) { + void *next = tree_next(child); + + if (sfunc(node, child) > 0) { + // the sibling could be a child -> re-link + cx_tree_link(node, child, cx_tree_ptr_locations); + } + + child = next; + } + + // add new node as new child + cx_tree_link(parent, node, cx_tree_ptr_locations); +} + +int cx_tree_add( + const void *src, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **cnode, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + *cnode = cfunc(src, cdata); + if (*cnode == NULL) return 1; + cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations); + + void *match = NULL; + int result = cx_tree_search( + root, + *cnode, + sfunc, + &match, + loc_children, + loc_next + ); + + if (result < 0) { + // node does not fit into the tree - return non-zero value + return 1; + } else if (result == 0) { + // data already found in the tree, link duplicate + cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations); + } else { + // closest match found, add new node + cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations); + } + + return 0; +} + +unsigned int cx_tree_add_look_around_depth = 3; + +size_t cx_tree_add_iter( + struct cx_iterator_base_s *iter, + size_t num, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **failed, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + // erase the failed pointer + *failed = NULL; + + // iter not valid? cancel... + if (!iter->valid(iter)) return 0; + + size_t processed = 0; + void *current_node = root; + const void *elem; + + for (void **eptr; processed < num && + iter->valid(iter) && (eptr = iter->current(iter)) != NULL; + iter->next(iter)) { + elem = *eptr; + + // create the new node + void *new_node = cfunc(elem, cdata); + if (new_node == NULL) return processed; + cx_tree_zero_pointers(new_node, cx_tree_ptr_locations); + + // start searching from current node + void *match; + int result; + unsigned int look_around_retries = cx_tree_add_look_around_depth; + cx_tree_add_look_around_retry: + result = cx_tree_search( + current_node, + new_node, + sfunc, + &match, + loc_children, + loc_next + ); + + if (result < 0) { + // traverse upwards and try to find better parents + void *parent = tree_parent(current_node); + if (parent != NULL) { + if (look_around_retries > 0) { + look_around_retries--; + current_node = parent; + } else { + // look around retries exhausted, start from the root + current_node = root; + } + goto cx_tree_add_look_around_retry; + } else { + // no parents. so we failed + *failed = new_node; + return processed; + } + } else if (result == 0) { + // data already found in the tree, link duplicate + cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations); + // but stick with the original match, in case we needed a new root + current_node = match; + } else { + // closest match found, add new node as child + cx_tree_add_link_new(match, new_node, sfunc, + cx_tree_ptr_locations); + current_node = match; + } + + processed++; + } + return processed; +} + +size_t cx_tree_add_array( + const void *src, + size_t num, + size_t elem_size, + cx_tree_search_func sfunc, + cx_tree_node_create_func cfunc, + void *cdata, + void **failed, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + // erase failed pointer + *failed = NULL; + + // super special case: zero elements + if (num == 0) { + return 0; + } + + // special case: one element does not need an iterator + if (num == 1) { + void *node; + if (0 == cx_tree_add( + src, sfunc, cfunc, cdata, &node, root, + loc_parent, loc_children, loc_last_child, + loc_prev, loc_next)) { + return 1; + } else { + *failed = node; + return 0; + } + } + + // otherwise, create iterator and hand over to other function + CxIterator iter = cxIterator(src, elem_size, num); + return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc, + cfunc, cdata, failed, root, + loc_parent, loc_children, loc_last_child, + loc_prev, loc_next); +} + +static void cx_tree_default_destructor(CxTree *tree) { + if (tree->simple_destructor != NULL || tree->advanced_destructor != NULL) { + CxTreeIterator iter = tree->cl->iterator(tree, true); + cx_foreach(void *, node, iter) { + if (iter.exiting) { + if (tree->simple_destructor) { + tree->simple_destructor(node); + } + if (tree->advanced_destructor) { + tree->advanced_destructor(tree->destructor_data, node); + } + } + } + } + cxFree(tree->allocator, tree); +} + +static CxTreeIterator cx_tree_default_iterator( + CxTree *tree, + bool visit_on_exit +) { + return cx_tree_iterator( + tree->root, visit_on_exit, + tree->loc_children, tree->loc_next + ); +} + +static CxTreeVisitor cx_tree_default_visitor(CxTree *tree) { + return cx_tree_visitor(tree->root, tree->loc_children, tree->loc_next); +} + +static int cx_tree_default_insert_element( + CxTree *tree, + const void *data +) { + void *node; + if (tree->root == NULL) { + node = tree->node_create(data, tree); + if (node == NULL) return 1; + cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); + tree->root = node; + tree->size = 1; + return 0; + } + int result = cx_tree_add(data, tree->search, tree->node_create, + tree, &node, tree->root, cx_tree_node_layout(tree)); + if (0 == result) { + tree->size++; + } else { + cxFree(tree->allocator, node); + } + return result; +} + +static size_t cx_tree_default_insert_many( + CxTree *tree, + struct cx_iterator_base_s *iter, + size_t n +) { + size_t ins = 0; + if (!iter->valid(iter)) return 0; + if (tree->root == NULL) { + // use the first element from the iter to create the root node + void **eptr = iter->current(iter); + void *node = tree->node_create(*eptr, tree); + if (node == NULL) return 0; + cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); + tree->root = node; + ins = 1; + iter->next(iter); + } + void *failed; + ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create, + tree, &failed, tree->root, cx_tree_node_layout(tree)); + tree->size += ins; + if (ins < n) { + cxFree(tree->allocator, failed); + } + return ins; +} + +static void *cx_tree_default_find( + CxTree *tree, + const void *subtree, + const void *data +) { + if (tree->root == NULL) return NULL; + + void *found; + if (0 == cx_tree_search_data( + subtree, + data, + tree->search_data, + &found, + tree->loc_children, + tree->loc_next + )) { + return found; + } else { + return NULL; + } +} + +static cx_tree_class cx_tree_default_class = { + cx_tree_default_destructor, + cx_tree_default_insert_element, + cx_tree_default_insert_many, + cx_tree_default_find, + cx_tree_default_iterator, + cx_tree_default_visitor +}; + +CxTree *cxTreeCreate( + const CxAllocator *allocator, + cx_tree_node_create_func create_func, + cx_tree_search_func search_func, + cx_tree_search_data_func search_data_func, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + CxTree *tree = cxMalloc(allocator, sizeof(CxTree)); + if (tree == NULL) return NULL; + + tree->cl = &cx_tree_default_class; + tree->allocator = allocator; + tree->node_create = create_func; + tree->search = search_func; + tree->search_data = search_data_func; + tree->advanced_destructor = (cx_destructor_func2) cxFree; + tree->destructor_data = (void *) allocator; + tree->loc_parent = loc_parent; + tree->loc_children = loc_children; + tree->loc_last_child = loc_last_child; + tree->loc_prev = loc_prev; + tree->loc_next = loc_next; + tree->root = NULL; + tree->size = 0; + + return tree; +} + +CxTree *cxTreeCreateWrapped( + const CxAllocator *allocator, + void *root, + ptrdiff_t loc_parent, + ptrdiff_t loc_children, + ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + CxTree *tree = cxMalloc(allocator, sizeof(CxTree)); + if (tree == NULL) return NULL; + + tree->cl = &cx_tree_default_class; + // set the allocator anyway, just in case... + tree->allocator = allocator; + tree->node_create = NULL; + tree->search = NULL; + tree->search_data = NULL; + tree->simple_destructor = NULL; + tree->advanced_destructor = NULL; + tree->destructor_data = NULL; + tree->loc_parent = loc_parent; + tree->loc_children = loc_children; + tree->loc_last_child = loc_last_child; + tree->loc_prev = loc_prev; + tree->loc_next = loc_next; + tree->root = root; + tree->size = cxTreeSubtreeSize(tree, root); + return tree; +} + +int cxTreeAddChild( + CxTree *tree, + void *parent, + const void *data) { + void *node = tree->node_create(data, tree); + if (node == NULL) return 1; + cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); + cx_tree_link(parent, node, cx_tree_node_layout(tree)); + tree->size++; + return 0; +} + +size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) { + CxTreeVisitor visitor = cx_tree_visitor( + subtree_root, + tree->loc_children, + tree->loc_next + ); + while (cxIteratorValid(visitor)) { + cxIteratorNext(visitor); + } + return visitor.counter; +} + +size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) { + CxTreeVisitor visitor = cx_tree_visitor( + subtree_root, + tree->loc_children, + tree->loc_next + ); + while (cxIteratorValid(visitor)) { + cxIteratorNext(visitor); + } + return visitor.depth; +} + +size_t cxTreeDepth(CxTree *tree) { + CxTreeVisitor visitor = tree->cl->visitor(tree); + while (cxIteratorValid(visitor)) { + cxIteratorNext(visitor); + } + return visitor.depth; +} + +int cxTreeRemove( + CxTree *tree, + void *node, + cx_tree_relink_func relink_func +) { + if (node == tree->root) return 1; + + // determine the new parent + ptrdiff_t loc_parent = tree->loc_parent; + void *new_parent = tree_parent(node); + + // first, unlink from the parent + cx_tree_unlink(node, cx_tree_node_layout(tree)); + + // then relink each child + ptrdiff_t loc_children = tree->loc_children; + ptrdiff_t loc_next = tree->loc_next; + void *child = tree_children(node); + while (child != NULL) { + // forcibly set the parent to NULL - we do not use the unlink function + // because that would unnecessarily modify the children linked list + tree_parent(child) = NULL; + + // update contents, if required + if (relink_func != NULL) { + relink_func(child, node, new_parent); + } + + // link to new parent + cx_tree_link(new_parent, child, cx_tree_node_layout(tree)); + + // proceed to next child + child = tree_next(child); + } + + // clear the linked list of the removed node + tree_children(node) = NULL; + ptrdiff_t loc_last_child = tree->loc_last_child; + if (loc_last_child >= 0) tree_last_child(node) = NULL; + + // the tree now has one member less + tree->size--; + + return 0; +} + +void cxTreeRemoveSubtree(CxTree *tree, void *node) { + if (node == tree->root) { + tree->root = NULL; + tree->size = 0; + return; + } + size_t subtree_size = cxTreeSubtreeSize(tree, node); + cx_tree_unlink(node, cx_tree_node_layout(tree)); + tree->size -= subtree_size; +}
--- a/ucx/ucx.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * @mainpage UAP Common Extensions - * Library with common and useful functions, macros and data structures. - * <p> - * Latest available source:<br> - * <a href="https://sourceforge.net/projects/ucx/files/"> - * https://sourceforge.net/projects/ucx/files/</a> - * </p> - * - * <p> - * Repositories:<br> - * <a href="https://sourceforge.net/p/ucx/code"> - * https://sourceforge.net/p/ucx/code</a> - * - or - - * <a href="https://develop.uap-core.de/hg/ucx"> - * https://develop.uap-core.de/hg/ucx</a> - * </p> - * - * <h2>LICENCE</h2> - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "ucx/ucx.h" - -int ucx_szmul_impl(size_t a, size_t b, size_t *result) { - if(a == 0 || b == 0) { - *result = 0; - return 0; - } - size_t r = a * b; - if(r / b == a) { - *result = r; - return 0; - } else { - *result = 0; - return 1; - } -} -
--- a/ucx/ucx/allocator.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Allocator for custom memory management. - * - * A UCX allocator consists of a pointer to the memory area / pool and four - * function pointers to memory management functions operating on this memory - * area / pool. These functions shall behave equivalent to the standard libc - * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>. - * - * The signature of the memory management functions is based on the signature - * of the respective libc function but each of them takes the pointer to the - * memory area / pool as first argument. - * - * As the pointer to the memory area / pool can be arbitrarily chosen, any data - * can be provided to the memory management functions. A UcxMempool is just - * one example. - * - * @see mempool.h - * @see UcxMap - * - * @file allocator.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_ALLOCATOR_H -#define UCX_ALLOCATOR_H - -#include "ucx.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * A function pointer to the allocators <code>malloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_malloc)(void *pool, size_t n); - -/** - * A function pointer to the allocators <code>calloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size); - -/** - * A function pointer to the allocators <code>realloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n); - -/** - * A function pointer to the allocators <code>free()</code> function. - * @see UcxAllocator - */ -typedef void(*ucx_allocator_free)(void *pool, void *data); - -/** - * UCX allocator data structure containing memory management functions. - */ -typedef struct { - /** Pointer to an area of memory or a complex memory pool. - * This pointer will be passed to any memory management function as first - * argument. - */ - void *pool; - /** - * The <code>malloc()</code> function for this allocator. - */ - ucx_allocator_malloc malloc; - /** - * The <code>calloc()</code> function for this allocator. - */ - ucx_allocator_calloc calloc; - /** - * The <code>realloc()</code> function for this allocator. - */ - ucx_allocator_realloc realloc; - /** - * The <code>free()</code> function for this allocator. - */ - ucx_allocator_free free; -} UcxAllocator; - -/** - * Returns a pointer to the default allocator. - * - * The default allocator contains wrappers to the standard libc memory - * management functions. Use this function to get a pointer to a globally - * available allocator. You may also define an own UcxAllocator by assigning - * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable - * to any function that takes a UcxAllocator as argument. Note that using - * this function is the recommended way of passing a default allocator, thus - * it never runs out of scope. - * - * @return a pointer to the default allocator - * - * @see UCX_ALLOCATOR_DEFAULT - */ -UcxAllocator *ucx_default_allocator(); - -/** - * A wrapper for the standard libc <code>malloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to <code>malloc()</code> - * @return return value of <code>malloc()</code> - */ -void *ucx_default_malloc(void *ignore, size_t n); -/** - * A wrapper for the standard libc <code>calloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to <code>calloc()</code> - * @param size argument passed to <code>calloc()</code> - * @return return value of <code>calloc()</code> - */ -void *ucx_default_calloc(void *ignore, size_t n, size_t size); -/** - * A wrapper for the standard libc <code>realloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argumend passed to <code>realloc()</code> - * @param n argument passed to <code>realloc()</code> - * @return return value of <code>realloc()</code> - */ -void *ucx_default_realloc(void *ignore, void *data, size_t n); -/** - * A wrapper for the standard libc <code>free()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argument passed to <code>free()</code> - */ -void ucx_default_free(void *ignore, void *data); - -/** - * Shorthand for calling an allocators malloc function. - * @param allocator the allocator to use - * @param n size of space to allocate - * @return a pointer to the allocated memory area - */ -#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n)) - -/** - * Shorthand for calling an allocators calloc function. - * @param allocator the allocator to use - * @param n the count of elements the space should be allocated for - * @param size the size of each element - * @return a pointer to the allocated memory area - */ -#define alcalloc(allocator, n, size) \ - ((allocator)->calloc((allocator)->pool, n, size)) - -/** - * Shorthand for calling an allocators realloc function. - * @param allocator the allocator to use - * @param ptr the pointer to the memory area that shall be reallocated - * @param n the new size of the allocated memory area - * @return a pointer to the reallocated memory area - */ -#define alrealloc(allocator, ptr, n) \ - ((allocator)->realloc((allocator)->pool, ptr, n)) - -/** - * Shorthand for calling an allocators free function. - * @param allocator the allocator to use - * @param ptr the pointer to the memory area that shall be freed - */ -#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr)) - -/** - * Convenient macro for a default allocator <code>struct</code> definition. - */ -#define UCX_ALLOCATOR_DEFAULT {NULL, \ - ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \ - ucx_default_free } - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_ALLOCATOR_H */ -
--- a/ucx/ucx/array.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,460 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Dynamically allocated array implementation. - * - * @file array.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_ARRAY_H -#define UCX_ARRAY_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX array type. - */ -typedef struct { - /** - * The current capacity of the array. - */ - size_t capacity; - /** - * The actual number of elements in the array. - */ - size_t size; - /** - * The size of an individual element in bytes. - */ - size_t elemsize; - /** - * A pointer to the data. - */ - void* data; - /** - * The allocator used for the data. - */ - UcxAllocator* allocator; -} UcxArray; - -/** - * Sets an element in an arbitrary user defined array. - * The data is copied from the specified data location. - * - * If the capacity is insufficient, the array is automatically reallocated and - * the possibly new pointer is stored in the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param elmsize the size of each element - * @param idx the index of the element to set - * @param data a pointer to the element data - * @return zero on success or non-zero on error (errno will be set) - */ -#define ucx_array_util_set(array, capacity, elmsize, idx, data) \ - ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \ - elmsize, idx, data) - -/** - * Sets an element in an arbitrary user defined array. - * The data is copied from the specified data location. - * - * If the capacity is insufficient, the array is automatically reallocated - * using the specified allocator and the possibly new pointer is stored in - * the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param alloc the allocator that shall be used to reallocate the array - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param elmsize the size of each element - * @param idx the index of the element to set - * @param data a pointer to the element data - * @return zero on success or non-zero on error (errno will be set) - */ -int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t elmsize, size_t idx, void* data); - -/** - * Stores a pointer in an arbitrary user defined array. - * The element size of the array must be sizeof(void*). - * - * If the capacity is insufficient, the array is automatically reallocated and - * the possibly new pointer is stored in the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param idx the index of the element to set - * @param ptr the pointer to store - * @return zero on success or non-zero on error (errno will be set) - */ -#define ucx_array_util_setptr(array, capacity, idx, ptr) \ - ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \ - capacity, idx, ptr) - -/** - * Stores a pointer in an arbitrary user defined array. - * The element size of the array must be sizeof(void*). - * - * If the capacity is insufficient, the array is automatically reallocated - * using the specified allocator and the possibly new pointer is stored in - * the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param alloc the allocator that shall be used to reallocate the array - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param idx the index of the element to set - * @param ptr the pointer to store - * @return zero on success or non-zero on error (errno will be set) - */ -int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t idx, void* ptr); - - -/** - * Creates a new UCX array with the given capacity and element size. - * @param capacity the initial capacity - * @param elemsize the element size - * @return a pointer to a new UCX array structure - */ -UcxArray* ucx_array_new(size_t capacity, size_t elemsize); - -/** - * Creates a new UCX array using the specified allocator. - * - * @param capacity the initial capacity - * @param elemsize the element size - * @param allocator the allocator to use - * @return a pointer to new UCX array structure - */ -UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize, - UcxAllocator* allocator); - -/** - * Initializes a UCX array structure with the given capacity and element size. - * The structure must be uninitialized as the data pointer will be overwritten. - * - * @param array the structure to initialize - * @param capacity the initial capacity - * @param elemsize the element size - */ -void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize); - -/** - * Initializes a UCX array structure using the specified allocator. - * The structure must be uninitialized as the data pointer will be overwritten. - * - * @param array the structure to initialize - * @param capacity the initial capacity - * @param elemsize the element size - * @param allocator the allocator to use - */ -void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize, - UcxAllocator* allocator); - -/** - * Creates an shallow copy of an array. - * - * This function clones the specified array by using memcpy(). - * If the destination capacity is insufficient, an automatic reallocation is - * attempted. - * - * Note: if the destination array is uninitialized, the behavior is undefined. - * - * @param dest the array to copy to - * @param src the array to copy from - * @return zero on success, non-zero on reallocation failure. - */ -int ucx_array_clone(UcxArray* dest, UcxArray const* src); - - -/** - * Compares two UCX arrays element-wise by using a compare function. - * - * Elements of the two specified arrays are compared by using the specified - * compare function and the additional data. The type and content of this - * additional data depends on the cmp_func() used. - * - * This function always returns zero, if the element sizes of the arrays do - * not match and performs no comparisons in this case. - * - * @param array1 the first array - * @param array2 the second array - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the two arrays equal element-wise, 0 otherwise - */ -int ucx_array_equals(UcxArray const *array1, UcxArray const *array2, - cmp_func cmpfnc, void* data); - -/** - * Destroys the array. - * - * The data is freed and both capacity and count are reset to zero. - * If the array structure itself has been dynamically allocated, it has to be - * freed separately. - * - * @param array the array to destroy - */ -void ucx_array_destroy(UcxArray *array); - -/** - * Destroys and frees the array. - * - * @param array the array to free - */ -void ucx_array_free(UcxArray *array); - -/** - * Inserts elements at the end of the array. - * - * This is an O(1) operation. - * The array will automatically grow, if the capacity is exceeded. - * If a pointer to data is provided, the data is copied into the array with - * memcpy(). Otherwise the new elements are completely zeroed. - * - * @param array a pointer the array where to append the data - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, zeroed elements are appended) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_set_from() - * @see ucx_array_append() - */ -int ucx_array_append_from(UcxArray *array, void *data, size_t count); - - -/** - * Inserts elements at the beginning of the array. - * - * This is an expensive operation, because the contents must be moved. - * If there is no particular reason to prepend data, you should use - * ucx_array_append_from() instead. - * - * @param array a pointer the array where to prepend the data - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, zeroed elements are inserted) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_append_from() - * @see ucx_array_set_from() - * @see ucx_array_prepend() - */ -int ucx_array_prepend_from(UcxArray *array, void *data, size_t count); - - -/** - * Sets elements starting at the specified index. - * - * If the any index is out of bounds, the array automatically grows. - * The pointer to the data may be NULL, in which case the elements are zeroed. - * - * @param array a pointer the array where to set the data - * @param index the index of the element to set - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, the memory in the array is zeroed) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_append_from() - * @see ucx_array_set() - */ -int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count); - -/** - * Concatenates two arrays. - * - * The contents of the second array are appended to the first array in one - * single operation. The second array is otherwise left untouched. - * - * The first array may grow automatically. If this fails, both arrays remain - * unmodified. - * - * @param array1 first array - * @param array2 second array - * @return zero on success, non-zero if reallocation was necessary but failed - * or the element size does not match - */ -int ucx_array_concat(UcxArray *array1, const UcxArray *array2); - -/** - * Returns a pointer to the array element at the specified index. - * - * @param array the array to retrieve the element from - * @param index index of the element to return - * @return a pointer to the element at the specified index or <code>NULL</code>, - * if the index is greater than the array size - */ -void *ucx_array_at(UcxArray const* array, size_t index); - -/** - * Returns the index of an element containing the specified data. - * - * This function uses a cmp_func() to compare the data of each list element - * with the specified data. If no cmp_func is provided, memcmp() is used. - * - * If the array contains the data more than once, the index of the first - * occurrence is returned. - * If the array does not contain the data, the size of array is returned. - * - * @param array the array where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return the index of the element containing the specified data or the size of - * the array, if the data is not found in this array - */ -size_t ucx_array_find(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Checks, if an array contains a specific element. - * - * An element is found, if ucx_array_find() returns a value less than the size. - * - * @param array the array where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the array contains the specified element data - * @see ucx_array_find() - */ -int ucx_array_contains(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Sorts a UcxArray with the best available sort algorithm. - * - * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS). - * The order of arguments is automatically adjusted for the FreeBSD and MacOS - * version of qsort_r(). - * - * If qsort_r() is not available, a merge sort algorithm is used, which is - * guaranteed to use no more additional memory than for exactly one element. - * - * @param array the array to sort - * @param cmpfnc the function that shall be used to compare the element data - * @param data additional data for the cmp_func() or <code>NULL</code> - */ -void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data); - -/** - * Removes an element from the array. - * - * This is in general an expensive operation, because several elements may - * be moved. If the order of the elements is not relevant, use - * ucx_array_remove_fast() instead. - * - * @param array pointer to the array from which the element shall be removed - * @param index the index of the element to remove - */ -void ucx_array_remove(UcxArray *array, size_t index); - -/** - * Removes an element from the array. - * - * This is an O(1) operation, but does not maintain the order of the elements. - * The last element in the array is moved to the location of the removed - * element. - * - * @param array pointer to the array from which the element shall be removed - * @param index the index of the element to remove - */ -void ucx_array_remove_fast(UcxArray *array, size_t index); - -/** - * Shrinks the memory to exactly fit the contents. - * - * After this operation, the capacity equals the size. - * - * @param array a pointer to the array - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_shrink(UcxArray* array); - -/** - * Sets the capacity of the array. - * - * If the new capacity is smaller than the size of the array, the elements - * are removed and the size is adjusted accordingly. - * - * @param array a pointer to the array - * @param capacity the new capacity - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_resize(UcxArray* array, size_t capacity); - -/** - * Resizes the array only, if the capacity is insufficient. - * - * If the requested capacity is smaller than the current capacity, this - * function does nothing. - * - * @param array a pointer to the array - * @param capacity the guaranteed capacity - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_reserve(UcxArray* array, size_t capacity); - -/** - * Resizes the capacity, if the specified number of elements would not fit. - * - * A call to ucx_array_grow(array, count) is effectively the same as - * ucx_array_reserve(array, array->size+count). - * - * @param array a pointer to the array - * @param count the number of elements that should additionally fit - * into the array - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_grow(UcxArray* array, size_t count); - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_ARRAY_H */ -
--- a/ucx/ucx/avl.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,353 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - -/** - * @file avl.h - * - * AVL tree implementation. - * - * This binary search tree implementation allows average O(1) insertion and - * removal of elements (excluding binary search time). - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_AVL_H -#define UCX_AVL_H - -#include "ucx.h" -#include "allocator.h" -#include <inttypes.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX AVL Node type. - * - * @see UcxAVLNode - */ -typedef struct UcxAVLNode UcxAVLNode; - -/** - * UCX AVL Node. - */ -struct UcxAVLNode { - /** - * The key for this node. - */ - intptr_t key; - /** - * Data contained by this node. - */ - void *value; - /** - * The height of this (sub)-tree. - */ - size_t height; - /** - * Parent node. - */ - UcxAVLNode *parent; - /** - * Root node of left subtree. - */ - UcxAVLNode *left; - /** - * Root node of right subtree. - */ - UcxAVLNode *right; -}; - -/** - * UCX AVL Tree. - */ -typedef struct { - /** - * The UcxAllocator that shall be used to manage the memory for node data. - */ - UcxAllocator *allocator; - /** - * Root node of the tree. - */ - UcxAVLNode *root; - /** - * Compare function that shall be used to compare the UcxAVLNode keys. - * @see UcxAVLNode.key - */ - cmp_func cmpfunc; - /** - * Custom user data. - * This data will also be provided to the cmpfunc. - */ - void *userdata; -} UcxAVLTree; - -/** - * Initializes a new UcxAVLTree with a default allocator. - * - * @param cmpfunc the compare function that shall be used - * @return a new UcxAVLTree object - * @see ucx_avl_new_a() - */ -UcxAVLTree *ucx_avl_new(cmp_func cmpfunc); - -/** - * Initializes a new UcxAVLTree with the specified allocator. - * - * The cmpfunc should be capable of comparing two keys within this AVL tree. - * So if you want to use null terminated strings as keys, you could use the - * ucx_cmp_str() function here. - * - * @param cmpfunc the compare function that shall be used - * @param allocator the UcxAllocator that shall be used - * @return a new UcxAVLTree object - */ -UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator); - -/** - * Destroys a UcxAVLTree. - * - * Note, that the contents are not automatically freed. - * Use may use #ucx_avl_free_content() before calling this function. - * - * @param tree the tree to destroy - * @see ucx_avl_free_content() - */ -void ucx_avl_free(UcxAVLTree *tree); - -/** - * Frees the contents of a UcxAVLTree. - * - * This is a convenience function that iterates over the tree and passes all - * values to the specified destructor function. - * - * If no destructor is specified (<code>NULL</code>), the free() function of - * the tree's own allocator is used. - * - * You must ensure, that it is valid to pass each value in the map to the same - * destructor function. - * - * You should free the entire tree afterwards, as the contents will be invalid. - * - * @param tree for which the contents shall be freed - * @param destr optional pointer to a destructor function - * @see ucx_avl_free() - */ -void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr); - -/** - * Macro for initializing a new UcxAVLTree with the default allocator and a - * ucx_cmp_ptr() compare function. - * - * @return a new default UcxAVLTree object - */ -#define ucx_avl_default_new() \ - ucx_avl_new_a(ucx_cmp_ptr, ucx_default_allocator()) - -/** - * Gets the node from the tree, that is associated with the specified key. - * @param tree the UcxAVLTree - * @param key the key - * @return the node (or <code>NULL</code>, if the key is not present) - */ -UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key); - -/** - * Gets the value from the tree, that is associated with the specified key. - * @param tree the UcxAVLTree - * @param key the key - * @return the value (or <code>NULL</code>, if the key is not present) - */ -void *ucx_avl_get(UcxAVLTree *tree, intptr_t key); - -/** - * A mode for #ucx_avl_find_node() with the same behavior as - * #ucx_avl_get_node(). - */ -#define UCX_AVL_FIND_EXACT 0 -/** - * A mode for #ucx_avl_find_node() finding the node whose key is at least - * as large as the specified key. - */ -#define UCX_AVL_FIND_LOWER_BOUNDED 1 -/** - * A mode for #ucx_avl_find_node() finding the node whose key is at most - * as large as the specified key. - */ -#define UCX_AVL_FIND_UPPER_BOUNDED 2 -/** - * A mode for #ucx_avl_find_node() finding the node with a key that is as close - * to the specified key as possible. If the key is present, the behavior is - * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on - * empty trees. - */ -#define UCX_AVL_FIND_CLOSEST 3 - -/** - * Finds a node within the tree. The following modes are supported: - * <ul> - * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li> - * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least - * as large as the specified key</li> - * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most - * as large as the specified key</li> - * <li>#UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to - * the specified key as possible. If the key is present, the behavior is - * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on - * empty trees.</li> - * </ul> - * - * The distance function provided MUST agree with the compare function of - * the AVL tree. - * - * @param tree the UcxAVLTree - * @param key the key - * @param dfnc the distance function - * @param mode the find mode - * @return the node (or <code>NULL</code>, if no node can be found) - */ -UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode); - -/** - * Finds a value within the tree. - * See #ucx_avl_find_node() for details. - * - * @param tree the UcxAVLTree - * @param key the key - * @param dfnc the distance function - * @param mode the find mode - * @return the value (or <code>NULL</code>, if no value can be found) - */ -void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode); - -/** - * Puts a key/value pair into the tree. - * - * Attention: use this function only, if a possible old value does not need - * to be preserved. - * - * @param tree the UcxAVLTree - * @param key the key - * @param value the new value - * @return zero, if and only if the operation succeeded - */ -int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value); - -/** - * Puts a key/value pair into the tree. - * - * This is a secure function which saves the old value to the variable pointed - * at by oldvalue. - * - * @param tree the UcxAVLTree - * @param key the key - * @param value the new value - * @param oldvalue optional: a pointer to the location where a possible old - * value shall be stored - * @return zero, if and only if the operation succeeded - */ -int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue); - -/** - * Removes a node from the AVL tree. - * - * Note: the specified node is logically removed. The tree implementation - * decides which memory area is freed. In most cases the here provided node - * is freed, so its further use is generally undefined. - * - * @param tree the UcxAVLTree - * @param node the node to remove - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node); - -/** - * Removes an element from the AVL tree. - * - * @param tree the UcxAVLTree - * @param key the key - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove(UcxAVLTree *tree, intptr_t key); - -/** - * Removes an element from the AVL tree. - * - * This is a secure function which saves the old key and value data from node - * to the variables at the location of oldkey and oldvalue (if specified), so - * they can be freed afterwards (if necessary). - * - * Note: the returned key in oldkey is possibly not the same as the provided - * key for the lookup (in terms of memory location). - * - * @param tree the UcxAVLTree - * @param key the key of the element to remove - * @param oldkey optional: a pointer to the location where the old key shall be - * stored - * @param oldvalue optional: a pointer to the location where the old value - * shall be stored - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, - intptr_t *oldkey, void **oldvalue); - -/** - * Counts the nodes in the specified UcxAVLTree. - * @param tree the AVL tree - * @return the node count - */ -size_t ucx_avl_count(UcxAVLTree *tree); - -/** - * Finds the in-order predecessor of the given node. - * @param node an AVL node - * @return the in-order predecessor of the given node, or <code>NULL</code> if - * the given node is the in-order minimum - */ -UcxAVLNode* ucx_avl_pred(UcxAVLNode* node); - -/** - * Finds the in-order successor of the given node. - * @param node an AVL node - * @return the in-order successor of the given node, or <code>NULL</code> if - * the given node is the in-order maximum - */ -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_AVL_H */ -
--- a/ucx/ucx/buffer.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file buffer.h - * - * Advanced buffer implementation. - * - * Instances of UcxBuffer can be used to read from or to write to like one - * would do with a stream. This allows the use of ucx_stream_copy() to copy - * contents from one buffer to another. - * - * Some features for convenient use of the buffer - * can be enabled. See the documentation of the macro constants for more - * information. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_BUFFER_H -#define UCX_BUFFER_H - -#include "ucx.h" -#include <sys/types.h> -#include <stdio.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * No buffer features enabled (all flags cleared). - */ -#define UCX_BUFFER_DEFAULT 0x00 - -/** - * If this flag is enabled, the buffer will automatically free its contents. - */ -#define UCX_BUFFER_AUTOFREE 0x01 - -/** - * If this flag is enabled, the buffer will automatically extends its capacity. - */ -#define UCX_BUFFER_AUTOEXTEND 0x02 - -/** UCX Buffer. */ -typedef struct { - /** A pointer to the buffer contents. */ - char *space; - /** Current position of the buffer. */ - size_t pos; - /** Current capacity (i.e. maximum size) of the buffer. */ - size_t capacity; - /** Current size of the buffer content. */ - size_t size; - /** - * Flag register for buffer features. - * @see #UCX_BUFFER_DEFAULT - * @see #UCX_BUFFER_AUTOFREE - * @see #UCX_BUFFER_AUTOEXTEND - */ - int flags; -} UcxBuffer; - -/** - * Creates a new buffer. - * - * <b>Note:</b> you may provide <code>NULL</code> as argument for - * <code>space</code>. Then this function will allocate the space and enforce - * the #UCX_BUFFER_AUTOFREE flag. - * - * @param space pointer to the memory area, or <code>NULL</code> to allocate - * new memory - * @param capacity the capacity of the buffer - * @param flags buffer features (see UcxBuffer.flags) - * @return the new buffer - */ -UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags); - -/** - * Destroys a buffer. - * - * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer - * are also freed. - * - * @param buffer the buffer to destroy - */ -void ucx_buffer_free(UcxBuffer* buffer); - -/** - * Creates a new buffer and fills it with extracted content from another buffer. - * - * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer. - * - * @param src the source buffer - * @param start the start position of extraction - * @param length the count of bytes to extract (must not be zero) - * @param flags feature mask for the new buffer - * @return a new buffer containing the extraction - */ -UcxBuffer* ucx_buffer_extract(UcxBuffer *src, - size_t start, size_t length, int flags); - -/** - * A shorthand macro for the full extraction of the buffer. - * - * @param src the source buffer - * @param flags feature mask for the new buffer - * @return a new buffer with the extracted content - */ -#define ucx_buffer_clone(src,flags) \ - ucx_buffer_extract(src, 0, (src)->capacity, flags) - - -/** - * Shifts the contents of the buffer by the given offset. - * - * If the offset is positive, the contents are shifted to the right. - * If auto extension is enabled, the buffer grows, if necessary. - * In case the auto extension fails, this function returns a non-zero value and - * no contents are changed. - * If auto extension is disabled, the contents that do not fit into the buffer - * are discarded. - * - * If the offset is negative, the contents are shifted to the left where the - * first <code>shift</code> bytes are discarded. - * The new size of the buffer is the old size minus - * the absolute shift value. - * If this value is larger than the buffer size, the buffer is emptied (but - * not cleared, see the security note below). - * - * The buffer position gets shifted alongside with the content but is kept - * within the boundaries of the buffer. - * - * <b>Security note:</b> the shifting operation does <em>not</em> erase the - * previously occupied memory cells. You can easily do that manually, e.g. by - * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or - * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code> - * for a left shift. - * - * @param buffer the buffer - * @param shift the shift offset (negative means left shift) - * @return 0 on success, non-zero if a required auto-extension fails - */ -int ucx_buffer_shift(UcxBuffer* buffer, off_t shift); - -/** - * Shifts the buffer to the right. - * See ucx_buffer_shift() for details. - * - * @param buffer the buffer - * @param shift the shift offset - * @return 0 on success, non-zero if a required auto-extension fails - * @see ucx_buffer_shift() - */ -int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift); - -/** - * Shifts the buffer to the left. - * - * See ucx_buffer_shift() for details. Note, however, that this method expects - * a positive shift offset. - * - * Since a left shift cannot fail due to memory allocation problems, this - * function always returns zero. - * - * @param buffer the buffer - * @param shift the shift offset - * @return always zero - * @see ucx_buffer_shift() - */ -int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift); - - -/** - * Moves the position of the buffer. - * - * The new position is relative to the <code>whence</code> argument. - * - * SEEK_SET marks the start of the buffer. - * SEEK_CUR marks the current position. - * SEEK_END marks the end of the buffer. - * - * With an offset of zero, this function sets the buffer position to zero - * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position - * unchanged (SEEK_CUR). - * - * @param buffer - * @param offset position offset relative to <code>whence</code> - * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END - * @return 0 on success, non-zero if the position is invalid - * - */ -int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence); - -/** - * Clears the buffer by resetting the position and deleting the data. - * - * The data is deleted by a zeroing it with call to <code>memset()</code>. - * - * @param buffer the buffer to be cleared - */ -#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \ - (buffer)->size = 0; (buffer)->pos = 0; - -/** - * Tests, if the buffer position has exceeded the buffer capacity. - * - * @param buffer the buffer to test - * @return non-zero, if the current buffer position has exceeded the last - * available byte of the buffer. - */ -int ucx_buffer_eof(UcxBuffer *buffer); - - -/** - * Extends the capacity of the buffer. - * - * <b>Note:</b> The buffer capacity increased by a power of two. I.e. - * the buffer capacity is doubled, as long as it would not hold the current - * content plus the additional required bytes. - * - * <b>Attention:</b> the argument provided is the number of <i>additional</i> - * bytes the buffer shall hold. It is <b>NOT</b> the total number of bytes the - * buffer shall hold. - * - * @param buffer the buffer to extend - * @param additional_bytes the number of additional bytes the buffer shall - * <i>at least</i> hold - * @return 0 on success or a non-zero value on failure - */ -int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes); - -/** - * Writes data to a UcxBuffer. - * - * The position of the buffer is increased by the number of bytes written. - * - * @param ptr a pointer to the memory area containing the bytes to be written - * @param size the length of one element - * @param nitems the element count - * @param buffer the UcxBuffer to write to - * @return the total count of bytes written - */ -size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer); - -/** - * Reads data from a UcxBuffer. - * - * The position of the buffer is increased by the number of bytes read. - * - * @param ptr a pointer to the memory area where to store the read data - * @param size the length of one element - * @param nitems the element count - * @param buffer the UcxBuffer to read from - * @return the total number of elements read - */ -size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer); - -/** - * Writes a character to a buffer. - * - * The least significant byte of the argument is written to the buffer. If the - * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled, - * the buffer capacity is extended by ucx_buffer_extend(). If the feature is - * disabled or buffer extension fails, <code>EOF</code> is returned. - * - * On successful write the position of the buffer is increased. - * - * @param buffer the buffer to write to - * @param c the character to write as <code>int</code> value - * @return the byte that has bean written as <code>int</code> value or - * <code>EOF</code> when the end of the stream is reached and automatic - * extension is not enabled or not possible - */ -int ucx_buffer_putc(UcxBuffer *buffer, int c); - -/** - * Gets a character from a buffer. - * - * The current position of the buffer is increased after a successful read. - * - * @param buffer the buffer to read from - * @return the character as <code>int</code> value or <code>EOF</code>, if the - * end of the buffer is reached - */ -int ucx_buffer_getc(UcxBuffer *buffer); - -/** - * Writes a string to a buffer. - * - * @param buffer the buffer - * @param str the string - * @return the number of bytes written - */ -size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str); - -/** - * Returns the complete buffer content as sstr_t. - * @param buffer the buffer - * @return the result of <code>sstrn()</code> with the buffer space and size - * as arguments - */ -#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_BUFFER_H */ -
--- a/ucx/ucx/list.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,512 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Doubly linked list implementation. - * - * @file list.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_LIST_H -#define UCX_LIST_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Loop statement for UCX lists. - * - * The first argument is the name of the iteration variable. The scope of - * this variable is limited to the <code>UCX_FOREACH</code> statement. - * - * The second argument is a pointer to the list. In most cases this will be the - * pointer to the first element of the list, but it may also be an arbitrary - * element of the list. The iteration will then start with that element. - * - * @param list The first element of the list - * @param elem The variable name of the element - */ -#define UCX_FOREACH(elem,list) \ - for (UcxList* elem = (UcxList*) list ; elem != NULL ; elem = elem->next) - -/** - * UCX list type. - * @see UcxList - */ -typedef struct UcxList UcxList; - -/** - * UCX list structure. - */ -struct UcxList { - /** - * List element payload. - */ - void *data; - /** - * Pointer to the next list element or <code>NULL</code>, if this is the - * last element. - */ - UcxList *next; - /** - * Pointer to the previous list element or <code>NULL</code>, if this is - * the first element. - */ - UcxList *prev; -}; - -/** - * Creates an element-wise copy of a list. - * - * This function clones the specified list by creating new list elements and - * copying the data with the specified copy_func(). If no copy_func() is - * specified, a shallow copy is created and the new list will reference the - * same data as the source list. - * - * @param list the list to copy - * @param cpyfnc a pointer to the function that shall copy an element (may be - * <code>NULL</code>) - * @param data additional data for the copy_func() - * @return a pointer to the copy - */ -UcxList *ucx_list_clone(const UcxList *list, copy_func cpyfnc, void* data); - -/** - * Creates an element-wise copy of a list using a UcxAllocator. - * - * See ucx_list_clone() for details. - * - * You might want to pass the allocator via the <code>data</code> parameter, - * to access it within the copy function for making deep copies. - * - * @param allocator the allocator to use - * @param list the list to copy - * @param cpyfnc a pointer to the function that shall copy an element (may be - * <code>NULL</code>) - * @param data additional data for the copy_func() - * @return a pointer to the copy - * @see ucx_list_clone() - */ -UcxList *ucx_list_clone_a(UcxAllocator *allocator, const UcxList *list, - copy_func cpyfnc, void* data); - -/** - * Compares two UCX lists element-wise by using a compare function. - * - * Each element of the two specified lists are compared by using the specified - * compare function and the additional data. The type and content of this - * additional data depends on the cmp_func() used. - * - * If the list pointers denote elements within a list, the lists are compared - * starting with the denoted elements. Thus any previous elements are not taken - * into account. This might be useful to check, if certain list tails match - * each other. - * - * @param list1 the first list - * @param list2 the second list - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the two lists equal element-wise, 0 otherwise - */ -int ucx_list_equals(const UcxList *list1, const UcxList *list2, - cmp_func cmpfnc, void* data); - -/** - * Destroys the entire list. - * - * The members of the list are not automatically freed, so ensure they are - * otherwise referenced or destroyed by ucx_list_free_contents(). - * Otherwise, a memory leak is likely to occur. - * - * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call - * to ucx_list_first() on the argument must return the argument itself) - * - * @param list the list to free - * @see ucx_list_free_contents() - */ -void ucx_list_free(UcxList *list); - -/** - * Destroys the entire list using a UcxAllocator. - * - * See ucx_list_free() for details. - * - * @param allocator the allocator to use - * @param list the list to free - * @see ucx_list_free() - */ -void ucx_list_free_a(UcxAllocator *allocator, UcxList *list); - -/** - * Destroys the contents of the specified list by calling the specified - * destructor on each of them. - * - * Note, that the contents are not usable afterwards and the list should be - * destroyed with ucx_list_free(). - * - * If no destructor is specified (<code>NULL</code>), stdlib's free() is used. - * - * @param list the list for which the contents shall be freed - * @param destr optional destructor function - * @see ucx_list_free() - */ -void ucx_list_free_content(UcxList* list, ucx_destructor destr); - - -/** - * Inserts an element at the end of the list. - * - * This is generally an O(n) operation, as the end of the list is retrieved with - * ucx_list_last(). - * - * @param list the list where to append the data, or <code>NULL</code> to - * create a new list - * @param data the data to insert - * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to - * the newly created list otherwise - */ -UcxList *ucx_list_append(UcxList *list, void *data); - -/** - * Inserts an element at the end of the list using a UcxAllocator. - * - * See ucx_list_append() for details. - * - * @param allocator the allocator to use - * @param list the list where to append the data, or <code>NULL</code> to - * create a new list - * @param data the data to insert - * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to - * the newly created list otherwise - * @see ucx_list_append() - */ -UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data); - - -/** - * Inserts an element at the beginning of the list. - * - * You <i>should</i> overwrite the old list pointer by calling - * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may - * also perform successive calls of ucx_list_prepend() on the same list pointer, - * as this function always searchs for the head of the list with - * ucx_list_first(). - * - * @param list the list where to insert the data or <code>NULL</code> to create - * a new list - * @param data the data to insert - * @return a pointer to the new list head - */ -UcxList *ucx_list_prepend(UcxList *list, void *data); - -/** - * Inserts an element at the beginning of the list using a UcxAllocator. - * - * See ucx_list_prepend() for details. - * - * @param allocator the allocator to use - * @param list the list where to insert the data or <code>NULL</code> to create - * a new list - * @param data the data to insert - * @return a pointer to the new list head - * @see ucx_list_prepend() - */ -UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data); - -/** - * Concatenates two lists. - * - * Either of the two arguments may be <code>NULL</code>. - * - * This function modifies the references to the next/previous element of - * the last/first element of <code>list1</code>/<code> - * list2</code>. - * - * @param list1 first list - * @param list2 second list - * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is - * returned, otherwise <code>list1</code> is returned - */ -UcxList *ucx_list_concat(UcxList *list1, UcxList *list2); - -/** - * Returns the first element of a list. - * - * If the argument is the list pointer, it is directly returned. Otherwise - * this function traverses to the first element of the list and returns the - * list pointer. - * - * @param elem one element of the list - * @return the first element of the list, the specified element is a member of - */ -UcxList *ucx_list_first(const UcxList *elem); - -/** - * Returns the last element of a list. - * - * If the argument has no successor, it is the last element and therefore - * directly returned. Otherwise this function traverses to the last element of - * the list and returns it. - * - * @param elem one element of the list - * @return the last element of the list, the specified element is a member of - */ -UcxList *ucx_list_last(const UcxList *elem); - -/** - * Returns the list element at the specified index. - * - * @param list the list to retrieve the element from - * @param index index of the element to return - * @return the element at the specified index or <code>NULL</code>, if the - * index is greater than the list size - */ -UcxList *ucx_list_get(const UcxList *list, size_t index); - -/** - * Returns the index of an element. - * - * @param list the list where to search for the element - * @param elem the element to find - * @return the index of the element or -1 if the list does not contain the - * element - */ -ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem); - -/** - * Returns the element count of the list. - * - * @param list the list whose elements are counted - * @return the element count - */ -size_t ucx_list_size(const UcxList *list); - -/** - * Returns the index of an element containing the specified data. - * - * This function uses a cmp_func() to compare the data of each list element - * with the specified data. If no cmp_func is provided, the pointers are - * compared. - * - * If the list contains the data more than once, the index of the first - * occurrence is returned. - * - * @param list the list where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return the index of the element containing the specified data or -1 if the - * data is not found in this list - */ -ssize_t ucx_list_find(const UcxList *list, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Checks, if a list contains a specific element. - * - * An element is found, if ucx_list_find() returns a value greater than -1. - * - * @param list the list where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the list contains the specified element data - * @see ucx_list_find() - */ -int ucx_list_contains(const UcxList *list, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Sorts a UcxList with natural merge sort. - * - * This function uses O(n) additional temporary memory for merge operations - * that is automatically freed after each merge. - * - * As the head of the list might change, you <b>MUST</b> call this function - * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>. - * - * @param list the list to sort - * @param cmpfnc the function that shall be used to compare the element data - * @param data additional data for the cmp_func() - * @return the sorted list - */ -UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data); - -/** - * Removes an element from the list. - * - * If the first element is removed, the list pointer changes. So it is - * <i>highly recommended</i> to <i>always</i> update the pointer by calling - * <code>mylist = ucx_list_remove(mylist, myelem);</code>. - * - * @param list the list from which the element shall be removed - * @param element the element to remove - * @return returns the updated list pointer or <code>NULL</code>, if the list - * is now empty - */ -UcxList *ucx_list_remove(UcxList *list, UcxList *element); - -/** - * Removes an element from the list using a UcxAllocator. - * - * See ucx_list_remove() for details. - * - * @param allocator the allocator to use - * @param list the list from which the element shall be removed - * @param element the element to remove - * @return returns the updated list pointer or <code>NULL</code>, if the list - * @see ucx_list_remove() - */ -UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list, - UcxList *element); - -/** - * Returns the union of two lists. - * - * The union is a list of unique elements regarding cmpfnc obtained from - * both source lists. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the union - */ -UcxList* ucx_list_union(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the union of two lists. - * - * The union is a list of unique elements regarding cmpfnc obtained from - * both source lists. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the union - */ -UcxList* ucx_list_union_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two lists. - * - * The intersection contains all elements of the left list - * (including duplicates) that can be found in the right list. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the intersection - */ -UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two lists. - * - * The intersection contains all elements of the left list - * (including duplicates) that can be found in the right list. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the intersection - */ -UcxList* ucx_list_intersection_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two lists. - * - * The difference contains all elements of the left list - * (including duplicates) that are not equal to any element of the right list. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxList* ucx_list_difference(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two lists. - * - * The difference contains all elements of the left list - * (including duplicates) that are not equal to any element of the right list. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxList* ucx_list_difference_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_LIST_H */ -
--- a/ucx/ucx/logging.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Logging API. - * - * @file logging.h - * @author Mike Becker, Olaf Wintermann - */ -#ifndef UCX_LOGGING_H -#define UCX_LOGGING_H - -#include "ucx.h" -#include "map.h" -#include "string.h" -#include <stdio.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/* leave enough space for custom log levels */ - -/** Log level for error messages. */ -#define UCX_LOGGER_ERROR 0x00 - -/** Log level for warning messages. */ -#define UCX_LOGGER_WARN 0x10 - -/** Log level for information messages. */ -#define UCX_LOGGER_INFO 0x20 - -/** Log level for debug messages. */ -#define UCX_LOGGER_DEBUG 0x30 - -/** Log level for trace messages. */ -#define UCX_LOGGER_TRACE 0x40 - -/** - * Output flag for the log level. - * If this flag is set, the log message will contain the log level. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_LEVEL 0x01 - -/** - * Output flag for the timestmap. - * If this flag is set, the log message will contain the timestmap. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_TIMESTAMP 0x02 - -/** - * Output flag for the source. - * If this flag is set, the log message will contain the source file and line - * number. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_SOURCE 0x04 - -/** - * The UCX Logger object. - */ -typedef struct { - /** The stream this logger writes its messages to.*/ - void *stream; - - /** - * The write function that shall be used. - * For standard file or stdout loggers this might be standard fwrite - * (default). - */ - write_func writer; - - /** - * The date format for timestamp outputs including the delimiter - * (default: <code>"%F %T %z "</code>). - * @see UCX_LOGGER_TIMESTAMP - */ - char *dateformat; - - /** - * The level, this logger operates on. - * If a log command is issued, the message will only be logged, if the log - * level of the message is less or equal than the log level of the logger. - */ - unsigned int level; - - /** - * A configuration mask for automatic output. - * For each flag that is set, the logger automatically outputs some extra - * information like the timestamp or the source file and line number. - * See the documentation for the flags for details. - */ - unsigned int mask; - - /** - * A map of valid log levels for this logger. - * - * The keys represent all valid log levels and the values provide string - * representations, that are used, if the UCX_LOGGER_LEVEL flag is set. - * - * The exact data types are <code>unsigned int</code> for the key and - * <code>const char*</code> for the value. - * - * @see UCX_LOGGER_LEVEL - */ - UcxMap* levels; -} UcxLogger; - -/** - * Creates a new logger. - * @param stream the stream, which the logger shall write to - * @param level the level on which the logger shall operate - * @param mask configuration mask (cf. UcxLogger.mask) - * @return a new logger object - */ -UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask); - -/** - * Destroys the logger. - * - * The map containing the valid log levels is also automatically destroyed. - * - * @param logger the logger to destroy - */ -void ucx_logger_free(UcxLogger* logger); - -/** - * Internal log function - use macros instead. - * - * This function uses the <code>format</code> and variadic arguments for a - * printf()-style output of the log message. - * - * Dependent on the UcxLogger.mask some information is prepended. The complete - * format is: - * - * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code> - * - * The source file name is reduced to the actual file name. This is necessary to - * get consistent behavior over different definitions of the __FILE__ macro. - * - * <b>Attention:</b> the message (including automatically generated information) - * is limited to 4096 characters. The level description is limited to - * 256 characters and the timestamp string is limited to 128 characters. - * - * @param logger the logger to use - * @param level the level to log on - * @param file information about the source file - * @param line information about the source line number - * @param format format string - * @param ... arguments - * @see ucx_logger_log() - */ -void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, - const unsigned int line, const char* format, ...); - -/** - * Registers a custom log level. - * @param logger the logger - * @param level the log level as unsigned integer - * @param name a string literal describing the level - */ -#define ucx_logger_register_level(logger, level, name) {\ - unsigned int l; \ - l = level; \ - ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \ - } while (0); - -/** - * Logs a message at the specified level. - * @param logger the logger to use - * @param level the level to log the message on - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_log(logger, level, ...) \ - ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__) - -/** - * Shortcut for logging an error message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_error(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__) - -/** - * Shortcut for logging an information message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_info(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__) - -/** - * Shortcut for logging a warning message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_warn(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__) - -/** - * Shortcut for logging a debug message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_debug(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__) - -/** - * Shortcut for logging a trace message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_trace(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_LOGGING_H */
--- a/ucx/ucx/map.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,549 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file map.h - * - * Hash map implementation. - * - * This implementation uses murmur hash 2 and separate chaining with linked - * lists. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_MAP_H -#define UCX_MAP_H - -#include "ucx.h" -#include "string.h" -#include "allocator.h" -#include <stdio.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Loop statement for UCX maps. - * - * The <code>key</code> variable is implicitly defined, but the - * <code>value</code> variable must be already declared as type information - * cannot be inferred. - * - * @param key the variable name for the key - * @param value the variable name for the value - * @param iter a UcxMapIterator - * @see ucx_map_iterator() - */ -#define UCX_MAP_FOREACH(key,value,iter) \ - for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);) - -/** Type for the UCX map. @see UcxMap */ -typedef struct UcxMap UcxMap; - -/** Type for a key of a UcxMap. @see UcxKey */ -typedef struct UcxKey UcxKey; - -/** Type for an element of a UcxMap. @see UcxMapElement */ -typedef struct UcxMapElement UcxMapElement; - -/** Type for an iterator over a UcxMap. @see UcxMapIterator */ -typedef struct UcxMapIterator UcxMapIterator; - -/** Structure for the UCX map. */ -struct UcxMap { - /** An allocator that is used for the map elements. */ - UcxAllocator *allocator; - /** The array of map element lists. */ - UcxMapElement **map; - /** The size of the map is the length of the element list array. */ - size_t size; - /** The count of elements currently stored in this map. */ - size_t count; -}; - -/** Structure to publicly denote a key of a UcxMap. */ -struct UcxKey { - /** The key data. */ - const void *data; - /** The length of the key data. */ - size_t len; - /** A cache for the hash value of the key data. */ - int hash; -}; - -/** Internal structure for a key of a UcxMap. */ -struct UcxMapKey { - /** The key data. */ - void *data; - /** The length of the key data. */ - size_t len; - /** The hash value of the key data. */ - int hash; -}; - -/** Structure for an element of a UcxMap. */ -struct UcxMapElement { - /** The value data. */ - void *data; - - /** A pointer to the next element in the current list. */ - UcxMapElement *next; - - /** The corresponding key. */ - struct UcxMapKey key; -}; - -/** Structure for an iterator over a UcxMap. */ -struct UcxMapIterator { - /** The map to iterate over. */ - UcxMap const *map; - - /** The current map element. */ - UcxMapElement *cur; - - /** - * The current index of the element list array. - * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b> - * manually iterate over the map by increasing this index. Use - * ucx_map_iter_next(). - * @see UcxMap.map*/ - size_t index; -}; - -/** - * Creates a new hash map with the specified size. - * @param size the size of the hash map - * @return a pointer to the new hash map - */ -UcxMap *ucx_map_new(size_t size); - -/** - * Creates a new hash map with the specified size using a UcxAllocator. - * @param allocator the allocator to use - * @param size the size of the hash map - * @return a pointer to the new hash map - */ -UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size); - -/** - * Frees a hash map. - * - * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content() - * before calling this function to achieve that. - * - * @param map the map to be freed - * @see ucx_map_free_content() - */ -void ucx_map_free(UcxMap *map); - -/** - * Frees the contents of a hash map. - * - * This is a convenience function that iterates over the map and passes all - * values to the specified destructor function. - * - * If no destructor is specified (<code>NULL</code>), the free() function of - * the map's own allocator is used. - * - * You must ensure, that it is valid to pass each value in the map to the same - * destructor function. - * - * You should free or clear the map afterwards, as the contents will be invalid. - * - * @param map for which the contents shall be freed - * @param destr optional pointer to a destructor function - * @see ucx_map_free() - * @see ucx_map_clear() - */ -void ucx_map_free_content(UcxMap *map, ucx_destructor destr); - -/** - * Clears a hash map. - * - * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content() - * before calling this function to achieve that. - * - * @param map the map to be cleared - * @see ucx_map_free_content() - */ -void ucx_map_clear(UcxMap *map); - - -/** - * Copies contents from a map to another map using a copy function. - * - * <b>Note:</b> The destination map does not need to be empty. However, if it - * contains data with keys that are also present in the source map, the contents - * are overwritten. - * - * @param from the source map - * @param to the destination map - * @param fnc the copy function or <code>NULL</code> if the pointer address - * shall be copied - * @param data additional data for the copy function - * @return 0 on success or a non-zero value on memory allocation errors - */ -int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data); - -/** - * Clones the map and rehashes if necessary. - * - * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant. - * This function <i>always</i> ensures a new UcxMap.size of at least - * 2.5*UcxMap.count. - * - * @param map the map to clone - * @param fnc the copy function to use or <code>NULL</code> if the new and - * the old map shall share the data pointers - * @param data additional data for the copy function - * @return the cloned map - * @see ucx_map_copy() - */ -UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data); - -/** - * Clones the map and rehashes if necessary. - * - * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant. - * This function <i>always</i> ensures a new UcxMap.size of at least - * 2.5*UcxMap.count. - * - * @param allocator the allocator to use for the cloned map - * @param map the map to clone - * @param fnc the copy function to use or <code>NULL</code> if the new and - * the old map shall share the data pointers - * @param data additional data for the copy function - * @return the cloned map - * @see ucx_map_copy() - */ -UcxMap *ucx_map_clone_a(UcxAllocator *allocator, - UcxMap const *map, copy_func fnc, void *data); - -/** - * Increases size of the hash map, if necessary. - * - * The load value is 0.75*UcxMap.size. If the element count exceeds the load - * value, the map needs to be rehashed. Otherwise no action is performed and - * this function simply returns 0. - * - * The rehashing process ensures, that the UcxMap.size is at least - * 2.5*UcxMap.count. So there is enough room for additional elements without - * the need of another soon rehashing. - * - * You can use this function to dramatically increase access performance. - * - * @param map the map to rehash - * @return 1, if a memory allocation error occurred, 0 otherwise - */ -int ucx_map_rehash(UcxMap *map); - -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ -int ucx_map_put(UcxMap *map, UcxKey key, void *value); - -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ -void* ucx_map_get(UcxMap const *map, UcxKey key); - -/** - * Removes a key/value-pair from the map by using the key. - * - * @param map the map - * @param key the key - * @return the removed value - */ -void* ucx_map_remove(UcxMap *map, UcxKey key); - -/** - * Shorthand for putting data with a sstr_t key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_sstr_put(map, key, value) \ - ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value) - -/** - * Shorthand for putting data with a C string key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_cstr_put(map, key, value) \ - ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value) - -/** - * Shorthand for putting data with an integer key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_int_put(map, key, value) \ - ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value) - -/** - * Shorthand for getting data from the map with a sstr_t key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_sstr_get(map, key) \ - ucx_map_get(map, ucx_key(key.ptr, key.length)) - -/** - * Shorthand for getting data from the map with a C string key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_cstr_get(map, key) \ - ucx_map_get(map, ucx_key(key, strlen(key))) - -/** - * Shorthand for getting data from the map with an integer key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_int_get(map, key) \ - ucx_map_get(map, ucx_key(&key, sizeof(int))) - -/** - * Shorthand for removing data from the map with a sstr_t key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_sstr_remove(map, key) \ - ucx_map_remove(map, ucx_key(key.ptr, key.length)) - -/** - * Shorthand for removing data from the map with a C string key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_cstr_remove(map, key) \ - ucx_map_remove(map, ucx_key(key, strlen(key))) - -/** - * Shorthand for removing data from the map with an integer key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_int_remove(map, key) \ - ucx_map_remove(map, ucx_key(&key, sizeof(key))) - -/** - * Creates a UcxKey based on the given data. - * - * This function implicitly computes the hash. - * - * @param data the data for the key - * @param len the length of the data - * @return a UcxKey with implicitly computed hash - * @see ucx_hash() - */ -UcxKey ucx_key(const void *data, size_t len); - -/** - * Computes a murmur hash-2. - * - * @param data the data to hash - * @param len the length of the data - * @return the murmur hash-2 of the data - */ -int ucx_hash(const char *data, size_t len); - -/** - * Creates an iterator for a map. - * - * <b>Note:</b> A UcxMapIterator iterates over all elements in all element - * lists successively. Therefore the order highly depends on the key hashes and - * may vary under different map sizes. So generally you may <b>NOT</b> rely on - * the iteration order. - * - * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call - * ucx_map_iter_next() at least once before accessing any information. However, - * it is not recommended to access the fields of a UcxMapIterator directly. - * - * @param map the map to create the iterator for - * @return an iterator initialized on the first element of the - * first element list - * @see ucx_map_iter_next() - */ -UcxMapIterator ucx_map_iterator(UcxMap const *map); - -/** - * Proceeds to the next element of the map (if any). - * - * Subsequent calls on the same iterator proceed to the next element and - * store the key/value-pair into the memory specified as arguments of this - * function. - * - * If no further elements are found, this function returns zero and leaves the - * last found key/value-pair in memory. - * - * @param iterator the iterator to use - * @param key a pointer to the memory where to store the key - * @param value a pointer to the memory where to store the value - * @return 1, if another element was found, 0 if all elements has been processed - * @see ucx_map_iterator() - */ -int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value); - -/** - * Returns the union of two maps. - * - * The union is a fresh map which is filled by two successive calls of - * ucx_map_copy() on the two input maps. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the union - */ -UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the union of two maps. - * - * The union is a fresh map which is filled by two successive calls of - * ucx_map_copy() on the two input maps. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the union - */ -UcxMap* ucx_map_union_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two maps. - * - * The intersection is defined as a copy of the first map with every element - * removed that has no valid key in the second map. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the intersection - */ -UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two maps. - * - * The intersection is defined as a copy of the first map with every element - * removed that has no valid key in the second map. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the intersection - */ -UcxMap* ucx_map_intersection_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two maps. - * - * The difference contains a copy of all elements of the first map - * for which the corresponding keys cannot be found in the second map. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two maps. - * - * The difference contains a copy of all elements of the first map - * for which the corresponding keys cannot be found in the second map. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxMap* ucx_map_difference_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_MAP_H */ -
--- a/ucx/ucx/mempool.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file mempool.h - * - * Memory pool implementation. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_MEMPOOL_H -#define UCX_MEMPOOL_H - -#include "ucx.h" -#include "allocator.h" -#include <stddef.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX mempool structure. - */ -typedef struct { - /** UcxAllocator based on this pool */ - UcxAllocator *allocator; - - /** List of pointers to pooled memory. */ - void **data; - - /** Count of pooled memory items. */ - size_t ndata; - - /** Memory pool size. */ - size_t size; -} UcxMempool; - -/** Shorthand for a new default memory pool with a capacity of 16 elements. */ -#define ucx_mempool_new_default() ucx_mempool_new(16) - - -/** - * Creates a memory pool with the specified initial size. - * - * As the created memory pool automatically grows in size by factor two when - * trying to allocate memory on a full pool, it is recommended that you use - * a power of two for the initial size. - * - * @param n initial pool size (should be a power of two, e.g. 16) - * @return a pointer to the new memory pool - * @see ucx_mempool_new_default() - */ -UcxMempool *ucx_mempool_new(size_t n); - -/** - * Resizes a memory pool. - * - * This function will fail if the new capacity is not sufficient for the - * present data. - * - * @param pool the pool to resize - * @param newcap the new capacity - * @return zero on success or non-zero on failure - */ -int ucx_mempool_chcap(UcxMempool *pool, size_t newcap); - -/** - * Allocates pooled memory. - * - * @param pool the memory pool - * @param n amount of memory to allocate - * @return a pointer to the allocated memory - * @see ucx_allocator_malloc() - */ -void *ucx_mempool_malloc(UcxMempool *pool, size_t n); -/** - * Allocates a pooled memory array. - * - * The content of the allocated memory is set to zero. - * - * @param pool the memory pool - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @return a pointer to the allocated memory - * @see ucx_allocator_calloc() - */ -void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize); - -/** - * Reallocates pooled memory. - * - * If the memory to be reallocated is not contained by the specified pool, the - * behavior is undefined. - * - * @param pool the memory pool - * @param ptr a pointer to the memory that shall be reallocated - * @param n the new size of the memory - * @return a pointer to the new location of the memory - * @see ucx_allocator_realloc() - */ -void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n); - -/** - * Frees pooled memory. - * - * Before freeing the memory, the specified destructor function (if any) - * is called. - * - * If you specify memory, that is not pooled by the specified memory pool, the - * program will terminate with a call to <code>abort()</code>. - * - * @param pool the memory pool - * @param ptr a pointer to the memory that shall be freed - * @see ucx_mempool_set_destr() - */ -void ucx_mempool_free(UcxMempool *pool, void *ptr); - -/** - * Destroys a memory pool. - * - * For each element the destructor function (if any) is called and the element - * is freed. - * - * Each of the registered destructor function that has no corresponding element - * within the pool (namely those registered by ucx_mempool_reg_destr) is - * called interleaving with the element destruction, but with guarantee to the - * order in which they were registered (FIFO order). - * - * - * @param pool the mempool to destroy - */ -void ucx_mempool_destroy(UcxMempool *pool); - -/** - * Sets a destructor function for the specified memory. - * - * The destructor is automatically called when the memory is freed or the - * pool is destroyed. - * A destructor for pooled memory <b>MUST NOT</b> free the memory itself, - * as this is done by the pool. Use a destructor to free any resources - * managed by the pooled object. - * - * The only requirement for the specified memory is, that it <b>MUST</b> be - * pooled memory by a UcxMempool or an element-compatible mempool. The pointer - * to the destructor function is saved in a reserved area before the actual - * memory. - * - * @param ptr pooled memory - * @param func a pointer to the destructor function - * @see ucx_mempool_free() - * @see ucx_mempool_destroy() - */ -void ucx_mempool_set_destr(void *ptr, ucx_destructor func); - -/** - * Registers a destructor function for the specified (non-pooled) memory. - * - * This is useful, if you have memory that has not been allocated by a mempool, - * but shall be managed by a mempool. - * - * This function creates an entry in the specified mempool and the memory will - * therefore (logically) convert to pooled memory. - * <b>However, this does not cause the memory to be freed automatically!</b>. - * If you want to use this function, make the memory pool free non-pooled - * memory, the specified destructor function must call <code>free()</code> - * by itself. But keep in mind, that you then MUST NOT use this destructor - * function with pooled memory (e.g. in ucx_mempool_set_destr()), as it - * would cause a double-free. - * - * @param pool the memory pool - * @param ptr data the destructor is registered for - * @param destr a pointer to the destructor function - */ -void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_MEMPOOL_H */ -
--- a/ucx/ucx/properties.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * @file properties.h - * - * Load / store utilities for properties files. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_PROPERTIES_H -#define UCX_PROPERTIES_H - -#include "ucx.h" -#include "map.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UcxProperties object for parsing properties data. - * Most of the fields are for internal use only. You may configure the - * properties parser, e.g. by changing the used delimiter or specifying - * up to three different characters that shall introduce comments. - */ -typedef struct { - /** - * Input buffer (don't set manually). - * Automatically set by calls to ucx_properties_fill(). - */ - char *buffer; - - /** - * Length of the input buffer (don't set manually). - * Automatically set by calls to ucx_properties_fill(). - */ - size_t buflen; - - /** - * Current buffer position (don't set manually). - * Used by ucx_properties_next(). - */ - size_t pos; - - /** - * Internal temporary buffer (don't set manually). - * Used by ucx_properties_next(). - */ - char *tmp; - - /** - * Internal temporary buffer length (don't set manually). - * Used by ucx_properties_next(). - */ - size_t tmplen; - - /** - * Internal temporary buffer capacity (don't set manually). - * Used by ucx_properties_next(). - */ - size_t tmpcap; - - /** - * Parser error code. - * This is always 0 on success and a nonzero value on syntax errors. - * The value is set by ucx_properties_next(). - */ - int error; - - /** - * The delimiter that shall be used. - * This is '=' by default. - */ - char delimiter; - - /** - * The first comment character. - * This is '#' by default. - */ - char comment1; - - /** - * The second comment character. - * This is not set by default. - */ - char comment2; - - /** - * The third comment character. - * This is not set by default. - */ - char comment3; -} UcxProperties; - - -/** - * Constructs a new UcxProperties object. - * @return a pointer to the new UcxProperties object - */ -UcxProperties *ucx_properties_new(); - -/** - * Destroys a UcxProperties object. - * @param prop the UcxProperties object to destroy - */ -void ucx_properties_free(UcxProperties *prop); - -/** - * Sets the input buffer for the properties parser. - * - * After calling this function, you may parse the data by calling - * ucx_properties_next() until it returns 0. The function ucx_properties2map() - * is a convenience function that reads as much data as possible by using this - * function. - * - * - * @param prop the UcxProperties object - * @param buf a pointer to the new buffer - * @param len the payload length of the buffer - * @see ucx_properties_next() - * @see ucx_properties2map() - */ -void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); - -/** - * Retrieves the next key/value-pair. - * - * This function returns a nonzero value as long as there are key/value-pairs - * found. If no more key/value-pairs are found, you may refill the input buffer - * with ucx_properties_fill(). - * - * <b>Attention:</b> the sstr_t.ptr pointers of the output parameters point to - * memory within the input buffer of the parser and will get invalid some time. - * If you want long term copies of the key/value-pairs, use sstrdup() after - * calling this function. - * - * @param prop the UcxProperties object - * @param name a pointer to the sstr_t that shall contain the property name - * @param value a pointer to the sstr_t that shall contain the property value - * @return Nonzero, if a key/value-pair was successfully retrieved - * @see ucx_properties_fill() - */ -int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value); - -/** - * Retrieves all available key/value-pairs and puts them into a UcxMap. - * - * This is done by successive calls to ucx_properties_next() until no more - * key/value-pairs can be retrieved. - * - * The memory for the map values is allocated by the map's own allocator. - * - * @param prop the UcxProperties object - * @param map the target map - * @return The UcxProperties.error code (i.e. 0 on success). - * @see ucx_properties_fill() - * @see UcxMap.allocator - */ -int ucx_properties2map(UcxProperties *prop, UcxMap *map); - -/** - * Loads a properties file to a UcxMap. - * - * This is a convenience function that reads data from an input - * stream until the end of the stream is reached. - * - * @param map the map object to write the key/value-pairs to - * @param file the <code>FILE*</code> stream to read from - * @return 0 on success, or a non-zero value on error - * - * @see ucx_properties_fill() - * @see ucx_properties2map() - */ -int ucx_properties_load(UcxMap *map, FILE *file); - -/** - * Stores a UcxMap to a file. - * - * The key/value-pairs are written by using the following format: - * - * <code>[key] = [value]\\n</code> - * - * @param map the map to store - * @param file the <code>FILE*</code> stream to write to - * @return 0 on success, or a non-zero value on error - */ -int ucx_properties_store(UcxMap *map, FILE *file); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_PROPERTIES_H */ -
--- a/ucx/ucx/stack.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file stack.h - * - * Default stack memory allocation implementation. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_STACK_H -#define UCX_STACK_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * UCX stack structure. - */ -typedef struct { - /** UcxAllocator based on this stack */ - UcxAllocator allocator; - - /** Stack size. */ - size_t size; - - /** Pointer to the bottom of the stack */ - char *space; - - /** Pointer to the top of the stack */ - char *top; -} UcxStack; - -/** - * Metadata for each UCX stack element. - */ -struct ucx_stack_metadata { - /** - * Location of the previous element (<code>NULL</code> if this is the first) - */ - char *prev; - - /** Size of this element */ - size_t size; -}; - -/** - * Initializes UcxStack structure with memory. - * - * @param stack a pointer to an uninitialized stack structure - * @param space the memory area that shall be managed - * @param size size of the memory area - * @return a new UcxStack structure - */ -void ucx_stack_init(UcxStack *stack, char* space, size_t size); - -/** - * Allocates stack memory. - * - * @param stack a pointer to the stack - * @param n amount of memory to allocate - * @return a pointer to the allocated memory or <code>NULL</code> on stack - * overflow - * @see ucx_allocator_malloc() - */ -void *ucx_stack_malloc(UcxStack *stack, size_t n); - -/** - * Allocates memory with #ucx_stack_malloc() and copies the specified data if - * the allocation was successful. - * - * @param stack a pointer to the stack - * @param n amount of memory to allocate - * @param data a pointer to the data to copy - * @return a pointer to the allocated memory - * @see ucx_stack_malloc - */ -void *ucx_stack_push(UcxStack *stack, size_t n, const void *data); - -/** - * Allocates an array of stack memory - * - * The content of the allocated memory is set to zero. - * - * @param stack a pointer to the stack - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @return a pointer to the allocated memory - * @see ucx_allocator_calloc() - */ -void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize); - -/** - * Allocates memory with #ucx_stack_calloc() and copies the specified data if - * the allocation was successful. - * - * @param stack a pointer to the stack - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @param data a pointer to the data - * @return a pointer to the allocated memory - * @see ucx_stack_calloc - */ -void *ucx_stack_pusharr(UcxStack *stack, - size_t nelem, size_t elsize, const void *data); - -/** - * Reallocates memory on the stack. - * - * Shrinking memory is always safe. Extending memory can be very expensive. - * - * @param stack the stack - * @param ptr a pointer to the memory that shall be reallocated - * @param n the new size of the memory - * @return a pointer to the new location of the memory - * @see ucx_allocator_realloc() - */ -void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n); - -/** - * Frees memory on the stack. - * - * Freeing stack memory behaves in a special way. - * - * If the element, that should be freed, is the top most element of the stack, - * it is removed from the stack. Otherwise it is marked as freed. Marked - * elements are removed, when they become the top most elements of the stack. - * - * @param stack a pointer to the stack - * @param ptr a pointer to the memory that shall be freed - */ -void ucx_stack_free(UcxStack *stack, void *ptr); - - -/** - * Returns the size of the top most element. - * @param stack a pointer to the stack - * @return the size of the top most element - */ -#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\ - (stack)->top - 1)->size : 0) - -/** - * Removes the top most element from the stack and copies the content to <code> - * dest</code>, if specified. - * - * Use #ucx_stack_topsize()# to get the amount of memory that must be available - * at the location of <code>dest</code>. - * - * @param stack a pointer to the stack - * @param dest the location where the contents shall be written to, or <code> - * NULL</code>, if the element shall only be removed. - * @see ucx_stack_free - * @see ucx_stack_popn - */ -#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1) - -/** - * Removes the top most element from the stack and copies the content to <code> - * dest</code>. - * - * This function copies at most <code>n</code> bytes to the destination, but - * the element is always freed as a whole. - * If the element was larger than <code>n</code>, the remaining data is lost. - * - * @param stack a pointer to the stack - * @param dest the location where the contents shall be written to - * @param n copies at most n bytes to <code>dest</code> - * @see ucx_stack_pop - */ -void ucx_stack_popn(UcxStack *stack, void *dest, size_t n); - -/** - * Returns the remaining available memory on the specified stack. - * - * @param stack a pointer to the stack - * @return the remaining available memory - */ -size_t ucx_stack_avail(UcxStack *stack); - -/** - * Checks, if the stack is empty. - * - * @param stack a pointer to the stack - * @return nonzero, if the stack is empty, zero otherwise - */ -#define ucx_stack_empty(stack) (!(stack)->top) - -/** - * Computes a recommended size for the stack memory area. Note, that - * reallocations have not been taken into account, so you might need to reserve - * twice as much memory to allow many reallocations. - * - * @param size the approximate payload - * @param elems the approximate count of element allocations - * @return a recommended size for the stack space based on the information - * provided - */ -#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \ - (elems + 1)) - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_STACK_H */ -
--- a/ucx/ucx/string.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1201 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Bounded string implementation. - * - * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings. - * The main difference to C strings is, that <code>sstr_t</code> does <b>not - * need to be <code>NULL</code>-terminated</b>. Instead the length is stored - * within the structure. - * - * When using <code>sstr_t</code>, developers must be full aware of what type - * of string (<code>NULL</code>-terminated) or not) they are using, when - * accessing the <code>char* ptr</code> directly. - * - * The UCX string module provides some common string functions, known from - * standard libc, working with <code>sstr_t</code>. - * - * @file string.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_STRING_H -#define UCX_STRING_H - -#include "ucx.h" -#include "allocator.h" -#include <stddef.h> - -/* - * Use this macro to disable the shortcuts if you experience macro collision. - */ -#ifndef UCX_NO_SSTR_SHORTCUTS -/** - * Shortcut for a <code>sstr_t struct</code> - * or <code>scstr_t struct</code> literal. - */ -#define ST(s) { s, sizeof(s)-1 } - -/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */ -#define S(s) sstrn(s, sizeof(s)-1) - -/** Shortcut for the conversion of a C string to a <code>scstr_t</code>. */ -#define SC(s) scstrn(s, sizeof(s)-1) -#endif /* UCX_NO_SSTR_SHORTCUTS */ - -/* - * Use this macro to disable the format macros. - */ -#ifndef UCX_NO_SSTR_FORMAT_MACROS -/** Expands a sstr_t or scstr_t to printf arguments. */ -#define SFMT(s) (int) (s).length, (s).ptr - -/** Format specifier for a sstr_t or scstr_t. */ -#define PRIsstr ".*s" -#endif /* UCX_NO_SSTR_FORMAT_MACROS */ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The UCX string structure. - */ -typedef struct { - /** A pointer to the string - * (<b>not necessarily <code>NULL</code>-terminated</b>) */ - char *ptr; - /** The length of the string */ - size_t length; -} sstr_t; - -/** - * The UCX string structure for immutable (constant) strings. - */ -typedef struct { - /** A constant pointer to the immutable string - * (<b>not necessarily <code>NULL</code>-terminated</b>) */ - const char *ptr; - /** The length of the string */ - size_t length; -} scstr_t; - -#ifdef __cplusplus -} -#endif - - -#ifdef __cplusplus -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * - * <b>Do not use this function manually.</b> - * - * @param str some sstr_t - * @return an immutable (scstr_t) version of the provided string. - */ -inline scstr_t s2scstr(sstr_t s) { - scstr_t c; - c.ptr = s.ptr; - c.length = s.length; - return c; -} - -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * This variant is used, when the string is already immutable and no operation - * needs to be performed. - * - * <b>Do not use this function manually.</b> - * - * @param str some scstr_t - * @return the argument itself - */ -inline scstr_t s2scstr(scstr_t str) { - return str; -} - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return an immutable version of the provided string - */ -#define SCSTR(s) s2scstr(s) -#else - -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * This variant is used, when the string is already immutable and no operation - * needs to be performed. - * - * <b>Do not use this function manually.</b> - * - * @param str some scstr_t - * @return the argument itself - */ -scstr_t ucx_sc2sc(scstr_t str); - -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * - * <b>Do not use this function manually.</b> - * - * @param str some sstr_t - * @return an immutable (scstr_t) version of the provided string. - */ -scstr_t ucx_ss2sc(sstr_t str); - -#if __STDC_VERSION__ >= 201112L -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return an immutable version of the provided string - */ -#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str) - -#elif defined(__GNUC__) || defined(__clang__) - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return an immutable version of the provided string - */ -#define SCSTR(str) __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(str), sstr_t), \ - ucx_ss2sc, \ - ucx_sc2sc)(str) - -#elif defined(__sun) - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \ - scstr_t ucx_tmp_var_c; \ - ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\ - ucx_tmp_var_c.length = ucx_tmp_var_str.length;\ - ucx_tmp_var_c; }) -#else /* no generics and no builtins */ - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * - * This <b>internal</b> function (ab)uses the C standard an expects one single - * argument which is then implicitly converted to scstr_t without a warning. - * - * <b>Do not use this function manually.</b> - * - * @return the an immutable version of the provided string - */ -scstr_t ucx_ss2c_s(); - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) ucx_ss2c_s(str) -#endif /* C11 feature test */ - -#endif /* C++ */ - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * Creates a new sstr_t based on a C string. - * - * The length is implicitly inferred by using a call to <code>strlen()</code>. - * - * <b>Note:</b> the sstr_t will share the specified pointer to the C string. - * If you do want a copy, use sstrdup() on the return value of this function. - * - * If you need to wrap a constant string, use scstr(). - * - * @param cstring the C string to wrap - * @return a new sstr_t containing the C string - * - * @see sstrn() - */ -sstr_t sstr(char *cstring); - -/** - * Creates a new sstr_t of the specified length based on a C string. - * - * <b>Note:</b> the sstr_t will share the specified pointer to the C string. - * If you do want a copy, use sstrdup() on the return value of this function. - * - * If you need to wrap a constant string, use scstrn(). - * - * @param cstring the C string to wrap - * @param length the length of the string - * @return a new sstr_t containing the C string - * - * @see sstr() - * @see S() - */ -sstr_t sstrn(char *cstring, size_t length); - -/** - * Creates a new scstr_t based on a constant C string. - * - * The length is implicitly inferred by using a call to <code>strlen()</code>. - * - * <b>Note:</b> the scstr_t will share the specified pointer to the C string. - * If you do want a copy, use scstrdup() on the return value of this function. - * - * @param cstring the C string to wrap - * @return a new scstr_t containing the C string - * - * @see scstrn() - */ -scstr_t scstr(const char *cstring); - - -/** - * Creates a new scstr_t of the specified length based on a constant C string. - * - * <b>Note:</b> the scstr_t will share the specified pointer to the C string. - * If you do want a copy, use scstrdup() on the return value of this function. * - * - * @param cstring the C string to wrap - * @param length the length of the string - * @return a new scstr_t containing the C string - * - * @see scstr() - */ -scstr_t scstrn(const char *cstring, size_t length); - -/** - * Returns the accumulated length of all specified strings. - * - * <b>Attention:</b> if the count argument is larger than the count of the - * specified strings, the behavior is undefined. - * - * @param count the total number of specified strings - * @param ... all strings - * @return the accumulated length of all strings - */ -size_t scstrnlen(size_t count, ...); - -/** - * Returns the accumulated length of all specified strings. - * - * <b>Attention:</b> if the count argument is larger than the count of the - * specified strings, the behavior is undefined. - * - * @param count the total number of specified strings - * @param ... all strings - * @return the cumulated length of all strings - */ -#define sstrnlen(count, ...) scstrnlen(count, __VA_ARGS__) - -/** - * Concatenates two or more strings. - * - * The resulting string will be allocated by standard <code>malloc()</code>. - * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -sstr_t scstrcat(size_t count, scstr_t s1, ...); - -/** - * Concatenates two or more strings. - * - * The resulting string will be allocated by standard <code>malloc()</code>. - * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__) - -/** - * Concatenates two or more strings using a UcxAllocator. - * - * The resulting string must be freed by the allocators <code>free()</code> - * implementation. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param alloc the allocator to use - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - * - * @see scstrcat() - */ -sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...); - -/** - * Concatenates two or more strings using a UcxAllocator. - * - * The resulting string must be freed by the allocators <code>free()</code> - * implementation. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param alloc the allocator to use - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - * - * @see sstrcat() - */ -#define sstrcat_a(alloc, count, s1, ...) \ - scstrcat_a(alloc, count, SCSTR(s1), __VA_ARGS__) - -/** - * Returns a substring starting at the specified location. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use sstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * - * @see sstrsubsl() - * @see sstrchr() - */ -sstr_t sstrsubs(sstr_t string, size_t start); - -/** - * Returns a substring with the given length starting at the specified location. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use sstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @param length the maximum length of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * with a maximum length of <code>length</code> - * - * @see sstrsubs() - * @see sstrchr() - */ -sstr_t sstrsubsl(sstr_t string, size_t start, size_t length); - -/** - * Returns a substring of an immutable string starting at the specified - * location. - * - * <b>Attention:</b> the new string references the same memory area as the -* input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use scstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * - * @see scstrsubsl() - * @see scstrchr() - */ -scstr_t scstrsubs(scstr_t string, size_t start); - -/** - * Returns a substring of an immutable string with a maximum length starting - * at the specified location. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use scstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @param length the maximum length of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * with a maximum length of <code>length</code> - * - * @see scstrsubs() - * @see scstrchr() - */ -scstr_t scstrsubsl(scstr_t string, size_t start, size_t length); - -/** - * Returns a substring starting at the location of the first occurrence of the - * specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the first location of <code>chr</code> - * - * @see sstrsubs() - */ -sstr_t sstrchr(sstr_t string, int chr); - -/** - * Returns a substring starting at the location of the last occurrence of the - * specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the last location of <code>chr</code> - * - * @see sstrsubs() - */ -sstr_t sstrrchr(sstr_t string, int chr); - -/** - * Returns an immutable substring starting at the location of the first - * occurrence of the specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the first location of <code>chr</code> - * - * @see scstrsubs() - */ -scstr_t scstrchr(scstr_t string, int chr); - -/** - * Returns an immutable substring starting at the location of the last - * occurrence of the specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the last location of <code>chr</code> - * - * @see scstrsubs() - */ -scstr_t scstrrchr(scstr_t string, int chr); - -/** - * Returns a substring starting at the location of the first occurrence of the - * specified string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If <code>match</code> is an empty string, the complete <code>string</code> is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -sstr_t scstrsstr(sstr_t string, scstr_t match); - -/** - * Returns a substring starting at the location of the first occurrence of the - * specified string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If <code>match</code> is an empty string, the complete <code>string</code> is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -#define sstrstr(string, match) scstrsstr(string, SCSTR(match)) - -/** - * Returns an immutable substring starting at the location of the - * first occurrence of the specified immutable string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If <code>match</code> is an empty string, the complete <code>string</code> is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -scstr_t scstrscstr(scstr_t string, scstr_t match); - -/** - * Returns an immutable substring starting at the location of the - * first occurrence of the specified immutable string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If <code>match</code> is an empty string, the complete <code>string</code> is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -#define sstrscstr(string, match) scstrscstr(string, SCSTR(match)) - -/** - * Splits a string into parts by using a delimiter string. - * - * This function will return <code>NULL</code>, if one of the following happens: - * <ul> - * <li>the string length is zero</li> - * <li>the delimeter length is zero</li> - * <li>the string equals the delimeter</li> - * <li>memory allocation fails</li> - * </ul> - * - * The integer referenced by <code>count</code> is used as input and determines - * the maximum size of the resulting array, i.e. the maximum count of splits to - * perform + 1. - * - * The integer referenced by <code>count</code> is also used as output and is - * set to - * <ul> - * <li>-2, on memory allocation errors</li> - * <li>-1, if either the string or the delimiter is an empty string</li> - * <li>0, if the string equals the delimiter</li> - * <li>1, if the string does not contain the delimiter</li> - * <li>the count of array items, otherwise</li> - * </ul> - * - * If the string starts with the delimiter, the first item of the resulting - * array will be an empty string. - * - * If the string ends with the delimiter and the maximum list size is not - * exceeded, the last array item will be an empty string. - * In case the list size would be exceeded, the last array item will be the - * remaining string after the last split, <i>including</i> the terminating - * delimiter. - * - * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array - * items must be manually passed to <code>free()</code>. Use scstrsplit_a() with - * an allocator to managed memory, to avoid this. - * - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * <code>NULL</code> on error - * - * @see scstrsplit_a() - */ -sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count); - -/** - * Splits a string into parts by using a delimiter string. - * - * This function will return <code>NULL</code>, if one of the following happens: - * <ul> - * <li>the string length is zero</li> - * <li>the delimeter length is zero</li> - * <li>the string equals the delimeter</li> - * <li>memory allocation fails</li> - * </ul> - * - * The integer referenced by <code>count</code> is used as input and determines - * the maximum size of the resulting array, i.e. the maximum count of splits to - * perform + 1. - * - * The integer referenced by <code>count</code> is also used as output and is - * set to - * <ul> - * <li>-2, on memory allocation errors</li> - * <li>-1, if either the string or the delimiter is an empty string</li> - * <li>0, if the string equals the delimiter</li> - * <li>1, if the string does not contain the delimiter</li> - * <li>the count of array items, otherwise</li> - * </ul> - * - * If the string starts with the delimiter, the first item of the resulting - * array will be an empty string. - * - * If the string ends with the delimiter and the maximum list size is not - * exceeded, the last array item will be an empty string. - * In case the list size would be exceeded, the last array item will be the - * remaining string after the last split, <i>including</i> the terminating - * delimiter. - * - * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array - * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with - * an allocator to managed memory, to avoid this. - * - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * <code>NULL</code> on error - * - * @see sstrsplit_a() - */ -#define sstrsplit(string, delim, count) \ - scstrsplit(SCSTR(string), SCSTR(delim), count) - -/** - * Performing scstrsplit() using a UcxAllocator. - * - * <i>Read the description of scstrsplit() for details.</i> - * - * The memory for the sstr_t.ptr pointers of the array items and the memory for - * the sstr_t array itself are allocated by using the UcxAllocator.malloc() - * function. - * - * @param allocator the UcxAllocator used for allocating memory - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * <code>NULL</code> on error - * - * @see scstrsplit() - */ -sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim, - ssize_t *count); - -/** - * Performing sstrsplit() using a UcxAllocator. - * - * <i>Read the description of sstrsplit() for details.</i> - * - * The memory for the sstr_t.ptr pointers of the array items and the memory for - * the sstr_t array itself are allocated by using the UcxAllocator.malloc() - * function. - * - * @param allocator the UcxAllocator used for allocating memory - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * <code>NULL</code> on error - * - * @see sstrsplit() - */ -#define sstrsplit_a(allocator, string, delim, count) \ - scstrsplit_a(allocator, SCSTR(string), SCSTR(delim), count) - -/** - * Compares two UCX strings with standard <code>memcmp()</code>. - * - * At first it compares the scstr_t.length attribute of the two strings. The - * <code>memcmp()</code> function is called, if and only if the lengths match. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of - * <code>memcmp()</code> otherwise (i.e. 0 if the strings match) - */ -int scstrcmp(scstr_t s1, scstr_t s2); - -/** - * Compares two UCX strings with standard <code>memcmp()</code>. - * - * At first it compares the sstr_t.length attribute of the two strings. The - * <code>memcmp()</code> function is called, if and only if the lengths match. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of - * <code>memcmp()</code> otherwise (i.e. 0 if the strings match) - */ -#define sstrcmp(s1, s2) scstrcmp(SCSTR(s1), SCSTR(s2)) - -/** - * Compares two UCX strings ignoring the case. - * - * At first it compares the scstr_t.length attribute of the two strings. If and - * only if the lengths match, both strings are compared char by char ignoring - * the case. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of the platform - * specific string comparison function ignoring the case. - */ -int scstrcasecmp(scstr_t s1, scstr_t s2); - -/** - * Compares two UCX strings ignoring the case. - * - * At first it compares the sstr_t.length attribute of the two strings. If and - * only if the lengths match, both strings are compared char by char ignoring - * the case. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of the platform - * specific string comparison function ignoring the case. - */ -#define sstrcasecmp(s1, s2) scstrcasecmp(SCSTR(s1), SCSTR(s2)) - -/** - * Creates a duplicate of the specified string. - * - * The new sstr_t will contain a copy allocated by standard - * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to - * <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated and mutable, regardless of the argument. - * - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup_a() - */ -sstr_t scstrdup(scstr_t string); - -/** - * Creates a duplicate of the specified string. - * - * The new sstr_t will contain a copy allocated by standard - * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to - * <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated, regardless of the argument. - * - * @param string the string to duplicate - * @return a duplicate of the string - * @see sstrdup_a() - */ -#define sstrdup(string) scstrdup(SCSTR(string)) - -/** - * Creates a duplicate of the specified string using a UcxAllocator. - * - * The new sstr_t will contain a copy allocated by the allocators - * UcxAllocator.malloc() function. So it is implementation depended, whether the - * returned sstr_t.ptr pointer must be passed to the allocators - * UcxAllocator.free() function manually. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated and mutable, regardless of the argument. - * - * @param allocator a valid instance of a UcxAllocator - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup() - */ -sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string); - -/** - * Creates a duplicate of the specified string using a UcxAllocator. - * - * The new sstr_t will contain a copy allocated by the allocators - * UcxAllocator.malloc() function. So it is implementation depended, whether the - * returned sstr_t.ptr pointer must be passed to the allocators - * UcxAllocator.free() function manually. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated, regardless of the argument. - * - * @param allocator a valid instance of a UcxAllocator - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup() - */ -#define sstrdup_a(allocator, string) scstrdup_a(allocator, SCSTR(string)) - - -/** - * Omits leading and trailing spaces. - * - * This function returns a new sstr_t containing a trimmed version of the - * specified string. - * - * <b>Note:</b> the new sstr_t references the same memory, thus you - * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to - * <code>free()</code>. It is also highly recommended to avoid assignments like - * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the - * source string. Assignments of this type are only permitted, if the - * sstr_t.ptr of the source string does not need to be freed or if another - * reference to the source string exists. - * - * @param string the string that shall be trimmed - * @return a new sstr_t containing the trimmed string - */ -sstr_t sstrtrim(sstr_t string); - -/** - * Omits leading and trailing spaces. - * - * This function returns a new scstr_t containing a trimmed version of the - * specified string. - * - * <b>Note:</b> the new scstr_t references the same memory, thus you - * <b>MUST NOT</b> pass the scstr_t.ptr of the return value to - * <code>free()</code>. It is also highly recommended to avoid assignments like - * <code>mystr = scstrtrim(mystr);</code> as you lose the reference to the - * source string. Assignments of this type are only permitted, if the - * scstr_t.ptr of the source string does not need to be freed or if another - * reference to the source string exists. - * - * @param string the string that shall be trimmed - * @return a new scstr_t containing the trimmed string - */ -scstr_t scstrtrim(scstr_t string); - -/** - * Checks, if a string has a specific prefix. - * - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -int scstrprefix(scstr_t string, scstr_t prefix); - -/** - * Checks, if a string has a specific prefix. - * - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -#define sstrprefix(string, prefix) scstrprefix(SCSTR(string), SCSTR(prefix)) - -/** - * Checks, if a string has a specific suffix. - * - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -int scstrsuffix(scstr_t string, scstr_t suffix); - -/** - * Checks, if a string has a specific suffix. - * - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(suffix)) - -/** - * Checks, if a string has a specific prefix, ignoring the case. - * - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -int scstrcaseprefix(scstr_t string, scstr_t prefix); - -/** - * Checks, if a string has a specific prefix, ignoring the case. - * - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -#define sstrcaseprefix(string, prefix) \ - scstrcaseprefix(SCSTR(string), SCSTR(prefix)) - -/** - * Checks, if a string has a specific suffix, ignoring the case. - * - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -int scstrcasesuffix(scstr_t string, scstr_t suffix); - -/** - * Checks, if a string has a specific suffix, ignoring the case. - * - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -#define sstrcasesuffix(string, suffix) \ - scstrcasesuffix(SCSTR(string), SCSTR(suffix)) - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see scstrdup()). - * - * @param string the input string - * @return the resulting lower case string - * @see scstrdup() - */ -sstr_t scstrlower(scstr_t string); - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup()). - * - * @param string the input string - * @return the resulting lower case string - */ -#define sstrlower(string) scstrlower(SCSTR(string)) - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see scstrdup_a()). - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting lower case string - * @see scstrdup_a() - */ -sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string); - - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup_a()). - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting lower case string - */ -#define sstrlower_a(allocator, string) scstrlower_a(allocator, SCSTR(string)) - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see scstrdup()). - * - * @param string the input string - * @return the resulting upper case string - * @see scstrdup() - */ -sstr_t scstrupper(scstr_t string); - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup()). - * - * @param string the input string - * @return the resulting upper case string - */ -#define sstrupper(string) scstrupper(SCSTR(string)) - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see scstrdup_a()). - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting upper case string - * @see scstrdup_a() - */ -sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string); - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup_a()). - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting upper case string - */ -#define sstrupper_a(allocator, string) scstrupper_a(allocator, string) - - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @param allocator the allocator to use - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @param replmax maximum number of replacements - * @return the resulting string after applying the replacements - */ -sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str, - scstr_t pattern, scstr_t replacement, size_t replmax); - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @param replmax maximum number of replacements - * @return the resulting string after applying the replacements - */ -sstr_t scstrreplacen(scstr_t str, scstr_t pattern, - scstr_t replacement, size_t replmax); - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * @param allocator the allocator to use - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @param replmax maximum number of replacements - * @return the resulting string after applying the replacements - */ -#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \ - scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \ - SCSTR(replacement), replmax) - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @param replmax maximum number of replacements - * @return the resulting string after applying the replacements - */ -#define sstrreplacen(str, pattern, replacement, replmax) \ - scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax) - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @param allocator the allocator to use - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @return the resulting string after applying the replacements - */ -#define sstrreplace_a(allocator, str, pattern, replacement) \ - scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \ - SCSTR(replacement), SIZE_MAX) - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @return the resulting string after applying the replacements - */ -#define sstrreplace(str, pattern, replacement) \ - scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_STRING_H */
--- a/ucx/ucx/test.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,241 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file: test.h - * - * UCX Test Framework. - * - * Usage of this test framework: - * - * **** IN HEADER FILE: **** - * - * <pre> - * UCX_TEST(function_name); - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional - * </pre> - * - * **** IN SOURCE FILE: **** - * <pre> - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { - * // tests with UCX_TEST_ASSERT() - * } - * - * UCX_TEST(function_name) { - * // memory allocation and other stuff here - * #UCX_TEST_BEGIN - * // tests with UCX_TEST_ASSERT() and/or - * // calls with UCX_TEST_CALL_SUBROUTINE() here - * #UCX_TEST_END - * // cleanup of memory here - * } - * </pre> - * - * <b>Note:</b> if a test fails, a longjump is performed - * back to the #UCX_TEST_BEGIN macro! - * - * <b>Attention:</b> Do not call own functions within a test, that use - * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE(). - * - * - * @author Mike Becker - * @author Olaf Wintermann - * - */ - -#ifndef UCX_TEST_H -#define UCX_TEST_H - -#include "ucx.h" -#include <stdio.h> -#include <string.h> -#include <setjmp.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __FUNCTION__ - -/** - * Alias for the <code>__func__</code> preprocessor macro. - * Some compilers use <code>__func__</code> and others use __FUNCTION__. - * We use __FUNCTION__ so we define it for those compilers which use - * <code>__func__</code>. - */ -#define __FUNCTION__ __func__ -#endif - -/** Type for the UcxTestSuite. */ -typedef struct UcxTestSuite UcxTestSuite; - -/** Pointer to a test function. */ -typedef void(*UcxTest)(UcxTestSuite*,FILE*); - -/** Type for the internal list of test cases. */ -typedef struct UcxTestList UcxTestList; - -/** Structure for the internal list of test cases. */ -struct UcxTestList { - - /** Test case. */ - UcxTest test; - - /** Pointer to the next list element. */ - UcxTestList *next; -}; - -/** - * A test suite containing multiple test cases. - */ -struct UcxTestSuite { - - /** The number of successful tests after the suite has been run. */ - unsigned int success; - - /** The number of failed tests after the suite has been run. */ - unsigned int failure; - - /** - * Internal list of test cases. - * Use ucx_test_register() to add tests to this list. - */ - UcxTestList *tests; -}; - -/** - * Creates a new test suite. - * @return a new test suite - */ -UcxTestSuite* ucx_test_suite_new(); - -/** - * Destroys a test suite. - * @param suite the test suite to destroy - */ -void ucx_test_suite_free(UcxTestSuite* suite); - -/** - * Registers a test function with the specified test suite. - * - * @param suite the suite, the test function shall be added to - * @param test the test function to register - * @return <code>EXIT_SUCCESS</code> on success or - * <code>EXIT_FAILURE</code> on failure - */ -int ucx_test_register(UcxTestSuite* suite, UcxTest test); - -/** - * Runs a test suite and writes the test log to the specified stream. - * @param suite the test suite to run - * @param outstream the stream the log shall be written to - */ -void ucx_test_run(UcxTestSuite* suite, FILE* outstream); - -/** - * Macro for a #UcxTest function header. - * - * Use this macro to declare and/or define a #UcxTest function. - * - * @param name the name of the test function - */ -#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_) - -/** - * Marks the begin of a test. - * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b> - * #UCX_TEST_BEGIN. - * - * @see #UCX_TEST_END - */ -#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\ - fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ - fwrite("... ", 1, 4, _output_);\ - jmp_buf _env_; \ - if (!setjmp(_env_)) { - -/** - * Checks a test assertion. - * If the assertion is correct, the test carries on. If the assertion is not - * correct, the specified message (terminated by a dot and a line break) is - * written to the test suites output stream. - * @param condition the condition to check - * @param message the message that shall be printed out on failure - */ -#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \ - fwrite(message".\n", 1, 2+strlen(message), _output_); \ - _suite_->failure++; \ - longjmp(_env_, 1);\ - } - -/** - * Macro for a test subroutine function header. - * - * Use this to declare and/or define a subroutine that can be called by using - * UCX_TEST_CALL_SUBROUTINE(). - * - * @param name the name of the subroutine - * @param ... the parameter list - * - * @see UCX_TEST_CALL_SUBROUTINE() - */ -#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\ - FILE *_output_, jmp_buf _env_, __VA_ARGS__) - -/** - * Macro for calling a test subroutine. - * - * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this - * macro. - * - * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN- - * #UCX_TEST_END-block. - * - * @param name the name of the subroutine - * @param ... the argument list - * - * @see UCX_TEST_SUBROUTINE() - */ -#define UCX_TEST_CALL_SUBROUTINE(name,...) \ - name(_suite_,_output_,_env_,__VA_ARGS__); - -/** - * Marks the end of a test. - * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b> - * #UCX_TEST_END. - * - * @see #UCX_TEST_BEGIN - */ -#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;} - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_TEST_H */ -
--- a/ucx/ucx/ucx.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Main UCX Header providing most common definitions. - * - * @file ucx.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_H -#define UCX_H - -/** Major UCX version as integer constant. */ -#define UCX_VERSION_MAJOR 2 - -/** Minor UCX version as integer constant. */ -#define UCX_VERSION_MINOR 1 - -/** Version constant which ensures to increase monotonically. */ -#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) - -#include <stdlib.h> -#include <stdint.h> - -#ifdef _WIN32 -#if !(defined __ssize_t_defined || defined _SSIZE_T_) -#include <BaseTsd.h> -typedef SSIZE_T ssize_t; -#define __ssize_t_defined -#define _SSIZE_T_ -#endif /* __ssize_t_defined and _SSIZE_T */ -#else /* !_WIN32 */ -#include <sys/types.h> -#endif /* _WIN32 */ - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * A function pointer to a destructor function. - * @see ucx_mempool_setdestr() - * @see ucx_mempool_regdestr() - */ -typedef void(*ucx_destructor)(void*); - -/** - * Function pointer to a compare function. - * - * The compare function shall take three arguments: the two values that shall be - * compared and optional additional data. - * The function shall then return -1 if the first argument is less than the - * second argument, 1 if the first argument is greater than the second argument - * and 0 if both arguments are equal. If the third argument is - * <code>NULL</code>, it shall be ignored. - */ -typedef int(*cmp_func)(const void*,const void*,void*); - -/** - * Function pointer to a distance function. - * - * The distance function shall take three arguments: the two values for which - * the distance shall be computed and optional additional data. - * The function shall then return the signed distance as integer value. - */ -typedef intmax_t(*distance_func)(const void*,const void*,void*); - -/** - * Function pointer to a copy function. - * - * The copy function shall create a copy of the first argument and may use - * additional data provided by the second argument. If the second argument is - * <code>NULL</code>, it shall be ignored. - - * <b>Attention:</b> if pointers returned by functions of this type may be - * passed to <code>free()</code> depends on the implementation of the - * respective <code>copy_func</code>. - */ -typedef void*(*copy_func)(const void*,void*); - -/** - * Function pointer to a write function. - * - * The signature of the write function shall be compatible to the signature - * of standard <code>fwrite</code>, though it may use arbitrary data types for - * source and destination. - * - * The arguments shall contain (in ascending order): a pointer to the source, - * the length of one element, the element count and a pointer to the - * destination. - */ -typedef size_t(*write_func)(const void*, size_t, size_t, void*); - -/** - * Function pointer to a read function. - * - * The signature of the read function shall be compatible to the signature - * of standard <code>fread</code>, though it may use arbitrary data types for - * source and destination. - * - * The arguments shall contain (in ascending order): a pointer to the - * destination, the length of one element, the element count and a pointer to - * the source. - */ -typedef size_t(*read_func)(void*, size_t, size_t, void*); - - - -#if __GNUC__ >= 5 || defined(__clang__) -#define UCX_MUL_BUILTIN - -#if __WORDSIZE == 32 -/** - * Alias for <code>__builtin_umul_overflow</code>. - * - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result) -#else /* __WORDSIZE != 32 */ -/** - * Alias for <code>__builtin_umull_overflow</code>. - * - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result) -#endif /* __WORDSIZE */ - -#else /* no GNUC or clang bultin */ - -/** - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result) - -/** - * Performs a multiplication of size_t values and checks for overflow. - * - * This is a custom implementation in case there is no compiler builtin - * available. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t where the result should be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -int ucx_szmul_impl(size_t a, size_t b, size_t *result); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_H */ -
--- a/ucx/ucx/utils.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,508 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file utils.h - * - * Compare, copy and printf functions. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_UTILS_H -#define UCX_UTILS_H - -#include "ucx.h" -#include "string.h" -#include "allocator.h" -#include <inttypes.h> -#include <string.h> -#include <stdarg.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy(). - */ -#define UCX_STREAM_COPY_BUFSIZE 4096 - -/** - * Copies a string. - * @param s the string to copy - * @param data omitted - * @return a pointer to a copy of s1 that can be passed to free(void*) - */ -void *ucx_strcpy(const void *s, void *data); - -/** - * Copies a memory area. - * @param m a pointer to the memory area - * @param n a pointer to the size_t containing the size of the memory area - * @return a pointer to a copy of the specified memory area that can - * be passed to free(void*) - */ -void *ucx_memcpy(const void *m, void *n); - - -/** - * Reads data from a stream and writes it to another stream. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if <code>NULL</code> was - * provided for <code>buf</code>, this is the size of the buffer that shall be - * implicitly created - * @param n the maximum number of bytes that shall be copied - * @return the total number of bytes copied - */ -size_t ucx_stream_bncopy(void *src, void *dest, read_func rfnc, write_func wfnc, - char* buf, size_t bufsize, size_t n); - -/** - * Shorthand for an unbounded ucx_stream_bncopy call using a default buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @return total number of bytes copied - * - * @see #UCX_STREAM_COPY_BUFSIZE - */ -#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1) - -/** - * Shorthand for ucx_stream_bncopy using a default copy buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param n maximum number of bytes that shall be copied - * @return total number of bytes copied - */ -#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - NULL, UCX_STREAM_COPY_BUFSIZE, n) - -/** - * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if <code>NULL</code> was - * provided for <code>buf</code>, this is the size of the buffer that shall be - * implicitly created - * @return total number of bytes copied - */ -#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - buf, bufsize, (size_t)-1) - -/** - * Wraps the strcmp function. - * @param s1 string one - * @param s2 string two - * @param data omitted - * @return the result of strcmp(s1, s2) - */ -int ucx_cmp_str(const void *s1, const void *s2, void *data); - -/** - * Wraps the strncmp function. - * @param s1 string one - * @param s2 string two - * @param n a pointer to the size_t containing the third strncmp parameter - * @return the result of strncmp(s1, s2, *n) - */ -int ucx_cmp_strn(const void *s1, const void *s2, void *n); - -/** - * Wraps the sstrcmp function. - * @param s1 sstr one - * @param s2 sstr two - * @param data ignored - * @return the result of sstrcmp(s1, s2) - */ -int ucx_cmp_sstr(const void *s1, const void *s2, void *data); - -/** - * Compares two integers of type int. - * @param i1 pointer to integer one - * @param i2 pointer to integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_int(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type long int. - * @param i1 pointer to long integer one - * @param i2 pointer to long integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_longint(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type long long. - * @param i1 pointer to long long one - * @param i2 pointer to long long two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_longlong(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int16_t. - * @param i1 pointer to int16_t one - * @param i2 pointer to int16_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_int16(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int32_t. - * @param i1 pointer to int32_t one - * @param i2 pointer to int32_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_int32(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int64_t. - * @param i1 pointer to int64_t one - * @param i2 pointer to int64_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_int64(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned int. - * @param i1 pointer to unsigned integer one - * @param i2 pointer to unsigned integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_uint(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned long int. - * @param i1 pointer to unsigned long integer one - * @param i2 pointer to unsigned long integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_ulongint(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned long long. - * @param i1 pointer to unsigned long long one - * @param i2 pointer to unsigned long long two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint16_t. - * @param i1 pointer to uint16_t one - * @param i2 pointer to uint16_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_uint16(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint32_t. - * @param i1 pointer to uint32_t one - * @param i2 pointer to uint32_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_uint32(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint64_t. - * @param i1 pointer to uint64_t one - * @param i2 pointer to uint64_t two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_uint64(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int. - * @param i1 pointer to integer one - * @param i2 pointer to integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type long int. - * @param i1 pointer to long integer one - * @param i2 pointer to long integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type long long. - * @param i1 pointer to long long one - * @param i2 pointer to long long two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int16_t. - * @param i1 pointer to int16_t one - * @param i2 pointer to int16_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int32_t. - * @param i1 pointer to int32_t one - * @param i2 pointer to int32_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int64_t. - * @param i1 pointer to int64_t one - * @param i2 pointer to int64_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned int. - * @param i1 pointer to unsigned integer one - * @param i2 pointer to unsigned integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned long int. - * @param i1 pointer to unsigned long integer one - * @param i2 pointer to unsigned long integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned long long. - * @param i1 pointer to unsigned long long one - * @param i2 pointer to unsigned long long two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint16_t. - * @param i1 pointer to uint16_t one - * @param i2 pointer to uint16_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint32_t. - * @param i1 pointer to uint32_t one - * @param i2 pointer to uint32_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint64_t. - * @param i1 pointer to uint64_t one - * @param i2 pointer to uint64_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data); - -/** - * Compares two real numbers of type float. - * @param f1 pointer to float one - * @param f2 pointer to float two - * @param data if provided: a pointer to precision (default: 1e-6f) - * @return -1, if *f1 is less than *f2, 0 if both are equal, - * 1 if *f1 is greater than *f2 - */ - -int ucx_cmp_float(const void *f1, const void *f2, void *data); - -/** - * Compares two real numbers of type double. - * @param d1 pointer to double one - * @param d2 pointer to double two - * @param data if provided: a pointer to precision (default: 1e-14) - * @return -1, if *d1 is less than *d2, 0 if both are equal, - * 1 if *d1 is greater than *d2 - */ -int ucx_cmp_double(const void *d1, const void *d2, void *data); - -/** - * Compares two pointers. - * @param ptr1 pointer one - * @param ptr2 pointer two - * @param data omitted - * @return -1 if ptr1 is less than ptr2, 0 if both are equal, - * 1 if ptr1 is greater than ptr2 - */ -int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data); - -/** - * Compares two memory areas. - * @param ptr1 pointer one - * @param ptr2 pointer two - * @param n a pointer to the size_t containing the third parameter for memcmp - * @return the result of memcmp(ptr1, ptr2, *n) - */ -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n); - -/** - * A <code>printf()</code> like function which writes the output to a stream by - * using a write_func(). - * @param stream the stream the data is written to - * @param wfc the write function - * @param fmt format string - * @param ... additional arguments - * @return the total number of bytes written - */ -int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...); - -/** - * <code>va_list</code> version of ucx_fprintf(). - * @param stream the stream the data is written to - * @param wfc the write function - * @param fmt format string - * @param ap argument list - * @return the total number of bytes written - * @see ucx_fprintf() - */ -int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap); - -/** - * A <code>printf()</code> like function which allocates space for a sstr_t - * the result is written to. - * - * <b>Attention</b>: The sstr_t data is allocated with the allocators - * ucx_allocator_malloc() function. So it is implementation dependent, if - * the returned sstr_t.ptr pointer must be passed to the allocators - * ucx_allocator_free() function manually. - * - * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be - * <code>NULL</code>-terminated. - * - * @param allocator the UcxAllocator used for allocating the result sstr_t - * @param fmt format string - * @param ... additional arguments - * @return a sstr_t containing the formatted string - */ -sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...); - -/** - * <code>va_list</code> version of ucx_asprintf(). - * - * @param allocator the UcxAllocator used for allocating the result sstr_t - * @param fmt format string - * @param ap argument list - * @return a sstr_t containing the formatted string - * @see ucx_asprintf() - */ -sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap); - -/** Shortcut for ucx_asprintf() with default allocator. */ -#define ucx_sprintf(...) \ - ucx_asprintf(ucx_default_allocator(), __VA_ARGS__) - -/** - * A <code>printf()</code> like function which writes the output to a - * UcxBuffer. - * - * @param buffer the buffer the data is written to - * @param ... format string and additional arguments - * @return the total number of bytes written - * @see ucx_fprintf() - */ -#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \ - (write_func)ucx_buffer_write, __VA_ARGS__) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_UTILS_H */ -
--- a/ucx/utils.c Sun May 23 09:44:43 2021 +0200 +++ b/ucx/utils.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,423 +26,74 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/utils.h" +#include "cx/utils.h" -#include <math.h> -#include <stdio.h> -#include <limits.h> -#include <errno.h> +#ifndef CX_STREAM_BCOPY_BUF_SIZE +#define CX_STREAM_BCOPY_BUF_SIZE 8192 +#endif + +#ifndef CX_STREAM_COPY_BUF_SIZE +#define CX_STREAM_COPY_BUF_SIZE 1024 +#endif -/* COPY FUCNTIONS */ -void* ucx_strcpy(const void* s, void* data) { - const char *str = (const char*) s; - size_t n = 1+strlen(str); - char *cpy = (char*) malloc(n); - memcpy(cpy, str, n); - return cpy; -} - -void* ucx_memcpy(const void* m, void* n) { - size_t k = *((size_t*)n); - void *cpy = malloc(k); - memcpy(cpy, m, k); - return cpy; -} - -size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc, - write_func writefnc, char* buf, size_t bufsize, size_t n) { - if(n == 0 || bufsize == 0) { +size_t cx_stream_bncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + char *buf, + size_t bufsize, + size_t n +) { + if (n == 0) { return 0; } - - char *lbuf; + + char *lbuf; size_t ncp = 0; - - if(buf) { + + if (buf) { + if (bufsize == 0) return 0; lbuf = buf; } else { - lbuf = (char*)malloc(bufsize); - if(lbuf == NULL) { + if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE; + lbuf = malloc(bufsize); + if (lbuf == NULL) { return 0; } } - + size_t r; size_t rn = bufsize > n ? n : bufsize; - while((r = readfnc(lbuf, 1, rn, src)) != 0) { - r = writefnc(lbuf, 1, r, dest); + while ((r = rfnc(lbuf, 1, rn, src)) != 0) { + r = wfnc(lbuf, 1, r, dest); ncp += r; n -= r; rn = bufsize > n ? n : bufsize; - if(r == 0 || n == 0) { + if (r == 0 || n == 0) { break; } } - + if (lbuf != buf) { free(lbuf); } - + return ncp; } -/* COMPARE FUNCTIONS */ - -int ucx_cmp_str(const void *s1, const void *s2, void *data) { - return strcmp((const char*)s1, (const char*)s2); -} - -int ucx_cmp_strn(const void *s1, const void *s2, void *n) { - return strncmp((const char*)s1, (const char*)s2, *((size_t*) n)); -} - -int ucx_cmp_sstr(const void *s1, const void *s2, void *data) { - sstr_t a = *(const sstr_t*) s1; - sstr_t b = *(const sstr_t*) s2; - return sstrcmp(a, b); -} - -int ucx_cmp_int(const void *i1, const void *i2, void *data) { - int a = *((const int*) i1); - int b = *((const int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_longint(const void *i1, const void *i2, void *data) { - long int a = *((const long int*) i1); - long int b = *((const long int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_longlong(const void *i1, const void *i2, void *data) { - long long a = *((const long long*) i1); - long long b = *((const long long*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int16(const void *i1, const void *i2, void *data) { - int16_t a = *((const int16_t*) i1); - int16_t b = *((const int16_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int32(const void *i1, const void *i2, void *data) { - int32_t a = *((const int32_t*) i1); - int32_t b = *((const int32_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_int64(const void *i1, const void *i2, void *data) { - int64_t a = *((const int64_t*) i1); - int64_t b = *((const int64_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint(const void *i1, const void *i2, void *data) { - unsigned int a = *((const unsigned int*) i1); - unsigned int b = *((const unsigned int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ulongint(const void *i1, const void *i2, void *data) { - unsigned long int a = *((const unsigned long int*) i1); - unsigned long int b = *((const unsigned long int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) { - unsigned long long a = *((const unsigned long long*) i1); - unsigned long long b = *((const unsigned long long*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint16(const void *i1, const void *i2, void *data) { - uint16_t a = *((const uint16_t*) i1); - uint16_t b = *((const uint16_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint32(const void *i1, const void *i2, void *data) { - uint32_t a = *((const uint32_t*) i1); - uint32_t b = *((const uint32_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_uint64(const void *i1, const void *i2, void *data) { - uint64_t a = *((const uint64_t*) i1); - uint64_t b = *((const uint64_t*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int*) i1); - intmax_t b = *((const int*) i2); - return a - b; -} - -intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long int*) i1); - intmax_t b = *((const long int*) i2); - return a - b; -} - -intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long long*) i1); - intmax_t b = *((const long long*) i2); - return a - b; -} - -intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int16_t*) i1); - intmax_t b = *((const int16_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int32_t*) i1); - intmax_t b = *((const int32_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int64_t*) i1); - intmax_t b = *((const int64_t*) i2); - return a - b; -} - -intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned int*) i1); - uintmax_t b = *((const unsigned int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); +size_t cx_stream_ncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + size_t n +) { + char buf[CX_STREAM_COPY_BUF_SIZE]; + return cx_stream_bncopy(src, dest, rfnc, wfnc, + buf, CX_STREAM_COPY_BUF_SIZE, n); } -intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long int*) i1); - uintmax_t b = *((const unsigned long int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long long*) i1); - uintmax_t b = *((const unsigned long long*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint16_t*) i1); - uintmax_t b = *((const uint16_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint32_t*) i1); - uintmax_t b = *((const uint32_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint64_t*) i1); - uintmax_t b = *((const uint64_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) { - float a = *((const float*) f1); - float b = *((const float*) f2); - float e = !epsilon ? 1e-6f : *((float*)epsilon); - if (fabsf(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) { - double a = *((const double*) d1); - double b = *((const double*) d2); - double e = !epsilon ? 1e-14 : *((double*)epsilon); - if (fabs(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) { - const intptr_t p1 = (const intptr_t) ptr1; - const intptr_t p2 = (const intptr_t) ptr2; - if (p1 == p2) { - return 0; - } else { - return p1 < p2 ? -1 : 1; - } -} - -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) { - return memcmp(ptr1, ptr2, *((size_t*)n)); -} - -/* PRINTF FUNCTIONS */ - -#ifdef va_copy -#define UCX_PRINTF_BUFSIZE 256 -#else -#pragma message("WARNING: C99 va_copy macro not supported by this platform" \ - " - limiting ucx_*printf to 2 KiB") -#define UCX_PRINTF_BUFSIZE 0x800 +#ifndef CX_SZMUL_BUILTIN +#include "szmul.c" #endif - -int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) { - int ret; - va_list ap; - va_start(ap, fmt); - ret = ucx_vfprintf(stream, wfc, fmt, ap); - va_end(ap); - return ret; -} - -int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) { - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - if (ret == INT_MAX) { - errno = ENOMEM; - return -1; - } - - int len = ret + 1; - char *newbuf = (char*)malloc(len); - if (!newbuf) { - return -1; - } - - ret = vsnprintf(newbuf, len, fmt, ap2); - if (ret > 0) { - ret = (int)wfc(newbuf, 1, ret, stream); - } - free(newbuf); - } - return ret; -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - errno = ENOMEM; - return -1; - } -#endif -} - -sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) { - va_list ap; - sstr_t ret; - va_start(ap, fmt); - ret = ucx_vasprintf(allocator, fmt, ap); - va_end(ap); - return ret; -} - -sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) { - sstr_t s; - s.ptr = NULL; - s.length = 0; - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else if (ret == INT_MAX) { - errno = ENOMEM; - } else { - int len = ret + 1; - s.ptr = (char*)almalloc(a, len); - if (s.ptr) { - ret = vsnprintf(s.ptr, len, fmt, ap2); - if (ret < 0) { - free(s.ptr); - s.ptr = NULL; - } else { - s.length = (size_t)ret; - } - } - } -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else { - errno = ENOMEM; - } -#endif - return s; -}
--- a/ui/Makefile Sun May 23 09:44:43 2021 +0200 +++ b/ui/Makefile Sat Jan 04 16:38:48 2025 +0100 @@ -33,7 +33,7 @@ include common/objs.mk -UI_LIB = ../build/lib/libuitk.$(LIB_EXT) +UI_LIB = ../build/lib/libuitk$(LIB_EXT) include $(TOOLKIT)/objs.mk OBJ = $(TOOLKITOBJS) $(COMMONOBJS) @@ -43,5 +43,5 @@ include $(TOOLKIT)/Makefile $(COMMON_OBJPRE)%.o: common/%.c - $(CC) -o $@ -c -I../ucx $(CFLAGS) $(TK_CFLAGS) $< + $(CC) -o $@ -c -I../ucx/ $(CFLAGS) $(TK_CFLAGS) $<
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/EventData.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,43 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "../ui/toolkit.h" +#import "../common/context.h" + +@interface EventData : NSObject +@property UiObject *obj; +@property ui_callback callback; +@property void *userdata; +@property void *data; +@property int value; + +- (EventData*)init:(ui_callback)cb userdata:(void*)userdata; + +- (void)handleEvent:(id)sender; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/EventData.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "EventData.h" + +@implementation EventData + +- (EventData*)init:(ui_callback)cb userdata:(void*)userdata { + _callback = cb; + _userdata = userdata; + return self; +} + +- (void)handleEvent:(id)sender { + if(self.callback) { + UiEvent event; + event.obj = self.obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = self.data; + event.intval = self.value; + self.callback(&event, self.userdata); + } +} + + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/GridLayout.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,71 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" + +#import "Container.h" + +#import <cx/array_list.h> + +typedef struct GridElm { + NSView *view; + int margin; + int x; + int y; + int colspan; + int rowspan; + BOOL hexpand; + BOOL vexpand; + BOOL hfill; + BOOL vfill; +} GridElm; + +typedef struct GridDef { + int size; + int pos; + int preferred_size; + BOOL extend; +} GridDef; + +@interface GridLayout : NSView<Container> + +@property int columnspacing; +@property int rowspacing; +@property CxList *children; +@property NSSize preferredSize; + +@property NSButton *test; + +@property int x; +@property int y; +@property int cols; +@property int rows; + +- (GridLayout*)init; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/GridLayout.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,214 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "GridLayout.h" + + + +@implementation GridLayout + +@synthesize label=_label; +@synthesize uilayout=_uilayout; +@synthesize newline=_newline; + +- (GridLayout*)init { + self = [super init]; + _columnspacing = 0; + _rowspacing = 0; + _children = cxArrayListCreateSimple(sizeof(GridElm), 32); + + return self; +} + +/* +- (void) layout { + [super layout]; + + NSRect r1 = _test.frame; + NSSize s1 = _test.intrinsicContentSize; + NSEdgeInsets e1 = _test.alignmentRectInsets; + + printf("fuck\n"); +} + */ + + +- (void) layout { + int ncols = _cols+1; + int nrows = _rows+1; + + GridDef *coldef = calloc(ncols, sizeof(GridDef)); + GridDef *rowdef = calloc(nrows, sizeof(GridDef)); + + NSRect viewFrame = self.frame; + + int colspacing = _columnspacing; + int rowspacing = _rowspacing; + + CxIterator i = cxListIterator(_children); + cx_foreach(GridElm *, elm, i) { + NSSize size = elm->view.intrinsicContentSize; + NSEdgeInsets alignment = elm->view.alignmentRectInsets; + if(size.width != NSViewNoIntrinsicMetric) { + CGFloat width = size.width + alignment.left + alignment.right; + if(width > coldef[elm->x].preferred_size) { + coldef[elm->x].preferred_size = width; + } + } + if(size.height != NSViewNoIntrinsicMetric) { + CGFloat height = size.height + alignment.top + alignment.right; + //CGFloat height = size.height; + if(height > rowdef[elm->y].preferred_size) { + rowdef[elm->y].preferred_size = height; + } + } + + if(elm->hexpand) { + coldef[elm->x].extend = TRUE; + } + if(elm->vexpand) { + rowdef[elm->y].extend = TRUE; + } + } + + int col_ext = 0; + int row_ext = 0; + + int preferred_width = 0; + int preferred_height = 0; + for(int j=0;j<ncols;j++) { + preferred_width += coldef[j].preferred_size + colspacing; + if(coldef[j].extend) { + col_ext++; + } + } + for(int j=0;j<nrows;j++) { + preferred_height += rowdef[j].preferred_size + rowspacing; + if(rowdef[j].extend) { + row_ext++; + } + } + + _preferredSize.width = preferred_width; + _preferredSize.height = preferred_height; + + + int hremaining = viewFrame.size.width - preferred_width; + int vremaining = viewFrame.size.height - preferred_height; + int hext = hremaining/col_ext; + int vext = vremaining/row_ext; + + for(int j=0;j<ncols;j++) { + GridDef *col = &coldef[j]; + if(col->extend) { + col->size = col->preferred_size + hext; + } else { + col->size = col->preferred_size; + } + } + for(int j=0;j<nrows;j++) { + GridDef *row = &rowdef[j]; + if(row->extend) { + row->size = row->preferred_size + vext; + } else { + row->size = row->preferred_size; + } + } + + int pos = 0; + for(int j=0;j<ncols;j++) { + coldef[j].pos = pos; + pos += coldef[j].size + colspacing; + } + pos = 0; + for(int j=0;j<nrows;j++) { + rowdef[j].pos = pos; + pos += rowdef[j].size + rowspacing; + } + + i = cxListIterator(_children); + cx_foreach(GridElm *, elm, i) { + //NSSize size = elm->view.intrinsicContentSize; + GridDef *col = &coldef[elm->x]; + GridDef *row = &rowdef[elm->y]; + + NSEdgeInsets alignment = elm->view.alignmentRectInsets; + NSRect frame; + frame.size.width = col->size; + frame.size.height = row->size; + frame.origin.x = col->pos - (alignment.left+alignment.right)/2; + frame.origin.y = viewFrame.size.height - row->pos - frame.size.height + ((alignment.top+alignment.right)/2); + elm->view.frame = frame; + } + + free(coldef); + free(rowdef); +} + + +- (NSSize)intrinsicContentSize { + return self.preferredSize; +} + +- (void) addView:(NSView*)view fill:(BOOL)fill { + if(_newline) { + _y++; + _x = 0; + _newline = FALSE; + } + + GridElm elm; + elm.x = _x; + elm.y = _y; + elm.margin = 0; + elm.colspan = _uilayout.colspan; + elm.rowspan = _uilayout.rowspan; + elm.hfill = _uilayout.hfill; + elm.vfill = _uilayout.vfill; + elm.hexpand = _uilayout.hexpand; + elm.vexpand = _uilayout.vexpand; + elm.view = view; + cxListAdd(_children, &elm); + + [self addSubview:view]; + self.needsLayout = YES; + + if(_x > _cols) { + _cols = _x; + } + if(_y > _rows) { + _rows = _y; + } + _x++; +} + +- (void) dealloc { + cxListDestroy(_children); +} + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/MainWindow.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,38 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" +#import "../ui/window.h" + +@interface MainWindow : NSWindow + +@property UiObject *uiobj; + +- (MainWindow*)init:(UiObject*)obj; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/MainWindow.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "MainWindow.h" +#import "Container.h" +#import "GridLayout.h" +#import "../common/object.h" + +@implementation MainWindow + +- (MainWindow*)init:(UiObject*)obj { + self.uiobj = obj; + NSRect frame = NSMakeRect(300, 200, 600, 500); + + self = [self initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | + NSWindowStyleMaskResizable | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable + backing:NSBackingStoreBuffered + defer:false]; + + // create a vertical stackview as default container + //BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; + GridLayout *vbox = [[GridLayout alloc] init]; + vbox.translatesAutoresizingMaskIntoConstraints = false; + [self.contentView addSubview:vbox]; + [NSLayoutConstraint activateConstraints:@[ + [vbox.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:4], + [vbox.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor], + [vbox.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor], + [vbox.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor] + ]]; + + uic_object_push_container(obj, ui_create_container(obj, vbox)); + + return self; +} + +@end
--- a/ui/cocoa/Makefile Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/Makefile Sat Jan 04 16:38:48 2025 +0100 @@ -27,7 +27,7 @@ # $(COCOA_OBJPRE)%.o: cocoa/%.m - $(CC) -o $@ -c $(CFLAGS) $< + $(CC) -o $@ -c -I../ucx -fobjc-arc $(CFLAGS) $(TK_CFLAGS) $< $(UI_LIB): $(OBJ) $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/UiJob.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,30 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/UiJob.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,29 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "UiJob.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/WindowManager.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,41 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" + +@interface WindowManager : NSObject<NSWindowDelegate> + +@property NSMutableArray<NSWindow*> *windows; + ++ (WindowManager*) sharedWindowManager; + +- (WindowManager*)init; + +- (void)addWindow:(NSWindow*)win; + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/WindowManager.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,57 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WindowManager.h" + +@implementation WindowManager + +static WindowManager *instance = nil; + ++ (WindowManager*) sharedWindowManager { + if(instance == nil) { + instance = [[WindowManager alloc] init]; + } + return instance; +} + +- (WindowManager*)init { + _windows = [[NSMutableArray alloc] initWithCapacity:16]; + return self; +} + +- (void)addWindow:(NSWindow*)win { + [_windows addObject:win]; + [win setDelegate:self]; +} + +- (void) windowWillClose:(NSNotification *) notification { + NSWindow *window = notification.object; + [_windows removeObject:window]; +} + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/appdelegate.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,37 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "../ui/toolkit.h" +#include "../common/context.h" +#include "../common/object.h" + +@interface AppDelegate : NSObject <NSApplicationDelegate> + + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/appdelegate.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,49 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "AppDelegate.h" + +#import "toolkit.h" + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + ui_cocoa_onstartup(); +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + ui_cocoa_onexit(); +} + + +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { + return YES; +} + + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/button.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" + +#import "../ui/button.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/button.m Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "button.h" +#import "EventData.h" +#import "Container.h" +#import <objc/runtime.h> + +UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { + NSButton *button = [[NSButton alloc] init]; + if(args.label) { + NSString *label = [[NSString alloc] initWithUTF8String:args.label]; + button.title = label; + } + + if(args.onclick) { + EventData *event = [[EventData alloc] init:args.onclick userdata:args.onclickdata]; + event.obj = obj; + button.target = event; + button.action = @selector(handleEvent:); + objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, button, &layout, FALSE); + + return (__bridge void*)button; +}
--- a/ui/cocoa/container.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/container.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,19 +26,65 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import "../ui/toolkit.h" #import "toolkit.h" -typedef void(*ui_container_add_f)(UiContainer*, NSView*); +#import "../ui/container.h" + +#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) +#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) + +typedef struct UiLayout UiLayout; +typedef enum UiLayoutBool UiLayoutBool; -struct UiContainer { - NSView* widget; - void (*add)(UiContainer*, NSView*); - NSRect (*getframe)(UiContainer*); +enum UiLayoutBool { + UI_LAYOUT_UNDEFINED = 0, + UI_LAYOUT_TRUE, + UI_LAYOUT_FALSE, +}; + +struct UiLayout { + UiTri fill; + //UiBool newline; + //char *label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + //int width; + int colspan; + int rowspan; }; -UiContainer* ui_window_container(UiObject *obj, NSWindow *window); +#define UI_INIT_LAYOUT(args) (UiLayout) {\ + .fill = args.fill, \ + .hexpand = args.hexpand, \ + .vexpand = args.vexpand, \ + .hfill = args.hfill, \ + .vfill = args.vfill, \ + .colspan = args.colspan, \ + .rowspan = args.rowspan } + + +@protocol Container + +@property UiLayout uilayout; +@property const char *label; +@property UiBool newline; -NSRect ui_container_getframe(UiContainer *ct); -void ui_container_add(UiContainer *ct, NSView *view); +- (void) addView:(NSView*)view fill:(BOOL)fill; + +@end + +@interface BoxContainer : NSStackView<Container> + +- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing; +@end + + + + + +UiContainerX* ui_create_container(UiObject *obj, id<Container> container); + +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill);
--- a/ui/cocoa/container.m Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/container.m Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,81 +26,143 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import <stdio.h> -#import <stdlib.h> +#import "Container.h" +#import "GridLayout.h" + +/* ------------------------- container classes ------------------------- */ -#import "container.h" +@implementation BoxContainer +@synthesize label=_label; +@synthesize uilayout=_uilayout; +@synthesize newline=_newline; -UiContainer* ui_window_container(UiObject *obj, NSWindow *window) { - UiContainer *ct = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct->widget = [window contentView]; - ct->add = ui_container_add; - ct->getframe = ui_container_getframe; - return ct; +- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing { + self = [super init]; + _label = NULL; + _uilayout = (UiLayout){ 0 }; + _newline = false; + + self.distribution = NSStackViewDistributionFillProportionally; + self.spacing = spacing; + + self.orientation = orientation; + if(orientation == NSUserInterfaceLayoutOrientationHorizontal) { + self.alignment = NSLayoutAttributeHeight; + } else { + self.alignment = NSLayoutAttributeWidth; + } + + + return self; } -UIWIDGET ui_sidebar(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - NSRect frame = ct->getframe(ct); +- (void) addView:(NSView*)view fill:(BOOL)fill { + if(_uilayout.fill != UI_LAYOUT_UNDEFINED) { + fill = ui_lb2bool(_uilayout.fill); + } - // create and add views - NSSplitView *splitview = [[NSSplitView alloc] initWithFrame:frame]; - [splitview setVertical:YES]; - [splitview setDividerStyle:NSSplitViewDividerStyleThin]; - ct->add(ct, splitview); + [self addArrangedSubview:view]; - NSRect lframe; - lframe.origin.x = 0; - lframe.origin.y = 0; - lframe.size.width = 200; - lframe.size.height = frame.size.height; - - NSRect rframe; - rframe.origin.x = 0; - rframe.origin.y = 0; - rframe.size.width = frame.size.width - 201; - rframe.size.height = frame.size.height; - - NSView *sidebar = [[NSView alloc]initWithFrame:lframe]; - NSView *contentarea = [[NSView alloc]initWithFrame:rframe]; + if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) { + [view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES; + if(!fill) { + [view.widthAnchor constraintEqualToConstant:view.intrinsicContentSize.width].active = YES; + } + } else { + [view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES; + if(!fill) { + [view.heightAnchor constraintEqualToConstant:view.intrinsicContentSize.height].active = YES; + } + } - [splitview addSubview:sidebar]; - [splitview addSubview:contentarea]; + // at the moment, only the fill layout option needs to be reset + _uilayout.fill = UI_DEFAULT; +} + +@end + + + +/* -------------------- public container functions --------------------- */ + +static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, NSUserInterfaceLayoutOrientation orientation) { + BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args.spacing]; + box.translatesAutoresizingMaskIntoConstraints = false; - // add ui objects for the sidebar and contentarea - // the sidebar is added last, so that new views are added first to it - UiObject *left = uic_object_new(obj, sidebar); - UiContainer *ct1 = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct1->widget = sidebar; - ct1->add = ui_container_add; - ct1->getframe = ui_container_getframe; - left->container = ct1; + // add box to the parent + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, box, &layout, TRUE); + + // add new box to the obj container chain + uic_object_push_container(obj, ui_create_container(obj, box)); - UiObject *right = uic_object_new(obj, sidebar); - UiContainer *ct2 = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct2->widget = contentarea; - ct2->add = ui_container_add; - ct2->getframe = ui_container_getframe; - right->container = ct2; + return (__bridge void*)box; +} + +UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { + return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationVertical); +} + +UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { + return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationHorizontal); +} + +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { + GridLayout *grid = [[GridLayout alloc] init]; + grid.translatesAutoresizingMaskIntoConstraints = false; - uic_obj_add(obj, right); - uic_obj_add(obj, left); + // add box to the parent + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, grid, &layout, TRUE); - return splitview; + // add new box to the obj container chain + uic_object_push_container(obj, ui_create_container(obj, grid)); + + return (__bridge void*)grid; } -NSRect ui_container_getframe(UiContainer *ct) { - return [ct->widget frame]; +void ui_container_begin_close(UiObject *obj) { + UiContainerX *ct = obj->container_end; + ct->close = 1; +} + +int ui_container_finish(UiObject *obj) { + UiContainerX *ct = obj->container_end; + if(ct->close) { + ui_end_new(obj); + return 0; + } + return 1; } -void ui_container_add(UiContainer *ct, NSView *view) { - [ct->widget addSubview: view]; +/* ------------------------- private functions ------------------------- */ + +UiContainerX* ui_create_container(UiObject *obj, id<Container> container) { + UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX)); + ctn->container = (__bridge void*)container; + ctn->close = 0; + ctn->prev = NULL; + ctn->next = NULL; + return ctn; } + +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill) { + UiContainerX *ctn = obj->container_end; + id<Container> container = (__bridge id<Container>)ctn->container; + container.uilayout = *layout; + [container addView:view fill:fill]; +} + +/* ---------------------- public layout functions ----------------------- */ + +void ui_newline(UiObject *obj) { + UiContainerX *ctn = obj->container_end; + if(ctn) { + id<Container> container = (__bridge id<Container>)ctn->container; + container.newline = TRUE; + } else { + fprintf(stderr, "Error: obj has no container\n"); + } +}
--- a/ui/cocoa/graphics.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "../ui/graphics.h" -#import "toolkit.h" - - -@interface UiCanvas : NSView { - UiObject *object; - ui_drawfunc callback; - void *userdata; -} - -- (UiObject*) object; -- (void) setObject:(void*)obj; - -- (void*) userdata; -- (void) setUserdata:(void*)d; - -- (ui_drawfunc) callback; -- (void) setCallback: (ui_drawfunc)f; - - -- (void)drawRect:(NSRect)rect; - -@end -
--- a/ui/cocoa/graphics.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,124 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> -#import <string.h> - -#import "graphics.h" -#import "container.h" -#import "../common/context.h" - - - -@implementation UiCanvas - -- (UiObject*) object { - return object; -} - -- (void) setObject:(void*)obj { - object = obj; -} - -- (void*) userdata { - return userdata; -} - -- (void) setUserdata:(void*)d { - userdata = d; -} - -- (ui_drawfunc) callback { - return callback; -} -- (void) setCallback: (ui_drawfunc)f { - callback = f; -} - -- (void) drawRect:(NSRect)rect { - UiGraphics g; - NSRect bounds = [self bounds]; - g.width = bounds.size.width; - g.height = bounds.size.height; - - UiEvent ev; - ev.obj = object; - ev.window = object->window; - ev.document = object->ctx->document; - - callback(&ev, &g, userdata); -} - -@end - - -UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) { - UiContainer *ct = uic_get_current_container(obj); - - NSRect frame = ct->getframe(ct); - - UiCanvas *canvas = [[UiCanvas alloc]initWithFrame:frame]; - [canvas setObject: obj]; - [canvas setCallback: f]; - [canvas setUserdata: userdata]; - ct->add(ct, canvas); - - return canvas; -} - - -// drawing functions -void ui_graphics_color(UiGraphics *gr, int red, int green, int blue) { - float r = ((float)red) / 255.f; - float g = ((float)green) / 255.f; - float b = ((float)blue) / 255.f; - - NSColor *color = [NSColor colorWithCalibratedRed:r green:g blue:b alpha:1]; - [color set]; -} - -void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) { - // translate y - y = g->height - y - h; - - NSRect bounds; - bounds.origin.x = x; - bounds.origin.y = y; - bounds.size.width = w; - bounds.size.height = h; - - if(fill) { - NSRectFill(bounds); - } else { - NSFrameRect(bounds); - } -} - - -
--- a/ui/cocoa/menu.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "../ui/menu.h" -#import "toolkit.h" -#import <ucx/list.h> - -typedef struct UiAbstractMenuItem { - int (*update)(id window, void *item); - void *item_data; -} UiAbstractMenuItem; - -typedef struct UiMenuItem { - NSMenuItem *item; - int state; -} UiMenuItem; - -typedef struct UiStateItem { - NSMenuItem *item; - char *var; -} UiStateItem; - -typedef struct UiMenuItemList { - NSMenu *menu; - NSMenuItem *first; - UiList *list; - int index; - int oldcount; - ui_callback callback; - void *data; -} UiMenuItemList; - -@interface UiMenuDelegate : NSObject <NSMenuDelegate> { - UcxList *items; // UiStateItem* - UcxList *itemlists; // UiMenuItemList* -} - -- (void) menuNeedsUpdate:(NSMenu*) menu; - -- (void) addItem:(NSMenuItem*) item var: (char*)name; - -- (void) addList:(UiList*) list menu:(NSMenu*)menu index: (int)i callback: (ui_callback)f data:(void*) data; - -- (UcxList*) items; - -- (UcxList*) lists; - -@end - -@interface UiGroupMenuItem : NSMenuItem { - NSMutableArray *groups; -} - -- (id)initWithTitle:(NSString*)title action:(SEL)action keyEquivalent:(NSString*)s; - -- (void) addGroup:(int)group; - -- (void) checkGroups:(int*)g count:(int)n; - -@end - -void ui_menu_init(); -UiMenuDelegate* ui_menu_delegate(); - -int ui_menuitem_get(UiInteger *i); -void ui_menuitem_set(UiInteger *i, int value); - -int ui_update_item(id window, void *data); -int ui_update_item_list(id window, void *data);
--- a/ui/cocoa/menu.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,319 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> -#import <string.h> -#import <stdarg.h> - -#import "menu.h" -#import "window.h" -#import "stock.h" - -@implementation UiMenuDelegate - -- (UiMenuDelegate*) init { - items = NULL; - itemlists = NULL; - return self; -} - -- (void) menuNeedsUpdate:(NSMenu *) menu { - NSWindow *activeWindow = [NSApp keyWindow]; - [(UiCocoaWindow*)activeWindow updateMenu: menu]; -} - -- (void) addItem:(NSMenuItem*) item var: (char*)name { - UiStateItem *i = malloc(sizeof(UiStateItem)); - i->item = item; - i->var = name; - items = ucx_list_append(items, i); -} - -- (void) addList:(UiList*) list menu:(NSMenu*)menu index: (int)i callback: (ui_callback)f data:(void*) data { - UiMenuItemList *itemList = malloc(sizeof(UiMenuItemList)); - itemList->list = list; - itemList->menu = menu; - itemList->first = NULL; - itemList->index = i; - itemList->oldcount = 0; - itemList->callback = f; - itemList->data = data; - itemlists = ucx_list_append(itemlists, itemList); -} - -- (UcxList*) items { - return items; -} - -- (UcxList*) lists { - return itemlists; - -} - -@end - - -@implementation UiGroupMenuItem - -- (id)initWithTitle:(NSString*)title action:(SEL)action keyEquivalent:(NSString*)s { - [super initWithTitle:title action:action keyEquivalent:s]; - groups = [[NSMutableArray alloc]initWithCapacity: 8]; - return self; -} - -- (void) addGroup:(int)group { - NSNumber *groupNumber = [NSNumber numberWithInteger:group]; - [groups addObject:groupNumber]; -} - -- (void) checkGroups:(int*)g count:(int)n { - int c = [groups count]; - - char *check = calloc(1, c); - for(int i=0;i<n;i++) { - for(int k=0;k<c;k++) { - NSNumber *groupNumber = [groups objectAtIndex:k]; - if([groupNumber intValue] == g[i]) { - check[k] = 1; - break; - } - } - } - - for(int j=0;j<c;j++) { - if(check[j] == 0) { - [self setEnabled:NO]; - return; - } - } - [self setEnabled:YES]; -} - -@end - - -//static NSMenu *currentMenu = NULL; - -static UcxList *current; - -static int currentItemIndex = 0; -static UiMenuDelegate *delegate; - -void ui_menu_init() { - delegate = [[UiMenuDelegate alloc]init]; -} - -UiMenuDelegate* ui_menu_delegate() { - return delegate; -} - - -void ui_menu(char *title) { - NSString *str = [[NSString alloc] initWithUTF8String:title]; - - NSMenu *menu = [[NSMenu alloc] initWithTitle: str]; - NSMenuItem *menuItem = [[NSApp mainMenu] addItemWithTitle:str - action:nil keyEquivalent:@""]; - [menu setDelegate: delegate]; - [menu setAutoenablesItems:NO]; - - [[NSApp mainMenu] setSubmenu:menu forItem:menuItem]; - //currentMenu = menu; - currentItemIndex = 0; - - current = ucx_list_prepend(NULL, menu); -} - -void ui_submenu(char *title) { - NSString *str = [[NSString alloc] initWithUTF8String:title]; - NSMenu *currentMenu = current->data; - - NSMenu *menu = [[NSMenu alloc] initWithTitle: str]; - NSMenuItem *menuItem = [currentMenu addItemWithTitle:str - action:nil keyEquivalent:@""]; - [menu setDelegate: delegate]; - [menu setAutoenablesItems:NO]; - - [currentMenu setSubmenu:menu forItem:menuItem]; - //currentMenu = menu; - currentItemIndex = 0; - - current = ucx_list_prepend(current, menu); -} - -void ui_submenu_end() { - if(ucx_list_size(current) < 2) { - return; - } - current = ucx_list_remove(current, current); -} - -void ui_menuitem(char *label, ui_callback f, void *data) { - ui_menuitem_gr(label, f, data, -1); -} - -void ui_menuitem_st(char *stockid, ui_callback f, void *data) { - ui_menuitem_stgr(stockid, f, data, -1); -} - -void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { - // create menu item - EventWrapper *event = [[EventWrapper alloc]initWithData:userdata callback:f]; - NSString *title = [[NSString alloc] initWithUTF8String:label]; - UiGroupMenuItem *item = [[UiGroupMenuItem alloc]initWithTitle:title action:@selector(handleEvent:) keyEquivalent:@""]; - [item setTarget:event]; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - [item addGroup: group]; - } - va_end(ap); - - NSMenu *currentMenu = current->data; - [currentMenu addItem:item]; - - currentItemIndex++; -} - -void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { - // create menu item - EventWrapper *event = [[EventWrapper alloc]initWithData:userdata callback:f]; - UiStockItem *si = ui_get_stock_item(stockid); - UiGroupMenuItem *item = [[UiGroupMenuItem alloc]initWithTitle:si->label - action:@selector(handleEvent:) - keyEquivalent:si->keyEquivalent]; - [item setTarget:event]; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - [item addGroup: group]; - } - va_end(ap); - - NSMenu *currentMenu = current->data; - [currentMenu addItem:item]; - - currentItemIndex++; -} - -void ui_checkitem(char *label, ui_callback f, void *data) { - EventWrapper *event = [[EventWrapper alloc]initWithData:data callback:f]; - NSString *str = [[NSString alloc] initWithUTF8String:label]; - - NSMenu *currentMenu = current->data; - NSMenuItem *item = [currentMenu addItemWithTitle:str - action:@selector(handleStateEvent:) keyEquivalent:@""]; - [item setTarget:event]; - - [delegate addItem: item var:NULL]; - currentItemIndex++; -} - -void ui_checkitem_nv(char *label, char *vname) { - EventWrapper *event = [[EventWrapper alloc]initWithData:NULL callback:NULL]; - NSString *str = [[NSString alloc] initWithUTF8String:label]; - - NSMenu *currentMenu = current->data; - NSMenuItem *item = [currentMenu addItemWithTitle:str - action:@selector(handleStateEvent:) keyEquivalent:@""]; - [item setTarget:event]; - - [delegate addItem: item var:vname]; - currentItemIndex++; -} - -void ui_menuseparator() { - NSMenu *currentMenu = current->data; - [currentMenu addItem: [NSMenuItem separatorItem]]; - currentItemIndex++; -} - -void ui_menuitem_list (UiList *items, ui_callback f, void *data) { - NSMenu *currentMenu = current->data; - [delegate addList:items menu:currentMenu index:currentItemIndex callback:f data:data]; -} - - - -int ui_menuitem_get(UiInteger *i) { - UiMenuItem *item = i->obj; - i->value = [item->item state]; - return i->value; -} - -void ui_menuitem_set(UiInteger *i, int value) { - UiMenuItem *item = i->obj; - [item->item setState: value]; - i->value = value; - item->state = value; -} - - -int ui_update_item(UiCocoaWindow *window, void *data) { - UiMenuItem *item = data; - [item->item setState: item->state]; - return 0; -} - -int ui_update_item_list(UiCocoaWindow *window, void *data) { - UiMenuItemList *itemList = data; - UiList *list = itemList->list; - - for(int r=0;r<itemList->oldcount;r++) { - [itemList->menu removeItemAtIndex:itemList->index]; - } - - char *str = ui_list_first(list); - int i = itemList->index; - [itemList->menu insertItem: [NSMenuItem separatorItem] atIndex: i]; - i++; - while(str) { - EventWrapper *event = [[EventWrapper alloc]initWithData:itemList->data callback:itemList->callback]; - [event setIntval: i - itemList->index - 1]; - - NSString *title = [[NSString alloc] initWithUTF8String:str]; - NSMenuItem *item = [[NSMenuItem alloc]initWithTitle:title action:@selector(handleEvent:) keyEquivalent:@""]; - [item setTarget:event]; - - [itemList->menu insertItem:item atIndex:i]; - - str = ui_list_next(list); - i++; - } - - itemList->oldcount = i - itemList->index; - - return 0; -}
--- a/ui/cocoa/objs.mk Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/objs.mk Sat Jan 04 16:38:48 2025 +0100 @@ -30,16 +30,15 @@ COCOA_OBJPRE = $(OBJ_DIR)/$(COCOA_SRC_DIR) COCOAOBJ = toolkit.o +COCOAOBJ += AppDelegate.o +COCOAOBJ += GridLayout.o +COCOAOBJ += EventData.o +COCOAOBJ += UiJob.o +COCOAOBJ += MainWindow.o +COCOAOBJ += WindowManager.o COCOAOBJ += window.o -COCOAOBJ += menu.o -COCOAOBJ += stock.o -COCOAOBJ += toolbar.o -COCOAOBJ += container.o -COCOAOBJ += text.o -COCOAOBJ += resource.o -COCOAOBJ += tree.o -COCOAOBJ += graphics.o - +COCOAOBJ += Container.o +COCOAOBJ += button.o TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%) TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m)
--- a/ui/cocoa/resource.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "../ui/toolkit.h" -#import "../ui/properties.h" - -
--- a/ui/cocoa/resource.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> -#import <string.h> - -#import "resource.h" -#import "../common/properties.h" - - - -void ui_load_lang_def(char *locale, char *default_locale) { - NSString *localeString = nil; - char tmp[6]; - if(!locale) { - NSString* lang = [[NSLocale currentLocale] localeIdentifier]; - if(lang) { - localeString = lang; - } else { - [[NSString alloc]initWithUTF8String:default_locale]; - } - } else { - localeString = [[NSString alloc]initWithUTF8String:locale]; - } - - NSString *path = [[NSBundle mainBundle] pathForResource:localeString ofType:@"properties" inDirectory:@"locales"]; - - const char *p = [path UTF8String]; - - if(uic_load_language_file((char*)p)) { - if(default_locale) { - ui_load_lang_def(default_locale, NULL); - } else { - // cannot find any language file - fprintf(stderr, "Ui Error: Cannot load language.\n"); - exit(-1); - } - } -} - -void ui_locales_dir(char *path) { - // empty -} - -void ui_pixmaps_dir(char *path) { - // empty -} \ No newline at end of file
--- a/ui/cocoa/stock.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "toolkit.h" -#import "../ui/stock.h" -#import <ucx/map.h> - -typedef struct UiStockItem { - NSString *label; - NSString *keyEquivalent; - NSImage *image; -} UiStockItem; - -void ui_stock_init(); - -void ui_add_stock_item(char *stock_id, NSString *label, NSString *keyEquivalent, NSImage *image); - -UiStockItem* ui_get_stock_item(char *stock_id);
--- a/ui/cocoa/stock.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> - -#import "stock.h" -#import "../common/properties.h" - -static UcxMap *stock_items; - -void ui_stock_init() { - stock_items = ucx_map_new(64); - - ui_add_stock_item(UI_STOCK_NEW, @"New", @"n", nil); - ui_add_stock_item(UI_STOCK_OPEN, @"Open", @"o", nil); - ui_add_stock_item(UI_STOCK_SAVE, @"Save", @"s", nil); - ui_add_stock_item(UI_STOCK_SAVE_AS, @"Save as ...", @"", nil); - ui_add_stock_item(UI_STOCK_CLOSE, @"Close", @"w", nil); - ui_add_stock_item(UI_STOCK_UNDO, @"Undo", @"z", nil); - ui_add_stock_item(UI_STOCK_REDO, @"Redo", @"", nil); - ui_add_stock_item(UI_STOCK_CUT, @"Cut", @"x", nil); - ui_add_stock_item(UI_STOCK_COPY, @"Copy", @"c", nil); - ui_add_stock_item(UI_STOCK_PASTE, @"Paste", @"v", nil); - ui_add_stock_item(UI_STOCK_DELETE, @"Delete", @"", nil); - - ui_add_stock_item(UI_STOCK_GO_BACK, @"Back", @"", [NSImage imageNamed: NSImageNameGoLeftTemplate]); - ui_add_stock_item(UI_STOCK_GO_FORWARD, @"Forward", @"", [NSImage imageNamed: NSImageNameGoRightTemplate]); -} - -void ui_add_stock_item(char *stock_id, NSString *label, NSString *keyEquivalent, NSImage *image) { - UiStockItem *i = malloc(sizeof(UiStockItem)); - i->label = label; - i->keyEquivalent = keyEquivalent; - i->image = image; - - ucx_map_cstr_put(stock_items, stock_id, i); -} - -UiStockItem* ui_get_stock_item(char *stock_id) { - UiStockItem *item = ucx_map_cstr_get(stock_items, stock_id); - if(item) { - char *label = uistr_n(stock_id); - if(label) { - NSString *str = [[NSString alloc]initWithUTF8String:label]; - item->label = str; - } - } - return item; -}
--- a/ui/cocoa/text.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "../ui/text.h" -#import "toolkit.h" -#import <ucx/list.h> - -@interface TextChangeMgr : NSObject<NSTextViewDelegate> { - UiContext *context; - UiText *value; - int last_length; -} - -- (TextChangeMgr*)initWithValue:(UiText*)text context:(UiContext*)ctx; - -- (NSUndoManager*)undoManagerForTextView:(NSTextView*)textview; - -@end - -#define UI_TEXTBUF_INSERT 0 -#define UI_TEXTBUF_DELETE 1 -typedef struct UiTextBufOp { - int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE - int start; - int end; - int len; - char *text; -} UiTextBufOp; - - - -char* ui_textarea_get(UiText *text); -void ui_textarea_set(UiText *text, char *str); -char* ui_textarea_getsubstr(UiText *text, int begin, int end); -void ui_textarea_insert(UiText *text, int pos, char *str); -int ui_textarea_position(UiText *text); -void ui_textarea_selection(UiText *text, int *begin, int *end); -int ui_textarea_length(UiText *text);
--- a/ui/cocoa/text.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,190 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> -#import <string.h> - -#import "text.h" -#import "container.h" - -@implementation TextChangeMgr - -- (TextChangeMgr*)initWithValue:(UiText*)text context:(UiContext*)ctx { - value = text; - context = ctx; - last_length = 0; - return self; -} - -- (NSUndoManager*)undoManagerForTextView:(NSTextView*)textview { - return (NSUndoManager*)value->undomgr; -} - -- (NSRange)textView:(NSTextView *)textview - willChangeSelectionFromCharacterRange:(NSRange)oldrange - toCharacterRange:(NSRange)newrange -{ - if(newrange.length != last_length) { - if(newrange.length == 0) { - ui_unset_group(context, UI_GROUP_SELECTION); - } else { - ui_set_group(context, UI_GROUP_SELECTION); - } - } - - last_length = newrange.length; - return newrange; -} - -@end - - -UIWIDGET ui_textarea(UiObject *obj, UiText *value) { - UiContainer *ct = uic_get_current_container(obj); - - NSRect frame = ct->getframe(ct); - - NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:frame]; - [scrollview setHasVerticalScroller:YES]; - //[scrollvew setHasHorizontalScroller:YES]; - [scrollview setBorderType:NSNoBorder]; - //[scrollview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - - //frame.size.width = frame.size.width - 15; - NSTextView *textview = [[NSTextView alloc]initWithFrame:frame]; - [textview setAllowsUndo:TRUE]; - [textview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - - [textview setFont:[NSFont fontWithName:@"Menlo" size:12]]; - - [scrollview setDocumentView:textview]; - - ct->add(ct, scrollview); - - // bind value - if(value) { - value->get = ui_textarea_get; - value->set = ui_textarea_set; - value->getsubstr = ui_textarea_getsubstr; - value->insert = ui_textarea_insert; - value->position = ui_textarea_position; - value->selection = ui_textarea_selection; - value->length = ui_textarea_length; - value->value = NULL; - value->obj = textview; - - TextChangeMgr *delegate = [[TextChangeMgr alloc]initWithValue:value context:obj->ctx]; - [textview setDelegate:delegate]; - - NSUndoManager *undomgr = [[NSUndoManager alloc]init]; - value->undomgr = undomgr; - } - - return textview; -} - -char* ui_textarea_get(UiText *text) { - if(text->value) { - free(text->value); - } - NSTextView *textview = (NSTextView*)text->obj; - NSString *str = [[textview textStorage]string]; - size_t length = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - const char *cstr = [str UTF8String]; - char *value = malloc(length + 1); - memcpy(value, cstr, length); - value[length] = '\0'; - text->value = value; - return value; -} - -void ui_textarea_set(UiText *text, char *str) { - if(text->value) { - free(text->value); - } - NSTextView *textview = (NSTextView*)text->obj; - NSString *s = [[NSString alloc]initWithUTF8String:str]; - NSAttributedString *as = [[NSAttributedString alloc]initWithString:s]; - [[textview textStorage] setAttributedString:as]; - text->value = NULL; -} - -char* ui_textarea_getsubstr(UiText *text, int begin, int end) { - if(text->value) { - free(text->value); - } - NSTextView *textview = (NSTextView*)text->obj; - NSString *str = [[textview textStorage]string]; - NSRange range; - range.location = begin; - range.length = end - begin; - - NSString *substr = [str substringWithRange:range]; - size_t length = [substr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - const char *cstr = [substr UTF8String]; - char *value = malloc(length + 1); - memcpy(value, cstr, length); - value[length] = '\0'; - text->value = value; - return value; -} - -void ui_textarea_insert(UiText *text, int pos, char *str) { - if(text->value) { - free(text->value); - } - NSTextView *textview = (NSTextView*)text->obj; - NSString *s = [[NSString alloc]initWithUTF8String:str]; - NSAttributedString *as = [[NSAttributedString alloc]initWithString:s]; - [[textview textStorage] insertAttributedString:as atIndex: pos]; - text->value = NULL; -} - -int ui_textarea_position(UiText *text) { - return [[[(NSTextView*)text->obj selectedRanges] objectAtIndex:0] rangeValue].location; -} - -void ui_textarea_selection(UiText *text, int *begin, int *end) { - NSRange range = [[[(NSTextView*)text->obj selectedRanges] objectAtIndex:0] rangeValue]; - *begin = range.location; - *end = range.location + range.length; -} - -int ui_textarea_length(UiText *text) { - return [[(NSTextView*)text->obj textStorage] length]; -} - -void ui_text_undo(UiText *text) { - [(NSUndoManager*)text->undomgr undo]; -} - -void ui_text_redo(UiText *text) { - [(NSUndoManager*)text->undomgr redo]; -} -
--- a/ui/cocoa/toolbar.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import "../ui/toolbar.h" -#import "toolkit.h" -#import <stdarg.h> - - -@protocol UiToolItem -- (NSToolbarItem *) createItem:(NSToolbar*)toolbar - identifier:(NSString*)identifier - object:(UiObject*)obj; - -- (void) addGroup:(int)group; - -- (UcxList*) groups; - -@end - - -/* - * UiToolbarStockItem - * - * creates a toolbar item from stock description - */ -@interface UiToolbarStockItem : NSObject <UiToolItem> { - char *name; - char *stockid; - ui_callback callback; - void *userdata; - UcxList *groups; - BOOL isToggleButton; -} - -- (UiToolbarStockItem*) initWithIdentifier:(char*)identifier - stockID:(char*)sid - callback:(ui_callback)f - userdata:(void*)data; - -- (void) setIsToggleButton:(BOOL)t; - - -@end - -/* - * UiToolbarItem - * - * toolbar item with label and icon - */ -@interface UiToolbarItem : NSObject <UiToolItem> { - char *name; - char *label; - // icon - ui_callback callback; - void *userdata; - UcxList *groups; - BOOL isToggleButton; -} - -- (UiToolbarItem*) initWithIdentifier:(char*)identifier - label:(char*)lbl - callback:(ui_callback)f - userdata:(void*)data; - -- (void) setIsToggleButton:(BOOL)t; - - -@end - - - -/* - * UiToolbarDelegate - */ -@interface UiToolbarDelegate : NSObject <NSToolbarDelegate> { - NSMutableArray *allowedItems; - NSMutableArray *defaultItems; - NSMutableDictionary *items; -} - -- (UiToolbarDelegate*) init; - -- (void) addDefault:(NSString*)identifier; - -- (void) addItem: (NSString*) identifier - item: (NSObject<UiToolItem>*) item; - -@end - - -/* - * UiToolbar - */ -@interface UiToolbar : NSToolbar { - UiObject *obj; -} - -- (UiToolbar*) initWithObject:(UiObject*)object; - -- (UiObject*) object; - -@end - -void ui_toolbar_init(); -void ui_toolbar_stock_button(char *name, char *stockid, BOOL toggle, ui_callback f, void *udata, va_list ap); -NSToolbar* ui_create_toolbar(UiObject *obj);
--- a/ui/cocoa/toolbar.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,358 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> -#import <string.h> -#import <inttypes.h> -#import <stdarg.h> - -#import "toolbar.h" -#import "window.h" -#import "stock.h" - - -static UiToolbarDelegate* toolbar_delegate; - -/* --------------------- UiToolbarStockItem --------------------- */ - -@implementation UiToolbarStockItem - -- (UiToolbarStockItem*) initWithIdentifier:(char*)identifier - stockID:(char*)sid - callback:(ui_callback)f - userdata:(void*)data -{ - name = identifier; - stockid = sid; - callback = f; - userdata = data; - groups = NULL; - isToggleButton = NO; - return self; -} - -- (void) setIsToggleButton:(BOOL)t { - isToggleButton = t; -} - -- (void) addGroup:(int)group { - groups = ucx_list_append(groups, (void*)(intptr_t)group); -} - - -- (NSToolbarItem *) createItem:(NSToolbar*)toolbar - identifier:(NSString*)identifier - object:(UiObject*)obj -{ - UiStockItem *s = ui_get_stock_item(stockid); - if(s == nil) { - printf("cannot find stock item\n"); - return nil; - } - - NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier: - identifier] autorelease]; - //[item setLabel:[s label]]; - //[item setPaletteLabel:[s label]]; - [item setLabel:s->label]; - [item setPaletteLabel:@"Operation"]; - - // create button ... - NSRect frame = NSMakeRect(0, 0, 40, 22); - //NSSearchField *sf = [[NSSearchField alloc]initWithFrame:frame]; - NSButton *button = [[NSButton alloc]initWithFrame:frame]; - //[button setImage:[s buttonImage]]; - //[button setImage:[NSImage imageNamed: NSImageNameAddTemplate]]; - if(s->image) { - [button setImage:s->image]; - } else { - [button setImage:[NSImage imageNamed: NSImageNameRemoveTemplate]]; - } - [button setBezelStyle: NSTexturedRoundedBezelStyle]; - - // event - EventWrapper *event = [[EventWrapper alloc] - initWithData:userdata callback:callback]; - if(isToggleButton) { - [button setButtonType: NSPushOnPushOffButton]; - [button setAction:@selector(handleToggleEvent:)]; - } else { - [button setAction:@selector(handleEvent:)]; - } - [button setTarget:event]; - - if(groups) { - uic_add_group_widget(obj->ctx, item, groups); - } - - [item setView:button]; - return item; -} - -- (UcxList*) groups { - return groups; -} - -@end - - -/* --------------------- UiToolbarItem --------------------- */ - -@implementation UiToolbarItem - -- (UiToolbarItem*) initWithIdentifier:(char*)identifier - label:(char*)lbl - callback:(ui_callback)f - userdata:(void*)data -{ - name = identifier; - label = lbl; - callback = f; - userdata = data; - groups = NULL; - isToggleButton = NO; - return self; -} - -- (void) setIsToggleButton:(BOOL)t { - isToggleButton = t; -} - -- (void) addGroup:(int)group { - groups = ucx_list_append(groups, (void*)(intptr_t)group); -} - - -- (NSToolbarItem *) createItem:(NSToolbar*)toolbar - identifier:(NSString*)identifier - object:(UiObject*)obj -{ - NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier: - identifier] autorelease]; - //[item setLabel:[s label]]; - //[item setPaletteLabel:[s label]]; - NSString *l = [[NSString alloc]initWithUTF8String:label]; - [item setLabel:l]; - [item setPaletteLabel:@"Operation"]; - - // create button ... - NSRect frame = NSMakeRect(0, 0, 40, 22); - //NSSearchField *sf = [[NSSearchField alloc]initWithFrame:frame]; - NSButton *button = [[NSButton alloc]initWithFrame:frame]; - //[button setImage:[s buttonImage]]; - //[button setImage:[NSImage imageNamed: NSImageNameAddTemplate]]; - - // TODO: image - [button setImage:[NSImage imageNamed: NSImageNameRemoveTemplate]]; - - [button setBezelStyle: NSTexturedRoundedBezelStyle]; - - // event - EventWrapper *event = [[EventWrapper alloc] - initWithData:userdata callback:callback]; - if(isToggleButton) { - [button setButtonType: NSPushOnPushOffButton]; - [button setAction:@selector(handleToggleEvent:)]; - } else { - [button setAction:@selector(handleEvent:)]; - } - [button setTarget:event]; - - if(groups) { - uic_add_group_widget(obj->ctx, item, groups); - } - - [item setView:button]; - return item; -} - -- (UcxList*) groups { - return groups; -} - -@end - - -/* --------------------- UiToolbarDelegate --------------------- */ - -@implementation UiToolbarDelegate - -- (UiToolbarDelegate*) init { - allowedItems = [[NSMutableArray alloc]initWithCapacity: 16]; - defaultItems = [[NSMutableArray alloc]initWithCapacity: 16]; - items = [[NSMutableDictionary alloc] init]; - return self; -} - -- (void) addDefault:(NSString*)identifier { - [defaultItems addObject: identifier]; -} - -- (void) addItem: (NSString*) identifier - item: (NSObject<UiToolItem>*) item -{ - [allowedItems addObject: identifier]; - [items setObject: item forKey:identifier]; -} - -/* -- (void) addStockItem:(char*)name - stockID:(char*)sid - callback:(ui_callback)f - data:(void*)userdata -{ - UiToolbarStockItem *item = [[UiToolbarStockItem alloc]initWithData:name - stockID:sid callback:f data:userdata]; - - NSString *s = [[NSString alloc]initWithUTF8String:name]; - [allowedItems addObject: s]; - [items setObject: item forKey:s]; -} -*/ - -// implementation of NSToolbarDelegate methods -- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar { - NSMutableArray *i = [[NSMutableArray alloc] - initWithCapacity:[allowedItems count] + 3]; - [i addObject: NSToolbarFlexibleSpaceItemIdentifier]; - [i addObject: NSToolbarSpaceItemIdentifier]; - [i addObject: NSToolbarSeparatorItemIdentifier]; - for(id item in allowedItems) { - [i addObject: item]; - } - - return i; -} - -- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar { - return defaultItems; -} - -- (NSToolbarItem *) toolbar:(NSToolbar*)toolbar - itemForItemIdentifier:(NSString*)identifier - willBeInsertedIntoToolbar:(BOOL)flag -{ - Protocol *item = @protocol(UiToolItem); - item = [items objectForKey: identifier]; - - // get UiObject from toolbar - UiObject *obj = [(UiToolbar*)toolbar object]; - - // create new NSToolbarItem - return [item createItem:toolbar identifier:identifier object:obj]; -} - -@end - - -@implementation UiToolbar - -- (UiToolbar*) initWithObject:(UiObject*)object { - [self initWithIdentifier: @"MainToolbar"]; - obj = object; - return self; -} - -- (UiObject*) object { - return obj; -} - -@end - - -/* --------------------- functions --------------------- */ - -void ui_toolbar_init() { - toolbar_delegate = [[UiToolbarDelegate alloc]init]; -} - -void ui_toolitem(char *name, char *label, ui_callback f, void *udata) { - UiToolbarItem *item = [[UiToolbarItem alloc] - initWithIdentifier: name - label: label - callback: f - userdata: udata]; - - NSString *identifier = [[NSString alloc]initWithUTF8String:name]; - [toolbar_delegate addItem: identifier item: item]; -} - -void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *udata) { - ui_toolitem_stgr(name, stockid, f, udata, -1); -} - -void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *udata, ...) { - va_list ap; - va_start(ap, udata); - ui_toolbar_stock_button(name, stockid, NO, f, udata, ap); - va_end(ap); -} - -void ui_toolitem_toggle_st(char *name, char *stockid, ui_callback f, void *udata) { - ui_toolitem_toggle_stgr(name, stockid, f, udata, -1); -} - -void ui_toolitem_toggle_stgr(char *name, char *stockid, ui_callback f, void *udata, ...) { - va_list ap; - va_start(ap, udata); - ui_toolbar_stock_button(name, stockid, YES, f, udata, ap); - va_end(ap); -} - - -void ui_toolbar_stock_button(char *name, char *stockid, BOOL toggle, ui_callback f, void *udata, va_list ap) { - UiToolbarStockItem *item = [[UiToolbarStockItem alloc] - initWithIdentifier: name - stockID: stockid - callback: f - userdata: udata]; - [item setIsToggleButton: toggle]; - NSString *identifier = [[NSString alloc]initWithUTF8String:name]; - [toolbar_delegate addItem: identifier item: item]; - - // add groups - int group; - while((group = va_arg(ap, int)) != -1) { - [item addGroup: group]; - } -} - - -void ui_toolbar_add_default(char *name) { - NSString *identifier = [[NSString alloc]initWithUTF8String:name]; - [toolbar_delegate addDefault: identifier]; -} - -NSToolbar* ui_create_toolbar(UiObject *obj) { - UiToolbar *toolbar = [[UiToolbar alloc] initWithObject:obj]; - [toolbar setDelegate: toolbar_delegate]; - [toolbar setAllowsUserCustomization: true]; - return toolbar; -} -
--- a/ui/cocoa/toolkit.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/toolkit.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -31,58 +31,7 @@ #include "../common/context.h" #include "../common/object.h" - -@interface UiApplicationDelegate : NSObject<NSApplicationDelegate> { - -} - -- (void)applicationWillTerminate:(NSNotification*)notification; - -- (BOOL)applicationShouldHandleReopen:(NSApplication *)app hasVisibleWindows:(BOOL)visible; - -- (BOOL)application:(NSApplication *)application openFile:(NSString *)filename; - -@end - -@interface EventWrapper : NSObject { - void *data; - ui_callback callback; - int value; -} - -- (EventWrapper*) initWithData: (void*)data callback:(ui_callback) f; - -- (void*) data; -- (void) setData:(void*)d; -- (ui_callback) callback; -- (void) setCallback: (ui_callback)f; -- (int) intval; -- (void) setIntval:(int)i; +void ui_cocoa_onstartup(void); +void ui_cocoa_onopen(const char *file); +void ui_cocoa_onexit(void); -- (BOOL)handleEvent:(id)sender; -- (BOOL)handleStateEvent:(id)sender; -- (BOOL)handleToggleEvent:(id)sender; - -@end - -@interface UiThread : NSObject { - UiObject *obj; - ui_threadfunc job_func; - void *job_data; - ui_callback finish_callback; - void *finish_data; -} - -- (id) initWithObject:(UiObject*)object; -- (void) setJobFunction:(ui_threadfunc)func; -- (void) setJobData:(void*)data; -- (void) setFinishCallback:(ui_callback)callback; -- (void) setFinishData:(void*)data; - -- (void) start; -- (void) runJob:(id)n; -- (void) finish:(id)n; - -@end - -
--- a/ui/cocoa/toolkit.m Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/toolkit.m Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,321 +26,126 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import <stdio.h> -#import <stdlib.h> -#import <sys/stat.h> -#import <sys/types.h> -#import <errno.h> - -#import "../common/context.h" -#import "../common/document.h" -#import "../common/properties.h" +#import "toolkit.h" -#import "toolkit.h" -#import "window.h" -#import "menu.h" -#import "toolbar.h" -#import "stock.h" +#include "../common/document.h" +#include "../common/properties.h" +#include "../common/menu.h" +#include "../common/toolbar.h" +#include "../common/threadpool.h" -NSAutoreleasePool *pool; +#import "AppDelegate.h" -static char *application_name; +static const char *application_name; + +static int app_argc; +static const char **app_argv; -static ui_callback appclose_fnc; -static void *appclose_udata; +static ui_callback startup_func; +static void *startup_data; +static ui_callback open_func; +void *open_data; +static ui_callback exit_func; +void *exit_data; -static ui_callback openfile_fnc; -static void *openfile_udata; +/* ------------------- App Init / Event Loop functions ------------------- */ -void ui_init(char *appname, int argc, char **argv) { - pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; - [NSBundle loadNibNamed:@"MainMenu" owner:NSApp]; +void ui_init(const char *appname, int argc, char **argv) { + application_name = appname; + app_argc = argc; + app_argv = (const char**)argv; - UiApplicationDelegate *delegate = [[UiApplicationDelegate alloc]init]; - [NSApp setDelegate: delegate]; - - + uic_init_global_context(); + uic_docmgr_init(); - ui_menu_init(); - ui_toolbar_init(); - ui_stock_init(); - + uic_menu_init(); + uic_toolbar_init(); + uic_load_app_properties(); + + [NSApplication sharedApplication]; + //[NSBundle loadNibNamed:@"MainMenu" owner:NSApp ]; + //[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&topLevelObjects]; } -char* ui_appname() { +const char* ui_appname() { return application_name; } -void ui_exitfunc(ui_callback f, void *userdata) { - appclose_fnc = f; - appclose_udata = userdata; -} - -void ui_openfilefunc(ui_callback f, void *userdata) { - openfile_fnc = f; - openfile_udata = userdata; +void ui_onstartup(ui_callback f, void *userdata) { + startup_func = f; + startup_data = userdata; } -void ui_show(UiObject *obj) { - uic_check_group_widgets(obj->ctx); - if([obj->widget class] == [UiCocoaWindow class]) { - UiCocoaWindow *window = (UiCocoaWindow*)obj->widget; - [window makeKeyAndOrderFront:nil]; - } else { - printf("Error: ui_show: Object is not a Window!\n"); - } -} - -void ui_set_show_all(UIWIDGET widget, int value) { - // TODO -} - -void ui_set_visible(UIWIDGET widget, int visible) { - // TODO -} - -void ui_set_enabled(UIWIDGET widget, int enabled) { - [(id)widget setEnabled: enabled]; +void ui_onopen(ui_callback f, void *userdata) { + open_func = f; + open_data = userdata; } - - -void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { - UiThread *thread = [[UiThread alloc]initWithObject:obj]; - [thread setJobFunction:tf]; - [thread setJobData:td]; - [thread setFinishCallback:f]; - [thread setFinishData:fd]; - [thread start]; -} - -void ui_main() { - [NSApp run]; - [pool release]; +void ui_onexit(ui_callback f, void *userdata) { + exit_func = f; + exit_data = userdata; } - -void ui_clipboard_set(char *str) { - NSString *string = [[NSString alloc] initWithUTF8String:str]; - NSPasteboard * pasteBoard = [NSPasteboard generalPasteboard]; - [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pasteBoard setString:string forType:NSStringPboardType]; -} - -char* ui_clipboard_get() { - NSPasteboard * pasteBoard = [NSPasteboard generalPasteboard]; - NSArray *classes = [[NSArray alloc] initWithObjects:[NSString class], nil]; - NSDictionary *options = [NSDictionary dictionary]; - NSArray *data = [pasteBoard readObjectsForClasses:classes options:options]; - - if(data != nil) { - NSString *str = [data componentsJoinedByString: @""]; - - // copy C string - size_t length = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - const char *cstr = [str UTF8String]; - char *value = malloc(length + 1); - memcpy(value, cstr, length); - value[length] = '\0'; - - return value; - } else { - return NULL; +void ui_cocoa_onstartup(void) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(startup_func) { + startup_func(&e, startup_data); } } - -@implementation UiApplicationDelegate - -- (void)applicationWillTerminate:(NSNotification*)notification { - printf("terminate\n"); -} - -- (BOOL)applicationShouldHandleReopen:(NSApplication *)app hasVisibleWindows:(BOOL)visible; { - if(!visible) { - printf("reopen\n"); - } - return NO; -} - -- (BOOL)application:(NSApplication*)application openFile:(NSString*)filename { - if(openfile_fnc) { - UiEvent event; - event.obj = NULL; - event.document = NULL; - event.window = NULL; - event.eventdata = (void*)[filename UTF8String]; - event.intval = 0; - openfile_fnc(&event, openfile_udata); +void ui_cocoa_onopen(const char *file) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(open_func) { + open_func(&e, open_data); } - - return NO; -} - -@end - - -@implementation EventWrapper - -- (EventWrapper*) initWithData: (void*)d callback:(ui_callback) f { - data = d; - callback = f; - value = 0; - return self; -} - - -- (void*) data { - return data; -} - -- (void) setData:(void*)d { - data = d; -} - - -- (ui_callback) callback { - return callback; -} - -- (void) setCallback: (ui_callback)f { - callback = f; -} - -- (int) intval { - return value; -} - -- (void) setIntval:(int)i { - value = i; -} - - -- (BOOL)handleEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - - UiEvent event; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - event.intval = value; - } - if(callback) { - callback(&event, data); - } - - return true; } -- (BOOL)handleStateEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - int state = [sender state] ? NSOffState : NSOnState; - - UiEvent event; - event.intval = state; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - // if the sender is a menu item, we have to save the state for this - // window - UiMenuItem *wmi = [(UiCocoaWindow*)activeWindow getMenuItem: sender]; - if(wmi) { - // update state in window data - wmi->state = state; - } - } else { - event.window = NULL; - event.document = NULL; - } - if(callback) { - callback(&event, data); - } - [sender setState: state]; - - return true; -} - -- (BOOL)handleToggleEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - - UiEvent event; - event.intval = [sender state]; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - } else { - event.window = NULL; - event.document = NULL; - } - if(callback) { - callback(&event, data); - } - - return true; -} - -@end - -@implementation UiThread - -- (id) initWithObject:(UiObject*)object { - obj = object; - job_func = NULL; - job_data = NULL; - finish_callback = NULL; - finish_data = NULL; - return self; -} - -- (void) setJobFunction:(ui_threadfunc)func { - job_func = func; -} - -- (void) setJobData:(void*)data { - job_data = data; -} - -- (void) setFinishCallback:(ui_callback)callback { - finish_callback = callback; -} - -- (void) setFinishData:(void*)data { - finish_data = data; -} - -- (void) start { - [NSThread detachNewThreadSelector:@selector(runJob:) - toTarget:self - withObject:nil]; -} - -- (void) runJob:(id)n { - int result = job_func(job_data); - if(!result) { - [self performSelectorOnMainThread:@selector(finish:) - withObject:nil - waitUntilDone:NO]; +void ui_cocoa_onexit(void) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(exit_func) { + exit_func(&e, exit_data); } } -- (void) finish:(id)n { - UiEvent event; - event.obj = obj; - event.window = obj->window; - event.document = obj->ctx->document; - event.eventdata = NULL; - event.intval = 0; - finish_callback(&event, finish_data); +void ui_main(void) { + NSApplicationMain(app_argc, app_argv); +} + +/* ------------------- Window Visibility functions ------------------- */ + +void ui_show(UiObject *obj) { + if(obj->wobj) { + NSWindow *window = (__bridge NSWindow*)obj->wobj; + [window makeKeyAndOrderFront:nil]; + } } -@end +void ui_close(UiObject *obj) { + +} + +/* ------------------- Job Control / Threadpool functions ------------------- */ +void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { +} + +void ui_call_mainthread(ui_threadfunc tf, void* td) { + +}
--- a/ui/cocoa/tree.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - - #import "../ui/tree.h" - #import "toolkit.h" - - -@interface UiTableDataSource : NSObject<NSTableViewDataSource, NSTableViewDelegate> { - UiList *data; - UiModelInfo *info; - UiListSelection *lastSelection; -} - -- (id)initWithData:(UiList*)list modelInfo:(UiModelInfo*)modelinfo; - -- (void)handleDoubleAction:(id)sender; - -@end - - -char* ui_type_to_string(UiModelType type, void *data, BOOL *free); -
--- a/ui/cocoa/tree.m Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2012 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#import <stdio.h> -#import <stdlib.h> - -#import "tree.h" -#import "container.h" -#import "window.h" -#import "../common/context.h" -#import <ucx/utils.h> - -@implementation UiTableDataSource - -- (id)initWithData:(UiList*)list modelInfo:(UiModelInfo*)modelinfo { - data = list; - info = modelinfo; - lastSelection = NULL; - return self; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableview { - return data->count(data); -} - -- (id)tableView: (NSTableView*)tableview - objectValueForTableColumn:(NSTableColumn*)column - row:(NSInteger)row -{ - int column_index = [[column identifier]intValue]; - - void *row_data = data->get(data, row); - void *cell_data = info->getvalue(row_data, column_index); - - BOOL f = false; - char *str = ui_type_to_string(info->types[column_index], cell_data, &f); - NSString *s = [[NSString alloc]initWithUTF8String:str]; - return s; -} - -- (void)tableView:(NSTableView *)tableview - setObjectValue:(id)object - forTableColumn:(NSTableColumn *)column - row:(NSInteger)row -{ - int column_index = [[column identifier]intValue]; - - // TODO -} - -- (void)tableViewSelectionDidChange:(NSNotification *)notification { - NSTableView *tableview = (NSTableView*)notification.object; - - // create selection object - UiListSelection *selection = malloc(sizeof(UiListSelection)); - selection->count = [tableview numberOfSelectedRows]; - - selection->rows = calloc(selection->count, sizeof(int)); - NSIndexSet *indices = [tableview selectedRowIndexes]; - NSUInteger index = [indices firstIndex]; - int i=0; - while (index!=NSNotFound) { - selection->rows[i] = index; - index = [indices indexGreaterThanIndex:index]; - i++; - } - - // create event object - UiEvent event; - NSWindow *activeWindow = [NSApp keyWindow]; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - } else { - event.window = NULL; - event.document = NULL; - } - event.eventdata = selection; - event.intval = selection->count == 0 ? -1 : selection->rows[0]; - - // callback - info->selection(&event, info->userdata); - - // cleanup - if(lastSelection) { - free(lastSelection->rows); - free(lastSelection); - } - lastSelection = selection; -} - -- (void)handleDoubleAction:(id)sender { - // create event object - UiEvent event; - NSWindow *activeWindow = [NSApp keyWindow]; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - } else { - event.window = NULL; - event.document = NULL; - } - event.eventdata = lastSelection; - event.intval = lastSelection->count == 0 ? -1 : lastSelection->rows[0]; - - info->activate(&event, info->userdata); -} - -- (BOOL)tableView:(NSTableView *)tableview isGroupRow:(NSInteger)row { - return NO; -} - -@end - - -UIWIDGET ui_table(UiObject *obj, UiList *model, UiModelInfo *modelinfo) { - UiContainer *ct = uic_get_current_container(obj); - NSRect frame = ct->getframe(ct); - - NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:frame]; - [scrollview setHasVerticalScroller:YES]; - //[scrollvew setHasHorizontalScroller:YES]; - [scrollview setBorderType:NSNoBorder]; - //[scrollview setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - - NSTableView *tableview = [[NSTableView alloc]initWithFrame:frame]; - [scrollview setDocumentView:tableview]; - [tableview setAllowsMultipleSelection: YES]; - //[tableview setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList]; - - // add columns - for(int i=0;i<modelinfo->columns;i++) { - NSString *cid = [[NSString alloc]initWithFormat: @"%d", i]; - NSTableColumn *column = [[NSTableColumn alloc]initWithIdentifier:cid]; - - NSString *title = [[NSString alloc]initWithUTF8String: modelinfo->titles[i]]; - [[column headerCell] setStringValue:title]; - - [tableview addTableColumn:column]; - } - - UiTableDataSource *source = [[UiTableDataSource alloc]initWithData:model modelInfo:modelinfo]; - [tableview setDataSource:source]; - [tableview setDelegate:source]; - - [tableview setDoubleAction:@selector(handleDoubleAction:)]; - [tableview setTarget:source]; - - - ct->add(ct, scrollview); - return scrollview; -} - - -UIWIDGET ui_listview_var(UiObject *obj, UiListPtr *list, ui_model_getvalue_f getvalue, ui_callback f, void *udata) { - UiContainer *ct = uic_get_current_container(obj); - NSRect frame = ct->getframe(ct); - - NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:frame]; - [scrollview setHasVerticalScroller:YES]; - - [scrollview setBorderType:NSNoBorder]; - - NSTableView *tableview = [[NSTableView alloc]initWithFrame:frame]; - [scrollview setDocumentView:tableview]; - [tableview setAllowsMultipleSelection: NO]; - - // add single column - NSTableColumn *column = [[NSTableColumn alloc]initWithIdentifier:@"c"]; - [tableview addTableColumn:column]; - - // create model info - UiModelInfo *modelinfo = ui_model_info(obj->ctx, UI_STRING, -1); - - // add source - UiTableDataSource *source = [[UiTableDataSource alloc]initWithData:list->list modelInfo:modelinfo]; - - [tableview setDataSource:source]; - [tableview setDelegate:source]; - - [tableview setDoubleAction:@selector(handleDoubleAction:)]; - [tableview setTarget:source]; - - ct->add(ct, scrollview); - return scrollview; -} - -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_model_getvalue_f getvalue, ui_callback f, void *udata) { - UiListPtr *listptr = ucx_mempool_malloc(obj->ctx->mempool, sizeof(UiListPtr)); - listptr->list = list; - return ui_listview_var(obj, listptr, getvalue, f, udata); -} - -UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_model_getvalue_f getvalue, ui_callback f, void *udata) { - UiVar *var = uic_connect_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - UiListVar *value = var->value; - return ui_listview_var(obj, value->listptr, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - - -// TODO: motif code duplicate -char* ui_type_to_string(UiModelType type, void *data, BOOL *free) { - switch(type) { - case UI_STRING: *free = FALSE; return data; - case UI_INTEGER: { - *free = TRUE; - int *val = data; - sstr_t str = ucx_asprintf(ucx_default_allocator(), "%d", *val); - return str.ptr; - } - } - *free = FALSE; - return NULL; -}
--- a/ui/cocoa/window.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/window.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,27 +26,4 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import <Cocoa/Cocoa.h> -#import "../ui/window.h" -#import <ucx/list.h> -#import <ucx/map.h> - -#import "menu.h" - - - -@interface UiCocoaWindow : NSWindow { - UiObject *uiobj; - UcxMap *menus; // key: NSMenu value: UcxList of UiMenuItem - UcxMap *items; // key: NSMenuItem value: UiMenuItem -} - -- (UiCocoaWindow*) init: (NSRect)frame object: (UiObject*)obj; -- (UiObject*) object; -- (void) setObject:(UiObject*)obj; -- (void) setMenuItems:(UcxList*)menuItems; -- (void) setMenuItemLists:(UcxList*)itemLists; -- (UiMenuItem*) getMenuItem:(NSMenuItem*)item; -- (void) updateMenu:(NSMenu*)menu; - -@end +#import "toolkit.h" \ No newline at end of file
--- a/ui/cocoa/window.m Sun May 23 09:44:43 2021 +0200 +++ b/ui/cocoa/window.m Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,195 +26,43 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#import "window.h" -#import "window.h" -#import "menu.h" -#import "toolbar.h" -#import "container.h" -#import <ucx/mempool.h> -#import "../common/context.h" - -static int window_default_width = 600; -static int window_default_height = 500; - -@implementation UiCocoaWindow +#import "MainWindow.h" +#import "WindowManager.h" -- (UiCocoaWindow*) init: (NSRect)frame object: (UiObject*)obj { - self = [self initWithContentRect:frame - styleMask:NSTitledWindowMask | - NSResizableWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered - defer:false]; - - uiobj = obj; - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; - menus = ucx_map_new_a(allocator, 8); - items = ucx_map_new_a(allocator, 64); - - return self; -} +#include "../ui/window.h" +#include "../ui/properties.h" +#include "../common/context.h" +#include "../common/menu.h" +#include "../common/toolbar.h" -- (UiObject*) object { - return uiobj; -} +#include <cx/mempool.h> -- (void) setObject:(UiObject*)obj { - uiobj = obj; -} - -- (void) setMenuItems:(UcxList*)menuItems { - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; +static UiObject* create_window(const char *title, BOOL simple) { + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + obj->ref = 0; - UCX_FOREACH(elm, menuItems) { - UiStateItem *item = elm->data; - NSMenu *menu = [item->item menu]; - - // create UiMenuItem which represents an NSMenuItem for a Window - UiMenuItem *windowItem = ucx_mempool_malloc(uiobj->ctx->mempool, sizeof(UiMenuItem)); - windowItem->item = item->item; - windowItem->state = 0; - if(item->var) { - // bind value - UiVar *var = uic_connect_var(uiobj->ctx, item->var, UI_VAR_INTEGER); - if(var) { - UiInteger *value = var->value; - value->obj = windowItem; - value->get = ui_menuitem_get; - value->set = ui_menuitem_set; - value = 0; - } else { - // TODO: error - } - } - - // add item - UiAbstractMenuItem *abstractItem = malloc(sizeof(UiAbstractMenuItem)); - abstractItem->update = ui_update_item; - abstractItem->item_data = windowItem; - UcxList *itemList = ucx_map_get(menus, ucx_key(&menu, sizeof(void*))); - itemList = ucx_list_append_a(allocator, itemList, abstractItem); - ucx_map_put(menus, ucx_key(&menu, sizeof(void*)), itemList); - - ucx_map_put(items, ucx_key(&windowItem->item, sizeof(void*)), windowItem); - } -} - -- (void) setMenuItemLists:(UcxList*)itemLists { - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; - - UCX_FOREACH(elm, itemLists) { - UiMenuItemList *list = elm->data; - - UiAbstractMenuItem *abstractItem = malloc(sizeof(UiAbstractMenuItem)); - abstractItem->update = ui_update_item_list; - abstractItem->item_data = list; - - UcxList *itemList = ucx_map_get(menus, ucx_key(&list->menu, sizeof(void*))); - itemList = ucx_list_append_a(allocator, itemList, abstractItem); - ucx_map_put(menus, ucx_key(&list->menu, sizeof(void*)), itemList); - - } -} - -- (UiMenuItem*) getMenuItem:(NSMenuItem*)item { - return ucx_map_get(items, ucx_key(&item, sizeof(void*))); -} - -- (void) updateMenu:(NSMenu*)menu { - UcxList *itemList = ucx_map_get(menus, ucx_key(&menu, sizeof(void*))); - UCX_FOREACH(elm, itemList) { - UiAbstractMenuItem *item = elm->data; - item->update(self, item->item_data); - } - - // update group items - // TODO: use only one loop for all items - int ngroups = 0; - int *groups = ui_active_groups(uiobj->ctx, &ngroups); - - NSArray *groupItems = [menu itemArray]; - int count = [groupItems count]; - for(int i=0;i<count;i++) { - id item = [groupItems objectAtIndex:i]; - if([item class] == [UiGroupMenuItem class]) { - [item checkGroups: groups count:ngroups]; - } - } - free(groups); -} - -@end - - -/* ------------------------------ public API ------------------------------ */ - -UiObject* ui_window(char *title, void *window_data) { - UcxMempool *mp = ucx_mempool_new(256); - UiObject *obj = ucx_mempool_calloc(mp, 1, sizeof(UiObject)); obj->ctx = uic_context(obj, mp); - // create native window - NSRect frame = NSMakeRect( - 300, - 200, - window_default_width, - window_default_height); + MainWindow *window = [[MainWindow alloc] init:obj]; + [[WindowManager sharedWindowManager] addWindow:window]; + window.releasedWhenClosed = false; - /* - UiCocoaWindow *window = [[UiCocoaWindow alloc] initWithContentRect:frame - styleMask:NSTitledWindowMask | NSResizableWindowMask | - NSClosableWindowMask | NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered - defer:false]; - */ - UiCocoaWindow *window = [[UiCocoaWindow alloc] init:frame object:obj]; - - NSString *titleStr = [[NSString alloc] initWithUTF8String:title]; - [window setTitle:titleStr]; - - UiMenuDelegate *menuDelegate = ui_menu_delegate(); - [window setMenuItems: [menuDelegate items]]; - [window setMenuItemLists: [menuDelegate lists]]; - - NSToolbar *toolbar = ui_create_toolbar(obj); - [window setToolbar: toolbar]; - - obj->widget = (NSView*)window; - obj->window = window_data; - obj->container = ui_window_container(obj, window); - + obj->wobj = (__bridge void*)window; return obj; } -void ui_close(UiObject *obj) { - // TODO +UiObject* ui_window(const char *title, void *window_data) { + UiObject *obj = create_window(title, FALSE); + obj->window = window_data; + return obj; } -char* ui_openfiledialog(UiObject *obj) { - NSOpenPanel* op = [NSOpenPanel openPanel]; - if ([op runModal] == NSOKButton) { - NSArray *urls = [op URLs]; - NSURL *url = [urls objectAtIndex:0]; - - const char *str = [[url path] UTF8String]; - return (char*)strdup(str); - } - return NULL; +UiObject* ui_simple_window(const char *title, void *window_data) { + UiObject *obj = create_window(title, TRUE); + obj->window = window_data; + return obj; } - -char* ui_savefiledialog(UiObject *obj) { - NSSavePanel* sp = [NSSavePanel savePanel]; - if ([sp runModal] == NSOKButton) { - NSURL *url = [sp URL]; - - const char *str = [[url path] UTF8String]; - return (char*)strdup(str); - } - return NULL; -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/condvar.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,70 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "condvar.h" + +#include <stdlib.h> + +UiCondVar* ui_condvar_create(void) { + UiPosixCondVar *var = malloc(sizeof(UiPosixCondVar)); + var->var.data = NULL; + var->var.intdata = 0; + var->set = 0; + pthread_mutex_init(&var->lock, NULL); + pthread_cond_init(&var->cond, NULL); + return (UiCondVar*)var; +} + +void ui_condvar_wait(UiCondVar *var) { + UiPosixCondVar *p = (UiPosixCondVar*)var; + pthread_mutex_lock(&p->lock); + if(!p->set) { + pthread_cond_wait(&p->cond, &p->lock); + } + p->set = 0; + pthread_mutex_unlock(&p->lock); + +} + +void ui_condvar_signal(UiCondVar *var, void *data, int intdata) { + UiPosixCondVar *p = (UiPosixCondVar*)var; + pthread_mutex_lock(&p->lock); + p->var.data = data; + p->var.intdata = intdata; + p->set = 1; + pthread_cond_signal(&p->cond); + pthread_mutex_unlock(&p->lock); +} + +void ui_condvar_destroy(UiCondVar *var) { + UiPosixCondVar *p = (UiPosixCondVar*)var; + pthread_mutex_destroy(&p->lock); + pthread_cond_destroy(&p->cond); + free(p); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/condvar.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UIC_CONDVAR_H +#define UIC_CONDVAR_H + +#include "../ui/toolkit.h" + +#include <pthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiPosixCondVar { + UiCondVar var; + int set; + pthread_mutex_t lock; + pthread_cond_t cond; +} UiPosixCondVar; + + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_CONDVAR_H */ +
--- a/ui/common/context.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/context.c Sat Jan 04 16:38:48 2025 +0100 @@ -32,15 +32,20 @@ #include <inttypes.h> #include <stdarg.h> +#include <cx/array_list.h> +#include <cx/compare.h> +#include <cx/mempool.h> + #include "context.h" #include "../ui/window.h" #include "document.h" #include "types.h" + static UiContext* global_context; void uic_init_global_context(void) { - UcxMempool *mp = ucx_mempool_new(32); + CxMempool *mp = cxBasicMempoolCreate(32); global_context = uic_context(NULL, mp); } @@ -48,17 +53,22 @@ return global_context; } -UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) { - UiContext *ctx = ucx_mempool_malloc(mp, sizeof(UiContext)); +UiContext* uic_context(UiObject *toplevel, CxMempool *mp) { + UiContext *ctx = cxMalloc(mp->allocator, sizeof(UiContext)); memset(ctx, 0, sizeof(UiContext)); - ctx->mempool = mp; + ctx->mp = mp; + ctx->allocator = mp->allocator; ctx->obj = toplevel; - ctx->vars = ucx_map_new_a(mp->allocator, 16); + ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); + + ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_intptr, CX_STORE_POINTERS); + ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget)); + ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document2; -#ifdef UI_GTK +#if UI_GTK2 || UI_GTK3 if(toplevel && toplevel->widget) { ctx->accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(toplevel->widget), ctx->accel_group); @@ -72,9 +82,14 @@ return ctx->parent ? uic_root_context(ctx->parent) : ctx; } +void uic_context_prepare_close(UiContext *ctx) { + cxListClear(ctx->groups); + cxListClear(ctx->group_widgets); +} + void uic_context_attach_document(UiContext *ctx, void *document) { - ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); - ctx->document = ctx->documents->data; + cxListAdd(ctx->documents, document); + ctx->document = document; UiContext *doc_ctx = ui_document_context(document); @@ -82,25 +97,18 @@ // as any document variable UiContext *var_ctx = ctx; while(var_ctx) { - if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { - UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); - UiVar *var; - // rmkeys holds all keys, that shall be removed from vars_unbound - UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); - size_t numkeys = 0; - UCX_MAP_FOREACH(key, var, i) { - UiVar *docvar = ucx_map_get(doc_ctx->vars, key); + if(var_ctx->vars_unbound && cxMapSize(var_ctx->vars_unbound) > 0) { + CxIterator i = cxMapIterator(var_ctx->vars_unbound); + cx_foreach(CxMapEntry*, entry, i) { + printf("attach %s\n", entry->key->data); + UiVar *var = entry->value; + UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) { // bind var to document var uic_copy_binding(var, docvar, TRUE); - rmkeys[numkeys++] = key; // save the key for removal + cxIteratorFlagRemoval(i); } } - // now that we may have bound some vars to the document, - // we can remove them from the unbound map - for(size_t k=0;k<numkeys;k++) { - ucx_map_remove(var_ctx->vars_unbound, rmkeys[k]); - } } var_ctx = ctx->parent; @@ -108,56 +116,63 @@ } static void uic_context_unbind_vars(UiContext *ctx) { - UcxMapIterator i = ucx_map_iterator(ctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { + CxIterator i = cxMapIterator(ctx->vars); + cx_foreach(CxMapEntry*, entry, i) { + UiVar *var = entry->value; if(var->from && var->from_ctx) { uic_save_var2(var); uic_copy_binding(var, var->from, FALSE); - ucx_map_put(var->from_ctx->vars_unbound, key, var->from); + cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from); var->from_ctx = ctx; } } - UCX_FOREACH(elm, ctx->documents) { - UiContext *subctx = ui_document_context(elm->data); - uic_context_unbind_vars(subctx); + if(ctx->documents) { + i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + UiContext *subctx = ui_document_context(doc); + uic_context_unbind_vars(subctx); + } } } void uic_context_detach_document2(UiContext *ctx, void *document) { // find the document in the documents list - UcxList *doc = NULL; - UCX_FOREACH(elm, ctx->documents) { - if(elm->data == document) { - doc = elm; - break; - } - } - if(!doc) { - return; // document is not a subdocument of this context + ssize_t docIndex = cxListFind(ctx->documents, document); + if(docIndex < 0) { + return; } - ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc); - ctx->document = ctx->documents ? ctx->documents->data : NULL; + cxListRemove(ctx->documents, docIndex); + ctx->document = cxListAt(ctx->documents, 0); UiContext *docctx = ui_document_context(document); uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent } void uic_context_detach_all(UiContext *ctx) { - UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); - UCX_FOREACH(elm, ls) { - ctx->detach_document2(ctx, elm->data); + // copy list + CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + CxIterator i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + cxListAdd(ls, doc); } - ucx_list_free(ls); + + // detach documents + i = cxListIterator(ls); + cx_foreach(void *, doc, i) { + ctx->detach_document2(ctx, doc); + } + + cxListDestroy(ls); } -static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { - UiVar *var = ucx_map_get(ctx->vars, key); - if(!var) { - UCX_FOREACH(elm, ctx->documents) { - UiContext *subctx = ui_document_context(elm->data); +static UiVar* ctx_getvar(UiContext *ctx, CxHashKey key) { + UiVar *var = cxMapGet(ctx->vars, key); + if(!var && ctx->documents) { + CxIterator i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + UiContext *subctx = ui_document_context(doc); var = ctx_getvar(subctx, key); if(var) { break; @@ -168,11 +183,18 @@ } UiVar* uic_get_var(UiContext *ctx, const char *name) { - UcxKey key = ucx_key(name, strlen(name)); + CxHashKey key = cx_hash_key(name, strlen(name)); return ctx_getvar(ctx, key); } UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { + if(ctx->vars_unbound) { + UiVar *unbound = cxMapGet(ctx->vars_unbound, name); + if(unbound) { + return unbound; + } + } + UiVar *var = uic_get_var(ctx, name); if(var) { if(var->type == type) { @@ -188,14 +210,25 @@ var->from = NULL; var->from_ctx = ctx; + cxMempoolSetDestructor(var, (cx_destructor_func)uic_unbind_var); + if(!ctx->vars_unbound) { - ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16); + ctx->vars_unbound = cxHashMapCreate(ctx->allocator, CX_STORE_POINTERS, 16); } - ucx_map_cstr_put(ctx->vars_unbound, name, var); + cxMapPut(ctx->vars_unbound, name, var); return var; } +UiVar* uic_create_value_var(UiContext* ctx, void* value) { + UiVar *var = (UiVar*)ui_malloc(ctx, sizeof(UiVar)); + var->from = NULL; + var->from_ctx = ctx; + var->value = value; + var->type = UI_VAR_SPECIAL; + return var; +} + void* uic_create_value(UiContext *ctx, UiVarType type) { void *val = NULL; switch(type) { @@ -224,10 +257,25 @@ val = ui_range_new(ctx, NULL); break; } + case UI_VAR_GENERIC: { + val = ui_generic_new(ctx, NULL); + } } return val; } + +UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type) { + if (value) { + return uic_create_value_var(current, value); + } + if (varname) { + return uic_create_var(toplevel, varname, type); + } + return NULL; +} + + void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { // check type if(from->type != to->type) { @@ -283,11 +331,24 @@ break; } case UI_VAR_LIST: { - UiList *f = fromvalue; + // TODO: not sure how correct this is + + UiList *f = from->value; UiList *t = to->value; - if(!f->obj) break; - uic_list_copy(f, t); - t->update(t, -1); + if (f->obj) { + t->obj = f->obj; + t->update = f->update; + t->getselection = f->getselection; + t->setselection = f->setselection; + } + + UiVar tmp = *from; + *from = *to; + *to = tmp; + + UiList* t2 = to->value; + ui_notify(t2->observers, NULL); + break; } case UI_VAR_RANGE: { @@ -300,6 +361,14 @@ t->set(t, t->value); break; } + case UI_VAR_GENERIC: { + UiGeneric *f = fromvalue; + UiGeneric *t = to->value; + if(!f->obj) break; + uic_generic_copy(f, t); + t->set(t, t->value, t->type); + break; + } } } @@ -312,6 +381,7 @@ case UI_VAR_TEXT: uic_text_save(var->value); break; case UI_VAR_LIST: break; case UI_VAR_RANGE: uic_range_save(var->value); break; + case UI_VAR_GENERIC: uic_generic_save(var->value); break; } } @@ -324,6 +394,7 @@ case UI_VAR_TEXT: uic_text_unbind(var->value); break; case UI_VAR_LIST: uic_list_unbind(var->value); break; case UI_VAR_RANGE: uic_range_unbind(var->value); break; + case UI_VAR_GENERIC: uic_generic_unbind(var->value); break; } } @@ -358,9 +429,9 @@ var->value = value; var->from = NULL; var->from_ctx = ctx; - size_t oldcount = ctx->vars->count; - ucx_map_cstr_put(ctx->vars, name, var); - if(ctx->vars->count != oldcount + 1) { + size_t oldcount = cxMapSize(ctx->vars); + cxMapPut(ctx->vars, name, var); + if(cxMapSize(ctx->vars) != oldcount + 1) { fprintf(stderr, "UiError: var '%s' already exists\n", name); } @@ -375,8 +446,7 @@ } void uic_remove_bound_var(UiContext *ctx, UiVar *var) { - // TODO: implement - printf("TODO: implement uic_remove_bound_var\n"); + // TODO } @@ -395,10 +465,14 @@ ctx->close_data = udata; } +UIEXPORT void ui_context_destroy(UiContext *ctx) { + cxMempoolDestroy(ctx->mp); +} + void ui_set_group(UiContext *ctx, int group) { - if(ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL) == -1) { - ctx->groups = ucx_list_append_a(ctx->mempool->allocator, ctx->groups, (void*)(intptr_t)group); + if(cxListFind(ctx->groups, &group) == -1) { + cxListAdd(ctx->groups, &group); } // enable/disable group widgets @@ -406,10 +480,9 @@ } void ui_unset_group(UiContext *ctx, int group) { - int i = ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL); + int i = cxListFind(ctx->groups, &group); if(i != -1) { - UcxList *elm = ucx_list_get(ctx->groups, i); - ctx->groups = ucx_list_remove_a(ctx->mempool->allocator, ctx->groups, elm); + cxListRemove(ctx->groups, i); } // enable/disable group widgets @@ -417,28 +490,16 @@ } int* ui_active_groups(UiContext *ctx, int *ngroups) { - if(!ctx->groups) { - return NULL; - } - - int nelm = ucx_list_size(ctx->groups); - int *groups = calloc(sizeof(int), nelm); - - int i = 0; - UCX_FOREACH(elm, ctx->groups) { - groups[i++] = (intptr_t)elm->data; - } - - *ngroups = nelm; - return groups; + *ngroups = cxListSize(ctx->groups); + return cxListAt(ctx->groups, 0); } void uic_check_group_widgets(UiContext *ctx) { int ngroups = 0; int *groups = ui_active_groups(ctx, &ngroups); - UCX_FOREACH(elm, ctx->group_widgets) { - UiGroupWidget *gw = elm->data; + CxIterator i = cxListIterator(ctx->group_widgets); + cx_foreach(UiGroupWidget *, gw, i) { char *check = calloc(1, gw->numgroups); for(int i=0;i<ngroups;i++) { @@ -456,64 +517,89 @@ break; } } + free(check); gw->enable(gw->widget, enable); } - - if(groups) { - free(groups); - } } void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { // get groups - UcxList *groups = NULL; + CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); va_list ap; va_start(ap, enable); int group; while((group = va_arg(ap, int)) != -1) { - groups = ucx_list_append(groups, (void*)(intptr_t)group); + cxListAdd(groups, &group); } va_end(ap); uic_add_group_widget(ctx, widget, enable, groups); - ucx_list_free(groups); + cxListDestroy(groups); +} + +size_t uic_group_array_size(const int *groups) { + int i; + for(i=0;groups[i] >= 0;i++) { } + return i; +} + +void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) { + uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups)); } -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups) { - UcxMempool *mp = ctx->mempool; - UiGroupWidget *gw = ucx_mempool_malloc(mp, sizeof(UiGroupWidget)); +void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) { + const CxAllocator *a = ctx->allocator; + UiGroupWidget gw; - gw->widget = widget; - gw->enable = enable; - gw->numgroups = ucx_list_size(groups); - gw->groups = ucx_mempool_calloc(mp, gw->numgroups, sizeof(int)); - int i = 0; - UCX_FOREACH(elm, groups) { - gw->groups[i++] = (intptr_t)elm->data; + gw.widget = widget; + gw.enable = enable; + gw.numgroups = numgroups; + gw.groups = cxCalloc(a, numgroups, sizeof(int)); + + // copy groups + if(groups) { + memcpy(gw.groups, groups, gw.numgroups * sizeof(int)); } - ctx->group_widgets = ucx_list_append_a( - mp->allocator, - ctx->group_widgets, - gw); + cxListAdd(ctx->group_widgets, &gw); +} + +void uic_remove_group_widget(UiContext *ctx, void *widget) { + (void)cxListFindRemove(ctx->group_widgets, widget); +} + +UIEXPORT void *ui_allocator(UiContext *ctx) { + return (void*)ctx->allocator; +} + +void* ui_cx_mempool(UiContext *ctx) { + return ctx->mp; } void* ui_malloc(UiContext *ctx, size_t size) { - return ctx ? ucx_mempool_malloc(ctx->mempool, size) : NULL; + return ctx ? cxMalloc(ctx->allocator, size) : NULL; } void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { - return ctx ? ucx_mempool_calloc(ctx->mempool, nelem, elsize) : NULL; + return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : NULL; } void ui_free(UiContext *ctx, void *ptr) { - if(ctx) { - ucx_mempool_free(ctx->mempool, ptr); + if(ctx && ptr) { + cxFree(ctx->allocator, ptr); } } void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { - return ctx ? ucx_mempool_realloc(ctx->mempool, ptr, size) : NULL; + return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; } +UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { + if(!ctx) { + return NULL; + } + cxstring s = cx_str(str); + cxmutstr d = cx_strdup_a(ctx->allocator, s); + return d.ptr; +}
--- a/ui/common/context.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/context.h Sat Jan 04 16:38:48 2025 +0100 @@ -30,9 +30,11 @@ #define UIC_CONTEXT_H #include "../ui/toolkit.h" -#include <ucx/map.h> -#include <ucx/mempool.h> -#include <ucx/list.h> +#include <cx/map.h> +#include <cx/hash_map.h> +#include <cx/mempool.h> +#include <cx/list.h> +#include <cx/linked_list.h> #ifdef __cplusplus extern "C" { @@ -52,22 +54,24 @@ UI_VAR_STRING, UI_VAR_TEXT, UI_VAR_LIST, - UI_VAR_RANGE + UI_VAR_RANGE, + UI_VAR_GENERIC }; struct UiContext { UiContext *parent; UiObject *obj; - UcxMempool *mempool; + CxMempool *mp; + const CxAllocator *allocator; void *document; - UcxList *documents; + CxList *documents; - UcxMap *vars; // manually created context vars - UcxMap *vars_unbound; // unbound vars created by widgets + CxMap *vars; // manually created context vars + CxMap *vars_unbound; // unbound vars created by widgets - UcxList *groups; // int list - UcxList *group_widgets; // UiGroupWidget* list + CxList *groups; // int list + CxList *group_widgets; // UiGroupWidget list void (*attach_document)(UiContext *ctx, void *document); void (*detach_document2)(UiContext *ctx, void *document); @@ -75,8 +79,14 @@ char *title; #ifdef UI_GTK +#if GTK_CHECK_VERSION(4, 0, 0) + GActionMap *action_map; +#elif UI_GTK2 || UI_GTK3 GtkAccelGroup *accel_group; +#endif #endif + + ui_callback close_callback; void *close_data; @@ -100,19 +110,24 @@ void uic_init_global_context(void); -UiContext* uic_context(UiObject *toplevel, UcxMempool *mp); +UiContext* uic_context(UiObject *toplevel, CxMempool *mp); UiContext* uic_root_context(UiContext *ctx); void uic_context_set_document(UiContext *ctx, void *document); // deprecated void uic_context_detach_document(UiContext *ctx); // deprecated +void uic_context_prepare_close(UiContext *ctx); + void uic_context_attach_document(UiContext *ctx, void *document); void uic_context_detach_document2(UiContext *ctx, void *document); void uic_context_detach_all(UiContext *ctx); UiVar* uic_get_var(UiContext *ctx, const char *name); UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type); +UiVar* uic_create_value_var(UiContext *ctx, void *value); void* uic_create_value(UiContext *ctx, UiVarType type); +UiVar* uic_widget_var(UiContext* toplevel, UiContext* current, void* value, const char* varname, UiVarType type); + void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc); void uic_save_var2(UiVar *var); void uic_unbind_var(UiVar *var); @@ -121,9 +136,11 @@ void uic_remove_bound_var(UiContext *ctx, UiVar *var); +size_t uic_group_array_size(const int *groups); void uic_check_group_widgets(UiContext *ctx); -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups); - +void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups); +void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups); +void uic_remove_group_widget(UiContext *ctx, void *widget); #ifdef __cplusplus }
--- a/ui/common/document.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/document.c Sat Jan 04 16:38:48 2025 +0100 @@ -32,10 +32,16 @@ #include "document.h" -static UcxMap *documents; +#include <cx/hash_map.h> +#include <cx/mempool.h> + + +static CxMap *documents; void uic_docmgr_init() { - documents = ucx_map_new(32); + if (!documents) { + documents = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); + } } void ui_set_document(UiObject *obj, void *document) { @@ -77,25 +83,40 @@ } void* ui_document_new(size_t size) { - UcxMempool *mp = ucx_mempool_new(256); - UiContext *ctx = ucx_mempool_calloc(mp, 1, sizeof(UiContext)); + CxMempool *mp = cxMempoolCreate(256, NULL); + const CxAllocator *a = mp->allocator; + UiContext *ctx = cxCalloc(a, 1, sizeof(UiContext)); + ctx->mp = mp; ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document2; - ctx->mempool = mp; - ctx->vars = ucx_map_new_a(mp->allocator, 16); + ctx->allocator = a; + ctx->vars = cxHashMapCreate(a, CX_STORE_POINTERS, 16); - void *document = ucx_mempool_calloc(mp, 1, size); - ucx_map_put(documents, ucx_key(&document, sizeof(void*)), ctx); + void *document = cxCalloc(a, size, 1); + cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx); return document; } void ui_document_destroy(void *doc) { - // TODO + UiContext *ctx = ui_document_context(doc); + if(ctx) { + UiEvent ev; + ev.window = NULL; + ev.document = doc; + ev.obj = NULL; + ev.eventdata = NULL; + ev.intval = 0; + + if(ctx->close_callback) { + ctx->close_callback(&ev, ctx->close_data); + } + cxMempoolDestroy(ctx->mp); + } } UiContext* ui_document_context(void *doc) { if(doc) { - return ucx_map_get(documents, ucx_key(&doc, sizeof(void*))); + return cxMapGet(documents, cx_hash_key(&doc, sizeof(void*))); } else { return NULL; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/menu.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,331 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "menu.h" + +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +#include <cx/linked_list.h> +#include <cx/array_list.h> + + +static UiMenuBuilder *current_builder; +static UiMenuBuilder global_builder; + +static int menu_item_counter = 0; + +void uic_menu_init(void) { + global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + current_builder = &global_builder; +} + +static void add_menu(UiMenu *menu) { + cx_linked_list_add( + (void**)¤t_builder->menus_begin, + (void**)¤t_builder->menus_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + menu); +} + +static void add_item(UiMenuItemI *item) { + UiMenu *menu = cxListAt(current_builder->current, 0); + cx_linked_list_add( + (void**)&menu->items_begin, + (void**)&menu->items_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + item); +} + +static void mitem_set_id(UiMenuItemI *item) { + snprintf(item->id, 8, "%x", menu_item_counter++); +} + +static char* nl_strdup(const char* s) { + return s ? strdup(s) : NULL; +} + +int* uic_copy_groups(const int* groups, size_t *ngroups) { + *ngroups = 0; + if (!groups) { + return NULL; + } + + size_t n; + for (n = 0; groups[n] > -1; n++) { } + + if (ngroups > 0) { + int* newarray = calloc(n+1, sizeof(int)); + memcpy(newarray, groups, n * sizeof(int)); + newarray[n] = -1; + *ngroups = n; + return newarray; + } + return NULL; +} + +void ui_menu_create(const char *label) { + // create menu + UiMenu *menu = malloc(sizeof(UiMenu)); + mitem_set_id(&menu->item); + menu->item.prev = NULL; + menu->item.next = NULL; + menu->item.type = UI_MENU; + + menu->label = label; + menu->items_begin = NULL; + menu->items_end = NULL; + menu->parent = NULL; + + menu->end = 0; + + if (cxListSize(current_builder->current) == 0) { + add_menu(menu); + } + else { + add_item((UiMenuItemI*)menu); + } + uic_add_menu_to_stack(menu); +} + +UIEXPORT void ui_menu_end(void) { + cxListRemove(current_builder->current, 0); + if(cxListSize(current_builder->current) == 0) { + current_builder = &global_builder; + } +} + + + +void ui_menuitem_create(UiMenuItemArgs args) { + UiMenuItem* item = malloc(sizeof(UiMenuItem)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->userdata = args.onclickdata; + item->callback = args.onclick; + item->groups = uic_copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menuseparator() { + UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); + item->id[0] = 0; + item->prev = NULL; + item->next = NULL; + item->type = UI_MENU_SEPARATOR; + + add_item((UiMenuItemI*)item); +} + +void ui_menu_toggleitem_create(UiMenuToggleItemArgs args) { + UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECK_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->varname = nl_strdup(args.varname); + item->userdata = args.onchangedata; + item->callback = args.onchange; + item->groups = uic_copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_radioitem_create(UiMenuToggleItemArgs args) { + UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_RADIO_ITEM; + + item->label = nl_strdup(args.label); + item->stockid = nl_strdup(args.stockid); + item->icon = nl_strdup(args.icon); + item->varname = nl_strdup(args.varname); + item->userdata = args.onchangedata; + item->callback = args.onchange; + item->groups = uic_copy_groups(args.groups, &item->ngroups); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_itemlist_create(UiMenuItemListArgs args) { + UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_ITEM_LIST; + item->getvalue = args.getvalue; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + item->addseparator = args.addseparator; + + add_item((UiMenuItemI*)item); +} + +void ui_menu_checkitemlist_create(UiMenuItemListArgs args) { + UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_CHECKITEM_LIST; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + + add_item((UiMenuItemI*)item); +} + +void ui_menu_radioitemlist_create(UiMenuItemListArgs args) { + UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); + item->item.prev = NULL; + item->item.next = NULL; + item->item.type = UI_MENU_RADIOITEM_LIST; + item->callback = args.onselect; + item->userdata = args.onselectdata; + item->varname = nl_strdup(args.varname); + + add_item((UiMenuItemI*)item); +} + + +void uic_add_menu_to_stack(UiMenu* menu) { + cxListInsert(current_builder->current, 0, menu); +} + +UiMenu* uic_get_menu_list(void) { + return current_builder->menus_begin; +} + +UIEXPORT void ui_menu_close(void) { + UiMenu* menu = cxListAt(current_builder->current, 0); + menu->end = 1; +} + +UIEXPORT int ui_menu_is_open(void) { + UiMenu* menu = cxListAt(current_builder->current, 0); + if (menu->end) { + ui_menu_end(); + return 0; + } + return 1; +} + + +void ui_contextmenu_builder(UiMenuBuilder **out_builder) { + UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder)); + builder->menus_begin = NULL; + builder->menus_end = NULL; + builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + current_builder = builder; + *out_builder = builder; + + ui_menu_create(NULL); +} + + + +static void free_menuitem(UiMenuItemI *item) { + switch(item->type) { + default: break; + case UI_MENU: { + UiMenu *menu = (UiMenu*)item; + UiMenuItemI *m = menu->items_begin; + while(m) { + UiMenuItemI *next = m->next; + free_menuitem(m); + m = next; + } + break; + } + case UI_MENU_ITEM: { + UiMenuItem *i = (UiMenuItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + break; + } + case UI_MENU_CHECK_ITEM: { + UiMenuCheckItem *i = (UiMenuCheckItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + free(i->varname); + break; + } + case UI_MENU_RADIO_ITEM: { + UiMenuRadioItem *i = (UiMenuRadioItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + //free(i->varname); + break; + } + case UI_MENU_ITEM_LIST: { + break; + } + case UI_MENU_CHECKITEM_LIST: { + break; + } + case UI_MENU_RADIOITEM_LIST: { + break; + } + } + + free(item); +} + +void ui_menubuilder_free(UiMenuBuilder *builder) { + UiMenuItemI *m = &builder->menus_begin->item; + while(m) { + UiMenuItemI *next = m->next; + free_menuitem(m); + m = next; + } + cxListDestroy(builder->current); + free(builder); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/menu.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,141 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UIC_MENU_H +#define UIC_MENU_H + +#include "../ui/menu.h" + +#include <cx/linked_list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiMenuItemI UiMenuItemI; +typedef struct UiMenu UiMenu; +typedef struct UiMenuItem UiMenuItem; +typedef struct UiMenuCheckItem UiMenuCheckItem; +typedef struct UiMenuRadioItem UiMenuRadioItem; +typedef struct UiMenuItemList UiMenuItemList; + +enum UiMenuItemType { + UI_MENU = 0, + UI_MENU_ITEM, + UI_MENU_CHECK_ITEM, + UI_MENU_RADIO_ITEM, + UI_MENU_ITEM_LIST, + UI_MENU_CHECKITEM_LIST, + UI_MENU_RADIOITEM_LIST, + UI_MENU_SEPARATOR +}; + +typedef enum UiMenuItemType UiMenuItemType; + +struct UiMenuItemI { + UiMenuItemI *prev; + UiMenuItemI *next; + UiMenuItemType type; + char id[8]; +}; + +struct UiMenu { + UiMenuItemI item; + const char *label; + UiMenuItemI *items_begin; + UiMenuItemI *items_end; + UiMenu *parent; + int end; +}; + +struct UiMenuItem { + UiMenuItemI item; + ui_callback callback; + char *label; + char *stockid; + char *icon; + void *userdata; + int *groups; + size_t ngroups; +}; + +struct UiMenuCheckItem { + UiMenuItemI item; + char *label; + char *stockid; + char *icon; + char *varname; + ui_callback callback; + void *userdata; + int *groups; + size_t ngroups; +}; + +struct UiMenuRadioItem { + UiMenuItemI item; + char *label; + char *stockid; + char *icon; + char *varname; + ui_callback callback; + void *userdata; + int *groups; + size_t ngroups; +}; + +struct UiMenuItemList { + UiMenuItemI item; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; + char *varname; + UiBool addseparator; +}; + + + +struct UiMenuBuilder { + UiMenu *menus_begin; + UiMenu *menus_end; + CxList *current; +}; + +void uic_menu_init(void); + +UiMenu* uic_get_menu_list(void); + +void uic_add_menu_to_stack(UiMenu* menu); + +int* uic_copy_groups(const int* groups, size_t *ngroups); + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_MENU_H */ +
--- a/ui/common/object.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/object.c Sat Jan 04 16:38:48 2025 +0100 @@ -32,6 +32,8 @@ #include "object.h" #include "context.h" +#include "../ui/container.h" + void ui_end(UiObject *obj) { if(!obj->next) { return; @@ -49,11 +51,51 @@ } } +void ui_end_new(UiObject *obj) { + if(!obj->container_end) { + return; + } + UiContainerX *rm = obj->container_end; + uic_object_pop_container(obj); + ui_free(obj->ctx, rm); +} + +void ui_object_ref(UiObject *obj) { + obj->ref++; +} + +int ui_object_unref(UiObject *obj) { + // it is possible to have 0 references, in case + // a window was created but ui_show was never called + if(obj->ref == 0 || --obj->ref == 0) { + if(obj->destroy) { + obj->destroy(obj); + } + uic_object_destroy(obj); + return 0; + } + return 1; +} + +void uic_object_destroy(UiObject *obj) { + if(obj->ctx->close_callback) { + UiEvent ev; + ev.window = obj->window; + ev.document = obj->ctx->document; + ev.obj = obj; + ev.eventdata = NULL; + ev.intval = 0; + obj->ctx->close_callback(&ev, obj->ctx->close_data); + } + cxMempoolDestroy(obj->ctx->mp); +} UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget) { - UiContext *ctx = toplevel->ctx; - - UiObject *newobj = ucx_mempool_calloc(ctx->mempool, 1, sizeof(UiObject)); + return uic_ctx_object_new(toplevel->ctx, widget); +} + +UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget) { + UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject)); newobj->ctx = ctx; newobj->widget = widget; @@ -79,3 +121,23 @@ UiContainer* uic_get_current_container(UiObject *obj) { return uic_current_obj(obj)->container; } + +void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer) { + newcontainer->prev = toplevel->container_end; + if(toplevel->container_end) { + toplevel->container_end->next = newcontainer; + toplevel->container_end = newcontainer; + } else { + toplevel->container_begin = newcontainer; + toplevel->container_end = newcontainer; + } +} + +void uic_object_pop_container(UiObject *toplevel) { + toplevel->container_end = toplevel->container_end->prev; + if(toplevel->container_end) { + toplevel->container_end->next = NULL; + } else { + toplevel->container_begin = NULL; + } +}
--- a/ui/common/object.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/object.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,11 +35,18 @@ extern "C" { #endif +void uic_object_destroy(UiObject *obj); + UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget); +UiObject* uic_ctx_object_new(UiContext *ctx, UIWIDGET widget); void uic_obj_add(UiObject *toplevel, UiObject *ctobj); UiObject* uic_current_obj(UiObject *toplevel); -UiContainer* uic_get_current_container(UiObject *obj); +UiContainer* uic_get_current_container(UiObject *obj); // deprecated + +void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer); +void uic_object_pop_container(UiObject *toplevel); + #ifdef __cplusplus
--- a/ui/common/objs.mk Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/objs.mk Sat Jan 04 16:38:48 2025 +0100 @@ -33,7 +33,13 @@ COMMON_OBJ += document.o COMMON_OBJ += object.o COMMON_OBJ += types.o +COMMON_OBJ += menu.o COMMON_OBJ += properties.o +COMMON_OBJ += menu.o +COMMON_OBJ += toolbar.o +COMMON_OBJ += ucx_properties.o +COMMON_OBJ += threadpool.o +COMMON_OBJ += condvar.o TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)%) TOOLKITSOURCE += $(COMMON_OBJ:%.o=common/%.c)
--- a/ui/common/properties.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/properties.c Sat Jan 04 16:38:48 2025 +0100 @@ -32,13 +32,18 @@ #include <sys/stat.h> #include <errno.h> +#include "../ui/toolkit.h" + + #include "properties.h" -#include <ucx/string.h> -#include <ucx/buffer.h> -#include <ucx/properties.h> +#include <cx/string.h> +#include <cx/buffer.h> -static UiProperties *application_properties; -static UiProperties *language; +#include <cx/hash_map.h> +#include "ucx_properties.h" + +static CxMap *application_properties; +static CxMap *language; #ifndef UI_COCOA @@ -47,53 +52,66 @@ #endif -char* ui_getappdir() { + +char* ui_getappdir(void) { if(ui_appname() == NULL) { return NULL; } - + return ui_configfile(NULL); } +#ifndef _WIN32 +#define UI_PATH_SEPARATOR '/' +#define UI_ENV_HOME "HOME" +#else +#define UI_PATH_SEPARATOR '\\' +#define UI_ENV_HOME "USERPROFILE" +#endif + char* ui_configfile(char *name) { - char *appname = ui_appname(); + const char *appname = ui_appname(); if(!appname) { return NULL; } - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // add base dir - char *homeenv = getenv("HOME"); + char *homeenv = getenv(UI_ENV_HOME); if(homeenv == NULL) { - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return NULL; } - sstr_t home = sstr(homeenv); + cxstring home = cx_str(homeenv); - ucx_buffer_write(home.ptr, 1, home.length, buf); - if(home.ptr[home.length-1] != '/') { - ucx_buffer_putc(buf, '/'); + cxBufferWrite(home.ptr, 1, home.length, &buf); + if(home.ptr[home.length-1] != UI_PATH_SEPARATOR) { + cxBufferPut(&buf, UI_PATH_SEPARATOR); } #ifdef UI_COCOA // on OS X the app dir is $HOME/Library/Application Support/$APPNAME/ - ucx_buffer_puts(buf, "Library/Application Support/"); + cxBufferPutString(&buf, "Library/Application Support/"); +#elif defined(_WIN32) + // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/ + cxBufferPutString(&buf, "AppData\\Local\\"); #else // app dir is $HOME/.$APPNAME/ - ucx_buffer_putc(buf, '.'); + cxBufferPut(&buf, '.'); #endif - ucx_buffer_puts(buf, appname); - ucx_buffer_putc(buf, '/'); + cxBufferPutString(&buf, appname); + cxBufferPut(&buf, UI_PATH_SEPARATOR); // add file name if(name) { - ucx_buffer_puts(buf, name); + cxBufferPutString(&buf, name); } - char *path = buf->space; - free(buf); - return path; + cxBufferPut(&buf, 0); + + return buf.space; } static int ui_mkdir(char *path) { @@ -105,7 +123,7 @@ } void uic_load_app_properties() { - application_properties = ucx_map_new(128); + application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128); if(!ui_appname()) { // applications without name cannot load app properties @@ -144,58 +162,67 @@ free(path); } -void uic_store_app_properties() { +int uic_store_app_properties() { char *path = ui_configfile("application.properties"); if(!path) { - return; + return 1; } FILE *file = fopen(path, "w"); if(!file) { fprintf(stderr, "Ui Error: Cannot open properties file: %s\n", path); free(path); - return; + return 1; } + int ret = 0; if(ucx_properties_store(application_properties, file)) { fprintf(stderr, "Ui Error: Cannot store application properties.\n"); + ret = 1; } fclose(file); free(path); + + return ret; } -char* ui_get_property(char *name) { - return ucx_map_cstr_get(application_properties, name); +const char* ui_get_property(const char *name) { + return cxMapGet(application_properties, name); } -void ui_set_property(char *name, char *value) { - ucx_map_cstr_put(application_properties, name, value); +void ui_set_property(const char *name, const char *value) { + cxMapPut(application_properties, name, (char*)value); } -void ui_set_default_property(char *name, char *value) { - char *v = ucx_map_cstr_get(application_properties, name); +const char* ui_set_default_property(const char *name, const char *value) { + const char *v = cxMapGet(application_properties, name); if(!v) { - ucx_map_cstr_put(application_properties, name, value); + cxMapPut(application_properties, name, (char*)value); + v = value; } + return v; } +int ui_properties_store(void) { + return uic_store_app_properties(); +} static char* uic_concat_path(const char *base, const char *p, const char *ext) { size_t baselen = strlen(base); - UcxBuffer *buf = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND); + CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(baselen > 0) { - ucx_buffer_write(base, 1, baselen, buf); + cxBufferWrite(base, 1, baselen, buf); if(base[baselen - 1] != '/') { - ucx_buffer_putc(buf, '/'); + cxBufferPut(buf, '/'); } } - ucx_buffer_write(p, 1, strlen(p), buf); + cxBufferWrite(p, 1, strlen(p), buf); if(ext) { - ucx_buffer_write(ext, 1, strlen(ext), buf); + cxBufferWrite(ext, 1, strlen(ext), buf); } char *str = buf->space; @@ -263,7 +290,7 @@ #endif int uic_load_language_file(const char *path) { - UcxMap *lang = ucx_map_new(256); + CxMap *lang = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 256); FILE *file = fopen(path, "r"); if(!file) { @@ -276,7 +303,7 @@ fclose(file); - ucx_map_rehash(lang); + cxMapRehash(lang); language = lang; @@ -292,6 +319,5 @@ if(!language) { return NULL; } - return ucx_map_cstr_get(language, name); + return cxMapGet(language, name); } -
--- a/ui/common/properties.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/properties.h Sat Jan 04 16:38:48 2025 +0100 @@ -30,8 +30,8 @@ #define UIC_PROPERTIES_H #include "../ui/properties.h" -#include <ucx/map.h> -#include <ucx/list.h> +#include <cx/hash_map.h> +#include <cx/linked_list.h> #ifdef __cplusplus extern "C" { @@ -44,7 +44,7 @@ #endif void uic_load_app_properties(); -void uic_store_app_properties(); +int uic_store_app_properties(); int uic_load_language_file(const char *path); char* uic_get_image_path(const char *imgfilename);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/threadpool.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,195 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "threadpool.h" +#include "context.h" + +#ifndef _WIN32 + +#include <pthread.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +static threadpool_job kill_job; + +UiThreadpool* threadpool_new(int min, int max) { + UiThreadpool *pool = malloc(sizeof(UiThreadpool)); + pool->queue = NULL; + pool->queue_len = 0; + pool->num_idle = 0; + pool->min_threads = min; + pool->max_threads = max; + + pthread_mutex_init(&pool->queue_lock, NULL); + pthread_mutex_init(&pool->avlbl_lock, NULL); + pthread_cond_init(&pool->available, NULL); + + return pool; +} + +int threadpool_start(UiThreadpool *pool) { + /* create pool threads */ + for(int i=0;i<pool->min_threads;i++) { + pthread_t t; + if (pthread_create(&t, NULL, threadpool_func, pool) != 0) { + fprintf(stderr, "uic: threadpool_start: pthread_create failed: %s", strerror(errno)); + return 1; + } + } + return 0; +} + +void* threadpool_func(void *data) { + UiThreadpool *pool = (UiThreadpool*)data; + + for(;;) { + threadpool_job *job = threadpool_get_job(pool); + if(job == &kill_job) { + break; + } + + job->callback(job->data); + + free(job); + } + return NULL; +} + +threadpool_job* threadpool_get_job(UiThreadpool *pool) { + pthread_mutex_lock(&pool->queue_lock); + + threadpool_job *job = NULL; + pool->num_idle++; + while(job == NULL) { + if(pool->queue_len == 0) { + pthread_cond_wait(&pool->available, &pool->queue_lock); + continue; + } else { + pool_queue_t *q = pool->queue; + job = q->job; + pool->queue = q->next; + pool->queue_len--; + free(q); + } + } + pool->num_idle--; + + pthread_mutex_unlock(&pool->queue_lock); + return job; +} + +void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) { + // TODO: handle errors + + threadpool_job *job = malloc(sizeof(threadpool_job)); + job->callback = func; + job->data = data; + + pthread_mutex_lock(&pool->queue_lock); + threadpool_enqueue_job(pool, job); + + int create_thread = 0; + int destroy_thread = 0; + int diff = pool->queue_len - pool->num_idle; + + //if(pool->queue_len == 1) { + pthread_cond_signal(&pool->available); + //} + + pthread_mutex_unlock(&pool->queue_lock); +} + +void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job) { + pool_queue_t *q = malloc(sizeof(pool_queue_t)); + q->job = job; + q->next = NULL; + + if(pool->queue == NULL) { + pool->queue = q; + } else { + pool_queue_t *last_elem = pool->queue; + while(last_elem->next != NULL) { + last_elem = last_elem->next; + } + last_elem->next = q; + } + pool->queue_len++; +} + + + + + + +UiThreadpool* ui_threadpool_create(int nthreads) { + UiThreadpool *pool = threadpool_new(nthreads, nthreads); + threadpool_start(pool); // TODO: check return value + return pool; +} + +void ui_threadpool_destroy(UiThreadpool* pool) { + +} + +static int ui_threadpool_job_finish(void *data) { + UiJob *job = data; + UiEvent event; + event.obj = job->obj; + event.window = job->obj->window; + event.document = job->obj->ctx->document; + event.intval = 0; + event.eventdata = NULL; + job->finish_callback(&event, job->finish_data); + free(job); + return 0; +} + +static void* ui_threadpool_job_func(void *data) { + UiJob *job = data; + if (!job->job_func(job->job_data) && job->finish_callback) { + ui_call_mainthread(ui_threadpool_job_finish, job); + } else { + free(job); + } + return NULL; +} + +void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { + UiJob* job = malloc(sizeof(UiJob)); + job->obj = obj; + job->job_func = tf; + job->job_data = td; + job->finish_callback = f; + job->finish_data = fd; + threadpool_run(pool, ui_threadpool_job_func, job); +} + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/threadpool.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UIC_THREADPOOL_H +#define UIC_THREADPOOL_H + +#include "../ui/toolkit.h" + +#ifndef _WIN32 +#include <pthread.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct UiJob { + UiObject *obj; + ui_threadfunc job_func; + void *job_data; + ui_callback finish_callback; + void *finish_data; +} UiJob; + + +typedef struct _threadpool_job threadpool_job; +typedef void*(*job_callback_f)(void *data); + +typedef struct _pool_queue pool_queue_t; +struct UiThreadpool { + pthread_mutex_t queue_lock; + pthread_mutex_t avlbl_lock; + pthread_cond_t available; + pool_queue_t *queue; + uint32_t queue_len; + uint32_t num_idle; + int min_threads; + int max_threads; +}; + +struct _threadpool_job { + job_callback_f callback; + void *data; +}; + +struct _pool_queue { + threadpool_job *job; + pool_queue_t *next; +}; + +UiThreadpool* threadpool_new(int min, int max); +int threadpool_start(UiThreadpool *pool); +void* threadpool_func(void *data); +threadpool_job* threadpool_get_job(UiThreadpool *pool); +void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data); +void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job); + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_THREADPOOL_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/toolbar.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,148 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "toolbar.h" +#include "menu.h" + +#include <string.h> + +static CxMap* toolbar_items; + +static CxList* toolbar_defaults[3]; // 0: left 1: center 2: right + +static UiToolbarMenuItem* ui_appmenu; + + +void uic_toolbar_init(void) { + toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + toolbar_defaults[0] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + toolbar_defaults[1] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + toolbar_defaults[2] = cxLinkedListCreateSimple(CX_STORE_POINTERS); +} + +static char* nl_strdup(const char* str) { + return str ? strdup(str) : NULL; +} + +static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs args, size_t *ngroups) { + UiToolbarItemArgs newargs; + newargs.label = nl_strdup(args.label); + newargs.stockid = nl_strdup(args.stockid); + newargs.icon = nl_strdup(args.icon); + newargs.onclick = args.onclick; + newargs.onclickdata = args.onclickdata; + newargs.groups = uic_copy_groups(args.groups, ngroups); + return newargs; +} + +void ui_toolbar_item_create(const char* name, UiToolbarItemArgs args) { + UiToolbarItem* item = malloc(sizeof(UiToolbarItem)); + item->item.type = UI_TOOLBAR_ITEM; + item->args = itemargs_copy(args, &item->ngroups); + cxMapPut(toolbar_items, name, item); +} + + +static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs args, size_t *ngroups) { + UiToolbarToggleItemArgs newargs; + newargs.label = nl_strdup(args.label); + newargs.stockid = nl_strdup(args.stockid); + newargs.icon = nl_strdup(args.icon); + newargs.varname = nl_strdup(args.varname); + newargs.onchange = args.onchange; + newargs.onchangedata = args.onchangedata; + newargs.groups = uic_copy_groups(args.groups, ngroups); + return newargs; +} + +void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs args) { + UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem)); + item->item.type = UI_TOOLBAR_TOGGLEITEM; + item->args = toggleitemargs_copy(args, &item->ngroups); + cxMapPut(toolbar_items, name, item); +} + +static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs args) { + UiToolbarMenuArgs newargs; + newargs.label = nl_strdup(args.label); + newargs.stockid = nl_strdup(args.stockid); + newargs.icon = nl_strdup(args.icon); + return newargs; +} + +UIEXPORT void ui_toolbar_menu_create(const char* name, UiToolbarMenuArgs args) { + UiToolbarMenuItem* item = malloc(sizeof(UiToolbarMenuItem)); + item->item.type = UI_TOOLBAR_MENU; + memset(&item->menu, 0, sizeof(UiMenu)); + item->args = menuargs_copy(args); + + item->end = 0; + + if (!name) { + // special appmenu + ui_appmenu = item; + } else { + // toplevel menu + cxMapPut(toolbar_items, name, item); + } + + uic_add_menu_to_stack(&item->menu); +} + + +CxMap* uic_get_toolbar_items(void) { + return toolbar_items; +} + +CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos) { + if (pos >= 0 && pos < 3) { + return toolbar_defaults[pos]; + } + return NULL; +} + +void ui_toolbar_add_default(const char* name, enum UiToolbarPos pos) { + char* cp = strdup(name); + if (pos >= 0 && pos < 3) { + cxListAdd(toolbar_defaults[pos], cp); + } else { + // TODO: error + } +} + +UiBool uic_toolbar_isenabled(void) { + return cxListSize(toolbar_defaults[0]) + cxListSize(toolbar_defaults[1]) + cxListSize(toolbar_defaults[2]) > 0; +} + +UiToolbarItemI* uic_toolbar_get_item(const char* name) { + return cxMapGet(toolbar_items, name); +} + +UiToolbarMenuItem* uic_get_appmenu(void) { + return ui_appmenu; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/toolbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UIC_TOOLBAR_H +#define UIC_TOOLBAR_H + +#include "../ui/toolbar.h" + +#include <cx/linked_list.h> +#include <cx/hash_map.h> + +#include "menu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiToolbarItemI UiToolbarItemI; + +typedef struct UiToolbarItem UiToolbarItem; +typedef struct UiToolbarToggleItem UiToolbarToggleItem; + +typedef struct UiToolbarMenuItem UiToolbarMenuItem; + +enum UiToolbarItemType { + UI_TOOLBAR_ITEM = 0, + UI_TOOLBAR_TOGGLEITEM, + UI_TOOLBAR_MENU +}; + +typedef enum UiToolbarItemType UiToolbarItemType; + +struct UiToolbarItemI { + UiToolbarItemType type; +}; + +struct UiToolbarItem { + UiToolbarItemI item; + UiToolbarItemArgs args; + size_t ngroups; +}; + +struct UiToolbarToggleItem { + UiToolbarItemI item; + UiToolbarToggleItemArgs args; + size_t ngroups; +}; + +struct UiToolbarMenuItem { + UiToolbarItemI item; + UiMenu menu; + UiToolbarMenuArgs args; + int end; +}; + + +void uic_toolbar_init(void); + +CxMap* uic_get_toolbar_items(void); +CxList* uic_get_toolbar_defaults(enum UiToolbarPos pos); + +UiBool uic_toolbar_isenabled(void); + +UiToolbarItemI* uic_toolbar_get_item(const char* name); + +UiToolbarMenuItem* uic_get_appmenu(void); + +#ifdef __cplusplus +} +#endif + +#endif /* UIC_TOOLBAR_H */ +
--- a/ui/common/types.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/types.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,11 +31,14 @@ #include <string.h> #include <stdarg.h> -#include <ucx/list.h> +#include <cx/list.h> +#include <cx/array_list.h> #include "../ui/tree.h" #include "types.h" #include "context.h" + + UiObserver* ui_observer_new(ui_callback f, void *data) { UiObserver *observer = malloc(sizeof(UiObserver)); observer->callback = f; @@ -99,10 +102,11 @@ list->count = ui_list_count; list->observers = NULL; - list->data = NULL; + list->data = cxArrayListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS, 32); list->iter = NULL; list->update = NULL; + list->getselection = NULL; list->obj = NULL; if(name) { @@ -113,54 +117,53 @@ } void ui_list_free(UiList *list) { - ucx_list_free(list->data); + cxListDestroy(list->data); free(list); } void* ui_list_first(UiList *list) { - UcxList *elm = list->data; - list->iter = elm; - return elm ? elm->data : NULL; + list->iter = (void*)(intptr_t)0; + return cxListAt(list->data, 0); } void* ui_list_next(UiList *list) { - UcxList *elm = list->iter; + intptr_t iter = (intptr_t)list->iter; + iter++; + void *elm = cxListAt(list->data, iter); if(elm) { - elm = elm->next; - if(elm) { - list->iter = elm; - return elm->data; - } + list->iter = (void*)iter; } - return NULL; + return elm; } void* ui_list_get(UiList *list, int i) { - UcxList *elm = ucx_list_get(list->data, i); - if(elm) { - list->iter = elm; - return elm->data; - } else { - return NULL; - } + return cxListAt(list->data, i); } int ui_list_count(UiList *list) { - UcxList *elm = list->data; - return (int)ucx_list_size(elm); + return cxListSize(list->data); } void ui_list_append(UiList *list, void *data) { - list->data = ucx_list_append(list->data, data); + cxListAdd(list->data, data); } void ui_list_prepend(UiList *list, void *data) { - list->data = ucx_list_prepend(list->data, data); + cxListInsert(list->data, 0, data); +} + +void ui_list_remove(UiList *list, int i) { + cxListRemove(list->data, i); } void ui_list_clear(UiList *list) { - ucx_list_free(list->data); - list->data = NULL; + cxListClear(list->data); +} + +UIEXPORT void ui_list_update(UiList *list) { + if(list->update) { + list->update(list, 0); + } } void ui_list_addobsv(UiList *list, ui_callback f, void *data) { @@ -183,42 +186,60 @@ va_list ap; va_start(ap, ctx); - UcxList *cols = NULL; + CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32); int type; while((type = va_arg(ap, int)) != -1) { char *name = va_arg(ap, char*); - UiColumn *column = malloc(sizeof(UiColumn)); - column->type = type; - column->name = name; + UiColumn column; + column.type = type; + column.name = name; - cols = ucx_list_append(cols, column); + cxListAdd(cols, &column); } va_end(ap); - size_t len = ucx_list_size(cols); + size_t len = cxListSize(cols); info->columns = len; info->types = ui_calloc(ctx, len, sizeof(UiModelType)); info->titles = ui_calloc(ctx, len, sizeof(char*)); + info->columnsize = ui_calloc(ctx, len, sizeof(int)); int i = 0; - UCX_FOREACH(elm, cols) { - UiColumn *c = elm->data; + CxIterator iter = cxListIterator(cols); + cx_foreach(UiColumn*, c, iter) { info->types[i] = c->type; info->titles[i] = c->name; - free(c); i++; } - ucx_list_free(cols); + cxListDestroy(cols); return info; } +UiModel* ui_model_copy(UiContext *ctx, UiModel* model) { + const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; + + UiModel* newmodel = cxMalloc(a, sizeof(UiModel)); + *newmodel = *model; + + newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType)); + memcpy(newmodel->types, model->types, model->columns); + + newmodel->titles = cxCalloc(a, model->columns, sizeof(char*)); + for (int i = 0; i < model->columns; i++) { + newmodel->titles[i] = model->titles[i] ? cx_strdup_a(a, cx_str(model->titles[i])).ptr : NULL; + } + + return newmodel; +} + void ui_model_free(UiContext *ctx, UiModel *mi) { - ucx_mempool_free(ctx->mempool, mi->types); - ucx_mempool_free(ctx->mempool, mi->titles); - ucx_mempool_free(ctx->mempool, mi); + const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; + cxFree(a, mi->types); + cxFree(a, mi->titles); + cxFree(a, mi); } // types @@ -269,6 +290,75 @@ return r; } +UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, char *name) { + UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric)); + memset(g, 0, sizeof(UiGeneric)); + if(name) { + uic_reg_var(ctx, name, UI_VAR_GENERIC, g); + } + return g; +} + + +void ui_int_set(UiInteger* i, int64_t value) { + if (i && i->set) { + i->set(i, value); + } +} + +int64_t ui_int_get(UiInteger* i) { + if (i) { + return i->get ? i->get(i) : i->value; + } else { + return 0; + } +} + +void ui_double_set(UiDouble* d, double value) { + if (d && d->set) { + d->set(d, value); + } +} + +double ui_double_get(UiDouble* d) { + if (d) { + return d->get ? d->get(d) : d->value; + } + else { + return 0; + } +} + +void ui_string_set(UiString* s, const char* value) { + if (s && s->set) { + s->set(s, value); + } +} + +char* ui_string_get(UiString* s) { + if (s) { + return s->get ? s->get(s) : s->value.ptr; + } + else { + return 0; + } +} + +void ui_text_set(UiText* s, const char* value) { + if (s && s->set) { + s->set(s, value); + } +} + +char* ui_text_get(UiText* s) { + if (s) { + return s->get ? s->get(s) : s->value.ptr; + } + else { + return 0; + } +} + // private functions void uic_int_copy(UiInteger *from, UiInteger *to) { @@ -317,6 +407,12 @@ to->obj = from->obj; } +void uic_generic_copy(UiGeneric *from, UiGeneric *to) { + to->get = from->get; + to->get_type = from->get_type; + to->set = from->set; + to->obj = from->obj; +} void uic_int_save(UiInteger *i) { if(!i->obj) return; @@ -344,6 +440,11 @@ r->get(r); } +void uic_generic_save(UiGeneric *g) { + if(!g->obj) return; + g->get(g); +} + void uic_int_unbind(UiInteger *i) { i->get = NULL; @@ -389,3 +490,90 @@ l->update = NULL; l->obj = NULL; } + +void uic_generic_unbind(UiGeneric *g) { + g->get = NULL; + g->get_type = NULL; + g->set = NULL; + g->obj = NULL; +} + + +UIEXPORT UiListSelection ui_list_getselection(UiList *list) { + if (list->getselection) { + return list->getselection(list); + } + return (UiListSelection){ 0, NULL }; +} + +UIEXPORT void ui_list_setselection(UiList *list, int index) { + if (list->setselection && index >= 0) { + UiListSelection sel; + sel.count = 1; + sel.rows = &index; + list->setselection(list, sel); + } +} + +UIEXPORT void ui_listselection_free(UiListSelection selection) { + if (selection.rows) { + free(selection.rows); + } +} + +UIEXPORT UiStr ui_str(char *cstr) { + return (UiStr) { cstr, NULL }; +} + +UIEXPORT UiStr ui_str_free(char *str, void (*freefunc)(void *v)) { + return (UiStr) { str, freefunc }; +} + + +UIEXPORT UiFileList ui_filelist_copy(UiFileList list) { + char **newlist = calloc(sizeof(char*), list.nfiles); + for (int i = 0; i < list.nfiles; i++) { + newlist[i] = strdup(list.files[i]); + } + return (UiFileList) { newlist, list.nfiles }; +} + +UIEXPORT void ui_filelist_free(UiFileList list) { + for (int i = 0; i < list.nfiles; i++) { + free(list.files[i]); + } + free(list.files); +} + + +typedef struct UiObserverDestructor { + UiList *list; + UiObserver *observer; +} UiObserverDestructor; + +static void observer_destructor(UiObserverDestructor *destr) { + UiObserver *remove_obs = destr->observer; + UiObserver *obs = destr->list->observers; + UiObserver *prev = NULL; + while(obs) { + if(obs == remove_obs) { + if(prev) { + prev->next = obs->next; + } else { + destr->list->observers = obs->next; + } + break; + } + prev = obs; + obs = obs->next; + } + free(remove_obs); +} + +void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer) { + CxMempool *mp = ctx->mp; + UiObserverDestructor *destr = cxMalloc(mp->allocator, sizeof(UiObserverDestructor)); + destr->list = list; + destr->observer = observer; + cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor); +}
--- a/ui/common/types.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/common/types.h Sat Jan 04 16:38:48 2025 +0100 @@ -42,12 +42,14 @@ void uic_text_copy(UiText *from, UiText *to); void uic_range_copy(UiRange *from, UiRange *to); void uic_list_copy(UiList *from, UiList *to); +void uic_generic_copy(UiGeneric *from, UiGeneric *to); void uic_int_save(UiInteger *i); void uic_double_save(UiDouble *d); void uic_string_save(UiString *s); void uic_text_save(UiText *t); void uic_range_save(UiRange *r); +void uic_generic_save(UiGeneric *g); void uic_int_unbind(UiInteger *i); void uic_double_unbind(UiDouble *d); @@ -55,7 +57,10 @@ void uic_text_unbind(UiText *t); void uic_range_unbind(UiRange *r); void uic_list_unbind(UiList *l); - +void uic_generic_unbind(UiGeneric *g); + +void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObserver *observer); + #ifdef __cplusplus } #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/ucx_properties.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,263 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ucx_properties.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +UcxProperties *ucx_properties_new() { + UcxProperties *parser = (UcxProperties*)malloc( + sizeof(UcxProperties)); + if(!parser) { + return NULL; + } + + parser->buffer = NULL; + parser->buflen = 0; + parser->pos = 0; + parser->tmp = NULL; + parser->tmplen = 0; + parser->tmpcap = 0; + parser->error = 0; + parser->delimiter = '='; + parser->comment1 = '#'; + parser->comment2 = 0; + parser->comment3 = 0; + + return parser; +} + +void ucx_properties_free(UcxProperties *parser) { + if(parser->tmp) { + free(parser->tmp); + } + free(parser); +} + +void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) { + parser->buffer = buf; + parser->buflen = len; + parser->pos = 0; +} + +static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) { + if(parser->tmpcap - parser->tmplen < len) { + size_t newcap = parser->tmpcap + len + 64; + parser->tmp = (char*)realloc(parser->tmp, newcap); + parser->tmpcap = newcap; + } + memcpy(parser->tmp + parser->tmplen, buf, len); + parser->tmplen += len; +} + +int ucx_properties_next(UcxProperties *parser, cxstring *name, cxstring *value) { + if(parser->tmplen > 0) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + cxstring str = cx_strn(buf, len); + cxstring nl = cx_strchr(str, '\n'); + if(nl.ptr) { + size_t newlen = (size_t)(nl.ptr - buf) + 1; + parser_tmp_append(parser, buf, newlen); + // the tmp buffer contains exactly one line now + + char *orig_buf = parser->buffer; + size_t orig_len = parser->buflen; + + parser->buffer = parser->tmp; + parser->buflen = parser->tmplen; + parser->pos = 0; + parser->tmp = NULL; + parser->tmpcap = 0; + parser->tmplen = 0; + // run ucx_properties_next with the tmp buffer as main buffer + int ret = ucx_properties_next(parser, name, value); + + // restore original buffer + parser->tmp = parser->buffer; + parser->buffer = orig_buf; + parser->buflen = orig_len; + parser->pos = newlen; + + /* + * if ret == 0 the tmp buffer contained just space or a comment + * we parse again with the original buffer to get a name/value + * or a new tmp buffer + */ + return ret ? ret : ucx_properties_next(parser, name, value); + } else { + parser_tmp_append(parser, buf, len); + return 0; + } + } else if(parser->tmp) { + free(parser->tmp); + parser->tmp = NULL; + } + + char comment1 = parser->comment1; + char comment2 = parser->comment2; + char comment3 = parser->comment3; + char delimiter = parser->delimiter; + + // get one line and parse it + while(parser->pos < parser->buflen) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + + /* + * First we check if we have at least one line. We also get indices of + * delimiter and comment chars + */ + size_t delimiter_index = 0; + size_t comment_index = 0; + int has_comment = 0; + + size_t i = 0; + char c = 0; + for(;i<len;i++) { + c = buf[i]; + if(c == comment1 || c == comment2 || c == comment3) { + if(comment_index == 0) { + comment_index = i; + has_comment = 1; + } + } else if(c == delimiter) { + if(delimiter_index == 0 && !has_comment) { + delimiter_index = i; + } + } else if(c == '\n') { + break; + } + } + + if(c != '\n') { + // we don't have enough data for a line + // store remaining bytes in temporary buffer for next round + parser->tmpcap = len + 128; + parser->tmp = (char*)malloc(parser->tmpcap); + parser->tmplen = len; + memcpy(parser->tmp, buf, len); + return 0; + } + + cxstring line = has_comment ? cx_strn(buf, comment_index) : cx_strn(buf, i); + // check line + if(delimiter_index == 0) { + line = cx_strtrim(line); + if(line.length != 0) { + parser->error = 1; + } + } else { + cxstring n = cx_strn(buf, delimiter_index); + cxstring v = cx_strn( + buf + delimiter_index + 1, + line.length - delimiter_index - 1); + n = cx_strtrim(n); + v = cx_strtrim(v); + if(n.length != 0 || v.length != 0) { + *name = n; + *value = v; + parser->pos += i + 1; + return 1; + } else { + parser->error = 1; + } + } + + parser->pos += i + 1; + } + + return 0; +} + +int ucx_properties2map(UcxProperties *parser, CxMap *map) { + cxstring name; + cxstring value; + while(ucx_properties_next(parser, &name, &value)) { + cxmutstr mutvalue = cx_strdup_a(map->collection.allocator, value); + if(!mutvalue.ptr) { + return 1; + } + if(cxMapPut(map, cx_hash_key_cxstr(name), mutvalue.ptr)) { + cxFree(map->collection.allocator, mutvalue.ptr); + return 1; + } + } + if (parser->error) { + return parser->error; + } else { + return 0; + } +} + +// buffer size is documented - change doc, when you change bufsize! +#define UCX_PROPLOAD_BUFSIZE 1024 +int ucx_properties_load(CxMap *map, FILE *file) { + UcxProperties *parser = ucx_properties_new(); + if(!(parser && map && file)) { + return 1; + } + + int error = 0; + size_t r; + char buf[UCX_PROPLOAD_BUFSIZE]; + while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) { + ucx_properties_fill(parser, buf, r); + error = ucx_properties2map(parser, map); + if (error) { + break; + } + } + ucx_properties_free(parser); + return error; +} + +int ucx_properties_store(CxMap *map, FILE *file) { + CxIterator iter = cxMapIterator(map); + cxstring value; + size_t written; + + cx_foreach(CxMapEntry *, v, iter) { + value = cx_str(v->value); + + written = 0; + written += fwrite(v->key->data, 1, v->key->len, file); + written += fwrite(" = ", 1, 3, file); + written += fwrite(value.ptr, 1, value.length, file); + written += fwrite("\n", 1, 1, file); + + if (written != v->key->len + value.length + 4) { + return 1; + } + } + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/ucx_properties.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,223 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * @file properties.h + * + * Load / store utilities for properties files. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_PROPERTIES_H +#define UCX_PROPERTIES_H + +#include <cx/hash_map.h> +#include <cx/string.h> + +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * UcxProperties object for parsing properties data. + * Most of the fields are for internal use only. You may configure the + * properties parser, e.g. by changing the used delimiter or specifying + * up to three different characters that shall introduce comments. + */ +typedef struct { + /** + * Input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + char *buffer; + + /** + * Length of the input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + size_t buflen; + + /** + * Current buffer position (don't set manually). + * Used by ucx_properties_next(). + */ + size_t pos; + + /** + * Internal temporary buffer (don't set manually). + * Used by ucx_properties_next(). + */ + char *tmp; + + /** + * Internal temporary buffer length (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmplen; + + /** + * Internal temporary buffer capacity (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmpcap; + + /** + * Parser error code. + * This is always 0 on success and a nonzero value on syntax errors. + * The value is set by ucx_properties_next(). + */ + int error; + + /** + * The delimiter that shall be used. + * This is '=' by default. + */ + char delimiter; + + /** + * The first comment character. + * This is '#' by default. + */ + char comment1; + + /** + * The second comment character. + * This is not set by default. + */ + char comment2; + + /** + * The third comment character. + * This is not set by default. + */ + char comment3; +} UcxProperties; + + +/** + * Constructs a new UcxProperties object. + * @return a pointer to the new UcxProperties object + */ +UcxProperties *ucx_properties_new(); + +/** + * Destroys a UcxProperties object. + * @param prop the UcxProperties object to destroy + */ +void ucx_properties_free(UcxProperties *prop); + +/** + * Sets the input buffer for the properties parser. + * + * After calling this function, you may parse the data by calling + * ucx_properties_next() until it returns 0. The function ucx_properties2map() + * is a convenience function that reads as much data as possible by using this + * function. + * + * + * @param prop the UcxProperties object + * @param buf a pointer to the new buffer + * @param len the payload length of the buffer + * @see ucx_properties_next() + * @see ucx_properties2map() + */ +void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); + +/** + * Retrieves the next key/value-pair. + * + * This function returns a nonzero value as long as there are key/value-pairs + * found. If no more key/value-pairs are found, you may refill the input buffer + * with ucx_properties_fill(). + * + * <b>Attention:</b> the cxstring.ptr pointers of the output parameters point to + * memory within the input buffer of the parser and will get invalid some time. + * If you want long term copies of the key/value-pairs, use sstrdup() after + * calling this function. + * + * @param prop the UcxProperties object + * @param name a pointer to the cxstring that shall contain the property name + * @param value a pointer to the cxstring that shall contain the property value + * @return Nonzero, if a key/value-pair was successfully retrieved + * @see ucx_properties_fill() + */ +int ucx_properties_next(UcxProperties *prop, cxstring *name, cxstring *value); + +/** + * Retrieves all available key/value-pairs and puts them into a UcxMap. + * + * This is done by successive calls to ucx_properties_next() until no more + * key/value-pairs can be retrieved. + * + * The memory for the map values is allocated by the map's own allocator. + * + * @param prop the UcxProperties object + * @param map the target map + * @return The UcxProperties.error code (i.e. 0 on success). + * @see ucx_properties_fill() + * @see UcxMap.allocator + */ +int ucx_properties2map(UcxProperties *prop, CxMap *map); + +/** + * Loads a properties file to a UcxMap. + * + * This is a convenience function that reads data from an input + * stream until the end of the stream is reached. + * + * @param map the map object to write the key/value-pairs to + * @param file the <code>FILE*</code> stream to read from + * @return 0 on success, or a non-zero value on error + * + * @see ucx_properties_fill() + * @see ucx_properties2map() + */ +int ucx_properties_load(CxMap *map, FILE *file); + +/** + * Stores a UcxMap to a file. + * + * The key/value-pairs are written by using the following format: + * + * <code>[key] = [value]\\n</code> + * + * @param map the map to store + * @param file the <code>FILE*</code> stream to write to + * @return 0 on success, or a non-zero value on error + */ +int ucx_properties_store(CxMap *map, FILE *file); + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_PROPERTIES_H */ +
--- a/ui/gtk/button.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/button.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,19 +31,48 @@ #include "button.h" #include "container.h" -#include <ucx/mempool.h> +#include <cx/allocator.h> #include "../common/context.h" #include "../common/object.h" -UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) { +void ui_button_set_icon_name(GtkWidget *button, const char *icon) { + if(!icon) { + return; + } + +#ifdef UI_GTK4 + gtk_button_set_icon_name(GTK_BUTTON(button), icon); +#else +#if GTK_CHECK_VERSION(2, 6, 0) + GtkWidget *image = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON); + if(image) { + gtk_button_set_image(GTK_BUTTON(button), image); + } +#else + // TODO +#endif +#endif +} + +GtkWidget* ui_create_button( + UiObject *obj, + const char *label, + const char *icon, + ui_callback onclick, + void *userdata, + int event_value, + bool activate_event) +{ GtkWidget *button = gtk_button_new_with_label(label); + ui_button_set_icon_name(button, icon); - if(f) { + if(onclick) { UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; - event->userdata = data; - event->callback = f; - event->value = 0; + event->userdata = userdata; + event->callback = onclick; + event->value = event_value; + event->customdata = NULL; g_signal_connect( button, @@ -55,11 +84,25 @@ "destroy", G_CALLBACK(ui_destroy_userdata), event); + if(activate_event) { + g_signal_connect( + button, + "activate", + G_CALLBACK(ui_button_clicked), + event); + } } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, button, FALSE); - + return button; +} + +UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs args) { + UiObject* current = uic_current_obj(obj); + GtkWidget *button = ui_create_button(obj, args.label, args.icon, args.onclick, args.onclickdata, 0, FALSE); + ui_set_name_and_style(button, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, button, args.groups); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, button, FALSE); return button; } @@ -86,81 +129,318 @@ gtk_toggle_button_set_active(button, value != 0 ? TRUE : FALSE); } -void ui_toggled_obs(GtkToggleToolButton *widget, UiVarEventData *event) { +void ui_toggled_obs(void *widget, UiVarEventData *event) { + UiInteger *i = event->var->value; UiEvent e; e.obj = event->obj; e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = event->var->value; - e.intval = gtk_toggle_tool_button_get_active(widget); + e.intval = i->get(i); - UiInteger *i = event->var->value; ui_notify_evt(i->observers, &e); } -UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var) { - GtkWidget *button = gtk_check_button_new_with_label(label); +static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = gtk_toggle_button_get_active(widget); + event->callback(&e, event->userdata); +} + +static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) { + if(gtk_toggle_button_get_active(widget)) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); + } +} + +void ui_setup_togglebutton( + UiObject *obj, + GtkWidget *togglebutton, + const char *label, + const char *icon, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state) +{ + if(label) { + gtk_button_set_label(GTK_BUTTON(togglebutton), label); + } + ui_button_set_icon_name(togglebutton, icon); - // bind value - if(var) { - UiInteger *value = var->value; - value->obj = GTK_TOGGLE_BUTTON(button); - value->get = ui_toggle_button_get; - value->set = ui_toggle_button_set; - gtk_toggle_button_set_active(value->obj, value->value); + ui_bind_togglebutton( + obj, + togglebutton, + ui_toggle_button_get, + ui_toggle_button_set, + varname, + value, + (ui_toggled_func)ui_toggled_callback, + onchange, + onchangedata, + (ui_toggled_func)ui_togglebutton_enable_state_callback, + enable_state + ); +} + +void ui_bind_togglebutton( + UiObject *obj, + GtkWidget *widget, + int64_t (*getfunc)(UiInteger*), + void (*setfunc)(UiInteger*, int64_t), + const char *varname, + UiInteger *value, + void (*toggled_callback)(void*, void*), + ui_callback onchange, + void *onchangedata, + void (*enable_state_func)(void*, void*), + int enable_state) +{ + UiObject* current = uic_current_obj(obj); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, value, varname, UI_VAR_INTEGER); + if (var) { + UiInteger* value = (UiInteger*)var->value; + value->obj = widget; + value->get = getfunc; + value->set = setfunc; UiVarEventData *event = malloc(sizeof(UiVarEventData)); event->obj = obj; event->var = var; event->observers = NULL; + event->callback = NULL; + event->userdata = NULL; g_signal_connect( - button, - "clicked", + widget, + "toggled", G_CALLBACK(ui_toggled_obs), event); g_signal_connect( - button, + widget, "destroy", G_CALLBACK(ui_destroy_vardata), event); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, button, FALSE); + if(onchange) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = onchangedata; + event->callback = onchange; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + widget, + "toggled", + G_CALLBACK(toggled_callback), + event); + g_signal_connect( + widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } + + if(enable_state > 0) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = NULL; + event->callback = NULL; + event->value = enable_state; + event->customdata = NULL; + + g_signal_connect( + widget, + "toggled", + G_CALLBACK(enable_state_func), + event); + g_signal_connect( + widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } +} + +static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); - return button; + ui_setup_togglebutton( + current, + widget, + args.label, + args.icon, + args.varname, + args.value, + args.onchange, + args.onchangedata, + args.enable_group); + ui_set_name_and_style(widget, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, widget, args.groups); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, widget, FALSE); + + return widget; +} + +UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { + return togglebutton_create(obj, gtk_toggle_button_new(), args); +} + +#if GTK_MAJOR_VERSION >= 4 + +int64_t ui_check_button_get(UiInteger *integer) { + GtkCheckButton *button = integer->obj; + integer->value = (int)gtk_check_button_get_active(button); + return integer->value; +} + +void ui_check_button_set(UiInteger *integer, int64_t value) { + GtkCheckButton *button = integer->obj; + integer->value = value; + gtk_check_button_set_active(button, value != 0 ? TRUE : FALSE); +} + +static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = gtk_check_button_get_active(widget); + event->callback(&e, event->userdata); } -UIWIDGET ui_checkbox(UiObject *obj, char *label, UiInteger *value) { - UiVar *var = NULL; - if(value) { - var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; +static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) { + if(gtk_check_button_get_active(widget)) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); } - return ui_checkbox_var(obj, label, var); +} + +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); + + GtkWidget *widget = gtk_check_button_new_with_label(args.label); + ui_bind_togglebutton( + obj, + widget, + ui_check_button_get, + ui_check_button_set, + args.varname, + args.value, + (ui_toggled_func)ui_checkbox_callback, + args.onchange, + args.onchangedata, + (ui_toggled_func)ui_checkbox_enable_state, + args.enable_group); + + ui_set_name_and_style(widget, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, widget, args.groups); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, widget, FALSE); + + return widget; } -UIWIDGET ui_checkbox_nv(UiObject *obj, char *label, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER); - return ui_checkbox_var(obj, label, var); +#else +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + return togglebutton_create(obj, gtk_check_button_new(), args); +} +#endif + +UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { +#ifdef UI_GTK3 + return NULL; // TODO +#else + return ui_checkbox_create(obj, args); +#endif } +#if GTK_MAJOR_VERSION >= 4 +#define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label) +#define RADIOBUTTON_SET_GROUP(button, group) +#define RADIOBUTTON_GET_GROUP(button) GTK_CHECK_BUTTON(button) +#define RADIOBUTTON_GET_ACTIVE(button) gtk_check_button_get_active(GTK_CHECK_BUTTON(button)) +#else +#define RADIOBUTTON_NEW(group, label) gtk_radio_button_new_with_label(group, label) +#define RADIOBUTTON_SET_GROUP(button, group) /* noop */ +#define RADIOBUTTON_GET_GROUP(button) gtk_radio_button_get_group(GTK_RADIO_BUTTON(button)) +#define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) +#endif -UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var) { +static void radiobutton_toggled(void *widget, UiEventData *event) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = RADIOBUTTON_GET_ACTIVE(widget); + event->callback(&e, event->userdata); +} + +typedef struct UiRadioButtonData { + UiInteger *value; + UiVarEventData *eventdata; + UiBool first; +} UiRadioButtonData; + +static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) { + if(data->first) { + ui_destroy_vardata(w, data->eventdata); + g_slist_free(data->value->obj); + data->value->obj = NULL; + data->value->get = NULL; + data->value->set = NULL; + } else { + free(data->eventdata); + } + free(data); +} + +UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); + GSList *rg = NULL; UiInteger *rgroup; + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + + UiBool first = FALSE; if(var) { rgroup = var->value; rg = rgroup->obj; + if(!rg) { + first = TRUE; + } } - GtkWidget *rbutton = gtk_radio_button_new_with_label(rg, label); - rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton)); - + GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args.label); + ui_set_name_and_style(rbutton, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, rbutton, args.groups); if(rgroup) { +#if GTK_MAJOR_VERSION >= 4 + if(rg) { + gtk_check_button_set_group(GTK_CHECK_BUTTON(rbutton), rg->data); + } + rg = g_slist_prepend(rg, rbutton); +#else + gtk_radio_button_set_group(GTK_RADIO_BUTTON(rbutton), rg); + rg = gtk_radio_button_get_group(GTK_RADIO_BUTTON(rbutton)); +#endif + rgroup->obj = rg; rgroup->get = ui_radiobutton_get; rgroup->set = ui_radiobutton_set; @@ -171,41 +451,53 @@ event->obj = obj; event->var = var; event->observers = NULL; + event->callback = NULL; + event->userdata = NULL; + + UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData)); + rbdata->value = rgroup; + rbdata->eventdata = event; + rbdata->first = first; g_signal_connect( rbutton, - "clicked", + "toggled", G_CALLBACK(ui_radio_obs), event); g_signal_connect( rbutton, "destroy", - G_CALLBACK(ui_destroy_vardata), + G_CALLBACK(destroy_radiobutton), + rbdata); + } + + if(args.onchange) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = args.onchangedata; + event->callback = args.onchange; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + rbutton, + "toggled", + G_CALLBACK(radiobutton_toggled), + event); + g_signal_connect( + rbutton, + "destroy", + G_CALLBACK(ui_destroy_userdata), event); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, rbutton, FALSE); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, rbutton, FALSE); return rbutton; } -UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) { - UiVar *var = NULL; - if(rgroup) { - var = malloc(sizeof(UiVar)); - var->value = rgroup; - var->type = UI_VAR_SPECIAL; - } - return ui_radiobutton_var(obj, label, var); -} - -UIWIDGET ui_radiobutton_nv(UiObject *obj, char *label, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER); - return ui_radiobutton_var(obj, label, var); -} - -void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event) { +void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) { UiInteger *i = event->var->value; UiEvent e; @@ -218,6 +510,41 @@ ui_notify_evt(i->observers, &e); } +#if GTK_MAJOR_VERSION >= 4 +int64_t ui_radiobutton_get(UiInteger *value) { + int selection = 0; + GSList *ls = value->obj; + int i = 0; + guint len = g_slist_length(ls); + while(ls) { + if(gtk_check_button_get_active(GTK_CHECK_BUTTON(ls->data))) { + selection = len - i - 1; + break; + } + ls = ls->next; + i++; + } + + value->value = selection; + return selection; +} + +void ui_radiobutton_set(UiInteger *value, int64_t i) { + GSList *ls = value->obj; + int s = g_slist_length(ls) - 1 - i; + int j = 0; + while(ls) { + if(j == s) { + gtk_check_button_set_active(GTK_CHECK_BUTTON(ls->data), TRUE); + break; + } + ls = ls->next; + j++; + } + + value->value = i; +} +#else int64_t ui_radiobutton_get(UiInteger *value) { int selection = 0; GSList *ls = value->obj; @@ -251,4 +578,4 @@ value->value = i; } - +#endif
--- a/ui/gtk/button.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/button.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,18 +36,55 @@ #ifdef __cplusplus extern "C" { #endif + +void ui_button_set_icon_name(GtkWidget *button, const char *icon_name); +typedef void (*ui_toggled_func)(void*, void*); + +GtkWidget* ui_create_button( + UiObject *obj, + const char *label, + const char *icon, + ui_callback onclick, + void *userdata, + int event_value, + bool activate_event); + +void ui_setup_togglebutton( + UiObject *obj, + GtkWidget *togglebutton, + const char *label, + const char *icon, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state); + +void ui_bind_togglebutton( + UiObject *obj, + GtkWidget *widget, + int64_t (*getfunc)(UiInteger*), + void (*setfunc)(UiInteger*, int64_t), + const char *varname, + UiInteger *value, + void (*toggled_callback)(void*, void*), + ui_callback onchange, + void *onchangedata, + void (*enable_state_func)(void*, void*), + int enable_state); + // event wrapper void ui_button_clicked(GtkWidget *widget, UiEventData *event); -void ui_toggled_obs(GtkToggleToolButton *widget, UiVarEventData *event); +void ui_toggled_obs(void *widget, UiVarEventData *event); UIWIDGET ui_checkbox_var(UiObject *obj, char *label, UiVar *var); UIWIDGET ui_radiobutton_var(UiObject *obj, char *label, UiVar *var); -void ui_radio_obs(GtkToggleToolButton *widget, UiVarEventData *event); +void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event); int64_t ui_radiobutton_get(UiInteger *value); void ui_radiobutton_set(UiInteger *value, int64_t i);
--- a/ui/gtk/container.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/container.c Sat Jan 04 16:38:48 2025 +0100 @@ -32,6 +32,7 @@ #include "container.h" #include "toolkit.h" +#include "headerbar.h" #include "../common/context.h" #include "../common/object.h" @@ -52,7 +53,7 @@ } GtkWidget* ui_gtk_vbox_new(int spacing) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing); #else return gtk_vbox_new(FALSE, spacing); @@ -60,39 +61,62 @@ } GtkWidget* ui_gtk_hbox_new(int spacing) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing); #else return gtk_hbox_new(FALSE, spacing); #endif } -/* -------------------- Frame Container (deprecated) -------------------- */ -UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiContainer)); - ct->widget = frame; - ct->add = ui_frame_container_add; - return ct; -} - -void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { - gtk_container_add(GTK_CONTAINER(ct->widget), widget); - ui_reset_layout(ct->layout); - ct->current = widget; +GtkWidget* ui_subcontainer_create( + UiSubContainerType type, + UiObject *newobj, + int spacing, + int columnspacing, + int rowspacing, + int margin) +{ + GtkWidget *sub = NULL; + GtkWidget *add = NULL; + switch(type) { + default: { + sub = ui_gtk_vbox_new(spacing); + add = ui_box_set_margin(sub, margin); + newobj->container = ui_box_container(newobj, sub, type); + newobj->widget = sub; + break; + } + case UI_CONTAINER_HBOX: { + sub = ui_gtk_hbox_new(spacing); + add = ui_box_set_margin(sub, margin); + newobj->container = ui_box_container(newobj, sub, type); + newobj->widget = sub; + break; + } + case UI_CONTAINER_GRID: { + sub = ui_create_grid_widget(columnspacing, rowspacing); + add = ui_box_set_margin(sub, margin); + newobj->container = ui_grid_container(newobj, sub); + newobj->widget = sub; + break; + } + case UI_CONTAINER_NO_SUB: { + break; + } + } + return add; } /* -------------------- Box Container -------------------- */ -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box) { - UiBoxContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, +UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) { + UiBoxContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiBoxContainer)); ct->container.widget = box; ct->container.add = ui_box_container_add; + ct->type = type; return (UiContainer*)ct; } @@ -111,27 +135,40 @@ } UiBool expand = fill; +#if GTK_MAJOR_VERSION >= 4 + gtk_box_append(GTK_BOX(ct->widget), widget); + GtkAlign align = expand ? GTK_ALIGN_FILL : GTK_ALIGN_START; + if(bc->type == UI_CONTAINER_VBOX) { + gtk_widget_set_valign(widget, align); + gtk_widget_set_vexpand(widget, expand); + gtk_widget_set_hexpand(widget, TRUE); + } else if(bc->type == UI_CONTAINER_HBOX) { + gtk_widget_set_halign(widget, align); + gtk_widget_set_hexpand(widget, expand); + gtk_widget_set_vexpand(widget, TRUE); + } + +#else gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0); +#endif ui_reset_layout(ct->layout); ct->current = widget; } UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid) { - UiGridContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiGridContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiGridContainer)); ct->container.widget = grid; ct->container.add = ui_grid_container_add; -#ifdef UI_GTK2 - ct->width = 0; - ct->height = 1; -#endif + UI_GTK_V2(ct->width = 0); + UI_GTK_V2(ct->height = 1); return (UiContainer*)ct; } -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { UiGridContainer *grid = (UiGridContainer*)ct; @@ -143,24 +180,39 @@ int hexpand = FALSE; int vexpand = FALSE; + int hfill = FALSE; + int vfill = FALSE; + if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { + fill = ui_lb2bool(ct->layout.fill); + } if(ct->layout.hexpand != UI_LAYOUT_UNDEFINED) { hexpand = ct->layout.hexpand; + hfill = TRUE; } if(ct->layout.vexpand != UI_LAYOUT_UNDEFINED) { vexpand = ct->layout.vexpand; + vfill = TRUE; + } + if(fill) { + hfill = TRUE; + vfill = TRUE; } - if(hexpand) { - gtk_widget_set_hexpand(widget, TRUE); + if(!hfill) { + gtk_widget_set_halign(widget, GTK_ALIGN_START); } - if(vexpand) { - gtk_widget_set_vexpand(widget, TRUE); + if(!vfill) { + gtk_widget_set_valign(widget, GTK_ALIGN_START); } - int gwidth = ct->layout.gridwidth > 0 ? ct->layout.gridwidth : 1; + gtk_widget_set_hexpand(widget, hexpand); + gtk_widget_set_vexpand(widget, vexpand); - gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, gwidth, 1); - grid->x += gwidth; + int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; + int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; + + gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan); + grid->x += colspan; ui_reset_layout(ct->layout); ct->current = widget; @@ -187,6 +239,10 @@ GtkAttachOptions xoptions = hexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; GtkAttachOptions yoptions = vexpand ? GTK_FILL | GTK_EXPAND : GTK_FILL; + int colspan = ct->layout.colspan > 0 ? ct->layout.colspan : 1; + int rowspan = ct->layout.rowspan > 0 ? ct->layout.rowspan : 1; + // TODO: use colspan/rowspan + gtk_table_attach(GTK_TABLE(ct->widget), widget, grid->x, grid->x+1, grid->y, grid->y+1, xoptions, yoptions, 0, 0); grid->x++; int nw = grid->x > grid->width ? grid->x : grid->width; @@ -201,9 +257,44 @@ } #endif +UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) { + UiContainer *ct = cxCalloc( + obj->ctx->allocator, + 1, + sizeof(UiContainer)); + ct->widget = frame; + ct->add = ui_frame_container_add; + return ct; +} + +void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + FRAME_SET_CHILD(ct->widget, widget); +} + +UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander) { + UiContainer *ct = cxCalloc( + obj->ctx->allocator, + 1, + sizeof(UiContainer)); + ct->widget = expander; + ct->add = ui_expander_container_add; + return ct; +} + +void ui_expander_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + EXPANDER_SET_CHILD(ct->widget, widget); +} + +void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + // TODO: check if the widget implements GtkScrollable + SCROLLEDWINDOW_SET_CHILD(ct->widget, widget); + ui_reset_layout(ct->layout); + ct->current = widget; +} + UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = scrolledwindow; @@ -211,20 +302,9 @@ return ct; } -void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { - // TODO: check if the widget implements GtkScrollable -#ifdef UI_GTK3 - gtk_container_add(GTK_CONTAINER(ct->widget), widget); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ct->widget), widget); -#endif - ui_reset_layout(ct->layout); - ct->current = widget; -} - UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { - UiTabViewContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiTabViewContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiTabViewContainer)); ct->container.widget = tabview; @@ -233,28 +313,23 @@ } void ui_tabview_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { - gtk_notebook_append_page( - GTK_NOTEBOOK(ct->widget), - widget, - gtk_label_new(ct->layout.label)); + UiGtkTabView *data = ui_widget_get_tabview_data(ct->widget); + if(!data) { + fprintf(stderr, "UI Error: widget is not a tabview"); + return; + } + data->add_tab(ct->widget, -1, ct->layout.label, widget); ui_reset_layout(ct->layout); ct->current = widget; } -UIWIDGET ui_vbox(UiObject *obj) { - return ui_vbox_sp(obj, 0, 0); -} -UIWIDGET ui_hbox(UiObject *obj) { - return ui_hbox_sp(obj, 0, 0); -} - -static GtkWidget* box_set_margin(GtkWidget *box, int margin) { +GtkWidget* ui_box_set_margin(GtkWidget *box, int margin) { GtkWidget *ret = box; -#ifdef UI_GTK3 -#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12 +#if GTK_MAJOR_VERSION >= 3 +#if GTK_MAJOR_VERSION * 1000 + GTK_MINOR_VERSION >= 3012 gtk_widget_set_margin_start(box, margin); gtk_widget_set_margin_end(box, margin); #else @@ -272,73 +347,53 @@ return ret; } -UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) { - UiContainer *ct = uic_get_current_container(obj); +UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type) { + UiObject *current = uic_current_obj(obj); + UiContainer *ct = current->container; + UI_APPLY_LAYOUT1(current, args); - GtkWidget *vbox = ui_gtk_vbox_new(spacing); - GtkWidget *widget = margin > 0 ? box_set_margin(vbox, margin) : vbox; + GtkWidget *box = type == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); + ui_set_name_and_style(box, args.name, args.style_class); + GtkWidget *widget = args.margin > 0 ? ui_box_set_margin(box, args.margin) : box; ct->add(ct, widget, TRUE); - UiObject *newobj = uic_object_new(obj, vbox); - newobj->container = ui_box_container(obj, vbox); - uic_obj_add(obj, newobj); - - return widget; -} - -UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) { - UiContainer *ct = uic_get_current_container(obj); - - GtkWidget *hbox = ui_gtk_hbox_new(spacing); - GtkWidget *widget = margin > 0 ? box_set_margin(hbox, margin) : hbox; - ct->add(ct, widget, TRUE); - - UiObject *newobj = uic_object_new(obj, hbox); - newobj->container = ui_box_container(obj, hbox); + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_box_container(obj, box, type); uic_obj_add(obj, newobj); return widget; } -UIWIDGET ui_grid(UiObject *obj) { - return ui_grid_sp(obj, 0, 0, 0); +UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { + return ui_box_create(obj, args, UI_CONTAINER_VBOX); +} + +UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { + return ui_box_create(obj, args, UI_CONTAINER_HBOX); } -UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) { - UiContainer *ct = uic_get_current_container(obj); +GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing) { +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *grid = gtk_grid_new(); + gtk_grid_set_column_spacing(GTK_GRID(grid), colspacing); + gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing); +#else + GtkWidget *grid = gtk_table_new(1, 1, FALSE); + gtk_table_set_col_spacings(GTK_TABLE(grid), colspacing); + gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing); +#endif + return grid; +} + +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); GtkWidget *widget; -#ifdef UI_GTK3 - GtkWidget *grid = gtk_grid_new(); - gtk_grid_set_column_spacing(GTK_GRID(grid), columnspacing); - gtk_grid_set_row_spacing(GTK_GRID(grid), rowspacing); -#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 12 - gtk_widget_set_margin_start(grid, margin); - gtk_widget_set_margin_end(grid, margin); -#else - gtk_widget_set_margin_left(grid, margin); - gtk_widget_set_margin_right(grid, margin); -#endif - gtk_widget_set_margin_top(grid, margin); - gtk_widget_set_margin_bottom(grid, margin); - - widget = grid; -#elif defined(UI_GTK2) - GtkWidget *grid = gtk_table_new(1, 1, FALSE); - - gtk_table_set_col_spacings(GTK_TABLE(grid), columnspacing); - gtk_table_set_row_spacings(GTK_TABLE(grid), rowspacing); - - if(margin > 0) { - GtkWidget *a = gtk_alignment_new(0.5, 0.5, 1, 1); - gtk_alignment_set_padding(GTK_ALIGNMENT(a), margin, margin, margin, margin); - gtk_container_add(GTK_CONTAINER(a), grid); - widget = a; - } else { - widget = grid; - } -#endif - ct->add(ct, widget, TRUE); + GtkWidget *grid = ui_create_grid_widget(args.columnspacing, args.rowspacing); + ui_set_name_and_style(grid, args.name, args.style_class); + widget = ui_box_set_margin(grid, args.margin); + current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, grid); newobj->container = ui_grid_container(obj, grid); @@ -347,47 +402,535 @@ return widget; } -UIWIDGET ui_scrolledwindow(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL); - ct->add(ct, sw, TRUE); +UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs args) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *frame = gtk_frame_new(args.label); + UiObject *newobj = uic_object_new(obj, frame); + GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); + if(sub) { + FRAME_SET_CHILD(frame, sub); + } else { + newobj->widget = frame; + newobj->container = ui_frame_container(obj, frame); + } + current->container->add(current->container, frame, FALSE); + uic_obj_add(obj, newobj); + + return frame; +} + +UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs args) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *expander = gtk_expander_new(args.label); + gtk_expander_set_expanded(GTK_EXPANDER(expander), args.isexpanded); + UiObject *newobj = uic_object_new(obj, expander); + GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); + if(sub) { + EXPANDER_SET_CHILD(expander, sub); + } else { + newobj->widget = expander; + newobj->container = ui_expander_container(obj, expander); + } + current->container->add(current->container, expander, FALSE); + uic_obj_add(obj, newobj); + + return expander; +} + + +UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *sw = SCROLLEDWINDOW_NEW(); + ui_set_name_and_style(sw, args.name, args.style_class); + GtkWidget *widget = ui_box_set_margin(sw, args.margin); + current->container->add(current->container, widget, TRUE); UiObject *newobj = uic_object_new(obj, sw); - newobj->container = ui_scrolledwindow_container(obj, sw); + GtkWidget *sub = ui_subcontainer_create(args.subcontainer, newobj, args.spacing, args.columnspacing, args.rowspacing, args.margin); + if(sub) { + SCROLLEDWINDOW_SET_CHILD(sw, sub); + } else { + newobj->widget = sw; + newobj->container = ui_scrolledwindow_container(obj, sw); + } + uic_obj_add(obj, newobj); return sw; } -UIWIDGET ui_tabview(UiObject *obj) { - GtkWidget *tabview = gtk_notebook_new(); - gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(tabview), FALSE); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, tabview, TRUE); - - UiObject *tabviewobj = uic_object_new(obj, tabview); - tabviewobj->container = ui_tabview_container(obj, tabview); - uic_obj_add(obj, tabviewobj); - - return tabview; + +void ui_notebook_tab_select(UIWIDGET tabview, int tab) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab); +} + +void ui_notebook_tab_remove(UIWIDGET tabview, int tab) { + gtk_notebook_remove_page(GTK_NOTEBOOK(tabview), tab); +} + +void ui_notebook_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { + gtk_notebook_insert_page( + GTK_NOTEBOOK(widget), + child, + gtk_label_new(name), + index); +} + +int64_t ui_notebook_get(UiInteger *i) { + GtkNotebook *nb = i->obj; + i->value = gtk_notebook_get_current_page(nb); + return i->value; +} + +void ui_notebook_set(UiInteger *i, int64_t value) { + GtkNotebook *nb = i->obj; + gtk_notebook_set_current_page(nb, value); + i->value = gtk_notebook_get_current_page(nb); +} + + +#if GTK_MAJOR_VERSION >= 4 +static int stack_set_page(GtkWidget *stack, int index) { + GtkSelectionModel *pages = gtk_stack_get_pages(GTK_STACK(stack)); + GListModel *list = G_LIST_MODEL(pages); + GtkStackPage *page = g_list_model_get_item(list, index); + if(page) { + gtk_stack_set_visible_child(GTK_STACK(stack), gtk_stack_page_get_child(page)); + } else { + fprintf(stderr, "UI Error: ui_stack_set value out of bounds\n"); + return -1; + } + return index; +} + +void ui_stack_tab_select(UIWIDGET tabview, int tab) { + stack_set_page(tabview, tab); +} + +void ui_stack_tab_remove(UIWIDGET tabview, int tab) { + GtkStack *stack = GTK_STACK(tabview); + GtkWidget *current = gtk_stack_get_visible_child(stack); + GtkSelectionModel *pages = gtk_stack_get_pages(stack); + GListModel *list = G_LIST_MODEL(pages); + GtkStackPage *page = g_list_model_get_item(list, tab); + if(page) { + gtk_stack_remove(stack, gtk_stack_page_get_child(page)); + } +} + +void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { + (void)gtk_stack_add_titled(GTK_STACK(widget), child, name, name); +} + +int64_t ui_stack_get(UiInteger *i) { + GtkStack *stack = GTK_STACK(i->obj); + GtkWidget *current = gtk_stack_get_visible_child(stack); + GtkSelectionModel *pages = gtk_stack_get_pages(stack); + GListModel *list = G_LIST_MODEL(pages); + int nitems = g_list_model_get_n_items(list); + for(int p=0;p<nitems;p++) { + GtkStackPage *page = g_list_model_get_item(list, p); + GtkWidget *child = gtk_stack_page_get_child(page); + if(child == current) { + i->value = p; + break; + } + } + return i->value; +} + +void ui_stack_set(UiInteger *i, int64_t value) { + GtkWidget *widget = i->obj; + if(stack_set_page(widget, value) >= 0) { + i->value = value; + } +} +#elif GTK_MAJOR_VERSION >= 3 +static GtkWidget* stack_get_child(GtkWidget *stack, int index) { + GList *children = gtk_container_get_children(GTK_CONTAINER(stack)); + if(children) { + return g_list_nth_data(children, index); + } + return NULL; +} + +void ui_stack_tab_select(UIWIDGET tabview, int tab) { + GtkWidget *child = stack_get_child(tabview, tab); + if(child) { + gtk_stack_set_visible_child(GTK_STACK(tabview), child); + } +} + +void ui_stack_tab_remove(UIWIDGET tabview, int tab) { + GtkWidget *child = stack_get_child(tabview, tab); + if(child) { + gtk_container_remove(GTK_CONTAINER(tabview), child); + } +} + +void ui_stack_tab_add(UIWIDGET widget, int index, const char *name, UIWIDGET child) { + gtk_stack_add_titled(GTK_STACK(widget), child, name, name); +} + +int64_t ui_stack_get(UiInteger *i) { + GtkWidget *visible = gtk_stack_get_visible_child(GTK_STACK(i->obj)); + GList *children = gtk_container_get_children(GTK_CONTAINER(i->obj)); + GList *elm = children; + int n = 0; + int64_t v = -1; + while(elm) { + GtkWidget *child = elm->data; + if(child == visible) { + v = n; + break; + } + + elm = elm->next; + n++; + } + g_list_free(children); + i->value = v; + return v; +} + +void ui_stack_set(UiInteger *i, int64_t value) { + GtkWidget *child = stack_get_child(i->obj, value); + if(child) { + gtk_stack_set_visible_child(GTK_STACK(i->obj), child); + i->value = value; + } +} + +#endif + + + + +UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview) { + return g_object_get_data(G_OBJECT(tabview), "ui_tabview"); } -void ui_tab(UiObject *obj, char *title) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.label = title; - ui_vbox(obj); +typedef int64_t(*ui_tabview_get_func)(UiInteger*); +typedef void (*ui_tabview_set_func)(UiInteger*, int64_t); + +UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) { + UiGtkTabView *data = malloc(sizeof(UiGtkTabView)); + data->margin = args.margin; + data->spacing = args.spacing; + data->columnspacing = args.columnspacing; + data->rowspacing = args.rowspacing; + + ui_tabview_get_func getfunc = NULL; + ui_tabview_set_func setfunc = NULL; + + GtkWidget *widget = NULL; + GtkWidget *data_widget = NULL; + switch(args.tabview) { + case UI_TABVIEW_DOC: { + // TODO + break; + } + case UI_TABVIEW_NAVIGATION_SIDE: { +#if GTK_CHECK_VERSION(3, 10, 0) + widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + GtkWidget *sidebar = gtk_stack_sidebar_new(); + BOX_ADD(widget, sidebar); + GtkWidget *stack = gtk_stack_new(); + gtk_stack_set_transition_type (GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN); + gtk_stack_sidebar_set_stack(GTK_STACK_SIDEBAR(sidebar), GTK_STACK(stack)); + BOX_ADD_EXPAND(widget, stack); + data->select_tab = ui_stack_tab_select; + data->remove_tab = ui_stack_tab_remove; + data->add_tab = ui_stack_tab_add; + getfunc = ui_stack_get; + setfunc = ui_stack_set; + data_widget = stack; +#else + // TODO +#endif + break; + } + case UI_TABVIEW_DEFAULT: /* fall through */ + case UI_TABVIEW_NAVIGATION_TOP: /* fall through */ + case UI_TABVIEW_INVISIBLE: /* fall through */ + case UI_TABVIEW_NAVIGATION_TOP2: { + widget = gtk_notebook_new(); + data_widget = widget; + data->select_tab = ui_notebook_tab_select; + data->remove_tab = ui_notebook_tab_remove; + data->add_tab = ui_notebook_tab_add; + getfunc = ui_notebook_get; + setfunc = ui_notebook_set; + if(args.tabview == UI_TABVIEW_INVISIBLE) { + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(widget), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(widget), FALSE); + } + break; + } + } + + UiObject* current = uic_current_obj(obj); + if(args.value || args.varname) { + UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + UiInteger *i = var->value; + i->get = getfunc; + i->set = setfunc; + i->obj = data_widget; + } + + g_object_set_data(G_OBJECT(widget), "ui_tabview", data); + data->widget = data_widget; + data->subcontainer = args.subcontainer; + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, widget, TRUE); + + UiObject *newobj = uic_object_new(obj, widget); + newobj->container = ui_tabview_container(obj, widget); + uic_obj_add(obj, newobj); + data->obj = newobj; + + return widget; +} + +void ui_tab_create(UiObject* obj, const char* title) { + UiObject* current = uic_current_obj(obj); + UiGtkTabView *data = ui_widget_get_tabview_data(current->widget); + if(!data) { + fprintf(stderr, "UI Error: widget is not a tabview\n"); + return; + } + + UiObject *newobj = ui_tabview_add(current->widget, title, -1); + current->next = newobj; +} + + + +void ui_tabview_select(UIWIDGET tabview, int tab) { + UiGtkTabView *data = ui_widget_get_tabview_data(tabview); + if(!data) { + fprintf(stderr, "UI Error: widget is not a tabview\n"); + return; + } + data->select_tab(tabview, tab); +} + +void ui_tabview_remove(UIWIDGET tabview, int tab) { + UiGtkTabView *data = ui_widget_get_tabview_data(tabview); + if(!data) { + fprintf(stderr, "UI Error: widget is not a tabview\n"); + return; + } + data->remove_tab(tabview, tab); +} + +UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { + UiGtkTabView *data = ui_widget_get_tabview_data(tabview); + if(!data) { + fprintf(stderr, "UI Error: widget is not a tabview\n"); + return NULL; + } + + UiObject *newobj = cxCalloc(data->obj->ctx->allocator, 1, sizeof(UiObject)); + newobj->ctx = data->obj->ctx; + + GtkWidget *sub; + switch(data->subcontainer) { + default: { + sub = ui_gtk_vbox_new(data->spacing); + newobj->container = ui_box_container(newobj, sub, data->subcontainer); + break; + } + case UI_CONTAINER_HBOX: { + sub = ui_gtk_hbox_new(data->spacing); + newobj->container = ui_box_container(newobj, sub, data->subcontainer); + break; + } + case UI_CONTAINER_GRID: { + sub = ui_create_grid_widget(data->columnspacing, data->rowspacing); + newobj->container = ui_grid_container(newobj, sub); + break; + } + } + newobj->widget = sub; + GtkWidget *widget = ui_box_set_margin(sub, data->margin); + + data->add_tab(data->widget, tab_index, name, widget); + + return newobj; } -void ui_select_tab(UIWIDGET tabview, int tab) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(tabview), tab); + +/* -------------------- Headerbar -------------------- */ + +static void hb_set_part(UiObject *obj, int part) { + UiObject* current = uic_current_obj(obj); + GtkWidget *headerbar = current->widget; + + UiHeaderbarContainer *hb = cxCalloc( + obj->ctx->allocator, + 1, + sizeof(UiHeaderbarContainer)); + memcpy(hb, current->container, sizeof(UiHeaderbarContainer)); + + UiObject *newobj = uic_object_new(obj, headerbar); + newobj->container = (UiContainer*)hb; + uic_obj_add(obj, newobj); + + hb->part = part; +} + +void ui_headerbar_start_create(UiObject *obj) { + hb_set_part(obj, 0); +} + +void ui_headerbar_center_create(UiObject *obj) { + hb_set_part(obj, 2); +} + +void ui_headerbar_end_create(UiObject *obj) { + hb_set_part(obj, 1); +} + +UIWIDGET ui_headerbar_fallback_create(UiObject *obj, UiHeaderbarArgs args) { + UiObject *current = uic_current_obj(obj); + UiContainer *ct = current->container; + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *box = ui_gtk_hbox_new(args.alt_spacing); + ui_set_name_and_style(box, args.name, args.style_class); + ct->add(ct, box, FALSE); + + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_headerbar_fallback_container(obj, box); + uic_obj_add(obj, newobj); + + return box; +} + +static void hb_fallback_set_part(UiObject *obj, int part) { + UiObject* current = uic_current_obj(obj); + GtkWidget *headerbar = current->widget; + + UiObject *newobj = uic_object_new(obj, headerbar); + newobj->container = ui_headerbar_container(obj, headerbar); + uic_obj_add(obj, newobj); + + UiHeaderbarContainer *hb = (UiHeaderbarContainer*)newobj->container; + hb->part = part; +} + +UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar) { + UiHeaderbarContainer *ct = cxCalloc( + obj->ctx->allocator, + 1, + sizeof(UiHeaderbarContainer)); + ct->container.widget = headerbar; + ct->container.add = ui_headerbar_fallback_container_add; + return (UiContainer*)ct; +} + +void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; + BOX_ADD(ct->widget, widget); } +#if GTK_CHECK_VERSION(3, 10, 0) + +UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { + GtkWidget *headerbar = g_object_get_data(G_OBJECT(obj->widget), "ui_headerbar"); + if(!headerbar) { + return ui_headerbar_fallback_create(obj, args); + } + + UiObject *newobj = uic_object_new(obj, headerbar); + newobj->container = ui_headerbar_container(obj, headerbar); + uic_obj_add(obj, newobj); + + return headerbar; +} + +UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar) { + UiHeaderbarContainer *ct = cxCalloc( + obj->ctx->allocator, + 1, + sizeof(UiHeaderbarContainer)); + ct->container.widget = headerbar; + ct->container.add = ui_headerbar_container_add; + return (UiContainer*)ct; +} + +void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { + UiHeaderbarContainer *hb = (UiHeaderbarContainer*)ct; + if(hb->part == 0) { + UI_HEADERBAR_PACK_START(ct->widget, widget); + } else if(hb->part == 1) { + UI_HEADERBAR_PACK_END(ct->widget, widget); + } else if(hb->part == 2) { + if(!hb->centerbox) { + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + hb->centerbox = box; + UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box); + } + BOX_ADD(hb->centerbox, widget); + } +} + +#else + +UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { + return ui_headerbar_fallback_create(obj, args); +} + +#endif + +/* -------------------- Sidebar -------------------- */ + +#ifdef UI_LIBADWAITA +UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { + GtkWidget *sidebar_toolbar_view = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); + if(!sidebar_toolbar_view) { + fprintf(stderr, "Error: window is not configured for sidebar\n"); + return NULL; + } + + GtkWidget *box = ui_gtk_vbox_new(args.spacing); + ui_box_set_margin(box, args.margin); + adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box); + + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_obj_add(obj, newobj); + + return box; +} +#else +UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { + GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); + + GtkWidget *box = ui_gtk_vbox_new(args.spacing); + ui_box_set_margin(box, args.margin); + BOX_ADD_EXPAND(sidebar_vbox, box); + + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_obj_add(obj, newobj); + + return box; +} +#endif + /* -------------------- Splitpane -------------------- */ static GtkWidget* create_paned(UiOrientation orientation) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 switch(orientation) { case UI_HORIZONTAL: return gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); case UI_VERTICAL: return gtk_paned_new(GTK_ORIENTATION_VERTICAL); @@ -401,191 +944,129 @@ return NULL; } -UIWIDGET ui_splitpane(UiObject *obj, int max, UiOrientation orientation) { - GtkWidget *paned = create_paned(orientation); - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, paned, TRUE); - - if(max <= 0) max = INT_MAX; - - UiPanedContainer *pctn = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiPanedContainer)); - pctn->container.widget = paned; - pctn->container.add = ui_paned_container_add; - pctn->current_pane = paned; - pctn->orientation = orientation; - pctn->max = max; - pctn->cur = 0; - - UiObject *pobj = uic_object_new(obj, paned); - pobj->container = (UiContainer*)pctn; - - uic_obj_add(obj, pobj); - - return paned; -} - -UIWIDGET ui_hsplitpane(UiObject *obj, int max) { - return ui_splitpane(obj, max, UI_HORIZONTAL); -} - -UIWIDGET ui_vsplitpane(UiObject *obj, int max) { - return ui_splitpane(obj, max, UI_VERTICAL); -} - -void ui_paned_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) { - UiPanedContainer *pctn = (UiPanedContainer*)ct; - - gboolean resize = (ct->layout.hexpand || ct->layout.vexpand) ? TRUE : FALSE; - int width = ct->layout.width; - ui_reset_layout(ct->layout); - - if(pctn->cur == 0) { - gtk_paned_pack1(GTK_PANED(pctn->current_pane), widget, resize, resize); - } else if(pctn->cur < pctn->max-1) { - GtkWidget *nextPane = create_paned(pctn->orientation); - gtk_paned_pack2(GTK_PANED(pctn->current_pane), nextPane, TRUE, TRUE); - gtk_paned_pack1(GTK_PANED(nextPane), widget, resize, resize); - pctn->current_pane = nextPane; - } else if(pctn->cur == pctn->max-1) { - gtk_paned_pack2(GTK_PANED(pctn->current_pane), widget, resize, resize); - width = 0; // disable potential call of gtk_paned_set_position - } else { - fprintf(stderr, "Splitpane max reached: %d\n", pctn->max); - return; - } - - if(width > 0) { - gtk_paned_set_position(GTK_PANED(pctn->current_pane), width); - } - - pctn->cur++; -} -/* -------------------- Sidebar (deprecated) -------------------- */ -UIWIDGET ui_sidebar(UiObject *obj) { -#ifdef UI_GTK3 - GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); -#else - GtkWidget *paned = gtk_hpaned_new(); -#endif - gtk_paned_set_position(GTK_PANED(paned), 200); - - GtkWidget *sidebar = ui_gtk_vbox_new(0); - gtk_paned_pack1(GTK_PANED(paned), sidebar, TRUE, FALSE); - - UiObject *left = uic_object_new(obj, sidebar); - UiContainer *ct1 = ui_box_container(obj, sidebar); - left->container = ct1; - - UiObject *right = uic_object_new(obj, sidebar); - UiContainer *ct2 = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct2->widget = paned; - ct2->add = ui_split_container_add2; - right->container = ct2; - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, paned, TRUE); - - uic_obj_add(obj, right); - uic_obj_add(obj, left); - - return sidebar; +/* -------------------- ItemList Container -------------------- */ + +static void remove_item(void *data, void *item) { + UiGtkItemListContainer *ct = data; + UiObject *obj = item; + if(ct->remove_items) { + BOX_REMOVE(ct->widget, obj->widget); + } + uic_object_destroy(obj); } -void ui_split_container_add1(UiContainer *ct, GtkWidget *widget, UiBool fill) { - // TODO: remove - gtk_paned_pack1(GTK_PANED(ct->widget), widget, TRUE, FALSE); +static void update_itemlist(UiList *list, int c) { + UiGtkItemListContainer *ct = list->obj; - ui_reset_layout(ct->layout); - ct->current = widget; -} - -void ui_split_container_add2(UiContainer *ct, GtkWidget *widget, UiBool fill) { - gtk_paned_pack2(GTK_PANED(ct->widget), widget, TRUE, FALSE); + CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + new_items->collection.advanced_destructor = remove_item; + new_items->collection.destructor_data = ct; - ui_reset_layout(ct->layout); - ct->current = widget; -} - - -/* -------------------- Document Tabview -------------------- */ -static void page_change(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer data) { - GQuark q = g_quark_from_static_string("ui.tab.object"); - UiObject *tab = g_object_get_qdata(G_OBJECT(page), q); - if(!tab) { - return; + // only create new widgets for new elements, so at first we have + // to find which elements are new + // check which elements in the list are already in the container + void *elm = list->first(list); + int j = 0; + while(elm) { + CxHashKey key = cx_hash_key(&elm, sizeof(void*)); + UiObject *item_obj = cxMapRemoveAndGet(ct->current_items, key); + if(item_obj) { + g_object_ref(G_OBJECT(item_obj->widget)); + BOX_REMOVE(ct->widget, item_obj->widget); + cxMapPut(new_items, key, item_obj); + } + elm = list->next(list); + j++; } - //printf("page_change: %d\n", page_num); - UiContext *ctx = tab->ctx; - uic_context_detach_all(ctx->parent); // TODO: fix? - ctx->parent->attach_document(ctx->parent, ctx->document); -} - -UiTabbedPane* ui_tabbed_document_view(UiObject *obj) { - GtkWidget *tabview = gtk_notebook_new(); - gtk_notebook_set_show_border(GTK_NOTEBOOK(tabview), FALSE); + // ct->current_items only contains elements, that are not in the list + cxMapDestroy(ct->current_items); // calls destructor remove_item + ct->current_items = new_items; - g_signal_connect( - tabview, - "switch-page", - G_CALLBACK(page_change), - NULL); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, tabview, TRUE); - - UiTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(UiTabbedPane)); - tabbedpane->ctx = uic_current_obj(obj)->ctx; - tabbedpane->widget = tabview; - tabbedpane->document = NULL; - - return tabbedpane; + // add all items + int index = 0; + elm = list->first(list); + while(elm) { + CxHashKey key = cx_hash_key(&elm, sizeof(void*)); + UiObject *item_obj = cxMapGet(ct->current_items, key); + if(item_obj) { + // re-add previously created widget + ui_box_container_add(ct->container, item_obj->widget, FALSE); + } else { + // create new widget and object for this list element + CxMempool *mp = cxBasicMempoolCreate(256); + const CxAllocator *a = mp->allocator; + UiObject *obj = cxCalloc(a, 1, sizeof(UiObject)); + obj->ctx = uic_context(obj, mp); + obj->window = NULL; + obj->widget = ui_subcontainer_create( + ct->subcontainer, + obj, + ct->spacing, + ct->columnspacing, + ct->rowspacing, + ct->margin); + ui_box_container_add(ct->container, obj->widget, FALSE); + if(ct->create_ui) { + ct->create_ui(obj, index, elm, ct->userdata); + } + cxMapPut(new_items, key, obj); + } + elm = list->next(list); + index++; + } } -UiObject* ui_document_tab(UiTabbedPane *view) { - GtkWidget *frame = gtk_frame_new(NULL); - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); - // TODO: label - gtk_notebook_append_page(GTK_NOTEBOOK(view->widget), frame, NULL); - - UiObject *tab = ui_malloc(view->ctx, sizeof(UiObject)); - tab->widget = NULL; // initialization for uic_context() - tab->ctx = uic_context(tab, view->ctx->mempool); - tab->ctx->parent = view->ctx; - tab->ctx->attach_document = uic_context_attach_document; - tab->ctx->detach_document2 = uic_context_detach_document2; - tab->widget = frame; - tab->window = view->ctx->obj->window; - tab->container = ui_frame_container(tab, frame); - tab->next = NULL; - - GQuark q = g_quark_from_static_string("ui.tab.object"); - g_object_set_qdata(G_OBJECT(frame), q, tab); - - return tab; +static void destroy_itemlist_container(GtkWidget *w, UiGtkItemListContainer *container) { + container->remove_items = FALSE; + cxMapDestroy(container->current_items); + free(container); } -void ui_tab_set_document(UiContext *ctx, void *document) { - // TODO: remove? - if(ctx->parent->document) { - //ctx->parent->detach_document(ctx->parent, ctx->parent->document); +UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args) { + UiObject *current = uic_current_obj(obj); + UiContainer *ct = current->container; + UI_APPLY_LAYOUT1(current, args); + + GtkWidget *box = args.container == UI_CONTAINER_VBOX ? ui_gtk_vbox_new(args.spacing) : ui_gtk_hbox_new(args.spacing); + ui_set_name_and_style(box, args.name, args.style_class); + GtkWidget *widget = args.margin > 0 ? ui_box_set_margin(box, args.margin) : box; + ct->add(ct, widget, TRUE); + + UiGtkItemListContainer *container = malloc(sizeof(UiGtkItemListContainer)); + container->parent = obj; + container->widget = box; + container->container = ui_box_container(current, box, args.container); + container->create_ui = args.create_ui; + container->userdata = args.userdata; + container->subcontainer = args.subcontainer; + container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS); + container->current_items->collection.advanced_destructor = remove_item; + container->current_items->collection.destructor_data = container; + container->margin = args.sub_margin; + container->spacing = args.sub_spacing; + container->columnspacing = args.sub_columnspacing; + container->rowspacing = args.sub_rowspacing; + container->remove_items = TRUE; + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_LIST); + if(var) { + UiList *list = var->value; + list->obj = container; + list->update = update_itemlist; + update_itemlist(list, 0); } - //uic_context_set_document(ctx, document); - //uic_context_set_document(ctx->parent, document); - //ctx->parent->document = document; + g_signal_connect( + box, + "destroy", + G_CALLBACK(destroy_itemlist_container), + container); + + return box; } -void ui_tab_detach_document(UiContext *ctx) { - // TODO: remove? - //uic_context_detach_document(ctx->parent); -} /* @@ -610,14 +1091,24 @@ ct->layout.vexpand = expand; } -void ui_layout_width(UiObject *obj, int width) { +void ui_layout_hfill(UiObject *obj, UiBool fill) { UiContainer *ct = uic_get_current_container(obj); - ct->layout.width = width; + ct->layout.hfill = fill; } -void ui_layout_gridwidth(UiObject *obj, int width) { +void ui_layout_vfill(UiObject *obj, UiBool fill) { UiContainer *ct = uic_get_current_container(obj); - ct->layout.gridwidth = width; + ct->layout.vfill = fill; +} + +void ui_layout_colspan(UiObject* obj, int cols) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.colspan = cols; +} + +void ui_layout_rowspan(UiObject* obj, int rows) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.rowspan = rows; } void ui_newline(UiObject *obj) {
--- a/ui/gtk/container.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/container.h Sat Jan 04 16:38:48 2025 +0100 @@ -33,6 +33,9 @@ #include "../ui/container.h" #include <string.h> +#include <cx/allocator.h> +#include <cx/hash_map.h> + #ifdef __cplusplus extern "C" { #endif @@ -60,13 +63,16 @@ char *label; UiBool hexpand; UiBool vexpand; + UiBool hfill; + UiBool vfill; int width; - int gridwidth; + int colspan; + int rowspan; }; struct UiContainer { GtkWidget *widget; - GtkMenu *menu; + UIMENU menu; GtkWidget *current; void (*add)(UiContainer*, GtkWidget*, UiBool); @@ -77,6 +83,7 @@ typedef struct UiBoxContainer { UiContainer container; + UiSubContainerType type; UiBool has_fill; } UiBoxContainer; @@ -90,6 +97,7 @@ #endif } UiGridContainer; +/* typedef struct UiPanedContainer { UiContainer container; GtkWidget *current_pane; @@ -97,23 +105,80 @@ int max; int cur; } UiPanedContainer; +*/ typedef struct UiTabViewContainer { UiContainer container; } UiTabViewContainer; +typedef void (*ui_select_tab_func)(UIWIDGET widget, int tab); +typedef void (*ui_add_tab_func)(UIWIDGET widget, int index, const char *name, UIWIDGET child); + +typedef struct UiGtkTabView { + UiObject *obj; + GtkWidget *widget; + ui_select_tab_func select_tab; + ui_select_tab_func remove_tab; + ui_add_tab_func add_tab; + UiSubContainerType subcontainer; + int margin; + int spacing; + int columnspacing; + int rowspacing; +} UiGtkTabView; + +typedef struct UiHeaderbarContainer { + UiContainer container; + GtkWidget *centerbox; + int part; + UiHeaderbarAlternative alternative; /* only used by fallback headerbar */ +} UiHeaderbarContainer; + +typedef struct UiGtkItemListContainer { + UiObject *parent; + GtkWidget *widget; + UiContainer *container; + void (*create_ui)(UiObject *, int, void *, void *); + void *userdata; + UiSubContainerType subcontainer; + CxMap *current_items; + int margin; + int spacing; + int columnspacing; + int rowspacing; + bool remove_items; +} UiGtkItemListContainer; + GtkWidget* ui_gtk_vbox_new(int spacing); GtkWidget* ui_gtk_hbox_new(int spacing); +GtkWidget* ui_subcontainer_create( + UiSubContainerType type, + UiObject *newobj, + int spacing, + int columnspacing, + int rowspacing, + int margin); + UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame); void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); -UiContainer* ui_box_container(UiObject *obj, GtkWidget *box); +GtkWidget* ui_box_set_margin(GtkWidget *box, int margin); +UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs args, UiSubContainerType type); + +UiContainer* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type); void ui_box_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +GtkWidget* ui_create_grid_widget(int colspacing, int rowspacing); UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid); void ui_grid_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame); +void ui_frame_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); + +UiContainer* ui_expander_container(UiObject *obj, GtkWidget *expander); +void ui_expander_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); + UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow); void ui_scrolledwindow_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); @@ -125,10 +190,17 @@ void ui_split_container_add1(UiContainer *ct, GtkWidget *widget, UiBool fill); void ui_split_container_add2(UiContainer *ct, GtkWidget *widget, UiBool fill); +UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview); -UiObject* ui_add_document_tab(UiDocumentView *view); -void ui_tab_set_document(UiContext *ctx, void *document); -void ui_tab_detach_document(UiContext *ctx); +void ui_gtk_notebook_select_tab(GtkWidget *widget, int tab); + +#if GTK_CHECK_VERSION(3, 10, 0) +UiContainer* ui_headerbar_container(UiObject *obj, GtkWidget *headerbar); +void ui_headerbar_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); +#endif + +UiContainer* ui_headerbar_fallback_container(UiObject *obj, GtkWidget *headerbar); +void ui_headerbar_fallback_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill); #ifdef __cplusplus }
--- a/ui/gtk/display.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/display.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,12 +31,14 @@ #include "display.h" #include "container.h" -#include <ucx/mempool.h> #include "../common/context.h" #include "../common/object.h" +#include "../ui/display.h" + +#include <cx/printf.h> static void set_alignment(GtkWidget *widget, float xalign, float yalign) { -#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16 +#if GTK_MAJOR_VERSION >= 4 || (GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16) gtk_label_set_xalign(GTK_LABEL(widget), xalign); gtk_label_set_yalign(GTK_LABEL(widget), yalign); #else @@ -44,30 +46,108 @@ #endif } -UIWIDGET ui_label(UiObject *obj, char *label) { - GtkWidget *widget = gtk_label_new(label); +UIWIDGET ui_label_create(UiObject *obj, UiLabelArgs args) { + UiObject* current = uic_current_obj(obj); - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, widget, FALSE); + const char *css_class = NULL; + char *markup = NULL; + if(args.label) { + #if GTK_MAJOR_VERSION < 3 + switch(args.style) { + case UI_LABEL_STYLE_DEFAULT: break; + case UI_LABEL_STYLE_TITLE: { + cxmutstr m = cx_asprintf("<b>%s</b>", args.label); + markup = m.ptr; + args.label = NULL; + } + case UI_LABEL_STYLE_SUBTITLE: { + break; + } + case UI_LABEL_STYLE_DIM: { + break; + } + } +# else + switch(args.style) { + case UI_LABEL_STYLE_DEFAULT: break; + case UI_LABEL_STYLE_TITLE: { + css_class = "ui_label_title"; + break; + } + case UI_LABEL_STYLE_SUBTITLE: { + css_class = "subtitle"; + break; + } + case UI_LABEL_STYLE_DIM: { + css_class = "dim-label"; + break; + } + } +# endif + } + + + GtkWidget *widget = gtk_label_new(args.label); + if(markup) { + gtk_label_set_markup(GTK_LABEL(widget), markup); + free(markup); + } + + if(css_class) { + WIDGET_ADD_CSS_CLASS(widget, css_class); + } + + switch(args.align) { + case UI_ALIGN_DEFAULT: break; + case UI_ALIGN_LEFT: set_alignment(widget, 0, .5); break; + case UI_ALIGN_RIGHT: set_alignment(widget, 1, .5); break; + case UI_ALIGN_CENTER: break; // TODO + } + + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if(var) { + UiString* value = (UiString*)var->value; + value->obj = widget; + value->get = ui_label_get; + value->set = ui_label_set; + } + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, widget, FALSE); return widget; } -UIWIDGET ui_llabel(UiObject *obj, char *label) { - UIWIDGET widget = ui_label(obj, label); - set_alignment(widget, 0, .5); - return widget; +UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args) { + args.align = UI_ALIGN_LEFT; + return ui_label_create(obj, args); +} + +UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args) { + args.align = UI_ALIGN_RIGHT; + return ui_label_create(obj, args); } -UIWIDGET ui_rlabel(UiObject *obj, char *label) { - UIWIDGET widget = ui_label(obj, label); - //gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_RIGHT); - - set_alignment(widget, 1, .5); - return widget; +char* ui_label_get(UiString *s) { + if(s->value.ptr) { + s->value.free(s->value.ptr); + } + s->value.ptr = g_strdup(gtk_label_get_text(GTK_LABEL(s->obj))); + s->value.free = (ui_freefunc)g_free; + return s->value.ptr; } -UIWIDGET ui_space(UiObject *obj) { +void ui_label_set(UiString *s, const char *value) { + gtk_label_set_text(GTK_LABEL(s->obj), value); + if(s->value.ptr) { + s->value.free(s->value.ptr); + s->value.ptr = NULL; + s->value.free = NULL; + } +} + +UIWIDGET ui_space_deprecated(UiObject *obj) { GtkWidget *widget = gtk_label_new(""); UiContainer *ct = uic_get_current_container(obj); ct->add(ct, widget, TRUE); @@ -75,8 +155,8 @@ return widget; } -UIWIDGET ui_separator(UiObject *obj) { -#if UI_GTK3 +UIWIDGET ui_separator_deprecated(UiObject *obj) { +#if GTK_MAJOR_VERSION >= 3 GtkWidget *widget = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); #else GtkWidget *widget = gtk_hseparator_new(); @@ -89,40 +169,97 @@ /* ------------------------- progress bar ------------------------- */ -UIWIDGET ui_progressbar(UiObject *obj, UiDouble *value) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - return ui_progressbar_var(obj, var); -} +typedef struct UiProgressBarRange { + double min; + double max; +} UiProgressBarRange; -UIWIDGET ui_progressbar_nv(UiObject *obj, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_DOUBLE); - return ui_progressbar_var(obj, var); -} - -UIWIDGET ui_progressbar_var(UiObject *obj, UiVar *var) { +UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs args) { + UiObject* current = uic_current_obj(obj); + GtkWidget *progressbar = gtk_progress_bar_new(); + if(args.max > args.min) { + UiProgressBarRange *range = malloc(sizeof(UiProgressBarRange)); + range->min = args.min; + range->max = args.max; + g_signal_connect( + progressbar, + "destroy", + G_CALLBACK(ui_destroy_userdata), + range); + g_object_set_data(G_OBJECT(progressbar), "ui_range", range); + } + + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_DOUBLE); if(var && var->value) { UiDouble *value = var->value; value->get = ui_progressbar_get; value->set = ui_progressbar_set; value->obj = progressbar; - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), 0.5); + ui_progressbar_set(value, value->value); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, progressbar, FALSE); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, progressbar, FALSE); return progressbar; } double ui_progressbar_get(UiDouble *d) { - d->value = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(d->obj)); + UiProgressBarRange *range = g_object_get_data(d->obj, "ui_range"); + double fraction = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(d->obj)); + if(range) { + fraction = range->min + (range->max - range->min) * fraction; + } + d->value = fraction; return d->value; } void ui_progressbar_set(UiDouble *d, double value) { + d->value = value; + UiProgressBarRange *range = g_object_get_data(d->obj, "ui_range"); + if(range) { + value = (value - range->min) / (range->max - range->min); + } gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(d->obj), value); - d->value = value; } + + +/* ------------------------- progress spinner ------------------------- */ + +UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs args) { + UiObject* current = uic_current_obj(obj); + + GtkWidget *spinner = gtk_spinner_new(); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + if(var && var->value) { + UiInteger *value = var->value; + value->get = ui_spinner_get; + value->set = ui_spinner_set; + value->obj = spinner; + ui_spinner_set(value, value->value); + } + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, spinner, FALSE); + + return spinner; +} + +int64_t ui_spinner_get(UiInteger *i) { + return i->value; +} + +void ui_spinner_set(UiInteger *i, int64_t value) { + i->value = value; + if(i->obj) { + GtkSpinner *spinner = GTK_SPINNER(i->obj); + if(value != 0) { + gtk_spinner_start(spinner); + } else { + gtk_spinner_stop(spinner); + } + } +}
--- a/ui/gtk/display.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/display.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,9 +36,14 @@ extern "C" { #endif +char* ui_label_get(UiString *s); +void ui_label_set(UiString *s, const char *value); + UIWIDGET ui_progressbar_var(UiObject *obj, UiVar *var); double ui_progressbar_get(UiDouble *d); void ui_progressbar_set(UiDouble *d, double value); +int64_t ui_spinner_get(UiInteger *i); +void ui_spinner_set(UiInteger *i, int64_t value); #ifdef __cplusplus }
--- a/ui/gtk/dnd.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/dnd.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,20 +31,21 @@ #include <string.h> #include "dnd.h" -#include <ucx/buffer.h> +#include <cx/buffer.h> +#include <cx/array_list.h> #ifdef UI_GTK2LEGACY static gboolean selection_data_set_uris(GtkSelectionData *selection_data, char **uris) { - UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *buf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); char *uri; int i = 0; while((uri = uris[i]) != NULL) { - ucx_buffer_puts(buf, uri); - ucx_buffer_puts(buf, "\r\n"); + cxBufferPutString(buf, uri); + cxBufferPutString(buf, "\r\n"); } GdkAtom type = gdk_atom_intern("text/uri-list", FALSE); gtk_selection_data_set(selection_data, type, 8, (guchar*)buf->space, buf->pos); - ucx_buffer_free(buf); + cxBufferFree(buf); return TRUE; } static char** selection_data_get_uris(GtkSelectionData *selection_data) { @@ -55,6 +56,7 @@ #define gtk_selection_data_get_uris selection_data_get_uris #endif +/* void ui_selection_settext(UiSelection *sel, char *str, int len) { // TODO: handle error? gtk_selection_data_set_text(sel->data, str, len); @@ -99,3 +101,195 @@ } return NULL; } +*/ + +#if GTK_MAJOR_VERSION >= 4 + +void ui_selection_settext(UiDnD *sel, char *str, int len) { + if(!sel->providers) { + return; + } + + if(len == -1) { + len = strlen(str); + } + GBytes *bytes = g_bytes_new(str, len); + GdkContentProvider *provider = gdk_content_provider_new_for_bytes("text/plain;charset=utf-8", bytes); + g_bytes_unref(bytes); + + cxListAdd(sel->providers, &provider); +} + +void ui_selection_seturis(UiDnD *sel, char **uris, int nelm) { + if(!sel->providers) { + return; + } + + GFile **files = calloc(nelm, sizeof(GFile*)); + for(int i=0;i<nelm;i++) { + GFile *file = uris[i][0] == '/' ? g_file_new_for_path(uris[i]) : g_file_new_for_uri(uris[i]); + files[i] = file; + } + GdkFileList *list = gdk_file_list_new_from_array(files, nelm); + + GdkContentProvider *provider = gdk_content_provider_new_typed(GDK_TYPE_FILE_LIST, list); + cxListAdd(sel->providers, &provider); + + g_slist_free_full ((GSList*)list, g_object_unref); + free(files); +} + +char* ui_selection_gettext(UiDnD *sel) { + if(!sel->value) { + return NULL; + } + + if(G_VALUE_HOLDS(sel->value, G_TYPE_STRING)) { + const char *str = g_value_get_string(sel->value); + return str ? strdup(str) : NULL; + } + + return NULL; +} + +UiFileList ui_selection_geturis(UiDnD *sel) { + if(!sel->value) { + return (UiFileList){NULL,0}; + } + + if(G_VALUE_HOLDS(sel->value, GDK_TYPE_FILE_LIST)) { + GSList *list = g_value_get_boxed(sel->value); + if(!list) { + return (UiFileList){NULL,0}; + } + guint size = g_slist_length(list); + + UiFileList flist; + flist.nfiles = size; + flist.files = calloc(size, sizeof(char*)); + int i=0; + while(list) { + GFile *file = list->data; + char *uri = g_file_get_uri(file); + flist.files[i++] = strdup(uri); + g_free(uri); + list = list->next; + } + return flist; + } + return (UiFileList){NULL,0}; +} + + +UiDnD* ui_create_dnd(void) { + UiDnD *dnd = malloc(sizeof(UiDnD)); + memset(dnd, 0, sizeof(UiDnD)); + dnd->providers = cxArrayListCreateSimple(sizeof(void*), 16); + dnd->selected_action = 0; + dnd->delete = FALSE; + return dnd; +} + +void ui_dnd_free(UiDnD *dnd) { + cxListDestroy(dnd->providers); + free(dnd); +} + +UiDnDAction ui_dnd_result(UiDnD *dnd) { + switch(dnd->selected_action) { + case 0: return UI_DND_ACTION_NONE; + case GDK_ACTION_COPY: return UI_DND_ACTION_COPY; + case GDK_ACTION_MOVE: return UI_DND_ACTION_MOVE; + case GDK_ACTION_LINK: return UI_DND_ACTION_LINK; + default: break; + } + return UI_DND_ACTION_CUSTOM; +} + +#else + +void ui_selection_settext(UiDnD *sel, char *str, int len) { + gtk_selection_data_set_text(sel->data, str, len); +} + +void ui_selection_seturis(UiDnD *sel, char **uris, int nelm) { + char **uriarray = calloc(nelm+1, sizeof(char*)); + for(int i=0;i<nelm;i++) { + uriarray[i] = uris[i]; + } + uriarray[nelm] = NULL; + gtk_selection_data_set_uris(sel->data, uriarray); + free(uriarray); +} + +char* ui_selection_gettext(UiDnD *sel) { + if(!sel->data) { + return NULL; + } + + guchar *text = gtk_selection_data_get_text(sel->data); + if(text) { + char *textcp = strdup((char*)text); + g_free(text); + return textcp; + } + return NULL; +} + +UiFileList ui_selection_geturis(UiDnD *sel) { + if(!sel->data) { + return (UiFileList){NULL,0}; + } + + gchar **uris = gtk_selection_data_get_uris(sel->data); + if(uris) { + size_t al = 32; + char **array = malloc(al * sizeof(char*)); + size_t i = 0; + while(uris[i] != NULL) { + if(i >= al) { + al *= 2; + array = realloc(array, al * sizeof(char*)); + } + array[i] = strdup((char*)uris[i]); + i++; + } + g_strfreev(uris); + return (UiFileList){array,i}; + } + + return (UiFileList){NULL,0}; +} + +UiDnDAction ui_dnd_result(UiDnD *dnd) { + switch(dnd->selected_action) { + case 0: return UI_DND_ACTION_NONE; + case GDK_ACTION_COPY: return UI_DND_ACTION_COPY; + case GDK_ACTION_MOVE: return UI_DND_ACTION_MOVE; + case GDK_ACTION_LINK: return UI_DND_ACTION_LINK; + default: break; + } + return UI_DND_ACTION_CUSTOM; +} + + +UiDnD* ui_create_dnd(void) { + UiDnD *dnd = malloc(sizeof(UiDnD)); + memset(dnd, 0, sizeof(UiDnD)); + return dnd; +} + +void ui_dnd_free(UiDnD *dnd) { + free(dnd); +} + +#endif + +UiBool ui_dnd_need_delete(UiDnD *dnd) { + return dnd->delete; +} + +void ui_dnd_accept(UiDnD *dnd, UiBool accept) { + dnd->accept = accept; +} +
--- a/ui/gtk/dnd.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/dnd.h Sat Jan 04 16:38:48 2025 +0100 @@ -32,12 +32,37 @@ #include "../ui/dnd.h" #include "toolkit.h" +#include <cx/list.h> + #ifdef __cplusplus extern "C" { #endif +#if GTK_MAJOR_VERSION >= 4 + +struct UiDnD { + GtkDropTarget *target; + const GValue *value; + CxList *providers; + GdkDragAction selected_action; + gboolean delete; + gboolean accept; +}; + +#else + +struct UiDnD { + GdkDragContext *context; + GtkSelectionData *data; + GdkDragAction selected_action; + gboolean delete; + gboolean accept; +}; +#endif +UiDnD* ui_create_dnd(void); +void ui_dnd_free(UiDnD *dnd); #ifdef __cplusplus }
--- a/ui/gtk/draw_cairo.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/draw_cairo.c Sat Jan 04 16:38:48 2025 +0100 @@ -33,12 +33,19 @@ #include "draw_cairo.h" -#ifdef UI_GTK3 -gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) { + +#if GTK_MAJOR_VERSION >= 3 +static void ui_drawingarea_draw( + GtkDrawingArea *area, + cairo_t *cr, + int width, + int height, + gpointer data) +{ UiCairoGraphics g; - g.g.width = gtk_widget_get_allocated_width(w); - g.g.height = gtk_widget_get_allocated_height(w); - g.widget = w; + g.g.width = width; + g.g.height = height; + g.widget = GTK_WIDGET(area); g.cr = cr; UiDrawEvent *event = data; @@ -48,10 +55,18 @@ ev.document = event->obj->ctx->document; event->callback(&ev, &g.g, event->userdata); - +} +#endif + +#if UI_GTK3 +gboolean ui_drawingarea_expose(GtkWidget *w, cairo_t *cr, void *data) { + int width = gtk_widget_get_allocated_width(w); + int height = gtk_widget_get_allocated_height(w); + ui_drawingarea_draw(GTK_DRAWING_AREA(w), cr, width, height, data); return FALSE; } -#else +#endif +#ifdef UI_GTK2 gboolean ui_canvas_expose(GtkWidget *w, GdkEventExpose *e, void *data) { UiCairoGraphics g; g.g.width = w->allocation.width; @@ -74,7 +89,9 @@ // function from graphics.h void ui_connect_draw_handler(GtkWidget *widget, UiDrawEvent *event) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 4 + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(widget), ui_drawingarea_draw, event, NULL); +#elif GTK_MAJOR_VERSION == 3 g_signal_connect(G_OBJECT(widget), "draw", G_CALLBACK(ui_drawingarea_expose),
--- a/ui/gtk/entry.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/entry.c Sat Jan 04 16:38:48 2025 +0100 @@ -34,67 +34,49 @@ #include "container.h" #include "entry.h" -#include <ucx/mempool.h> -UIWIDGET ui_spinner(UiObject *obj, int step, UiInteger *i) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = i; - var->type = UI_VAR_SPECIAL; - return ui_spinner_var(obj, step, 0, var, UI_VAR_INTEGER); -} - -UIWIDGET ui_spinnerf(UiObject *obj, double step, int digits, UiDouble *d) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = d; - var->type = UI_VAR_SPECIAL; - return ui_spinner_var(obj, step, digits, var, UI_VAR_DOUBLE); -} - -UIWIDGET ui_spinnerr(UiObject *obj, UiRange *r) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = r; - var->type = UI_VAR_SPECIAL; - return ui_spinner_var(obj, r->extent, 1, var, UI_VAR_RANGE); -} - -UIWIDGET ui_spinner_nv(UiObject *obj, int step, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_INTEGER); - return ui_spinner_var(obj, step, 0, var, UI_VAR_INTEGER); -} - -UIWIDGET ui_spinnerf_nv(UiObject *obj, double step, int digits, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_DOUBLE); - return ui_spinner_var(obj, step, digits, var, UI_VAR_DOUBLE); -} - -UIWIDGET ui_spinnerr_nv(UiObject *obj, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_RANGE); - UiRange *r = var->value; - return ui_spinner_var(obj, r->extent, 1, var, UI_VAR_RANGE); -} - -UIWIDGET ui_spinner_var(UiObject *obj, double step, int digits, UiVar *var, UiVarType type) { +UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs args) { double min = 0; double max = 1000; - if(type == UI_VAR_RANGE) { + + UiObject* current = uic_current_obj(obj); + + UiVar *var = NULL; + if(args.varname) { + var = uic_get_var(obj->ctx, args.varname); + } + + if(!var) { + if(args.intvalue) { + var = uic_widget_var(obj->ctx, current->ctx, args.intvalue, NULL, UI_VAR_INTEGER); + } else if(args.doublevalue) { + var = uic_widget_var(obj->ctx, current->ctx, args.doublevalue, NULL, UI_VAR_DOUBLE); + } else if(args.rangevalue) { + var = uic_widget_var(obj->ctx, current->ctx, args.rangevalue, NULL, UI_VAR_RANGE); + } + } + + if(var && var->type == UI_VAR_RANGE) { UiRange *r = var->value; min = r->min; max = r->max; } - if(step == 0) { - step = 1; + if(args.step == 0) { + args.step = 1; } #ifdef UI_GTK2LEGACY if(min == max) { max = min + 1; } #endif - GtkWidget *spin = gtk_spin_button_new_with_range(min, max, step); - gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), digits); + GtkWidget *spin = gtk_spin_button_new_with_range(min, max, args.step); + ui_set_name_and_style(spin, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, spin, args.groups); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spin), args.digits); + UiObserver **obs = NULL; if(var) { double value = 0; - UiObserver **obs = NULL; - switch(type) { + switch(var->type) { default: break; case UI_VAR_INTEGER: { UiInteger *i = var->value; @@ -127,26 +109,28 @@ } } gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); - - UiVarEventData *event = malloc(sizeof(UiVarEventData)); - event->obj = obj; - event->var = var; - event->observers = obs; - - g_signal_connect( - spin, - "value-changed", - G_CALLBACK(ui_spinner_changed), - event); - g_signal_connect( - spin, - "destroy", - G_CALLBACK(ui_destroy_vardata), - event); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, spin, FALSE); + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->var = var; + event->observers = obs; + event->callback = args.onchange; + event->userdata = args.onchangedata; + + g_signal_connect( + spin, + "value-changed", + G_CALLBACK(ui_spinner_changed), + event); + g_signal_connect( + spin, + "destroy", + G_CALLBACK(ui_destroy_vardata), + event); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, spin, FALSE); return spin; } @@ -161,15 +145,22 @@ void ui_spinner_changed(GtkSpinButton *spinner, UiVarEventData *event) { + gdouble value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinner)); UiEvent e; e.obj = event->obj; e.window = event->obj->window; e.document = event->obj->ctx->document; - e.eventdata = event->var->value; - e.intval = 0; + e.eventdata = &value; + e.intval = (int64_t)value; - UiObserver *observer = *event->observers; - ui_notify_evt(observer, &e); + if(event->callback) { + event->callback(&e, event->userdata); + } + + if(event->observers) { + UiObserver *observer = *event->observers; + ui_notify_evt(observer, &e); + } }
--- a/ui/gtk/entry.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/entry.h Sat Jan 04 16:38:48 2025 +0100 @@ -22,7 +22,6 @@ extern "C" { #endif -UIWIDGET ui_spinner_var(UiObject *obj, double step, int digits, UiVar *var, UiVarType type); void ui_spinner_changed(GtkSpinButton *spinner, UiVarEventData *event); int64_t ui_spinbutton_getint(UiInteger *i);
--- a/ui/gtk/graphics.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/graphics.c Sat Jan 04 16:38:48 2025 +0100 @@ -51,6 +51,7 @@ } +#if GTK_MAJOR_VERSION <= 3 static gboolean widget_button_pressed( GtkWidget *widget, GdkEvent *event, @@ -82,14 +83,18 @@ } return TRUE; } +#endif void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { -#ifdef UI_GTK3 - *width = gtk_widget_get_allocated_width(drawingarea); - *height = gtk_widget_get_allocated_height(drawingarea); +#if GTK_MAJOR_VERSION >= 4 + *width = gtk_widget_get_width(drawingarea); + *height = gtk_widget_get_height(drawingarea); +#elif GTK_MAJOR_VERSION == 3 + *width = gtk_widget_get_allocated_width(drawingarea); + *height = gtk_widget_get_allocated_height(drawingarea); #else - *width = drawingarea->allocation.width; - *height = drawingarea->allocation.height; + *width = drawingarea->allocation.width; + *height = drawingarea->allocation.height; #endif } @@ -98,12 +103,17 @@ } void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) { +#if GTK_MAJOR_VERSION >= 4 + // TODO +#else gtk_widget_set_events(widget, GDK_BUTTON_PRESS_MASK); if(f) { UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; event->callback = f; event->userdata = u; + event->customdata = NULL; + event->value = 0; g_signal_connect(G_OBJECT(widget), "button-press-event", @@ -112,6 +122,7 @@ } else { // TODO: warning } +#endif }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/headerbar.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,174 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "headerbar.h" + +#include "button.h" +#include "menu.h" + +#if GTK_CHECK_VERSION(3, 10, 0) + +void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar) { + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + ui_headerbar_add_items(obj, headerbar, left_defaults, UI_TOOLBAR_LEFT); + ui_headerbar_add_items(obj, headerbar, center_defaults, UI_TOOLBAR_CENTER); + + UiToolbarMenuItem *appmenu = uic_get_appmenu(); + if(appmenu) { + ui_add_headerbar_menu(headerbar, NULL, appmenu, obj, UI_TOOLBAR_RIGHT); + } + ui_headerbar_add_items(obj, headerbar, right_defaults, UI_TOOLBAR_RIGHT); +} + +static void create_item(UiObject *obj, GtkWidget *headerbar, GtkWidget *box, UiToolbarItemI *i, enum UiToolbarPos pos) { + switch(i->type) { + case UI_TOOLBAR_ITEM: { + ui_add_headerbar_item(headerbar, box, (UiToolbarItem*)i, obj, pos); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + ui_add_headerbar_toggleitem(headerbar, box, (UiToolbarToggleItem*)i, obj, pos); + break; + } + case UI_TOOLBAR_MENU: { + ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos); + break; + } + default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); + } +} + +static void headerbar_add(GtkWidget *headerbar, GtkWidget *box, GtkWidget *item, enum UiToolbarPos pos) { + switch(pos) { + case UI_TOOLBAR_LEFT: { + UI_HEADERBAR_PACK_START(headerbar, item); + break; + } + case UI_TOOLBAR_CENTER: { + +#if GTK_MAJOR_VERSION >= 4 + gtk_box_append(GTK_BOX(box), item); +#else + gtk_box_pack_start(GTK_BOX(box), item, 0, 0, 0); +#endif + break; + } + case UI_TOOLBAR_RIGHT: { + UI_HEADERBAR_PACK_END(headerbar, item); + break; + } + } +} + +void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos) { + GtkWidget *box = NULL; + + if(pos == UI_TOOLBAR_CENTER && cxListSize(items) > 0) { + box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + UI_HEADERBAR_SET_TITLE_WIDGET(headerbar, box); + } + + CxIterator i = pos == UI_TOOLBAR_RIGHT ? cxListBackwardsIterator(items) : cxListIterator(items); + cx_foreach(char*, def, i) { + UiToolbarItemI* item = uic_toolbar_get_item(def); + if (!item) { + fprintf(stderr, "unknown toolbar item: %s\n", def); + continue; + } + create_item(obj, headerbar, box, item, pos); + } +} + +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata, 0, FALSE); + ui_set_widget_groups(obj->ctx, button, item->args.groups); + WIDGET_ADD_CSS_CLASS(button, "flat"); + headerbar_add(headerbar, box, button, pos); +} + +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *button = gtk_toggle_button_new(); + ui_set_widget_groups(obj->ctx, button, item->args.groups); + WIDGET_ADD_CSS_CLASS(button, "flat"); + ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); + headerbar_add(headerbar, box, button, pos); +} + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + + +#if GTK_MAJOR_VERSION >= 4 + GtkWidget *menubutton = gtk_menu_button_new(); + if(item->args.label) { + gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label); + } + if(item->args.icon) { + gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), item->args.icon); + } + + if(!item->args.label && !item->args.icon) { + gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), "open-menu-symbolic"); + } + + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, &item->menu, obj); + + gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu)); +#else + GtkWidget *menubutton = gtk_menu_button_new(); + + // TODO + + +#endif + + headerbar_add(headerbar, box, menubutton, pos); +} + +#endif // GTK_CHECK_VERSION(3, 10, 0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/headerbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,93 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HEADERBAR_H +#define HEADERBAR_H + +#include "toolkit.h" +#include "../ui/toolbar.h" +#include "../common/toolbar.h" +#include <cx/list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if GTK_CHECK_VERSION(3, 10, 0) + +#ifdef UI_LIBADWAITA +#define UI_HEADERBAR AdwHeaderBar* +#define UI_HEADERBAR_CAST(h) ADW_HEADER_BAR(h) +#define UI_HEADERBAR_PACK_START(h, w) adw_header_bar_pack_start(ADW_HEADER_BAR(h), w) +#define UI_HEADERBAR_PACK_END(h, w) adw_header_bar_pack_end(ADW_HEADER_BAR(h), w) +#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) adw_header_bar_set_title_widget(ADW_HEADER_BAR(h), w) +#else +#define UI_HEADERBAR GtkHeaderBar* +#define UI_HEADERBAR_CAST(h) GTK_HEADER_BAR(h) +#define UI_HEADERBAR_PACK_START(h, w) gtk_header_bar_pack_start(GTK_HEADER_BAR(h), w) +#define UI_HEADERBAR_PACK_END(h, w) gtk_header_bar_pack_end(GTK_HEADER_BAR(h), w) +#if GTK_MAJOR_VERSION >= 4 +#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w) +#else +#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w) +#endif +#endif + +void ui_fill_headerbar(UiObject *obj, GtkWidget *headerbar); + +void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos); + +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HEADERBAR_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/icon.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,208 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <cx/map.h> + +#include "toolkit.h" +#include "icon.h" +#include "../common/properties.h" + +static CxMap *image_map; + +static GtkIconTheme *icon_theme; + +#if GTK_MAJOR_VERSION >= 4 +#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_for_display(gdk_display_get_default()) +#else +#define ICONTHEME_GET_DEFAULT() gtk_icon_theme_get_default() +#endif + +void ui_image_init(void) { + image_map = cxHashMapCreateSimple(CX_STORE_POINTERS); + + icon_theme = ICONTHEME_GET_DEFAULT(); +} + +// **** deprecated functions **** + +GdkPixbuf* ui_get_image(const char *name) { + UiImage *img = cxMapGet(image_map, name); + if(img) { + return img->pixbuf; + } else { + //ui_add_image(name, name); + //return ucx_map_cstr_get(image_map, name); + // TODO + return NULL; + } +} + +// **** deprecated2**** + +static UiIcon* get_icon(const char *name, int size, int scale) { +#if GTK_MAJOR_VERSION >= 4 + GtkIconPaintable *info = gtk_icon_theme_lookup_icon(icon_theme, name, NULL, size, scale, GTK_TEXT_DIR_LTR, GTK_ICON_LOOKUP_FORCE_REGULAR); +#elif defined(UI_SUPPORTS_SCALE) + GtkIconInfo *info = gtk_icon_theme_lookup_icon_for_scale(icon_theme, name, size, scale, 0); +#else + GtkIconInfo *info = gtk_icon_theme_lookup_icon(icon_theme, name, size, 0); +#endif + if(info) { + UiIcon *icon = malloc(sizeof(UiIcon)); + icon->info = info; + icon->pixbuf = NULL; + return icon; + } + return NULL; +} + +UiIcon* ui_icon(const char* name, size_t size) { + return get_icon(name, size, ui_get_scalefactor()); +} + +UiIcon* ui_imageicon(const char* file) { + GError *error = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(file, &error); + if(!pixbuf) { + fprintf(stderr, "UiError: Cannot load image: %s\n", file); + return NULL; + } + + UiIcon *icon = malloc(sizeof(UiIcon)); + icon->info = NULL; + icon->pixbuf = pixbuf; + return icon; +} + +void ui_icon_free(UiIcon* icon) { + if(icon->info) { + g_object_unref(icon->info); + } + if(icon->pixbuf) { + g_object_unref(icon->pixbuf); + } + free(icon); +} + +UiIcon* ui_foldericon(size_t size) { + return ui_icon("folder", size); +} + +UiIcon* ui_fileicon(size_t size) { + UiIcon *icon = ui_icon("file", size); +#if GTK_MAJOR_VERSION >= 4 + GFile *file = gtk_icon_paintable_get_file(icon->info); + char *path = g_file_get_path(file); + if(!path) { + icon = ui_icon("application-x-generic", size); + } +#else + if(!icon) { + icon = ui_icon("application-x-generic", size); + } +#endif + return icon; +} + +UiIcon* ui_icon_unscaled(const char *name, int size) { + return get_icon(name, size, 1); +} + +#if GTK_MAJOR_VERSION >= 4 +GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) { + if(!icon->pixbuf) { + GFile *file = gtk_icon_paintable_get_file(icon->info); + GError *error = NULL; + char *path = g_file_get_path(file); + icon->pixbuf = gdk_pixbuf_new_from_file(path, &error); + } + return icon->pixbuf; +} +#else +GdkPixbuf* ui_icon_pixbuf(UiIcon *icon) { + if(!icon->pixbuf) { + GError *error = NULL; + icon->pixbuf = gtk_icon_info_load_icon(icon->info, &error); + } + return icon->pixbuf; +} +#endif + +/* +UiImage* ui_icon_image(UiIcon *icon) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + if(pixbuf) { + UiImage *img = malloc(sizeof(UiImage)); + img->pixbuf = pixbuf; + return img; + } + return NULL; +} +*/ + +/* +UiImage* ui_image(const char *filename) { + return ui_named_image(filename, NULL); +} + +UiImage* ui_named_image(const char *filename, const char *name) { + char *path = uic_get_image_path(filename); + if(!path) { + fprintf(stderr, "UiError: pixmaps directory not set\n"); + return NULL; + } + UiImage *img = ui_load_image_from_path(path, name); + free(path); + return img; +} + +UiImage* ui_load_image_from_path(const char *path, const char *name) { + GError *error = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error); + if(!pixbuf) { + fprintf(stderr, "UiError: Cannot load image: %s\n", path); + return NULL; + } + + UiImage *img = malloc(sizeof(UiImage)); + img->pixbuf = pixbuf; + if(name) { + cxMapPut(image_map, name, img); + } + return img; +} +*/ + +void ui_free_image(UiImage *img) { + g_object_unref(img->pixbuf); + free(img); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/icon.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ICON_H +#define ICON_H + +#include "../ui/icons.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 10 +#define UI_SUPPORTS_SCALE +#endif + + +struct UiIcon { +#if GTK_MAJOR_VERSION >= 4 + GtkIconPaintable *info; +#else + GtkIconInfo *info; +#endif + GdkPixbuf *pixbuf; +}; + +struct UiImage { + GdkPixbuf *pixbuf; +}; + +void ui_image_init(void); + +GdkPixbuf* ui_get_image(const char *name); + +GdkPixbuf* ui_icon_pixbuf(UiIcon *icon); + +#ifdef __cplusplus +} +#endif + +#endif /* ICON_H */ +
--- a/ui/gtk/image.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/image.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,111 +26,110 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ucx/map.h> +#include "image.h" + +#include "container.h" +#include "menu.h" +#include "../common/context.h" +#include "../common/object.h" + -#include "toolkit.h" -#include "image.h" -#include "../common/properties.h" - -static UcxMap *image_map; - -static GtkIconTheme *icon_theme; - -void ui_image_init(void) { - image_map = ucx_map_new(8); +UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { + UiObject *current = uic_current_obj(obj); + + GtkWidget *scrolledwindow = SCROLLEDWINDOW_NEW(); +#if GTK_CHECK_VERSION(4, 0, 0) + GtkWidget *image = gtk_picture_new(); +#else + GtkWidget *image = gtk_image_new(); +#endif + + ui_set_name_and_style(image, args.name, args.style_class); - icon_theme = gtk_icon_theme_get_default(); -} - -// **** deprecated functions **** - -GdkPixbuf* ui_get_image(const char *name) { - UiImage *img = ucx_map_cstr_get(image_map, name); - if(img) { - return img->pixbuf; - } else { - //ui_add_image(name, name); - //return ucx_map_cstr_get(image_map, name); - // TODO - return NULL; +#if GTK_MAJOR_VERSION < 4 + GtkWidget *eventbox = gtk_event_box_new(); + SCROLLEDWINDOW_SET_CHILD(scrolledwindow, eventbox); + gtk_container_add(GTK_CONTAINER(eventbox), image); +#else + SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image); + GtkWidget *eventbox = image; +#endif + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scrolledwindow, TRUE); + + UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); + if(var) { + UiGeneric *value = var->value; + value->get = ui_imageviewer_get; + value->get_type = ui_imageviewer_get_type; + value->set = ui_imageviewer_set; + value->obj = image; + if(value->value && value->type && !strcmp(value->type, UI_IMAGE_OBJECT_TYPE)) { + GdkPixbuf *pixbuf = value->value; + value->value = NULL; + ui_imageviewer_set(value, pixbuf, UI_IMAGE_OBJECT_TYPE); + } } + + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); + ui_widget_set_contextmenu(eventbox, menu); + } + + return scrolledwindow; } -// **** new functions **** - -static UiIcon* get_icon(const char *name, int size, int scale) { -#ifdef UI_SUPPORTS_SCALE - GtkIconInfo *info = gtk_icon_theme_lookup_icon_for_scale(icon_theme, name, size, scale, 0); -#else - GtkIconInfo *info = gtk_icon_theme_lookup_icon(icon_theme, name, size, 0); -#endif - if(info) { - UiIcon *icon = malloc(sizeof(UiIcon)); - icon->info = info; - return icon; - } - return NULL; +void* ui_imageviewer_get(UiGeneric *g) { + return g->value; } -UiIcon* ui_icon(const char *name, int size) { - return get_icon(name, size, ui_get_scalefactor()); -} - -UiIcon* ui_icon_unscaled(const char *name, int size) { - return get_icon(name, size, 1); +const char* ui_imageviewer_get_type(UiGeneric *g) { + } -void ui_free_icon(UiIcon *icon) { - g_object_unref(icon->info); - free(icon); -} +int ui_imageviewer_set(UiGeneric *g, void *value, const char *type) { + if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { + return 1; + } + + // TODO: do we need to free the previous value here? + + g->value = value; + g->type = type; + GdkPixbuf *pixbuf = value; + + if(pixbuf) { + int width = gdk_pixbuf_get_width(pixbuf); + int height = gdk_pixbuf_get_height(pixbuf); + +#if GTK_CHECK_VERSION(4, 0, 0) + GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf); + gtk_picture_set_paintable(GTK_PICTURE(g->obj), GDK_PAINTABLE(texture)); +#else + gtk_image_set_from_pixbuf(GTK_IMAGE(g->obj), pixbuf); +#endif + gtk_widget_set_size_request(g->obj, width, height); + } -UiImage* ui_icon_image(UiIcon *icon) { - GError *error = NULL; - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); - if(pixbuf) { - UiImage *img = malloc(sizeof(UiImage)); - img->pixbuf = pixbuf; - return img; - } - return NULL; + + return 0; } -UiImage* ui_image(const char *filename) { - return ui_named_image(filename, NULL); -} + -UiImage* ui_named_image(const char *filename, const char *name) { - char *path = uic_get_image_path(filename); - if(!path) { - fprintf(stderr, "UiError: pixmaps directory not set\n"); - return NULL; - } - UiImage *img = ui_load_image_from_path(path, name); - free(path); - return img; -} - -UiImage* ui_load_image_from_path(const char *path, const char *name) { +int ui_image_load_file(UiGeneric *obj, const char *path) { GError *error = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error); if(!pixbuf) { - fprintf(stderr, "UiError: Cannot load image: %s\n", path); - return NULL; + return 1; } - UiImage *img = malloc(sizeof(UiImage)); - img->pixbuf = pixbuf; - if(name) { - ucx_map_cstr_put(image_map, name, img); + if(obj->set) { + obj->set(obj, pixbuf, UI_IMAGE_OBJECT_TYPE); + } else { + obj->value = pixbuf; } - return img; + + return 0; } - -void ui_free_image(UiImage *img) { - g_object_unref(img->pixbuf); - free(img); -}
--- a/ui/gtk/image.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/image.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,35 +27,23 @@ */ #ifndef IMAGE_H -#define IMAGE_H +#define IMAGE_H #include "../ui/image.h" +#include "toolkit.h" -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif -#if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 10 -#define UI_SUPPORTS_SCALE -#endif - - -struct UiIcon { - GtkIconInfo *info; -}; -struct UiImage { - GdkPixbuf *pixbuf; -}; +void* ui_imageviewer_get(UiGeneric *g); +const char* ui_imageviewer_get_type(UiGeneric *g); +int ui_imageviewer_set(UiGeneric *g, void *value, const char *type); -void ui_image_init(void); - -GdkPixbuf* ui_get_image(const char *name); - - -#ifdef __cplusplus +#ifdef __cplusplus } #endif -#endif /* IMAGE_H */ +#endif /* IMAGE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/list.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,1278 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "../common/context.h" +#include "../common/object.h" +#include "container.h" + +#include <cx/array_list.h> +#include <cx/linked_list.h> + +#include "list.h" +#include "icon.h" +#include "menu.h" +#include "dnd.h" + + +void* ui_strmodel_getvalue(void *elm, int column) { + return column == 0 ? elm : NULL; +} + +static GtkListStore* create_list_store(UiList *list, UiModel *model) { + int columns = model->columns; + GType types[2*columns]; + int c = 0; + for(int i=0;i<columns;i++,c++) { + switch(model->types[i]) { + case UI_STRING: + case UI_STRING_FREE: types[c] = G_TYPE_STRING; break; + case UI_INTEGER: types[c] = G_TYPE_INT; break; + case UI_ICON: types[c] = G_TYPE_OBJECT; break; + case UI_ICON_TEXT: + case UI_ICON_TEXT_FREE: { + types[c] = G_TYPE_OBJECT; + types[++c] = G_TYPE_STRING; + } + } + } + + GtkListStore *store = gtk_list_store_newv(c, types); + + if(list) { + void *elm = list->first(list); + while(elm) { + // insert new row + GtkTreeIter iter; + gtk_list_store_insert (store, &iter, -1); + + // set column values + int c = 0; + for(int i=0;i<columns;i++,c++) { + void *data = model->getvalue(elm, c); + + GValue value = G_VALUE_INIT; + switch(model->types[i]) { + case UI_STRING: + case UI_STRING_FREE: { + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, data); + if(model->types[i] == UI_STRING_FREE) { + free(data); + } + break; + } + case UI_INTEGER: { + g_value_init(&value, G_TYPE_INT); + int *intptr = data; + g_value_set_int(&value, *intptr); + break; + } + case UI_ICON: { + g_value_init(&value, G_TYPE_OBJECT); + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + g_value_set_object(&value, icon->info); // TODO: does this work? +#else + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + + if(icon->pixbuf) { + g_value_set_object(&value, icon->pixbuf); + } +#endif + break; + } + case UI_ICON_TEXT: + case UI_ICON_TEXT_FREE: { + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + if(icon) { + GValue iconvalue = G_VALUE_INIT; + g_value_init(&iconvalue, G_TYPE_OBJECT); + g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); + gtk_list_store_set_value(store, &iter, c, &iconvalue); + } +#else + GValue pixbufvalue = G_VALUE_INIT; + if(icon) { + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + g_value_init(&pixbufvalue, G_TYPE_OBJECT); + g_value_set_object(&pixbufvalue, icon->pixbuf); + gtk_list_store_set_value(store, &iter, c, &pixbufvalue); + } +#endif + c++; + + char *str = model->getvalue(elm, c); + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, str); + if(model->types[i] == UI_ICON_TEXT_FREE) { + free(str); + } + break; + } + } + + gtk_list_store_set_value(store, &iter, c, &value); + } + + // next row + elm = list->next(list); + } + } + + return store; +} + + +UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create treeview + GtkWidget *view = gtk_tree_view_new(); + ui_set_name_and_style(view, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, view, args.groups); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); +#ifdef UI_GTK3 +#if GTK_MINOR_VERSION >= 8 + //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); +#else + // TODO: implement for older gtk3 +#endif +#else + // TODO: implement for gtk2 +#endif + + UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); + model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(list, model); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + + UiListView *listview = malloc(sizeof(UiListView)); + listview->obj = obj; + listview->widget = view; + listview->var = var; + listview->model = model; + g_signal_connect( + view, + "destroy", + G_CALLBACK(ui_listview_destroy), + listview); + + // bind var + list->update = ui_listview_update; + list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; + list->obj = listview; + + // add callback + UiTreeEventData *event = malloc(sizeof(UiTreeEventData)); + event->obj = obj; + event->activate = args.onactivate; + event->activatedata = args.onactivatedata; + event->selection = args.onselection; + event->selectiondata = args.onselectiondata; + g_signal_connect( + view, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + if(args.onactivate) { + g_signal_connect( + view, + "row-activated", + G_CALLBACK(ui_listview_activate_event), + event); + } + if(args.onselection) { + GtkTreeSelection *selection = gtk_tree_view_get_selection( + GTK_TREE_VIEW(view)); + g_signal_connect( + selection, + "changed", + G_CALLBACK(ui_listview_selection_event), + event); + } + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } + + + // add widget to the current container + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(scroll_area), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS + SCROLLEDWINDOW_SET_CHILD(scroll_area, view); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scroll_area, FALSE); + + // ct->current should point to view, not scroll_area, to make it possible + // to add a context menu + current->container->current = view; + + return scroll_area; +} + +/* +static void drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer udata) { + printf("drag begin\n"); + +} + +static void drag_end( + GtkWidget *widget, + GdkDragContext *context, + guint time, + gpointer udata) +{ + printf("drag end\n"); + +} +*/ + +/* +static GtkTargetEntry targetentries[] = + { + { "STRING", 0, 0 }, + { "text/plain", 0, 1 }, + { "text/uri-list", 0, 2 }, + }; +*/ + +UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create treeview + GtkWidget *view = gtk_tree_view_new(); + + UiModel *model = args.model; + int columns = model ? model->columns : 0; + + int addi = 0; + for(int i=0;i<columns;i++) { + GtkTreeViewColumn *column = NULL; + if(model->types[i] == UI_ICON_TEXT) { + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, model->titles[i]); + + GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); + GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new(); + + gtk_tree_view_column_pack_end(column, textrenderer, TRUE); + gtk_tree_view_column_pack_start(column, iconrenderer, FALSE); + + + gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", i); + gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1); + + addi++; + } else if (model->types[i] == UI_ICON) { + GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes( + model->titles[i], + iconrenderer, + "pixbuf", + i + addi, + NULL); + } else { + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + model->titles[i], + renderer, + "text", + i + addi, + NULL); + } + + int colsz = model->columnsize[i]; + if(colsz > 0) { + gtk_tree_view_column_set_fixed_width(column, colsz); + } else if(colsz < 0) { + gtk_tree_view_column_set_expand(column, TRUE); + } + + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); + } + + //gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); +#ifdef UI_GTK3 + //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); +#else + +#endif + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(list, model); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + + //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); + //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); + + // add TreeView as observer to the UiList to update the TreeView if the + // data changes + UiListView *tableview = malloc(sizeof(UiListView)); + tableview->obj = obj; + tableview->widget = view; + tableview->var = var; + tableview->model = model; + tableview->ondragstart = args.ondragstart; + tableview->ondragstartdata = args.ondragstartdata; + tableview->ondragcomplete = args.ondragcomplete; + tableview->ondragcompletedata = args.ondragcompletedata; + tableview->ondrop = args.ondrop; + tableview->ondropdata = args.ondropsdata; + g_signal_connect( + view, + "destroy", + G_CALLBACK(ui_listview_destroy), + tableview); + + // bind var + list->update = ui_listview_update; + list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; + list->obj = tableview; + + // add callback + UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); + event->obj = obj; + event->activate = args.onactivate; + event->selection = args.onselection; + event->activatedata = args.onactivatedata; + event->selectiondata = args.onselectiondata; + if(args.onactivate) { + g_signal_connect( + view, + "row-activated", + G_CALLBACK(ui_listview_activate_event), + event); + } + if(args.onselection) { + GtkTreeSelection *selection = gtk_tree_view_get_selection( + GTK_TREE_VIEW(view)); + g_signal_connect( + selection, + "changed", + G_CALLBACK(ui_listview_selection_event), + event); + } + // TODO: destroy callback + + + if(args.ondragstart) { + ui_listview_add_dnd(tableview, &args); + } + if(args.ondrop) { + ui_listview_enable_drop(tableview, &args); + } + + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); + if(args.multiselection) { + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + } + + // add widget to the current container + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(scroll_area), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS + SCROLLEDWINDOW_SET_CHILD(scroll_area, view); + + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area); +#if GTK_MAJOR_VERSION >= 4 + ui_widget_set_contextmenu(scroll_area, menu); +#else + ui_widget_set_contextmenu(view, menu); +#endif + } + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scroll_area, FALSE); + + // ct->current should point to view, not scroll_area, to make it possible + // to add a context menu + current->container->current = view; + + return scroll_area; +} + +#if GTK_MAJOR_VERSION >= 4 + +static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) { + //printf("drag prepare\n"); + UiListView *listview = data; + + UiDnD *dnd = ui_create_dnd(); + GdkContentProvider *provider = NULL; + + + if(listview->ondragstart) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = dnd; + event.intval = 0; + listview->ondragstart(&event, listview->ondragstartdata); + } + + size_t numproviders = cxListSize(dnd->providers); + if(numproviders > 0) { + GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers, 0); + provider = gdk_content_provider_new_union(providers, numproviders); + } + ui_dnd_free(dnd); + + return provider; +} + +static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) { + //printf("drag begin\n"); +} + +static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) { + //printf("drag end\n"); + UiListView *listview = user_data; + if(listview->ondragcomplete) { + UiDnD dnd; + dnd.target = NULL; + dnd.value = NULL; + dnd.providers = NULL; + dnd.selected_action = gdk_drag_get_selected_action(drag); + dnd.delete = delete_data; + dnd.accept = FALSE; + + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragcomplete(&event, listview->ondragcompletedata); + } +} + +static gboolean ui_listview_drop( + GtkDropTarget *target, + const GValue* value, + gdouble x, + gdouble y, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.providers = NULL; + dnd.target = target; + dnd.value = value; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondrop) { + dnd.accept = TRUE; + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondrop(&event, listview->ondropdata); + } + + return dnd.accept; +} + +void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) { + GtkDragSource *dragsource = gtk_drag_source_new(); + gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(dragsource)); + g_signal_connect (dragsource, "prepare", G_CALLBACK (ui_listview_dnd_prepare), listview); + g_signal_connect( + dragsource, + "drag-begin", + G_CALLBACK(ui_listview_drag_begin), + listview); + g_signal_connect( + dragsource, + "drag-end", + G_CALLBACK(ui_listview_drag_end), + listview); +} + +void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) { + GtkDropTarget *target = gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY); + gtk_widget_add_controller(listview->widget, GTK_EVENT_CONTROLLER(target)); + GType default_types[2] = { GDK_TYPE_FILE_LIST, G_TYPE_STRING }; + gtk_drop_target_set_gtypes(target, default_types, 2); + g_signal_connect(target, "drop", G_CALLBACK(ui_listview_drop), listview); +} + +#else + +static GtkTargetEntry targetentries[] = +{ + { "STRING", 0, 0 }, + { "text/plain", 0, 1 }, + { "text/uri-list", 0, 2 }, +}; + +static void ui_listview_drag_getdata( + GtkWidget* self, + GdkDragContext* context, + GtkSelectionData* data, + guint info, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = data; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondragstart) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragstart(&event, listview->ondragstartdata); + } +} + +static void ui_listview_drag_end( + GtkWidget *widget, + GdkDragContext *context, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = NULL; + dnd.selected_action = gdk_drag_context_get_selected_action(context); + dnd.delete = dnd.selected_action == UI_DND_ACTION_MOVE ? TRUE : FALSE; + dnd.accept = FALSE; + if(listview->ondragcomplete) { + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondragcomplete(&event, listview->ondragcompletedata); + } +} + +void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) { + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(listview->widget), + GDK_BUTTON1_MASK, + targetentries, + 2, + GDK_ACTION_COPY); + + g_signal_connect(listview->widget, "drag-data-get", G_CALLBACK(ui_listview_drag_getdata), listview); + g_signal_connect(listview->widget, "drag-end", G_CALLBACK(ui_listview_drag_end), listview); +} + + + + +static void ui_listview_drag_data_received( + GtkWidget *self, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + UiListView *listview = user_data; + UiDnD dnd; + dnd.context = context; + dnd.data = data; + dnd.selected_action = 0; + dnd.delete = FALSE; + dnd.accept = FALSE; + + if(listview->ondrop) { + dnd.accept = TRUE; + UiEvent event; + event.obj = listview->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &dnd; + event.intval = 0; + listview->ondrop(&event, listview->ondropdata); + } +} + +void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) { + gtk_tree_view_enable_model_drag_dest( + GTK_TREE_VIEW(listview->widget), + targetentries, + 3, + GDK_ACTION_COPY); + if(listview->ondrop) { + g_signal_connect(listview->widget, "drag_data_received", G_CALLBACK(ui_listview_drag_data_received), listview); + } +} + +#endif + + +GtkWidget* ui_get_tree_widget(UIWIDGET widget) { + return SCROLLEDWINDOW_GET_CHILD(widget); +} + +static char** targets2array(char *target0, va_list ap, int *nelm) { + int al = 16; + char **targets = calloc(16, sizeof(char*)); + targets[0] = target0; + + int i = 1; + char *target; + while((target = va_arg(ap, char*)) != NULL) { + if(i >= al) { + al *= 2; + targets = realloc(targets, al*sizeof(char*)); + } + targets[i] = target; + i++; + } + + *nelm = i; + return targets; +} + +/* +static GtkTargetEntry* targetstr2gtktargets(char **str, int nelm) { + GtkTargetEntry *targets = calloc(nelm, sizeof(GtkTargetEntry)); + for(int i=0;i<nelm;i++) { + targets[i].target = str[i]; + } + return targets; +} +*/ + +void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...) { + va_list ap; + va_start(ap, target0); + int nelm; + char **targets = targets2array(target0, ap, &nelm); + va_end(ap); + + // disabled + //ui_table_dragsource_a(tablewidget, actions, targets, nelm); + + free(targets); +} + +/* +void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) { + GtkTargetEntry* t = targetstr2gtktargets(targets, nelm); + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)), + GDK_BUTTON1_MASK, + t, + nelm, + GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK); + free(t); +} + + +void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...) { + va_list ap; + va_start(ap, target0); + int nelm; + char **targets = targets2array(target0, ap, &nelm); + va_end(ap); + ui_table_dragdest_a(tablewidget, actions, targets, nelm); + free(targets); +} + +void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) { + GtkTargetEntry* t = targetstr2gtktargets(targets, nelm); + gtk_tree_view_enable_model_drag_dest( + GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)), + t, + nelm, + GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK); + free(t); +} +*/ + +void ui_listview_update(UiList *list, int i) { + UiListView *view = list->obj; + GtkListStore *store = create_list_store(list, view->model); + gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); +} + +UiListSelection ui_listview_getselection(UiList *list) { + UiListView *view = list->obj; + UiListSelection selection = ui_listview_selection( + gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)), + NULL); + return selection; +} + +void ui_listview_setselection(UiList *list, UiListSelection selection) { + UiListView *view = list->obj; + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)); + GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count); + gtk_tree_selection_select_path(sel, path); + //g_object_unref(path); +} + +void ui_listview_destroy(GtkWidget *w, UiListView *v) { + //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); + ui_destroy_boundvar(v->obj->ctx, v->var); + free(v); +} + +void ui_combobox_destroy(GtkWidget *w, UiListView *v) { + ui_destroy_boundvar(v->obj->ctx, v->var); + free(v); +} + + +void ui_listview_activate_event( + GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + UiTreeEventData *event) +{ + UiListSelection selection = ui_listview_selection( + gtk_tree_view_get_selection(treeview), + event); + + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = &selection; + e.intval = selection.count > 0 ? selection.rows[0] : -1; + event->activate(&e, event->activatedata); + + if(selection.count > 0) { + free(selection.rows); + } +} + +void ui_listview_selection_event( + GtkTreeSelection *treeselection, + UiTreeEventData *event) +{ + UiListSelection selection = ui_listview_selection(treeselection, event); + + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = &selection; + e.intval = selection.count > 0 ? selection.rows[0] : -1; + event->selection(&e, event->selectiondata); + + if(selection.count > 0) { + free(selection.rows); + } +} + +UiListSelection ui_listview_selection( + GtkTreeSelection *selection, + UiTreeEventData *event) +{ + GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL); + + UiListSelection ls; + ls.count = g_list_length(rows); + ls.rows = calloc(ls.count, sizeof(int)); + GList *r = rows; + int i = 0; + while(r) { + GtkTreePath *path = r->data; + ls.rows[i] = ui_tree_path_list_index(path); + r = r->next; + i++; + } + return ls; +} + +int ui_tree_path_list_index(GtkTreePath *path) { + int depth = gtk_tree_path_get_depth(path); + if(depth == 0) { + fprintf(stderr, "UiError: treeview selection: depth == 0\n"); + return -1; + } + int *indices = gtk_tree_path_get_indices(path); + return indices[depth - 1]; +} + + +/* --------------------------- ComboBox --------------------------- */ + +UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); + model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + + GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata); + ui_set_name_and_style(combobox, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, combobox, args.groups); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, combobox, FALSE); + current->container->current = combobox; + return combobox; +} + +GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata) { + GtkWidget *combobox = gtk_combo_box_new(); + + UiListView *uicbox = malloc(sizeof(UiListView)); + uicbox->obj = obj; + uicbox->widget = combobox; + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(list, model); + + if(listmodel) { + gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + } + + uicbox->var = var; + uicbox->model = model; + + g_signal_connect( + combobox, + "destroy", + G_CALLBACK(ui_combobox_destroy), + uicbox); + + // bind var + if(list) { + list->update = ui_combobox_modelupdate; + list->getselection = ui_combobox_getselection; + list->setselection = ui_combobox_setselection; + list->obj = uicbox; + } + + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); + gtk_cell_layout_set_attributes( + GTK_CELL_LAYOUT(combobox), + renderer, + "text", + 0, + NULL); + gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); + + // add callback + if(f) { + UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); + event->obj = obj; + event->userdata = udata; + event->callback = f; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + combobox, + "changed", + G_CALLBACK(ui_combobox_change_event), + event); + } + + return combobox; +} + +void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { + UiEvent event; + event.obj = e->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.intval = gtk_combo_box_get_active(widget); + e->callback(&event, e->userdata); +} + +void ui_combobox_modelupdate(UiList *list, int i) { + UiListView *view = list->obj; + GtkListStore *store = create_list_store(view->var->value, view->model); + gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store)); + g_object_unref(store); +} + +UiListSelection ui_combobox_getselection(UiList *list) { + UiListView *combobox = list->obj; + UiListSelection ret; + ret.rows = malloc(sizeof(int*)); + ret.count = 1; + ret.rows[0] = gtk_combo_box_get_active(GTK_COMBO_BOX(combobox->widget)); + return ret; +} + +void ui_combobox_setselection(UiList *list, UiListSelection selection) { + UiListView *combobox = list->obj; + if(selection.count > 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]); + } +} + + +/* ------------------------------ Source List ------------------------------ */ + +static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { + cxListDestroy(v->sublists); + free(v); +} + +static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) { + free(sublist->header); + ui_destroy_boundvar(obj->ctx, sublist->var); + cxListDestroy(sublist->widgets); +} + +static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) { + // first rows in sublists have the ui_listbox property + UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox"); + if(!listbox) { + return; + } + + UiListBoxSubList *sublist = g_object_get_data(G_OBJECT(row), "ui_listbox_sublist"); + if(!sublist) { + return; + } + + if(sublist->separator) { + GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + gtk_list_box_row_set_header(row, separator); + } else if(sublist->header) { + GtkWidget *header = gtk_label_new(sublist->header); + gtk_widget_set_halign(header, GTK_ALIGN_START); + if(row == listbox->first_row) { + WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first"); + } else { + WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header"); + } + gtk_list_box_row_set_header(row, header); + } +} + +#ifdef UI_GTK3 +typedef struct _UiSidebarListBoxClass { + GtkListBoxClass parent_class; +} UiSidebarListBoxClass; + +typedef struct _UiSidebarListBox { + GtkListBox parent_instance; +} UiSidebarListBox; + +G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box, GTK_TYPE_LIST_BOX) + +/* Initialize the instance */ +static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + gtk_widget_class_set_css_name (widget_class, "placessidebar"); +} + +static void ui_sidebar_list_box_init(UiSidebarListBox *self) { + +} +#endif + +UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) { + UiObject* current = uic_current_obj(obj); + +#ifdef UI_GTK3 + GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); +#else + GtkWidget *listbox = gtk_list_box_new(); +#endif + if(!args.style_class) { +#if GTK_MAJOR_VERSION >= 4 + WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar"); +#else + WIDGET_ADD_CSS_CLASS(listbox, "sidebar"); +#endif + } + gtk_list_box_set_header_func(GTK_LIST_BOX(listbox), listbox_create_header, NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); + SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); + + ui_set_name_and_style(listbox, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, listbox, args.groups); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scroll_area, TRUE); + + UiListBox *uilistbox = malloc(sizeof(UiListBox)); + uilistbox->obj = obj; + uilistbox->listbox = GTK_LIST_BOX(listbox); + uilistbox->getvalue = args.getvalue; + uilistbox->onactivate = args.onactivate; + uilistbox->onactivatedata = args.onactivatedata; + uilistbox->onbuttonclick = args.onbuttonclick; + uilistbox->onbuttonclickdata = args.onbuttonclickdata; + uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4); + uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy; + uilistbox->sublists->collection.destructor_data = obj; + uilistbox->first_row = NULL; + + if(args.numsublists == 0 && args.sublists) { + args.numsublists = INT_MAX; + } + for(int i=0;i<args.numsublists;i++) { + UiSubList sublist = args.sublists[i]; + if(!sublist.varname && !sublist.value) { + break; + } + + UiListBoxSubList uisublist; + uisublist.var = uic_widget_var( + obj->ctx, + current->ctx, + sublist.value, + sublist.varname, + UI_VAR_LIST); + uisublist.numitems = 0; + uisublist.header = sublist.header ? strdup(sublist.header) : NULL; + uisublist.separator = sublist.separator; + uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS); + uisublist.listbox = uilistbox; + uisublist.userdata = sublist.userdata; + uisublist.index = i; + + cxListAdd(uilistbox->sublists, &uisublist); + + // bind UiList + UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-1); + UiList *list = uisublist.var->value; + if(list) { + list->obj = sublist_ptr; + list->update = ui_listbox_list_update; + } + } + // fill items + ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); + + // register uilistbox for both widgets, so it doesn't matter which + // widget is used later + g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); + g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); + + // signals + g_signal_connect( + listbox, + "destroy", + G_CALLBACK(ui_destroy_sourcelist), + uilistbox); + + if(args.onactivate) { + g_signal_connect( + listbox, + "row-activated", + G_CALLBACK(ui_listbox_row_activate), + NULL); + } + + return scroll_area; +} + +void ui_listbox_update(UiListBox *listbox, int from, int to) { + CxIterator i = cxListIterator(listbox->sublists); + size_t pos = 0; + cx_foreach(UiListBoxSubList *, sublist, i) { + if(i.index < from) { + pos += sublist->numitems; + continue; + } + if(i.index > to) { + break; + } + + // reload sublist + ui_listbox_update_sublist(listbox, sublist, pos); + pos += sublist->numitems; + } +} + +static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); + if(item->icon) { + GtkWidget *icon = ICON_IMAGE(item->icon); + BOX_ADD(hbox, icon); + } + GtkWidget *label = gtk_label_new(item->label); + gtk_widget_set_halign(label, GTK_ALIGN_START); + BOX_ADD_EXPAND(hbox, label); + // TODO: badge, button + GtkWidget *row = gtk_list_box_row_new(); + LISTBOX_ROW_SET_CHILD(row, hbox); + + // signals + UiEventDataExt *event = malloc(sizeof(UiEventDataExt)); + memset(event, 0, sizeof(UiEventDataExt)); + event->obj = listbox->obj; + event->customdata0 = sublist; + event->customdata1 = sublist->var; + event->customdata2 = item->eventdata; + event->callback = listbox->onactivate; + event->userdata = listbox->onactivatedata; + event->callback2 = listbox->onbuttonclick; + event->userdata2 = listbox->onbuttonclickdata; + event->value0 = index; + + g_signal_connect( + row, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); + + return row; +} + +void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { + // clear sublist + CxIterator r = cxListIterator(sublist->widgets); + cx_foreach(GtkWidget*, widget, r) { + LISTBOX_REMOVE(listbox->listbox, widget); + } + cxListClear(sublist->widgets); + + sublist->numitems = 0; + + // create items for each UiList element + UiList *list = sublist->var->value; + if(!list) { + return; + } + + size_t index = 0; + void *elm = list->first(list); + while(elm) { + UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; + listbox->getvalue(sublist->userdata, elm, index, &item); + + // create listbox item + GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); + if(index == 0) { + // first row in the sublist, set ui_listbox data to the row + // which is then used by the headerfunc + g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); + g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); + + if(listbox_insert_index == 0) { + // first row in the GtkListBox + listbox->first_row = GTK_LIST_BOX_ROW(row); + } + } + intptr_t rowindex = listbox_insert_index + index; + g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + cxListAdd(sublist->widgets, row); + + // cleanup + free(item.label); + free(item.icon); + free(item.button_label); + free(item.button_icon); + free(item.badge); + + // next row + elm = list->next(list); + index++; + } + + sublist->numitems = cxListSize(sublist->widgets); +} + +void ui_listbox_list_update(UiList *list, int i) { + UiListBoxSubList *sublist = list->obj; +} + +void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { + UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); + if(!data) { + return; + } + UiListBoxSubList *sublist = data->customdata0; + + UiEvent event; + event.obj = data->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = data->customdata2; + event.intval = data->value0; + + if(data->callback) { + data->callback(&event, data->userdata); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/gtk/list.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,137 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TREE_H +#define TREE_H + +#include "../ui/tree.h" +#include "toolkit.h" + +#include <cx/array_list.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiListView { + UiObject *obj; + GtkWidget *widget; + UiVar *var; + UiModel *model; + ui_callback ondragstart; + void *ondragstartdata; + ui_callback ondragcomplete; + void *ondragcompletedata; + ui_callback ondrop; + void *ondropdata; + +} UiListView; + +typedef struct UiTreeEventData { + UiObject *obj; + ui_callback activate; + ui_callback selection; + void *activatedata; + void *selectiondata; +} UiTreeEventData; + +typedef struct UiListBox UiListBox; + +typedef struct UiListBoxSubList { + UiVar *var; + size_t numitems; + char *header; + UiBool separator; + CxList *widgets; + UiListBox *listbox; + void *userdata; + size_t index; +} UiListBoxSubList; + +struct UiListBox { + UiObject *obj; + GtkListBox *listbox; + CxList *sublists; // contains UiListBoxSubList elements + ui_sublist_getvalue_func getvalue; + ui_callback onactivate; + void *onactivatedata; + ui_callback onbuttonclick; + void *onbuttonclickdata; + + GtkListBoxRow *first_row; +}; + +void* ui_strmodel_getvalue(void *elm, int column); + +UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); +UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb); + +GtkWidget* ui_get_tree_widget(UIWIDGET widget); + +void ui_listview_update(UiList *list, int i); +UiListSelection ui_listview_getselection(UiList *list); +void ui_listview_setselection(UiList *list, UiListSelection selection); + +void ui_combobox_destroy(GtkWidget *w, UiListView *v); +void ui_listview_destroy(GtkWidget *w, UiListView *v); + +void ui_listview_activate_event( + GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + UiTreeEventData *event); +void ui_listview_selection_event( + GtkTreeSelection *treeselection, + UiTreeEventData *event); +UiListSelection ui_listview_selection( + GtkTreeSelection *selection, + UiTreeEventData *event); +int ui_tree_path_list_index(GtkTreePath *path); + +void ui_listview_add_dnd(UiListView *listview, UiListArgs *args); +void ui_listview_enable_drop(UiListView *listview, UiListArgs *args); + +UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); +GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata); +void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); +void ui_combobox_modelupdate(UiList *list, int i); +UiListSelection ui_combobox_getselection(UiList *list); +void ui_combobox_setselection(UiList *list, UiListSelection selection); + +void ui_listbox_update(UiListBox *listbox, int from, int to); +void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index); +void ui_listbox_list_update(UiList *list, int i); + +void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* TREE_H */ +
--- a/ui/gtk/menu.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/menu.c Sat Jan 04 16:38:48 2025 +0100 @@ -34,189 +34,58 @@ #include "menu.h" #include "toolkit.h" #include "../common/context.h" +#include "../common/menu.h" +#include "../common/types.h" #include "../ui/properties.h" #include "../ui/window.h" #include "container.h" -static UcxList *menus; -static UcxList *current; - -void ui_menu(char *label) { - // free current menu hierarchy - ucx_list_free(current); - - // create menu - UiMenu *menu = malloc(sizeof(UiMenu)); - menu->item.add_to = (ui_menu_add_f)add_menu_widget; - - menu->label = label; - menu->items = NULL; - menu->parent = NULL; - - current = ucx_list_prepend(NULL, menu); - menus = ucx_list_append(menus, menu); - -} - -void ui_submenu(char *label) { - UiMenu *menu = malloc(sizeof(UiMenu)); - menu->item.add_to = (ui_menu_add_f)add_menu_widget; - - menu->label = label; - menu->items = NULL; - menu->parent = NULL; - - // add submenu to current menu - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, menu); - - // set the submenu to current menu - current = ucx_list_prepend(current, menu); -} +#include <cx/linked_list.h> +#include <cx/array_list.h> -void ui_submenu_end() { - if(ucx_list_size(current) < 2) { - return; - } - current = ucx_list_remove(current, current); - //UcxList *c = current; -} - -void ui_menuitem(char *label, ui_callback f, void *userdata) { - ui_menuitem_gr(label, f, userdata, -1); -} - -void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) { - ui_menuitem_stgr(stockid, f, userdata, -1); -} - -void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { - if(!current) { - return; - } - - UiMenuItem *item = malloc(sizeof(UiMenuItem)); - item->item.add_to = (ui_menu_add_f)add_menuitem_widget; - - item->label = label; - item->userdata = userdata; - item->callback = f; - item->groups = NULL; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} +#if GTK_MAJOR_VERSION <= 3 -void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { - if(!current) { - return; - } - - UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); - item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget; - - item->stockid = stockid; - item->userdata = userdata; - item->callback = f; - item->groups = NULL; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_menuseparator() { - if(!current) { - return; - } - - UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); - item->add_to = (ui_menu_add_f)add_menuseparator_widget; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_checkitem(char *label, ui_callback f, void *userdata) { - if(!current) { - return; - } - - UiCheckItem *item = malloc(sizeof(UiCheckItem)); - item->item.add_to = (ui_menu_add_f)add_checkitem_widget; - item->label = label; - item->callback = f; - item->userdata = userdata; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_checkitem_nv(char *label, char *vname) { - if(!current) { - return; - } - - UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV)); - item->item.add_to = (ui_menu_add_f)add_checkitemnv_widget; - item->varname = vname; - item->label = label; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) { - if(!current) { - return; - } - - UiMenuItemList *item = malloc(sizeof(UiMenuItemList)); - item->item.add_to = (ui_menu_add_f)add_menuitem_list_widget; - item->callback = f; - item->userdata = userdata; - item->list = items; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} +static ui_menu_add_f createMenuItem[] = { + /* UI_MENU */ add_menu_widget, + /* UI_MENU_ITEM */ add_menuitem_widget, + /* UI_MENU_CHECK_ITEM */ add_checkitem_widget, + /* UI_MENU_RADIO_ITEM */ add_radioitem_widget, + /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_SEPARATOR */ add_menuseparator_widget +}; // private menu functions GtkWidget *ui_create_menubar(UiObject *obj) { - if(menus == NULL) { + UiMenu *menus_begin = uic_get_menu_list(); + if(menus_begin == NULL) { return NULL; } GtkWidget *mb = gtk_menu_bar_new(); - UcxList *ls = menus; + UiMenu *ls = menus_begin; while(ls) { - UiMenu *menu = ls->data; - menu->item.add_to(mb, 0, &menu->item, obj); + UiMenu *menu = ls; + add_menu_widget(mb, 0, &menu->item, obj); - ls = ls->next; + ls = (UiMenu*)ls->item.next; } return mb; } +void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj) { + UiMenuItemI *it = menu->items_begin; + int index = 0; + while(it) { + createMenuItem[it->type](parent, index, it, obj); + it = it->next; + index++; + } +} + void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) { UiMenu *menu = (UiMenu*)item; @@ -224,15 +93,8 @@ GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget); - UcxList *ls = menu->items; - int index = 0; - while(ls) { - UiMenuItemI *i = ls->data; - i->add_to(menu_widget, index, i, obj); - - ls = ls->next; - index++; - } + ui_add_menu_items(menu_widget, i, menu, obj); + gtk_menu_shell_append(GTK_MENU_SHELL(parent), menu_item); } @@ -249,6 +111,7 @@ event->userdata = i->userdata; event->callback = i->callback; event->value = 0; + event->customdata = NULL; g_signal_connect( widget, @@ -265,10 +128,14 @@ gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget); if(i->groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups); + CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); + cxListAddArray(groups, i->groups, i->ngroups); + uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); + cxListDestroy(groups); } } +/* void add_menuitem_st_widget( GtkWidget *parent, int index, @@ -304,6 +171,7 @@ uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups); } } +*/ void add_menuseparator_widget( GtkWidget *parent, @@ -317,7 +185,7 @@ } void add_checkitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { - UiCheckItem *ci = (UiCheckItem*)item; + UiMenuCheckItem *ci = (UiMenuCheckItem*)item; GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label); gtk_menu_shell_append(GTK_MENU_SHELL(p), widget); @@ -327,7 +195,8 @@ event->userdata = ci->userdata; event->callback = ci->callback; event->value = 0; - + event->customdata = NULL; + g_signal_connect( widget, "toggled", @@ -341,6 +210,11 @@ } } +void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { + // TODO +} + +/* void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { UiCheckItemNV *ci = (UiCheckItemNV*)item; GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label); @@ -357,27 +231,31 @@ // TODO: error } } +*/ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { UiMenuItemList *il = (UiMenuItemList*)item; - UcxMempool *mp = obj->ctx->mempool; + const CxAllocator *a = obj->ctx->allocator; - UiActiveMenuItemList *ls = ucx_mempool_malloc( - mp, + UiActiveMenuItemList *ls = cxMalloc( + a, sizeof(UiActiveMenuItemList)); ls->object = obj; ls->menu = GTK_MENU_SHELL(p); ls->index = index; ls->oldcount = 0; - ls->list = il->list; + ls->getvalue = il->getvalue; + + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + ls->list = var->value; + ls->callback = il->callback; ls->userdata = il->userdata; - ls->list->observers = ui_add_observer( - ls->list->observers, - (ui_callback)ui_update_menuitem_list, - ls); + UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls); + ls->list->observers = ui_obsvlist_add(ls->list->observers, observer); + uic_list_register_observer_destructor(obj->ctx, ls->list, observer); ui_update_menuitem_list(NULL, ls); } @@ -398,25 +276,29 @@ } } - char *str = ui_list_first(list->list); - if(str) { + void* elm = ui_list_first(list->list); + if(elm) { GtkWidget *widget = gtk_separator_menu_item_new(); gtk_menu_shell_insert(list->menu, widget, list->index); gtk_widget_show(widget); } + + ui_getvaluefunc getvalue = list->getvalue; int i = 1; - while(str) { - GtkWidget *widget = gtk_menu_item_new_with_label(str); + while(elm) { + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + + GtkWidget *widget = gtk_menu_item_new_with_label(label); gtk_menu_shell_insert(list->menu, widget, list->index + i); gtk_widget_show(widget); if(list->callback) { - // TODO: use mempool UiEventData *event = malloc(sizeof(UiEventData)); event->obj = list->object; event->userdata = list->userdata; event->callback = list->callback; event->value = i - 1; + event->customdata = elm; g_signal_connect( widget, @@ -430,7 +312,7 @@ event); } - str = ui_list_next(list->list); + elm = ui_list_next(list->list); i++; } @@ -442,7 +324,7 @@ evt.obj = event->obj; evt.window = event->obj->window; evt.document = event->obj->ctx->document; - evt.eventdata = NULL; + evt.eventdata = event->customdata; evt.intval = event->value; event->callback(&evt, event->userdata); } @@ -473,34 +355,28 @@ * widget menu functions */ +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget) { + GtkWidget *menu_widget = gtk_menu_new(); + ui_add_menu_items(menu_widget, 0, builder->menus_begin, obj); + return GTK_MENU(menu_widget); +} + static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) { if(event->type == GDK_BUTTON_PRESS) { GdkEventButton *e = (GdkEventButton*)event; if(e->button == 3) { gtk_widget_show_all(GTK_WIDGET(menu)); - ui_contextmenu_popup(menu); - return TRUE; + ui_contextmenu_popup(menu, widget, 0, 0); } } return FALSE; } -UIMENU ui_contextmenu(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - return ui_contextmenu_w(obj, ct->current); +void ui_widget_set_contextmenu(GtkWidget *widget, GtkMenu *menu) { + g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu); } -UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { - UiContainer *ct = uic_get_current_container(obj); - - GtkMenu *menu = GTK_MENU(gtk_menu_new()); - g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu); - - ct->menu = menu; - return menu; -} - -void ui_contextmenu_popup(UIMENU menu) { +void ui_contextmenu_popup(UIMENU menu, GtkWidget *widget, int x, int y) { #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16 gtk_menu_popup_at_pointer(menu, NULL); #else @@ -508,102 +384,255 @@ #endif } -void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { - ui_widget_menuitem_gr(obj, label, f, userdata, -1); +#endif /* GTK_MAJOR_VERSION <= 3 */ + + + +#if GTK_MAJOR_VERSION >= 4 + + + +static ui_gmenu_add_f createMenuItem[] = { + /* UI_MENU */ ui_gmenu_add_menu, + /* UI_MENU_ITEM */ ui_gmenu_add_menuitem, + /* UI_MENU_CHECK_ITEM */ ui_gmenu_add_checkitem, + /* UI_MENU_RADIO_ITEM */ ui_gmenu_add_radioitem, + /* UI_MENU_ITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_CHECKITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_RADIOITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_SEPARATOR */ ui_gmenu_add_menuseparator +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) { + UiMenuItemI *it = menu->items_begin; + int index = 0; + int index_section = 0; + GMenu *section = NULL; + while(it) { + if(it->type == UI_MENU_SEPARATOR) { + section = g_menu_new(); + g_menu_append_section(parent, NULL, G_MENU_MODEL(section)); + index++; + index_section = 0; + } else { + if(section) { + createMenuItem[it->type](section, index_section++, it, obj); + } else { + createMenuItem[it->type](parent, index++, it, obj); + } + } + it = it->next; + } } -void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenu *mi = (UiMenu*)item; + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, mi, obj); + g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); +} + +static void action_enable(GSimpleAction *action, int enabled) { + g_simple_action_set_enabled(action, enabled); +} + +void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuItem *i = (UiMenuItem*)item; + + GSimpleAction *action = g_simple_action_new(item->id, NULL); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + + if(i->groups) { + CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); + cxListAddArray(groups, i->groups, i->ngroups); + uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); + cxListDestroy(groups); } - // add groups - UcxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); - } - va_end(ap); - - // create menuitem - GtkWidget *widget = gtk_menu_item_new_with_mnemonic(label); - gtk_widget_show(widget); - - if(f) { + if(i->callback != NULL) { UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; - event->userdata = userdata; - event->callback = f; + event->userdata = i->userdata; + event->callback = i->callback; event->value = 0; + event->customdata = NULL; g_signal_connect( - widget, + action, "activate", - G_CALLBACK(ui_menu_event_wrapper), + G_CALLBACK(ui_activate_event_wrapper), event); g_signal_connect( - widget, + obj->widget, "destroy", G_CALLBACK(ui_destroy_userdata), event); } - gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); + char action_name[32]; + snprintf(action_name, 32, "win.%s", item->id); + g_menu_append(parent, i->label, action_name); +} + +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { - if(groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); - } +} + +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item; + + // TODO +} + +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + } -void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { - ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuItemList *il = (UiMenuItemList*)item; + + const CxAllocator *a = obj->ctx->allocator; + + UiActiveGMenuItemList *ls = cxMalloc( + a, + sizeof(UiActiveGMenuItemList)); + + ls->object = obj; + ls->menu = p; + ls->index = index; + ls->oldcount = 0; + ls->getvalue = il->getvalue; + + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + ls->var = var; + UiList *list = var->value; + + ls->callback = il->callback; + ls->userdata = il->userdata; + + UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls); + list->observers = ui_obsvlist_add(list->observers, observer); + uic_list_register_observer_destructor(obj->ctx, list, observer); + + GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); + g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + snprintf(ls->action, 32, "win.%s", item->id); + + + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = il->userdata; + event->callback = il->callback; + event->customdata = var; + event->value = 0; + + g_signal_connect( + action, + "activate", + G_CALLBACK(ui_menu_list_item_activate_event_wrapper), + event); + g_signal_connect( + obj->widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + ui_update_gmenu_item_list(NULL, ls); } -void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; +void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { + int intval = event->value; + if(parameter && g_variant_is_of_type(parameter, G_VARIANT_TYPE_INT32)) { + intval = g_variant_get_int32(parameter); } - // add groups - UcxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); + UiEvent evt; + evt.obj = event->obj; + evt.window = event->obj->window; + evt.document = event->obj->ctx->document; + evt.eventdata = event->customdata; + evt.intval = intval; + event->callback(&evt, event->userdata); +} + +void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) { + int index = g_variant_get_int32(parameter); + UiVar *var = event->customdata; + UiList *list = var->value; + + UiEvent evt; + evt.obj = event->obj; + evt.window = event->obj->window; + evt.document = event->obj->ctx->document; + evt.eventdata = ui_list_get(list, index); + evt.intval = index; + event->callback(&evt, event->userdata); + +} + +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) { + // remove old items + for(int i=0;i<list->oldcount;i++) { + g_menu_remove(list->menu, list->index); } - va_end(ap); - - // create menuitem - GtkWidget *widget = gtk_image_menu_item_new_from_stock(stockid, obj->ctx->accel_group); - gtk_widget_show(widget); + UiList *ls = list->var->value; - if(f) { - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = obj; - event->userdata = userdata; - event->callback = f; - event->value = 0; + // add list items + ui_getvaluefunc getvalue = list->getvalue; + int i = 0; + void* elm = ui_list_first(ls); + while(elm) { + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + + GMenuItem *item = g_menu_item_new(label, NULL); + GVariant *v = g_variant_new("i", i); + g_menu_item_set_action_and_target_value(item, list->action, v); + g_menu_insert_item(list->menu, list->index+i, item); + + elm = ui_list_next(ls); + i++; + } + + list->oldcount = i; +} - g_signal_connect( - widget, - "activate", - G_CALLBACK(ui_menu_event_wrapper), - event); - g_signal_connect( + +/* --------------------- context menu / menubuilder --------------------- */ + +static void remove_popover(GtkWidget *object, GtkPopoverMenu *menu) { + gtk_widget_unparent(GTK_WIDGET(menu)); +} + +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) { + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj); + GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); + gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE); + gtk_widget_set_halign(contextmenu, GTK_ALIGN_START); + gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget); + g_signal_connect( widget, "destroy", - G_CALLBACK(ui_destroy_userdata), - event); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); - - if(groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); - } + G_CALLBACK(remove_popover), + contextmenu); + return GTK_POPOVER_MENU(contextmenu); +} + +static void gesture_button_press(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) { + gtk_popover_set_pointing_to(GTK_POPOVER(user_data), &(GdkRectangle){ x, y, 0, 0 }); + gtk_popover_popup(GTK_POPOVER(user_data)); } + +void ui_widget_set_contextmenu(GtkWidget *widget, GtkPopoverMenu *menu) { + GtkGesture *gesture = gtk_gesture_click_new(); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3); + gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture)); + g_signal_connect(gesture, "pressed", G_CALLBACK(gesture_button_press), menu); +} + +void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) { + gtk_popover_set_pointing_to(GTK_POPOVER(menu), &(GdkRectangle){ x, y, 0, 0 }); + gtk_popover_popup(GTK_POPOVER(menu)); +} + +#endif
--- a/ui/gtk/menu.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/menu.h Sat Jan 04 16:38:48 2025 +0100 @@ -30,89 +30,45 @@ #define MENU_H #include "../ui/menu.h" -#include <ucx/list.h> +#include "../common/menu.h" +#include <cx/list.h> #include "toolkit.h" + #ifdef __cplusplus extern "C" { #endif -typedef struct UiMenuItemI UiMenuItemI; -typedef struct UiMenu UiMenu; -typedef struct UiMenuItem UiMenuItem; -typedef struct UiStMenuItem UiStMenuItem; -typedef struct UiCheckItem UiCheckItem; -typedef struct UiCheckItemNV UiCheckItemNV; -typedef struct UiMenuItemList UiMenuItemList; +UIMENU ui_create_menu(UiMenuBuilder *builder, UiObject *obj); +void ui_widget_set_contextmenu(GtkWidget *widget, UIMENU menu); + +#if GTK_MAJOR_VERSION <= 3 typedef struct UiActiveMenuItemList UiActiveMenuItemList; -typedef GtkWidget*(*ui_menu_add_f)(GtkWidget *, int, UiMenuItemI*, UiObject*); - -struct UiMenuItemI { - ui_menu_add_f add_to; -}; - -struct UiMenu { - UiMenuItemI item; - char *label; - UcxList *items; - UiMenu *parent; -}; - -struct UiMenuItem { - UiMenuItemI item; - ui_callback callback; - char *label; - void *userdata; - UcxList *groups; -}; - -struct UiStMenuItem { - UiMenuItemI item; - ui_callback callback; - char *stockid; - void *userdata; - UcxList *groups; -}; - -struct UiCheckItem { - UiMenuItemI item; - char *label; - ui_callback callback; - void *userdata; -}; - -struct UiCheckItemNV { - UiMenuItemI item; - char *label; - char *varname; -}; - -struct UiMenuItemList { - UiMenuItemI item; - ui_callback callback; - void *userdata; - UiList *list; -}; +typedef void(*ui_menu_add_f)(GtkWidget *, int, UiMenuItemI*, UiObject*); struct UiActiveMenuItemList { - UiObject *object; - GtkMenuShell *menu; - int index; - int oldcount; - UiList *list; - ui_callback callback; - void *userdata; + UiObject *object; + GtkMenuShell *menu; + int index; + int oldcount; + UiList *list; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; }; GtkWidget *ui_create_menubar(UiObject *obj); +void ui_add_menu_items(GtkWidget *parent, int i, UiMenu *menu, UiObject *obj); + void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj); void add_menuitem_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj); void add_menuitem_st_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); void add_menuseparator_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); void add_checkitem_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); +void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj); void add_checkitemnv_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); void add_menuitem_list_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *obj); @@ -122,6 +78,41 @@ int64_t ui_checkitem_get(UiInteger *i); void ui_checkitem_set(UiInteger *i, int64_t value); +#endif /* GTK_MAJOR_VERSION <= 3 */ + +#if GTK_MAJOR_VERSION >= 4 + +typedef void(*ui_gmenu_add_f)(GMenu *, int, UiMenuItemI*, UiObject*); + +typedef struct UiActiveGMenuItemList UiActiveGMenuItemList; +struct UiActiveGMenuItemList { + UiObject *object; + GMenu *menu; + char action[32]; + int index; + int oldcount; + UiVar *var; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj); + +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); + +void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); +void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list); + +#endif + + #ifdef __cplusplus } #endif
--- a/ui/gtk/model.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,539 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> - -#include "model.h" -#include "image.h" -#include "toolkit.h" - -#define IS_UI_LIST_MODEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), list_model_type)) -#define UI_LIST_MODEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), list_model_type, UiListModel)) - -static void list_model_class_init(GObjectClass *cl, gpointer data); -static void list_model_interface_init(GtkTreeModelIface *i, gpointer data); -static void list_model_init(UiListModel *instance, GObjectClass *cl); - -static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data); -static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data); - -static GObjectClass list_model_class; -static const GTypeInfo list_model_info = { - sizeof(GObjectClass), - NULL, - NULL, - (GClassInitFunc)list_model_class_init, - NULL, - NULL, - sizeof(UiListModel), - 0, - (GInstanceInitFunc)list_model_init -}; -static const GInterfaceInfo list_model_interface_info = { - (GInterfaceInitFunc)list_model_interface_init, - NULL, - NULL -}; -static GType list_model_type; - -static const GInterfaceInfo list_model_dnd_dest_interface_info = { - (GInterfaceInitFunc)list_model_dnd_dest_interface_init, - NULL, - NULL -}; -static const GInterfaceInfo list_model_dnd_src_interface_info = { - (GInterfaceInitFunc)list_model_dnd_src_interface_init, - NULL, - NULL -}; - -void ui_list_init() { - list_model_type = g_type_register_static( - G_TYPE_OBJECT, - "UiListModel", - &list_model_info, - (GTypeFlags)0); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_MODEL, - &list_model_interface_info); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_DRAG_DEST, - &list_model_dnd_dest_interface_info); - g_type_add_interface_static( - list_model_type, - GTK_TYPE_TREE_DRAG_SOURCE, - &list_model_dnd_src_interface_info); -} - -static void list_model_class_init(GObjectClass *cl, gpointer data) { - cl->dispose = ui_list_model_dispose; - cl->finalize = ui_list_model_finalize; - -} - -static void list_model_interface_init(GtkTreeModelIface *i, gpointer data) { - i->get_flags = ui_list_model_get_flags; - i->get_n_columns = ui_list_model_get_n_columns; - i->get_column_type = ui_list_model_get_column_type; - i->get_iter = ui_list_model_get_iter; - i->get_path = ui_list_model_get_path; - i->get_value = ui_list_model_get_value; - i->iter_next = ui_list_model_iter_next; - i->iter_children = ui_list_model_iter_children; - i->iter_has_child = ui_list_model_iter_has_child; - i->iter_n_children = ui_list_model_iter_n_children; - i->iter_nth_child = ui_list_model_iter_nth_child; - i->iter_parent = ui_list_model_iter_parent; -} - -static void list_model_dnd_dest_interface_init(GtkTreeDragDestIface *i, gpointer data) { - i->drag_data_received = ui_list_model_drag_data_received; - i->row_drop_possible = ui_list_model_row_drop_possible; -} - -static void list_model_dnd_src_interface_init(GtkTreeDragSourceIface *i, gpointer data) { - i->drag_data_delete = ui_list_model_drag_data_delete; - i->drag_data_get = ui_list_model_drag_data_get; - i->row_draggable = ui_list_model_row_draggable; -} - -static void list_model_init(UiListModel *instance, GObjectClass *cl) { - instance->columntypes = NULL; - instance->var = NULL; - instance->numcolumns = 0; - instance->stamp = g_random_int(); -} - -static GType ui_gtk_type(UiModelType type) { - switch(type) { - default: break; - case UI_STRING: return G_TYPE_STRING; - case UI_INTEGER: return G_TYPE_INT; - } - return G_TYPE_INVALID; -} - -static void ui_model_set_value(GType type, void *data, GValue *value) { - switch(type) { - default: break; - case G_TYPE_OBJECT: { - value->g_type = G_TYPE_OBJECT; - g_value_set_object(value, data); - return; - } - case G_TYPE_STRING: { - value->g_type = G_TYPE_STRING; - g_value_set_string(value, data); - return; - } - case G_TYPE_INT: { - value->g_type = G_TYPE_INT; - int *i = data; - g_value_set_int(value, *i); - return; - } - } - value->g_type = G_TYPE_INVALID; -} - -UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info) { - UiListModel *model = g_object_new(list_model_type, NULL); - model->obj = obj; - model->model = info; - model->var = var; - model->columntypes = calloc(sizeof(GType), 2 * info->columns); - int ncol = 0; - for(int i=0;i<info->columns;i++) { - UiModelType type = info->types[i]; - if(type == UI_ICON_TEXT) { - model->columntypes[ncol] = G_TYPE_OBJECT; - ncol++; - model->columntypes[ncol] = G_TYPE_STRING; - } else { - model->columntypes[ncol] = ui_gtk_type(info->types[i]); - } - ncol++; - } - model->numcolumns = ncol; - return model; -} - -void ui_list_model_dispose(GObject *obj) { - -} - -void ui_list_model_finalize(GObject *obj) { - -} - - -GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model) { - return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); -} - -gint ui_list_model_get_n_columns(GtkTreeModel *tree_model) { - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), 0); - UiListModel *model = UI_LIST_MODEL(tree_model); - return model->numcolumns; -} - -GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index) { - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), G_TYPE_INVALID); - UiListModel *model = UI_LIST_MODEL(tree_model); - g_return_val_if_fail(index < model->numcolumns, G_TYPE_INVALID); - return model->columntypes[index]; -} - -gboolean ui_list_model_get_iter( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - g_assert(IS_UI_LIST_MODEL(tree_model)); - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - // check the depth of the path - // a list must have a depth of 1 - gint depth = gtk_tree_path_get_depth(path); - g_assert(depth == 1); - - // get row - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - - // check row - if(row == 0) { - // we don't need to count if the first element is requested - if(list->first(list) == NULL) { - return FALSE; - } - } else if(row >= list->count(list)) { - return FALSE; - } - - // the UiList has an integrated iterator - // we only get a value to adjust it - void *val = NULL; - if(row == 0) { - val = list->first(list); - } else { - val = list->get(list, row); - } - - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)(intptr_t)row; // list->index - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -GtkTreePath* ui_list_model_get_path( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), NULL); - g_return_val_if_fail(iter != NULL, NULL); - g_return_val_if_fail(iter->user_data != NULL, NULL); - - UiListModel *model = UI_LIST_MODEL(tree_model); - - GtkTreePath *path = gtk_tree_path_new(); - gtk_tree_path_append_index(path, (int)(intptr_t)iter->user_data2); // list->index - - return path; -} - -void ui_list_model_get_value( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value) -{ - g_return_if_fail(IS_UI_LIST_MODEL(tree_model)); - g_return_if_fail(iter != NULL); - g_return_if_fail(iter->user_data != NULL); - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - g_return_if_fail(column < model->numcolumns); - - // TODO: return correct value from column - - //value->g_type = G_TYPE_STRING; - list->iter = iter->user_data; - //list->index = (int)(intptr_t)iter->user_data2; - //list->current = iter->user_data3; - if(model->model->getvalue) { - void *data = model->model->getvalue(iter->user_data3, column); - if(model->columntypes[column] == G_TYPE_OBJECT) { - UiImage *img = data; - ui_model_set_value(model->columntypes[column], img->pixbuf, value); - } else { - ui_model_set_value(model->columntypes[column], data, value); - } - } else { - value->g_type = G_TYPE_INVALID; - } -} - -gboolean ui_list_model_iter_next( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - g_return_val_if_fail(iter != NULL, FALSE); - //g_return_val_if_fail(iter->user_data != NULL, FALSE); - - if(!iter->user_data) { - return FALSE; - } - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - list->iter = iter->user_data; - //list->index = (int)(intptr_t)iter->user_data2; - void *val = list->next(list); - iter->user_data = list->iter; - intptr_t index = (intptr_t)iter->user_data2; - index++; - //iter->user_data2 = (gpointer)(intptr_t)list->index; - iter->user_data2 = (gpointer)index; - iter->user_data3 = val; - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - if(parent) { - return FALSE; - } - - /* - * a list element has no children - * we set the iter to the first element - */ - void *val = list->first(list); - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)0; - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_has_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - return FALSE; -} - -gint ui_list_model_iter_n_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - g_assert(IS_UI_LIST_MODEL(tree_model)); - - if(!iter) { - // return number of rows - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - return list->count(list); - } - - return 0; -} - -gboolean ui_list_model_iter_nth_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) -{ - g_return_val_if_fail(IS_UI_LIST_MODEL(tree_model), FALSE); - - if(parent) { - return FALSE; - } - - UiListModel *model = UI_LIST_MODEL(tree_model); - UiList *list = model->var->value; - - // check n - if(n == 0) { - // we don't need to count if the first element is requested - if(list->first(list) == NULL) { - return FALSE; - } - } else if(n >= list->count(list)) { - return FALSE; - } - - void *val = list->get(list, n); - iter->stamp = model->stamp; - iter->user_data = list->iter; - iter->user_data2 = (gpointer)(intptr_t)n; // list->index - iter->user_data3 = val; - - return val ? TRUE : FALSE; -} - -gboolean ui_list_model_iter_parent( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - return FALSE; -} - -// ****** dnd ****** - -gboolean ui_list_model_drag_data_received( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data) -{ - //printf("drag received\n"); - UiListModel *model = UI_LIST_MODEL(drag_dest); - if(model->model->drop) { - gint *indices = gtk_tree_path_get_indices(dest_path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - model->model->drop(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_row_drop_possible( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data) -{ - //printf("row_drop_possible\n"); - UiListModel *model = UI_LIST_MODEL(drag_dest); - if(model->model->candrop) { - gint *indices = gtk_tree_path_get_indices(dest_path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - return model->model->candrop(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_row_draggable( - GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - //printf("row_draggable\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->candrag) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - return model->model->candrag(&e, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_drag_data_get( - GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - //printf("drag_data_get\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->data_get) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - UiSelection s; - s.data = selection_data; - model->model->data_get(&e, &s, model->var->value, row); - } - return TRUE; -} - -gboolean ui_list_model_drag_data_delete( - GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - //printf("drag_data_delete\n"); - UiListModel *model = UI_LIST_MODEL(drag_source); - if(model->model->data_get) { - gint *indices = gtk_tree_path_get_indices(path); - gint row = indices[0]; - UiEvent e; - e.obj = model->obj; - e.window = e.obj->window; - e.document = e.obj->ctx->document; - e.eventdata = NULL; - e.intval = 0; - model->model->data_delete(&e, model->var->value, row); - } - return TRUE; -}
--- a/ui/gtk/model.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MODEL_H -#define MODEL_H - -#include "../ui/toolkit.h" -#include "../common/context.h" -#include "../ui/tree.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UiListModel UiListModel; - -/* - * UiList to GtkTreeModel wrapper - */ -struct UiListModel { - GObject object; - UiObject *obj; - UiModel *model; - UiVar *var; - GType *columntypes; - int numcolumns; - int stamp; -}; - -/* - * initialize the class and register the type - */ -void ui_list_init(); - -/* - * Creates a UiListModel for a given UiList - */ -UiListModel* ui_list_model_new(UiObject *obj, UiVar *var, UiModel *info); - -void ui_list_model_dispose(GObject *obj); -void ui_list_model_finalize(GObject *obj); - - -// interface functions - -GtkTreeModelFlags ui_list_model_get_flags(GtkTreeModel *tree_model); - -gint ui_list_model_get_n_columns(GtkTreeModel *tree_model); - -GType ui_list_model_get_column_type(GtkTreeModel *tree_model, gint index); - -gboolean ui_list_model_get_iter( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); - -GtkTreePath* ui_list_model_get_path( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -void ui_list_model_get_value( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value); - -gboolean ui_list_model_iter_next( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gboolean ui_list_model_iter_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); - -gboolean ui_list_model_iter_has_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gint ui_list_model_iter_n_children( - GtkTreeModel *tree_model, - GtkTreeIter *iter); - -gboolean ui_list_model_iter_nth_child( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); - -gboolean ui_list_model_iter_parent( - GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); - -/* dnd */ - -gboolean ui_list_model_drag_data_received( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest, - GtkSelectionData *selection_data); - -gboolean ui_list_model_row_drop_possible( - GtkTreeDragDest *drag_dest, - GtkTreePath *dest_path, - GtkSelectionData *selection_data); - -gboolean ui_list_model_row_draggable( - GtkTreeDragSource *drag_source, - GtkTreePath *path); - -gboolean ui_list_model_drag_data_get( - GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data); - -gboolean ui_list_model_drag_data_delete( - GtkTreeDragSource *drag_source, - GtkTreePath *path); - -#ifdef __cplusplus -} -#endif - -#endif /* MODEL_H */
--- a/ui/gtk/objs.mk Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/objs.mk Sat Jan 04 16:38:48 2025 +0100 @@ -38,13 +38,14 @@ GTKOBJ += button.o GTKOBJ += display.o GTKOBJ += text.o -GTKOBJ += model.o -GTKOBJ += tree.o +GTKOBJ += list.o GTKOBJ += image.o +GTKOBJ += icon.o GTKOBJ += graphics.o GTKOBJ += range.o GTKOBJ += entry.o GTKOBJ += dnd.o +GTKOBJ += headerbar.o TOOLKITOBJS += $(GTKOBJ:%=$(GTK_OBJPRE)%) TOOLKITSOURCE += $(GTKOBJ:%.o=gtk/%.c)
--- a/ui/gtk/range.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/range.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,13 +31,12 @@ #include "range.h" #include "container.h" -#include <ucx/mempool.h> #include "../common/context.h" #include "../common/object.h" static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) { -#ifdef UI_GTK3 +#if GTK_MAJOR_VERSION >= 3 GtkWidget *scrollbar = gtk_scrollbar_new(orientation == UI_HORIZONTAL ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, NULL); #else GtkWidget *scrollbar; @@ -62,6 +61,7 @@ event->userdata = userdata; event->callback = f; event->value = 0; + event->customdata = NULL; g_signal_connect( G_OBJECT(scrollbar), @@ -124,7 +124,7 @@ #else gtk_adjustment_set_page_size(a, extent); #endif -#if !(GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 18) +#if GTK_MAJOR_VERSION * 100 + GTK_MIMOR_VERSION < 318 gtk_adjustment_changed(a); #endif range->extent = extent;
--- a/ui/gtk/text.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/text.c Sat Jan 04 16:38:48 2025 +0100 @@ -33,6 +33,12 @@ #include "text.h" #include "container.h" +#include <cx/printf.h> + +#include <gdk/gdkkeysyms.h> + + +#include "../common/types.h" static void selection_handler( GtkTextBuffer *buf, @@ -56,8 +62,14 @@ } } -UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) { +UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { + UiObject* current = uic_current_obj(obj); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_TEXT); + GtkWidget *text_area = gtk_text_view_new(); + ui_set_name_and_style(text_area, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, text_area, args.groups); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_area), GTK_WRAP_WORD_CHAR); g_signal_connect( text_area, @@ -66,9 +78,12 @@ NULL); UiTextArea *uitext = malloc(sizeof(UiTextArea)); + uitext->obj = obj; uitext->ctx = obj->ctx; uitext->var = var; uitext->last_selection_state = 0; + uitext->onchange = args.onchange; + uitext->onchangedata = args.onchangedata; g_signal_connect( text_area, @@ -76,29 +91,29 @@ G_CALLBACK(ui_textarea_destroy), uitext); - GtkWidget *scroll_area = gtk_scrolled_window_new (NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(scroll_area), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), text_area); + SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area); // font and padding - PangoFontDescription *font; - font = pango_font_description_from_string("Monospace"); - gtk_widget_modify_font(text_area, font); - pango_font_description_free(font); + //PangoFontDescription *font; + //font = pango_font_description_from_string("Monospace"); + //gtk_widget_modify_font(text_area, font); // TODO + //pango_font_description_free(font); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_area), 2); gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_area), 2); // add - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scroll_area, TRUE); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scroll_area, TRUE); // bind value - UiText *value = var->value; - if(value) { + if(var) { + UiText *value = var->value; GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_area)); if(value->value.ptr) { @@ -150,31 +165,14 @@ } void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea) { - ui_destroy_boundvar(textarea->ctx, textarea->var); + if(textarea->var) { + ui_destroy_boundvar(textarea->ctx, textarea->var); + } free(textarea); } -UIWIDGET ui_textarea(UiObject *obj, UiText *value) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - var->from = NULL; - var->from_ctx = NULL; - return ui_textarea_var(obj, var); -} - -UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT); - if(var) { - return ui_textarea_var(obj, var); - } else { - // TODO: error - } - return NULL; -} - UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { - return gtk_bin_get_child(GTK_BIN(textarea)); + return SCROLLEDWINDOW_GET_CHILD(textarea); } char* ui_textarea_get(UiText *text) { @@ -191,7 +189,7 @@ return str; } -void ui_textarea_set(UiText *text, char *str) { +void ui_textarea_set(UiText *text, const char *str) { gtk_text_buffer_set_text((GtkTextBuffer*)text->obj, str, -1); if(text->value.ptr) { text->value.free(text->value.ptr); @@ -273,13 +271,19 @@ void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) { UiText *value = textarea->var->value; + + UiEvent e; + e.obj = textarea->obj; + e.window = e.obj->window; + e.document = textarea->ctx->document; + e.eventdata = value; + e.intval = 0; + + if(textarea->onchange) { + textarea->onchange(&e, textarea->onchangedata); + } + if(value->observers) { - UiEvent e; - e.obj = textarea->ctx->obj; - e.window = e.obj->window; - e.document = textarea->ctx->document; - e.eventdata = value; - e.intval = 0; ui_notify_evt(value->observers, &e); } } @@ -304,19 +308,19 @@ } if(mgr->cur) { - UcxList *elm = mgr->cur->next; + UiTextBufOp *elm = mgr->cur->next; if(elm) { mgr->cur->next = NULL; + mgr->end = mgr->cur; while(elm) { elm->prev = NULL; - UcxList *next = elm->next; - ui_free_textbuf_op(elm->data); - free(elm); + UiTextBufOp *next = elm->next; + ui_free_textbuf_op(elm); elm = next; } } - UiTextBufOp *last_op = mgr->cur->data; + UiTextBufOp *last_op = mgr->cur; if( last_op->type == UI_TEXTBUF_INSERT && ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) @@ -341,15 +345,22 @@ dpstr[length] = 0; UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); + op->prev = NULL; + op->next = NULL; op->type = UI_TEXTBUF_INSERT; op->start = gtk_text_iter_get_offset(location); op->end = op->start+length; op->len = length; op->text = dpstr; - UcxList *elm = ucx_list_append(NULL, op); - mgr->cur = elm; - mgr->begin = ucx_list_concat(mgr->begin, elm); + cx_linked_list_add( + (void**)&mgr->begin, + (void**)&mgr->end, + offsetof(UiTextBufOp, prev), + offsetof(UiTextBufOp, next), + op); + + mgr->cur = op; } void ui_textbuf_delete( @@ -369,14 +380,14 @@ } if(mgr->cur) { - UcxList *elm = mgr->cur->next; + UiTextBufOp *elm = mgr->cur->next; if(elm) { mgr->cur->next = NULL; + mgr->end = mgr->cur; while(elm) { elm->prev = NULL; - UcxList *next = elm->next; - ui_free_textbuf_op(elm->data); - free(elm); + UiTextBufOp *next = elm->next; + ui_free_textbuf_op(elm); elm = next; } } @@ -385,6 +396,8 @@ char *text = gtk_text_buffer_get_text(value->obj, start, end, FALSE); UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); + op->prev = NULL; + op->next = NULL; op->type = UI_TEXTBUF_DELETE; op->start = gtk_text_iter_get_offset(start); op->end = gtk_text_iter_get_offset(end); @@ -395,20 +408,39 @@ dpstr[op->len] = 0; op->text = dpstr; - UcxList *elm = ucx_list_append(NULL, op); - mgr->cur = elm; - mgr->begin = ucx_list_concat(mgr->begin, elm); + cx_linked_list_add( + (void**)&mgr->begin, + (void**)&mgr->end, + offsetof(UiTextBufOp, prev), + offsetof(UiTextBufOp, next), + op); + + mgr->cur = op; } UiUndoMgr* ui_create_undomgr() { UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr)); mgr->begin = NULL; + mgr->end = NULL; mgr->cur = NULL; mgr->length = 0; mgr->event = 1; return mgr; } +void ui_destroy_undomgr(UiUndoMgr *mgr) { + UiTextBufOp *op = mgr->begin; + while(op) { + UiTextBufOp *nextOp = op->next; + if(op->text) { + free(op->text); + } + free(op); + op = nextOp; + } + free(mgr); +} + void ui_free_textbuf_op(UiTextBufOp *op) { if(op->text) { free(op->text); @@ -440,7 +472,7 @@ UiUndoMgr *mgr = value->undomgr; if(mgr->cur) { - UiTextBufOp *op = mgr->cur->data; + UiTextBufOp *op = mgr->cur; mgr->event = 0; switch(op->type) { case UI_TEXTBUF_INSERT: { @@ -468,7 +500,7 @@ void ui_text_redo(UiText *value) { UiUndoMgr *mgr = value->undomgr; - UcxList *elm = NULL; + UiTextBufOp *elm = NULL; if(mgr->cur) { if(mgr->cur->next) { elm = mgr->cur->next; @@ -478,7 +510,7 @@ } if(elm) { - UiTextBufOp *op = elm->data; + UiTextBufOp *op = elm; mgr->event = 0; switch(op->type) { case UI_TEXTBUF_INSERT: { @@ -504,12 +536,23 @@ } -static UIWIDGET create_textfield_var(UiObject *obj, int width, UiBool frameless, UiBool password, UiVar *var) { + + +static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool password, UiTextFieldArgs args) { GtkWidget *textfield = gtk_entry_new(); + ui_set_name_and_style(textfield, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, textfield, args.groups); + + UiObject* current = uic_current_obj(obj); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); UiTextField *uitext = malloc(sizeof(UiTextField)); - uitext->ctx = obj->ctx; + uitext->obj = obj; uitext->var = var; + uitext->onchange = args.onchange; + uitext->onchangedata = args.onchangedata; + uitext->onactivate = args.onactivate; + uitext->onactivatedata = args.onactivatedata; g_signal_connect( textfield, @@ -517,8 +560,11 @@ G_CALLBACK(ui_textfield_destroy), uitext); - if(width > 0) { - gtk_entry_set_width_chars(GTK_ENTRY(textfield), width); + if(args.width > 0) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 + gtk_entry_set_width_chars(GTK_ENTRY(textfield), args.width); +#endif } if(frameless) { // TODO: gtk2legacy workaroud @@ -528,13 +574,13 @@ gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE); } - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, textfield, FALSE); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, textfield, FALSE); if(var) { UiString *value = var->value; if(value->value.ptr) { - gtk_entry_set_text(GTK_ENTRY(textfield), value->value.ptr); + ENTRY_SET_TEXT(textfield, value->value.ptr); value->value.free(value->value.ptr); value->value.ptr = NULL; value->value.free = NULL; @@ -545,7 +591,9 @@ value->value.ptr = NULL; value->value.free = NULL; value->obj = GTK_ENTRY(textfield); - + } + + if(args.onchange || var) { g_signal_connect( textfield, "changed", @@ -553,105 +601,544 @@ uitext); } + if(args.onactivate) { + g_signal_connect( + textfield, + "activate", + G_CALLBACK(ui_textfield_activate), + uitext); + } + return textfield; } -static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING); - if(var) { - return create_textfield_var(obj, width, frameless, password, var); - } else { - // TODO: error - } - return NULL; +UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs args) { + return create_textfield(obj, FALSE, FALSE, args); } -static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) { - UiVar *var = NULL; - if(value) { - var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - var->from = NULL; - var->from_ctx = NULL; - } - return create_textfield_var(obj, width, frameless, password, var); +UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { + return create_textfield(obj, TRUE, FALSE, args); } +UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { + return create_textfield(obj, FALSE, TRUE, args); +} + + void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) { - if(textfield->var) { - ui_destroy_boundvar(textfield->ctx, textfield->var); - } free(textfield); } void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield) { UiString *value = textfield->var->value; - if(value->observers) { - UiEvent e; - e.obj = textfield->ctx->obj; - e.window = e.obj->window; - e.document = textfield->ctx->document; - e.eventdata = value; - e.intval = 0; + + UiEvent e; + e.obj = textfield->obj; + e.window = e.obj->window; + e.document = textfield->obj->ctx->document; + e.eventdata = value; + e.intval = 0; + + if(textfield->onchange) { + textfield->onchange(&e, textfield->onchangedata); + } + + if(textfield->var) { ui_notify_evt(value->observers, &e); } } -UIWIDGET ui_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, FALSE, varname); -} - -UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, FALSE, varname); -} - -UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, TRUE, FALSE, value); -} - -UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, TRUE, FALSE, varname); -} - -UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, TRUE, value); -} - -UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, TRUE, varname); -} - -UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, TRUE, value); -} - -UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, TRUE, varname); +void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) { + if(textfield->onactivate) { + UiEvent e; + e.obj = textfield->obj; + e.window = e.obj->window; + e.document = textfield->obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + textfield->onactivate(&e, textfield->onactivatedata); + } } char* ui_textfield_get(UiString *str) { if(str->value.ptr) { str->value.free(str->value.ptr); } - str->value.ptr = g_strdup(gtk_entry_get_text(str->obj)); + str->value.ptr = g_strdup(ENTRY_GET_TEXT(str->obj)); str->value.free = (ui_freefunc)g_free; return str->value.ptr; } -void ui_textfield_set(UiString *str, char *value) { - gtk_entry_set_text(str->obj, value); +void ui_textfield_set(UiString *str, const char *value) { + ENTRY_SET_TEXT(str->obj, value); if(str->value.ptr) { str->value.free(str->value.ptr); str->value.ptr = NULL; str->value.free = NULL; } } + +// ----------------------- path textfield ----------------------- + +// TODO: move to common +static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return NULL; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} + +static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { + for(int i=0;i<nelm;i++) { + free(elms[i].name); + free(elms[i].path); + } + free(elms); +} + +static void ui_path_textfield_destroy(GtkWidget *object, UiPathTextField *pathtf) { + g_object_unref(pathtf->entry); + free(pathtf); +} + +void ui_path_button_clicked(GtkWidget *widget, UiEventDataExt *event) { + UiPathTextField *pathtf = event->customdata1; + for(int i=0;i<event->value1;i++) { + if(i <= event->value0) { + WIDGET_REMOVE_CSS_CLASS(pathtf->current_path_buttons[i], "pathbar-button-inactive"); + } else { + WIDGET_ADD_CSS_CLASS(pathtf->current_path_buttons[i], "pathbar-button-inactive"); + } + } + + UiPathElm *elm = event->customdata0; + cxmutstr path = cx_strdup(cx_strn(elm->path, elm->path_len)); + UiEvent evt; + evt.obj = event->obj; + evt.window = evt.obj->window; + evt.document = evt.obj->ctx->document; + evt.eventdata = elm->path; + evt.intval = event->value0; + event->callback(&evt, event->userdata); + free(path.ptr); +} + +int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path) { + size_t full_path_len = strlen(full_path); + if(full_path_len == 0) { + return 1; + } + + size_t nelm = 0; + UiPathElm* path_elm = pathtf->getpathelm(full_path, full_path_len, &nelm, pathtf->getpathelmdata); + if (!path_elm) { + return 1; + } + + free(pathtf->current_path); + pathtf->current_path = strdup(full_path); + + ui_pathelm_destroy(pathtf->current_pathelms, pathtf->current_nelm); + free(pathtf->current_path_buttons); + pathtf->current_path_buttons = calloc(nelm, sizeof(GtkWidget*)); + pathtf->current_pathelms = path_elm; + pathtf->current_nelm = nelm; + + return ui_pathtextfield_update_widget(pathtf); +} + +static GtkWidget* ui_path_elm_button(UiPathTextField *pathtf, UiPathElm *elm, int i) { + cxmutstr name = cx_strdup(cx_strn(elm->name, elm->name_len)); + GtkWidget *button = gtk_button_new_with_label(name.ptr); + pathtf->current_path_buttons[i] = button; + free(name.ptr); + + if(pathtf->onactivate) { + UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt)); + memset(eventdata, 0, sizeof(UiEventDataExt)); + eventdata->callback = pathtf->onactivate; + eventdata->userdata = pathtf->onactivatedata; + eventdata->obj = pathtf->obj; + eventdata->customdata0 = elm; + eventdata->customdata1 = pathtf; + eventdata->value0 = i; + eventdata->value1 = pathtf->current_nelm; + + g_signal_connect( + button, + "clicked", + G_CALLBACK(ui_path_button_clicked), + eventdata); + + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_destroy_userdata), + eventdata); + } + + return button; +} + +static void ui_path_textfield_activate(GtkWidget *entry, UiPathTextField *pathtf) { + const gchar *text = ENTRY_GET_TEXT(pathtf->entry); + if(strlen(text) == 0) { + return; + } + + UiObject *obj = pathtf->obj; + + if(ui_pathtextfield_update(pathtf, text)) { + return; + } + + if(pathtf->onactivate) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = (char*)text; + evt.intval = -1; + pathtf->onactivate(&evt, pathtf->onactivatedata); + } +} + +#if GTK_MAJOR_VERSION >= 4 + +static void pathbar_show_hbox(GtkWidget *widget, UiPathTextField *pathtf) { + if(pathtf->current_path) { + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox); + ENTRY_SET_TEXT(pathtf->entry, pathtf->current_path); + } +} + +static gboolean ui_path_textfield_key_controller( + GtkEventControllerKey* self, + guint keyval, + guint keycode, + GdkModifierType state, + UiPathTextField *pathtf) +{ + if(keyval == GDK_KEY_Escape) { + pathbar_show_hbox(NULL, pathtf); + } + return FALSE; +} + +UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + UiPathTextField *pathtf = malloc(sizeof(UiPathTextField)); + memset(pathtf, 0, sizeof(UiPathTextField)); + pathtf->obj = obj; + pathtf->getpathelm = args.getpathelm; + pathtf->getpathelmdata = args.getpathelmdata; + pathtf->onactivate = args.onactivate; + pathtf->onactivatedata = args.onactivatedata; + pathtf->ondragcomplete = args.ondragcomplete; + pathtf->ondragcompletedata = args.ondragcompletedata; + pathtf->ondragstart = args.ondragstart; + pathtf->ondragstartdata = args.ondragstartdata; + pathtf->ondrop = args.ondrop; + pathtf->ondropdata = args.ondropsdata; + + if(!pathtf->getpathelm) { + pathtf->getpathelm = default_pathelm_func; + pathtf->getpathelmdata = NULL; + } + + pathtf->stack = gtk_stack_new(); + gtk_widget_set_name(pathtf->stack, "path-textfield-box"); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, pathtf->stack, FALSE); + + pathtf->entry_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + pathtf->entry = gtk_entry_new(); + gtk_box_append(GTK_BOX(pathtf->entry_box), pathtf->entry); + gtk_widget_set_hexpand(pathtf->entry, TRUE); + + GtkWidget *cancel_button = gtk_button_new_from_icon_name("window-close-symbolic"); + gtk_widget_add_css_class(cancel_button, "flat"); + gtk_widget_add_css_class(cancel_button, "pathbar-extra-button"); + gtk_box_append(GTK_BOX(pathtf->entry_box), cancel_button); + g_signal_connect( + cancel_button, + "clicked", + G_CALLBACK(pathbar_show_hbox), + pathtf); + + gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + g_object_ref(pathtf->entry); // for compatibility with older pathbar version + g_signal_connect( + pathtf->entry, + "activate", + G_CALLBACK(ui_path_textfield_activate), + pathtf); + + GtkEventController *entry_cancel = gtk_event_controller_key_new(); + gtk_widget_add_controller(pathtf->entry, entry_cancel); + g_signal_connect(entry_cancel, "key-pressed", G_CALLBACK(ui_path_textfield_key_controller), pathtf); + + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = pathtf; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + + if(value->value.ptr) { + char *str = strdup(value->value.ptr); + ui_string_set(value, str); + free(str); + } + } + + return pathtf->stack; +} + +static void pathbar_pressed( + GtkGestureClick* self, + gint n_press, + gdouble x, + gdouble y, + UiPathTextField *pathtf) +{ + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->entry_box); + gtk_widget_grab_focus(pathtf->entry); +} + +int ui_pathtextfield_update_widget(UiPathTextField* pathtf) { + // recreate button hbox + if(pathtf->hbox) { + gtk_stack_remove(GTK_STACK(pathtf->stack), pathtf->hbox); + } + pathtf->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous(GTK_BOX(pathtf->hbox), FALSE); + gtk_stack_add_child(GTK_STACK(pathtf->stack), pathtf->hbox); + gtk_widget_set_name(pathtf->hbox, "pathbar"); + + // add buttons for path elements + for (int i=0;i<pathtf->current_nelm;i++) { + UiPathElm *elm = &pathtf->current_pathelms[i]; + + GtkWidget *button = ui_path_elm_button(pathtf, elm, i); + gtk_widget_add_css_class(button, "flat"); + + gtk_box_append(GTK_BOX(pathtf->hbox), button); + + if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) { + GtkWidget *path_separator = gtk_label_new("/"); + gtk_widget_add_css_class(path_separator, "pathbar-button-inactive"); + gtk_box_append(GTK_BOX(pathtf->hbox), path_separator); + } + } + gtk_stack_set_visible_child(GTK_STACK(pathtf->stack), pathtf->hbox); + + // create a widget for receiving button press events + GtkWidget *event_area = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + GtkGesture *handler = gtk_gesture_click_new(); + gtk_widget_add_controller(event_area, GTK_EVENT_CONTROLLER(handler)); + g_signal_connect( + handler, + "pressed", + G_CALLBACK(pathbar_pressed), + pathtf); + gtk_widget_set_hexpand(event_area, TRUE); + gtk_widget_set_vexpand(event_area, TRUE); + gtk_box_append(GTK_BOX(pathtf->hbox), event_area); + + return 0; +} + +#else + +static gboolean path_textfield_btn_pressed(GtkWidget *widget, GdkEventButton *event, UiPathTextField *pathtf) { + gtk_box_pack_start(GTK_BOX(pathtf->hbox), pathtf->entry, TRUE, TRUE, 0); + gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox); + pathtf->buttonbox = NULL; + + gtk_widget_show(pathtf->entry); + gtk_widget_grab_focus(pathtf->entry); + + return TRUE; +} + +static gboolean ui_path_textfield_key_press(GtkWidget *self, GdkEventKey *event, UiPathTextField *pathtf) { + if (event->keyval == GDK_KEY_Escape) { + // reset GtkEntry value + gtk_entry_set_text(GTK_ENTRY(self), pathtf->current_path); + const gchar *text = gtk_entry_get_text(GTK_ENTRY(self)); + ui_pathtextfield_update(pathtf, text); + return TRUE; + } + return FALSE; +} + +static GtkWidget* create_path_button_box() { + GtkWidget *bb = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_EXPAND); // linked style + gtk_box_set_homogeneous(GTK_BOX(bb), FALSE); + gtk_box_set_spacing(GTK_BOX(bb), 0); + return bb; +} + +UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + UiPathTextField *pathtf = malloc(sizeof(UiPathTextField)); + memset(pathtf, 0, sizeof(UiPathTextField)); + pathtf->obj = obj; + pathtf->getpathelm = args.getpathelm; + pathtf->getpathelmdata = args.getpathelmdata; + pathtf->onactivate = args.onactivate; + pathtf->onactivatedata = args.onactivatedata; + pathtf->ondragcomplete = args.ondragcomplete; + pathtf->ondragcompletedata = args.ondragcompletedata; + pathtf->ondragstart = args.ondragstart; + pathtf->ondragstartdata = args.ondragstartdata; + pathtf->ondrop = args.ondrop; + pathtf->ondropdata = args.ondropsdata; + + if(!pathtf->getpathelm) { + pathtf->getpathelm = default_pathelm_func; + pathtf->getpathelmdata = NULL; + } + + // top level container for the path textfield is a GtkEventBox + // the event box is needed to handle background button presses + GtkWidget *eventbox = gtk_event_box_new(); + g_signal_connect( + eventbox, + "button-press-event", + G_CALLBACK(path_textfield_btn_pressed), + pathtf); + g_signal_connect( + eventbox, + "destroy", + G_CALLBACK(ui_path_textfield_destroy), + pathtf); + + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, eventbox, FALSE); + + // hbox as parent for the GtkEntry and GtkButtonBox + GtkWidget *hbox = ui_gtk_hbox_new(0); + pathtf->hbox = hbox; + gtk_container_add(GTK_CONTAINER(eventbox), hbox); + gtk_widget_set_name(hbox, "path-textfield-box"); + + // create GtkEntry, that is also visible by default (with input yet) + pathtf->entry = gtk_entry_new(); + g_object_ref(G_OBJECT(pathtf->entry)); + gtk_box_pack_start(GTK_BOX(hbox), pathtf->entry, TRUE, TRUE, 0); + + g_signal_connect( + pathtf->entry, + "activate", + G_CALLBACK(ui_path_textfield_activate), + pathtf); + g_signal_connect( + pathtf->entry, + "key-press-event", + G_CALLBACK(ui_path_textfield_key_press), + pathtf); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = pathtf; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + + if(value->value.ptr) { + char *str = strdup(value->value.ptr); + ui_string_set(value, str); + free(str); + } + } + + return hbox; +} + +int ui_pathtextfield_update_widget(UiPathTextField* pathtf) { + GtkWidget *buttonbox = create_path_button_box(); + + // switch from entry to buttonbox or remove current buttonbox + if(pathtf->buttonbox) { + gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->buttonbox); + } else { + gtk_container_remove(GTK_CONTAINER(pathtf->hbox), pathtf->entry); + } + gtk_box_pack_start(GTK_BOX(pathtf->hbox), buttonbox, FALSE, FALSE, 0); + pathtf->buttonbox = buttonbox; + + for (int i=0;i<pathtf->current_nelm;i++) { + UiPathElm *elm = &pathtf->current_pathelms[i]; + GtkWidget *button = ui_path_elm_button(pathtf, elm, i); + gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 0); + } + + gtk_widget_show_all(buttonbox); + + return 0; +} + +#endif + +char* ui_path_textfield_get(UiString *str) { + if(str->value.ptr) { + str->value.free(str->value.ptr); + } + UiPathTextField *tf = str->obj; + str->value.ptr = g_strdup(ENTRY_GET_TEXT(tf->entry)); + str->value.free = (ui_freefunc)g_free; + return str->value.ptr; +} + +void ui_path_textfield_set(UiString *str, const char *value) { + UiPathTextField *tf = str->obj; + ENTRY_SET_TEXT(tf->entry, value); + ui_pathtextfield_update(tf, value); + if(str->value.ptr) { + str->value.free(str->value.ptr); + str->value.ptr = NULL; + str->value.free = NULL; + } +}
--- a/ui/gtk/text.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/text.h Sat Jan 04 16:38:48 2025 +0100 @@ -31,7 +31,7 @@ #include "../ui/text.h" #include "toolkit.h" -#include <ucx/list.h> +#include <cx/linked_list.h> #include "../common/context.h" #ifdef __cplusplus @@ -40,38 +40,79 @@ #define UI_TEXTBUF_INSERT 0 #define UI_TEXTBUF_DELETE 1 -typedef struct UiTextBufOp { + +typedef struct UiTextBufOp UiTextBufOp; +struct UiTextBufOp { + UiTextBufOp *prev; + UiTextBufOp *next; int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE int start; int end; int len; char *text; -} UiTextBufOp; +}; typedef struct UiUndoMgr { - UcxList *begin; - UcxList *cur; - int length; - int event; + UiTextBufOp *begin; + UiTextBufOp *end; + UiTextBufOp *cur; + int length; + int event; } UiUndoMgr; typedef struct UiTextArea { - UiContext *ctx; - UiVar *var; - int last_selection_state; + UiObject *obj; + UiContext *ctx; + UiVar *var; + int last_selection_state; + ui_callback onchange; + void *onchangedata; } UiTextArea; typedef struct UiTextField { - UiContext *ctx; - UiVar *var; - // TODO: validatefunc + UiObject *obj; + UiVar *var; + ui_callback onchange; + void *onchangedata; + ui_callback onactivate; + void *onactivatedata; } UiTextField; +typedef struct UiPathTextField { + UiObject *obj; + + GtkWidget *stack; + GtkWidget *hbox; + GtkWidget *entry_box; + GtkWidget *entry; +#if GTK_MAJOR_VERSION == 3 + GtkWidget *buttonbox; +#endif + + char *current_path; + UiPathElm *current_pathelms; + GtkWidget **current_path_buttons; + size_t current_nelm; + + ui_pathelm_func getpathelm; + void* getpathelmdata; + + ui_callback onactivate; + void* onactivatedata; + + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropdata; +} UiPathTextField; + UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var); void ui_textarea_destroy(GtkWidget *object, UiTextArea *textarea); char* ui_textarea_get(UiText *text); -void ui_textarea_set(UiText *text, char *str); +void ui_textarea_set(UiText *text, const char *str); char* ui_textarea_getsubstr(UiText *text, int begin, int end); void ui_textarea_insert(UiText *text, int pos, char *str); void ui_textarea_setposition(UiText *text, int pos); @@ -95,14 +136,21 @@ GtkTextIter *end, void *data); UiUndoMgr* ui_create_undomgr(); +void ui_destroy_undomgr(UiUndoMgr *mgr); void ui_free_textbuf_op(UiTextBufOp *op); int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen); void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield); void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield); +void ui_textfield_activate(GtkEntry* self, UiTextField *textfield); char* ui_textfield_get(UiString *str); -void ui_textfield_set(UiString *str, char *value); +void ui_textfield_set(UiString *str, const char *value); + +int ui_pathtextfield_update(UiPathTextField* pathtf, const char *full_path); +int ui_pathtextfield_update_widget(UiPathTextField* pathtf); +char* ui_path_textfield_get(UiString *str); +void ui_path_textfield_set(UiString *str, const char *value); #ifdef __cplusplus }
--- a/ui/gtk/toolbar.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/toolbar.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,199 +31,20 @@ #include <string.h> #include "toolbar.h" +#include "menu.h" #include "button.h" -#include "image.h" -#include "tree.h" -#include <ucx/mempool.h> +#include "icon.h" +#include "list.h" +#include <cx/mempool.h> +#include <cx/hash_map.h> +#include <cx/linked_list.h> +#include <cx/array_list.h> #include "../common/context.h" -static UcxMap *toolbar_items; -static UcxList *defaults; -void ui_toolbar_init() { - toolbar_items = ucx_map_new(16); -} - -void ui_toolitem(char *name, char *label, ui_callback f, void *udata) { - ui_toolitem_img(name, label, NULL, f, udata); -} - -void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *userdata) { - ui_toolitem_stgr(name, stockid, f, userdata, -1); -} - -void ui_toolitem_sti(char *name, char *stockid, ui_callback f, void *userdata) { - ui_toolitem_stgri(name, stockid, f, userdata, -1); -} - -void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *userdata, ...) { - va_list ap; - va_start(ap, userdata); - ui_toolitem_vstgr(name, stockid, 0, f, userdata, ap); - va_end(ap); -} - -void ui_toolitem_stgri(char *name, char *stockid, ui_callback f, void *userdata, ...) { - va_list ap; - va_start(ap, userdata); - ui_toolitem_vstgr(name, stockid, 1, f, userdata, ap); - va_end(ap); -} - -void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata) { - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget; - item->label = label; - item->image = img; - item->callback = f; - item->userdata = udata; - item->isimportant = 0; - item->groups = NULL; - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_vstgr( - char *name, - char *stockid, - int isimportant, - ui_callback f, - void *userdata, - va_list ap) -{ - UiStToolItem *item = malloc(sizeof(UiStToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_widget; - item->stockid = stockid; - item->callback = f; - item->userdata = userdata; - item->groups = NULL; - item->isimportant = isimportant; - - // add groups - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_toggle(const char *name, const char *label, const char *img, UiInteger *i) { - UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = label; - item->image = img; - item->stockid = NULL; - item->groups = NULL; - item->isimportant = 0; - item->value = i; - item->var = NULL; - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_toggle_st(const char *name, const char *stockid, UiInteger *i) { - UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = NULL; - item->image = NULL; - item->stockid = stockid; - item->groups = NULL; - item->isimportant = 0; - item->value = i; - item->var = NULL; - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_toggle_nv(const char *name, const char *label, const char *img, const char *intvar) { - UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = label; - item->image = img; - item->stockid = NULL; - item->groups = NULL; - item->isimportant = 0; - item->value = NULL; - item->var = intvar; - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_toggle_stnv(const char *name, const char *stockid, const char *intvar) { - UiToggleToolItem *item = malloc(sizeof(UiToggleToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = NULL; - item->image = NULL; - item->stockid = stockid; - item->groups = NULL; - item->isimportant = 0; - item->value = NULL; - item->var = intvar; - - ucx_map_cstr_put(toolbar_items, name, item); -} - - -void ui_toolbar_combobox( - char *name, - UiList *list, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBox *cb = malloc(sizeof(UiToolbarComboBox)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox; - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - var->from = NULL; - var->from_ctx = NULL; - cb->var = var; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - ucx_map_cstr_put(toolbar_items, name, cb); -} - -void ui_toolbar_combobox_str( - char *name, - UiList *list, - ui_callback f, - void *udata) -{ - ui_toolbar_combobox(name, list, ui_strmodel_getvalue, f, udata); -} - -void ui_toolbar_combobox_nv( - char *name, - char *listname, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBoxNV *cb = malloc(sizeof(UiToolbarComboBoxNV)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox_nv; - cb->listname = listname; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - ucx_map_cstr_put(toolbar_items, name, cb); -} - - -void ui_toolbar_add_default(char *name) { - char *s = strdup(name); - defaults = ucx_list_append(defaults, s); -} +#if UI_GTK2 || UI_GTK3 GtkWidget* ui_create_toolbar(UiObject *obj) { - if(!defaults) { - return NULL; - } - GtkWidget *toolbar = gtk_toolbar_new(); #ifdef UI_GTK3 gtk_style_context_add_class( @@ -231,39 +52,110 @@ GTK_STYLE_CLASS_PRIMARY_TOOLBAR); #endif + CxMap *items = uic_get_toolbar_items(); + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + ui_toolbar_add_items(obj, toolbar, items, left_defaults); + ui_toolbar_add_items(obj, toolbar, items, center_defaults); + ui_toolbar_add_items(obj, toolbar, items, right_defaults); + + /* GtkToolbar *tb = GTK_TOOLBAR(toolbar); - UCX_FOREACH(elm, defaults) { - UiToolItemI *item = ucx_map_cstr_get(toolbar_items, elm->data); + CxIterator i = cxListIterator(defaults); + cx_foreach(char *, def, i) { + UiToolItemI *item = cxMapGet(toolbar_items, def); if(item) { item->add_to(tb, item, obj); - } else if(!strcmp(elm->data, "@separator")) { + } else if(!strcmp(def, "@separator")) { gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1); } else { - fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", elm->data); + fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def); } } + */ return toolbar; } -void add_toolitem_widget(GtkToolbar *tb, UiToolItem *item, UiObject *obj) { - GtkToolItem *button = gtk_tool_button_new(NULL, item->label); - gtk_tool_item_set_homogeneous(button, FALSE); - if(item->image) { - GdkPixbuf *pixbuf = ui_get_image(item->image); - GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); - gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(button), image); +static void create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) { + GtkToolbar *tb = GTK_TOOLBAR(toolbar); + switch(i->type) { + case UI_TOOLBAR_ITEM: { + add_toolitem_widget(tb, (UiToolbarItem*)i, obj); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + add_toolitem_toggle_widget(tb, (UiToolbarToggleItem*)i, obj); + break; + } + case UI_TOOLBAR_MENU: { + add_toolitem_menu_widget(tb, (UiToolbarMenuItem*)i, obj); + break; + } + default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); + } +} + +void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults) { + // add pre-configured items + CxIterator i = cxListIterator(defaults); + cx_foreach(char*, def, i) { + UiToolbarItemI* item = uic_toolbar_get_item(def); + if (!item) { + fprintf(stderr, "unknown toolbar item: %s\n", def); + continue; + } + create_item(obj, toolbar, item); + } +} + +static void set_toolbutton_icon(GtkToolItem *item, const char *icon_name) { +#if GTK_MAJOR_VERSION >= 3 + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), icon_name); +#else + UiIcon *icon = ui_icon(icon_name, 24); + if(icon) { + GdkPixbuf *pixbuf = ui_icon_pixbuf(icon); + if(pixbuf) { + GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(item), image); + } + } +#endif +} + +void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) { + GtkToolItem *button; + if(item->args.stockid) { +#ifdef UI_GTK2 + button = gtk_tool_button_new_from_stock(item->args.stockid); +#else + // TODO: gtk3 stock + button = gtk_tool_button_new(NULL, item->args.label); +#endif } else { - gtk_tool_item_set_is_important(button, TRUE); + button = gtk_tool_button_new(NULL, item->args.label); } - if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, + gtk_tool_item_set_homogeneous(button, FALSE); + if(item->args.icon) { + set_toolbutton_icon(button, item->args.icon); + } + gtk_tool_item_set_is_important(button, TRUE); + + ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups); + + if(item->args.onclick) { + UiEventData *event = cxMalloc( + obj->ctx->allocator, sizeof(UiEventData)); event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; + event->callback = item->args.onclick; + event->userdata = item->args.onclickdata; + event->customdata = NULL; + event->value = 0; g_signal_connect( button, @@ -274,103 +166,72 @@ gtk_toolbar_insert(tb, button, -1); + /* if(item->groups) { uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups); } + */ } -void add_toolitem_st_widget(GtkToolbar *tb, UiStToolItem *item, UiObject *obj) { - GtkToolItem *button = gtk_tool_button_new_from_stock(item->stockid); - gtk_tool_item_set_homogeneous(button, FALSE); - if(item->isimportant) { - gtk_tool_item_set_is_important(button, TRUE); - } - - if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - - g_signal_connect( - button, - "clicked", - G_CALLBACK(ui_button_clicked), - event); - } - - gtk_toolbar_insert(tb, button, -1); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups); - } -} - -void add_toolitem_toggle_widget(GtkToolbar *tb, UiToggleToolItem *item, UiObject *obj) { +void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj) { GtkToolItem *button; - if(item->stockid) { - button = gtk_toggle_tool_button_new_from_stock(item->stockid); + if(item->args.stockid) { +#ifdef UI_GTK2 + button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); +#else + button = gtk_toggle_tool_button_new_from_stock(item->args.stockid); // TODO: gtk3 stock +#endif } else { button = gtk_toggle_tool_button_new(); gtk_tool_item_set_homogeneous(button, FALSE); - if(item->label) { - gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->label); + if(item->args.label) { + gtk_tool_button_set_label(GTK_TOOL_BUTTON(button), item->args.label); } - if(item->image) { - GdkPixbuf *pixbuf = ui_get_image(item->image); - GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); - gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(button), image); + if(item->args.icon) { + set_toolbutton_icon(button, item->args.icon); } } + ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups); - UiVar *var; - if(item->value) { - var = malloc(sizeof(UiVar)); - var->value = item->value; - var->type = UI_VAR_SPECIAL; - var->from = NULL; - var->from_ctx = NULL; - } else { - var = uic_create_var(obj->ctx, item->var, UI_VAR_INTEGER); - } - - if(var->value) { - UiInteger *i = var->value; - i->get = ui_tool_toggle_button_get; - i->set = ui_tool_toggle_button_set; - i->obj = button; - - if(i->value != 0) { - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), TRUE); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER); + if(var) { + UiInteger *i = (UiInteger*)var->value; + if(i) { + i->get = ui_tool_toggle_button_get; + i->set = ui_tool_toggle_button_set; + i->obj = button; + + if(i->value != 0) { + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), TRUE); + } } } - // register event - // the event func will call the UiInteger observer callbacks - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); + UiVarEventData *event = cxMalloc( + obj->ctx->allocator, + sizeof(UiVarEventData)); event->obj = obj; - event->userdata = var; - event->callback = NULL; + event->callback = item->args.onchange; + event->userdata = item->args.onchangedata; + event->var = var; g_signal_connect( - button, - "toggled", - G_CALLBACK(ui_tool_button_toggled), - event); + button, + "toggled", + G_CALLBACK(ui_tool_button_toggled), + event); // add item to toolbar gtk_toolbar_insert(tb, button, -1); + /* if(item->groups) { uic_add_group_widget(obj->ctx, button, (ui_enablefunc)ui_set_enabled, item->groups); } + */ } -void ui_tool_button_toggled(GtkToggleToolButton *widget, UiEventData *event) { +void ui_tool_button_toggled(GtkToggleToolButton *widget, UiVarEventData *event) { UiEvent e; e.obj = event->obj; e.window = event->obj->window; @@ -378,10 +239,16 @@ e.eventdata = NULL; e.intval = gtk_toggle_tool_button_get_active(widget); - UiVar *var = event->userdata; - UiInteger *i = var->value; + if(event->callback) { + event->callback(&e, event->userdata); + } - ui_notify_evt(i->observers, &e); + UiVar *var = event->var; + UiInteger *i = var ? var->value : NULL; + + if(i) { + ui_notify_evt(i->observers, &e); + } } int64_t ui_tool_toggle_button_get(UiInteger *integer) { @@ -395,6 +262,70 @@ integer->value = s; } + + +typedef struct UiToolbarMenuWidget { + GtkWidget *button; + GtkMenu *menu; +} UiToolbarMenuWidget; + +static void ui_toolbar_menubutton_clicked(GtkWidget *widget, UiToolbarMenuWidget *menu) { + int x; + gtk_menu_popup_at_widget(menu->menu, menu->button, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL); +} + +static void ui_toolbar_menubutton_destroy(GtkWidget *widget, UiToolbarMenuWidget *menu) { + free(menu); +} + +void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj) { + GtkToolItem *button; + if(item->args.stockid) { +#ifdef UI_GTK2 + button = gtk_tool_button_new_from_stock(item->args.stockid); +#else + // TODO: gtk3 stock + button = gtk_tool_button_new(NULL, item->args.label); +#endif + } else { + button = gtk_tool_button_new(NULL, item->args.label); + } + + gtk_tool_item_set_homogeneous(button, FALSE); + if(item->args.icon) { + set_toolbutton_icon(button, item->args.icon); + } + gtk_tool_item_set_is_important(button, TRUE); + + gtk_toolbar_insert(tb, button, -1); + + // menu + GtkWidget *menu_widget = gtk_menu_new(); + ui_add_menu_items(menu_widget, 0, &item->menu, obj); + gtk_widget_show_all(menu_widget); + + UiToolbarMenuWidget *tbmenu = malloc(sizeof(UiToolbarMenuWidget)); + tbmenu->button = GTK_WIDGET(button); + tbmenu->menu = GTK_MENU(menu_widget); + + g_signal_connect( + button, + "clicked", + G_CALLBACK(ui_toolbar_menubutton_clicked), + tbmenu); + + g_signal_connect( + button, + "destroy", + G_CALLBACK(ui_toolbar_menubutton_destroy), + tbmenu); +} + + + + +// deprecated / unsupported +/* void add_toolbar_combobox(GtkToolbar *tb, UiToolbarComboBox *cb, UiObject *obj) { UiModel *modelinfo = ui_model(obj->ctx, UI_STRING, "", -1); modelinfo->getvalue = cb->getvalue; @@ -419,4 +350,79 @@ gtk_toolbar_insert(tb, item, -1); } } +*/ + + +#ifdef UI_GTK3 + +GtkWidget* ui_create_headerbar(UiObject *obj) { + GtkWidget *headerbar = gtk_header_bar_new(); + + CxMap *items = uic_get_toolbar_items(); + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + ui_toolbar_headerbar_add_items(obj, headerbar, items, left_defaults); + ui_toolbar_headerbar_add_items(obj, headerbar, items, center_defaults); + ui_toolbar_headerbar_add_items(obj, headerbar, items, right_defaults); + + return headerbar; +} + +static void hb_create_item(UiObject *obj, GtkWidget *toolbar, UiToolbarItemI *i) { + GtkHeaderBar *tb = GTK_HEADER_BAR(toolbar); + switch(i->type) { + case UI_TOOLBAR_ITEM: { + add_headerbar_item_widget(tb, (UiToolbarItem*)i, obj); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + add_headerbar_item_toggle_widget(tb, (UiToolbarToggleItem*)i, obj); + break; + } + case UI_TOOLBAR_MENU: { + add_headerbar_item_menu_widget(tb, (UiToolbarMenuItem*)i, obj); + break; + } + default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); + } +} + + +void ui_toolbar_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults) { + // add pre-configured items + CxIterator i = cxListIterator(defaults); + cx_foreach(char*, def, i) { + UiToolbarItemI* item = uic_toolbar_get_item(def); + if (!item) { + fprintf(stderr, "unknown toolbar item: %s\n", def); + continue; + } + hb_create_item(obj, headerbar, item); + } +} + +void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj) { + GtkWidget *button = gtk_button_new_with_label(item->args.label); + if(item->args.icon) { + ui_button_set_icon_name(button, item->args.icon); + } + ui_set_widget_groups(obj->ctx, button, item->args.groups); + + gtk_header_bar_pack_start(hb, button); + +} + +void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj) { + +} + +void add_headerbar_item_menu_widget(GtkHeaderBar *hb, UiToolbarMenuItem *item, UiObject *obj) { + +} + +#endif /* UI_GTK3 */ + +#endif /* UI_GTK2 || UI_GTK3 */
--- a/ui/gtk/toolbar.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/toolbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -30,16 +30,18 @@ #define TOOLBAR_H #include "../ui/toolbar.h" -#include <ucx/map.h> -#include <ucx/list.h> +#include "../common/toolbar.h" +#include <cx/map.h> +#include <cx/list.h> -#include "model.h" -#include "tree.h" +#include "list.h" #ifdef __cplusplus extern "C" { #endif +#if UI_GTK2 || UI_GTK3 + typedef struct UiToolItemI UiToolItemI; typedef struct UiToolItem UiToolItem; typedef struct UiStToolItem UiStToolItem; @@ -61,7 +63,7 @@ ui_callback callback; void *userdata; const char *varname; - UcxList *groups; + CxList *groups; int isimportant; }; @@ -71,7 +73,7 @@ ui_callback callback; void *userdata; const char *varname; - UcxList *groups; + CxList *groups; int isimportant; }; @@ -82,7 +84,7 @@ const char *stockid; UiInteger *value; const char *var; - UcxList *groups; + CxList *groups; int isimportant; }; @@ -97,12 +99,11 @@ struct UiToolbarComboBoxNV { UiToolItemI item; char *listname; - ui_getvaluefunc getvalue; + ui_getvaluefunc getvalue; ui_callback callback; void *userdata; }; -void ui_toolbar_init(); void ui_toolitem_vstgr( char *name, @@ -114,18 +115,33 @@ GtkWidget* ui_create_toolbar(UiObject *obj); -void add_toolitem_widget(GtkToolbar *tb, UiToolItem *item, UiObject *obj); -void add_toolitem_st_widget(GtkToolbar *tb, UiStToolItem *item, UiObject *obj); -void add_toolitem_toggle_widget(GtkToolbar *tb, UiToggleToolItem *item, UiObject *obj); +void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults); + +void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj); +void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObject *obj); +void add_toolitem_menu_widget(GtkToolbar *tb, UiToolbarMenuItem *item, UiObject *obj); + +void ui_tool_button_toggled(GtkToggleToolButton *widget, UiVarEventData *event); +int64_t ui_tool_toggle_button_get(UiInteger *integer); +void ui_tool_toggle_button_set(UiInteger *integer, int64_t value); +GtkWidget* ui_create_headerbar(UiObject *obj); + +void ui_toolbar_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxMap *items, CxList *defaults); + +void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *obj); +void add_headerbar_item_toggle_widget(GtkHeaderBar *hb, UiToolbarToggleItem *item, UiObject *obj); +void add_headerbar_item_menu_widget(GtkHeaderBar *hb, UiToolbarMenuItem *item, UiObject *obj); + + +/* void add_toolbar_combobox(GtkToolbar *tb, UiToolbarComboBox *cb, UiObject *obj); void add_toolbar_combobox_nv(GtkToolbar *tb, UiToolbarComboBoxNV *cb, UiObject *obj); void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); void ui_combobox_update(UiEvent *event, void *combobox); +*/ -void ui_tool_button_toggled(GtkToggleToolButton *widget, UiEventData *event); -int64_t ui_tool_toggle_button_get(UiInteger *integer); -void ui_tool_toggle_button_set(UiInteger *integer, int64_t value); +#endif #ifdef __cplusplus }
--- a/ui/gtk/toolkit.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/toolkit.c Sat Jan 04 16:38:48 2025 +0100 @@ -33,20 +33,24 @@ #include "toolkit.h" #include "toolbar.h" -#include "model.h" -#include "image.h" +#include "icon.h" #include "../common/document.h" #include "../common/properties.h" +#include "../common/menu.h" +#include "../common/toolbar.h" +#include "../common/threadpool.h" -#include <ucx/utils.h> +#include <cx/utils.h> +#include <cx/string.h> +#include <cx/printf.h> #include <pthread.h> -#ifndef UI_GTK2 -static GtkApplication *app; +#ifdef UI_APPLICATION +UI_APPLICATION app; #endif -static char *application_name; +static const char *application_name; static ui_callback startup_func; static void *startup_data; @@ -62,29 +66,32 @@ static int scale_factor = 1; -void ui_init(char *appname, int argc, char **argv) { +UIEXPORT void ui_init(const char *appname, int argc, char **argv) { + application_name = appname; uic_init_global_context(); +#if GTK_MAJOR_VERSION >= 4 + gtk_init(); +#else gtk_init(&argc, &argv); - application_name = appname; - - uic_docmgr_init(); - ui_toolbar_init(); +#endif - // init custom types - ui_list_init(); - + ui_css_init(); + uic_docmgr_init(); + uic_menu_init(); + uic_toolbar_init(); ui_image_init(); - uic_load_app_properties(); -#ifdef UI_SUPPORTS_SCALE +#if GTK_MAJOR_VERSION >= 4 + scale_factor = 1; // TODO +#elif defined(UI_SUPPORTS_SCALE) scale_factor = gdk_monitor_get_scale_factor( gdk_display_get_primary_monitor(gdk_display_get_default())); #endif } -char* ui_appname() { +const char* ui_appname() { return application_name; } @@ -117,14 +124,11 @@ #endif void ui_main() { -#ifndef UI_GTK2 - sstr_t appid = ucx_sprintf( +#ifdef UI_APPLICATION + cxmutstr appid = cx_asprintf( "ui.%s", application_name ? application_name : "application1"); - - app = gtk_application_new( - appid.ptr, - G_APPLICATION_FLAGS_NONE); + app = UI_APPLICATION_NEW(appid.ptr); g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL); g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL); g_application_run(G_APPLICATION (app), 0, NULL); @@ -149,17 +153,32 @@ } GtkApplication* ui_get_application() { - return app; + return GTK_APPLICATION(app); } #endif void ui_show(UiObject *obj) { + gboolean visible = gtk_widget_is_visible(obj->widget); + uic_check_group_widgets(obj->ctx); +#if GTK_MAJOR_VERSION >= 4 + gtk_window_present(GTK_WINDOW(obj->widget)); +#elif GTK_MAJOR_VERSION <= 3 gtk_widget_show_all(obj->widget); +#endif + + if(!visible) { + obj->ref++; + } } void ui_close(UiObject *obj) { + uic_context_prepare_close(obj->ctx); +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_window_close(GTK_WINDOW(obj->widget)); +#else gtk_widget_destroy(obj->widget); +#endif } @@ -181,12 +200,31 @@ static void* ui_jobthread(void *data) { UiJob *job = data; int result = job->job_func(job->job_data); - if(!result) { + if(!result && job->finish_callback) { g_idle_add(ui_job_finished, job); + } else { + free(job); } return NULL; } +static gboolean ui_idle_func(void *data) { + UiJob *job = data; + job->job_func(job->job_data); + free(job); + return FALSE; +} + +void ui_call_mainthread(ui_threadfunc tf, void* td) { + UiJob *job = malloc(sizeof(UiJob)); + job->job_func = tf; + job->job_data = td; + job->finish_callback = NULL; + job->finish_data = NULL; + job->obj = NULL; + g_idle_add(ui_idle_func, job); +} + void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { UiJob *job = malloc(sizeof(UiJob)); job->obj = obj; @@ -203,24 +241,38 @@ } void ui_set_show_all(UIWIDGET widget, int value) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 gtk_widget_set_no_show_all(widget, !value); +#endif } void ui_set_visible(UIWIDGET widget, int visible) { + // TODO: gtk4 +#if GTK_MAJOR_VERSION <= 3 if(visible) { gtk_widget_set_no_show_all(widget, FALSE); gtk_widget_show_all(widget); } else { gtk_widget_hide(widget); } +#endif } void ui_clipboard_set(char *str) { +#if GTK_MAJOR_VERSION >= 4 + // TODO: gtk4: needs widget +#else GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(cb, str, strlen(str)); +#endif } char* ui_clipboard_get() { +#if GTK_MAJOR_VERSION >= 4 + // TODO: gtk4: needs widget + return NULL; +#else GtkClipboard *cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); char *str = gtk_clipboard_wait_for_text(cb); if(str) { @@ -230,6 +282,7 @@ } else { return NULL; } +#endif } int ui_get_scalefactor() { @@ -241,15 +294,25 @@ } void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) { - ui_destroy_boundvar(data->obj->ctx, data->var); + if(data->var) { + ui_destroy_boundvar(data->obj->ctx, data->var); + } free(data); } +void ui_destroy_widget_var(GtkWidget *object, UiVar *var) { + ui_destroy_boundvar(NULL, var); +} + void ui_destroy_boundvar(UiContext *ctx, UiVar *var) { + uic_unbind_var(var); + if(var->type == UI_VAR_SPECIAL) { - free(var); + ui_free(var->from_ctx, var); } else { - uic_remove_bound_var(ctx, var); + ui_free(var->from_ctx, var); + // TODO: free or unbound + //uic_remove_bound_var(ctx, var); } } @@ -262,3 +325,160 @@ } +#if GTK_MAJOR_VERSION >= 3 + +static GtkCssProvider* ui_gtk_css_provider; + +#if GTK_MAJOR_VERSION == 4 +static const char *ui_gtk_css = +"#path-textfield-box {\n" +" background-color: alpha(currentColor, 0.1);" +" border-radius: 6px;" +" padding: 0px;" +"}\n" +".pathbar-extra-button {\n" +" border-top-right-radius: 6px;" +" border-bottom-right-radius: 6px;" +" border-top-left-radius: 0px;" +" border-bottom-left-radius: 0px;" +"}\n" +"#pathbar button {\n" +" margin: 3px;" +" border-radius: 4px;" +" padding-top: 0px;" +" padding-bottom: 0px;" +" padding-left: 8px;" +" padding-right: 8px;" +"}\n" +"#path-textfield-box entry {\n" +" background-color: #00000000;" +" border-top-left-radius: 6px;" +" border-bottom-left-radius: 6px;" +" border-top-right-radius: 0px;" +" border-bottom-right-radius: 0px;" +"}\n" +".pathbar-button-inactive {\n" +" color: alpha(currentColor, 0.5);" +"}\n" +".ui_test {\n" +" background-color: red;\n" +"}\n" +".ui_label_title {\n" +" font-weight: bold;\n" +"}\n" +".ui-listbox-header {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 12px;\n" +" margin-bottom: 10px;\n" +"}\n" +".ui-listbox-header-first {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 4px;\n" +" margin-bottom: 10px;\n" +"}\n" +; + +#elif GTK_MAJOR_VERSION == 3 +static const char *ui_gtk_css = +"#path-textfield-box {\n" +" background-color: @theme_base_color;\n" +" border-radius: 5px;\n" +" padding: 0px;\n" +"}\n" +".pathbar-button-inactive {\n" +" color: alpha(currentColor, 0.5);" +"}\n" +".ui_test {\n" +" background-color: red;\n" +"}\n" +".ui_label_title {\n" +" font-weight: bold;\n" +"}\n" +"placessidebar row {\n" +" padding-left: 10px;\n" +"}\n" +".ui-listbox-header {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 12px;\n" +" margin-bottom: 10px;\n" +"}\n" +".ui-listbox-header-first {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 4px;\n" +" margin-bottom: 10px;\n" +"}\n" +; +#endif + +void ui_css_init(void) { + ui_gtk_css_provider = gtk_css_provider_new(); + +#ifdef UI_GTK3 + gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1, NULL); + + GdkScreen *screen = gdk_screen_get_default(); + gtk_style_context_add_provider_for_screen( + screen, + GTK_STYLE_PROVIDER(ui_gtk_css_provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); +#endif /* UI_GTK3 */ + +#ifdef UI_GTK4 + + +#if GTK_MINOR_VERSION < 12 + gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1); +#else + gtk_css_provider_load_from_string(ui_gtk_css_provider, ui_gtk_css); +#endif /* GTK_MINOR_VERSION < 12 */ + + GdkDisplay *display = gdk_display_get_default(); + gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + +#endif /* UI_GTK4 */ +} + + + +#endif + +void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style_classes) { + if(name) { + gtk_widget_set_name(widget, name); + } + if(style_classes) { + cxstring *cls = NULL; + size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls); + for(int i=0;i<numClasses;i++) { + cxmutstr m = cx_strdup(cls[i]); +#if GTK_MAJOR_VERSION >= 4 + gtk_widget_add_css_class(widget, m.ptr); +#elif GTK_MAJOR_VERSION >= 3 + GtkStyleContext *ctx = gtk_widget_get_style_context(widget); + gtk_style_context_add_class(ctx, m.ptr); +#endif + free(m.ptr); + } + free(cls); + + } +} + +void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) { + if(!groups) { + return; + } + size_t ngroups = uic_group_array_size(groups); + ui_set_widget_ngroups(ctx, widget, groups, ngroups); +} + +void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); + ui_set_enabled(widget, FALSE); + } +}
--- a/ui/gtk/toolkit.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/toolkit.h Sat Jan 04 16:38:48 2025 +0100 @@ -39,49 +39,150 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#if GLIB_MAJOR_VERSION * 1000 + GLIB_MINOR_VERSION > 2074 +#define UI_G_APPLICATION_FLAGS G_APPLICATION_DEFAULT_FLAGS +#else +#define UI_G_APPLICATION_FLAGS G_APPLICATION_FLAGS_NONE +#endif + +#ifdef UI_LIBADWAITA +#define UI_APPLICATION AdwApplication* +#define UI_APPLICATION_NEW(id) adw_application_new(id, UI_G_APPLICATION_FLAGS) +#elif GTK_MAJOR_VERSION >= 3 +#define UI_APPLICATION GtkApplication* +#define UI_APPLICATION_NEW(id) gtk_application_new(id, UI_G_APPLICATION_FLAGS) +#endif + +#if GTK_MAJOR_VERSION >= 4 +#define WINDOW_SHOW(window) gtk_window_present(GTK_WINDOW(window)) +#define WINDOW_DESTROY(window) gtk_window_destroy(GTK_WINDOW(window)) +#define WINDOW_SET_CONTENT(window, child) gtk_window_set_child(GTK_WINDOW(window), child) +#define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child) +#define BOX_ADD_EXPAND(box, child) gtk_widget_set_hexpand(child, TRUE); gtk_widget_set_vexpand(child, TRUE); gtk_box_append(GTK_BOX(box), child) +#define BOX_ADD_NO_EXPAND(box, child) gtk_box_append(GTK_BOX(box), child) +#define BOX_REMOVE(box, child) gtk_box_remove(GTK_BOX(box), child) +#define ENTRY_SET_TEXT(entry, text) gtk_editable_set_text(GTK_EDITABLE(entry), text) +#define ENTRY_GET_TEXT(entry) gtk_editable_get_text(GTK_EDITABLE(entry)) +#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new() +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), child) +#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_scrolled_window_get_child(GTK_SCROLLED_WINDOW(sw)) +#define FRAME_SET_CHILD(frame, child) gtk_frame_set_child(GTK_FRAME(frame), child) +#define EXPANDER_SET_CHILD(expander, child) gtk_expander_set_child(GTK_EXPANDER(expander), child) +#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_widget_add_css_class(w, cssclass) +#define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_widget_remove_css_class(w, cssclass) +#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon) +#define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row) +#define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child) +#else +#define WINDOW_SHOW(window) gtk_widget_show_all(window) +#define WINDOW_DESTROY(window) gtk_widget_destroy(window) +#define WINDOW_SET_CONTENT(window, child) gtk_container_add(GTK_CONTAINER(window), child) +#define BOX_ADD(box, child) gtk_container_add(GTK_CONTAINER(box), child) +#define BOX_ADD_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, TRUE, 0) +#define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, FALSE, 0) +#define BOX_REMOVE(box, child) gtk_container_remove(GTK_CONTAINER(box), child) +#define ENTRY_SET_TEXT(entry, text) gtk_entry_set_text(GTK_ENTRY(entry), text) +#define ENTRY_GET_TEXT(entry) gtk_entry_get_text(GTK_ENTRY(entry)) +#define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new(NULL, NULL) +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_container_add(GTK_CONTAINER(sw), child) +#define SCROLLEDWINDOW_GET_CHILD(sw) gtk_bin_get_child(GTK_BIN(sw)) +#define FRAME_SET_CHILD(frame, child) gtk_container_add(GTK_CONTAINER(frame), child) +#define EXPANDER_SET_CHILD(expander, child) gtk_container_add(GTK_CONTAINER(expander), child) +#define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_style_context_add_class(gtk_widget_get_style_context(w), cssclass) +#define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_style_context_remove_class(gtk_widget_get_style_context(w), cssclass) +#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON) +#define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row) +#define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child) +#endif + +#ifdef UI_GTK2 +#undef SCROLLEDWINDOW_SET_CHILD +#define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), child) +#endif + +#if GTK_MAJOR_VERSION >= 4 +#define UI_GTK_SINCE_V4(st) st +#define UI_GTK_SINCE_V3(st) +#define UI_GTK_V2(st) +#define UI_GTK_V3(st) +#define UI_GTK_V4(st) st +#elif GTK_MAJOR_VERSION >= 3 +#define UI_GTK_SINCE_V4(st) st +#define UI_GTK_SINCE_V3(st) st +#define UI_GTK_V2(st) +#define UI_GTK_V3(st) st +#define UI_GTK_V4(st) +#else +#define UI_GTK_SINCE_V4(st) +#define UI_GTK_SINCE_V3(st) +#define UI_GTK_V2(st) st +#define UI_GTK_V3(st) +#define UI_GTK_V4(st) +#endif + + typedef struct UiEventData { UiObject *obj; ui_callback callback; void *userdata; int value; + void *customdata; } UiEventData; +typedef struct UiEventDataExt { + UiObject *obj; + ui_callback callback; + void *userdata; + ui_callback callback2; + void *userdata2; + int value0; + int value1; + int value2; + int value3; + void *customdata0; + void *customdata1; + void *customdata2; + void *customdata3; +} UiEventDataExt; + typedef struct UiVarEventData { - UiObject *obj; - UiVar *var; - UiObserver **observers; + UiObject *obj; + UiVar *var; + UiObserver **observers; + ui_callback callback; + void *userdata; } UiVarEventData; - -typedef struct UiJob { - UiObject *obj; - ui_threadfunc job_func; - void *job_data; - ui_callback finish_callback; - void *finish_data; -} UiJob; - +#ifndef UI_GTK4 struct UiSelection { GtkSelectionData *data; }; +#endif -typedef enum UiOrientation UiOrientation; -enum UiOrientation { UI_HORIZONTAL = 0, UI_VERTICAL }; - -#ifndef UI_GTK2 +#ifdef UI_APPLICATION void ui_app_quit(); GtkApplication* ui_get_application(); #endif int ui_get_scalefactor(); +void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style); +void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups); +void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups); + void ui_destroy_userdata(GtkWidget *object, void *userdata); void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data); +void ui_destroy_widget_var(GtkWidget *object, UiVar *var); void ui_destroy_boundvar(UiContext *ctx, UiVar *var); void ui_set_active_window(UiObject *obj); UiObject *ui_get_active_window(); +#if GTK_MAJOR_VERSION >= 3 +void ui_css_init(void); +#endif + #ifdef __cplusplus } #endif
--- a/ui/gtk/tree.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,562 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdarg.h> - -#include "../common/context.h" -#include "../common/object.h" -#include "container.h" - -#include "tree.h" - - -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; -} - - -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_listview(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - // create treeview - GtkWidget *view = gtk_tree_view_new(); - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); -#ifdef UI_GTK3 -#if GTK_MINOR_VERSION >= 8 - gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); -#else - // TODO: implement for older gtk3 -#endif -#else - // TODO: implement for gtk2 -#endif - - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = getvalue; - UiList *list = var->value; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - - UiListView *listview = malloc(sizeof(UiListView)); - listview->obj = obj; - listview->widget = view; - listview->var = var; - listview->model = model; - g_signal_connect( - view, - "destroy", - G_CALLBACK(ui_listview_destroy), - listview); - - // bind var - list->update = ui_listview_update; - list->obj = listview; - - // add callback - if(f) { - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); - event->obj = obj; - event->userdata = udata; - event->activate = f; - event->selection = NULL; - - g_signal_connect( - view, - "row-activated", - G_CALLBACK(ui_listview_activate_event), - event); - } - - // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_area), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scroll_area, TRUE); - - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - ct->current = view; - - return scroll_area; -} - -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_listview_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_listview_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - -static void drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer udata) { - printf("drag begin\n"); - -} - -static void drag_end( - GtkWidget *widget, - GdkDragContext *context, - guint time, - gpointer udata) -{ - printf("drag end\n"); - -} - -static GtkTargetEntry targetentries[] = - { - { "STRING", 0, 0 }, - { "text/plain", 0, 1 }, - { "text/uri-list", 0, 2 }, - }; - -UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) { - // create treeview - GtkWidget *view = gtk_tree_view_new(); - - int addi = 0; - for(int i=0;i<model->columns;i++) { - GtkTreeViewColumn *column = NULL; - if(model->types[i] == UI_ICON_TEXT) { - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, model->titles[i]); - - GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); - GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new(); - - gtk_tree_view_column_pack_end(column, textrenderer, TRUE); - gtk_tree_view_column_pack_start(column, iconrenderer, FALSE); - - - gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", i); - gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1); - - addi++; - } else { - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - model->titles[i], - renderer, - "text", - i + addi, - NULL); - } - gtk_tree_view_column_set_resizable(column, TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); - } - - //gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); -#ifdef UI_GTK3 - //gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(view), TRUE); -#else - -#endif - - UiList *list = var->value; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - - //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); - //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); - - // add TreeView as observer to the UiList to update the TreeView if the - // data changes - UiListView *tableview = malloc(sizeof(UiListView)); - tableview->obj = obj; - tableview->widget = view; - tableview->var = var; - tableview->model = model; - g_signal_connect( - view, - "destroy", - G_CALLBACK(ui_listview_destroy), - tableview); - - // bind var - list->update = ui_listview_update; - list->obj = tableview; - - // add callback - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); - event->obj = obj; - event->activate = cb.activate; - event->selection = cb.selection; - event->userdata = cb.userdata; - if(cb.activate) { - g_signal_connect( - view, - "row-activated", - G_CALLBACK(ui_listview_activate_event), - event); - } - if(cb.selection) { - GtkTreeSelection *selection = gtk_tree_view_get_selection( - GTK_TREE_VIEW(view)); - g_signal_connect( - selection, - "changed", - G_CALLBACK(ui_listview_selection_event), - event); - } - // TODO: destroy callback - - - GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); - - // add widget to the current container - GtkWidget *scroll_area = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy( - GTK_SCROLLED_WINDOW(scroll_area), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS - gtk_container_add(GTK_CONTAINER(scroll_area), view); - - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, scroll_area, TRUE); - - // ct->current should point to view, not scroll_area, to make it possible - // to add a context menu - ct->current = view; - - return scroll_area; -} - -UIWIDGET ui_table(UiObject *obj, UiList *list, UiModel *model, UiListCallbacks cb) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_table_var(obj, var, model, cb); -} - -UIWIDGET ui_table_nv(UiObject *obj, char *varname, UiModel *model, UiListCallbacks cb) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_table_var(obj, var, model, cb); - } else { - // TODO: error - } - return NULL; -} - -GtkWidget* ui_get_tree_widget(UIWIDGET widget) { - GList *c = gtk_container_get_children(GTK_CONTAINER(widget)); - if(c) { - return c->data; - } - return NULL; -} - -static char** targets2array(char *target0, va_list ap, int *nelm) { - int al = 16; - char **targets = calloc(16, sizeof(char*)); - targets[0] = target0; - - int i = 1; - char *target; - while((target = va_arg(ap, char*)) != NULL) { - if(i >= al) { - al *= 2; - targets = realloc(targets, al*sizeof(char*)); - } - targets[i] = target; - i++; - } - - *nelm = i; - return targets; -} - -static GtkTargetEntry* targetstr2gtktargets(char **str, int nelm) { - GtkTargetEntry *targets = calloc(nelm, sizeof(GtkTargetEntry)); - for(int i=0;i<nelm;i++) { - targets[i].target = str[i]; - } - return targets; -} - -void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...) { - va_list ap; - va_start(ap, target0); - int nelm; - char **targets = targets2array(target0, ap, &nelm); - va_end(ap); - ui_table_dragsource_a(tablewidget, actions, targets, nelm); - free(targets); -} - -void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) { - GtkTargetEntry* t = targetstr2gtktargets(targets, nelm); - gtk_tree_view_enable_model_drag_source( - GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)), - GDK_BUTTON1_MASK, - t, - nelm, - GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK); - free(t); -} - -void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...) { - va_list ap; - va_start(ap, target0); - int nelm; - char **targets = targets2array(target0, ap, &nelm); - va_end(ap); - ui_table_dragdest_a(tablewidget, actions, targets, nelm); - free(targets); -} - -void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm) { - GtkTargetEntry* t = targetstr2gtktargets(targets, nelm); - gtk_tree_view_enable_model_drag_dest( - GTK_TREE_VIEW(ui_get_tree_widget(tablewidget)), - t, - nelm, - GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK); - free(t); -} - -void ui_listview_update(UiList *list, int i) { - UiListView *view = list->obj; - UiListModel *model = ui_list_model_new(view->obj, view->var, view->model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(model)); - g_object_unref(G_OBJECT(model)); - // TODO: free old model -} - -void ui_listview_destroy(GtkWidget *w, UiListView *v) { - gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); - // TODO: destroy model? - free(v); -} - -void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - gtk_combo_box_set_model(GTK_COMBO_BOX(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); - // TODO: destroy model? - free(v); -} - - -void ui_listview_activate_event( - GtkTreeView *treeview, - GtkTreePath *path, - GtkTreeViewColumn *column, - UiTreeEventData *event) -{ - UiListSelection *selection = ui_listview_selection( - gtk_tree_view_get_selection(treeview), - event); - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->activate(&e, event->userdata); - - if(selection->count > 0) { - free(selection->rows); - } - free(selection); -} - -void ui_listview_selection_event( - GtkTreeSelection *treeselection, - UiTreeEventData *event) -{ - UiListSelection *selection = ui_listview_selection(treeselection, event); - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->selection(&e, event->userdata); - - if(selection->count > 0) { - free(selection->rows); - } - free(selection); -} - -UiListSelection* ui_listview_selection( - GtkTreeSelection *selection, - UiTreeEventData *event) -{ - GList *rows = gtk_tree_selection_get_selected_rows(selection, NULL); - - UiListSelection *ls = malloc(sizeof(UiListSelection)); - ls->count = g_list_length(rows); - ls->rows = calloc(ls->count, sizeof(int)); - GList *r = rows; - int i = 0; - while(r) { - GtkTreePath *path = r->data; - ls->rows[i] = ui_tree_path_list_index(path); - r = r->next; - i++; - } - return ls; -} - -int ui_tree_path_list_index(GtkTreePath *path) { - int depth = gtk_tree_path_get_depth(path); - if(depth == 0) { - fprintf(stderr, "UiError: treeview selection: depth == 0\n"); - return -1; - } - int *indices = gtk_tree_path_get_indices(path); - return indices[depth - 1]; -} - - -/* --------------------------- ComboBox --------------------------- */ - -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_combobox(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_combobox_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - return ui_combobox_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = getvalue; - UiListModel *listmodel = ui_list_model_new(obj, var, model); - - GtkWidget *combobox = ui_create_combobox(obj, listmodel, f, udata); - UiContainer *ct = uic_get_current_container(obj); - ct->add(ct, combobox, FALSE); - return combobox; -} - -GtkWidget* ui_create_combobox(UiObject *obj, UiListModel *model, ui_callback f, void *udata) { - GtkWidget *combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); - - UiListView *uicbox = malloc(sizeof(UiListView)); - uicbox->obj = obj; - uicbox->widget = combobox; - uicbox->var = model->var; - uicbox->model = model->model; - - g_signal_connect( - combobox, - "destroy", - G_CALLBACK(ui_combobox_destroy), - uicbox); - - // bind var - UiList *list = model->var->value; - list->update = ui_combobox_modelupdate; - list->obj = uicbox; - - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); - gtk_cell_layout_set_attributes( - GTK_CELL_LAYOUT(combobox), - renderer, - "text", - 0, - NULL); - gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); - - // add callback - if(f) { - UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); - event->obj = obj; - event->userdata = udata; - event->callback = f; - event->value = 0; - - g_signal_connect( - combobox, - "changed", - G_CALLBACK(ui_combobox_change_event), - event); - } - - return combobox; -} - -void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { - UiEvent event; - event.obj = e->obj; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - event.eventdata = NULL; - event.intval = gtk_combo_box_get_active(widget); - e->callback(&event, e->userdata); -} - -void ui_combobox_modelupdate(UiList *list, int i) { - UiListView *view = list->obj; - UiListModel *model = ui_list_model_new(view->obj, view->var, view->model); - gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(model)); -} -
--- a/ui/gtk/tree.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TREE_H -#define TREE_H - -#include "../ui/tree.h" -#include "toolkit.h" -#include "model.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UiListView { - UiObject *obj; - GtkWidget *widget; - UiVar *var; - UiModel *model; -} UiListView; - -typedef struct UiTreeEventData { - UiObject *obj; - ui_callback activate; - ui_callback selection; - void *userdata; -} UiTreeEventData; - -void* ui_strmodel_getvalue(void *elm, int column); - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb); - -GtkWidget* ui_get_tree_widget(UIWIDGET widget); - -void ui_listview_update(UiList *list, int i); -void ui_combobox_destroy(GtkWidget *w, UiListView *v); -void ui_listview_destroy(GtkWidget *w, UiListView *v); - -void ui_listview_activate_event( - GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - UiTreeEventData *event); -void ui_listview_selection_event( - GtkTreeSelection *treeselection, - UiTreeEventData *event); -UiListSelection* ui_listview_selection( - GtkTreeSelection *selection, - UiTreeEventData *event); -int ui_tree_path_list_index(GtkTreePath *path); - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -GtkWidget* ui_create_combobox(UiObject *obj, UiListModel *model, ui_callback f, void *udata); -void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); -void ui_combobox_modelupdate(UiList *list, int i); - -#ifdef __cplusplus -} -#endif - -#endif /* TREE_H */ -
--- a/ui/gtk/window.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/gtk/window.c Sat Jan 04 16:38:48 2025 +0100 @@ -33,29 +33,25 @@ #include "../ui/window.h" #include "../ui/properties.h" #include "../common/context.h" +#include "../common/menu.h" +#include "../common/toolbar.h" + +#include <cx/mempool.h> #include "menu.h" #include "toolbar.h" #include "container.h" +#include "headerbar.h" +#include "button.h" static int nwindows = 0; static int window_default_width = 650; static int window_default_height = 550; -void ui_exit_event(GtkWidget *widget, gpointer data) { +static gboolean ui_window_destroy(void *data) { UiObject *obj = data; - UiEvent ev; - ev.window = obj->window; - ev.document = obj->ctx->document; - ev.obj = obj; - ev.eventdata = NULL; - ev.intval = 0; - - if(obj->ctx->close_callback) { - obj->ctx->close_callback(&ev, obj->ctx->close_data); - } - // TODO: free UiObject + uic_object_destroy(obj); nwindows--; #ifdef UI_GTK2 @@ -63,13 +59,56 @@ gtk_main_quit(); } #endif + + return FALSE; +} + +void ui_window_widget_destroy(UiObject *obj) { +#if GTK_MAJOR_VERSION >= 4 + gtk_window_destroy(GTK_WINDOW(obj->widget)); +#else + gtk_widget_destroy(obj->widget); +#endif +} + +void ui_exit_event(GtkWidget *widget, gpointer data) { + // delay exit handler + g_idle_add(ui_window_destroy, data); } -static UiObject* create_window(char *title, void *window_data, UiBool simple) { - UcxMempool *mp = ucx_mempool_new(256); - UiObject *obj = ucx_mempool_calloc(mp, 1, sizeof(UiObject)); - -#ifndef UI_GTK2 +static gboolean ui_window_close_request(UiObject *obj) { + uic_context_prepare_close(obj->ctx); + obj->ref--; + if(obj->ref > 0) { +#if GTK_CHECK_VERSION(2, 18, 0) + gtk_widget_set_visible(obj->widget, FALSE); +#else + gtk_widget_hide(obj->widget); +#endif + return TRUE; + } else { + return FALSE; + } +} + +#if GTK_MAJOR_VERSION >= 4 +static gboolean close_request(GtkWindow* self, UiObject *obj) { + return ui_window_close_request(obj); +} +#else +static gboolean close_request(GtkWidget* self, GdkEvent* event, UiObject *obj) { + return ui_window_close_request(obj); +} +#endif + +static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) { + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + obj->ref = 0; + +#ifdef UI_LIBADWAITA + obj->widget = adw_application_window_new(ui_get_application()); +#elif !defined(UI_GTK2) obj->widget = gtk_application_window_new(ui_get_application()); #else obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -79,12 +118,16 @@ obj->ctx = uic_context(obj, mp); obj->window = window_data; +#if GTK_CHECK_VERSION(4, 0, 0) + obj->ctx->action_map = G_ACTION_MAP(obj->widget); +#endif + if(title != NULL) { gtk_window_set_title(GTK_WINDOW(obj->widget), title); } - char *width = ui_get_property("ui.window.width"); - char *height = ui_get_property("ui.window.height"); + const char *width = ui_get_property("ui.window.width"); + const char *height = ui_get_property("ui.window.height"); if(width && height) { gtk_window_set_default_size( GTK_WINDOW(obj->widget), @@ -93,35 +136,117 @@ } else { gtk_window_set_default_size( GTK_WINDOW(obj->widget), - window_default_width, + window_default_width + sidebar*250, window_default_height); } + obj->destroy = ui_window_widget_destroy; g_signal_connect( obj->widget, "destroy", G_CALLBACK(ui_exit_event), obj); +#if GTK_MAJOR_VERSION >= 4 + g_signal_connect( + obj->widget, + "close-request", + G_CALLBACK(close_request), + obj); +#else + g_signal_connect( + obj->widget, + "delete-event", + G_CALLBACK(close_request), + obj); +#endif GtkWidget *vbox = ui_gtk_vbox_new(0); - gtk_container_add(GTK_CONTAINER(obj->widget), vbox); +#ifdef UI_LIBADWAITA + GtkWidget *toolbar_view = adw_toolbar_view_new(); + adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox); + + GtkWidget *content_box = ui_gtk_vbox_new(0); + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + + if(sidebar) { + GtkWidget *splitview = adw_overlay_split_view_new(); + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview); + + GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new(); + adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view); + GtkWidget *sidebar_headerbar = adw_header_bar_new(); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), sidebar_headerbar); + + adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), toolbar_view); + + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view); + } else { + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view); + } + + + GtkWidget *headerbar = adw_header_bar_new(); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar); + g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar); if(!simple) { + ui_fill_headerbar(obj, headerbar); + } +#elif GTK_MAJOR_VERSION >= 4 + GtkWidget *content_box = ui_gtk_vbox_new(0); + WINDOW_SET_CONTENT(obj->widget, vbox); + if(sidebar) { + GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); + gtk_paned_set_start_child(GTK_PANED(paned), sidebar_vbox); + gtk_paned_set_end_child(GTK_PANED(paned), content_box); + BOX_ADD_EXPAND(GTK_BOX(vbox), paned); + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); + } else { + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + } +#else + if(!simple) { // menu - GtkWidget *mb = ui_create_menubar(obj); - if(mb) { - gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, FALSE, 0); + if(uic_get_menu_list()) { + GtkWidget *mb = ui_create_menubar(obj); + if(mb) { + gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, FALSE, 0); + } } // toolbar - GtkWidget *tb = ui_create_toolbar(obj); - if(tb) { - gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0); + if(uic_toolbar_isenabled()) { + GtkWidget *tb = ui_create_toolbar(obj); + if(tb) { + gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0); + } } + + //GtkWidget *hb = ui_create_headerbar(obj); + //gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb); } + GtkWidget *content_box = ui_gtk_vbox_new(0); + WINDOW_SET_CONTENT(obj->widget, vbox); + if(sidebar) { + GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); + gtk_paned_add1(GTK_PANED(paned), sidebar_vbox); + gtk_paned_add2(GTK_PANED(paned), content_box); + BOX_ADD_EXPAND(GTK_BOX(vbox), paned); + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); + gtk_paned_set_position (GTK_PANED(paned), 200); + } else { + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + } + +#endif + // window content // the content has a (TODO: not yet) configurable frame + // TODO: really? why + /* GtkWidget *frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); @@ -130,58 +255,637 @@ GtkWidget *content_box = ui_gtk_vbox_new(0); gtk_container_add(GTK_CONTAINER(frame), content_box); obj->container = ui_box_container(obj, content_box); + */ + obj->container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX); nwindows++; return obj; } -UiObject* ui_window(char *title, void *window_data) { - return create_window(title, window_data, FALSE); +UiObject* ui_window(const char *title, void *window_data) { + return create_window(title, window_data, FALSE, FALSE); +} + +UiObject *ui_sidebar_window(const char *title, void *window_data) { + return create_window(title, window_data, TRUE, FALSE); +} + +UiObject* ui_simple_window(const char *title, void *window_data) { + return create_window(title, window_data, FALSE, TRUE); +} + +void ui_window_size(UiObject *obj, int width, int height) { + gtk_window_set_default_size( + GTK_WINDOW(obj->widget), + width, + height); +} + +#ifdef UI_LIBADWAITA + +static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData *data) { + UiEvent evt; + evt.obj = data->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.eventdata = NULL; + evt.intval = 0; + + if(!strcmp(response, "btn1")) { + evt.intval = 1; + } else if(!strcmp(response, "btn2")) { + evt.intval = 2; + } + + if(data->customdata) { + GtkWidget *entry = data->customdata; + evt.eventdata = (void*)ENTRY_GET_TEXT(GTK_ENTRY(entry)); + } + + if(data->callback) { + data->callback(&evt, data->userdata); + } +} + +void ui_dialog_create(UiObject *parent, UiDialogArgs args) { + AdwDialog *dialog = adw_alert_dialog_new(args.title, args.content); + UiEventData *event = malloc(sizeof(UiEventData)); + event->callback = args.result; + event->userdata = args.resultdata; + event->customdata = NULL; + event->value = 0; + event->obj = parent; + + if(args.button1_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn1", args.button1_label); + } + if(args.button2_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "btn2", args.button2_label); + } + if(args.closebutton_label) { + adw_alert_dialog_add_response(ADW_ALERT_DIALOG(dialog), "close", args.closebutton_label); + adw_alert_dialog_set_close_response(ADW_ALERT_DIALOG(dialog), "close"); + } + + GtkWidget *entry = NULL; + if(args.input || args.password) { + entry = gtk_entry_new(); + if(args.password) { + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + } + if(args.input_value) { + ENTRY_SET_TEXT(entry, args.input_value); + } + adw_alert_dialog_set_extra_child(ADW_ALERT_DIALOG(dialog), entry); + event->customdata = entry; + } + + g_signal_connect( + dialog, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + g_signal_connect(dialog, "response", G_CALLBACK(dialog_response), event); + adw_dialog_present(dialog, parent->widget); + + if(entry) { + gtk_entry_grab_focus_without_selecting(GTK_ENTRY(entry)); + } +} +#else + +static void ui_dialog_response (GtkDialog* self, gint response_id, gpointer user_data) { + UiEventData *data = user_data; + UiEvent evt; + evt.obj = data->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.eventdata = NULL; + evt.intval = 0; + + if(data->customdata) { + GtkWidget *entry = data->customdata; + evt.eventdata = (void*)ENTRY_GET_TEXT(GTK_ENTRY(entry)); + + } + + if(response_id == 1 || response_id == 2) { + evt.intval = response_id; + } + + + if(data->callback) { + data->callback(&evt, data->userdata); + } + + WINDOW_DESTROY(GTK_WIDGET(self)); } -UiObject* ui_simplewindow(char *title, void *window_data) { - return create_window(title, window_data, TRUE); +void ui_dialog_create(UiObject *parent, UiDialogArgs args) { + GtkDialog *dialog = GTK_DIALOG(gtk_dialog_new()); + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + + GtkWidget *dialog_w = GTK_WIDGET(dialog); + if(args.title) { + gtk_window_set_title(GTK_WINDOW(dialog), args.title); + } + if(args.button1_label) { + gtk_dialog_add_button(dialog, args.button1_label, 1); + } + if(args.button2_label) { + gtk_dialog_add_button(dialog, args.button2_label, 2); + } + if(args.closebutton_label) { + gtk_dialog_add_button(dialog, args.closebutton_label, 0); + } + + GtkWidget *content_area = gtk_dialog_get_content_area(dialog); + if(args.content) { + GtkWidget *label = gtk_label_new(args.content); + BOX_ADD(content_area, label); + } + + GtkWidget *textfield = NULL; + if(args.input || args.password) { + textfield = gtk_entry_new(); + if(args.password) { + gtk_entry_set_visibility(GTK_ENTRY(textfield), FALSE); + } + if(args.input_value) { + ENTRY_SET_TEXT(textfield, args.input_value); + } + BOX_ADD(content_area, textfield); + } + + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = parent; + event->callback = args.result; + event->userdata = args.resultdata; + event->value = 0; + event->customdata = textfield; + + g_signal_connect(dialog_w, + "response", + G_CALLBACK(ui_dialog_response), + event); + + WINDOW_SHOW(GTK_WIDGET(dialog_w)); +} +#endif + + +#if GTK_MAJOR_VERSION >= 3 +UiFileList listmodel2filelist(GListModel *selection) { + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + flist.nfiles = g_list_model_get_n_items(selection); + flist.files = calloc(flist.nfiles, sizeof(char*)); + for(int i=0;i<flist.nfiles;i++) { + GFile *file = g_list_model_get_item(selection, i); + char *path = g_file_get_path(file); + flist.files[i] = path ? strdup(path) : NULL; + g_object_unref(file); + } + return flist; +} +#endif + + +#if GTK_CHECK_VERSION(4, 10, 0) + +#define UI_GTK_FILEDIALOG_OPEN 16 +#define UI_GTK_FILEDIALOG_SAVE 32 + +static void filechooser_opened(GObject *source, GAsyncResult *result, void *data) { + UiEventData *event = data; + + GFile *file = NULL; + GListModel *selection = NULL; + GError *error = NULL; + + int mode = event->value; + int multi = mode & UI_FILEDIALOG_SELECT_MULTI; + if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) { + if(multi) { + selection = gtk_file_dialog_select_multiple_folders_finish(GTK_FILE_DIALOG(source), result, &error); + } else { + file = gtk_file_dialog_select_folder_finish(GTK_FILE_DIALOG(source), result, &error); + } + } else if((mode & UI_GTK_FILEDIALOG_OPEN) == UI_GTK_FILEDIALOG_OPEN) { + if(multi) { + selection = gtk_file_dialog_open_multiple_finish(GTK_FILE_DIALOG(source), result, &error); + } else { + file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(source), result, &error); + } + } else { + file = gtk_file_dialog_save_finish(GTK_FILE_DIALOG(source), result, &error); + } + + UiEvent evt; + evt.obj = event->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.intval = 0; + + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + evt.eventdata = &flist; + + if(selection) { + flist = listmodel2filelist(selection); + g_object_unref(selection); + } else if(file) { + char *path = g_file_get_path(file); + if(path) { + flist.nfiles = 1; + flist.files = calloc(flist.nfiles, sizeof(char*)); + flist.files[0] = strdup(path); + } + g_object_unref(file); + } + + if(event->callback) { + event->callback(&evt, event->userdata); + } + + for(int i=0;i<flist.nfiles;i++) { + free(flist.files[i]); + } } -static char* ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action) { +static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, const char *name, ui_callback file_selected_callback, void *cbdata) { + if(action == GTK_FILE_CHOOSER_ACTION_OPEN) { + mode |= UI_GTK_FILEDIALOG_OPEN; + } else { + mode |= UI_GTK_FILEDIALOG_SAVE; + } + + UiEventData *event = malloc(sizeof(UiEventData)); + event->callback = file_selected_callback; + event->userdata = cbdata; + event->customdata = NULL; + event->value = mode; + event->obj = obj; + + GtkWindow *parent = GTK_WINDOW(gtk_widget_get_root(obj->widget)); + GtkFileDialog *dialog = gtk_file_dialog_new(); + if(name) { + gtk_file_dialog_set_initial_name(dialog, name); + } + + int multi = mode & UI_FILEDIALOG_SELECT_MULTI; + if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) { + if(multi) { + gtk_file_dialog_select_multiple_folders(dialog, parent, NULL, filechooser_opened, event); + } else { + gtk_file_dialog_select_folder(dialog, parent, NULL, filechooser_opened, event); + } + } else if(action == GTK_FILE_CHOOSER_ACTION_OPEN) { + if(multi) { + gtk_file_dialog_open_multiple(dialog, parent, NULL, filechooser_opened, event); + } else { + gtk_file_dialog_open(dialog, parent, NULL, filechooser_opened, event); + } + } else { + gtk_file_dialog_save(dialog, parent, NULL, filechooser_opened, event); + } + + g_object_unref(dialog); +} +#else + + + +static void filechooser_response(GtkDialog* self, gint response_id, UiEventData *data) { + UiEvent evt; + evt.obj = data->obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.intval = 0; + + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + evt.eventdata = &flist; + + if(response_id == GTK_RESPONSE_ACCEPT) { +#if GTK_CHECK_VERSION(4, 0, 0) + GListModel *selection = gtk_file_chooser_get_files(GTK_FILE_CHOOSER(self)); + flist = flist = listmodel2filelist(selection); + g_object_unref(selection); +#else + GSList *selection = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(self)); + flist.nfiles = g_slist_length(selection); + flist.files = calloc(flist.nfiles, sizeof(char*)); + int i = 0; + while(selection) { + char *file = selection->data; + flist.files[i] = strdup(file); + g_free(file); + selection = selection->next; + i++; + } + g_slist_free(selection); +#endif + } + + + if(data->callback) { + data->callback(&evt, data->userdata); + } + + for(int i=0;i<flist.nfiles;i++) { + free(flist.files[i]); + } + + WINDOW_DESTROY(GTK_WIDGET(self)); +} + +static void ui_gtkfilechooser(UiObject *obj, GtkFileChooserAction action, unsigned int mode, const char *name, ui_callback file_selected_callback, void *cbdata) { char *button; char *title; - if(action == GTK_FILE_CHOOSER_ACTION_OPEN) { - button = GTK_STOCK_OPEN; - title = "Datei öffnen..."; + GtkWidget *dialog; + if((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) { + dialog = gtk_file_chooser_dialog_new ( + "Open Folder", + GTK_WINDOW(obj->widget), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "Cancel", + GTK_RESPONSE_CANCEL, + "Select Folder", + GTK_RESPONSE_ACCEPT, + NULL); + } else if(action == GTK_FILE_CHOOSER_ACTION_OPEN) { + dialog = gtk_file_chooser_dialog_new ( + "Select Folder", + GTK_WINDOW(obj->widget), + action, + "Cancel", + GTK_RESPONSE_CANCEL, + "Open File", + GTK_RESPONSE_ACCEPT, + NULL); } else { - button = GTK_STOCK_SAVE; - title = "Datei speichern..."; + dialog = gtk_file_chooser_dialog_new ( + "Save File", + GTK_WINDOW(obj->widget), + action, + "Cancel", + GTK_RESPONSE_CANCEL, + "Save File", + GTK_RESPONSE_ACCEPT, + NULL); + } + + if((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) { + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); } - GtkWidget *dialog = gtk_file_chooser_dialog_new( - title, - GTK_WINDOW(obj->widget), - action, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - button, - GTK_RESPONSE_ACCEPT, - NULL); - if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { - char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - gtk_widget_destroy(dialog); - char *copy = strdup(file); - g_free(file); - return copy; - } else { - gtk_widget_destroy(dialog); - return NULL; + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = cbdata; + event->callback = file_selected_callback; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + dialog, + "response", + G_CALLBACK(filechooser_response), + event); + g_signal_connect( + dialog, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + + UiEvent evt; + evt.obj = obj; + evt.document = evt.obj->ctx->document; + evt.window = evt.obj->window; + evt.intval = 0; + + UiFileList flist; + flist.files = NULL; + flist.nfiles = 0; + evt.eventdata = &flist; + + gtk_widget_show(dialog); +} +#endif + +void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN, mode, NULL, file_selected_callback, cbdata); +} + +void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) { + ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_SAVE, 0, name, file_selected_callback, cbdata); +} + +#if GTK_CHECK_VERSION(4, 10, 0) +#define DIALOG_NEW() gtk_window_new() +#else +#define DIALOG_NEW() gtk_dialog_new() + +static void ui_dialogwindow_response(GtkDialog* self, gint response_id, gpointer user_data) { + UiEventData *event = user_data; + // TODO: do we need to check if response_id == GTK_RESPONSE_DELETE_EVENT? + if(event->callback) { + UiEvent e; + e.obj = event->obj; + e.window = event->obj->window; + e.document = event->obj->ctx->document; + e.eventdata = NULL; + e.intval = event->value; + event->callback(&e, event->userdata); } } -char* ui_openfiledialog(UiObject *obj) { - return ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_OPEN); -} +#endif + +#if GTK_CHECK_VERSION(4, 0, 0) +#define HEADERBAR_SHOW_CLOSEBUTTON(headerbar, set) gtk_header_bar_set_show_title_buttons(GTK_HEADER_BAR(headerbar), set) +#define DEFAULT_BUTTON(window, button) gtk_window_set_default_widget(GTK_WINDOW(window), button) +#else +#define HEADERBAR_SHOW_CLOSEBUTTON(headerbar, set) gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(headerbar), set) +#define DEFAULT_BUTTON(window, button) gtk_widget_set_can_default(button, TRUE); gtk_window_set_default(GTK_WINDOW(window), button) +#endif + + + +UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { + GtkWidget *dialog = DIALOG_NEW(); + if(args.width > 0 || args.height > 0) { + gtk_window_set_default_size( + GTK_WINDOW(dialog), + args.width, + args.height); + } + + + gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent->widget)); + if(args.modal != UI_OFF) { + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + } + + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + obj->ctx = uic_context(obj, mp); + obj->widget = dialog; + obj->ref = 0; + obj->destroy = ui_window_widget_destroy; + nwindows++; + + if(args.title != NULL) { + gtk_window_set_title(GTK_WINDOW(dialog), args.title); + } + +#if ! GTK_CHECK_VERSION(4, 10, 0) + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = args.onclickdata; + event->callback = args.onclick; + event->value = 0; + event->customdata = NULL; -char* ui_savefiledialog(UiObject *obj) { - return ui_gtkfilechooser(obj, GTK_FILE_CHOOSER_ACTION_SAVE); + g_signal_connect(dialog, "response", G_CALLBACK(ui_dialogwindow_response), event); + g_signal_connect( + dialog, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); +#endif + + g_signal_connect( + dialog, + "destroy", + G_CALLBACK(ui_exit_event), + obj); +#if GTK_MAJOR_VERSION >= 4 + g_signal_connect( + obj->widget, + "close-request", + G_CALLBACK(close_request), + obj); +#else + g_signal_connect( + obj->widget, + "delete-event", + G_CALLBACK(close_request), + obj); +#endif + +#if GTK_MAJOR_VERSION < 4 + GtkWidget *c = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_remove(GTK_CONTAINER(dialog), c); +#endif + + GtkWidget *content_vbox = ui_gtk_vbox_new(0); + obj->container = ui_box_container(obj, content_vbox, UI_CONTAINER_VBOX); + if(args.lbutton1 || args.lbutton2 || args.rbutton3 || args.rbutton4) { +#if GTK_CHECK_VERSION(3, 10, 0) + if(args.titlebar_buttons != UI_OFF) { + GtkWidget *headerbar = gtk_header_bar_new(); + gtk_window_set_titlebar(GTK_WINDOW(dialog), headerbar); + if(args.show_closebutton == UI_OFF) { + HEADERBAR_SHOW_CLOSEBUTTON(headerbar, FALSE); + } + + if(args.lbutton1) { + GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 1) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + if(args.lbutton2) { + GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 2) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + + if(args.rbutton4) { + GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 4) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + if(args.rbutton3) { + GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); + if(args.default_button == 3) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + WINDOW_SET_CONTENT(obj->widget, content_vbox); + return obj; + } +#endif + GtkWidget *vbox = ui_gtk_vbox_new(0); + WINDOW_SET_CONTENT(obj->widget, vbox); + + GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + + GtkWidget *grid = ui_create_grid_widget(10, 10); + GtkWidget *widget = ui_box_set_margin(grid, 16); + gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); + + if(args.lbutton1) { + GtkWidget *button = ui_create_button(obj, args.lbutton1, NULL, args.onclick, args.onclickdata, 1, args.default_button == 1); + gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1); + if(args.default_button == 1) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + if(args.lbutton2) { + GtkWidget *button = ui_create_button(obj, args.lbutton2, NULL, args.onclick, args.onclickdata, 2, args.default_button == 2); + gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1); + if(args.default_button == 2) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + GtkWidget *space = gtk_label_new(NULL); + gtk_widget_set_hexpand(space, TRUE); + gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1); + if(args.rbutton3) { + GtkWidget *button = ui_create_button(obj, args.rbutton3, NULL, args.onclick, args.onclickdata, 3, args.default_button == 3); + gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1); + if(args.default_button == 3) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + if(args.rbutton4) { + GtkWidget *button = ui_create_button(obj, args.rbutton4, NULL, args.onclick, args.onclickdata, 4, args.default_button == 4); + gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1); + if(args.default_button == 4) { + WIDGET_ADD_CSS_CLASS(button, "suggested-action"); + DEFAULT_BUTTON(dialog, button); + } + } + + BOX_ADD_EXPAND(vbox, content_vbox); + BOX_ADD_NO_EXPAND(vbox, separator); + BOX_ADD_NO_EXPAND(vbox, widget); + } else { + WINDOW_SET_CONTENT(obj->widget, content_vbox); + } + + return obj; } -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/Grid.c Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,636 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "Grid.h" + +#include <X11/Xlib.h> + + + +static XtActionsRec actionslist[] = { + {"getfocus",grid_getfocus}, + {"loosefocus",grid_loosefocus}, + {"NULL",NULL} +}; + +//static char defaultTranslations[] = "<BtnDown>: mousedown()\n"; +static char defaultTranslations[] = "\ +<EnterWindow>: getfocus()\n\ +<LeaveWindow>: loosefocus()\n"; + +static XtResource resources[] = +{ + { + gridColumnSpacing, + gridColumnSpacing, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + mywidget.columnspacing), + XmRImmediate, + (XtPointer) 0 + }, + { + gridRowSpacing, + gridRowSpacing, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + mywidget.rowspacing), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMargin, + gridMargin, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridRec, + mywidget.margin), + XmRImmediate, + (XtPointer) 0 + } +}; + +///* +static XtResource constraints[] = +{ + { + gridColumn, + gridColumn, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.x), + XmRImmediate, + (XtPointer) 0 + }, + { + gridRow, + gridRow, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.y), + XmRImmediate, + (XtPointer) 0 + }, + { + gridColspan, + gridColspan, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.colspan), + XmRImmediate, + (XtPointer) 0 + }, + { + gridRowspan, + gridRowspan, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.rowspan), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginLeft, + gridMarginLeft, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_left), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginRight, + gridMarginRight, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_right), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginTop, + gridMarginTop, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_top), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginBottom, + gridMarginBottom, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_bottom), + XmRImmediate, + (XtPointer) 0 + }, + { + gridHExpand, + gridHExpand, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.hexpand), + XmRImmediate, + (XtPointer) 0 + }, + { + gridVExpand, + gridVExpand, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.vexpand), + XmRImmediate, + (XtPointer) 0 + }, + { + gridHFill, + gridHFill, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.hfill), + XmRImmediate, + (XtPointer) 0 + }, + { + gridVFill, + gridVFill, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.vfill), + XmRImmediate, + (XtPointer) 0 + } + +}; +//*/ + +GridClassRec gridClassRec = { + // Core Class + { + //(WidgetClass)&constraintClassRec, // superclass + (WidgetClass)&xmManagerClassRec, + "Grid", // class_name + sizeof(GridRec), // widget_size + grid_class_initialize, // class_initialize + NULL, // class_part_initialize + FALSE, // class_inited + (XtInitProc)grid_initialize, // initialize + NULL, // initialize_hook + grid_realize, // realize + actionslist, // actions + XtNumber(actionslist), // num_actions + resources, // resources + XtNumber(resources), // num_resources + NULLQUARK, // xrm_class + True, // compress_motion + True, // compress_exposure + True, // compress_enterleave + False, // visible_interest + (XtWidgetProc)grid_destroy, // destroy + (XtWidgetProc)grid_resize, // resize + (XtExposeProc)grid_expose, // expose + grid_set_values, // set_values + NULL, // set_values_hook + XtInheritSetValuesAlmost, // set_values_almost + NULL, // get_values_hook + (XtAcceptFocusProc)grid_acceptfocus, // accept_focus + XtVersion, // version + NULL, // callback_offsets + //NULL, // tm_table + defaultTranslations, + XtInheritQueryGeometry, // query_geometry + NULL, // display_accelerator + NULL, // extension + }, + // Composite Class + { + GridGeometryManager, /* geometry_manager */ + GridChangeManaged, /* change_managed */ + XtInheritInsertChild, /* insert_child */ + XtInheritDeleteChild, /* delete_child */ + NULL, /* extension */ + }, + // Constraint Class + { + constraints, /* resources */ + XtNumber(constraints), /* num_resources */ + sizeof(GridConstraintRec), /* constraint_size */ + grid_constraint_init, /* initialize */ + NULL, /* destroy */ + ConstraintSetValues, /* set_values */ + NULL, /* extension */ + }, + // XmManager Class + ///* + { + XtInheritTranslations, + NULL, + 0, + NULL, + 0, + XmInheritParentProcess, + NULL + }, + //*/ + // MyWidget Class + { + 0 + } +}; + +WidgetClass gridClass = (WidgetClass)&gridClassRec; + + +void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) { + +} +void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) { + MyWidget mn = (MyWidget)new; + + mn->mywidget.max_col = 0; + mn->mywidget.max_row = 0; + +} +void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) { + XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL); + (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); + grid_place_children(w); +} + + +void grid_destroy(MyWidget widget) { + +} +void grid_resize(MyWidget widget) { + grid_place_children(widget); +} + +void grid_expose(MyWidget widget, XEvent *event, Region region) { + +} + + +Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { + return False; +} + +Boolean grid_acceptfocus(Widget w, Time *t) { + +} + +void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { + +} + +void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { + +} + + + +XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { + GridRec *grid = (GridRec*)XtParent(widget); + GridConstraintRec *constraints = widget->core.constraints; + //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL); + if((request->request_mode & CWWidth) == CWWidth) { + widget->core.width = request->width; + constraints->grid.pref_width = request->width; + } + if((request->request_mode & CWHeight) == CWHeight) { + widget->core.height = request->height; + constraints->grid.pref_height = request->height; + } + grid_place_children((MyWidget)XtParent(widget)); + return XtGeometryYes; +} + +void GridChangeManaged(Widget widget) { + +} + +Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { + GridConstraintRec *constraints = neww->core.constraints; + MyWidget grid = (MyWidget)XtParent(neww); + if(constraints->grid.x > grid->mywidget.max_col) { + grid->mywidget.max_col = constraints->grid.x; + } + if(constraints->grid.y > grid->mywidget.max_row) { + grid->mywidget.max_row = constraints->grid.y; + } +} + + +void grid_constraint_init( + Widget request, + Widget neww, + ArgList args, + Cardinal* num_args +) +{ + GridConstraintRec *constraints = neww->core.constraints; + + MyWidget grid = (MyWidget)XtParent(neww); + if(constraints->grid.x > grid->mywidget.max_col) { + grid->mywidget.max_col = constraints->grid.x; + } + if(constraints->grid.y > grid->mywidget.max_row) { + grid->mywidget.max_row = constraints->grid.y; + } + constraints->grid.pref_width = neww->core.width; + constraints->grid.pref_height = neww->core.height; +} + +void grid_place_children(MyWidget w) { + int ncols = w->mywidget.max_col+1; + int nrows = w->mywidget.max_row+1; + GridDef *cols = calloc(ncols, sizeof(GridDef)); + GridDef *rows = calloc(nrows, sizeof(GridDef)); + int num_cols_expanding = 0; + int num_rows_expanding = 0; + int req_width = 0; + int req_height = 0; + + //printf("container width: %d\n", (int)w->core.width); + + // calculate the minimum size requirements for all columns and rows + // we need to run this 2 times: for widgets without colspan/rowspan first + // and then again for colspan/rowspan > 1 + int span_max = 1; + for(int r=0;r<2;r++) { + for(int i=0;i<w->composite.num_children;i++) { + Widget child = w->composite.children[i]; + GridConstraintRec *constraints = child->core.constraints; + if(constraints->grid.pref_width == 0) { + constraints->grid.pref_width = child->core.width; + } + if(constraints->grid.pref_height == 0) { + constraints->grid.pref_height = child->core.height; + } + + if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) { + continue; + } + + int x = constraints->grid.x; + int y = constraints->grid.y; + // make sure ncols/nrows is correct + // errors shouldn't happen, unless someone messes up the grid internals + if(x >= ncols) { + fprintf(stderr, "Error: widget x out of bounds\n"); + continue; + } + if(y >= nrows) { + fprintf(stderr, "Error: widget y out of bounds\n"); + continue; + } + GridDef *col = &cols[x]; + GridDef *row = &rows[y]; + + if(constraints->grid.hexpand) { + if(constraints->grid.colspan > 1) { + // check if any column in the span is expanding + // if not, make the last column expanding + GridDef *last_col = col; + for(int c=x;c<ncols;c++) { + last_col = &cols[c]; + if(last_col->expand) { + break; + } + } + last_col->expand = TRUE; + } else { + col->expand = TRUE; + } + } + if(constraints->grid.vexpand) { + if(constraints->grid.rowspan > 1) { + GridDef *last_row = row; + for(int c=x;c<nrows;c++) { + last_row = &rows[c]; + if(last_row->expand) { + break; + } + } + last_row->expand = TRUE; + } else { + row->expand = TRUE; + } + } + + // column size + if(constraints->grid.colspan > 1) { + // check size of all columns in span + Dimension span_width = col->size; + GridDef *last_col = col; + for(int s=x+1;s<ncols;s++) { + last_col = &cols[s]; + span_width = last_col->size; + + } + int diff = constraints->grid.pref_width - span_width; + if(diff > 0) { + last_col->size += diff; + } + } else if(constraints->grid.pref_width > col->size) { + col->size = constraints->grid.pref_width; + } + // row size + if(constraints->grid.rowspan > 1) { + Dimension span_height = row->size; + GridDef *last_row = row; + for(int s=x+1;s<nrows;s++) { + last_row = &rows[s]; + span_height = last_row->size; + + } + int diff = constraints->grid.pref_height - span_height; + if(diff > 0) { + last_row->size += diff; + } + } else if(constraints->grid.pref_height > row->size) { + row->size = constraints->grid.pref_height; + } + } + span_max = 50000; // not sure if this is unreasonable low or high + } + + // calc required size + for(int i=0;i<ncols;i++) { + if(cols[i].expand) { + num_cols_expanding++; + } + req_width += cols[i].size; + } + for(int i=0;i<nrows;i++) { + if(rows[i].expand) { + num_rows_expanding++; + } + req_height += rows[i].size; + } + + if(req_width > 0 && req_height > 0) { + // add col/row spacing + req_width += (ncols-1)*w->mywidget.columnspacing; + req_height += (nrows-1)*w->mywidget.rowspacing; + + Widget parent = w->core.parent; + Dimension rwidth = req_width; + Dimension rheight = req_height; + if(rwidth < w->core.width) { + //rwidth = w->core.width; + } + if(rheight < w->core.height) { + //rheight = w->core.height; + } + + if(!w->mywidget.sizerequest) { + Dimension actual_width, actual_height; + w->mywidget.sizerequest = TRUE; + + //XtWidgetGeometry request; + //request.width = req_width; + //request.request_mode = CWWidth; + //XtWidgetGeometry reply; + //XtGeometryResult result = XtMakeGeometryRequest((Widget)w, &request, &reply); + + XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height); + w->mywidget.sizerequest = FALSE; + //printf("size request: %d %d\n", (int)actual_width, (int)actual_height); + } + + + + } + + // how much space can we add to each expanding col/row + int hexpand = 0; + int width_diff = (int)w->core.width - req_width; + int hexpand2 = 0; + if(width_diff > 0 && num_cols_expanding > 0) { + hexpand = width_diff / num_cols_expanding; + hexpand2 = width_diff-hexpand*num_cols_expanding; + } + int x = 0; + for(int i=0;i<ncols;i++) { + cols[i].pos = x; + if(cols[i].expand) { + cols[i].size += hexpand + hexpand2; + } + x += cols[i].size + w->mywidget.columnspacing; + + hexpand2 = 0; + } + + int vexpand = 0; + int height_diff = (int)w->core.height - req_height; + int vexpand2 = 0; + if(height_diff > 0 && num_rows_expanding > 0) { + vexpand = height_diff / num_rows_expanding; + vexpand2 = height_diff-vexpand*num_rows_expanding; + } + int y = 0; + for(int i=0;i<nrows;i++) { + rows[i].pos = y; + if(rows[i].expand) { + rows[i].size += vexpand + vexpand2; + } + y += rows[i].size += w->mywidget.rowspacing; + + vexpand2 = 0; + } + + for(int i=0;i<w->composite.num_children;i++) { + Widget child = w->composite.children[i]; + GridConstraintRec *constraints = child->core.constraints; + GridDef c = cols[constraints->grid.x]; + GridDef r = rows[constraints->grid.y]; + int x = c.pos; + int y = r.pos; + int width = constraints->grid.pref_width; + int height = constraints->grid.pref_height; + if(constraints->grid.hfill) { + if(constraints->grid.colspan > 1) { + Dimension cwidth = 0; + for(int j=0;j<constraints->grid.colspan;j++) { + if(constraints->grid.x+j < ncols) { + cwidth += cols[constraints->grid.x+j].size + (j > 0 ? w->mywidget.columnspacing : 0); + } + } + width = cwidth; + } else { + width = c.size - w->mywidget.columnspacing; + } + } + if(constraints->grid.vfill) { + if(constraints->grid.rowspan > 1) { + Dimension cheight = 0; + for(int j=0;j<constraints->grid.rowspan;j++) { + if(constraints->grid.y+j < nrows) { + cheight += rows[constraints->grid.y+j].size + (j > 0 ? w->mywidget.rowspacing : 0); + } + } + height = cheight; + } else { + height = r.size - w->mywidget.rowspacing; + } + } + + if(width > 0 && height > 0) { + XtConfigureWidget(child, x, y, width, height, child->core.border_width); + } + //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height); + } + + free(cols); + free(rows); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/Grid.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GRID_H +#define GRID_H + +#include <X11/Intrinsic.h> +#include <X11/IntrinsicP.h> +#include <Xm/XmAll.h> +#include <Xm/Primitive.h> +#include <Xm/PrimitiveP.h> +#include <Xm/ManagerP.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// resources +#define gridColumnSpacing "gridColumnSpacing" +#define gridRowSpacing "gridRowSpacing" +#define gridMargin "gridMargin" + +// constraints +#define gridColumn "gridColumn" +#define gridRow "gridRow" +#define gridColspan "gridColspan" +#define gridRowspan "gridRowspan" +#define gridHExpand "gridHExpand" +#define gridVExpand "gridVExpand" +#define gridHFill "gridHFill" +#define gridVFill "gridVFill" +#define gridMarginLeft "gridMarginLeft" +#define gridMarginRight "gridMarginRight" +#define gridMarginTop "gridMarginTop" +#define gridMarginBottom "gridMarginBottom" + + +typedef struct GridDef { + Dimension size; + Dimension pos; + Boolean expand; +} GridDef; + +typedef struct GridClassPart { + int test; +} GridClassPart; + +typedef struct GridClassRec { + CoreClassPart core_class; + CompositeClassPart composite_class; + ConstraintClassPart constraint_class; + XmManagerClassPart manager_class; + GridClassPart mywidgetclass; +} GridClassRec; + + +typedef struct GridPart { + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int max_col; + int max_row; + Dimension columnspacing; + Dimension rowspacing; + Dimension margin; + + Boolean sizerequest; +} GridPart; + +typedef struct GridRec { + CorePart core; + CompositePart composite; + ConstraintPart constraint; + XmManagerPart manager; + GridPart mywidget; +} GridRec; + +typedef struct GridContraintPart { + Dimension x; + Dimension y; + Dimension margin_left; + Dimension margin_right; + Dimension margin_top; + Dimension margin_bottom; + Boolean hexpand; + Boolean vexpand; + Boolean hfill; + Boolean vfill; + Dimension colspan; + Dimension rowspan; + Dimension pref_width; + Dimension pref_height; +} GridContraintPart; + +typedef struct GridConstraintRec { + XmManagerConstraintPart manager; + GridContraintPart grid; +} GridConstraintRec; + +typedef GridRec* MyWidget; + +extern WidgetClass gridClass; + +void grid_class_initialize(); +void grid_initialize(); +void grid_realize(); +void grid_destroy(); +void grid_resize(); +void grid_expose(); +Boolean grid_set_values(); +Boolean grid_acceptfocus(Widget , Time*); + +void grid_place_children(MyWidget w); + +void grid_getfocus(); +void grid_loosefocus(); + +void grid_constraint_init( + Widget request, + Widget neww, + ArgList args, + Cardinal* num_args +); + +XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply); +void GridChangeManaged(Widget widget); +Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args); + + +#ifdef __cplusplus +} +#endif + +#endif /* GRID_H */ +
--- a/ui/motif/button.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/button.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -32,71 +32,60 @@ #include "button.h" #include "container.h" #include "../common/context.h" -#include <ucx/mempool.h> +#include <cx/mempool.h> + +#include <cx/linked_list.h> +#include <cx/array_list.h> +#include <cx/compare.h> + +#include <Xm/XmAll.h> -UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); - +UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { + Arg xargs[16]; int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNlabelString, str); - n++; + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget button = XmCreatePushButton(parent, "button", args, n); - ct->add(ct, button); + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } - if(f) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = data; - event->callback = f; - event->value = 0; + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreatePushButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + if(args.onclick) { + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = args.onclick; + eventdata->userdata = args.onclickdata; + eventdata->obj = obj; + eventdata->value = 0; XtAddCallback( button, XmNactivateCallback, (XtCallbackProc)ui_push_button_callback, - event); + eventdata); + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + eventdata); } - XtManageChild(button); + XmStringFree(label); return button; } -// wrapper -int64_t ui_toggle_button_get(UiInteger *i) { - int state = 0; - XtVaGetValues(i->obj, XmNset, &state, NULL); - i->value = state; - return state; -} - -void ui_toggle_button_set(UiInteger *i, int64_t value) { - Arg arg; - XtSetArg(arg, XmNset, value); - XtSetValues(i->obj, &arg, 1); - i->value = value; -} - -void ui_toggle_button_callback( - Widget widget, - UiEventData *event, - XmToggleButtonCallbackStruct *tb) -{ - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - // TODO: e.document - e.intval = tb->set; - event->callback(&e, event->userdata); -} - void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) { UiEvent e; e.obj = event->obj; @@ -106,102 +95,344 @@ event->callback(&e, event->userdata); } +UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + XtSetArg(xargs[n], XmNfillOnSelect, True); n++; + XtSetArg(xargs[n], XmNindicatorOn, False); n++; + + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "togglebutton"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); + + XmStringFree(label); + return button; +} -static void radio_callback( - Widget widget, - RadioEventData *event, - XmToggleButtonCallbackStruct *tb) -{ - if(tb->set) { - RadioButtonGroup *group = event->group; - if(group->current) { - Arg arg; - XtSetArg(arg, XmNset, FALSE); - XtSetValues(group->current, &arg, 1); +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); + + XmStringFree(label); + return button; +} + +UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { + return ui_checkbox_create(obj, args); +} + +static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { + if(event->value > 0) { + // button in configured to enable/disable states + if(tb->set) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); } - group->current = widget; + } + + UiEvent e; + e.obj = event->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = XmToggleButtonGetState(w); + + if(event->callback) { + event->callback(&e, event->userdata); + } + + if(event->var && event->var->value) { + UiInteger *v = event->var->value; + v->value = e.intval; + ui_notify_evt(v->observers, &e); } } -UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); - - int n = 0; - Arg args[16]; - - XtSetArg(args[n], XmNlabelString, str); - n++; - XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); - n++; - - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget button = XmCreateToggleButton(parent, "radiobutton", args, n); - ct->add(ct, button); - - if(rgroup) { - RadioButtonGroup *group; - if(rgroup->obj) { - group = rgroup->obj; - group->buttons = ucx_list_append(group->buttons, button); - group->ref++; - } else { - group = malloc(sizeof(RadioButtonGroup)); - group->buttons = ucx_list_append(NULL, button); - group->current = button; - // this is the first button in the radiobutton group - // so we should enable it - Arg arg; - XtSetArg(arg, XmNset, TRUE); - XtSetValues(button, &arg, 1); - rgroup->obj = group; - - group->current = button; +void ui_bind_togglebutton( + UiObject *obj, + Widget widget, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state) +{ + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER); + if(var) { + value = (UiInteger*)var->value; + value->obj = widget; + value->get = ui_togglebutton_get; + value->set = ui_togglebutton_set; + + if(value->value) { + XmToggleButtonSetState(widget, True, False); } - - RadioEventData *event = malloc(sizeof(RadioEventData)); - event->obj = obj; - event->callback = NULL; - event->userdata = NULL; - event->group = group; - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)radio_callback, - event); - - rgroup->get = ui_radiobutton_get; - rgroup->set = ui_radiobutton_set; } - XtManageChild(button); - return button; + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = onchange; + event->userdata = onchangedata; + event->var = var; + event->observers = NULL; + event->value = enable_state; + XtAddCallback( + widget, + XmNvalueChangedCallback, + (XtCallbackProc)togglebutton_changed, + event); + XtAddCallback( + widget, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + event); +} + +int64_t ui_togglebutton_get(UiInteger *i) { + Widget togglebutton = i->obj; + Boolean state = XmToggleButtonGetState(togglebutton); + i->value = state; + return state; +} + +void ui_togglebutton_set(UiInteger *i, int64_t value) { + Widget togglebutton = i->obj; + i->value = value; + XmToggleButtonSetState(togglebutton, (Boolean)value, False); +} + +static void destroy_list(Widget w, CxList *list, XtPointer d) { + cxListDestroy(list); } -int64_t ui_radiobutton_get(UiInteger *value) { - RadioButtonGroup *group = value->obj; +static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { + if(event->value > 0) { + // button in configured to enable/disable states + if(tb->set) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); + } + } + + if(!tb->set) { + return; // only handle set-events + } - int i = ucx_list_find(group->buttons, group->current, NULL, NULL); - if (i >= 0) { - value->value = i; - return i; - } else { - return 0; + UiInteger *value = NULL; + int64_t v = 0; + if(event->var) { + value = event->var->value; + // find widget index and update all radiobuttons + // the UiInteger value must always be up-to-date + CxList *list = value->obj; + CxIterator i = cxListIterator(list); + cx_foreach(Widget, button, i) { + Boolean state = False; + if(button == w) { + value->value = i.index+1; // update value + state = True; + } + XmToggleButtonSetState(button, state, False); + } + v = value->value; + } + + UiEvent e; + e.obj = event->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = value; + e.intval = v; + + if(event->callback) { + event->callback(&e, event->userdata); + } + + if(value) { + ui_notify_evt(value->observers, &e); } } -void ui_radiobutton_set(UiInteger *value, int64_t i) { - RadioButtonGroup *group = value->obj; - Arg arg; +void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const char *varname, ui_callback onchange, void *onchangedata, int enable_group) { + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER); + if(var) { + UiInteger *value = var->value; + CxList *rb = value->obj; + if(!rb) { + // first button in the radiobutton group + // create a list for all buttons and use the list as value obj + rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + value->obj = rb; + value->get = ui_radiobutton_get; + value->set = ui_radiobutton_set; + + // the first radio button is also responsible for cleanup + XtAddCallback( + rbutton, + XmNdestroyCallback, + (XtCallbackProc)destroy_list, + rb); + } + cxListAdd(rb, rbutton); + + // set the radiobutton state, if the value is already set + if(cxListSize(rb) == value->value) { + XmToggleButtonSetState(rbutton, True, False); + } + } - XtSetArg(arg, XmNset, FALSE); - XtSetValues(group->current, &arg, 1); + // the radio button needs to handle change events to update all + // other buttons in the radio button group + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = onchange; + event->userdata = onchangedata; + event->observers = NULL; + event->var = var; + event->value = enable_group; + XtAddCallback( + rbutton, + XmNvalueChangedCallback, + (XtCallbackProc)radiobutton_changed, + event); + XtAddCallback( + rbutton, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + event); +} + +UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++; + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); - UcxList *elm = ucx_list_get(group->buttons, i); - if(elm) { - Widget button = elm->data; - XtSetArg(arg, XmNset, TRUE); - XtSetValues(button, &arg, 1); - group->current = button; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER); + if(var) { + UiInteger *value = var->value; + CxList *rb = value->obj; + if(!rb) { + // first button in the radiobutton group + // create a list for all buttons and use the list as value obj + rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + value->obj = rb; + value->get = ui_radiobutton_get; + value->set = ui_radiobutton_set; + + // the first radio button is also responsible for cleanup + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)destroy_list, + rb); + } + cxListAdd(rb, button); + + // set the radiobutton state, if the value is already set + if(cxListSize(rb) == value->value) { + XmToggleButtonSetState(button, True, False); + } + } + + // the radio button needs to handle change events to update all + // other buttons in the radio button group + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = args.onchange; + event->userdata = args.onchangedata; + event->observers = NULL; + event->var = var; + event->value = args.enable_group; + XtAddCallback( + button, + XmNvalueChangedCallback, + (XtCallbackProc)radiobutton_changed, + event); + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + event); + + XmStringFree(label); + return button; + + +} + +int64_t ui_radiobutton_get(UiInteger *i) { + // the UiInteger should be updated automatically by change events + return i->value; +} + +void ui_radiobutton_set(UiInteger *i, int64_t value) { + CxList *list = i->obj; + if(i->value > 0) { + Widget current = cxListAt(list, i->value-1); + if(current) { + XmToggleButtonSetState(current, False, False); + } + } + if(value > 0 && value <= cxListSize(list)) { + Widget button = cxListAt(list, value-1); + if(button) { + XmToggleButtonSetState(button, True, False); + i->value = value; + } } }
--- a/ui/motif/button.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/button.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,30 +36,24 @@ extern "C" { #endif -typedef struct { - UcxList *buttons; - Widget current; - int ref; -} RadioButtonGroup; - -typedef struct { - UiObject *obj; - ui_callback callback; - void *userdata; - RadioButtonGroup *group; -} RadioEventData; - -// wrapper -int64_t ui_toggle_button_get(UiInteger *i); -void ui_toggle_button_set(UiInteger *i, int64_t value); -void ui_toggle_button_callback( - Widget widget, - UiEventData *data, - XmToggleButtonCallbackStruct *e); void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d); -int64_t ui_radiobutton_get(UiInteger *value); -void ui_radiobutton_set(UiInteger *value, int64_t i); +void ui_bind_togglebutton( + UiObject *obj, + Widget widget, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state); + +int64_t ui_togglebutton_get(UiInteger *i); +void ui_togglebutton_set(UiInteger *i, int64_t value); + +void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const char *varname, ui_callback onchange, void *onchangedata, int enable_group); + +int64_t ui_radiobutton_get(UiInteger *i); +void ui_radiobutton_set(UiInteger *i, int64_t value); #ifdef __cplusplus }
--- a/ui/motif/container.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/container.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,742 +34,190 @@ #include "../common/context.h" #include "../common/object.h" -#define UI_GRID_MAX_COLUMNS 512 - -static UiBool ui_lb2bool(UiLayoutBool b) { - return b == UI_LAYOUT_TRUE ? TRUE : FALSE; -} - -static UiLayoutBool ui_bool2lb(UiBool b) { - return b ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE; -} - +#include "Grid.h" -UiContainer* ui_frame_container(UiObject *obj, Widget frame) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiContainer)); - ct->widget = frame; - ct->prepare = ui_frame_container_prepare; - ct->add = ui_frame_container_add; - return ct; -} - -Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - return ct->widget; -} - -void ui_frame_container_add(UiContainer *ct, Widget widget) { - ui_reset_layout(ct->layout); - ct->current = widget; -} - +/* ---------------------------- Box Container ---------------------------- */ -UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation) { - UiBoxContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiBoxContainer)); - ct->container.widget = box; - ct->container.prepare = ui_box_container_prepare; - ct->container.add = ui_box_container_add; - ct->orientation = orientation; - ct->margin = margin; - ct->spacing = spacing; - return (UiContainer*)ct; -} - -Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - UiBoxContainer *bc = (UiBoxContainer*)ct; - if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { - fill = ui_lb2bool(ct->layout.fill); - } +static UIWIDGET box_create(UiObject *obj, UiContainerArgs args, UiBoxOrientation orientation) { + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - if(bc->has_fill && fill) { - fprintf(stderr, "UiError: container has 2 filled widgets"); - fill = FALSE; - } - if(fill) { - bc->has_fill = TRUE; - } + Arg xargs[16]; + int n = 0; - int a = *n; - // determine fixed and dynamic attachments - void *f1; - void *f2; - void *d1; - void *d2; - void *w1; - void *w2; - if(bc->orientation == UI_BOX_VERTICAL) { - f1 = XmNleftAttachment; - f2 = XmNrightAttachment; - d1 = XmNtopAttachment; - d2 = XmNbottomAttachment; - w1 = XmNtopWidget; - w2 = XmNbottomWidget; - - // margin/spacing - XtSetArg(args[a], XmNleftOffset, bc->margin); a++; - XtSetArg(args[a], XmNrightOffset, bc->margin); a++; - - XtSetArg(args[a], XmNtopOffset, bc->prev_widget ? bc->spacing : bc->margin); a++; + if(orientation == UI_BOX_VERTICAL) { + //XtSetArg(xargs[n], gridRowSpacing, args.spacing); n++; } else { - f1 = XmNtopAttachment; - f2 = XmNbottomAttachment; - d1 = XmNleftAttachment; - d2 = XmNrightAttachment; - w1 = XmNleftWidget; - w2 = XmNrightWidget; - - // margin/spacing - XtSetArg(args[a], XmNtopOffset, bc->margin); a++; - XtSetArg(args[a], XmNbottomOffset, bc->margin); a++; - - XtSetArg(args[a], XmNleftOffset, bc->prev_widget ? bc->spacing : bc->margin); a++; - } - XtSetArg(args[a], f1, XmATTACH_FORM); a++; - XtSetArg(args[a], f2, XmATTACH_FORM); a++; - - if(fill) { - XtSetArg(args[a], d2, XmATTACH_FORM); a++; - } - if(bc->prev_widget) { - XtSetArg(args[a], d1, XmATTACH_WIDGET); a++; - XtSetArg(args[a], w1, bc->prev_widget); a++; - } else { - XtSetArg(args[a], d1, XmATTACH_FORM); a++; - } - - *n = a; - return ct->widget; -} - -void ui_box_container_add(UiContainer *ct, Widget widget) { - UiBoxContainer *bc = (UiBoxContainer*)ct; - // determine dynamic attachments - void *d1; - void *d2; - void *w1; - void *w2; - if(bc->orientation == UI_BOX_VERTICAL) { - d1 = XmNtopAttachment; - d2 = XmNbottomAttachment; - w1 = XmNtopWidget; - w2 = XmNbottomWidget; - - } else { - d1 = XmNleftAttachment; - d2 = XmNrightAttachment; - w1 = XmNleftWidget; - w2 = XmNrightWidget; - } - - if(bc->prev_widget) { - int v = 0; - XtVaGetValues(bc->prev_widget, d2, &v, NULL); - if(v == XmATTACH_FORM) { - XtVaSetValues( - bc->prev_widget, - d2, - XmATTACH_WIDGET, - w2, - widget, - NULL); - XtVaSetValues( - widget, - d1, - XmATTACH_NONE, - d2, - XmATTACH_FORM, - NULL); - } - } - bc->prev_widget = widget; - - ui_reset_layout(ct->layout); - ct->current = widget; -} - -UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing) { - UiGridContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiGridContainer)); - ct->container.widget = form; - ct->container.prepare = ui_grid_container_prepare; - ct->container.add = ui_grid_container_add; - ct->columnspacing = columnspacing; - ct->rowspacing = rowspacing; - return (UiContainer*)ct; -} - -void ui_grid_newline(UiGridContainer *grid) { - if(grid->current) { - grid->current = NULL; - } - grid->container.layout.newline = FALSE; -} - -Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - UiGridContainer *grid = (UiGridContainer*)ct; - if(ct->layout.newline) { - ui_grid_newline(grid); - } - return ct->widget; -} - -void ui_grid_container_add(UiContainer *ct, Widget widget) { - UiGridContainer *grid = (UiGridContainer*)ct; - - if(grid->current) { - grid->current = ucx_list_append(grid->current, widget); - } else { - grid->current = ucx_list_append(grid->current, widget); - grid->lines = ucx_list_append(grid->lines, grid->current); + //XtSetArg(xargs[n], gridColumnSpacing, args.spacing); n++; } - ui_reset_layout(ct->layout); - ct->current = widget; -} - -static void ui_grid_resize(Widget widget, XtPointer udata, XtPointer cdata) { - UiGridContainer *grid = udata; - - UcxList *rowdim = NULL; - int coldim[UI_GRID_MAX_COLUMNS]; - memset(coldim, 0, UI_GRID_MAX_COLUMNS*sizeof(int)); - int numcol = 0; - - // get the minimum size of the columns and rows - int sumw = 0; - int sumh = 0; - UCX_FOREACH(row, grid->lines) { - int rheight = 0; - int i=0; - int sum_width = 0; - UCX_FOREACH(elm, row->data) { - Widget w = elm->data; - int widget_width = 0; - int widget_height = 0; - XtVaGetValues( - w, - XmNwidth, - &widget_width, - XmNheight, - &widget_height, - NULL); - - // get the maximum height in this row - if(widget_height > rheight) { - rheight = widget_height; - } - - // get the maximum width in this column - if(widget_width > coldim[i]) { - coldim[i] = widget_width; - } - sum_width += widget_width; - if(sum_width > sumw) { - sumw = sum_width; - } - - i++; - if(i > numcol) { - numcol = i; - } - } - rowdim = ucx_list_append(rowdim, (void*)(intptr_t)rheight); - sumh += rheight; - } - - // check container size - int gwidth = 0; - int gheight = 0; - XtVaGetValues(widget, XmNwidth, &gwidth, XmNheight, &gheight, NULL); - if(gwidth < sumw || gheight < sumh) { - XtVaSetValues(widget, XmNwidth, sumw, XmNheight, sumh, NULL); - ucx_list_free(rowdim); - return; - } - - - // adjust the positions of all children - int y = 0; - UCX_FOREACH(row, grid->lines) { - int x = 0; - int i=0; - UCX_FOREACH(elm, row->data) { - Widget w = elm->data; - XtVaSetValues( - w, - XmNx, x, - XmNy, y, - XmNwidth, coldim[i], - XmNheight, rowdim->data, - NULL); - - x += coldim[i]; - i++; - } - y += (intptr_t)rowdim->data; - rowdim = rowdim->next; - } + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget grid = XtCreateManagedWidget(args.name ? args.name : "boxcontainer", gridClass, parent, xargs, n); + ctn->add(ctn, grid); - ucx_list_free(rowdim); -} - -UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiContainer)); - ct->widget = scrolledwindow; - ct->prepare = ui_scrolledwindow_container_prepare; - ct->add = ui_scrolledwindow_container_add; - return ct; -} - -Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - return ct->widget; -} - -void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget) { - ui_reset_layout(ct->layout); - ct->current = widget; -} - - -UiContainer* ui_tabview_container(UiObject *obj, Widget frame) { - UiTabViewContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, - 1, - sizeof(UiTabViewContainer)); - ct->context = obj->ctx; - ct->container.widget = frame; - ct->container.prepare = ui_tabview_container_prepare; - ct->container.add = ui_tabview_container_add; - return (UiContainer*)ct; -} - -Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - int a = *n; - XtSetArg(args[a], XmNleftAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNrightAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNtopAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNbottomAttachment, XmATTACH_FORM); a++; - *n = a; - return ct->widget; -} - -void ui_tabview_container_add(UiContainer *ct, Widget widget) { - UiTabViewContainer *tabview = (UiTabViewContainer*)ct; - - if(tabview->current) { - XtUnmanageChild(tabview->current); - } - - tabview->current = widget; - tabview->tabs = ucx_list_append(tabview->tabs, widget); - - ui_select_tab(ct->widget, 0); - ui_reset_layout(ct->layout); - ct->current = widget; -} - -UIWIDGET ui_box(UiObject *obj, int margin, int spacing, UiBoxOrientation orientation) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget form = XmCreateForm(parent, "vbox", args, n); - ct->add(ct, form); - XtManageChild(form); - - UiObject *newobj = uic_object_new(obj, form); - newobj->container = ui_box_container(obj, form, margin, spacing, orientation); - uic_obj_add(obj, newobj); - - return form; -} - -UIWIDGET ui_vbox(UiObject *obj) { - return ui_box(obj, 0, 0, UI_BOX_VERTICAL); -} - -UIWIDGET ui_hbox(UiObject *obj) { - return ui_box(obj, 0, 0, UI_BOX_HORIZONTAL); -} - -UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) { - return ui_box(obj, margin, spacing, UI_BOX_VERTICAL); -} - -UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) { - return ui_box(obj, margin, spacing, UI_BOX_HORIZONTAL); -} - -UIWIDGET ui_grid(UiObject *obj) { - return ui_grid_sp(obj, 0, 0, 0); -} - -UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget grid = XmCreateDrawingArea(parent, "grid", args, n); - ct->add(ct, grid); - XtManageChild(grid); - - UiObject *newobj = uic_object_new(obj, grid); - newobj->container = ui_grid_container(obj, grid, columnspacing, rowspacing); - uic_obj_add(obj, newobj); - - XtAddCallback (grid, XmNresizeCallback , ui_grid_resize, newobj->container); + UiContainerX *container = ui_box_container(obj, grid, orientation); + uic_object_push_container(obj, container); return grid; } -UIWIDGET ui_scrolledwindow(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); // TODO: dosn't work, use XmAPPLICATION_DEFINED - n++; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", args, n); - ct->add(ct, scrolledwindow); - XtManageChild(scrolledwindow); - - UiObject *newobj = uic_object_new(obj, scrolledwindow); - newobj->container = ui_scrolledwindow_container(obj, scrolledwindow); - uic_obj_add(obj, newobj); - - return scrolledwindow; +// public +UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { + return box_create(obj, args, UI_BOX_VERTICAL); +} + +// public +UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { + return box_create(obj, args, UI_BOX_HORIZONTAL); } -UIWIDGET ui_sidebar(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - - XtSetArg(args[n], XmNorientation, XmHORIZONTAL); - n++; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget pane = XmCreatePanedWindow(parent, "pane", args, n); - ct->add(ct, pane); - XtManageChild(pane); - - // add sidebar widget - Widget sidebar = XmCreateForm(pane, "sidebar", args, 0); - XtManageChild(sidebar); - - UiObject *left = uic_object_new(obj, sidebar); - left->container = ui_box_container(left, sidebar, 0, 0, UI_BOX_VERTICAL); - - // add content widget - XtSetArg (args[0], XmNpaneMaximum, 8000); - Widget content = XmCreateForm(pane, "content_area", args, 1); - XtManageChild(content); - - UiObject *right = uic_object_new(obj, content); - right->container = ui_box_container(right, content, 0, 0, UI_BOX_VERTICAL); - - uic_obj_add(obj, right); - uic_obj_add(obj, left); - - return sidebar; +UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation) { + UiBoxContainer *ctn = ui_malloc(obj->ctx, sizeof(UiBoxContainer)); + memset(ctn, 0, sizeof(UiBoxContainer)); + ctn->container.prepare = orientation == UI_BOX_VERTICAL ? ui_vbox_prepare : ui_hbox_prepare; + ctn->container.add = ui_box_container_add; + ctn->container.widget = grid; + ctn->n = 0; + return (UiContainerX*)ctn; } -UIWIDGET ui_tabview(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - // create a simple frame as container widget - // when tabs are selected, the current child will be replaced by the - // the new tab widget - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); - n++; - XtSetArg(args[n], XmNshadowThickness, 0); - n++; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget form = XmCreateForm(parent, "tabview", args, n); - ct->add(ct, form); - XtManageChild(form); - - UiObject *tabviewobj = uic_object_new(obj, form); - tabviewobj->container = ui_tabview_container(obj, form); - uic_obj_add(obj, tabviewobj); - - XtVaSetValues(form, XmNuserData, tabviewobj->container, NULL); - - return form; +static Widget ui_box_container_prepare(UiBoxContainer *box, Arg *args, int *n) { + int a = *n; + box->n++; + return box->container.widget; } -void ui_tab(UiObject *obj, char *title) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.label = title; - - ui_vbox(obj); -} - -void ui_select_tab(UIWIDGET tabview, int tab) { - UiTabViewContainer *ct = NULL; - XtVaGetValues(tabview, XmNuserData, &ct, NULL); - if(ct) { - XtUnmanageChild(ct->current); - UcxList *elm = ucx_list_get(ct->tabs, tab); - if(elm) { - XtManageChild(elm->data); - ct->current = elm->data; - } else { - fprintf(stderr, "UiError: front tab index: %d\n", tab); - } - } else { - fprintf(stderr, "UiError: widget is not a tabview\n"); +Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiBoxContainer *box = (UiBoxContainer*)ctn; + int a = *n; + XtSetArg(args[a], gridRow, box->n); a++; + if(box->container.layout.fill == UI_ON) { + XtSetArg(args[a], gridVExpand, TRUE); a++; + XtSetArg(args[a], gridVFill, TRUE); a++; } + XtSetArg(args[a], gridHExpand, TRUE); a++; + XtSetArg(args[a], gridHFill, TRUE); a++; + *n = a; + return ui_box_container_prepare(box, args, n); } - -/* document tabview */ - -static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) { - MotifTabbedPane *v = (MotifTabbedPane*)udata; - - int width = 0; - int height = 0; - XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL); - int button_width = width / 4; - int x = 0; - UCX_FOREACH(elm, v->tabs) { - UiTab *tab = elm->data; - XtVaSetValues( - tab->tab_button, - XmNx, x, - XmNy, 0, - XmNwidth, - button_width, - - NULL); - x += button_width; +Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiBoxContainer *box = (UiBoxContainer*)ctn; + int a = *n; + XtSetArg(args[a], gridColumn, box->n); a++; + if(box->container.layout.fill == UI_ON) { + XtSetArg(args[a], gridHExpand, TRUE); a++; + XtSetArg(args[a], gridHFill, TRUE); a++; } - - if(height <= v->height) { - XtVaSetValues(widget, XmNheight, v->height + 4, NULL); - } + XtSetArg(args[a], gridVExpand, TRUE); a++; + XtSetArg(args[a], gridVFill, TRUE); a++; + *n = a; + return ui_box_container_prepare(box, args, n); } -static void ui_tabbar_expose(Widget widget, XtPointer udata, XtPointer cdata) { - MotifTabbedPane *v = (MotifTabbedPane*)udata; - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)cdata; - XEvent *event = cbs->event; - Display *dpy = XtDisplay(widget); - - XGCValues gcvals; - GC gc; - Pixel fgpix; - - int tab_x; - int tab_width; - XtVaGetValues(v->current->tab_button, XmNx, &tab_x, XmNwidth, &tab_width, XmNhighlightColor, &fgpix, NULL); - - gcvals.foreground = v->bg1; - gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals); - - int width = 0; - int height = 0; - XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL); - XFillRectangle(dpy, XtWindow(widget), gc, 0, 0, width, height); - - gcvals.foreground = fgpix; - gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals); - - XFillRectangle(dpy, XtWindow(widget), gc, tab_x, 0, tab_width, height); +void ui_box_container_add(UiContainerPrivate *ctn, Widget widget) { + ui_reset_layout(ctn->layout); } -UiTabbedPane* ui_tabbed_document_view(UiObject *obj) { + +/* ---------------------------- Grid Container ---------------------------- */ + +// public +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { + Arg xargs[16]; int n = 0; - Arg args[16]; - - UiContainer *ct = uic_get_current_container(obj); - Widget parent = ct->prepare(ct, args, &n, TRUE); - - Widget tabview = XmCreateForm(parent, "tabview_form", args, n); - XtManageChild(tabview); - XtSetArg(args[0], XmNorientation, XmHORIZONTAL); - XtSetArg(args[1], XmNpacking, XmPACK_TIGHT); - XtSetArg(args[2], XmNspacing, 1); - XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[6], XmNmarginWidth, 0); - XtSetArg(args[7], XmNmarginHeight, 0); - Widget tabbar = XmCreateDrawingArea(tabview, "tabbar", args, 8); - XtManageChild(tabbar); + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - XtSetArg(args[0], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[1], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[2], XmNtopAttachment, XmATTACH_WIDGET); - XtSetArg(args[3], XmNtopWidget, tabbar); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNshadowThickness, 0); - Widget tabct = XmCreateForm(tabview, "tabview", args, 6); - XtManageChild(tabct); + Widget parent = ctn->prepare(ctn, xargs, &n); + XtSetArg(xargs[n], gridMargin, args.margin); n++; + XtSetArg(xargs[n], gridColumnSpacing, args.columnspacing); n++; + XtSetArg(xargs[n], gridRowSpacing, args.rowspacing); n++; + Widget grid = XtCreateManagedWidget(args.name ? args.name : "gridcontainer", gridClass, parent, xargs, n); + ctn->add(ctn, grid); - MotifTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(MotifTabbedPane)); - tabbedpane->view.ctx = uic_current_obj(obj)->ctx; - tabbedpane->view.widget = tabct; - tabbedpane->view.document = NULL; - tabbedpane->tabbar = tabbar; - tabbedpane->tabs = NULL; - tabbedpane->current = NULL; - tabbedpane->height = 0; + UiContainerX *container = ui_grid_container(obj, grid); + uic_object_push_container(obj, container); - XtAddCallback(tabbar, XmNresizeCallback , ui_tabbar_resize, tabbedpane); - XtAddCallback(tabbar, XmNexposeCallback, ui_tabbar_expose, tabbedpane); - - return &tabbedpane->view; + return grid; } -UiObject* ui_document_tab(UiTabbedPane *view) { - MotifTabbedPane *v = (MotifTabbedPane*)view; - int n = 0; - Arg args[16]; - - // hide the current tab content - if(v->current) { - XtUnmanageChild(v->current->content->widget); +UiContainerX* ui_grid_container(UiObject *obj, Widget grid) { + UiGridContainer *ctn = ui_malloc(obj->ctx, sizeof(UiGridContainer)); + memset(ctn, 0, sizeof(UiBoxContainer)); + ctn->container.prepare = ui_grid_container_prepare; + ctn->container.add = ui_grid_container_add; + ctn->container.widget = grid; + ctn->x = 0; + ctn->y = 0; + return (UiContainerX*)ctn; +} + +Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiGridContainer *grid = (UiGridContainer*)ctn; + if(ctn->layout.newline) { + grid->y++; + grid->x = 0; } - UiTab *tab = ui_malloc(view->ctx, sizeof(UiTab)); - - // create the new tab content - XtSetArg(args[0], XmNshadowThickness, 0); - XtSetArg(args[1], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[2], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNuserData, tab); - Widget frame = XmCreateFrame(view->widget, "tab", args, 6); - XtManageChild(frame); - - UiObject *content = ui_malloc(view->ctx, sizeof(UiObject)); - content->widget = NULL; // initialization for uic_context() - content->ctx = uic_context(content, view->ctx->mempool); - content->ctx->parent = view->ctx; - content->ctx->attach_document = uic_context_attach_document; - content->ctx->detach_document2 = uic_context_detach_document2; - content->widget = frame; - content->window = view->ctx->obj->window; - content->container = ui_frame_container(content, frame); - content->next = NULL; - - // add tab button - v->tabs = ucx_list_append_a(view->ctx->mempool->allocator, v->tabs, tab); + int a = *n; + XtSetArg(args[a], gridColumn, grid->x); a++; + XtSetArg(args[a], gridRow, grid->y); a++; + if(ctn->layout.colspan > 0) { + XtSetArg(args[a], gridColspan, ctn->layout.colspan); a++; + } + if(ctn->layout.rowspan > 0) { + XtSetArg(args[a], gridRowspan, ctn->layout.rowspan); a++; + } - XmString label = XmStringCreateLocalized("tab"); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 0); - XtSetArg(args[2], XmNhighlightThickness, 0); - - Widget button = XmCreatePushButton(v->tabbar, "tab_button", args, 3); - tab->tabbedpane = v; - tab->content = content; - tab->tab_button = button; - XtManageChild(button); - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_tab_button_callback, - tab); - - if(v->height == 0) { - XtVaGetValues( - button, - XmNarmColor, - &v->bg1, - XmNbackground, - &v->bg2, - XmNheight, - &v->height, - NULL); - v->height += 2; // border + if(grid->container.layout.fill == UI_ON) { + grid->container.layout.hfill = TRUE; + grid->container.layout.vfill = TRUE; + grid->container.layout.hexpand = TRUE; + grid->container.layout.vexpand = TRUE; } - ui_change_tab(v, tab); - ui_tabbar_resize(v->tabbar, v, NULL); + if(grid->container.layout.hfill) { + XtSetArg(args[a], gridHFill, TRUE); a++; + } + if(grid->container.layout.vfill) { + XtSetArg(args[a], gridVFill, TRUE); a++; + } + if(grid->container.layout.hexpand) { + XtSetArg(args[a], gridHExpand, TRUE); a++; + } + if(grid->container.layout.vexpand) { + XtSetArg(args[a], gridVExpand, TRUE); a++; + } - return content; -} - -void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d) { - MotifTabbedPane *t = tab->tabbedpane; - if(t->current) { - XtUnmanageChild(t->current->content->widget); - XtVaSetValues(t->current->tab_button, XmNset, 0, NULL); - } - XtManageChild(tab->content->widget); - - ui_change_tab(t, tab); - + *n = a; + return ctn->widget; } -void ui_change_tab(MotifTabbedPane *pane, UiTab *tab) { - UiContext *ctx = tab->content->ctx; - ctx->parent->detach_document2(ctx->parent, pane->current->content->ctx->document); - ctx->parent->attach_document(ctx->parent, ctx->document); - - if(pane->current) { - XtVaSetValues(pane->current->tab_button, XmNshadowThickness, 0, XmNbackground, pane->bg1, NULL); - } - XtVaSetValues(tab->tab_button, XmNshadowThickness, 1, XmNbackground, pane->bg2, NULL); - - pane->current = tab; - pane->index = ucx_list_find(pane->tabs, tab, NULL, NULL); - printf("index: %d\n", pane->index); - - // redraw tabbar - Display *dpy = XtDisplay(pane->tabbar); - Window window = XtWindow(pane->tabbar); - if(dpy && window) { - XClearArea(dpy, XtWindow(pane->tabbar), 0, 0, 0, 0, TRUE); - XFlush(dpy); - } +void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget) { + UiGridContainer *grid = (UiGridContainer*)ctn; + grid->x++; + ui_reset_layout(ctn->layout); } -void ui_tab_set_document(UiContext *ctx, void *document) { - if(ctx->parent->document) { - //ctx->parent->detach_document(ctx->parent, ctx->parent->document); - } - uic_context_attach_document(ctx, document); - //uic_context_set_document(ctx->parent, document); - //ctx->parent->document = document; - - UiTab *tab = NULL; - XtVaGetValues( - ctx->obj->widget, - XmNuserData, - &tab, - NULL); - if(tab) { - if(tab->tabbedpane->current == tab) { - ctx->parent->attach_document(ctx->parent, ctx->document); - } - } else { - fprintf(stderr, "UiError: ui_bar_set_document: Cannot set document"); - } + +/* -------------------- Container Helper Functions -------------------- */ + +void ui_container_begin_close(UiObject *obj) { + UiContainerPrivate *ct = ui_obj_container(obj); + ct->container.close = 1; } +int ui_container_finish(UiObject *obj) { + UiContainerPrivate *ct = ui_obj_container(obj); + if(ct->container.close) { + ui_end_new(obj); + return 0; + } + return 1; +} /* @@ -779,27 +227,7 @@ * */ -void ui_layout_fill(UiObject *obj, UiBool fill) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.fill = ui_bool2lb(fill); -} - -void ui_layout_hexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.hexpand = expand; -} - -void ui_layout_vexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.vexpand = expand; -} - -void ui_layout_gridwidth(UiObject *obj, int width) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.gridwidth = width; -} - void ui_newline(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); + UiContainerPrivate *ct = ui_obj_container(obj); ct->layout.newline = TRUE; }
--- a/ui/motif/container.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/container.h Sat Jan 04 16:38:48 2025 +0100 @@ -31,32 +31,43 @@ #include "../ui/toolkit.h" #include "../ui/container.h" -#include <ucx/list.h> +#include <cx/list.h> #include <string.h> #ifdef __cplusplus extern "C" { #endif - -#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) -typedef struct MotifTabbedPane MotifTabbedPane; -typedef struct UiTab UiTab; -typedef struct UiBoxContainer UiBoxContainer; -typedef struct UiGridContainer UiGridContainer; -typedef struct UiTabViewContainer UiTabViewContainer; -typedef struct UiLayout UiLayout; +#define UI_APPLY_LAYOUT(layout, args) \ + layout.fill = args.fill; \ + layout.hexpand = args.hexpand; \ + layout.vexpand = args.vexpand; \ + layout.hfill = args.hfill; \ + layout.vfill = args.vfill; \ + layout.colspan = args.colspan; \ + layout.rowspan = args.rowspan + +typedef enum UiBoxOrientation UiBoxOrientation; + +#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) +#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) +#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) -typedef Widget (*ui_container_add_f)(UiContainer*, Arg*, int*, UiBool); - -typedef enum UiLayoutBool UiLayoutBool; -typedef enum UiBoxOrientation UiBoxOrientation; +#define ui_obj_container(obj) (UiContainerPrivate*)obj->container_end + +typedef struct UiLayout UiLayout; - -enum UiLayoutBool { - UI_LAYOUT_UNDEFINED = 0, - UI_LAYOUT_TRUE, - UI_LAYOUT_FALSE, +struct UiLayout { + UiTri fill; + UiBool newline; + char *label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int width; + int colspan; + int rowspan; }; enum UiBoxOrientation { @@ -64,93 +75,37 @@ UI_BOX_HORIZONTAL }; -struct UiLayout { - UiLayoutBool fill; - UiBool newline; - char *label; - UiBool hexpand; - UiBool vexpand; - int gridwidth; -}; +typedef struct UiContainerPrivate UiContainerPrivate; -struct UiContainer { - Widget widget; - Widget (*prepare)(UiContainer*, Arg *, int*, UiBool); - void (*add)(UiContainer*, Widget); - UiLayout layout; - Widget current; - Widget menu; -}; -struct UiBoxContainer { - UiContainer container; - Widget prev_widget; - UiBool has_fill; - UiBoxOrientation orientation; - int margin; - int spacing; -}; - -struct UiGridContainer { - UiContainer container; - UcxList *lines; - UcxList *current; - int columnspacing; - int rowspacing; -}; - -struct UiTabViewContainer { - UiContainer container; - UiContext *context; - Widget widget; - UcxList *tabs; - Widget current; +struct UiContainerPrivate { + UiContainerX container; + Widget (*prepare)(UiContainerPrivate*, Arg *, int*); + void (*add)(UiContainerPrivate*, Widget); + Widget widget; + UiLayout layout; }; -struct MotifTabbedPane { - UiTabbedPane view; - Widget tabbar; - UcxList *tabs; - UiTab *current; - int index; - Pixel bg1; - Pixel bg2; - int height; -}; - -struct UiTab { - MotifTabbedPane *tabbedpane; - UiObject *content; - Widget tab_button; -}; - - -UiContainer* ui_frame_container(UiObject *obj, Widget frame); -Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_frame_container_add(UiContainer *ct, Widget widget); +typedef struct UiBoxContainer { + UiContainerPrivate container; + Dimension n; +} UiBoxContainer; -UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation); -Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_box_container_add(UiContainer *ct, Widget widget); - -UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing); -Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_grid_container_add(UiContainer *ct, Widget widget); +typedef struct UiGridContainer { + UiContainerPrivate container; + Dimension x; + Dimension y; +} UiGridContainer; -UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow); -Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget); +UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation); +Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +void ui_box_container_add(UiContainerPrivate *ctn, Widget widget); -UiContainer* ui_tabview_container(UiObject *obj, Widget rowcolumn); -Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_tabview_container_add(UiContainer *ct, Widget widget); -void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d); -void ui_change_tab(MotifTabbedPane *pane, UiTab *tab); - -void ui_tab_set_document(UiContext *ctx, void *document); -void ui_tab_detach_document(UiContext *ctx); - +UiContainerX* ui_grid_container(UiObject *obj, Widget grid); +Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget); #ifdef __cplusplus }
--- a/ui/motif/dnd.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/dnd.c Sat Jan 04 16:38:48 2025 +0100 @@ -28,18 +28,3 @@ #include "dnd.h" -void ui_selection_settext(UiSelection *sel, char *str, int len) { - -} - -void ui_selection_seturis(UiSelection *sel, char **uris, int nelm) { - -} - -char* ui_selection_gettext(UiSelection *sel) { - -} - -char** ui_selection_geturis(UiSelection *sel, size_t *nelm) { - -}
--- a/ui/motif/graphics.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/graphics.c Sat Jan 04 16:38:48 2025 +0100 @@ -34,250 +34,3 @@ #include "graphics.h" #include "container.h" - -static void ui_drawingarea_expose(Widget widget, XtPointer u, XtPointer c) { - UiDrawEvent *drawevent = u; - //XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)c; - //XEvent *event = cbs->event; - Display *dpy = XtDisplay(widget); - - UiEvent ev; - ev.obj = drawevent->obj; - ev.window = drawevent->obj->window; - ev.document = drawevent->obj->ctx->document; - ev.eventdata = NULL; - ev.intval = 0; - - XtVaGetValues( - widget, - XmNwidth, - &drawevent->gr.g.width, - XmNheight, - &drawevent->gr.g.height, - NULL); - - XGCValues gcvals; - gcvals.foreground = BlackPixelOfScreen(XtScreen(widget)); - drawevent->gr.gc = XCreateGC(dpy, XtWindow(widget), (GCForeground), &gcvals); - - drawevent->callback(&ev, &drawevent->gr.g, drawevent->userdata); -} - -UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) { - UiContainer *ct = uic_get_current_container(obj); - - int n = 0; - Arg args[16]; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget drawingarea = XmCreateDrawingArea(parent, "drawingarea", args, n); - - if(f) { - UiDrawEvent *event = malloc(sizeof(UiDrawEvent)); - event->obj = obj; - event->callback = f; - event->userdata = userdata; - - event->gr.display = XtDisplay(drawingarea); - event->gr.widget = drawingarea; - - Colormap colormap; - XtVaGetValues(drawingarea, XmNcolormap, &colormap, NULL); - event->gr.colormap = colormap; - - XtAddCallback( - drawingarea, - XmNexposeCallback, - ui_drawingarea_expose, - event); - - XtVaSetValues(drawingarea, XmNuserData, event, NULL); - } - - XtManageChild(drawingarea); - return drawingarea; -} - -static void ui_drawingarea_input(Widget widget, XtPointer u, XtPointer c) { - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - UiMouseEventData *event = u; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - UiMouseEvent me; - me.x = xevent->xbutton.x; - me.y = xevent->xbutton.y; - // TODO: configurable double click time - me.type = xevent->xbutton.time - event->last_event > 300 ? UI_PRESS : UI_PRESS2; - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = &me; - e.intval = 0; - event->callback(&e, event->userdata); - - - event->last_event = me.type == UI_PRESS2 ? 0 : xevent->xbutton.time; - } - } - -} - -void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) { - if(f) { - UiMouseEventData *event = malloc(sizeof(UiMouseEventData)); - event->obj = obj; - event->callback = f; - event->userdata = u; - event->last_event = 0; - - XtAddCallback(widget, XmNinputCallback, ui_drawingarea_input, event); - } -} - -void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { - XtVaGetValues( - drawingarea, - XmNwidth, - width, - XmNheight, - height, - NULL); -} - -void ui_drawingarea_redraw(UIWIDGET drawingarea) { - //XClearArea(XtDisplay(drawingarea), drawingarea->core.window, 0, 0, drawingarea->core.width, drawingarea->core.height, True); - UiDrawEvent *event; - XtVaGetValues(drawingarea, XmNuserData, &event, NULL); - ui_drawingarea_expose(drawingarea, event, NULL); -} - - -/* -------------------- text layout functions -------------------- */ -UiTextLayout* ui_text(UiGraphics *g) { - UiTextLayout *text = malloc(sizeof(UiTextLayout)); - memset(text, 0, sizeof(UiTextLayout)); - text->text = NULL; - text->length = 0; - text->widget = ((UiXlibGraphics*)g)->widget; - text->fontset = NULL; - return text; -} - -static void create_default_fontset(UiTextLayout *layout) { - char **missing = NULL; - int num_missing = 0; - char *def = NULL; - Display *dpy = XtDisplay(layout->widget); - XFontSet fs = XCreateFontSet( - dpy, - "-dt-interface system-medium-r-normal-s*utf*:," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-1," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-10," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-15," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-2," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-3," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-4," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-5," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-9," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-e," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-r," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-ru," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-u," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-uni," - "-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208", - &missing, &num_missing, &def); - layout->fontset = fs; -} - -void ui_text_free(UiTextLayout *text) { - // TODO -} - -void ui_text_setstring(UiTextLayout *layout, char *str) { - ui_text_setstringl(layout, str, strlen(str)); -} - -void ui_text_setstringl(UiTextLayout *layout, char *str, int len) { - layout->text = str; - layout->length = len; - layout->changed = 1; -} - -void ui_text_setfont(UiTextLayout *layout, char *font, int size) { - create_default_fontset(layout);//TODO - layout->changed = 1; -} - -void ui_text_getsize(UiTextLayout *layout, int *width, int *height) { - if(layout->changed) { - XRectangle ext, lext; - XmbTextExtents(layout->fontset, layout->text, layout->length, &ext, &lext); - layout->width = ext.width; - layout->height = ext.height; - layout->changed = 0; - } - *width = layout->width; - *height = layout->height; -} - -void ui_text_setwidth(UiTextLayout *layout, int width) { - layout->maxwidth = width; -} - - -/* -------------------- drawing functions -------------------- */ - -void ui_graphics_color(UiGraphics *g, int red, int green, int blue) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - XColor color; - color.flags= DoRed | DoGreen | DoBlue; - color.red = red * 257; - color.green = green * 257; - color.blue = blue * 257; - XAllocColor(gr->display, gr->colormap, &color); - XSetForeground(gr->display, gr->gc, color.pixel); -} - -void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - XDrawLine(gr->display, XtWindow(gr->widget), gr->gc, x1, y1, x2, y2); -} - -void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - if(fill) { - XFillRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h); - } else { - XDrawRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h); - } -} - -void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - int width, height; - ui_text_getsize(text, &width, &height); - if(text->maxwidth > 0) { - XRectangle clip; - clip.x = x; - clip.y = y; - clip.width = text->maxwidth; - clip.height = height; - XSetClipRectangles(gr->display, gr->gc, 0, 0, &clip, 1, Unsorted); - } - - XmbDrawString( - gr->display, - XtWindow(gr->widget), - text->fontset, - gr->gc, - x, - y + height, - text->text, - text->length); - - XSetClipMask(gr->display, gr->gc, None); -}
--- a/ui/motif/graphics.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/graphics.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,38 +36,6 @@ extern "C" { #endif -typedef struct UiXlibGraphics { - UiGraphics g; - Display *display; - Widget widget; - Colormap colormap; - GC gc; -} UiXlibGraphics; - -typedef struct UiDrawEvent { - ui_drawfunc callback; - UiObject *obj; - void *userdata; - UiXlibGraphics gr; -} UiDrawEvent; - -typedef struct UiMouseEventData { - UiObject *obj; - ui_callback callback; - void *userdata; - Time last_event; -} UiMouseEventData; - -struct UiTextLayout { - char *text; - size_t length; - Widget widget; - XFontSet fontset; - int maxwidth; - int width; - int height; - int changed; -}; #ifdef __cplusplus
--- a/ui/motif/image.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/image.c Sat Jan 04 16:38:48 2025 +0100 @@ -6,35 +6,3 @@ #include "image.h" -UiIcon* ui_icon(const char *name, int size) { - return NULL; -} - -UiIcon* ui_icon_unscaled(const char *name, int size) { - return NULL; -} - -void ui_free_icon(UiIcon *icon) { - -} - -UiImage* ui_icon_image(UiIcon *icon) { - return NULL; -} - -UiImage* ui_image(const char *filename) { - return NULL; -} - -UiImage* ui_named_image(const char *filename, const char *name) { - return NULL; -} - -UiImage* ui_load_image_from_path(const char *path, const char *name) { - return NULL; -} - -void ui_free_image(UiImage *img) { - -} -
--- a/ui/motif/label.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/label.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -33,38 +33,154 @@ #include "container.h" #include "../common/context.h" #include "../common/object.h" -#include <ucx/mempool.h> + +#include "Grid.h" -UIWIDGET ui_label(UiObject *obj, char *label) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); - +static UIWIDGET label_create(UiObject *obj, UiLabelArgs args, int align) { + Arg xargs[16]; int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNlabelString, str); - n++; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + + XtSetArg(xargs[n], XmNalignment, align); n++; + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "label"; + Widget w = XmCreateLabel(parent, name, xargs, n); + XtManageChild(w); + ctn->add(ctn, w); - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget widget = XmCreateLabel(parent, "label", args, n); - ct->add(ct, widget); - XtManageChild(widget); + XmStringFree(label); + return w; +} + +UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs args) { + return label_create(obj, args, XmALIGNMENT_CENTER); +} + +UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args) { + return label_create(obj, args, XmALIGNMENT_BEGINNING); +} + +UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args) { + return label_create(obj, args, XmALIGNMENT_END); +} + + +/* ------------------------------ progressbar ------------------------------ */ + +static void ui_destroy_progressbar(Widget w, UiProgressBar *pb, XtPointer d) { + // TODO: free other stuff + free(pb); +} + +static void ui_progressbar_expose(Widget widget, UiProgressBar *pb, XtPointer c) { + Display *dp = XtDisplay(widget); + Window w = XtWindow(widget); + if(w == 0) { + return; + } + if(!pb->gc) { + XGCValues gcvals; + gcvals.foreground = pb->color; + pb->gc = XCreateGC(dp, w, (GCForeground), &gcvals); + } - return widget; + Dimension width = widget->core.width; + Dimension height = widget->core.height; + + double value = (pb->value - pb->min) / (pb->max - pb->min); + Dimension valueW = (double)width * value; + + XClearArea(dp, w, 0, 0, width, height, False); + XFillRectangle(dp, w, pb->gc, 0, 0, valueW, widget->core.height); } -UIWIDGET ui_space(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(""); - +UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs args) { + Arg xargs[16]; int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNlabelString, str); - n++; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + + char *name = args.name ? (char*)args.name : "progressbar"; + Widget frame = XmCreateFrame(parent, name, xargs, n); + + // create a button and get some informations about the height, shadow, highlight, .... + // we want the frame to have the same dimensions as a normal button + Widget test = XmCreatePushButton(frame, "button", NULL, 0); + XtManageChild(test); + Dimension h, highlightThickness, shadowThickness; + Pixel highlightColor; + XtVaGetValues(test, XmNheight, &h, XmNhighlightThickness, &highlightThickness, + XmNshadowThickness, &shadowThickness, XmNhighlightColor, &highlightColor, NULL); + XtDestroyWidget(test); + + // adjust frame + XtVaSetValues(frame, XmNshadowThickness, shadowThickness, gridMarginLeft, highlightThickness, + gridMarginRight, highlightThickness, gridMarginTop, highlightThickness, + gridMarginBottom, highlightThickness, NULL); + + // create drawing area + Dimension da_height = h - 2*highlightThickness - 2*shadowThickness; + n = 0; + XtSetArg(xargs[n], XmNheight, da_height); n++; + Widget drawingArea = XmCreateDrawingArea(frame, "progressbar_drawingarea", xargs, n); + XtManageChild(drawingArea); + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_DOUBLE); - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget widget = XmCreateLabel(parent, "space_label", args, n); - ct->add(ct, widget); - XtManageChild(widget); + UiProgressBar *progressbarData = malloc(sizeof(UiProgressBar)); + progressbarData->widget = drawingArea; + progressbarData->min = args.min; + progressbarData->max = args.max == 0 ? 100 : args.max; + progressbarData->value = 50; + progressbarData->var = var; + progressbarData->color = highlightColor; + progressbarData->gc = NULL; // initialize on first expose + + if(var) { + UiDouble *d = var->value; + progressbarData->value = d->value; + d->obj = progressbarData; + d->get = ui_progressbar_get; + d->set = ui_progressbar_set; + } - return widget; + XtAddCallback( + drawingArea, + XmNexposeCallback, + (XtCallbackProc)ui_progressbar_expose, + progressbarData); + XtAddCallback( + drawingArea, + XmNresizeCallback, + (XtCallbackProc)ui_progressbar_expose, + progressbarData); + + + XtManageChild(frame); + return frame; } + +double ui_progressbar_get(UiDouble *d) { + UiProgressBar *pb = d->obj; + d->value = pb->value; + return d->value; +} + +void ui_progressbar_set(UiDouble *d, double value) { + UiProgressBar *pb = d->obj; + d->value = value; + pb->value = value; + ui_progressbar_expose(pb->widget, pb, NULL); +}
--- a/ui/motif/label.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/label.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -29,12 +29,26 @@ #ifndef LABEL_H #define LABEL_H +#include "../ui/display.h" +#include "../common/context.h" + #ifdef __cplusplus extern "C" { #endif +typedef struct UiProgressBar { + Widget widget; + GC gc; + UiVar *var; + double min; + double max; + double value; + Pixel color; +} UiProgressBar; +double ui_progressbar_get(UiDouble *d); +void ui_progressbar_set(UiDouble *d, double value); #ifdef __cplusplus }
--- a/ui/motif/list.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/list.c Sat Jan 04 16:38:48 2025 +0100 @@ -34,87 +34,118 @@ #include "list.h" #include "../common/object.h" - -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; -} - - -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_listview(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - int count; - XmStringTable items = ui_create_stringlist(var->value, getvalue, &count); +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - Arg args[8]; - int n = 0; - XtSetArg(args[n], XmNitemCount, count); - n++; - XtSetArg(args[n], XmNitems, count == 0 ? NULL : items); - n++; + if(args.multiselection) { + XtSetArg(xargs[n], XmNselectionPolicy, XmEXTENDED_SELECT); n++; + } else { + XtSetArg(xargs[n], XmNselectionPolicy, XmSINGLE_SELECT); n++; + } - UiContainer *ct = uic_get_current_container(obj); - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget widget = XmCreateScrolledList(parent, "listview", args, n); - ct->add(ct, XtParent(widget)); + char *name = args.name ? (char*)args.name : "listview"; + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget widget = XmCreateScrolledList(parent, name, xargs, n); XtManageChild(widget); - UiListView *listview = ucx_mempool_malloc(obj->ctx->mempool, sizeof(UiListView)); - listview->widget = widget; - listview->list = var; - listview->getvalue = getvalue; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.list, args.varname, UI_VAR_LIST); - for (int i=0;i<count;i++) { - XmStringFree(items[i]); - } - XtFree((char *)items); + UiListView *listview = malloc(sizeof(UiListView)); + memset(listview, 0, sizeof(UiListView)); + listview->obj = obj; + listview->widget = widget; + listview->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; + listview->var = var; + listview->onactivate = args.onactivate; + listview->onactivatedata = args.onactivatedata; + listview->onselection = args.onselection; + listview->onselectiondata = args.onselectiondata; - if(f) { - UiListViewEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiListViewEventData)); - event->event.obj = obj; - event->event.userdata = udata; - event->event.callback = f; - event->event.value = 0; - event->var = var; - XtAddCallback( + if(var) { + UiList *list = var->value; + list->obj = listview; + list->update = ui_listview_update; + list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; + ui_listview_update(list, 0); + } + + XtAddCallback( + widget, + XmNdestroyCallback, + (XtCallbackProc)ui_listview_destroy, + listview); + + XtAddCallback( widget, XmNdefaultActionCallback, - (XtCallbackProc)ui_list_selection_callback, - event); - } + (XtCallbackProc)ui_listview_activate, + listview); + XtAddCallback( + widget, + XmNextendedSelectionCallback, + (XtCallbackProc)ui_listview_selection, + listview); + XtAddCallback( + widget, + XmNsingleSelectionCallback, + (XtCallbackProc)ui_listview_selection, + listview); return widget; } -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_listview_var(obj, var, getvalue, f, udata); +void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d) { + // TODO +} + +static void list_callback(UiObject *obj, UiListSelection sel, ui_callback callback, void *userdata) { + UiEvent event; + event.obj = obj; + event.window = obj->window; + event.document = obj->ctx->document; + event.eventdata = &sel; + event.intval = sel.count > 0 ? sel.rows[0] : -1; + callback(&event, userdata); } -UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - UiListVar *value = var->value; - return ui_listview_var(obj, var, getvalue, f, udata); - } else { - // TODO: error +static void listview_save_selection(UiListView *listview, XmListCallbackStruct *cb) { + UiListSelection sel = { cb->selected_item_count, NULL }; + if(sel.count > 0) { + sel.rows = calloc(sel.count, sizeof(int)); + for(int i=0;i<sel.count;i++) { + sel.rows[i] = cb->selected_item_positions[i]-1; + } } - return NULL; + free(listview->current_selection.rows); + listview->current_selection = sel; } +void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *cb) { + listview_save_selection(listview, cb); + if(listview->onactivate) { + list_callback(listview->obj, listview->current_selection, listview->onactivate, listview->onactivatedata); + } +} -XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count) { +void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb) { + listview_save_selection(listview, cb); + if(listview->onselection) { + list_callback(listview->obj, listview->current_selection, listview->onselection, listview->onselectiondata); + } +} + +static XmStringTable create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count) { int num = list->count(list); XmStringTable items = (XmStringTable)XtMalloc(num * sizeof(XmString)); void *data = list->first(list); for(int i=0;i<num;i++) { - items[i] = XmStringCreateLocalized(getvalue(data, 0)); + char *s = getvalue(data, 0); + items[i] = XmStringCreateLocalized(s ? s : ""); data = list->next(list); } @@ -122,16 +153,17 @@ return items; } - -void ui_listview_update(UiEvent *event, UiListView *view) { +void ui_listview_update(UiList *list, int i) { + UiListView *listview = list->obj; + int count; - XmStringTable items = ui_create_stringlist( - view->list->value, - view->getvalue, + XmStringTable items = create_stringlist( + list, + listview->getvalue, &count); XtVaSetValues( - view->widget, + listview->widget, XmNitems, count == 0 ? NULL : items, XmNitemCount, count, @@ -143,65 +175,24 @@ XtFree((char *)items); } -void ui_list_selection_callback (Widget widget, UiListViewEventData *event, XtPointer data) { - XmListCallbackStruct *cbs = (XmListCallbackStruct *)data; - - UiEvent e; - e.obj = event->event.obj; - e.window = event->event.obj->window; - e.document = event->event.obj->ctx->document; - UiList *list = event->var->value; - e.eventdata = list->get(list, cbs->item_position - 1); - e.intval = cbs->item_position - 1; - event->event.callback(&e, event->event.userdata); -} - - -/* --------------------------- ComboBox --------------------------- */ - -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_combobox(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_combobox_var(obj, var, getvalue, f, udata); +UiListSelection ui_listview_getselection(UiList *list) { + UiListView *listview = list->obj; + UiListSelection sel = { listview->current_selection.count, NULL }; + if(sel.count > 0) { + sel.rows = calloc(sel.count, sizeof(int)); + memcpy(sel.rows, listview->current_selection.rows, sel.count*sizeof(int)); + } + return sel; } -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - UiListVar *value = var->value; - return ui_combobox_var(obj, var, getvalue, f, udata); - } else { - // TODO: error +void ui_listview_setselection(UiList *list, UiListSelection selection) { + UiListView *listview = list->obj; + XmListDeselectAllItems(listview->widget); + for(int i=0;i<selection.count;i++) { + XmListSelectPos(listview->widget, selection.rows[i]+1, False); } - return NULL; } -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiListView *listview = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiListView)); - - UiContainer *ct = uic_get_current_container(obj); - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_NONE); - n++; - XtSetArg(args[n], XmNtraversalOn, FALSE); - n++; - XtSetArg(args[n], XmNwidth, 160); - n++; - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget combobox = XmCreateDropDownList(parent, "combobox", args, n); - XtManageChild(combobox); - listview->widget = combobox; - listview->list = var; - listview->getvalue = getvalue; - - ui_listview_update(NULL, listview); - +void* ui_strmodel_getvalue(void *elm, int column) { + return column == 0 ? elm : NULL; }
--- a/ui/motif/list.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/list.h Sat Jan 04 16:38:48 2025 +0100 @@ -38,24 +38,38 @@ #endif typedef struct UiListView { - Widget widget; - UiVar *list; + UiObject *obj; + Widget widget; + UiVar *var; + UiModel* model; ui_getvaluefunc getvalue; + + UiListSelection current_selection; + + ui_callback onactivate; + void* onactivatedata; + ui_callback onselection; + void* onselectiondata; + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropsdata; + UiBool multiselection; } UiListView; -typedef struct UiListViewEventData { - UiEventData event; - UiVar *var; -} UiListViewEventData; +void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d); + +void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *cb); +void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb); + +void ui_listview_update(UiList *list, int i); +UiListSelection ui_listview_getselection(UiList *list); +void ui_listview_setselection(UiList *list, UiListSelection selection); void* ui_strmodel_getvalue(void *elm, int column); -XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count); -void ui_listview_update(UiEvent *event, UiListView *view); -void ui_list_selection_callback (Widget widget, UiListViewEventData *event, XtPointer data); - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); - #ifdef __cplusplus } #endif
--- a/ui/motif/menu.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/menu.c Sat Jan 04 16:38:48 2025 +0100 @@ -36,275 +36,89 @@ #include "stock.h" #include "container.h" #include "../common/context.h" +#include "../common/menu.h" +#include "../common/types.h" #include "../ui/window.h" -UcxList *menus; -UcxList *current; - -void ui_menu(char *label) { - // free current menu hierarchy - ucx_list_free(current); - - // create menu - UiMenu *menu = malloc(sizeof(UiMenu)); - menu->item.add_to = (ui_menu_add_f)add_menu_widget; - - menu->label = label; - menu->items = NULL; - menu->parent = NULL; - - current = ucx_list_prepend(NULL, menu); - menus = ucx_list_append(menus, menu); - -} - -void ui_submenu(char *label) { - UiMenu *menu = malloc(sizeof(UiMenu)); - menu->item.add_to = (ui_menu_add_f)add_menu_widget; - - menu->label = label; - menu->items = NULL; - menu->parent = NULL; - - // add submenu to current menu - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, menu); - - // set the submenu to current menu - current = ucx_list_prepend(current, menu); -} - -void ui_submenu_end() { - if(ucx_list_size(current) < 2) { - return; - } - current = ucx_list_remove(current, current); -} - -void ui_menuitem(char *label, ui_callback f, void *userdata) { - ui_menuitem_gr(label, f, userdata, -1); -} - -void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) { - ui_menuitem_stgr(stockid, f, userdata, -1); -} - -void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) { - if(!current) { - return; - } - - UiMenuItem *item = malloc(sizeof(UiMenuItem)); - item->item.add_to = (ui_menu_add_f)add_menuitem_widget; - - item->label = label; - item->userdata = userdata; - item->callback = f; - item->groups = NULL; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { - if(!current) { - return; - } - - UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); - item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget; - - item->stockid = stockid; - item->userdata = userdata; - item->callback = f; - item->groups = NULL; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_menuseparator() { - if(!current) { - return; - } - - UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); - item->add_to = (ui_menu_add_f)add_menuseparator_widget; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} +#include <cx/linked_list.h> +#include <cx/array_list.h> -void ui_checkitem(char *label, ui_callback f, void *userdata) { - if(!current) { +static ui_menu_add_f createMenuItem[] = { + /* UI_MENU */ add_menu_widget, + /* UI_MENU_ITEM */ add_menuitem_widget, + /* UI_MENU_CHECK_ITEM */ add_checkitem_widget, + /* UI_MENU_RADIO_ITEM */ add_radioitem_widget, + /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_SEPARATOR */ add_menuseparator_widget +}; + +void ui_create_menubar(UiObject *obj, Widget window) { + UiMenu *menus_begin = uic_get_menu_list(); + if(!menus_begin) { return; } - UiCheckItem *item = malloc(sizeof(UiCheckItem)); - item->item.add_to = (ui_menu_add_f)add_checkitem_widget; - item->label = label; - item->callback = f; - item->userdata = userdata; + Widget menubar = XmCreateMenuBar(window, "menubar", NULL, 0); + XtManageChild(menubar); - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - -void ui_checkitem_nv(char *label, char *vname) { - if(!current) { - return; + UiMenu *ls = menus_begin; + while(ls) { + UiMenu *menu = ls; + add_menu_widget(menubar, 0, &menu->item, obj); + ls = (UiMenu*)ls->item.next; } - - UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV)); - item->item.add_to = (ui_menu_add_f)add_checkitemnv_widget; - item->varname = vname; - item->label = label; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); } -void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) { - if(!current) { - return; - } - - UiMenuItemList *item = malloc(sizeof(UiMenuItemList)); - item->item.add_to = (ui_menu_add_f)add_menuitem_list_widget; - item->callback = f; - item->userdata = userdata; - item->list = items; - - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); -} - - -// private menu functions -void ui_create_menubar(UiObject *obj) { - if(!menus) { - return; - } - - Widget menubar = XmCreateMenuBar(obj->widget, "main_list", NULL, 0); - XtManageChild(menubar); - - UcxList *ls = menus; - int menu_index = 0; - while(ls) { - UiMenu *menu = ls->data; - menu_index += menu->item.add_to(menubar, menu_index, &menu->item, obj); - - ls = ls->next; +void ui_add_menu_items(Widget parent, int i, UiMenu *menu, UiObject *obj) { + UiMenuItemI *it = menu->items_begin; + int index = 0; + while(it) { + createMenuItem[it->type](parent, index, it, obj); + it = it->next; + index++; } } -int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { +void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { UiMenu *menu = (UiMenu*)item; + Arg args[4]; + int n = 0; - Widget menuItem = XtVaCreateManagedWidget( - menu->label, - xmCascadeButtonWidgetClass, - parent, - NULL); - Widget m = XmVaCreateSimplePulldownMenu(parent, menu->label, i, NULL, NULL); - - UcxList *ls = menu->items; - int menu_index = 0; - while(ls) { - UiMenuItemI *mi = ls->data; - menu_index += mi->add_to(m, menu_index, mi, obj); - ls = ls->next; + XmString s = NULL; + if(menu->label) { + s = XmStringCreateLocalized((char*)menu->label); + XtSetArg(args[n], XmNlabelString, s); n++; } - return 1; -} - -int add_menuitem_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiMenuItem *mi = (UiMenuItem*)item; - - Arg args[1]; - XmString label = XmStringCreateLocalized(mi->label); - XtSetArg(args[0], XmNlabelString, label); - - Widget mitem = XtCreateManagedWidget( - "menubutton", - xmPushButtonWidgetClass, + Widget submenu = XmVaCreateSimplePulldownMenu(parent, "menu_pulldown", i, NULL, NULL); + XtSetArg(args[n], XmNsubMenuId, submenu); n++; + Widget menuItem = XtCreateManagedWidget( + "menuitem", + xmCascadeButtonWidgetClass, parent, args, - 1); - XmStringFree(label); + n); + - if(mi->callback != NULL) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = mi->userdata; - event->callback = mi->callback; - event->value = 0; - XtAddCallback( - mitem, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); + if(s) { + XmStringFree(s); } - if(mi->groups) { - uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); - } - - return 1; + ui_add_menu_items(submenu, i, menu, obj); } -int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { - UiStMenuItem *mi = (UiStMenuItem*)item; +void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { + UiMenuItem *it = (UiMenuItem*)item; - UiStockItem *si = ui_get_stock_item(mi->stockid); - if(!si) { - fprintf(stderr, "UI Error: unknown stock id: %s\n", mi->stockid); - return 0; - } - - int n = 0; + XmString s = NULL; Arg args[4]; - XmString label = XmStringCreateLocalized(si->label); - XmString at = NULL; - - XtSetArg(args[n], XmNlabelString, label); - n++; - if(si->accelerator) { - XtSetArg(args[n], XmNaccelerator, si->accelerator); - n++; - } - if(si->accelerator_label) { - at = XmStringCreateLocalized(si->accelerator_label); - XtSetArg(args[n], XmNacceleratorText, at); - n++; + int n = 0; + if(it->label) { + s = XmStringCreateLocalized((char*)it->label); + XtSetArg(args[n], XmNlabelString, s); n++; } Widget mitem = XtCreateManagedWidget( @@ -313,150 +127,119 @@ parent, args, n); - XmStringFree(label); - if(at) { - XmStringFree(at); + if(s) { + XmStringFree(s); } - if(mi->callback != NULL) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = mi->userdata; - event->callback = mi->callback; - event->value = 0; + if(it->callback) { + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = it->callback; + eventdata->userdata = it->userdata; + eventdata->obj = obj; + eventdata->value = 0; XtAddCallback( mitem, XmNactivateCallback, (XtCallbackProc)ui_push_button_callback, - event); + eventdata); + XtAddCallback( + mitem, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + eventdata); } - if(mi->groups) { - uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); - } - - return 1; + ui_set_widget_groups(obj->ctx, mitem, it->groups); } -int add_menuseparator_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - Widget s = XmCreateSeparatorGadget (parent, "menu_separator", NULL, 0); +void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) { + Widget s = XmCreateSeparatorGadget (p, "menuseparator", NULL, 0); XtManageChild(s); - return 1; } -int add_checkitem_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiCheckItem *ci = (UiCheckItem*)item; +void add_checkitem_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) { + UiMenuCheckItem *it = (UiMenuCheckItem*)item; - Arg args[3]; - XmString label = XmStringCreateLocalized(ci->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNvisibleWhenOff, 1); + Arg args[4]; + int n = 0; + XmString s = NULL; + if(it->label) { + s = XmStringCreateLocalized(it->label); + XtSetArg(args[n], XmNlabelString, s); n++; + } + + //XtSetArg(args[n], XmNvisibleWhenOff, 0); n++; Widget checkbox = XtCreateManagedWidget( "menutogglebutton", xmToggleButtonWidgetClass, - parent, + p, args, - 2); - XmStringFree(label); - - if(ci->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = ci->userdata; - event->callback = ci->callback; - XtAddCallback( - checkbox, - XmNvalueChangedCallback, - (XtCallbackProc)ui_toggle_button_callback, - event); + n); + if(s) { + XmStringFree(s); } - return 1; + ui_bind_togglebutton(obj, checkbox, it->varname, NULL, it->callback, it->userdata, 0); + + ui_set_widget_groups(obj->ctx, checkbox, it->groups); } -int add_checkitemnv_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiCheckItemNV *ci = (UiCheckItemNV*)item; +void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuRadioItem *it = (UiMenuRadioItem*)item; - Arg args[3]; - XmString label = XmStringCreateLocalized(ci->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNvisibleWhenOff, 1); - Widget checkbox = XtCreateManagedWidget( - "menutogglebutton", - xmToggleButtonWidgetClass, - parent, - args, - 2); - XmStringFree(label); + Arg args[4]; + int n = 0; + XmString s = NULL; + if(it->label) { + s = XmStringCreateLocalized(it->label); + XtSetArg(args[n], XmNlabelString, s); n++; + } + XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++; - UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER); - if(var) { - UiInteger *value = var->value; - value->obj = checkbox; - value->get = ui_toggle_button_get; - value->set = ui_toggle_button_set; - value = 0; - } else { - // TODO: error - } + Widget button = XmCreateToggleButton(p, "menuradiobutton", args, n); + XtManageChild(button); - return 1; + ui_bind_radiobutton(obj, button, NULL, it->varname, it->callback, it->userdata, 0); } -int add_menuitem_list_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ +void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) { UiMenuItemList *il = (UiMenuItemList*)item; - UcxMempool *mp = obj->ctx->mempool; + const CxAllocator *a = obj->ctx->allocator; - UiActiveMenuItemList *ls = ucx_mempool_malloc( - mp, + UiActiveMenuItemList *ls = cxMalloc( + a, sizeof(UiActiveMenuItemList)); - ls->object = obj; - ls->menu = parent; + ls->menu = p; ls->index = i; ls->oldcount = 0; - ls->list = il->list; + ls->getvalue = il->getvalue; ls->callback = il->callback; ls->userdata = il->userdata; + ls->addseparator = il->addseparator; - ls->list->observers = ui_add_observer( - ls->list->observers, - (ui_callback)ui_update_menuitem_list, - ls); + ls->var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); //uic_widget_var(obj->ctx, obj->ctx, NULL, il->varname, UI_VAR_LIST); + UiList *list = ls->var->value; + + UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls); + list->observers = ui_obsvlist_add(list->observers, observer); + uic_list_register_observer_destructor(obj->ctx, list, observer); ui_update_menuitem_list(NULL, ls); - - return 0; } void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { + XmString s = NULL; Arg args[4]; + int n; - // remove old items + UiList *ls; + if(list->var && list->var->value) { + ls = list->var->value; + } else { + return; + } + if(list->oldcount > 0) { Widget *children; int nc; @@ -474,153 +257,57 @@ } } - char *str = ui_list_first(list->list); - if(str) { - // add separator + void* elm = ui_list_first(ls); + int i = 0; + if(elm && list->addseparator) { XtSetArg(args[0], XmNpositionIndex, list->index); - Widget s = XmCreateSeparatorGadget (list->menu, "menu_separator", args, 1); + Widget s = XmCreateSeparatorGadget(list->menu, "menuseparator", args, 1); XtManageChild(s); + i++; } - int i = 1; - while(str) { - XmString label = XmStringCreateLocalized(str); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNpositionIndex, list->index + i); - + + ui_getvaluefunc getvalue = list->getvalue; + int pos = list->index; + while(elm) { + n = 0; + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + if(label) { + s = XmStringCreateLocalized(label); + XtSetArg(args[n], XmNlabelString, s); n++; + } + XtSetArg(args[n], XmNpositionIndex, pos+i); n++; + Widget mitem = XtCreateManagedWidget( "menubutton", xmPushButtonWidgetClass, list->menu, args, - 2); - XmStringFree(label); - + n); + if(s) { + XmStringFree(s); + } + if(list->callback) { - // TODO: use mempool - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = list->object; - event->userdata = list->userdata; - event->callback = list->callback; - event->value = i - 1; - + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = list->callback; + eventdata->userdata = list->userdata; + eventdata->obj = list->object; + eventdata->value = 0; XtAddCallback( - mitem, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); + mitem, + XmNactivateCallback, + (XtCallbackProc)ui_push_button_callback, + eventdata); + XtAddCallback( + mitem, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + eventdata); } - str = ui_list_next(list->list); + elm = ui_list_next(ls); i++; } list->oldcount = i; } - -void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata) { - UiEventData *event = udata; - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.intval = 0; - event->callback(&e, event->userdata); -} - - -/* - * widget menu functions - */ - -static void ui_popup_handler(Widget widget, XtPointer data, XEvent *event, Boolean *c) { - Widget menu = data; - XmMenuPosition(menu, (XButtonPressedEvent *)event); - XtManageChild(menu); - - *c = FALSE; -} - -UIMENU ui_contextmenu(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - if(ct->current) { - return ui_contextmenu_w(obj, ct->current); - } else { - return NULL; // TODO: warn - } -} - -UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { - UiContainer *ct = uic_get_current_container(obj); - - Widget menu = XmCreatePopupMenu(widget, "popup_menu", NULL, 0); - ct->menu = menu; - - XtAddEventHandler(widget, ButtonPressMask, FALSE, ui_popup_handler, menu); - - return menu; -} - -void ui_contextmenu_popup(UIMENU menu) { - -} - -void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { - ui_widget_menuitem_gr(obj, label, f, userdata, -1); -} - -void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - UcxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); - } - va_end(ap); - - // create menuitem - Arg args[4]; - XmString labelstr = XmStringCreateLocalized(label); - XtSetArg(args[0], XmNlabelString, labelstr); - - Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); - XtManageChild(item); - XmStringFree(labelstr); -} - -void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { - ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); -} - -void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - UcxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); - } - va_end(ap); - - // create menuitem - UiStockItem *stockItem = ui_get_stock_item(stockid); - Arg args[4]; - XmString labelstr = XmStringCreateLocalized(stockItem->label); - XtSetArg(args[0], XmNlabelString, labelstr); - - Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); - XtManageChild(item); - XmStringFree(labelstr); -}
--- a/ui/motif/menu.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/menu.h Sat Jan 04 16:38:48 2025 +0100 @@ -30,94 +30,41 @@ #define MENU_H #include "../ui/menu.h" -#include <ucx/list.h> +#include "../common/menu.h" +#include "../common/context.h" #ifdef __cplusplus extern "C" { #endif -typedef struct UiMenuItemI UiMenuItemI; -typedef struct UiMenu UiMenu; -typedef struct UiMenuItem UiMenuItem; -typedef struct UiStMenuItem UiStMenuItem; -typedef struct UiCheckItem UiCheckItem; -typedef struct UiCheckItemNV UiCheckItemNV; -typedef struct UiMenuItemList UiMenuItemList; - typedef struct UiActiveMenuItemList UiActiveMenuItemList; - -typedef int(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*); - -struct UiMenuItemI { - ui_menu_add_f add_to; +struct UiActiveMenuItemList { + UiObject *object; + Widget menu; + int index; + int oldcount; + UiVar *var; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; + bool addseparator; }; - -struct UiMenu { - UiMenuItemI item; - char *label; - UcxList *items; - UiMenu *parent; -}; - -struct UiMenuItem { - UiMenuItemI item; - ui_callback callback; - char *label; - void *userdata; - UcxList *groups; -}; - -struct UiStMenuItem { - UiMenuItemI item; - ui_callback callback; - char *stockid; - void *userdata; - UcxList *groups; -}; + +typedef void(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*); + +void ui_create_menubar(UiObject *obj, Widget window); +void ui_add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -struct UiCheckItem { - UiMenuItemI item; - char *label; - ui_callback callback; - void *userdata; -}; - -struct UiCheckItemNV { - UiMenuItemI item; - char *label; - char *varname; -}; - -struct UiMenuItemList { - UiMenuItemI item; - ui_callback callback; - void *userdata; - UiList *list; -}; - -struct UiActiveMenuItemList { - UiObject *object; - Widget menu; - int index; - int oldcount; - UiList *list; - ui_callback callback; - void *userdata; -}; - -void ui_create_menubar(UiObject *obj); - -int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuseparator_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_checkitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_checkitemnv_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_list_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); +void add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); +void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); +void add_menuitem_st_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj); +void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj); +void add_checkitem_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj); +void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj); +void add_checkitemnv_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj); +void add_menuitem_list_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj); void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list); -void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata); - #ifdef __cplusplus }
--- a/ui/motif/objs.mk Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/objs.mk Sat Jan 04 16:38:48 2025 +0100 @@ -39,11 +39,11 @@ MOTIFOBJ += label.o MOTIFOBJ += text.o MOTIFOBJ += list.o -MOTIFOBJ += tree.o MOTIFOBJ += graphics.o MOTIFOBJ += range.o MOTIFOBJ += dnd.o MOTIFOBJ += image.o +MOTIFOBJ += Grid.o TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%) TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c)
--- a/ui/motif/range.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/range.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,108 +31,6 @@ #include "range.h" #include "container.h" -#include <ucx/mempool.h> #include "../common/context.h" #include "../common/object.h" - -static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) { - UiContainer *ct = uic_get_current_container(obj); - - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNorientation, orientation == UI_HORIZONTAL ? XmHORIZONTAL : XmVERTICAL); - n++; - XtSetArg (args[n], XmNmaximum, 10); - n++; - XtSetArg (args[n], XmNsliderSize, 1); - n++; - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget scrollbar = XmCreateScrollBar(parent, "scrollbar", args, n); - XtManageChild(scrollbar); - ct->add(ct, scrollbar); - - if(range) { - range->get = ui_scrollbar_get; - range->set = ui_scrollbar_set; - range->setrange = ui_scrollbar_setrange; - range->setextent = ui_scrollbar_setextent; - range->obj = scrollbar; - } - - if(f) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = userdata; - event->callback = f; - event->value = 0; - XtAddCallback( - scrollbar, - XmNvalueChangedCallback, - (XtCallbackProc)ui_scrollbar_callback, - event); - } - - return scrollbar; -} - -UIWIDGET ui_hscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) { - return ui_scrollbar(obj, UI_HORIZONTAL, range, f, userdata); -} - -UIWIDGET ui_vscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) { - return ui_scrollbar(obj, UI_VERTICAL, range, f, userdata); -} - -void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata) { - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.intval = event->value; - event->callback(&e, event->userdata); -} - -double ui_scrollbar_get(UiRange *range) { - int intval; - XtVaGetValues( - range->obj, - XmNvalue, - &intval, - NULL); - double value = (double)intval / 10; - range->value = value; - return value; -} - -void ui_scrollbar_set(UiRange *range, double value) { - XtVaSetValues( - range->obj, - XmNvalue, - (int)(value * 10), - NULL); - range->value = value; -} - -void ui_scrollbar_setrange(UiRange *range, double min, double max) { - XtVaSetValues( - range->obj, - XmNminimum, - (int)(min * 10), - XmNmaximum, - (int)(max * 10), - NULL); - range->min = min; - range->max = max; -} - -void ui_scrollbar_setextent(UiRange *range, double extent) { - XtVaSetValues( - range->obj, - XmNsliderSize, - (int)(extent * 10), - NULL); - range->extent = extent; -}
--- a/ui/motif/range.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/range.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,11 +36,6 @@ extern "C" { #endif -void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata); -double ui_scrollbar_get(UiRange *range); -void ui_scrollbar_set(UiRange *range, double value); -void ui_scrollbar_setrange(UiRange *range, double min, double max); -void ui_scrollbar_setextent(UiRange *range, double extent); #ifdef __cplusplus
--- a/ui/motif/stock.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/stock.c Sat Jan 04 16:38:48 2025 +0100 @@ -31,46 +31,6 @@ #include "stock.h" #include "../ui/properties.h" -#include <ucx/map.h> - -static UcxMap *stock_items; +#include <cx/hash_map.h> -void ui_stock_init() { - stock_items = ucx_map_new(64); - - ui_add_stock_item(UI_STOCK_NEW, "New", "Ctrl<Key>N", "Ctrl+N", NULL); - ui_add_stock_item(UI_STOCK_OPEN, "Open", "Ctrl<Key>O", "Ctrl+O", NULL); - ui_add_stock_item(UI_STOCK_SAVE, "Save", "Ctrl<Key>S", "Ctrl+S", NULL); - ui_add_stock_item(UI_STOCK_SAVE_AS, "Save as ...", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_REVERT_TO_SAVED, "Revert to saved", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_CLOSE, "Close", "Ctrl<Key>W", "Ctrl+W", NULL); - ui_add_stock_item(UI_STOCK_UNDO, "Undo", "Ctrl<Key>Z", "Ctrl+Z", NULL); - ui_add_stock_item(UI_STOCK_REDO, "Redo", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_GO_BACK, "Back", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_GO_FORWARD, "Forward", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_CUT, "Cut", "Ctrl<Key>X", "Ctrl+X", NULL); - ui_add_stock_item(UI_STOCK_COPY, "Copy", "Ctrl<Key>C", "Ctrl+C", NULL); - ui_add_stock_item(UI_STOCK_PASTE, "Paste", "Ctrl<Key>V", "Ctrl+V", NULL); - ui_add_stock_item(UI_STOCK_DELETE, "Delete", NULL, NULL, NULL); -} -void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon) { - UiStockItem *i = malloc(sizeof(UiStockItem)); - i->label = label; - i->accelerator = accelerator; - i->accelerator_label = accelerator_label; - // TODO: icon - - ucx_map_cstr_put(stock_items, id, i); -} - -UiStockItem* ui_get_stock_item(char *id) { - UiStockItem *item = ucx_map_cstr_get(stock_items, id); - if(item) { - char *label = uistr_n(id); - if(label) { - item->label = label; - } - } - return item; -}
--- a/ui/motif/stock.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/stock.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,18 +35,7 @@ extern "C" { #endif -typedef struct UiStockItem { - char *label; - char *accelerator; - char *accelerator_label; - // TODO: icon -} UiStockItem; - -void ui_stock_init(); -void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon); - -UiStockItem* ui_get_stock_item(char *id); #ifdef __cplusplus }
--- a/ui/motif/text.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/text.c Sat Jan 04 16:38:48 2025 +0100 @@ -28,461 +28,666 @@ #include <stdio.h> #include <stdlib.h> +#include <unistd.h> #include "text.h" #include "container.h" - -UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) { - UiContainer *ct = uic_get_current_container(obj); - int n = 0; - Arg args[16]; - - //XtSetArg(args[n], XmNeditable, TRUE); - //n++; - XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); - n++; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget text_area = XmCreateScrolledText(parent, "text_area", args, n); - ct->add(ct, XtParent(text_area)); - XtManageChild(text_area); - - UiTextArea *uitext = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiTextArea)); - uitext->ctx = obj->ctx; - uitext->last_selection_state = 0; - XtAddCallback( - text_area, - XmNmotionVerifyCallback, - (XtCallbackProc)ui_text_selection_callback, - uitext); - - // bind value - if(var->value) { - UiText *value = var->value; - if(value->value.ptr) { - XmTextSetString(text_area, value->value.ptr); - value->value.free(value->value.ptr); - } - - value->set = ui_textarea_set; - value->get = ui_textarea_get; - value->getsubstr = ui_textarea_getsubstr; - value->insert = ui_textarea_insert; - value->setposition = ui_textarea_setposition; - value->position = ui_textarea_position; - value->selection = ui_textarea_selection; - value->length = ui_textarea_length; - value->value.ptr = NULL; - value->obj = text_area; - - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); - } - - XtAddCallback( - text_area, - XmNmodifyVerifyCallback, - (XtCallbackProc)ui_text_modify_callback, - var); - } - - return text_area; -} - -UIWIDGET ui_textarea(UiObject *obj, UiText *value) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - return ui_textarea_var(obj, var); -} - -UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT); - if(var) { - return ui_textarea_var(obj, var); - } else { - // TODO: error - } - return NULL; -} +#include <cx/string.h> -char* ui_textarea_get(UiText *text) { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - char *str = XmTextGetString(text->obj); - text->value.ptr = str; - text->value.free = (ui_freefunc)XtFree; - return str; -} - -void ui_textarea_set(UiText *text, char *str) { - XmTextSetString(text->obj, str); - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - text->value.ptr = NULL; -} - -char* ui_textarea_getsubstr(UiText *text, int begin, int end) { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - int length = end - begin; - char *str = XtMalloc(length + 1); - XmTextGetSubstring(text->obj, begin, length, length + 1, str); - text->value.ptr = str; - text->value.free = (ui_freefunc)XtFree; - return str; -} - -void ui_textarea_insert(UiText *text, int pos, char *str) { - text->value.ptr = NULL; - XmTextInsert(text->obj, pos, str); - if(text->value.ptr) { - text->value.free(text->value.ptr); - } -} - -void ui_textarea_setposition(UiText *text, int pos) { - XmTextSetInsertionPosition(text->obj, pos); -} - -int ui_textarea_position(UiText *text) { - long begin; - long end; - XmTextGetSelectionPosition(text->obj, &begin, &end); - text->pos = begin; - return text->pos; -} - -void ui_textarea_selection(UiText *text, int *begin, int *end) { - XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end); -} - -int ui_textarea_length(UiText *text) { - return (int)XmTextGetLastPosition(text->obj); -} - - -void ui_text_set(UiText *text, char *str) { - if(text->set) { - text->set(text, str); - } else { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - text->value.ptr = XtNewString(str); - text->value.free = (ui_freefunc)XtFree; - } -} - -char* ui_text_get(UiText *text) { - if(text->get) { - return text->get(text); - } else { - return text->value.ptr; - } -} -UiUndoMgr* ui_create_undomgr() { - UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr)); - mgr->begin = NULL; - mgr->cur = NULL; - mgr->length = 0; - mgr->event = 1; - return mgr; -} - -void ui_text_selection_callback( - Widget widget, - UiTextArea *textarea, - XtPointer data) -{ - long left = 0; - long right = 0; - XmTextGetSelectionPosition(widget, &left, &right); - int sel = left < right ? 1 : 0; - if(sel != textarea->last_selection_state) { - if(sel) { - ui_set_group(textarea->ctx, UI_GROUP_SELECTION); - } else { - ui_unset_group(textarea->ctx, UI_GROUP_SELECTION); - } - } - textarea->last_selection_state = sel; -} +/* ------------------------------ Text Field ------------------------------ */ -void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) { - UiText *value = var->value; - if(!value->obj) { - // TODO: bug, fix - return; - } - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); - } - - XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data; - int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE; - UiUndoMgr *mgr = value->undomgr; - if(!mgr->event) { - return; - } - - char *text = txv->text->ptr; - int length = txv->text->length; - - if(mgr->cur) { - UcxList *elm = mgr->cur->next; - if(elm) { - mgr->cur->next = NULL; - while(elm) { - elm->prev = NULL; - UcxList *next = elm->next; - ui_free_textbuf_op(elm->data); - free(elm); - elm = next; - } - } - - if(type == UI_TEXTBUF_INSERT) { - UiTextBufOp *last_op = mgr->cur->data; - if( - last_op->type == UI_TEXTBUF_INSERT && - ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) - { - // append text to last op - int ln = last_op->len; - char *newtext = malloc(ln + length + 1); - memcpy(newtext, last_op->text, ln); - memcpy(newtext+ln, text, length); - newtext[ln+length] = '\0'; - - last_op->text = newtext; - last_op->len = ln + length; - last_op->end += length; - - return; - } - } - } - - char *str; - if(type == UI_TEXTBUF_INSERT) { - str = malloc(length + 1); - memcpy(str, text, length); - str[length] = 0; - } else { - length = txv->endPos - txv->startPos; - str = malloc(length + 1); - XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str); - } +static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs args, int frameless, int password) { + Arg xargs[16]; + int n = 0; - UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); - op->type = type; - op->start = txv->startPos; - op->end = txv->endPos + 1; - op->len = length; - op->text = str; - - UcxList *elm = ucx_list_append(NULL, op); - mgr->cur = elm; - mgr->begin = ucx_list_concat(mgr->begin, elm); -} - -int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) { - // return 1 if oldstr + newstr are one word - - int has_space = 0; - for(int i=0;i<oldlen;i++) { - if(oldstr[i] < 33) { - has_space = 1; - break; - } - } - - for(int i=0;i<newlen;i++) { - if(has_space && newstr[i] > 32) { - return 1; - } - } - - return 0; -} - -void ui_free_textbuf_op(UiTextBufOp *op) { - if(op->text) { - free(op->text); - } - free(op); -} - - -void ui_text_undo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; - - if(mgr->cur) { - UiTextBufOp *op = mgr->cur->data; - mgr->event = 0; - switch(op->type) { - case UI_TEXTBUF_INSERT: { - XmTextReplace(value->obj, op->start, op->end, ""); - break; - } - case UI_TEXTBUF_DELETE: { - XmTextInsert(value->obj, op->start, op->text); - break; - } - } - mgr->event = 1; - mgr->cur = mgr->cur->prev; - } -} - -void ui_text_redo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; - - UcxList *elm = NULL; - if(mgr->cur) { - if(mgr->cur->next) { - elm = mgr->cur->next; - } - } else if(mgr->begin) { - elm = mgr->begin; - } - - if(elm) { - UiTextBufOp *op = elm->data; - mgr->event = 0; - switch(op->type) { - case UI_TEXTBUF_INSERT: { - XmTextInsert(value->obj, op->start, op->text); - break; - } - case UI_TEXTBUF_DELETE: { - XmTextReplace(value->obj, op->start, op->end, ""); - break; - } - } - mgr->event = 1; - mgr->cur = elm; - } -} - - -/* ------------------------- textfield ------------------------- */ - -static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) { - UiContainer *ct = uic_get_current_container(obj); - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT); - n++; - if(width > 0) { - XtSetArg(args[n], XmNcolumns, width / 2 + 1); - n++; - } if(frameless) { - XtSetArg(args[n], XmNshadowThickness, 0); + XtSetArg(xargs[n], XmNshadowThickness, 0); n++; } if(password) { // TODO } - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget textfield = XmCreateText(parent, "text_field", args, n); - ct->add(ct, textfield); + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + char *name = args.name ? (char*)args.name : "textfield"; + Widget textfield = XmCreateTextField(parent, name, xargs, n); XtManageChild(textfield); - // bind value - if(value) { + ui_set_widget_groups(obj->ctx, textfield, args.groups); + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_STRING); + if(var) { + UiString *value = (UiString*)var->value; + value->obj = textfield; + value->get = ui_textfield_get; + value->set = ui_textfield_set; + if(value->value.ptr) { - XmTextSetString(textfield, value->value.ptr); - value->value.free(value->value.ptr); + ui_textfield_set(value, value->value.ptr); } - - value->set = ui_textfield_set; - value->get = ui_textfield_get; - value->value.ptr = NULL; - value->obj = textfield; } return textfield; } -static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING); - if(var) { - UiString *value = var->value; - return ui_textfield(obj, value); - } else { - // TODO: error - } - return NULL; +UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs args) { + return create_textfield(obj, args, FALSE, FALSE); } -UIWIDGET ui_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, FALSE, varname); -} - -UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, FALSE, varname); +UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { + return create_textfield(obj, args, TRUE, FALSE); } -UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, TRUE, FALSE, value); -} - -UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, TRUE, FALSE, varname); -} - -UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, TRUE, value); +UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { + return create_textfield(obj, args, FALSE, FALSE); } -UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, TRUE, varname); -} - -UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, TRUE, value); -} - -UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, TRUE, varname); -} - - char* ui_textfield_get(UiString *str) { - if(str->value.ptr) { - str->value.free(str->value.ptr); - } - char *value = XmTextGetString(str->obj); + str->value.free(str->value.ptr); + char *value = XmTextFieldGetString(str->obj); str->value.ptr = value; str->value.free = (ui_freefunc)XtFree; return value; } -void ui_textfield_set(UiString *str, char *value) { - XmTextSetString(str->obj, value); - if(str->value.ptr) { - str->value.free(str->value.ptr); +void ui_textfield_set(UiString *str, const char *value) { + XmTextFieldSetString(str->obj, (void*)value); + str->value.ptr = NULL; + str->value.free(str->value.ptr); +} + + + + + +/* -------------------- path bar -------------------- */ + +#define XNECreateText(parent,name,args,count) XmCreateTextField(parent,name,args,count) +#define XNETextSetString(widget,value) XmTextFieldSetString(widget,value) +#define XNETextGetString(widget) XmTextFieldGetString(widget) +#define XNETextGetLastPosition(widget) XmTextFieldGetLastPosition(widget) +#define XNETextSetInsertionPosition(widget, i) XmTextFieldSetInsertionPosition(widget, i) +#define XNETextSetSelection(w, f, l, t) XmTextFieldSetSelection(w, f, l, t) + +typedef void(*updatedir_callback)(void*,char*,int); + +typedef struct PathBar { + Widget widget; + Widget textfield; + + Widget focus_widget; + + Widget left; + Widget right; + Dimension lw; + Dimension rw; + + int shift; + + UiPathElm *current_pathelms; + Widget *pathSegments; + size_t numSegments; + size_t segmentAlloc; + + char *path; + int selection; + Boolean input; + + int focus; + + updatedir_callback updateDir; + void *updateDirData; + + ui_pathelm_func getpathelm; + void *getpathelmdata; +} PathBar; + +void PathBarSetPath(PathBar *bar, const char *path); + +void pathbar_resize(Widget w, PathBar *p, XtPointer d) +{ + Dimension width, height; + XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL); + + Dimension *segW = (void*)XtCalloc(p->numSegments, sizeof(Dimension)); + + Dimension maxHeight = 0; + + /* get width/height from all widgets */ + Dimension pathWidth = 0; + for(int i=0;i<p->numSegments;i++) { + Dimension segWidth; + Dimension segHeight; + XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL); + segW[i] = segWidth; + pathWidth += segWidth; + if(segHeight > maxHeight) { + maxHeight = segHeight; + } + } + Dimension tfHeight; + XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL); + if(tfHeight > maxHeight) { + maxHeight = tfHeight; + } + + Boolean arrows = False; + if(pathWidth + 10 > width) { + arrows = True; + pathWidth += p->lw + p->rw; + } + + /* calc max visible widgets */ + int start = 0; + if(arrows) { + Dimension vis = p->lw+p->rw; + for(int i=p->numSegments;i>0;i--) { + Dimension segWidth = segW[i-1]; + if(vis + segWidth + 10 > width) { + start = i; + arrows = True; + break; + } + vis += segWidth; + } + } else { + p->shift = 0; + } + + int leftShift = 0; + if(p->shift < 0) { + if(start + p->shift < 0) { + leftShift = start; + start = 0; + p->shift = -leftShift; + } else { + leftShift = -p->shift; /* negative shift */ + start += p->shift; + } + } + + int x = 0; + if(arrows) { + XtManageChild(p->left); + XtManageChild(p->right); + x = p->lw; + } else { + XtUnmanageChild(p->left); + XtUnmanageChild(p->right); + } + + for(int i=0;i<p->numSegments;i++) { + if(i >= start && i < p->numSegments - leftShift && !p->input) { + XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); + x += segW[i]; + XtManageChild(p->pathSegments[i]); + } else { + XtUnmanageChild(p->pathSegments[i]); + } + } + + if(arrows) { + XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL); + XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL); } - str->value.ptr = NULL; + + free(segW); + + Dimension rw, rh; + XtMakeResizeRequest(w, width, maxHeight, &rw, &rh); + + XtVaSetValues(p->textfield, XmNwidth, rw, XmNheight, rh, NULL); +} + +static void pathbarActivateTF(PathBar *p) +{ + XtUnmanageChild(p->left); + XtUnmanageChild(p->right); + XNETextSetSelection(p->textfield, 0, XNETextGetLastPosition(p->textfield), 0); + XtManageChild(p->textfield); + p->input = 1; + + XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT); + + pathbar_resize(p->widget, p, NULL); +} + +void PathBarActivateTextfield(PathBar *p) +{ + p->focus = 1; + pathbarActivateTF(p); +} + +void pathbar_input(Widget w, PathBar *p, XtPointer c) +{ + XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; + XEvent *xevent = cbs->event; + + if (cbs->reason == XmCR_INPUT) { + if (xevent->xany.type == ButtonPress) { + p->focus = 0; + pathbarActivateTF(p); + } + } +} + +void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c) +{ + if(--p->focus < 0) { + p->input = False; + XtUnmanageChild(p->textfield); + } +} + +static cxmutstr concat_path_s(cxstring base, cxstring path) { + if(!path.ptr) { + path = CX_STR(""); + } + + int add_separator = 0; + if(base.length != 0 && base.ptr[base.length-1] == '/') { + if(path.ptr[0] == '/') { + base.length--; + } + } else { + if(path.length == 0 || path.ptr[0] != '/') { + add_separator = 1; + } + } + + cxmutstr url; + if(add_separator) { + url = cx_strcat(3, base, CX_STR("/"), path); + } else { + url = cx_strcat(2, base, path); + } + + return url; +} + +static char* ConcatPath(const char *path1, const char *path2) { + return concat_path_s(cx_str(path1), cx_str(path2)).ptr; +} + +void pathbar_pathinput(Widget w, PathBar *p, XtPointer d) +{ + char *newpath = XNETextGetString(p->textfield); + if(newpath) { + if(newpath[0] == '~') { + char *p = newpath+1; + char *home = getenv("HOME"); + char *cp = ConcatPath(home, p); + XtFree(newpath); + newpath = cp; + } else if(newpath[0] != '/') { + char curdir[2048]; + curdir[0] = 0; + getcwd(curdir, 2048); + char *cp = ConcatPath(curdir, newpath); + XtFree(newpath); + newpath = cp; + } + + /* update path */ + PathBarSetPath(p, newpath); + if(p->updateDir) { + p->updateDir(p->updateDirData, newpath, -1); + } + XtFree(newpath); + + /* hide textfield and show path as buttons */ + XtUnmanageChild(p->textfield); + pathbar_resize(p->widget, p, NULL); + + if(p->focus_widget) { + XmProcessTraversal(p->focus_widget, XmTRAVERSE_CURRENT); + } + } +} + +void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) +{ + p->shift--; + pathbar_resize(p->widget, p, NULL); +} + +void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) +{ + if(p->shift < 0) { + p->shift++; + } + pathbar_resize(p->widget, p, NULL); +} + +static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) { + PathBar *pb = data; + if(event->type == KeyReleaseMask) { + if(event->xkey.keycode == 9) { + XtUnmanageChild(pb->textfield); + pathbar_resize(pb->widget, pb, NULL); + *dispatch = False; + } else if(event->xkey.keycode == 36) { + pathbar_pathinput(pb->textfield, pb, NULL); + *dispatch = False; + } + } } +PathBar* CreatePathBar(Widget parent, ArgList args, int n) +{ + PathBar *bar = (PathBar*)XtMalloc(sizeof(PathBar)); + bar->path = NULL; + bar->updateDir = NULL; + bar->updateDirData = NULL; + + bar->focus_widget = NULL; + + bar->getpathelm = NULL; + bar->getpathelmdata = NULL; + bar->current_pathelms = NULL; + + bar->shift = 0; + + XtSetArg(args[n], XmNmarginWidth, 0); n++; + XtSetArg(args[n], XmNmarginHeight, 0); n++; + bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n); + XtAddCallback( + bar->widget, + XmNresizeCallback, + (XtCallbackProc)pathbar_resize, + bar); + XtAddCallback( + bar->widget, + XmNinputCallback, + (XtCallbackProc)pathbar_input, + bar); + + Arg a[4]; + XtSetArg(a[0], XmNshadowThickness, 0); + XtSetArg(a[1], XmNx, 0); + XtSetArg(a[2], XmNy, 0); + bar->textfield = XNECreateText(bar->widget, "pbtext", a, 3); + bar->input = 0; + XtAddCallback( + bar->textfield, + XmNlosingFocusCallback, + (XtCallbackProc)pathbar_losingfocus, + bar); + XtAddCallback(bar->textfield, XmNactivateCallback, + (XtCallbackProc)pathbar_pathinput, bar); + XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar); + + XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT); + bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); + XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT); + bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1); + XtAddCallback( + bar->left, + XmNactivateCallback, + (XtCallbackProc)pathbar_shift_left, + bar); + XtAddCallback( + bar->right, + XmNactivateCallback, + (XtCallbackProc)pathbar_shift_right, + bar); + + Pixel bg; + XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL); + XtVaSetValues(bar->widget, XmNbackground, bg, NULL); + + XtManageChild(bar->left); + XtManageChild(bar->right); + + XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL); + XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL); + + bar->segmentAlloc = 16; + bar->numSegments = 0; + bar->pathSegments = (Widget*)XtCalloc(16, sizeof(Widget)); + + bar->selection = 0; + + return bar; +} + +void PathBarChangeDir(Widget w, PathBar *bar, XtPointer c) +{ + XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False); + + int i; + for(i=0;i<bar->numSegments;i++) { + if(bar->pathSegments[i] == w) { + bar->selection = i; + XmToggleButtonSetState(w, True, False); + break; + } + } + + UiPathElm elm = bar->current_pathelms[i]; + cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len)); + if(bar->updateDir) { + bar->updateDir(bar->updateDirData, name.ptr, i); + } + free(name.ptr); +} + +static void ui_pathelm_destroy(UiPathElm *elms, size_t nelm) { + for(int i=0;i<nelm;i++) { + free(elms[i].name); + free(elms[i].path); + } + free(elms); +} + +void PathBarSetPath(PathBar *bar, const char *path) +{ + if(bar->path) { + free(bar->path); + } + bar->path = strdup(path); + + for(int i=0;i<bar->numSegments;i++) { + XtDestroyWidget(bar->pathSegments[i]); + } + XtUnmanageChild(bar->textfield); + XtManageChild(bar->left); + XtManageChild(bar->right); + bar->input = False; + + Arg args[4]; + XmString str; + + bar->numSegments = 0; + + ui_pathelm_destroy(bar->current_pathelms, bar->numSegments); + size_t nelm = 0; + UiPathElm* path_elm = bar->getpathelm(bar->path, strlen(bar->path), &nelm, bar->getpathelmdata); + if (!path_elm) { + return; + } + bar->current_pathelms = path_elm; + bar->numSegments = nelm; + bar->pathSegments = realloc(bar->pathSegments, nelm * sizeof(Widget*)); + + for(int i=0;i<nelm;i++) { + UiPathElm elm = path_elm[i]; + + cxmutstr name = cx_strdup(cx_strn(elm.name, elm.name_len)); + str = XmStringCreateLocalized(elm.name); + free(name.ptr); + + XtSetArg(args[0], XmNlabelString, str); + XtSetArg(args[1], XmNfillOnSelect, True); + XtSetArg(args[2], XmNindicatorOn, False); + Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3); + XtAddCallback( + button, + XmNvalueChangedCallback, + (XtCallbackProc)PathBarChangeDir, + bar); + XmStringFree(str); + + bar->pathSegments[i] = button; + } + + bar->selection = bar->numSegments-1; + XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False); + + XNETextSetString(bar->textfield, (char*)path); + XNETextSetInsertionPosition(bar->textfield, XNETextGetLastPosition(bar->textfield)); + + pathbar_resize(bar->widget, bar, NULL); +} + +void PathBarDestroy(PathBar *pathbar) { + if(pathbar->path) { + XtFree(pathbar->path); + } + XtFree((void*)pathbar->pathSegments); + XtFree((void*)pathbar); +} + + +/* ---------------------------- Path Text Field ---------------------------- */ + +static void destroy_pathbar(Widget w, XtPointer *data, XtPointer d) { + PathBar *pathbar = (PathBar*)data; + // TODO: check if there is somonething missing + XtFree((void*)pathbar->pathSegments); + XtFree((void*)pathbar); +} + +// TODO: move to common +static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return NULL; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} + +static void pathbar_activate(void *data, char *path, int index) { + UiEventData *event = data; + UiEvent evt; + evt.obj = event->obj; + evt.window = evt.obj->window; + evt.document = evt.obj->ctx->document; + evt.eventdata = path; + evt.intval = index; + event->callback(&evt, event->userdata); +} + +UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + // TODO: name + + + PathBar *pathbar = CreatePathBar(parent, xargs, n); + if(!args.getpathelm) { + pathbar->getpathelm= default_pathelm_func; + } else { + pathbar->getpathelm = args.getpathelm; + pathbar->getpathelmdata = args.getpathelmdata; + } + + + XtManageChild(pathbar->widget); + ctn->add(ctn, pathbar->widget); + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = pathbar; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + + if(value->value.ptr) { + char *str = strdup(value->value.ptr); + ui_string_set(value, str); + free(str); + } + } + + if(args.onactivate) { + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = args.onactivate; + eventdata->userdata = args.onactivatedata; + eventdata->obj = obj; + eventdata->value = 0; + + pathbar->updateDir = pathbar_activate; + pathbar->updateDirData = eventdata; + + XtAddCallback( + pathbar->widget, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + eventdata); + } + + XtAddCallback( + pathbar->widget, + XmNdestroyCallback, + (XtCallbackProc)destroy_pathbar, + pathbar); + + return pathbar->widget; +} + +char* ui_path_textfield_get(UiString *str) { + PathBar *pathbar = str->obj; + str->value.free(str->value.ptr); + char *value = XmTextFieldGetString(pathbar->textfield); + str->value.ptr = value; + str->value.free = (ui_freefunc)XtFree; + return value; +} + +void ui_path_textfield_set(UiString *str, const char *value) { + PathBarSetPath(str->obj, value); +}
--- a/ui/motif/text.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/text.h Sat Jan 04 16:38:48 2025 +0100 @@ -31,55 +31,19 @@ #include "../ui/text.h" #include "toolkit.h" -#include <ucx/list.h> +#include <cx/list.h> #ifdef __cplusplus extern "C" { #endif -#define UI_TEXTBUF_INSERT 0 -#define UI_TEXTBUF_DELETE 1 -typedef struct UiTextBufOp { - int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE - int start; - int end; - int len; - char *text; -} UiTextBufOp; - -typedef struct UiUndoMgr { - UcxList *begin; - UcxList *cur; - int length; - int event; -} UiUndoMgr; +char* ui_textfield_get(UiString *str); +void ui_textfield_set(UiString *str, const char *value); -typedef struct UiTextArea { - UiContext *ctx; - int last_selection_state; -} UiTextArea; +char* ui_path_textfield_get(UiString *str); +void ui_path_textfield_set(UiString *str, const char *value); + -char* ui_textarea_get(UiText *text); -void ui_textarea_set(UiText *text, char *str); -char* ui_textarea_getsubstr(UiText *text, int begin, int end); -void ui_textarea_insert(UiText *text, int pos, char *str); -void ui_textarea_setposition(UiText *text, int pos); -int ui_textarea_position(UiText *text); -void ui_textarea_selection(UiText *text, int *begin, int *end); -int ui_textarea_length(UiText *text); - -UiUndoMgr* ui_create_undomgr(); -void ui_text_selection_callback( - Widget widget, - UiTextArea *textarea, - XtPointer data); -void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data); -int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen); -void ui_free_textbuf_op(UiTextBufOp *op); - -char* ui_textfield_get(UiString *str); -void ui_textfield_set(UiString *str, char *value); - #ifdef __cplusplus } #endif
--- a/ui/motif/toolbar.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/toolbar.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -35,332 +35,10 @@ #include "button.h" #include "stock.h" #include "list.h" -#include <ucx/mempool.h> + +#include <cx/hash_map.h> +#include <cx/linked_list.h> +#include <cx/array_list.h> + #include "../common/context.h" -static UcxMap *toolbar_items; -static UcxList *defaults; - -void ui_toolbar_init() { - toolbar_items = ucx_map_new(16); -} - -void ui_toolitem(char *name, char *label, ui_callback f, void *userdata) { - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget; - item->label = label; - item->image = NULL; - item->callback = f; - item->userdata = userdata; - item->groups = NULL; - item->isimportant = FALSE; - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *userdata) { - ui_toolitem_stgr(name, stockid, f, userdata, -1); -} - -void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *userdata, ...) { - UiStToolItem *item = malloc(sizeof(UiStToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_widget; - item->stockid = stockid; - item->callback = f; - item->userdata = userdata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata) { - // TODO - - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget; - item->label = label; - item->image = img; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - ucx_map_cstr_put(toolbar_items, name, item); -} - - -void ui_toolitem_toggle_stgr(char *name, char *stockid, ui_callback f, void *udata, ...) { - // TODO - - UiStToolItem *item = malloc(sizeof(UiStToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_toggle_widget; - item->stockid = stockid; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, udata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolitem_toggle_imggr(char *name, char *label, char *img, ui_callback f, void *udata, ...) { - // TODO - - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = label; - item->image = img; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, udata); - int group; - while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); - } - va_end(ap); - - ucx_map_cstr_put(toolbar_items, name, item); -} - -void ui_toolbar_combobox( - char *name, - UiList *list, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBox *cb = malloc(sizeof(UiToolbarComboBox)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox; - cb->list = list; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - ucx_map_cstr_put(toolbar_items, name, cb); -} - -void ui_toolbar_combobox_str( - char *name, - UiList *list, - ui_callback f, - void *udata) -{ - ui_toolbar_combobox(name, list, ui_strmodel_getvalue, f, udata); -} - -void ui_toolbar_combobox_nv( - char *name, - char *listname, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBoxNV *cb = malloc(sizeof(UiToolbarComboBoxNV)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox_nv; - cb->listname = listname; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - ucx_map_cstr_put(toolbar_items, name, cb); -} - - -void ui_toolbar_add_default(char *name) { - char *s = strdup(name); - defaults = ucx_list_append(defaults, s); -} - -Widget ui_create_toolbar(UiObject *obj, Widget parent) { - if(!defaults) { - return NULL; - } - - Arg args[8]; - XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM); - Widget frame = XmCreateFrame(parent, "toolbar_frame", args, 5); - - XtSetArg(args[0], XmNorientation, XmHORIZONTAL); - XtSetArg(args[1], XmNpacking, XmPACK_TIGHT); - XtSetArg(args[2], XmNspacing, 1); - Widget toolbar = XmCreateRowColumn (frame, "toolbar", args, 3); - - UCX_FOREACH(elm, defaults) { - UiToolItemI *item = ucx_map_cstr_get(toolbar_items, elm->data); - if(item) { - item->add_to(toolbar, item, obj); - } else if(!strcmp(elm->data, "@separator")) { - // TODO - } else { - fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", elm->data); - } - } - - XtManageChild(toolbar); - XtManageChild(frame); - - return frame; -} - -void add_toolitem_widget(Widget parent, UiToolItem *item, UiObject *obj) { - Arg args[4]; - - XmString label = XmStringCreateLocalized(item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_st_widget(Widget parent, UiStToolItem *item, UiObject *obj) { - Arg args[8]; - - UiStockItem *stock_item = ui_get_stock_item(item->stockid); - - XmString label = XmStringCreateLocalized(stock_item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_toggle_widget(Widget parent, UiToolItem *item, UiObject *obj) { - Arg args[8]; - - XmString label = XmStringCreateLocalized(item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - XtSetArg(args[3], XmNindicatorOn, XmINDICATOR_NONE); - Widget button = XmCreateToggleButton(parent, "toolbar_toggle_button", args, 4); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)ui_toggle_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_st_toggle_widget(Widget parent, UiStToolItem *item, UiObject *obj) { - -} - -void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj) { - UiListView *listview = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiListView)); - - UiVar *var = ucx_mempool_malloc(obj->ctx->mempool, sizeof(UiVar)); - var->value = item->list; - var->type = UI_VAR_SPECIAL; - - Arg args[8]; - XtSetArg(args[0], XmNshadowThickness, 1); - XtSetArg(args[1], XmNindicatorOn, XmINDICATOR_NONE); - XtSetArg(args[2], XmNtraversalOn, FALSE); - XtSetArg(args[3], XmNwidth, 120); - Widget combobox = XmCreateDropDownList(tb, "toolbar_combobox", args, 4); - XtManageChild(combobox); - listview->widget = combobox; - listview->list = var; - listview->getvalue = item->getvalue; - - ui_listview_update(NULL, listview); - - if(item->callback) { - // TODO: - - } -} - -void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj) { - -}
--- a/ui/motif/toolbar.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/toolbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,73 +30,13 @@ #define TOOLBAR_H #include "../ui/toolbar.h" -#include <ucx/map.h> -#include <ucx/list.h> +#include <cx/hash_map.h> +#include <cx/linked_list.h> #ifdef __cplusplus extern "C" { #endif -typedef struct UiToolItemI UiToolItemI; -typedef struct UiToolItem UiToolItem; -typedef struct UiStToolItem UiStToolItem; - -typedef struct UiToolbarComboBox UiToolbarComboBox; -typedef struct UiToolbarComboBoxNV UiToolbarComboBoxNV; - -typedef void(*ui_toolbar_add_f)(Widget, UiToolItemI*, UiObject*); - -struct UiToolItemI { - ui_toolbar_add_f add_to; -}; - -struct UiToolItem { - UiToolItemI item; - char *label; - void *image; - ui_callback callback; - void *userdata; - UcxList *groups; - Boolean isimportant; -}; - -struct UiStToolItem { - UiToolItemI item; - char *stockid; - ui_callback callback; - void *userdata; - UcxList *groups; - Boolean isimportant; -}; - -struct UiToolbarComboBox { - UiToolItemI item; - UiList *list; - ui_getvaluefunc getvalue; - ui_callback callback; - void *userdata; -}; - -struct UiToolbarComboBoxNV { - UiToolItemI item; - char *listname; - ui_getvaluefunc getvalue; - ui_callback callback; - void *userdata; -}; - -void ui_toolbar_init(); - -Widget ui_create_toolbar(UiObject *obj, Widget parent); - -void add_toolitem_widget(Widget tb, UiToolItem *item, UiObject *obj); -void add_toolitem_st_widget(Widget tb, UiStToolItem *item, UiObject *obj); -void add_toolitem_toggle_widget(Widget tb, UiToolItem *item, UiObject *obj); -void add_toolitem_st_toggle_widget(Widget tb, UiStToolItem *item, UiObject *obj); - -void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj); -void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj); - #ifdef __cplusplus } #endif
--- a/ui/motif/toolkit.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/toolkit.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -33,15 +33,21 @@ #include "toolkit.h" #include "toolbar.h" +#include "container.h" #include "stock.h" +#include "../common/menu.h" +#include "../common/toolbar.h" #include "../common/document.h" #include "../common/properties.h" -#include <ucx/buffer.h> +#include <cx/buffer.h> + +#include <X11/Intrinsic.h> +#include <Xm/CutPaste.h> static XtAppContext app; static Display *display; static Widget active_window; -static char *application_name; +static const char *application_name; static ui_callback startup_func; static void *startup_data; @@ -68,6 +74,11 @@ "*rt*fontType: FONT_IS_XFT", "*rt*fontName: Sans", "*rt*fontSize: 11", + + "*window_frame.shadowType: SHADOW_ETCHED_OUT", + "*window_frame.shadowThickness: 1", + "*togglebutton.shadowThickness: 1", + "*togglebutton.highlightThickness: 2", NULL }; @@ -76,8 +87,9 @@ read(event_pipe[0], &ptr, sizeof(void*)); } -void ui_init(char *appname, int argc, char **argv) { +void ui_init(const char *appname, int argc, char **argv) { application_name = appname; + uic_init_global_context(); XtToolkitInitialize(); XtSetLanguageProc(NULL, NULL, NULL); @@ -85,15 +97,10 @@ XtAppSetFallbackResources(app, fallback); display = XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv); - char **missing = NULL; - int nm = 0; - char *def = NULL; - XCreateFontSet(display, "-dt-interface system-medium-r-normal-s*utf*", &missing, &nm, &def); uic_docmgr_init(); - ui_toolbar_init(); - ui_stock_init(); - + uic_menu_init(); + uic_toolbar_init(); uic_load_app_properties(); if(pipe(event_pipe)) { @@ -108,11 +115,11 @@ NULL); } -char* ui_appname() { +const char* ui_appname() { return application_name; } -Display* ui_get_display() { +Display* ui_motif_get_display() { return display; } @@ -157,11 +164,12 @@ void ui_show(UiObject *obj) { uic_check_group_widgets(obj->ctx); XtRealizeWidget(obj->widget); - ui_window_dark_theme(XtDisplay(obj->widget), XtWindow(obj->widget)); // TODO: if } -// implemented in window.c -//void ui_close(UiObject *obj) +void ui_close(UiObject *obj) { + +} + void ui_set_enabled(UIWIDGET widget, int enabled) { XtSetSensitive(widget, enabled); @@ -182,17 +190,16 @@ } static Boolean ui_job_finished(void *data) { - printf("WorkProc\n"); UiJob *job = data; - - UiEvent event; - event.obj = job->obj; - event.window = job->obj->window; - event.document = job->obj->ctx->document; - event.intval = 0; - event.eventdata = NULL; - - job->finish_callback(&event, job->finish_data); + if(job->finish_callback) { + UiEvent event; + event.obj = job->obj; + event.window = job->obj->window; + event.document = job->obj->ctx->document; + event.intval = 0; + event.eventdata = NULL; + job->finish_callback(&event, job->finish_data); + } free(job); return TRUE; } @@ -201,11 +208,11 @@ UiJob *job = data; int result = job->job_func(job->job_data); if(!result) { - printf("XtAppAddWorkProc\n"); write(event_pipe[1], &job, sizeof(void*)); // hack XtAppAddWorkProc(app, ui_job_finished, job); } + return NULL; } void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { @@ -220,7 +227,6 @@ } void ui_clipboard_set(char *str) { - printf("copy: {%s}\n", str); int length = strlen(str) + 1; Display *dp = XtDisplayOfObject(active_window); @@ -286,6 +292,9 @@ return active_window; } +/* + * doesn't work with gnome anymore + */ void ui_window_dark_theme(Display *dp, Window window) { Atom atom = XInternAtom(dp, "_GTK_THEME_VARIANT", False); Atom type = XInternAtom(dp, "UTF8_STRING", False); @@ -299,3 +308,23 @@ (const unsigned char*)"dark", 4); } + +void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d) { + free(data); +} + +void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) { + if(!groups) { + return; + } + size_t ngroups = uic_group_array_size(groups); + ui_set_widget_ngroups(ctx, widget, groups, ngroups); +} + +void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); + ui_set_enabled(widget, FALSE); + } +} +
--- a/ui/motif/toolkit.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/toolkit.h Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -38,15 +38,39 @@ extern "C" { #endif -Display* ui_get_display(); - typedef struct UiEventData { UiObject *obj; ui_callback callback; void *userdata; int value; + void *customdata; } UiEventData; +typedef struct UiEventDataExt { + UiObject *obj; + ui_callback callback; + void *userdata; + ui_callback callback2; + void *userdata2; + int value0; + int value1; + int value2; + int value3; + void *customdata0; + void *customdata1; + void *customdata2; + void *customdata3; +} UiEventDataExt; + +typedef struct UiVarEventData { + UiObject *obj; + UiVar *var; + UiObserver **observers; + ui_callback callback; + void *userdata; + int value; +} UiVarEventData; + typedef struct UiJob { UiObject *obj; ui_threadfunc job_func; @@ -60,12 +84,19 @@ void ui_exit_mainloop(); +Display* ui_motif_get_display(void); + void ui_set_active_window(Widget w); Widget ui_get_active_window(); void ui_secondary_event_loop(int *loop); void ui_window_dark_theme(Display *dp, Window window); +void ui_destroy_eventdata(Widget w, XtPointer data, XtPointer d); + +void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) ; +void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups); + #ifdef __cplusplus } #endif
--- a/ui/motif/tree.c Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <inttypes.h> - -#include "tree.h" - -#include "container.h" -#include "../common/object.h" -#include "../common/context.h" -#include <ucx/utils.h> - -UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb) { - // TODO: check if modelinfo is complete - - Arg args[32]; - int n = 0; - - // create scrolled window - UiContainer *ct = uic_get_current_container(obj); - Widget parent = ct->prepare(ct, args, &n, TRUE); - - XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); - n++; - XtSetArg(args[n], XmNshadowThickness, 0); - n++; - Widget scrollw = XmCreateScrolledWindow(parent, "scroll_win", args, n); - ct->add(ct, scrollw); - XtManageChild(scrollw); - - // create table headers - XmStringTable header = (XmStringTable)XtMalloc( - model->columns * sizeof(XmString)); - for(int i=0;i<model->columns;i++) { - header[i] = XmStringCreateLocalized(model->titles[i]); - } - n = 0; - XtSetArg(args[n], XmNdetailColumnHeading, header); - n++; - XtSetArg(args[n], XmNdetailColumnHeadingCount, model->columns); - n++; - - // set res - XtSetArg(args[n], XmNlayoutType, XmDETAIL); - n++; - XtSetArg(args[n], XmNentryViewType, XmSMALL_ICON); - n++; - XtSetArg(args[n], XmNselectionPolicy, XmSINGLE_SELECT); - n++; - XtSetArg(args[n], XmNwidth, 600); - n++; - - // create widget - //UiContainer *ct = uic_get_current_container(obj); - //Widget parent = ct->add(ct, args, &n); - - Widget container = XmCreateContainer(scrollw, "table", args, n); - XtManageChild(container); - - // add callbacks - UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); - event->obj = obj; - event->activate = cb.activate; - event->selection = cb.selection; - event->userdata = cb.userdata; - event->last_selection = NULL; - if(cb.selection) { - XtAddCallback( - container, - XmNselectionCallback, - (XtCallbackProc)ui_table_select_callback, - event); - } - if(cb.activate) { - XtAddCallback( - container, - XmNdefaultActionCallback, - (XtCallbackProc)ui_table_action_callback, - event); - } - - // add initial data - UiList *list = var->value; - void *data = list->first(list); - int width = 0; - while(data) { - int w = ui_add_icon_gadget(container, model, data); - if(w > width) { - width = w; - } - data = list->next(list); - } - - UiTableView *tableview = ucx_mempool_malloc(obj->ctx->mempool, sizeof(UiTableView)); - tableview->widget = container; - tableview->var = var; - tableview->model = model; - - // set new XmContainer width - XtVaSetValues(container, XmNwidth, width, NULL); - - // cleanup - for(int i=0;i<model->columns;i++) { - XmStringFree(header[i]); - } - XtFree((char*)header); - - return scrollw; -} - -UIWIDGET ui_table(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = data; - var->type = UI_VAR_SPECIAL; - return ui_table_var(obj, var, model, cb); -} - -void ui_table_update(UiEvent *event, UiTableView *view) { - // clear container - Widget *children; - int nc; - - XtVaGetValues( - view->widget, - XmNchildren, - &children, - XmNnumChildren, - &nc, - NULL); - - for(int i=0;i<nc;i++) { - XtDestroyWidget(children[i]); - } - - UiList *list = view->var->value; - - void *data = list->first(list); - int width = 0; - while(data) { - int w = ui_add_icon_gadget(view->widget, view->model, data); - if(w > width) { - width = w; - } - data = list->next(list); - } - -} - -#define UI_COL_CHAR_WIDTH 12 - -int ui_add_icon_gadget(Widget container, UiModel *model, void *data) { - int width = 50; - - if(model->columns == 0) { - return width; - } - - XmString label = NULL; - Arg args[8]; - Boolean f; - // first column - if(model->types[0] != 12345678) { // TODO: icon/label type - char *str = ui_type_to_string( - model->types[0], - model->getvalue(data, 0), - &f); - - // column width - width += strlen(str) * UI_COL_CHAR_WIDTH; - - - XmString label = XmStringCreateLocalized(str); - XtSetArg(args[0], XmNlabelString, label); - if(f) { - free(str); - } - } else { - // TODO - } - - // remaining columns are the icon gadget details - XmStringTable details = (XmStringTable)XtMalloc( - (model->columns - 1) * sizeof(XmString)); - for(int i=1;i<model->columns;i++) { - char *str = ui_type_to_string( - model->types[i], - model->getvalue(data, i), - &f); - - // column width - width += strlen(str) * UI_COL_CHAR_WIDTH; - - details[i - 1] = XmStringCreateLocalized(str); - if(f) { - free(str); - } - } - XtSetArg(args[1], XmNdetail, details); - XtSetArg(args[2], XmNdetailCount, model->columns - 1); - XtSetArg(args[3], XmNshadowThickness, 0); - // create widget - Widget item = XmCreateIconGadget(container, "table_item", args, 4); - XtManageChild(item); - - // cleanup - XmStringFree(label); - for(int i=0;i<model->columns-1;i++) { - XmStringFree(details[i]); - } - XtFree((char*)details); - - return width; -} - -char* ui_type_to_string(UiModelType type, void *data, Boolean *free) { - switch(type) { - case UI_STRING: *free = FALSE; return data; - case UI_INTEGER: { - *free = TRUE; - int *val = data; - sstr_t str = ucx_asprintf(ucx_default_allocator(), "%d", *val); - return str.ptr; - } - } - *free = FALSE; - return NULL; -} - -void ui_table_action_callback( - Widget widget, - UiTreeEventData *event, - XmContainerSelectCallbackStruct *sel) -{ - UiListSelection *selection = ui_list_selection(sel); - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->activate(&e, event->userdata); - - free(event->last_selection->rows); - free(event->last_selection); - event->last_selection = selection; -} - -void ui_table_select_callback( - Widget widget, - UiTreeEventData *event, - XmContainerSelectCallbackStruct *sel) -{ - UiListSelection *selection = ui_list_selection(sel); - if(!ui_compare_list_selection(selection, event->last_selection)) { - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = selection; - e.intval = selection->count > 0 ? selection->rows[0] : -1; - event->selection(&e, event->userdata); - } - if(event->last_selection) { - free(event->last_selection->rows); - free(event->last_selection); - } - event->last_selection = selection; -} - -UiListSelection* ui_list_selection(XmContainerSelectCallbackStruct *xs) { - UiListSelection *selection = malloc(sizeof(UiListSelection)); - selection->count = xs->selected_item_count; - selection->rows = calloc(selection->count, sizeof(int)); - for(int i=0;i<selection->count;i++) { - int index; - XtVaGetValues(xs->selected_items[i], XmNpositionIndex, &index, NULL); - selection->rows[i] = index; - } - return selection; -} - -Boolean ui_compare_list_selection(UiListSelection *s1, UiListSelection *s2) { - if(!s1 || !s2) { - return FALSE; - } - if(s1->count != s2->count) { - return FALSE; - } - for(int i=0;i<s1->count;i++) { - if(s1->rows[i] != s2->rows[i]) { - return FALSE; - } - } - return TRUE; -}
--- a/ui/motif/tree.h Sun May 23 09:44:43 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2014 Olaf Wintermann. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TREE_H -#define TREE_H - -#include "../ui/tree.h" -#include "../common/context.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct UiTreeEventData { - UiObject *obj; - ui_callback activate; - ui_callback selection; - void *userdata; - UiListSelection *last_selection; -} UiTreeEventData; - -typedef struct UiTableView { - Widget widget; - UiVar *var; - UiModel *model; -} UiTableView; - -void ui_table_update(UiEvent *event, UiTableView *view); -int ui_add_icon_gadget(Widget container, UiModel *model, void *data); -char* ui_type_to_string(UiModelType type, void *data, Boolean *free); - -void ui_table_action_callback( - Widget widget, - UiTreeEventData *event, - XmContainerSelectCallbackStruct *sel); -void ui_table_select_callback( - Widget widget, - UiTreeEventData *event, - XmContainerSelectCallbackStruct *sel); - -UiListSelection* ui_list_selection(XmContainerSelectCallbackStruct *xs); - -Boolean ui_compare_list_selection(UiListSelection *s1, UiListSelection *s2); - - -#ifdef __cplusplus -} -#endif - -#endif /* TREE_H */ -
--- a/ui/motif/window.c Sun May 23 09:44:43 2021 +0200 +++ b/ui/motif/window.c Sat Jan 04 16:38:48 2025 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,6 +36,10 @@ #include "../ui/window.h" #include "../common/context.h" +#include "Grid.h" + +#include <cx/mempool.h> + static int nwindows = 0; static int window_default_width = 600; @@ -43,17 +47,7 @@ static void window_close_handler(Widget window, void *udata, void *cdata) { UiObject *obj = udata; - UiEvent ev; - ev.window = obj->window; - ev.document = obj->ctx->document; - ev.obj = obj; - ev.eventdata = NULL; - ev.intval = 0; - - if(obj->ctx->close_callback) { - obj->ctx->close_callback(&ev, obj->ctx->close_data); - } - // TODO: free UiObject + uic_object_destroy(obj); nwindows--; if(nwindows == 0) { @@ -61,31 +55,30 @@ } } -static UiObject* create_window(char *title, void *window_data, UiBool simple) { - UcxMempool *mp = ucx_mempool_new(256); - UiObject *obj = ucx_mempool_calloc(mp, 1, sizeof(UiObject)); + +static UiObject* create_window(const char *title, void *window_data, Boolean simple) { + CxMempool *mp = cxBasicMempoolCreate(256); + const CxAllocator *a = mp->allocator; + UiObject *obj = cxCalloc(a, 1, sizeof(UiObject)); obj->ctx = uic_context(obj, mp); obj->window = window_data; Arg args[16]; int n = 0; - - XtSetArg(args[0], XmNtitle, title); - //XtSetArg(args[1], XmNbaseWidth, window_default_width); - //XtSetArg(args[2], XmNbaseHeight, window_default_height); - XtSetArg(args[1], XmNminWidth, 100); - XtSetArg(args[2], XmNminHeight, 50); - XtSetArg(args[3], XmNwidth, window_default_width); - XtSetArg(args[4], XmNheight, window_default_height); + XtSetArg(args[n], XmNtitle, title); n++; + XtSetArg(args[n], XmNminWidth, 100); n++; + XtSetArg(args[n], XmNminHeight, 50); n++; + XtSetArg(args[n], XmNwidth, window_default_width); n++; + XtSetArg(args[n], XmNheight, window_default_height); n++; Widget toplevel = XtAppCreateShell( - "Test123", - "abc", + ui_appname(), + "mainwindow", //applicationShellWidgetClass, vendorShellWidgetClass, - ui_get_display(), + ui_motif_get_display(), args, - 5); + n); Atom wm_delete_window; wm_delete_window = XmInternAtom( @@ -98,111 +91,43 @@ window_close_handler, obj); - // TODO: use callback - ui_set_active_window(toplevel); - Widget window = XtVaCreateManagedWidget( title, xmMainWindowWidgetClass, toplevel, NULL); - obj->widget = window; - Widget form = XtVaCreateManagedWidget( - "window_form", - xmFormWidgetClass, - window, - NULL); - Widget toolbar = NULL; + // menu if(!simple) { - ui_create_menubar(obj); - toolbar = ui_create_toolbar(obj, form); + ui_create_menubar(obj, window); } - // window content - XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT); - XtSetArg(args[1], XmNshadowThickness, 0); - XtSetArg(args[2], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - if(toolbar) { - XtSetArg(args[5], XmNtopAttachment, XmATTACH_WIDGET); - XtSetArg(args[6], XmNtopWidget, toolbar); - n = 7; - } else { - XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM); - n = 6; - } - Widget frame = XmCreateFrame(form, "content_frame", args, n); + // content frame + n = 0; + Widget frame = XmCreateFrame(window, "window_frame", args, n); XtManageChild(frame); - Widget content_form = XmCreateForm(frame, "content_form", NULL, 0); - XtManageChild(content_form); - obj->container = ui_box_container(obj, content_form, 0, 0, UI_BOX_VERTICAL); + Widget form = XmCreateForm(frame, "window_form", args, 0); + XtManageChild(form); - XtManageChild(form); - + n = 0; + XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++; + XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++; + Widget vbox = XtCreateManagedWidget("window_vbox", gridClass, form, args, n); + UiContainerX *container = ui_box_container(obj, vbox, UI_BOX_VERTICAL); + uic_object_push_container(obj, container); + obj->widget = toplevel; nwindows++; return obj; -} +} -UiObject* ui_window(char *title, void *window_data) { +UiObject* ui_window(const char *title, void *window_data) { return create_window(title, window_data, FALSE); } -UiObject* ui_simplewindow(char *title, void *window_data) { +UiObject* ui_simple_window(const char *title, void *window_data) { return create_window(title, window_data, TRUE); } - -void ui_close(UiObject *obj) { - XtDestroyWidget(obj->widget); - window_close_handler(obj->widget, obj, NULL); -} - -typedef struct FileDialogData { - int running; - char *file; -} FileDialogData; - -static void filedialog_select( - Widget widget, - FileDialogData *data, - XmFileSelectionBoxCallbackStruct *selection) -{ - char *path = NULL; - XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &path); - data->running = 0; - data->file = strdup(path); - XtFree(path); - XtUnmanageChild(widget); -} - -static void filedialog_cancel( - Widget widget, - FileDialogData *data, - XmFileSelectionBoxCallbackStruct *selection) - -{ - data->running = 0; - XtUnmanageChild(widget); -} - -char* ui_openfiledialog(UiObject *obj) { - Widget dialog = XmCreateFileSelectionDialog(obj->widget, "openfiledialog", NULL, 0); - XtManageChild(dialog); - - FileDialogData data; - data.running = 1; - data.file = NULL; - - XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, &data); - XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, &data); - - ui_secondary_event_loop(&data.running); - return data.file; -} - -char* ui_savefiledialog(UiObject *obj) { - return ui_openfiledialog(obj); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/window.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,44 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WINDOW_H +#define WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* WINDOW_H */ +
--- a/ui/ui/button.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/button.h Sat Jan 04 16:38:48 2025 +0100 @@ -34,14 +34,64 @@ #ifdef __cplusplus extern "C" { #endif + +typedef struct UiButtonArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const char* label; + const char* stockid; + const char* icon; + UiLabelType labeltype; + ui_callback onclick; + void* onclickdata; -UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data); + const int* groups; +} UiButtonArgs; -UIWIDGET ui_checkbox(UiObject *obj, char *label, UiInteger *value); -UIWIDGET ui_checkbox_nv(UiObject *obj, char *label, char *varname); +typedef struct UiToggleArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const char* label; + const char* stockid; + const char* icon; + UiLabelType labeltype; + UiInteger* value; + const char* varname; + ui_callback onchange; + void* onchangedata; + int enable_group; + + const int* groups; +} UiToggleArgs; + +#define ui_button(obj, ...) ui_button_create(obj, (UiButtonArgs){ __VA_ARGS__ } ) +#define ui_togglebutton(obj, ...) ui_togglebutton_create(obj, (UiToggleArgs){ __VA_ARGS__ } ) +#define ui_checkbox(obj, ...) ui_checkbox_create(obj, (UiToggleArgs){ __VA_ARGS__ } ) +#define ui_switch(obj, ...) ui_switch_create(obj, (UiToggleArgs){ __VA_ARGS__ } ) +#define ui_radiobutton(obj, ...) ui_radiobutton_create(obj, (UiToggleArgs){ __VA_ARGS__ } ) -UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup); -UIWIDGET ui_radiobutton_nv(UiObject *obj, char *label, char *varname); +UIEXPORT UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args); +UIEXPORT UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args); +UIEXPORT UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args); +UIEXPORT UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args); +UIEXPORT UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args); + #ifdef __cplusplus
--- a/ui/ui/container.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/container.h Sat Jan 04 16:38:48 2025 +0100 @@ -34,54 +34,262 @@ #ifdef __cplusplus extern "C" { #endif + +typedef enum UiSubContainerType { + UI_CONTAINER_VBOX = 0, + UI_CONTAINER_HBOX, + UI_CONTAINER_GRID, + UI_CONTAINER_NO_SUB +} UiSubContainerType; + +typedef enum UiTabViewType { + UI_TABVIEW_DEFAULT = 0, + UI_TABVIEW_DOC, + UI_TABVIEW_NAVIGATION_SIDE, + UI_TABVIEW_NAVIGATION_TOP, + UI_TABVIEW_NAVIGATION_TOP2, + UI_TABVIEW_INVISIBLE +} UiTabViewType; + +typedef enum UiHeaderbarAlternative { + UI_HEADERBAR_ALTERNATIVE_DEFAULT = 0, + UI_HEADERBAR_ALTERNATIVE_TOOLBAR, + UI_HEADERBAR_ALTERNATIVE_BOX +} UiHeaderbarAlternative; + +typedef struct UiContainerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + int margin; + int spacing; + int columnspacing; + int rowspacing; +} UiContainerArgs; + +typedef struct UiFrameArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + UiSubContainerType subcontainer; + + int margin; + int spacing; + int columnspacing; + int rowspacing; + + const char* label; + UiBool isexpanded; +} UiFrameArgs; + +typedef struct UiTabViewArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + UiTabViewType tabview; + + UiSubContainerType subcontainer; -#define UI_CTN(obj, ctn) for(ctn;ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_VBOX(obj) for(ui_vbox(obj);ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_HBOX(obj) for(ui_hbox(obj);ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_VBOX_SP(obj, margin, spacing) for(ui_vbox_sp(obj,margin,spacing);ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_HBOX_SP(obj, margin, spacing) for(ui_hbox_sp(obj,margin,spacing);ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_GRID(obj) for(ui_grid(obj);ui_container_finish(obj);ui_container_begin_close(obj)) -#define UI_GRID_SP(obj, margin, columnspacing, rowspacing) for(ui_grid_sp(obj,margin,columnspacing,rowspacing);ui_container_finish(obj);ui_container_begin_close(obj)) + UiInteger *value; + const char* varname; + + int margin; + int spacing; + int columnspacing; + int rowspacing; + + const char* label; + UiBool isexpanded; +} UiTabViewArgs; + +typedef struct UiHeaderbarArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; -void ui_end(UiObject *obj); + UiBool showtitle; + UiBool showwindowbuttons; -UIWIDGET ui_vbox(UiObject *obj); -UIWIDGET ui_hbox(UiObject *obj); -UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing); -UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing); + UiHeaderbarAlternative alternative; + int alt_spacing; +} UiHeaderbarArgs; + +typedef struct UiSidebarArgs { + const char *name; + const char *style_class; + int margin; + int spacing; +} UiSidebarArgs; -UIWIDGET ui_grid(UiObject *obj); -UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing); +typedef struct UiItemListContainerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + int margin; + int spacing; + + int sub_margin; + int sub_spacing; + int sub_columnspacing; + int sub_rowspacing; + + UiList *value; + const char *varname; + /* + * void create_ui(UiObject *obj, int index, void *elm, void *userdata) + * + * UI constructor for each list element + * + * This callback is executed for each list element. A new UiObject is + * created for every item. + */ + void (*create_ui)(UiObject *, int, void *, void *); + void *userdata; + + /* + * ItemList container type + * Only UI_CONTAINER_VBOX or UI_CONTAINER_HBOX are supported + */ + UiSubContainerType container; + + /* + * item root container + */ + UiSubContainerType subcontainer; +} UiItemListContainerArgs; -UIWIDGET ui_scrolledwindow(UiObject *obj); +struct UiContainerX { + void *container; + int close; + UiContainerX *prev; + UiContainerX *next; +}; + + +#define UI_CTN(obj, ctn) for(ctn;ui_container_finish(obj);ui_container_begin_close(obj)) -UIWIDGET ui_sidebar(UiObject *obj); +#define ui_vbox(obj, ...) for(ui_vbox_create(obj, (UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_hbox(obj, ...) for(ui_hbox_create(obj, (UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_grid(obj, ...) for(ui_grid_create(obj, (UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_frame(obj, ...) for(ui_frame_create(obj, (UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_expander(obj, ...) for(ui_expander_create(obj, (UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_scrolledwindow(obj, ...) for(ui_scrolledwindow_create(obj, (UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_tabview(obj, ...) for(ui_tabview_create(obj, (UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, (UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, (UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) + +#define ui_vbox0(obj) for(ui_vbox_create(obj, (UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_hbox0(obj) for(ui_hbox_create(obj, (UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_grid0(obj) for(ui_grid_create(obj, (UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_frame0(obj) for(ui_frame_create(obj, (UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_expander0(obj) for(ui_expande_create(obj, (UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_scrolledwindow0(obj) for(ui_scrolledwindow_create(obj, (UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_tabview0(obj) for(ui_tabview_create(obj, (UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_headerbar0(obj) for(ui_headerbar_create(obj, (UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_sidebar0(obj) for(ui_sidebar_create(obj, (UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) + +#define ui_tab(obj, label) for(ui_tab_create(obj, label);ui_container_finish(obj);ui_container_begin_close(obj)) -UIWIDGET ui_hsplitpane(UiObject *obj, int max); -UIWIDGET ui_vsplitpane(UiObject *obj, int max); +#define ui_headerbar_start(obj) for(ui_headerbar_start_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_headerbar_center(obj) for(ui_headerbar_center_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_headerbar_end(obj) for(ui_headerbar_end_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) + +#define ui_itemlist(obj, ...) ui_itemlist_create(obj, (UiItemListContainerArgs) { __VA_ARGS__} ) + +UIEXPORT void ui_end(UiObject *obj); // deprecated +UIEXPORT void ui_end_new(UiObject *obj); // TODO: rename to ui_end + +UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args); +UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args); +UIEXPORT UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args); +UIEXPORT UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs args); +UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs args); +UIEXPORT UIWIDGET ui_scrolledwindow_create(UiObject *obj, UiFrameArgs args); -UIWIDGET ui_tabview(UiObject *obj); -void ui_tab(UiObject *obj, char *title); -void ui_select_tab(UIWIDGET tabview, int tab); +UIEXPORT UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs args); +UIEXPORT void ui_tab_create(UiObject *obj, const char* title); +UIEXPORT void ui_tabview_select(UIWIDGET tabview, int tab); +UIEXPORT void ui_tabview_remove(UIWIDGET tabview, int tab); +UIEXPORT UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index); + +UIEXPORT UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args); +UIEXPORT void ui_headerbar_start_create(UiObject *obj); +UIEXPORT void ui_headerbar_center_create(UiObject *obj); +UIEXPORT void ui_headerbar_end_create(UiObject *obj); + +UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args); + +UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs args); + +UIEXPORT UIWIDGET ui_hsplitpane(UiObject *obj, int max); // TODO +UIEXPORT UIWIDGET ui_vsplitpane(UiObject *obj, int max); // TODO + // box container layout functions -void ui_layout_fill(UiObject *obj, UiBool fill); +UIEXPORT void ui_layout_fill(UiObject *obj, UiBool fill); // grid container layout functions -void ui_layout_hexpand(UiObject *obj, UiBool expand); -void ui_layout_vexpand(UiObject *obj, UiBool expand); -void ui_layout_width(UiObject *obj, int width); -void ui_layout_gridwidth(UiObject *obj, int width); -void ui_newline(UiObject *obj); +UIEXPORT void ui_layout_hexpand(UiObject *obj, UiBool expand); +UIEXPORT void ui_layout_vexpand(UiObject *obj, UiBool expand); +UIEXPORT void ui_layout_hfill(UiObject *obj, UiBool fill); +UIEXPORT void ui_layout_vfill(UiObject *obj, UiBool fill); +UIEXPORT void ui_layout_width(UiObject *obj, int width); +UIEXPORT void ui_layout_height(UiObject* obj, int width); +UIEXPORT void ui_layout_colspan(UiObject *obj, int cols); +UIEXPORT void ui_layout_rowspan(UiObject* obj, int rows); +UIEXPORT void ui_newline(UiObject *obj); - -UiTabbedPane* ui_tabbed_document_view(UiObject *obj); - -UiObject* ui_document_tab(UiTabbedPane *view); +// TODO +UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj); +UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view); /* used for macro */ -void ui_container_begin_close(UiObject *obj); -int ui_container_finish(UiObject *obj); +UIEXPORT void ui_container_begin_close(UiObject *obj); +UIEXPORT int ui_container_finish(UiObject *obj); + +#define UI_APPLY_LAYOUT1(obj, args) \ + if(args.fill != UI_DEFAULT) ui_layout_fill(obj, args.fill == UI_ON ? 1 : 0 ); \ + if(args.hexpand) ui_layout_hexpand(obj, 1); \ + if(args.vexpand) ui_layout_vexpand(obj, 1); \ + if(args.hfill) ui_layout_hfill(obj, 1); \ + if(args.vfill) ui_layout_vfill(obj, 1); \ + if(args.colspan > 0) ui_layout_colspan(obj, args.colspan); \ + if(args.rowspan > 0) ui_layout_rowspan(obj, args.rowspan); \ + /*force caller to add ';'*/(void)0 #ifdef __cplusplus
--- a/ui/ui/display.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/display.h Sat Jan 04 16:38:48 2025 +0100 @@ -39,16 +39,94 @@ extern "C" { #endif +enum UiAlignment { + UI_ALIGN_DEFAULT = 0, + UI_ALIGN_LEFT, + UI_ALIGN_RIGHT, + UI_ALIGN_CENTER +}; + +typedef enum UiAlignment UiAlignment; + +enum UiLabelStyle { + UI_LABEL_STYLE_DEFAULT = 0, + UI_LABEL_STYLE_TITLE, + UI_LABEL_STYLE_SUBTITLE, + UI_LABEL_STYLE_DIM +}; + +typedef enum UiLabelStyle UiLabelStyle; + +typedef struct UiLabelArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const char* label; + UiAlignment align; + UiLabelStyle style; + UiString* value; + const char* varname; +} UiLabelArgs; + +typedef struct UiProgressbarArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + int width; + const char *name; + const char *style_class; + + double min; + double max; + UiDouble* value; + const char* varname; +} UiProgressbarArgs; + +typedef struct UiProgressbarSpinnerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + + UiInteger* value; + const char* varname; +} UiProgressbarSpinnerArgs; + /* label widgets */ -UIWIDGET ui_label(UiObject *obj, char *label); -UIWIDGET ui_llabel(UiObject *obj, char *label); -UIWIDGET ui_rlabel(UiObject *obj, char *label); -UIWIDGET ui_space(UiObject *obj); -UIWIDGET ui_separator(UiObject *obj); + +#define ui_label(obj, ...) ui_label_create(obj, (UiLabelArgs) { __VA_ARGS__ }) +#define ui_llabel(obj, ...) ui_llabel_create(obj, (UiLabelArgs) { __VA_ARGS__ }) +#define ui_rlabel(obj, ...) ui_rlabel_create(obj, (UiLabelArgs) { __VA_ARGS__ }) + + +UIEXPORT UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs args); +UIEXPORT UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args); +UIEXPORT UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args); -/* progress bar */ -UIWIDGET ui_progressbar(UiObject *obj, UiDouble *value); -UIWIDGET ui_progressbar_nv(UiObject *obj, char *varname); +UIWIDGET ui_space_deprecated(UiObject *obj); +UIWIDGET ui_separator_deprecated(UiObject *obj); + +/* progress bar/spinner */ + +#define ui_progressbar(obj, ...) ui_progressbar_create(obj, (UiProgressbarArgs) { __VA_ARGS__ } ) +#define ui_progressspinner(obj, ...) ui_progressspinner_create(obj, (UiProgressbarSpinnerArgs) { __VA_ARGS__ } ) + +UIEXPORT UIWIDGET ui_progressbar_create(UiObject *obj, UiProgressbarArgs args); +UIEXPORT UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs args); #ifdef __cplusplus
--- a/ui/ui/dnd.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/dnd.h Sat Jan 04 16:38:48 2025 +0100 @@ -37,11 +37,16 @@ #define UI_DND_FILE_TARGET "XdndDirectSave0" -void ui_selection_settext(UiSelection *sel, char *str, int len); -void ui_selection_seturis(UiSelection *sel, char **uris, int nelm); +UIEXPORT void ui_selection_settext(UiDnD *sel, char *str, int len); +UIEXPORT void ui_selection_seturis(UiDnD *sel, char **uris, int nelm); -char* ui_selection_gettext(UiSelection *sel); -char** ui_selection_geturis(UiSelection *sel, size_t *nelm); +UIEXPORT char* ui_selection_gettext(UiDnD *sel); +UIEXPORT UiFileList ui_selection_geturis(UiDnD *sel); + +UIEXPORT UiDnDAction ui_dnd_result(UiDnD *dnd); +UIEXPORT UiBool ui_dnd_need_delete(UiDnD *dnd); + +UIEXPORT void ui_dnd_accept(UiDnD *dnd, UiBool accept); #ifdef __cplusplus
--- a/ui/ui/entry.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/entry.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,13 +35,35 @@ extern "C" { #endif -UIWIDGET ui_spinner(UiObject *obj, int step, UiInteger *i); -UIWIDGET ui_spinnerf(UiObject *obj, double step, int digits, UiDouble *d); -UIWIDGET ui_spinnerr(UiObject *obj, UiRange *r); + +typedef struct UiSpinnerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; -UIWIDGET ui_spinner_nv(UiObject *obj, int step, char *varname); -UIWIDGET ui_spinnerf_nv(UiObject *obj, double step, int digits, char *varname); -UIWIDGET ui_spinnerr_nv(UiObject *obj, char *varname); + double step; + int digits; + UiInteger *intvalue; + UiDouble* doublevalue; + UiRange *rangevalue; + const char* varname; + ui_callback onchange; + void* onchangedata; + + const int *groups; +} UiSpinnerArgs; + + + +UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs args); + +#define ui_spinner(obj, ...) ui_spinner_create(obj, (UiSpinnerArgs){ __VA_ARGS__ } ) void ui_spinner_setrange(UIWIDGET spinner, double min, double max); void ui_spinner_setdigits(UIWIDGET spinner, int digits);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/ui/icons.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UI_ICONS_H +#define UI_ICONS_H + +#include "toolkit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef UI_GTK + +#define UI_ICON_HOME "go-home" +#define UI_ICON_NEW_WINDOW "list-add" +#define UI_ICON_REFRESH "view-refresh" +#define UI_ICON_NEW_FOLDER "folder-new" +#define UI_ICON_ADD "document-new" +#define UI_ICON_UPLOAD "document-open" +#define UI_ICON_SAVE_LOCAL "document-save-as" +#define UI_ICON_DELETE "edit-delete" +#define UI_ICON_DOCK_LEFT "" +#define UI_ICON_DOCK_RIGHT "" +#define UI_ICON_GO_BACK "go-previous" +#define UI_ICON_GO_FORWARD "go-next" + +#endif /* UI_GTK */ + + + +#ifdef UI_WINUI + +#define UI_ICON_HOME "Home" +#define UI_ICON_NEW_WINDOW "NewWindow" +#define UI_ICON_REFRESH "Refresh" +#define UI_ICON_NEW_FOLDER "NewFolder" +#define UI_ICON_ADD "Add" +#define UI_ICON_UPLOAD "Upload" +#define UI_ICON_SAVE_LOCAL "SaveLocal" +#define UI_ICON_DELETE "Delete" +#define UI_ICON_DOCK_LEFT "DockLeft" +#define UI_ICON_DOCK_RIGHT "DockRight" +#define UI_ICON_GO_BACK "Back" +#define UI_ICON_GO_FORWARD "Forward" + +#endif /* UI_WINUI */ + + +UIEXPORT UiIcon* ui_icon(const char* name, size_t size); +UIEXPORT UiIcon* ui_icon_unscaled(const char *name, int size); +UIEXPORT UiIcon* ui_imageicon(const char* file); +UIEXPORT void ui_icon_free(UiIcon* icon); + +UIEXPORT UiIcon* ui_foldericon(size_t size); +UIEXPORT UiIcon* ui_fileicon(size_t size); + + +#ifdef __cplusplus +} +#endif + +#endif /* UI_ICONS_H */ +
--- a/ui/ui/image.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/image.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,16 +35,31 @@ extern "C" { #endif -UiIcon* ui_icon(const char *name, int size); -UiIcon* ui_icon_unscaled(const char *name, int size); -void ui_free_icon(UiIcon *icon); +#define UI_IMAGE_OBJECT_TYPE "image" + +typedef struct UiImageViewerArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; -UiImage* ui_icon_image(UiIcon *icon); -UiImage* ui_image(const char *filename); -UiImage* ui_named_image(const char *filename, const char *name); -UiImage* ui_load_image_from_path(const char *path, const char *name); -void ui_free_image(UiImage *img); + UiBool scrollarea; + UiBool autoscale; + UiGeneric *value; + const char *varname; + UiMenuBuilder *contextmenu; +} UiImageViewerArgs; + +#define ui_imageviewer(obj, ...) ui_imageviewer_create(obj, (UiImageViewerArgs){ __VA_ARGS__ } ) + +UIEXPORT UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args); +UIEXPORT int ui_image_load_file(UiGeneric *obj, const char *path); #ifdef __cplusplus }
--- a/ui/ui/menu.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/menu.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,36 +35,74 @@ extern "C" { #endif -/* - * application menu functions - */ -void ui_menu(char *label); -void ui_submenu(char *label); -void ui_submenu_end(); + +typedef struct UiMenuItemArgs { + const char* label; + const char* stockid; + const char* icon; + + ui_callback onclick; + void* onclickdata; + + const int* groups; +} UiMenuItemArgs; + +typedef struct UiMenuToggleItemArgs { + const char* label; + const char* stockid; + const char* icon; + + const char* varname; + ui_callback onchange; + void* onchangedata; + + const int* groups; +} UiMenuToggleItemArgs; -void ui_menuitem(char *label, ui_callback f, void *userdata); -void ui_menuitem_st(char *stockid, ui_callback f, void *userdata); -void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...); -void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...); +typedef struct UiMenuItemListArgs { + const char* varname; + ui_getvaluefunc getvalue; + ui_callback onselect; + void* onselectdata; + UiBool addseparator; +} UiMenuItemListArgs; + +#define ui_menu(label) for(ui_menu_create(label);ui_menu_is_open();ui_menu_close()) -void ui_menuseparator(); +#define ui_menuitem(...) ui_menuitem_create((UiMenuItemArgs){ __VA_ARGS__ }) +#define ui_menu_toggleitem(...) ui_menu_toggleitem_create((UiMenuToggleItemArgs){ __VA_ARGS__ }) +#define ui_menu_radioitem(...) ui_menu_radioitem_create((UiMenuToggleItemArgs){ __VA_ARGS__ }) +#define ui_menu_itemlist(...) ui_menu_itemlist_create((UiMenuItemListArgs) { __VA_ARGS__ } ) +#define ui_menu_togglelist(...) ui_menu_itemlist_create((UiMenuItemListArgs) { __VA_ARGS} ) +#define ui_menu_radiolist(...) ui_menu_itemlist_create((UiMenuItemListArgs) { __VA_ARGS} ) -void ui_checkitem(char *label, ui_callback f, void *userdata); -void ui_checkitem_nv(char *label, char *vname); +UIEXPORT void ui_menu_create(const char* label); +UIEXPORT void ui_menuitem_create(UiMenuItemArgs args); +UIEXPORT void ui_menu_toggleitem_create(UiMenuToggleItemArgs args); +UIEXPORT void ui_menu_radioitem_create(UiMenuToggleItemArgs args); -void ui_menuitem_list(UiList *items, ui_callback f, void *userdata); +UIEXPORT void ui_menuseparator(); + +UIEXPORT void ui_menu_itemlist_create(UiMenuItemListArgs args); +UIEXPORT void ui_menu_toggleitemlist_create(UiMenuItemListArgs args); +UIEXPORT void ui_menu_radioitemlist_create(UiMenuItemListArgs args); + +UIEXPORT void ui_menu_end(void); // TODO: private /* * widget menu functions */ -UIMENU ui_contextmenu(UiObject *obj); -UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget); -void ui_contextmenu_popup(UIMENU menu); + +#define ui_contextmenu(builder) for(ui_contextmenu_builder(builder);ui_menu_is_open();ui_menu_close()) -void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata); -void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata); -void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...); -void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...); +UIEXPORT void ui_contextmenu_builder(UiMenuBuilder **out_builder); +UIEXPORT void ui_menubuilder_free(UiMenuBuilder *builder); +UIEXPORT UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget); +UIEXPORT void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y); + +// used for macro +UIEXPORT void ui_menu_close(void); +UIEXPORT int ui_menu_is_open(void); #ifdef __cplusplus }
--- a/ui/ui/properties.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/properties.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,15 +35,11 @@ extern "C" { #endif -typedef struct UcxMap UiProperties; - -char* ui_getappdir(); -char* ui_configfile(char *name); +const char* ui_get_property(const char *name); +void ui_set_property(const char *name, const char *value); +const char* ui_set_default_property(const char *name, const char *value); -char* ui_get_property(char *name); -void ui_set_property(char *name, char *value); - -void ui_set_default_property(char *name, char *value); +int ui_properties_store(void); void ui_locales_dir(char *path); void ui_pixmaps_dir(char *path);
--- a/ui/ui/text.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/text.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,28 +35,106 @@ extern "C" { #endif -UIWIDGET ui_textarea(UiObject *obj, UiText *value); -UIWIDGET ui_textarea_nv(UiObject *obj, char *varname); - -UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea); +typedef struct UiTextAreaArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + int width; + const char *name; + const char *style_class; -void ui_text_undo(UiText *value); -void ui_text_redo(UiText *value); + UiText *value; + const char *varname; + ui_callback onchange; + void *onchangedata; + + const int *groups; +} UiTextAreaArgs; + +typedef struct UiTextFieldArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + int width; + const char *name; + const char *style_class; -UIWIDGET ui_textfield(UiObject *obj, UiString *value); -UIWIDGET ui_textfield_nv(UiObject *obj, char *varname); + UiString* value; + const char *varname; + ui_callback onchange; + void *onchangedata; + ui_callback onactivate; + void *onactivatedata; + + const int *groups; +} UiTextFieldArgs; + +typedef struct UiPathElmRet { + char *name; + size_t name_len; + char *path; + size_t path_len; +} UiPathElm; -UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value); -UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname); +typedef UiPathElm*(*ui_pathelm_func)(const char *full_path, size_t len, size_t *ret_nelm, void *data); + + -UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value); -UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname); +typedef struct UiPathTextFieldArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + UiString *value; + const char *varname; + + ui_pathelm_func getpathelm; + void *getpathelmdata; -UIWIDGET ui_passwordfield(UiObject *obj, UiString *value); -UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname); -UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value); -UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname); + ui_callback onactivate; + void *onactivatedata; + + ui_callback ondragstart; + void *ondragstartdata; + ui_callback ondragcomplete; + void *ondragcompletedata; + ui_callback ondrop; + void *ondropsdata; +} UiPathTextFieldArgs; + +#define ui_textarea(obj, ...) ui_textarea_create(obj, (UiTextAreaArgs) { __VA_ARGS__ }) + +UIEXPORT UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args); +UIEXPORT UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea); + +UIEXPORT void ui_text_undo(UiText *value); +UIEXPORT void ui_text_redo(UiText *value); + +#define ui_textfield(obj, ...) ui_textfield_create(obj, (UiTextFieldArgs) { __VA_ARGS__ }) +#define ui_frameless_textfield(obj, ...) ui_frameless_field_create(obj, (UiTextFieldArgs) { __VA_ARGS__ }) +#define ui_passwordfield(obj, ...) ui_passwordfield_create(obj, (UiTextFieldArgs) { __VA_ARGS__ }) +#define ui_path_textfield(obj, ...) ui_path_textfield_create(obj, (UiPathTextFieldArgs) { __VA_ARGS__ } ) + +UIEXPORT UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs args); +UIEXPORT UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args); +UIEXPORT UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args); + +UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args); #ifdef __cplusplus }
--- a/ui/ui/toolbar.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/toolbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -36,23 +36,55 @@ extern "C" { #endif -void ui_toolitem(char *name, char *label, ui_callback f, void *udata); -void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *udata); -void ui_toolitem_sti(char *name, char *stockid, ui_callback f, void *udata); -void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *udata, ...); -void ui_toolitem_stgri(char *name, char *stockid, ui_callback f, void *userdata, ...); -void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata); +typedef struct UiToolbarItemArgs { + const char* label; + const char* stockid; + const char* icon; + + ui_callback onclick; + void* onclickdata; + + const int *groups; +} UiToolbarItemArgs; + +typedef struct UiToolbarToggleItemArgs { + const char* label; + const char* stockid; + const char* icon; + + const char* varname; + ui_callback onchange; + void* onchangedata; + + const int *groups; +} UiToolbarToggleItemArgs; -void ui_toolitem_toggle(const char *name, const char *label, const char *img, UiInteger *i); -void ui_toolitem_toggle_st(const char *name, const char *stockid, UiInteger *i); -void ui_toolitem_toggle_nv(const char *name, const char *label, const char *img, const char *intvar); -void ui_toolitem_toggle_stnv(const char *name, const char *stockid, const char *intvar); +typedef struct UiToolbarMenuArgs { + const char* label; + const char* stockid; + const char* icon; +} UiToolbarMenuArgs; + +enum UiToolbarPos { + UI_TOOLBAR_LEFT = 0, + UI_TOOLBAR_CENTER, + UI_TOOLBAR_RIGHT +}; -void ui_toolbar_combobox(char *name, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); -void ui_toolbar_combobox_str(char *name, UiList *list, ui_callback f, void *udata); -void ui_toolbar_combobox_nv(char *name, char *listname, ui_getvaluefunc getvalue, ui_callback f, void *udata); +#define ui_toolbar_item(name, ...) ui_toolbar_item_create(name, (UiToolbarItemArgs){ __VA_ARGS__ } ) +#define ui_toolbar_toggleitem(name, ...) ui_toolbar_toggleitem_create(name, (UiToolbarToggleItemArgs){ __VA_ARGS__ } ) + +#define ui_toolbar_menu(name, ...) for(ui_toolbar_menu_create(name, (UiToolbarMenuArgs){ __VA_ARGS__ });ui_menu_is_open();ui_menu_close()) +#define ui_toolbar_appmenu() for(ui_toolbar_menu_create(NULL, (UiToolbarMenuArgs){ 0 });ui_menu_is_open();ui_menu_close()) + -void ui_toolbar_add_default(char *name); +UIEXPORT void ui_toolbar_item_create(const char* name, UiToolbarItemArgs args); +UIEXPORT void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs args); +UIEXPORT void ui_toolbar_menu_create(const char* name, UiToolbarMenuArgs args); + +UIEXPORT void ui_toolbar_add_default(const char *name, enum UiToolbarPos pos); + + #ifdef __cplusplus }
--- a/ui/ui/toolkit.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/toolkit.h Sat Jan 04 16:38:48 2025 +0100 @@ -33,22 +33,32 @@ #ifdef UI_COCOA +#include <stdlib.h> #ifdef __OBJC__ #import <Cocoa/Cocoa.h> -#define UIWIDGET NSView* -#define UIMENU NSMenu* -#else -typedef void* UIWIDGET; -typedef void* UIMENU; #endif +typedef void* UIWIDGET; // NSView* +typedef void* UIWINDOW; // NSWindow* +typedef void* UIMENU; // NSMenu* -#elif UI_GTK2 || UI_GTK3 +#elif UI_GTK2 || UI_GTK3 || UI_GTK4 #include <gtk/gtk.h> #define UIWIDGET GtkWidget* + +#if UI_GTK2 || UI_GTK3 #define UIMENU GtkMenu* +#endif +#ifdef UI_GTK4 +#define UIMENU GtkPopoverMenu* +#endif + #define UI_GTK +#ifdef UI_LIBADWAITA +#include <adwaita.h> +#endif + #elif UI_MOTIF #include <Xm/XmAll.h> @@ -67,9 +77,58 @@ #define UIMENU void* #endif -#elif UI_WPF +#elif UI_WINUI + +#define UIEXPORT __declspec(dllexport) + +#ifdef __cplusplus + +#include <functional> +#ifndef UI_WINUI_PCH +#include <Windows.h> +#undef GetCurrentTime +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#endif + +class UiWindow { +public: + winrt::Microsoft::UI::Xaml::Window window { nullptr }; + + UiWindow(winrt::Microsoft::UI::Xaml::Window& win); +}; + +class UiWidget { +public: + winrt::Microsoft::UI::Xaml::UIElement uielement; + void* data1 = nullptr; + void* data2 = nullptr; + void* data3 = nullptr; + std::function<void()> Show; + UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm); + +}; + +#define UIWIDGET UiWidget* +#define UIWINDOW UiWindow* +#define UIMENU void* + +/* +// winrt::Microsoft::UI::Xaml::UIElement #define UIWIDGET void* +// winrt::Microsoft::UI::Xaml::Window +#define UIWINDOW void* #define UIMENU void* +*/ + +#else +#define UIWIDGET void* +#define UIWINDOW void* +#define UIMENU void* +#endif + + #endif #ifndef TRUE @@ -79,16 +138,23 @@ #define FALSE 0 #endif +#ifndef UIEXPORT +#define UIEXPORT +#endif + #ifdef __cplusplus extern "C" { #endif #define UI_GROUP_SELECTION 20000 + +#define UI_GROUPS(...) (const int[]){ __VA_ARGS__, -1 } /* public types */ -typedef int UiBool; +typedef _Bool UiBool; typedef struct UiObject UiObject; +typedef struct UiContainerX UiContainerX; typedef struct UiEvent UiEvent; typedef struct UiMouseEvent UiMouseEvent; typedef struct UiObserver UiObserver; @@ -99,24 +165,39 @@ typedef struct UiText UiText; typedef struct UiList UiList; typedef struct UiRange UiRange; +typedef struct UiGeneric UiGeneric; typedef struct UiStr UiStr; -/* begin opaque types */ -typedef struct UiContext UiContext; -typedef struct UiContainer UiContainer; +typedef struct UiFileList UiFileList; + +typedef struct UiListSelection UiListSelection; -typedef struct UiIcon UiIcon; -typedef struct UiImage UiImage; +/* begin opaque types */ +typedef struct UiContext UiContext; +typedef struct UiContainer UiContainer; +typedef struct UiMenuBuilder UiMenuBuilder; -typedef struct UiSelection UiSelection; +typedef struct UiIcon UiIcon; +typedef struct UiImage UiImage; + +typedef struct UiDnD UiDnD; + +typedef struct UiThreadpool UiThreadpool; /* end opaque types */ typedef struct UiTabbedPane UiTabbedPane; +typedef enum UiTri UiTri; +typedef enum UiLabelType UiLabelType; + +typedef enum UiDnDAction UiDnDAction; + enum UiMouseEventType { UI_PRESS = 0, UI_PRESS2 }; +enum UiLabelType { UI_LABEL_DEFAULT, UI_LABEL_TEXT, UI_LABEL_ICON, UI_LABEL_TEXT_ICON }; +enum UiDnDAction { UI_DND_ACTION_NONE, UI_DND_ACTION_COPY, UI_DND_ACTION_MOVE, UI_DND_ACTION_LINK, UI_DND_ACTION_CUSTOM }; typedef void(*ui_callback)(UiEvent*, void*); /* event, user data */ @@ -126,13 +207,20 @@ typedef void(*ui_freefunc)(void*); -typedef void(*ui_enablefunc)(void*, UiBool); +typedef void(*ui_enablefunc)(void*, int); struct UiObject { /* * native widget */ UIWIDGET widget; + +#if defined(UI_COCOA) || defined(UI_WINUI) + /* + * native window object + */ + UIWINDOW wobj; +#endif /* * user window data @@ -145,14 +233,31 @@ UiContext *ctx; /* - * container interface + * container interface (deprecated) */ UiContainer *container; /* + * container list + * TODO: remove old UiContainer and rename UiContainerX to UiContainer + */ + UiContainerX *container_begin; + UiContainerX *container_end; + + /* * next container object */ UiObject *next; + + /* + * obj destroy func + */ + void (*destroy)(UiObject *obj); + + /* + * reference counter + */ + unsigned int ref; }; struct UiTabbedPane { @@ -218,7 +323,7 @@ struct UiString { char* (*get)(UiString*); - void (*set)(UiString*, char*); + void (*set)(UiString*, const char*); void *obj; UiStr value; @@ -226,7 +331,7 @@ }; struct UiText { - void (*set)(UiText*, char*); + void (*set)(UiText*, const char*); char* (*get)(UiText*); char* (*getsubstr)(UiText*, int, int); /* text, begin, end */ void (*insert)(UiText*, int, char*); @@ -242,6 +347,17 @@ // TODO: replacefunc, ... UiObserver *observers; }; + +struct UiGeneric { + void* (*get)(UiGeneric*); + const char* (*get_type)(UiGeneric*); + int (*set)(UiGeneric*, void *, const char *type); + void *obj; + + void *value; + const char *type; + UiObserver *observers; +}; /* * abstract list @@ -260,8 +376,10 @@ /* private - implementation dependent */ void *data; - /* binding function */ + /* binding functions */ void (*update)(UiList *list, int i); + UiListSelection (*getselection)(UiList *list); + void (*setselection)(UiList *list, UiListSelection selection); /* binding object */ void *obj; @@ -269,6 +387,19 @@ UiObserver *observers; }; + +struct UiListSelection { + /* + * number of selected items + */ + int count; + + /* + * indices of selected rows + */ + int *rows; +}; + struct UiRange { double (*get)(UiRange *range); void (*set)(UiRange *range, double value); @@ -283,87 +414,169 @@ UiObserver *observers; }; - -void ui_init(char *appname, int argc, char **argv); -char* ui_appname(); +enum UiTri { + UI_DEFAULT = 0, + UI_ON, + UI_OFF +}; -UiContext* ui_global_context(void); +struct UiFileList { + char **files; + size_t nfiles; +}; -void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata); +typedef struct UiCondVar { + void *data; + int intdata; +} UiCondVar; -void ui_onstartup(ui_callback f, void *userdata); -void ui_onopen(ui_callback f, void *userdata); -void ui_onexit(ui_callback f, void *userdata); + +UIEXPORT void ui_init(const char *appname, int argc, char **argv); +UIEXPORT const char* ui_appname(); + +UIEXPORT UiContext* ui_global_context(void); + +UIEXPORT void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata); -void ui_main(); -void ui_show(UiObject *obj); -void ui_close(UiObject *obj); +UIEXPORT void ui_context_destroy(UiContext *ctx); -void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd); +UIEXPORT void ui_object_ref(UiObject *obj); +UIEXPORT int ui_object_unref(UiObject *obj); -void* ui_document_new(size_t size); -void ui_document_destroy(void *doc); +UIEXPORT void ui_onstartup(ui_callback f, void *userdata); +UIEXPORT void ui_onopen(ui_callback f, void *userdata); +UIEXPORT void ui_onexit(ui_callback f, void *userdata); -void ui_set_document(UiObject *obj, void *document); // deprecated -void ui_detach_document(UiObject *obj); // deprecated -void* ui_get_document(UiObject *obj); // deprecated -void ui_set_subdocument(void *document, void *sub); // deprecated -void ui_detach_subdocument(void *document, void *sub); // deprecated -void* ui_get_subdocument(void *document); // deprecated +UIEXPORT void ui_main(void); +UIEXPORT void ui_show(UiObject *obj); +UIEXPORT void ui_close(UiObject *obj); + +UIEXPORT void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd); +UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td); +UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads); +UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool); +UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd); -UiContext* ui_document_context(void *doc); +UIEXPORT void* ui_document_new(size_t size); +UIEXPORT void ui_document_destroy(void *doc); + +UIEXPORT void ui_set_document(UiObject *obj, void *document); // deprecated +UIEXPORT void ui_detach_document(UiObject *obj); // deprecated +UIEXPORT void* ui_get_document(UiObject *obj); // deprecated +UIEXPORT void ui_set_subdocument(void *document, void *sub); // deprecated +UIEXPORT void ui_detach_subdocument(void *document, void *sub); // deprecated +UIEXPORT void* ui_get_subdocument(void *document); // deprecated -void ui_attach_document(UiContext *ctx, void *document); -void ui_detach_document2(UiContext *ctx, void *document); +UIEXPORT UiContext* ui_document_context(void *doc); -void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); +UIEXPORT void ui_attach_document(UiContext *ctx, void *document); +UIEXPORT void ui_detach_document2(UiContext *ctx, void *document); + +UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); -void ui_set_group(UiContext *ctx, int group); -void ui_unset_group(UiContext *ctx, int group); -int* ui_active_groups(UiContext *ctx, int *ngroups); +UIEXPORT void ui_set_group(UiContext *ctx, int group); +UIEXPORT void ui_unset_group(UiContext *ctx, int group); +UIEXPORT int* ui_active_groups(UiContext *ctx, int *ngroups); -void* ui_malloc(UiContext *ctx, size_t size); -void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize); -void ui_free(UiContext *ctx, void *ptr); -void* ui_realloc(UiContext *ctx, void *ptr, size_t size); +UIEXPORT void* ui_allocator(UiContext *ctx); +UIEXPORT void* ui_cx_mempool(UiContext *ctx); + +UIEXPORT void* ui_malloc(UiContext *ctx, size_t size); +UIEXPORT void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize); +UIEXPORT void ui_free(UiContext *ctx, void *ptr); +UIEXPORT void* ui_realloc(UiContext *ctx, void *ptr, size_t size); +UIEXPORT char* ui_strdup(UiContext *ctx, const char *str); // types -UiInteger* ui_int_new(UiContext *ctx, char *name); -UiDouble* ui_double_new(UiContext *ctx, char *name); -UiString* ui_string_new(UiContext *ctx, char *name); -UiText* ui_text_new(UiContext *ctx, char *name); -UiRange* ui_range_new(UiContext *ctx, char *name); +UIEXPORT UiInteger* ui_int_new(UiContext *ctx, char *name); +UIEXPORT UiDouble* ui_double_new(UiContext *ctx, char *name); +UIEXPORT UiString* ui_string_new(UiContext *ctx, char *name); +UIEXPORT UiText* ui_text_new(UiContext *ctx, char *name); +UIEXPORT UiRange* ui_range_new(UiContext *ctx, char *name); +UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, char *name); + +#define ui_get(v) _Generic(v, \ + UiInteger*: ui_int_get, \ + UiDouble*: ui_double_get, \ + UiString*: ui_string_get, \ + UiText*:ui_text_get) (v) + +#define ui_set(v, n) _Generic(v, \ + UiInteger*: ui_int_set, \ + UiDouble*: ui_double_set, \ + UiString*: ui_string_set, \ + UiText*:ui_text_set) (v, n) -UiObserver* ui_observer_new(ui_callback f, void *data); -UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer); -UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data); -void ui_notify(UiObserver *observer, void *data); -void ui_notify_except(UiObserver *observer, UiObserver *exc, void *data); -void ui_notify_evt(UiObserver *observer, UiEvent *event); +UIEXPORT void ui_int_set(UiInteger *i, int64_t value); +UIEXPORT int64_t ui_int_get(UiInteger *i); +UIEXPORT void ui_double_set(UiDouble *d, double value); +UIEXPORT double ui_double_get(UiDouble *d); +UIEXPORT void ui_string_set(UiString *s, const char *value); +UIEXPORT char* ui_string_get(UiString *s); +UIEXPORT void ui_text_set(UiText *s, const char* value); +UIEXPORT char* ui_text_get(UiText *s); + +UIEXPORT void ui_var_set_int(UiContext *ctx, const char *name, int64_t value); +UIEXPORT int64_t ui_var_get_int(UiContext *ctx, const char *name); +UIEXPORT void ui_var_set_double(UiContext *ctx, const char *name, double value); +UIEXPORT double ui_var_get_double(UiContext *ctx, const char *name); +UIEXPORT void ui_var_set_string(UiContext *ctx, const char *name, char *value); +UIEXPORT char* ui_var_get_string(UiContext *ctx, const char *name); + +UIEXPORT UiObserver* ui_observer_new(ui_callback f, void *data); +UIEXPORT UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer); +UIEXPORT UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data); +UIEXPORT void ui_notify(UiObserver *observer, void *data); +UIEXPORT void ui_notify_except(UiObserver *observer, UiObserver *exc, void *data); +UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event); -UiList* ui_list_new(UiContext *ctx, char *name); -void* ui_list_first(UiList *list); -void* ui_list_next(UiList *list); -void* ui_list_get(UiList *list, int i); -int ui_list_count(UiList *list); -void ui_list_append(UiList *list, void *data); -void ui_list_prepend(UiList *list, void *data); -void ui_list_clear(UiList *list); -void ui_list_addobsv(UiList *list, ui_callback f, void *data); -void ui_list_notify(UiList *list); +UIEXPORT UiList* ui_list_new(UiContext *ctx, char *name); +UIEXPORT void* ui_list_first(UiList *list); +UIEXPORT void* ui_list_next(UiList *list); +UIEXPORT void* ui_list_get(UiList *list, int i); +UIEXPORT int ui_list_count(UiList *list); +UIEXPORT void ui_list_append(UiList *list, void *data); +UIEXPORT void ui_list_prepend(UiList *list, void *data); +UIEXPORT void ui_list_remove(UiList *list, int i); +UIEXPORT void ui_list_clear(UiList *list); +UIEXPORT void ui_list_update(UiList *list); +UIEXPORT void ui_list_addobsv(UiList *list, ui_callback f, void *data); +UIEXPORT void ui_list_notify(UiList *list); -void ui_clipboard_set(char *str); -char* ui_clipboard_get(); +UIEXPORT UiListSelection ui_list_getselection(UiList *list); +UIEXPORT void ui_list_setselection(UiList *list, int index); -void ui_add_image(char *imgname, char *filename); // TODO: remove? +UIEXPORT UiFileList ui_filelist_copy(UiFileList list); +UIEXPORT void ui_filelist_free(UiFileList list); + +UIEXPORT void ui_clipboard_set(char *str); +UIEXPORT char* ui_clipboard_get(); + +UIEXPORT void ui_add_image(char *imgname, char *filename); // TODO: remove? // general widget functions -void ui_set_enabled(UIWIDGET widget, int enabled); -void ui_set_show_all(UIWIDGET widget, int value); -void ui_set_visible(UIWIDGET widget, int visible); +UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled); +UIEXPORT void ui_set_show_all(UIWIDGET widget, int value); +UIEXPORT void ui_set_visible(UIWIDGET widget, int visible); + + + +UIEXPORT void ui_listselection_free(UiListSelection selection); + +UIEXPORT UiStr ui_str(char *cstr); +UIEXPORT UiStr ui_str_free(char *str, void (*free)(void *v)); + + +UIEXPORT char* ui_getappdir(void); +UIEXPORT char* ui_configfile(char *name); + +UIEXPORT UiCondVar* ui_condvar_create(void); +UIEXPORT void ui_condvar_wait(UiCondVar *var); +UIEXPORT void ui_condvar_signal(UiCondVar *var, void *data, int intdata); +UIEXPORT void ui_condvar_destroy(UiCondVar *var); #ifdef __cplusplus }
--- a/ui/ui/tree.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/tree.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,15 +35,23 @@ extern "C" { #endif -typedef struct UiModel UiModel; -typedef struct UiListCallbacks UiListCallbacks; -typedef struct UiListSelection UiListSelection; +typedef struct UiModel UiModel; +typedef struct UiListCallbacks UiListCallbacks; +typedef struct UiListDnd UiListDnd; + +typedef struct UiListArgs UiListArgs; +typedef struct UiSourceListArgs UiSourceListArgs; + +typedef struct UiSubList UiSubList; +typedef struct UiSubListItem UiSubListItem; typedef enum UiModelType { UI_STRING = 0, + UI_STRING_FREE, UI_INTEGER, UI_ICON, UI_ICON_TEXT, + UI_ICON_TEXT_FREE } UiModelType; struct UiModel { @@ -65,18 +73,17 @@ char **titles; /* + * array of column size hints + */ + int *columnsize; + + /* * function for translating model data to view data * first argument is the pointer returned by UiList->get or UiTree->get * second argument is the column index * TODO: return */ void*(*getvalue)(void*, int); - - UiBool(*candrop)(UiEvent*, UiSelection*, UiList*, int); - void(*drop)(UiEvent*, UiSelection*, UiList*, int); - UiBool(*candrag)(UiEvent*, UiList*, int); - void(*data_get)(UiEvent*, UiSelection*, UiList*, int); - void(*data_delete)(UiEvent*, UiList*, int); }; struct UiListCallbacks { @@ -96,36 +103,129 @@ void *userdata; }; -struct UiListSelection { - /* - * number of selected items - */ - int count; +struct UiListArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + UiList* list; + const char* varname; + UiModel* model; + ui_getvaluefunc getvalue; + ui_callback onactivate; + void* onactivatedata; + ui_callback onselection; + void* onselectiondata; + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropsdata; + UiBool multiselection; + UiMenuBuilder *contextmenu; - /* - * indices of selected rows - */ - int *rows; + const int *groups; +}; + +typedef void (*ui_sublist_getvalue_func)(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item); + +struct UiSubList { + UiList *value; + const char *varname; + const char *header; + UiBool separator; + void *userdata; +}; + +/* + * list item members must be filled by the sublist getvalue func + * all members must be allocated (by malloc, strdup, ...) the pointer + * will be passed to free + */ +struct UiSubListItem { + char *icon; + char *label; + char *button_icon; + char *button_label; + char *badge; + void *eventdata; }; -UiModel* ui_model(UiContext *ctx, ...); -void ui_model_free(UiContext *ctx, UiModel *mi); - -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata); -UIWIDGET ui_listview_nv(UiObject *obj, char *listname, ui_getvaluefunc getvalue, ui_callback f, void *udata); +struct UiSourceListArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const int *groups; + + /* + * list of sublists + * a sublist must have a varname or a value + * + * the last entry in the list must contain all NULL values or numsublists + * must contain the number of sublists + */ + UiSubList *sublists; + /* + * optional number of sublists + * if the value is 0, it is assumed, that sublists is null-terminated + * (last item contains only NULL values) + */ + size_t numsublists; + + /* + * callback for each list item, that should fill all necessary + * UiSubListItem fields + */ + ui_sublist_getvalue_func getvalue; + + /* + * activated when a list item is selected + */ + ui_callback onactivate; + void *onactivatedata; + + /* + * activated, when the additional list item button is clicked + */ + ui_callback onbuttonclick; + void *onbuttonclickdata; +}; -UIWIDGET ui_table(UiObject *obj, UiList *data, UiModel *model, UiListCallbacks cb); -UIWIDGET ui_table_nv(UiObject *obj, char *varname, UiModel *model, UiListCallbacks cb); +#define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ } +#define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} } + + +UIEXPORT UiModel* ui_model(UiContext *ctx, ...); +UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model); +UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi); -void ui_table_dragsource(UIWIDGET tablewidget, int actions, char *target0, ...); -void ui_table_dragsource_a(UIWIDGET tablewidget, int actions, char **targets, int nelm); -void ui_table_dragdest(UIWIDGET tablewidget, int actions, char *target0, ...); -void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int nelm); +#define ui_listview(obj, ...) ui_listview_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_table(obj, ...) ui_table_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_combobox(obj, ...) ui_combobox_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, (UiSourceListArgs) { __VA_ARGS__ } ) -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata); -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata); -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata); +UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args); +UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args); +UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs args); +UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs args); + +UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args); + #ifdef __cplusplus }
--- a/ui/ui/ui.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/ui.h Sat Jan 04 16:38:48 2025 +0100 @@ -45,6 +45,7 @@ #include "image.h" #include "display.h" #include "dnd.h" +#include "icons.h" #endif /* UI_H */
--- a/ui/ui/window.h Sun May 23 09:44:43 2021 +0200 +++ b/ui/ui/window.h Sat Jan 04 16:38:48 2025 +0100 @@ -35,11 +35,61 @@ extern "C" { #endif -UiObject* ui_window(char *title, void *window_data); -UiObject* ui_simplewindow(char *title, void *window_data); +#define UI_FILEDIALOG_SELECT_SINGLE 0 +#define UI_FILEDIALOG_SELECT_MULTI 1 +#define UI_FILEDIALOG_SELECT_FOLDER 2 + +typedef struct UiDialogArgs { + const char *title; + const char *content; + const char *button1_label; + const char *button2_label; + const char *closebutton_label; + const char *input_value; + UiBool input; + UiBool password; + ui_callback result; + void *resultdata; +} UiDialogArgs; -char* ui_openfiledialog(UiObject *obj); -char* ui_savefiledialog(UiObject *obj); +typedef struct UiDialogWindowArgs { + UiTri modal; + UiTri titlebar_buttons; + UiTri show_closebutton; + const char *title; + const char *lbutton1; + const char *lbutton2; + const char *rbutton3; + const char *rbutton4; + const int *lbutton1_groups; + const int *lbutton2_groups; + const int *rbutton3_groups; + const int *rbutton4_groups; + int default_button; + int width; + int height; + ui_callback onclick; + void *onclickdata; +} UiDialogWindowArgs; + +UIEXPORT UiObject *ui_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args); + +#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, (UiDialogWindowArgs){ __VA_ARGS__ }); +#define ui_dialog_window0(parent) ui_dialog_window_create(parent, (UiDialogWindowArgs){ 0 }); + +UIEXPORT void ui_window_size(UiObject *obj, int width, int height); + +#define ui_dialog(parent, ...) ui_dialog_create(parent, (UiDialogArgs){ __VA_ARGS__ } ) + +UIEXPORT void ui_dialog_create(UiObject *parent, UiDialogArgs args); + +UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata); +UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata); + + #ifdef __cplusplus }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/App.idl Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +namespace winui +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/App.xaml Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Application + x:Class="winui.App" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:winui"> + <Application.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> + <!-- Other merged dictionaries here --> + </ResourceDictionary.MergedDictionaries> + <!-- Other app resources here --> + <SolidColorBrush x:Key="WindowCaptionBackground">Transparent</SolidColorBrush> + <SolidColorBrush x:Key="WindowCaptionBackgroundDisabled">Transparent</SolidColorBrush> + </ResourceDictionary> + </Application.Resources> +</Application>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/App.xaml.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" + +#include "App.xaml.h" +#include "MainWindow.xaml.h" + +#include "toolkit.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::Navigation; +using namespace winui; +using namespace winui::implementation; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +/// <summary> +/// Initializes the singleton application object. This is the first line of authored code +/// executed, and as such is the logical equivalent of main() or WinMain(). +/// </summary> +App::App() +{ + InitializeComponent(); + +#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION + UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) + { + if (IsDebuggerPresent()) + { + auto errorMessage = e.Message(); + __debugbreak(); + } + }); +#endif +} + +/// <summary> +/// Invoked when the application is launched. +/// </summary> +/// <param name="e">Details about the launch request and process.</param> +void App::OnLaunched(LaunchActivatedEventArgs const&) +{ + ui_app_run_startup(); + //window = make<MainWindow>(); + //window.Activate(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/App.xaml.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once + +#include "App.xaml.g.h" + +namespace winrt::winui::implementation +{ + struct App : AppT<App> + { + App(); + + void OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&); + + private: + winrt::Microsoft::UI::Xaml::Window window{ nullptr }; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/MainWindow.idl Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +namespace winui +{ + [default_interface] + runtimeclass MainWindow : Microsoft.UI.Xaml.Window + { + MainWindow(); + Int32 MyProperty; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/MainWindow.xaml Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<Window + x:Class="winui.MainWindow" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:winui" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d"> + + <Window.SystemBackdrop> + <MicaBackdrop /> + </Window.SystemBackdrop> + + <Grid RowDefinitions="Auto, Auto" ColumnDefinitions="*" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + <StackPanel x:Name="AppTitleBar" Orientation="Horizontal" Grid.Column="0" Grid.Row="0" Padding="10,5,5,10"> + <TextBlock x:Name="AppTitleTextBlock" Text="Window Title" CanDrag="True"></TextBlock> + </StackPanel> + </Grid> +</Window>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/MainWindow.xaml.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h" +#include "MainWindow.xaml.h" +#if __has_include("MainWindow.g.cpp") +#include "MainWindow.g.cpp" +#endif + +using namespace winrt; +using namespace Microsoft::UI::Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace winrt::winui::implementation +{ + MainWindow::MainWindow() + { + InitializeComponent(); + //ExtendsContentIntoTitleBar(true); + //SetTitleBar(AppTitleBar()); + } + + int32_t MainWindow::MyProperty() + { + throw hresult_not_implemented(); + } + + void MainWindow::MyProperty(int32_t /* value */) + { + throw hresult_not_implemented(); + } + + void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) + { + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/MainWindow.xaml.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once + +#include "MainWindow.g.h" + +namespace winrt::winui::implementation +{ + struct MainWindow : MainWindowT<MainWindow> + { + MainWindow(); + + int32_t MyProperty(); + void MyProperty(int32_t value); + + void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args); + }; +} + +namespace winrt::winui::factory_implementation +{ + struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow> + { + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/Package.appxmanifest Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Package + xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" + xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" + xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" + IgnorableNamespaces="uap rescap"> + + <Identity + Name="e3554f39-079b-4a0b-aa08-4ce5c4306644" + Publisher="CN=Olaf" + Version="1.0.0.0" /> + + <mp:PhoneIdentity PhoneProductId="e3554f39-079b-4a0b-aa08-4ce5c4306644" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> + + <Properties> + <DisplayName>winui</DisplayName> + <PublisherDisplayName>Olaf</PublisherDisplayName> + <Logo>Assets\StoreLogo.png</Logo> + </Properties> + + <Dependencies> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> + </Dependencies> + + <Resources> + <Resource Language="x-generate"/> + </Resources> + + <Applications> + <Application Id="App" + Executable="$targetnametoken$.exe" + EntryPoint="$targetentrypoint$"> + <uap:VisualElements + DisplayName="winui" + Description="winui" + BackgroundColor="transparent" + Square150x150Logo="Assets\Square150x150Logo.png" + Square44x44Logo="Assets\Square44x44Logo.png"> + <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" /> + <uap:SplashScreen Image="Assets\SplashScreen.png" /> + </uap:VisualElements> + </Application> + </Applications> + + <Capabilities> + <rescap:Capability Name="runFullTrust" /> + </Capabilities> +</Package>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/app.manifest Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="winui.app"/> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8. + For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1 + + It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.--> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + </windowsSettings> + </application> +</assembly> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/appmenu.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,295 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "appmenu.h" + +#include <cx/linked_list.h> +#include <cx/array_list.h> + +#include "../common/context.h" +#include "../common/object.h" + +#include "util.h" + + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::XamlTypeInfo; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Windows::UI::Xaml::Interop; + +static void add_top_menu_widget(MenuBar &parent, int i, UiMenuItemI* item, UiObject* obj); + +static void add_menu_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); +static void add_menuitem_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); +static void add_menuseparator_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); +static void add_checkitem_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); +static void add_radioitem_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); +static void add_menuitem_list_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj); + +static ui_menu_add_f createMenuItem[] = { + /* UI_MENU */ add_menu_widget, + /* UI_MENU_ITEM */ add_menuitem_widget, + /* UI_MENU_CHECK_ITEM */ add_checkitem_widget, + /* UI_MENU_RADIO_ITEM */ add_radioitem_widget, + /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_CHECKITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_RADIOITEM_LIST */ add_menuitem_list_widget, + /* UI_MENU_SEPARATOR */ add_menuseparator_widget +}; + +winrt::Microsoft::UI::Xaml::Controls::MenuBar ui_create_menubar(UiObject* obj) { + MenuBar mb = MenuBar(); + + UiMenu* menus_begin = uic_get_menu_list(); + + UiMenu* ls = menus_begin; + while (ls) { + UiMenu* menu = ls; + add_top_menu_widget(mb, 0, &menu->item, obj); + + ls = (UiMenu*)ls->item.next; + } + + return mb; +} + +static void add_top_menu_widget(MenuBar& parent, int i, UiMenuItemI* item, UiObject* obj) { + UiMenu* menu = (UiMenu*)item; + + MenuBarItem mi = MenuBarItem(); + wchar_t* wlabel = str2wstr(menu->label, NULL); + mi.Title(wlabel); + free(wlabel); + + UiMenuItemI* it = menu->items_begin; + int index = 0; + while (it) { + createMenuItem[it->type](mi.Items(), index, it, obj); + + it = it->next; + index++; + } + + parent.Items().Append(mi); +} + +static void add_menu_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj) { + UiMenu* menu = (UiMenu*)item; + + MenuFlyoutSubItem mi = MenuFlyoutSubItem(); + wchar_t* wlabel = str2wstr(menu->label, NULL); + mi.Text(wlabel); + free(wlabel); + + parent.Append(mi); + + UiMenuItemI* it = menu->items_begin; + int index = 0; + while (it) { + createMenuItem[it->type](mi.Items(), index, it, obj); + + it = it->next; + index++; + } + + +} + +static void add_menuitem_widget(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int i, UiMenuItemI* item, UiObject* obj) { + UiMenuItem* it = (UiMenuItem*)item; + + MenuFlyoutItem mi = MenuFlyoutItem(); + wchar_t* wlabel = str2wstr(it->label, NULL); + mi.Text(wlabel); + free(wlabel); + + parent.Append(mi); +} + +static void add_menuseparator_widget( + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, + int i, + UiMenuItemI* item, + UiObject* obj) +{ + +} + +static void add_checkitem_widget( + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, + int i, + UiMenuItemI* item, + UiObject* obj) +{ + +} + +static void add_radioitem_widget( + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, + int i, + UiMenuItemI* item, + UiObject* obj) +{ + +} + + +class UiMenuList { +public: + UiObject *obj = nullptr; + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent = { nullptr }; + UiMenuItemType type; + int prevSize = 0; + int insertPos = 0; + UiVar* var = nullptr; + ui_getvaluefunc getvalue = nullptr; + ui_callback callback = nullptr; + void* userdata = nullptr; + + UiMenuList() { + + } + + void updateItems() { + UiList* list = (UiList*)var->value; + + // delete previous items + for (int i = 0; i < prevSize; i++) { + parent.RemoveAt(insertPos); + } + + // insert new items + int count = 0; + void* elm = list->first(list); + while (elm) { + char *menuItemLabel = (char*) (getvalue ? getvalue(elm, 0) : elm); + + MenuFlyoutItem mi = MenuFlyoutItem(); + wchar_t* wlabel = str2wstr(menuItemLabel ? menuItemLabel : "", NULL); + mi.Text(wlabel); + free(wlabel); + + if (callback) { + mi.Click([this, elm, count](Windows::Foundation::IInspectable const& sender, RoutedEventArgs const& e) + { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = elm; + evt.intval = count; + callback(&evt, userdata); + }); + } + + parent.InsertAt(insertPos + count, mi); + + elm = list->next(list); + count++; + } + + prevSize = count; + } +}; + +extern "C" void destroy_ui_menu_list(void* ptr) { + UiMenuList* ls = (UiMenuList*)ptr; + delete ls; +} + +static void ui_context_add_menu_list_destructor(UiContext* ctx, UiMenuList* list) { + cxMempoolRegister(ctx->mp, list, destroy_ui_menu_list); +} + +static void ui_menulist_update(UiEvent* event, void* userdata) { + UiMenuList* mlist = (UiMenuList*)userdata; + mlist->updateItems(); +} + +static void add_menuitem_list_widget( + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, + int i, + UiMenuItemI* item, + UiObject* obj) +{ + UiMenuItemList* it = (UiMenuItemList*)item; + if (!it->varname) { + return; + } + + uint32_t size = parent.Size(); + + UiVar* var = uic_create_var(ui_global_context(), it->varname, UI_VAR_LIST); + + UiMenuList* mlist = new UiMenuList(); + mlist->obj = obj; + mlist->parent = parent; + mlist->getvalue = it->getvalue; + mlist->callback = it->callback; + mlist->userdata = it->userdata; + mlist->prevSize = 0; + mlist->insertPos = size; + mlist->type = item->type; + mlist->var = var; + ui_context_add_menu_list_destructor(obj->ctx, mlist); + + UiList* list = (UiList*)var->value; + list->observers = ui_add_observer(list->observers, ui_menulist_update, mlist); + + mlist->updateItems(); +} + + + + +winrt::Microsoft::UI::Xaml::Controls::MenuFlyout ui_create_menu_flyout(UiObject* obj, UiMenu* menudef) { + MenuFlyout flyout = MenuFlyout(); + + UiMenuItemI* it = menudef->items_begin; + int index = 0; + while (it) { + createMenuItem[it->type](flyout.Items(), index, it, obj); + + it = it->next; + index++; + } + + return flyout; +} + +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget) { + return NULL; +} + +void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) { + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/appmenu.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "toolkit.h" +#include "../common/menu.h" + +#include <Windows.h> +#undef GetCurrentTime +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h> +#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h> +#include <winrt/Microsoft.UI.Xaml.Markup.h> + + + +typedef void(*ui_menu_add_f)(winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::UI::Xaml::Controls::MenuFlyoutItemBase> parent, int, UiMenuItemI*, UiObject*); + +winrt::Microsoft::UI::Xaml::Controls::MenuBar ui_create_menubar(UiObject* obj); + +winrt::Microsoft::UI::Xaml::Controls::MenuFlyout ui_create_menu_flyout(UiObject* obj, UiMenu* menudef); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/button.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,408 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "button.h" + +#include "util.h" +#include "container.h" +#include "icons.h" + +#include "../common/object.h" +#include "../common/context.h" + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; + + + +void ui_set_button_label(ButtonBase button, const char* label, const char* stockid, const char *icon, UiLabelType type) { + // TODO: stockid + + if (type == UI_LABEL_ICON) { + label = NULL; + } + else if (type == UI_LABEL_TEXT) { + icon = NULL; + } + + IconElement icon_elm = { nullptr }; + if (icon) { + icon_elm = ui_get_icon(icon); + } + + if (label && icon_elm) { + StackPanel panel = StackPanel(); + panel.Orientation(Orientation::Horizontal); + panel.Spacing(5); + + panel.Children().Append(icon_elm); + + wchar_t* wlabel = str2wstr(label, nullptr); + TextBlock label = TextBlock(); + label.Text(wlabel); + panel.Children().Append(label); + free(wlabel); + + button.Content(panel); + } + else if (label) { + wchar_t* wlabel = str2wstr(label, nullptr); + button.Content(box_value(wlabel)); + free(wlabel); + } + else if (icon_elm) { + button.Content(ui_get_icon(icon)); + } +} + + +UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { + UiObject* current = uic_current_obj(obj); + + // create button with label + Button button = Button(); + ui_set_button_label(button, args.label, args.stockid, args.icon, args.labeltype); + + // create toolkit wrapper object and register destructor + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // register callback + if (args.onclick) { + ui_callback cbfunc = args.onclick; + void* cbdata = args.onclickdata; + button.Click([cbfunc, cbdata, obj](IInspectable const& sender, RoutedEventArgs) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = 0; + cbfunc(&evt, cbdata); + }); + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(button, false); + + return widget; +} + + +void togglebutton_register_checked_observers(ToggleButton button, UiObject* obj, UiVar* var) { + button.Checked([button, obj, var](IInspectable const& sender, RoutedEventArgs) { + UiInteger* i = (UiInteger*)var->value; + UiEvent evt = ui_create_int_event(obj, i->get(i)); + ui_notify_evt(i->observers, &evt); + }); +} + +void togglebutton_register_unchecked_observers(ToggleButton button, UiObject* obj, UiVar* var) { + button.Unchecked([button, obj, var](IInspectable const& sender, RoutedEventArgs) { + UiInteger* i = (UiInteger*)var->value; + UiEvent evt = ui_create_int_event(obj, i->get(i)); + ui_notify_evt(i->observers, &evt); + }); +} + +void togglebutton_register_callback(ToggleButton button, UiObject *obj, UiToggleArgs& args) { + ui_callback callback = args.onchange; + void* cbdata = args.onchangedata; + if (callback) { + button.Checked([button, obj, callback, cbdata](IInspectable const& sender, RoutedEventArgs) { + UiEvent evt = ui_create_int_event(obj, true); + callback(&evt, cbdata); + }); + button.Unchecked([button, obj, callback, cbdata](IInspectable const& sender, RoutedEventArgs) { + UiEvent evt = ui_create_int_event(obj, false); + callback(&evt, cbdata); + }); + } +} + +// for some stupid reason the ToggleSwitch is not a ToggleButton and we need to write the same +// code again, because although everything is basically the same, it is named differently +static void switch_register_observers(ToggleSwitch button, UiObject* obj, UiVar* var) { + button.Toggled([button, obj, var](IInspectable const& sender, RoutedEventArgs) { + UiInteger* i = (UiInteger*)var->value; + UiEvent evt = ui_create_int_event(obj, i->get(i)); + ui_notify_evt(i->observers, &evt); + }); +} + +static void switch_register_callback(ToggleSwitch button, UiObject* obj, UiToggleArgs& args) { + ui_callback callback = args.onchange; + void* cbdata = args.onchangedata; + if (callback) { + button.Toggled([button, obj, callback, cbdata](IInspectable const& sender, RoutedEventArgs) { + UiEvent evt = ui_create_int_event(obj, button.IsOn()); + callback(&evt, cbdata); + }); + } +} + +static void togglebutton_changed(UiObject *obj, bool checked, ui_callback onchange, void *onchangedata, int enable_state) { + if (onchange) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = checked; + onchange(&evt, onchangedata); + } + if (enable_state > 0) { + if (checked) { + ui_set_group(obj->ctx, enable_state); + } else { + ui_unset_group(obj->ctx, enable_state); + } + } +} + +static UIWIDGET create_togglebutton(UiObject *obj, ToggleButton button, UiToggleArgs args) { + UiObject* current = uic_current_obj(obj); + + // set label + ui_set_button_label(button, args.label, args.stockid, args.icon, args.labeltype); + togglebutton_register_callback(button, obj, args); + + // create toolkit wrapper object and register destructor + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // bind variable + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + if (var) { + UiInteger* value = (UiInteger*)var->value; + value->obj = widget; + value->get = ui_toggle_button_get; + value->set = ui_toggle_button_set; + + // listener for notifying observers + togglebutton_register_checked_observers(button, obj, var); + togglebutton_register_unchecked_observers(button, obj, var); + } + + if (args.enable_group > 0 || args.onchange) { + button.Checked([obj, args](IInspectable const& sender, RoutedEventArgs) { + togglebutton_changed(obj, true, args.onchange, args.onchangedata, args.enable_group); + }); + button.Unchecked([obj, args](IInspectable const& sender, RoutedEventArgs) { + togglebutton_changed(obj, false, args.onchange, args.onchangedata, args.enable_group); + }); + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(button, false); + + return widget; +} + +UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { + ToggleButton button = ToggleButton(); + return create_togglebutton(obj, button, args); +} + +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + CheckBox button = CheckBox(); + return create_togglebutton(obj, button, args); +} + +UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { + ToggleSwitch button = ToggleSwitch(); + if (args.label) { + wchar_t* wlabel = str2wstr(args.label, nullptr); + button.Header(box_value(wlabel)); + free(wlabel); + } + switch_register_callback(button, obj, args); + + UiObject* current = uic_current_obj(obj); + + // create toolkit wrapper object and register destructor + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // bind variable + UiVar* var = nullptr; + if (args.value) { + var = uic_create_value_var(current->ctx, args.value); + } + else if (args.varname) { + var = uic_create_var(obj->ctx, args.varname, UI_VAR_INTEGER); + } + if (var) { + UiInteger* value = (UiInteger*)var->value; + value->obj = widget; + value->get = ui_toggle_button_get; + value->set = ui_toggle_button_set; + + // listener for notifying observers + switch_register_observers(button, obj, var); + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(button, false); + + return widget; +} + +UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) { + RadioButton button = RadioButton(); + + UiObject* current = uic_current_obj(obj); + + // set label + ui_set_button_label(button, args.label, args.stockid, args.icon, args.labeltype); + togglebutton_register_callback(button, obj, args); + + // create toolkit wrapper object and register destructor + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + UiVar* var = nullptr; + if (args.value) { + var = uic_create_value_var(current->ctx, args.value); + } + else if (args.varname) { + var = uic_create_var(obj->ctx, args.varname, UI_VAR_INTEGER); + } + + // bind radio button to the value + if (var) { + UiInteger* value = (UiInteger*)var->value; + + // store a list of radio buttons in the value + CxList* radioButtons = (CxList*) (value->obj ? value->obj : cxLinkedListCreate(current->ctx->allocator, NULL, CX_STORE_POINTERS)); + // get or create the group name + static int groupCount = 0; + winrt::hstring groupName; + if (cxListSize(radioButtons) == 0) { + groupName = winrt::to_hstring(groupCount++); + } else { + UiWidget* firstButtonWidget = (UiWidget*)cxListAt(radioButtons, 0); + RadioButton firstRadioButton = firstButtonWidget->uielement.as<RadioButton>(); + groupName = firstRadioButton.GroupName(); + } + + // set the group name for the new radiobutton + cxListAdd(radioButtons, widget); + button.GroupName(groupName); + + value->obj = radioButtons; + value->get = ui_radio_button_get; + value->set = ui_radio_button_set; + + // listener for notifying observers (only checked, not unchecked) + togglebutton_register_checked_observers(button, obj, var); + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(button, false); + + return widget; +} + + +int64_t ui_toggle_button_get(UiInteger* integer) { + UiWidget* widget = (UiWidget*)integer->obj; + ToggleButton toggleButton = widget->uielement.as<ToggleButton>(); + int val = toggleButton.IsChecked().GetBoolean(); + integer->value = val; + return val; +} + +void ui_toggle_button_set(UiInteger* integer, int64_t value) { + UiWidget* widget = (UiWidget*)integer->obj; + ToggleButton toggleButton = widget->uielement.as<ToggleButton>(); + toggleButton.IsChecked((bool)value); + integer->value = value; +} + +int64_t ui_switch_get(UiInteger * integer) { + UiWidget* widget = (UiWidget*)integer->obj; + ToggleSwitch toggleButton = widget->uielement.as<ToggleSwitch>(); + int val = toggleButton.IsOn(); + integer->value = val; + return val; +} + +void ui_switch_set(UiInteger * integer, int64_t value) { + UiWidget* widget = (UiWidget*)integer->obj; + ToggleSwitch toggleButton = widget->uielement.as<ToggleSwitch>(); + toggleButton.IsOn((bool)value); + integer->value = value; +} + +int64_t ui_radio_button_get(UiInteger * integer) { + CxList* list = (CxList*)integer->obj; + CxIterator i = cxListIterator(list); + int selection = -1; + cx_foreach(UiWidget*, widget, i) { + ToggleButton button = widget->uielement.as<ToggleButton>(); + if (button.IsChecked().GetBoolean()) { + selection = i.index; + break; + } + } + integer->value = selection; + return selection; +} + +void ui_radio_button_set(UiInteger * integer, int64_t value) { + CxList* list = (CxList*)integer->obj; + UiWidget* widget = (UiWidget*)cxListAt(list, value); + if (widget) { + ToggleButton button = widget->uielement.as<ToggleButton>(); + button.IsChecked(true); + integer->value = value; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/button.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "toolkit.h" +#include "../ui/container.h" + +#include "../ui/button.h" + +#include "../common/context.h" + +void ui_set_button_label(winrt::Microsoft::UI::Xaml::Controls::Primitives::ButtonBase button, const char* label, const char* stockid, const char *icon, UiLabelType type); + +void togglebutton_register_checked_observers(winrt::Microsoft::UI::Xaml::Controls::Primitives::ToggleButton button, UiObject* obj, UiVar* var); +void togglebutton_register_unchecked_observers(winrt::Microsoft::UI::Xaml::Controls::Primitives::ToggleButton button, UiObject* obj, UiVar* var); +void togglebutton_register_callback(winrt::Microsoft::UI::Xaml::Controls::Primitives::ToggleButton button, UiObject* obj, UiToggleArgs& args); + +extern "C" int64_t ui_toggle_button_get(UiInteger * integer); +extern "C" void ui_toggle_button_set(UiInteger * integer, int64_t value); + +extern "C" int64_t ui_switch_get(UiInteger * integer); +extern "C" void ui_switch_set(UiInteger * integer, int64_t value); + +extern "C" int64_t ui_radio_button_get(UiInteger * integer); +extern "C" void ui_radio_button_set(UiInteger * integer, int64_t value);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/commandbar.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,218 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "commandbar.h" + +#include "util.h" +#include "../common/object.h" +#include "../common/context.h" + +#include "button.h" +#include "appmenu.h" +#include "icons.h" + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::XamlTypeInfo; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Windows::UI::Xaml::Interop; + +static void create_item(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarItemI* i); +static void create_cmditem(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarItem* item); +static void create_toggleitem(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarToggleItem* item); +static void create_menuitem(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarMenuItem* item); + +static void create_appmenu_items(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarMenuItem* i); + +CommandBar ui_create_toolbar(UiObject *obj, CxList* defaults, bool addappmenu) { + + CommandBar cb = CommandBar(); + cb.DefaultLabelPosition(CommandBarDefaultLabelPosition::Right); + + // add pre-configured items + CxIterator i = cxListIterator(defaults); + cx_foreach(char*, def, i) { + UiToolbarItemI* item = uic_toolbar_get_item(def); + if (!item) { + exit(-1); // TODO: maybe an error dialog? + } + create_item(obj, cb.PrimaryCommands(), item); + } + + // add appmenu + if (addappmenu) { + UiToolbarMenuItem* appmenu = uic_get_appmenu(); + if (appmenu) { + create_appmenu_items(obj, cb.SecondaryCommands(), appmenu); + } + } + + return cb; +} + +static void create_item(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarItemI* i) { + switch (i->type) { + case UI_TOOLBAR_ITEM: { + create_cmditem(obj, cb, (UiToolbarItem*)i); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + create_toggleitem(obj, cb, (UiToolbarToggleItem*)i); + break; + } + case UI_TOOLBAR_MENU: { + create_menuitem(obj, cb, (UiToolbarMenuItem*)i); + break; + } + } +} + +static void create_appmenu_items(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarMenuItem* i) { + for (UiMenuItemI* mi = i->menu.items_begin; mi; mi = mi->next) { + // convert UiMenuItemI to UiToolbarItemI + switch (mi->type) { + case UI_MENU: { + UiMenu* mitem = (UiMenu*)mi; + UiToolbarMenuItem tbitem; + memset(&tbitem, 0, sizeof(UiToolbarMenuItem)); + tbitem.item.type = UI_TOOLBAR_MENU; + tbitem.args.label = mitem->label; + tbitem.menu.items_begin = mitem->items_begin; + tbitem.menu.items_end = mitem->items_end; + create_menuitem(obj, cb, &tbitem); + break; + } + case UI_MENU_ITEM: { + UiMenuItem* mitem = (UiMenuItem*)mi; + UiToolbarItem tbitem; + memset(&tbitem, 0, sizeof(UiToolbarItem)); + tbitem.item.type = UI_TOOLBAR_ITEM; + tbitem.args.label = mitem->label; + tbitem.args.onclick = mitem->callback; + tbitem.args.onclickdata = mitem->userdata; + create_cmditem(obj, cb, &tbitem); + break; + } + } + } +} + +static void create_cmditem(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarItem* item) { + AppBarButton button = AppBarButton(); + if (item->args.label) { + wchar_t* wlabel = str2wstr(item->args.label, nullptr); + button.Label(wlabel); + free(wlabel); + } + if(item->args.icon) { + button.Icon(ui_get_icon(item->args.icon)); + } + + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(obj->ctx, widget); + ui_set_widget_groups(obj->ctx, widget, item->args.groups); + + // register callback + if (item->args.onclick) { + ui_callback cbfunc = item->args.onclick; + void* cbdata = item->args.onclickdata; + button.Click([cbfunc, cbdata, obj](Windows::Foundation::IInspectable const& sender, RoutedEventArgs) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = 0; + cbfunc(&evt, cbdata); + }); + } + + cb.Append(button); +} + +static void create_toggleitem(UiObject *obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarToggleItem* item) { + AppBarToggleButton button = AppBarToggleButton(); + if (item->args.label) { + wchar_t* wlabel = str2wstr(item->args.label, nullptr); + button.Label(wlabel); + free(wlabel); + } + if (item->args.icon) { + button.Icon(ui_get_icon(item->args.icon)); + } + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, nullptr, item->args.varname, UI_VAR_INTEGER); + if (var) { + UIElement elm = button; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(obj->ctx, widget); + ui_set_widget_groups(obj->ctx, widget, item->args.groups); + + UiInteger* value = (UiInteger*)var->value; + int64_t i = value->value; + value->get = ui_toggle_button_get; + value->set = ui_toggle_button_set; + value->obj = widget; + ui_toggle_button_set(value, i); // init togglebutton state + + // listener for notifying observers + togglebutton_register_checked_observers(button, obj, var); + togglebutton_register_unchecked_observers(button, obj, var); + } + + UiToggleArgs args = {}; + args.onchange = item->args.onchange; + args.onchangedata = item->args.onchangedata; + togglebutton_register_callback(button, obj, args); + + + cb.Append(button); +} + +static void create_menuitem(UiObject* obj, Windows::Foundation::Collections::IObservableVector<ICommandBarElement> cb, UiToolbarMenuItem* item) { + AppBarButton button = AppBarButton(); + if (item->args.label) { + wchar_t* wlabel = str2wstr(item->args.label, nullptr); + button.Label(wlabel); + free(wlabel); + } + if (item->args.icon) { + button.Icon(ui_get_icon(item->args.icon)); + } + + MenuFlyoutItem mi = MenuFlyoutItem(); + + MenuFlyout flyout = ui_create_menu_flyout(obj, &item->menu); + button.Flyout(flyout); + + cb.Append(button); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/commandbar.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "toolkit.h" +#include "../ui/toolbar.h" +#include "../common/toolbar.h" + +#include <Windows.h> +#undef GetCurrentTime +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h> +#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h> +#include <winrt/Microsoft.UI.Xaml.Markup.h> + +winrt::Microsoft::UI::Xaml::Controls::CommandBar ui_create_toolbar(UiObject *obj, CxList* defaults, bool addappmenu); + +extern "C" int64_t ui_appbar_togglebutton_get(UiInteger * integer); +extern "C" void ui_appbar_togglebutton_set(UiInteger * integer, int64_t value); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/condvar.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" +#include "condvar.h" + + + +UiCondVar* ui_condvar_create(void) { + UiWinCondVar *var = new UiWinCondVar(); + var->var.data = NULL; + var->var.intdata = 0; + var->set = 0; + return (UiCondVar*)var; +} + +void ui_condvar_wait(UiCondVar *var) { + UiWinCondVar *p = (UiWinCondVar*)var; + std::unique_lock<std::mutex> lock(p->mutex); + + if(!p->set) { + p->cond.wait(lock); + } + p->set = 0; +} + +void ui_condvar_signal(UiCondVar *var, void *data, int intdata) { + UiWinCondVar *p = (UiWinCondVar*)var; + std::unique_lock<std::mutex> lock(p->mutex); + p->var.data = data; + p->var.intdata = intdata; + p->set = 1; + lock.unlock(); + p->cond.notify_one(); +} + +void ui_condvar_destroy(UiCondVar *var) { + UiWinCondVar *p = (UiWinCondVar*)var; + delete p; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/condvar.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,55 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2024 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UI_WIN_CONDVAR_H +#define UI_WIN_CONDVAR_H + +#include "toolkit.h" + +#include <queue> +#include <mutex> +#include <condition_variable> + +#ifdef __cplusplus +extern "C" { +#endif + +struct UiWinCondVar { + UiCondVar var; + int set; + std::mutex mutex; + std::condition_variable cond; +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* UI_WIN_CONDVAR_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/container.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,932 @@ +/* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +* +* Copyright 2023 Olaf Wintermann. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "pch.h" + +#include "container.h" + +#include "../common/context.h" +#include "../common/object.h" + +#include "util.h" + + +void ui_container_begin_close(UiObject* obj) { + UiContainer* ct = uic_get_current_container(obj); + ct->close = 1; +} + +int ui_container_finish(UiObject* obj) { + UiContainer* ct = uic_get_current_container(obj); + if (ct->close) { + ui_end(obj); + return 0; + } + return 1; +} + + +// --------------------- UiBoxContainer --------------------- + +static UIWIDGET ui_box(UiObject* obj, UiContainerArgs args, UiBoxContainerType type) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + + Grid grid = Grid(); + current->container->Add(grid, true); + + UIElement elm = grid; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = new UiBoxContainer(grid, type, args.margin, args.spacing); + ui_context_add_container_destructor(current->ctx, newobj->container); + uic_obj_add(obj, newobj); + + return widget; +} + +UIWIDGET ui_vbox_create(UiObject* obj, UiContainerArgs args) { + return ui_box(obj, args, UI_BOX_CONTAINER_VBOX); +} + +UIWIDGET ui_hbox_create(UiObject* obj, UiContainerArgs args) { + return ui_box(obj, args, UI_BOX_CONTAINER_HBOX); +} + +UiBoxContainer::UiBoxContainer(Grid grid, enum UiBoxContainerType type, int margin, int spacing) { + this->grid = grid; + this->type = type; + + Thickness t = { (double)margin, (double)margin, (double)margin, (double)margin }; + grid.Margin(t); + grid.ColumnSpacing((double)spacing); + grid.RowSpacing((double)spacing); + + GridLength gl; + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + + // hbox needs one row def, vbox needs one col def + // all other col/row defs are created when elements are added + if (type == UI_BOX_CONTAINER_HBOX) { + boxRowDef = RowDefinition(); + boxRowDef.Height(gl); + grid.RowDefinitions().Append(boxRowDef); + } else { + boxColDef = ColumnDefinition(); + boxColDef.Width(gl); + grid.ColumnDefinitions().Append(boxColDef); + } + + ui_reset_layout(layout); +} + +void UiBoxContainer::Add(FrameworkElement control, UiBool fill) { + if (this->layout.fill != UI_LAYOUT_UNDEFINED) { + fill = ui_lb2bool(this->layout.fill); + } + + GridLength gl; + if (fill) { + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + } + else { + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + } + + control.HorizontalAlignment(HorizontalAlignment::Stretch); + control.VerticalAlignment(VerticalAlignment::Stretch); + + if (type == UI_CONTAINER_HBOX) { + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + grid.ColumnDefinitions().Append(coldef); + grid.SetColumn(control, grid.Children().Size()); + grid.SetRow(control, 0); + } else { + RowDefinition rowdef = RowDefinition(); + rowdef.Height(gl); + grid.RowDefinitions().Append(rowdef); + grid.SetRow(control, grid.Children().Size()); + grid.SetColumn(control, 0); + } + + grid.Children().Append(control); + + ui_reset_layout(layout); +} + + +// --------------------- UiGridContainer --------------------- + +UIWIDGET ui_grid_create(UiObject* obj, UiContainerArgs args) { + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + + Grid grid = Grid(); + current->container->Add(grid, true); + + UIElement elm = grid; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = new UiGridContainer(grid, args.margin, args.columnspacing, args.rowspacing); + ui_context_add_container_destructor(current->ctx, newobj->container); + uic_obj_add(obj, newobj); + + return widget; +} + +UiGridContainer::UiGridContainer(Grid grid, int margin, int columnspacing, int rowspacing) { + this->grid = grid; + Thickness t = { (double)margin, (double)margin, (double)margin, (double)margin }; + grid.Margin(t); + grid.ColumnSpacing((double)columnspacing); + grid.RowSpacing((double)rowspacing); + ui_reset_layout(layout); +} + +void UiGridContainer::Add(FrameworkElement control, UiBool fill) { + GridLength gl; + + bool hexpand = false; + bool vexpand = false; + bool hfill = false; + bool vfill = false; + if(layout.fill != UI_LAYOUT_UNDEFINED) { + fill = ui_lb2bool(layout.fill); + } + if (layout.hexpand != UI_LAYOUT_UNDEFINED) { + hexpand = layout.hexpand; + hfill = true; + } + if (layout.vexpand != UI_LAYOUT_UNDEFINED) { + vexpand = layout.vexpand; + vfill = true; + } + if (fill) { + hfill = true; + vfill = true; + } + + // create new RowDefinition for the new line + if (layout.newline || y == -1) { + x = 0; + y++; + RowDefinition rowdef = RowDefinition(); + if (vexpand) { + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + } + else { + gl.GridUnitType = GridUnitType::Auto; + gl.Value = 0; + } + rowdef.Height(gl); + grid.RowDefinitions().Append(rowdef); + } else if (vexpand) { + // adjust row + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + grid.RowDefinitions().GetAt(y).Height(gl); + } + + // create new columndefinition, if a new column is added + if (x == cols) { + if (hexpand) { + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + } + else { + gl.GridUnitType = GridUnitType::Auto; + gl.Value = 0; + } + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + grid.ColumnDefinitions().Append(coldef); + cols++; + } else if(hexpand) { + // adjust column + if (layout.colspan == 0) { + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + grid.ColumnDefinitions().GetAt(x).Width(gl); + } else { + int adjust_col = x; + bool adjust = true; + for (int i = 0; i < layout.colspan; i++) { + if (grid.ColumnDefinitions().Size() == x + i) { + break; + } + adjust_col = x + i; + GridLength w = grid.ColumnDefinitions().GetAt(adjust_col).Width(); + if (w.GridUnitType == GridUnitType::Star) { + adjust = false; + break; + } + } + + if (adjust) { + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + grid.ColumnDefinitions().GetAt(adjust_col).Width(gl); + } + } + } + + // add control + if (hfill) { + control.HorizontalAlignment(HorizontalAlignment::Stretch); + } + if (vfill) { + control.VerticalAlignment(VerticalAlignment::Stretch); + } + + if (layout.colspan > 0) { + grid.SetColumnSpan(control, layout.colspan); + } + if (layout.rowspan > 0) { + grid.SetRowSpan(control, layout.rowspan); + } + + grid.SetRow(control, y); + grid.SetColumn(control, x); + grid.Children().Append(control); + + x++; + + ui_reset_layout(layout); +} + +// --------------------- UI Frame --------------------- + +UIWIDGET ui_frame_create(UiObject* obj, UiFrameArgs args) { + // create a grid for the frame, that contains the label and a sub-frame + Grid frame = Grid(); + + GridLength gl; + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + frame.ColumnDefinitions().Append(coldef); + + RowDefinition rowdefFrame = RowDefinition(); + rowdefFrame.Height(gl); + + // label + int row = 0; + if (args.label) { + RowDefinition rowdefLabel = RowDefinition(); + gl.GridUnitType = GridUnitType::Auto; + gl.Value = 0; + rowdefLabel.Height(gl); + frame.RowDefinitions().Append(rowdefLabel); + + TextBlock label = TextBlock(); + wchar_t* wlabel = str2wstr(args.label, nullptr); + winrt::hstring hstr(wlabel); + label.Text(hstr); + free(wlabel); + + frame.SetRow(label, row++); + frame.SetColumn(label, 0); + frame.Children().Append(label); + } + + // workarea frame + frame.RowDefinitions().Append(rowdefFrame); + + Grid workarea = Grid(); + frame.SetRow(workarea, row); + frame.SetColumn(workarea, 0); + frame.Children().Append(workarea); + + // some styling for the workarea + winrt::Microsoft::UI::Xaml::Media::SolidColorBrush brush{ winrt::Microsoft::UI::ColorHelper::FromArgb(150, 150, 150, 150) }; + workarea.BorderBrush(brush); + CornerRadius radius{ 8, 8, 8, 8 }; + Thickness t = { 1, 1, 1, 1 }; + workarea.CornerRadius(radius); + workarea.BorderThickness(t); + + Thickness padding = { 10, 10, 10, 10 }; + workarea.Padding(padding); + + // add frame to the parent container + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + current->container->Add(frame, true); + + UIElement elm = frame; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + // sub container + UiContainer* ctn = nullptr; + switch (args.subcontainer) { + default: + case UI_CONTAINER_VBOX: { + ctn = new UiBoxContainer(workarea, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_HBOX: { + ctn = new UiBoxContainer(workarea, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_GRID: { + ctn = new UiGridContainer(workarea, args.margin, args.columnspacing, args.rowspacing); + break; + } + } + ui_context_add_container_destructor(current->ctx, ctn); + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = ctn; + uic_obj_add(obj, newobj); + + return widget; +} + +// --------------------- UI Expander --------------------- + +UIWIDGET ui_expander_create(UiObject* obj, UiFrameArgs args) { + Expander expander = Expander(); + if (args.label) { + wchar_t* wlabel = str2wstr(args.label, nullptr); + expander.Header(box_value(wlabel)); + free(wlabel); + } + expander.IsExpanded(args.isexpanded); + + // add frame to the parent container + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + current->container->Add(expander, true); + + UIElement elm = expander; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + Grid content = Grid(); + expander.Content(content); + + UiContainer* ctn = nullptr; + switch (args.subcontainer) { + default: + case UI_CONTAINER_VBOX: { + ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_HBOX: { + ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_GRID: { + ctn = new UiGridContainer(content, args.margin, args.columnspacing, args.rowspacing); + break; + } + } + ui_context_add_container_destructor(current->ctx, ctn); + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = ctn; + uic_obj_add(obj, newobj); + + return widget; +} + +// --------------------- UI ScrolledWindow --------------------- + +UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs args) { + ScrollViewer scrollW = ScrollViewer(); + + // add frame to the parent container + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + current->container->Add(scrollW, true); + + UIElement elm = scrollW; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + // create child container + Grid content = Grid(); + scrollW.Content(content); + + UiContainer* ctn = nullptr; + switch (args.subcontainer) { + default: + case UI_CONTAINER_VBOX: { + ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_VBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_HBOX: { + ctn = new UiBoxContainer(content, UI_BOX_CONTAINER_HBOX, args.margin, args.spacing); + break; + } + case UI_CONTAINER_GRID: { + ctn = new UiGridContainer(content, args.margin, args.columnspacing, args.rowspacing); + break; + } + } + ui_context_add_container_destructor(current->ctx, ctn); + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = ctn; + uic_obj_add(obj, newobj); + + return widget; +} + +// --------------------- UI TabView --------------------- + +UiTabViewContainer::UiTabViewContainer(UiTabView* tabview) { + this->tabview = tabview; +} + +void UiTabViewContainer::Add(FrameworkElement control, UiBool fill) { + // noop +} + +static UiObject* create_subcontainer_obj(UiObject* current, Grid subcontainer, UiSubContainerType type, int margin, int spacing, int columnspacing, int rowspacing) { + UiContainer* ctn = nullptr; + switch (type) { + default: + case UI_CONTAINER_VBOX: { + ctn = new UiBoxContainer(subcontainer, UI_BOX_CONTAINER_VBOX, margin, spacing); + break; + } + case UI_CONTAINER_HBOX: { + ctn = new UiBoxContainer(subcontainer, UI_BOX_CONTAINER_HBOX, margin, spacing); + break; + } + case UI_CONTAINER_GRID: { + ctn = new UiGridContainer(subcontainer, margin, columnspacing, rowspacing); + break; + } + } + ui_context_add_container_destructor(current->ctx, ctn); + + UIElement elm = subcontainer; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + UiObject* newobj = uic_object_new(current, widget); + newobj->container = ctn; + return newobj; +} + +static UiTabView* tabview_pivot_create(UiObject* obj, UiTabViewArgs args) { + Pivot pivot = Pivot(); + UiPivotTabView* tabview = new UiPivotTabView(obj, pivot, args); + + return tabview; +} + +UiPivotTabView::UiPivotTabView(UiObject* obj, Pivot pivot, UiTabViewArgs args) { + this->current = obj; + this->pivot = pivot; + this->subcontainer = args.subcontainer; + this->margin = args.margin; + this->spacing = args.spacing; + this->columnspacing = args.columnspacing; + this->rowspacing = args.rowspacing; +} + +UiObject* UiPivotTabView::AddTab(const char* label, int index) { + TextBlock text = TextBlock(); + wchar_t* wlabel = str2wstr(label, nullptr); + winrt::hstring hstr(wlabel); + text.Text(hstr); + free(wlabel); + + PivotItem item = PivotItem(); + item.Header(text); + + // sub container + Grid subcontainer = Grid(); + item.Content(subcontainer); + pivot.Items().Append(item); + + return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing); +} + +void UiPivotTabView::Remove(int index) { + pivot.Items().RemoveAt(index); +} + +void UiPivotTabView::Select(int index) { + +} + +FrameworkElement UiPivotTabView::GetFrameworkElement() { + return pivot; +} + + +static UiTabView* tabview_invisible_create(UiObject *obj, UiTabViewArgs args) { + Grid container = Grid(); + container.HorizontalAlignment(HorizontalAlignment::Stretch); + container.VerticalAlignment(VerticalAlignment::Stretch); + UiInvisibleTabView *tabview = new UiInvisibleTabView(obj, container, args); + return tabview; +} + +UiInvisibleTabView::UiInvisibleTabView(UiObject* obj, Grid container, UiTabViewArgs args) { + this->current = obj; + this->container = container; + this->subcontainer = args.subcontainer; + this->margin = args.margin; + this->spacing = args.spacing; + this->columnspacing = args.columnspacing; + this->rowspacing = args.rowspacing; + this->currentIndex = -1; + + GridLength gl; + gl.GridUnitType = GridUnitType::Star; + gl.Value = 1; + + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + container.ColumnDefinitions().Append(coldef); + + RowDefinition rowdef = RowDefinition(); + rowdef.Height(gl); + container.RowDefinitions().Append(rowdef); +} + +UiObject* UiInvisibleTabView::AddTab(const char* label, int index) { + Grid subcontainer = Grid(); + subcontainer.HorizontalAlignment(HorizontalAlignment::Stretch); + subcontainer.VerticalAlignment(VerticalAlignment::Stretch); + + if (pages.size() == 0) { + container.Children().Append(subcontainer); + currentIndex = 0; + } + + if (index < 0) { + pages.push_back(subcontainer); + } else { + pages.insert(pages.begin() + index, subcontainer); + } + + // sub container + return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing); +} + +void UiInvisibleTabView::Remove(int index) { + +} + +void UiInvisibleTabView::Select(int index) { + if (index >= 0 && index < pages.size()) { + if (currentIndex != -1) { + container.Children().RemoveAt(0); + } + + container.Children().Append(pages.at(index)); + } +} + +FrameworkElement UiInvisibleTabView::GetFrameworkElement() { + return container; +} + + +static UiTabView* tabview_main_create(UiObject* obj, UiTabViewArgs args) { + TabView tabview = TabView(); + tabview.IsAddTabButtonVisible(false); + //tabview.CanDragTabs(false); + //tabview.CanReorderTabs(false); + UiMainTabView* uitabview = new UiMainTabView(obj, tabview, args); + + return uitabview; +} + +UiMainTabView::UiMainTabView(UiObject* obj, TabView tabview, UiTabViewArgs args) { + this->current = obj; + this->tabview = tabview; + this->subcontainer = args.subcontainer; + this->margin = args.margin; + this->spacing = args.spacing; + this->columnspacing = args.columnspacing; + this->rowspacing = args.rowspacing; +} + +UiObject* UiMainTabView::AddTab(const char* label, int index) { + TextBlock text = TextBlock(); + wchar_t* wlabel = str2wstr(label, nullptr); + winrt::hstring hstr(wlabel); + text.Text(hstr); + free(wlabel); + + TabViewItem item = TabViewItem(); + item.Header(text); + item.CanDrag(false); + item.IsClosable(false); + + // sub container + Grid subcontainer = Grid(); + item.Content(subcontainer); + tabview.TabItems().Append(item); + + return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing); +} + +void UiMainTabView::Remove(int index) { + this->tabview.TabItems().RemoveAt(index); +} + +void UiMainTabView::Select(int index) { + +} + +FrameworkElement UiMainTabView::GetFrameworkElement() { + return tabview; +} + + +static UiTabView* tabview_navigationview_create(UiObject* obj, UiTabViewArgs args, UiTabViewType type) { + NavigationView navigationview = NavigationView(); + UiNavigationTabView* tabview = new UiNavigationTabView(obj, navigationview, args, type); + navigationview.IsBackButtonVisible(NavigationViewBackButtonVisible::Collapsed); + navigationview.IsSettingsVisible(false); + + return tabview; +} + +UiNavigationTabView::UiNavigationTabView(UiObject* obj, NavigationView navigationview, UiTabViewArgs args, UiTabViewType type) { + this->current = obj; + this->navigationview = navigationview; + this->type = type; + this->margin = args.margin; + this->spacing = args.spacing; + this->columnspacing = args.columnspacing; + this->rowspacing = args.rowspacing; + + if (type == UI_TABVIEW_NAVIGATION_TOP) { + navigationview.PaneDisplayMode(NavigationViewPaneDisplayMode::Top); + } + + navigationview.SelectionChanged({ this, &UiNavigationTabView::SelectionChanged }); +} + +UiObject* UiNavigationTabView::AddTab(const char* label, int index1) { + TextBlock text = TextBlock(); + wchar_t* wlabel = str2wstr(label, nullptr); + winrt::hstring hstr(wlabel); + text.Text(hstr); + free(wlabel); + + NavigationViewItem item = NavigationViewItem(); + item.Content(text); + + // sub container + Grid subcontainer = Grid(); + if (pages.size() == 0) { + navigationview.Content(subcontainer); + navigationview.SelectedItem(item); + } + + navigationview.MenuItems().Append(item); + auto page = std::tuple<NavigationViewItem, FrameworkElement>{ item, subcontainer }; + pages.push_back(page); + + return create_subcontainer_obj(current, subcontainer, this->subcontainer, margin, spacing, columnspacing, rowspacing); +} + +void UiNavigationTabView::Remove(int index) { + navigationview.MenuItems().RemoveAt(index); + pages.erase(pages.begin() + index); +} + +void UiNavigationTabView::Select(int index) { + +} + +FrameworkElement UiNavigationTabView::GetFrameworkElement() { + return navigationview; +} + +void UiNavigationTabView::SelectionChanged(NavigationView const& sender, NavigationViewSelectionChangedEventArgs const& args) { + for (auto page : pages) { + NavigationViewItem item = std::get<0>(page); + FrameworkElement elm = std::get<1>(page); + if (item == navigationview.SelectedItem()) { + navigationview.Content(elm); + break; + } + } +} + +static int64_t ui_tabview_get(UiInteger *i) { + return 0; +} + +static void ui_tabview_set(UiInteger *i, int64_t value) { + UiTabView *tabview = (UiTabView*)i->obj; + tabview->Select(value); +} + +UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs args) { + UiTabViewType type = args.tabview == UI_TABVIEW_DEFAULT ? UI_TABVIEW_NAVIGATION_TOP2 : args.tabview; + UiTabView* tabview = nullptr; + switch (type) { + default: { + tabview = tabview_pivot_create(obj, args); + break; + } + case UI_TABVIEW_DOC: { + tabview = tabview_main_create(obj, args); + break; + } + case UI_TABVIEW_NAVIGATION_SIDE: { + tabview = tabview_navigationview_create(obj, args, type); + break; + } + case UI_TABVIEW_NAVIGATION_TOP: { + tabview = tabview_navigationview_create(obj, args, type); + break; + } + case UI_TABVIEW_NAVIGATION_TOP2: { + tabview = tabview_pivot_create(obj, args); + break; + } + case UI_TABVIEW_INVISIBLE: { + tabview = tabview_invisible_create(obj, args); + break; + } + } + UiTabViewContainer* ctn = new UiTabViewContainer(tabview); + + // add frame to the parent container + UiObject* current = uic_current_obj(obj); + UI_APPLY_LAYOUT1(current, args); + current->container->Add(tabview->GetFrameworkElement(), true); + + UIElement elm = tabview->GetFrameworkElement(); + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + widget->data1 = tabview; + + // TODO: add tabview destructor + + // bind variable + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_INTEGER); + if (var) { + UiInteger *i = (UiInteger*)var->value; + i->obj = tabview; + i->get = ui_tabview_get; + i->set = ui_tabview_set; + } + + UiObject* newobj = uic_object_new(obj, widget); + newobj->container = ctn; + uic_obj_add(obj, newobj); + + return widget; +} + +void ui_tab_create(UiObject* obj, const char* title) { + UiObject* current = uic_current_obj(obj); + UiTabView* tabview = (UiTabView*)current->widget->data1; + UiObject* newobj = tabview->AddTab(title); + uic_obj_add(current, newobj); +} + +UIEXPORT void ui_tabview_select(UIWIDGET tabview, int tab) { + UiTabView* t = (UiTabView*)tabview->data1; + t->Select(tab); +} + +UIEXPORT void ui_tabview_remove(UIWIDGET tabview, int tab) { + UiTabView* t = (UiTabView*)tabview->data1; + t->Remove(tab); +} + +UIEXPORT UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { + UiTabView* t = (UiTabView*)tabview->data1; + UiObject* newobj = t->AddTab(name, tab_index); + return newobj; +} + + + +// --------------------- UI Headerbar --------------------- + +// TODO: replace placeholder implementation + +UIEXPORT UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs args) { + UiContainerArgs boxargs = { }; + boxargs.fill = UI_OFF; + return ui_hbox_create(obj, boxargs); +} + +UIEXPORT void ui_headerbar_start_create(UiObject *obj) { + UiContainerArgs boxargs = { }; + boxargs.fill = UI_OFF; + ui_hbox_create(obj, boxargs); +} + +UIEXPORT void ui_headerbar_center_create(UiObject *obj) { + UiContainerArgs boxargs = { }; + boxargs.fill = UI_OFF; + ui_hbox_create(obj, boxargs); +} + +UIEXPORT void ui_headerbar_end_create(UiObject *obj) { + UiContainerArgs boxargs = { }; + boxargs.fill = UI_OFF; + ui_hbox_create(obj, boxargs); +} + + +/* +* -------------------- Layout Functions -------------------- +* +* functions for setting layout attributes for the current container +* +*/ + +void ui_layout_fill(UiObject* obj, UiBool fill) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.fill = ui_bool2lb(fill); +} + +void ui_layout_hexpand(UiObject* obj, UiBool expand) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.hexpand = expand; +} + +void ui_layout_vexpand(UiObject* obj, UiBool expand) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.vexpand = expand; +} + +void ui_layout_hfill(UiObject* obj, UiBool fill) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.hfill = fill; +} + +void ui_layout_vfill(UiObject* obj, UiBool fill) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.vfill = fill; +} + +void ui_layout_width(UiObject* obj, int width) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.width = width; +} + +void ui_layout_height(UiObject* obj, int height) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.height = height; +} + +void ui_layout_colspan(UiObject* obj, int cols) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.colspan = cols; +} + +void ui_layout_rowspan(UiObject* obj, int rows) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.rowspan = rows; +} + +void ui_newline(UiObject* obj) { + UiContainer* ct = uic_get_current_container(obj); + ct->layout.newline = TRUE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/container.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,187 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "toolkit.h" +#include "../ui/container.h" + +#include <Windows.h> +#undef GetCurrentTime +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h> +#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h> +#include <winrt/Microsoft.UI.Xaml.Markup.h> + + +#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) +#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) +#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) + +typedef struct UiLayout UiLayout; +typedef enum UiLayoutBool UiLayoutBool; + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::XamlTypeInfo; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Windows::UI::Xaml::Interop; + +enum UiLayoutBool { + UI_LAYOUT_UNDEFINED = 0, + UI_LAYOUT_TRUE, + UI_LAYOUT_FALSE, +}; + +struct UiLayout { + UiLayoutBool fill; + UiBool newline; + char* label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int width; + int height; + int colspan; + int rowspan; +}; + +struct UiContainer { + UiLayout layout; + int close = 0; + + virtual void Add(FrameworkElement control, UiBool fill) = 0; +}; + +enum UiBoxContainerType { + UI_BOX_CONTAINER_VBOX = 0, + UI_BOX_CONTAINER_HBOX +}; + +enum UiNavigationViewType { + UI_NAVIGATIONVIEW_TOP = 0, + UI_NAVIGATIONVIEW_SIDE +}; + +struct UiBoxContainer : UiContainer { + Grid grid; + enum UiBoxContainerType type; + RowDefinition boxRowDef; + ColumnDefinition boxColDef; + + UiBoxContainer(Grid grid, enum UiBoxContainerType type, int margin, int spacing); + + void Add(FrameworkElement control, UiBool fill); +}; + +struct UiGridContainer : UiContainer { + Grid grid; + int x = 0; + int y = -1; + int cols = 0; + + UiGridContainer(Grid grid, int margin, int columnspacing, int rowspacing); + + void Add(FrameworkElement control, UiBool fill); +}; + +struct UiTabView { + UiObject* current; + UiSubContainerType subcontainer; + int margin; + int spacing; + int columnspacing; + int rowspacing; + + virtual UiObject* AddTab(const char* label, int index = -1) = 0; + virtual void Remove(int index) = 0; + virtual void Select(int index) = 0; + virtual FrameworkElement GetFrameworkElement() = 0; +}; + +struct UiTabViewContainer : UiContainer { + UiTabView* tabview; + + UiTabViewContainer(UiTabView* tabview); + + void Add(FrameworkElement control, UiBool fill); +}; + +struct UiPivotTabView : UiTabView { + Pivot pivot; + + UiPivotTabView(UiObject *obj, Pivot pivot, UiTabViewArgs args); + + UiObject* AddTab(const char* label, int index = -1); + void Remove(int index); + void Select(int index); + FrameworkElement GetFrameworkElement(); +}; + +struct UiMainTabView : UiTabView { + TabView tabview; + + UiMainTabView(UiObject* obj, TabView tabview, UiTabViewArgs args); + + UiObject* AddTab(const char* label, int index = -1); + void Remove(int index); + void Select(int index); + FrameworkElement GetFrameworkElement(); +}; + +struct UiNavigationTabView : UiTabView { + NavigationView navigationview; + UiTabViewType type; + std::vector<std::tuple<NavigationViewItem, FrameworkElement> > pages; + + UiNavigationTabView(UiObject* obj, NavigationView navigationview, UiTabViewArgs args, UiTabViewType type); + + UiObject* AddTab(const char* label, int index = -1); + void Remove(int index); + void Select(int index); + FrameworkElement GetFrameworkElement(); + + void SelectionChanged(NavigationView const& sender, NavigationViewSelectionChangedEventArgs const& args); +}; + +struct UiInvisibleTabView : UiTabView { + Grid container; + std::vector<FrameworkElement> pages; + int currentIndex; + + UiInvisibleTabView(UiObject *obj, Grid container, UiTabViewArgs args); + + UiObject* AddTab(const char* label, int index = -1); + void Remove(int index); + void Select(int index); + FrameworkElement GetFrameworkElement(); +}; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/dnd.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "dnd.h" +#include "util.h" + +#include <thread> + +using namespace winrt; +using namespace Windows::ApplicationModel::DataTransfer; +using namespace Windows::Storage; +using namespace Windows::Storage::Streams; + +UIEXPORT void ui_selection_settext(UiDnD* dnd, char* str, int len) { + if (dnd->data) { + if (len < 0) { + len = strlen(str); + } + wchar_t *wstr = str2wstr_len(str, len, nullptr); + + dnd->data.SetText(wstr); + + free(wstr); + + } +} + +UIEXPORT void ui_selection_seturis(UiDnD* dnd, char** uris, int nelm) { + +} + + +UIEXPORT char* ui_selection_gettext(UiDnD* dnd) { + return nullptr; +} + + +UIEXPORT UiFileList ui_selection_geturis(UiDnD *dnd) { + UiFileList flist; + flist.files = nullptr; + flist.nfiles = 0; + + if (dnd->dataview.Contains(StandardDataFormats::StorageItems())) { + UiFileList *flist_ptr = &flist; + + // we need to execute this in a different thread + // this could block the main gui thread, but shouldn't happen with a simple uri list + std::thread getDataThread([dnd, flist_ptr]() { + auto items = dnd->dataview.GetStorageItemsAsync().get(); + + char **uris = (char**)calloc(items.Size(), sizeof(char*)); + flist_ptr->files = uris; + flist_ptr->nfiles = items.Size(); + + int i = 0; + for (IStorageItem const& item : items) { + winrt::hstring path = item.Path(); + uris[i++] = wchar2utf8(path.c_str(), path.size()); + } + }); + getDataThread.join(); + } + return flist; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/dnd.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,40 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/dnd.h" + +struct UiDnD { + int evttype = 0; + winrt::Microsoft::UI::Xaml::DragStartingEventArgs dndstartargs = { nullptr }; + winrt::Microsoft::UI::Xaml::DropCompletedEventArgs dndcompletedargs = { nullptr }; + winrt::Microsoft::UI::Xaml::DragEventArgs drageventargs = { nullptr }; + winrt::Windows::ApplicationModel::DataTransfer::DataPackage data = { nullptr }; + winrt::Windows::ApplicationModel::DataTransfer::DataPackageView dataview = { nullptr }; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/icons.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,421 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "icons.h" +#include "../ui/icons.h" + +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +#include <Windows.h> +#include <Shellapi.h> + + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; +using namespace winrt::Microsoft::UI::Xaml::Media::Imaging; +//using namespace Windows::Storage::Streams; + +static UiIcon* sys_folder_icon16; +static UiIcon* sys_file_icon16; + +static UiIcon* sys_folder_icon32; +static UiIcon* sys_file_icon32; + +std::unordered_map<std::string, Symbol> ui_symbol_icons = { + {"Accept", Symbol::Accept }, + {"Account", Symbol::Account }, + {"Add", Symbol::Add }, + {"AddFriend", Symbol::AddFriend }, + {"Admin", Symbol::Admin }, + {"AlignCenter", Symbol::AlignCenter }, + {"AlignLeft", Symbol::AlignLeft }, + {"AlignRight", Symbol::AlignRight }, + {"AllApps", Symbol::AllApps }, + {"Attach", Symbol::Attach }, + {"AttachCamera", Symbol::AttachCamera }, + {"Audio", Symbol::Audio }, + {"Back", Symbol::Back }, + {"BackToWindow", Symbol::BackToWindow }, + {"BlockContact", Symbol::BlockContact }, + {"Bold", Symbol::Bold }, + {"Bookmarks", Symbol::Bookmarks }, + {"BrowsePhotos", Symbol::BrowsePhotos }, + {"Bullets", Symbol::Bullets }, + {"Calculator", Symbol::Calculator }, + {"Calendar", Symbol::Calendar }, + {"CalendarDay", Symbol::CalendarDay }, + {"CalendarReply", Symbol::CalendarReply }, + {"CalendarWeek", Symbol::CalendarWeek }, + {"Camera", Symbol::Camera }, + {"Cancel", Symbol::Cancel }, + {"Caption", Symbol::Caption }, + {"CellPhone", Symbol::CellPhone }, + {"Character", Symbol::Character }, + {"Clear", Symbol::Clear }, + {"ClearSelection", Symbol::ClearSelection }, + {"Clock", Symbol::Clock }, + {"ClosedCaption", Symbol::ClosedCaption }, + {"ClosePane", Symbol::ClosePane }, + {"Comment", Symbol::Comment }, + {"Contact", Symbol::Contact }, + {"Contact2", Symbol::Contact2 }, + {"ContactInfo", Symbol::ContactInfo }, + {"ContactPresence", Symbol::ContactPresence }, + {"Copy", Symbol::Copy }, + {"Crop", Symbol::Crop }, + {"Cut", Symbol::Cut }, + {"Delete", Symbol::Delete }, + {"Directions", Symbol::Directions }, + {"DisableUpdates", Symbol::DisableUpdates }, + {"DisconnectDrive", Symbol::DisconnectDrive }, + {"Dislike", Symbol::Dislike }, + {"DockBottom", Symbol::DockBottom }, + {"DockLeft", Symbol::DockLeft }, + {"DockRight", Symbol::DockRight }, + {"Document", Symbol::Document }, + {"Download", Symbol::Download }, + {"Edit", Symbol::Edit }, + {"Emoji", Symbol::Emoji }, + {"Emoji2", Symbol::Emoji2 }, + {"Favorite", Symbol::Favorite }, + {"Filter", Symbol::Filter }, + {"Find", Symbol::Find }, + {"Flag", Symbol::Flag }, + {"Folder", Symbol::Folder }, + {"Font", Symbol::Font }, + {"FontColor", Symbol::FontColor }, + {"FontDecrease", Symbol::FontDecrease }, + {"FontIncrease", Symbol::FontIncrease }, + {"FontSize", Symbol::FontSize }, + {"Forward", Symbol::Forward }, + {"FourBars", Symbol::FourBars }, + {"FullScreen", Symbol::FullScreen }, + {"GlobalNavigationButton", Symbol::GlobalNavigationButton }, + {"Globe", Symbol::Globe }, + {"Go", Symbol::Go }, + {"GoToStart", Symbol::GoToStart }, + {"GoToToday", Symbol::GoToToday }, + {"HangUp", Symbol::HangUp }, + {"Help", Symbol::Help }, + {"HideBcc", Symbol::HideBcc }, + {"Highlight", Symbol::Highlight }, + {"Home", Symbol::Home }, + {"Import", Symbol::Import }, + {"ImportAll", Symbol::ImportAll }, + {"Important", Symbol::Important }, + {"Italic", Symbol::Italic }, + {"Keyboard", Symbol::Keyboard }, + {"LeaveChat", Symbol::LeaveChat }, + {"Library", Symbol::Library }, + {"Like", Symbol::Like }, + {"LikeDislike", Symbol::LikeDislike }, + {"Link", Symbol::Link }, + {"List", Symbol::List }, + {"Mail", Symbol::Mail }, + {"MailFilled", Symbol::MailFilled }, + {"MailForward", Symbol::MailForward }, + {"MailReply", Symbol::MailReply }, + {"MailReplyAll", Symbol::MailReplyAll }, + {"Manage", Symbol::Manage }, + {"Map", Symbol::Map }, + {"MapDrive", Symbol::MapDrive }, + {"MapPin", Symbol::MapPin }, + {"Memo", Symbol::Memo }, + {"Message", Symbol::Message }, + {"Microphone", Symbol::Microphone }, + {"More", Symbol::More }, + {"MoveToFolder", Symbol::MoveToFolder }, + {"MusicInfo", Symbol::MusicInfo }, + {"Mute", Symbol::Mute }, + {"NewFolder", Symbol::NewFolder }, + {"NewWindow", Symbol::NewWindow }, + {"Next", Symbol::Next }, + {"OneBar", Symbol::OneBar }, + {"OpenFile", Symbol::OpenFile }, + {"OpenLocal", Symbol::OpenLocal }, + {"OpenPane", Symbol::OpenPane }, + {"OpenWith", Symbol::OpenWith }, + {"Orientation", Symbol::Orientation }, + {"OtherUser", Symbol::OtherUser }, + {"OutlineStar", Symbol::OutlineStar }, + {"Page", Symbol::Page }, + {"Page2", Symbol::Page2 }, + {"Paste", Symbol::Paste }, + {"Pause", Symbol::Pause }, + {"People", Symbol::People }, + {"Permissions", Symbol::Permissions }, + {"Phone", Symbol::Phone }, + {"PhoneBook", Symbol::PhoneBook }, + {"Pictures", Symbol::Pictures }, + {"Pin", Symbol::Pin }, + {"Placeholder", Symbol::Placeholder }, + {"Play", Symbol::Play }, + {"PostUpdate", Symbol::PostUpdate }, + {"Preview", Symbol::Preview }, + {"PreviewLink", Symbol::PreviewLink }, + {"Previous", Symbol::Previous }, + {"Print", Symbol::Print }, + {"Priority", Symbol::Priority }, + {"ProtectedDocument", Symbol::ProtectedDocument }, + {"Read", Symbol::Read }, + {"Redo", Symbol::Redo }, + {"Refresh", Symbol::Refresh }, + {"Remote", Symbol::Remote }, + {"Remove", Symbol::Remove }, + {"Rename", Symbol::Rename }, + {"Repair", Symbol::Repair }, + {"RepeatAll", Symbol::RepeatAll }, + {"RepeatOne", Symbol::RepeatOne }, + {"ReportHacked", Symbol::ReportHacked }, + {"ReShare", Symbol::ReShare }, + {"Rotate", Symbol::Rotate }, + {"RotateCamera", Symbol::RotateCamera }, + {"Save", Symbol::Save }, + {"SaveLocal", Symbol::SaveLocal }, + {"Scan", Symbol::Scan }, + {"SelectAll", Symbol::SelectAll }, + {"Send", Symbol::Send }, + {"SetLockScreen", Symbol::SetLockScreen }, + {"SetTile", Symbol::SetTile }, + {"Setting", Symbol::Setting }, + {"Share", Symbol::Share }, + {"Shop", Symbol::Shop }, + {"ShowBcc", Symbol::ShowBcc }, + {"ShowResults", Symbol::ShowResults }, + {"Shuffle", Symbol::Shuffle }, + {"SlideShow", Symbol::SlideShow }, + {"SolidStar", Symbol::SolidStar }, + {"Sort", Symbol::Sort }, + {"Stop", Symbol::Stop }, + {"StopSlideShow", Symbol::StopSlideShow }, + {"Street", Symbol::Street }, + {"Switch", Symbol::Switch }, + {"SwitchApps", Symbol::SwitchApps }, + {"Sync", Symbol::Sync }, + {"SyncFolder", Symbol::SyncFolder }, + {"Tag", Symbol::Tag }, + {"Target", Symbol::Target }, + {"ThreeBars", Symbol::ThreeBars }, + {"TouchPointer", Symbol::TouchPointer }, + {"Trim", Symbol::Trim }, + {"TwoBars", Symbol::TwoBars }, + {"TwoPage", Symbol::TwoPage }, + {"Underline", Symbol::Underline }, + {"Undo", Symbol::Undo }, + {"UnFavorite", Symbol::UnFavorite }, + {"UnPin", Symbol::UnPin }, + {"UnSyncFolder", Symbol::UnSyncFolder }, + {"Up", Symbol::Up }, + {"Upload", Symbol::Upload }, + {"Video", Symbol::Video }, + {"VideoChat", Symbol::VideoChat }, + {"View", Symbol::View }, + {"ViewAll", Symbol::ViewAll }, + {"Volume", Symbol::Volume }, + {"WebCam", Symbol::WebCam }, + {"World", Symbol::World }, + {"XboxOneConsole", Symbol::XboxOneConsole }, + {"ZeroBars", Symbol::ZeroBars }, + {"Zoom", Symbol::Zoom }, + {"ZoomIn", Symbol::ZoomIn }, + {"ZoomOut", Symbol::ZoomOut } +}; + +winrt::Microsoft::UI::Xaml::Controls::IconElement ui_get_icon(const char* name) { + if (ui_symbol_icons.find(name) == ui_symbol_icons.end()) { + SymbolIcon no_icon = { nullptr }; + return no_icon; + } + + Symbol symbol = ui_symbol_icons[name]; + SymbolIcon icon = SymbolIcon(symbol); + return icon; +} + + +// symbol icon implementation +UiSymbolIcon::UiSymbolIcon(winrt::Microsoft::UI::Xaml::Controls::Symbol sym) { + symbol = sym; +} + +UiSymbolIcon::~UiSymbolIcon() { + +} + +winrt::Microsoft::UI::Xaml::Controls::IconElement UiSymbolIcon::getIcon() { + return SymbolIcon(symbol); +} + +// image icon implementation +UiImageIcon::UiImageIcon(const char* uristr) { + wchar_t* wuri = str2wstr(uristr, nullptr); + Windows::Foundation::Uri uri{ wuri }; + this->uri = uri; + free(wuri); +} + +UiImageIcon::~UiImageIcon() { + +} + +winrt::Microsoft::UI::Xaml::Controls::IconElement UiImageIcon::getIcon() { + BitmapIcon icon = BitmapIcon(); + icon.UriSource(uri); + ImageIcon img = ImageIcon(); + img.Source(); + return icon; +} + +// bitmap icon implementation +UiBitmapIcon::UiBitmapIcon(winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapSource bitmap) { + this->bitmap = bitmap; +} + +UiBitmapIcon::~UiBitmapIcon() { + +} + +winrt::Microsoft::UI::Xaml::Controls::IconElement UiBitmapIcon::getIcon() { + ImageIcon icon = ImageIcon(); + icon.Source(bitmap); + return icon; +} + +UIEXPORT UiIcon* ui_icon(const char* name, size_t size) { + Symbol symbol = ui_symbol_icons[name]; + UiSymbolIcon* icon = new UiSymbolIcon(symbol); + return icon; +} + + +UIEXPORT UiIcon* ui_imageicon(const char* file) { + return new UiImageIcon(file); +} + +UIEXPORT void ui_icon_free(UiIcon* icon) { + delete icon; +} + + +struct __declspec(uuid("905a0fef-bc53-11df-8c49-001e4fc686da")) IBufferByteAccess : ::IUnknown +{ + virtual HRESULT __stdcall Buffer(uint8_t** value) = 0; +}; + + + +winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap ui_dllicon2bitmap(const char* dll, int iconindex, bool large) { + WriteableBitmap wbitmap = { nullptr }; + + // get the icon from the dll + HICON hicon_small; + HICON hicon_large; + if (ExtractIconExA(dll, iconindex, &hicon_large, &hicon_small, 1) > 0) { + HICON hicon = large ? hicon_large : hicon_small; + + // convert icon to (gdi) bitmap + ICONINFO info; + info.hbmColor = nullptr; + info.hbmMask = nullptr; + if (GetIconInfo(hicon, &info)) { + BITMAP bitmap; + if (GetObjectW(info.hbmColor, sizeof(BITMAP), &bitmap) != 0) { + size_t bitmap_size = bitmap.bmWidthBytes * bitmap.bmHeight; + char *bitmap_data = (char*)malloc(bitmap_size); + + // get the pixel data + if (GetBitmapBits(info.hbmColor, bitmap_size, bitmap_data) != 0) { + WriteableBitmap wb = WriteableBitmap(bitmap.bmWidth, bitmap.bmHeight); + void *wb_data = wb.PixelBuffer().data(); + memcpy(wb_data, bitmap_data, bitmap_size); + wbitmap = wb; + } + free(bitmap_data); + } + if (info.hbmMask) { + DeleteObject(info.hbmMask); + } + if (info.hbmColor) { + DeleteObject(info.hbmColor); + } + } + + DestroyIcon(hicon_small); + DestroyIcon(hicon_large); + } + + return wbitmap; +} + +UiIcon* ui_dllicon(const char* dll, int iconindex, bool large) { + WriteableBitmap wbitmap = ui_dllicon2bitmap(dll, iconindex, large); + return new UiBitmapIcon(wbitmap); +} + +UIEXPORT UiIcon* ui_foldericon(size_t size) { + bool large = true; + UiIcon** sys_folder_icon = &sys_folder_icon32; + if (size <= 24) { + large = false; + sys_folder_icon = &sys_folder_icon16; + } + + if (*sys_folder_icon) { + return *sys_folder_icon; + } + + UiIcon* icon = ui_dllicon("shell32.dll", 3, large); + *sys_folder_icon = icon; + return icon; +} + +UIEXPORT UiIcon* ui_fileicon(size_t size) { + bool large = true; + UiIcon** sys_folder_icon = &sys_file_icon32; + if (size <= 24) { + large = false; + sys_folder_icon = &sys_file_icon16; + } + + if (*sys_folder_icon) { + return *sys_folder_icon; + } + + UiIcon* icon = ui_dllicon("shell32.dll", 0, large); + *sys_folder_icon = icon; + return icon; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/icons.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/toolkit.h" + + + +struct UiIcon { + //virtual ~UiIcon() = 0; + + virtual winrt::Microsoft::UI::Xaml::Controls::IconElement getIcon() = 0; +}; + +struct UiSymbolIcon : UiIcon { + winrt::Microsoft::UI::Xaml::Controls::Symbol symbol; + + UiSymbolIcon(winrt::Microsoft::UI::Xaml::Controls::Symbol sym); + + ~UiSymbolIcon(); + + winrt::Microsoft::UI::Xaml::Controls::IconElement getIcon(); +}; + +struct UiImageIcon : UiIcon { + winrt::Windows::Foundation::Uri uri{ nullptr }; + + UiImageIcon(const char* uristr); + + ~UiImageIcon(); + + winrt::Microsoft::UI::Xaml::Controls::IconElement getIcon(); +}; + +struct UiBitmapIcon : UiIcon { + winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapSource bitmap{ nullptr }; + + UiBitmapIcon(winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapSource bitmap); + + ~UiBitmapIcon(); + + winrt::Microsoft::UI::Xaml::Controls::IconElement getIcon(); +}; + + +winrt::Microsoft::UI::Xaml::Controls::IconElement ui_get_icon(const char* name); + +winrt::Microsoft::UI::Xaml::Media::Imaging::WriteableBitmap ui_dllicon2bitmap(const char* dll, int iconindex, bool large); + +UiIcon* ui_dllicon(const char* dll, int iconindex, bool large);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/image.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,124 @@ +/* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +* +* Copyright 2024 Olaf Wintermann. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "pch.h" + +#include "image.h" + +#include "toolkit.h" +#include "container.h" +#include "../common/object.h" +#include "../common/context.h" +#include "util.h" + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; +using namespace winrt::Microsoft::UI::Xaml::Media::Imaging; +using namespace winrt::Microsoft::UI::Xaml::Media; + +UiImageSource::UiImageSource(winrt::Microsoft::UI::Xaml::Media::ImageSource& src) : imgsrc(src) {} + +UIEXPORT UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs args) { + UiObject* current = uic_current_obj(obj); + + Image image = Image(); + FrameworkElement elm = image; + if (args.scrollarea) { + ScrollViewer scroll = ScrollViewer(); + scroll.Content(image); + elm = scroll; + } + + // create toolkit wrapper object and register destructor + UIElement uielm = image; + UiWidget* widget = new UiWidget(uielm); + ui_context_add_widget_destructor(current->ctx, widget); + + // bind variable + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC); + if (var) { + UiGeneric *value = (UiGeneric*)var->value; + value->obj = widget; + value->get = ui_image_get; + value->set = ui_image_set; + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(elm, true); + + return widget; +} + +extern "C" void* ui_image_get(UiGeneric *g) { + + + return NULL; +} + +extern "C" int ui_image_set(UiGeneric *g, void *data, const char *type) { + if(!type || strcmp(type, UI_IMAGE_OBJECT_TYPE)) { + return 1; + } + + UiImageSource *imgdata = (UiImageSource*)data; + if (g->value) { + UiImageSource *prevData = (UiImageSource*)g->value; + delete prevData; + } + g->value = imgdata; + + UiWidget* widget = (UiWidget*)g->obj; + Image image = widget->uielement.as<Image>(); + image.Source(imgdata->imgsrc); + + return 0; +} + +UIEXPORT int ui_image_load_file(UiGeneric *obj, const char *path) { + wchar_t* wpath = str2wstr(path, nullptr); + std::wstring wPath = wpath; + std::wstring uriPath = L"file:///" + wPath; + Uri uri{ uriPath }; + + BitmapImage bitmapImage = BitmapImage(); + bitmapImage.UriSource(uri); + ImageSource src = bitmapImage; + + UiImageSource *imgdata = new UiImageSource(src); + obj->set(obj, imgdata, UI_IMAGE_OBJECT_TYPE); + + free(wpath); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/image.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,43 @@ +/* +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +* +* Copyright 2024 Olaf Wintermann. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "../ui/toolkit.h" +#include "../ui/image.h" + +class UiImageSource { +public: + winrt::Microsoft::UI::Xaml::Media::ImageSource imgsrc { nullptr }; + + UiImageSource(winrt::Microsoft::UI::Xaml::Media::ImageSource& src); +}; + + +extern "C" void* ui_image_get(UiGeneric *g); +extern "C" int ui_image_set(UiGeneric *g, void *data, const char *type);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/label.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,199 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "label.h" +#include "text.h" +#include "util.h" + +#include "toolkit.h" +#include "container.h" +#include "../common/object.h" +#include "../common/context.h" + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; + +UIWIDGET ui_label_create(UiObject* obj, UiLabelArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textbox and toolkit wrapper + TextBlock label = TextBlock(); + if (args.label) { + wchar_t* wlabel = str2wstr(args.label, nullptr); + label.Text(wlabel); + free(wlabel); + } + + UIElement elm = label; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = widget; + value->get = ui_label_get; + value->set = ui_label_set; + + // listener for notifying observers + // TODO: + } + + // add label to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(label, false); + + return widget; +} + +UIWIDGET ui_llabel_create(UiObject* obj, UiLabelArgs args) { + args.align = UI_ALIGN_LEFT; + return ui_label_create(obj, args); +} + +UIWIDGET ui_rlabel_create(UiObject* obj, UiLabelArgs args) { + args.align = UI_ALIGN_RIGHT; + return ui_label_create(obj, args); +} + + + +char* ui_label_get(UiString* str) { + UiWidget* widget = (UiWidget*)str->obj; + TextBlock box = widget->uielement.as<TextBlock>(); + std::wstring wstr(box.Text()); + return ui_wstring_get(str, wstr); +} + +void ui_label_set(UiString* str, const char* newvalue) { + UiWidget* widget = (UiWidget*)str->obj; + TextBlock box = widget->uielement.as<TextBlock>(); + box.Text(ui_wstring_set(str, newvalue)); +} + + +// -------------------- progressbar ------------------------- + +UIWIDGET ui_progressbar_create(UiObject* obj, UiProgressbarArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textbox and toolkit wrapper + ProgressBar progressbar = ProgressBar(); + progressbar.Minimum(args.min); + progressbar.Maximum(args.max == 0 ? 100 : args.max); + if (args.width > 0) { + progressbar.Width(args.width); + } + + UIElement elm = progressbar; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_DOUBLE); + if (var) { + UiDouble* value = (UiDouble*)var->value; + value->obj = widget; + value->get = ui_progressbar_get; + value->set = ui_progressbar_set; + + // listener for notifying observers + // TODO: + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(progressbar, false); + + return widget; +} + +double ui_progressbar_get(UiDouble * d) { + UiWidget* widget = (UiWidget*)d->obj; + ProgressBar progressbar = widget->uielement.as<ProgressBar>(); + d->value = progressbar.Value(); + return d->value; +} + +void ui_progressbar_set(UiDouble * d, double newvalue) { + UiWidget* widget = (UiWidget*)d->obj; + ProgressBar progressbar = widget->uielement.as<ProgressBar>(); + d->value = newvalue; + progressbar.Value(newvalue); +} + +UIWIDGET ui_progressspinner_create(UiObject* obj, UiProgressbarSpinnerArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textbox and toolkit wrapper + ProgressRing spinner = ProgressRing(); + spinner.IsActive(false); + + UIElement elm = spinner; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_DOUBLE); + if (var) { + UiInteger* value = (UiInteger*)var->value; + value->obj = widget; + value->get = ui_progressspinner_get; + value->set = ui_progressspinner_set; + + // listener for notifying observers + // TODO: + } + + // add button to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(spinner, false); + + return widget; +} + +int64_t ui_progressspinner_get(UiInteger * i) { + UiWidget* widget = (UiWidget*)i->obj; + ProgressRing spinner = widget->uielement.as<ProgressRing>(); + i->value = spinner.IsActive(); + return i->value; +} + +void ui_progressspinner_set(UiInteger * i, int64_t newvalue) { + UiWidget* widget = (UiWidget*)i->obj; + ProgressRing spinner = widget->uielement.as<ProgressRing>(); + i->value = newvalue != 0 ? 1 : 0; + spinner.IsActive(i->value); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/label.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#pragma once + +#include "../ui/toolkit.h" +#include "../ui/display.h" + +extern "C" char* ui_label_get(UiString * str); +extern "C" void ui_label_set(UiString * str, const char* newvalue); + +extern "C" double ui_progressbar_get(UiDouble *d); +extern "C" void ui_progressbar_set(UiDouble *d, double newvalue); + +extern "C" int64_t ui_progressspinner_get(UiInteger * i); +extern "C" void ui_progressspinner_set(UiInteger * i, int64_t newvalue);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/list.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,346 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "list.h" +#include "container.h" +#include "util.h" + +#include "../common/context.h" +#include "../common/object.h" + + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Microsoft::UI::Xaml::Media; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; + + +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create listview and toolkit wrapper + ListView listview = ListView(); + if (args.multiselection) { + listview.SelectionMode(ListViewSelectionMode::Extended); + } + + bool clickEnabled = listview.IsItemClickEnabled(); + listview.IsItemClickEnabled(true); + + + UIElement elm = listview; + UiWidget* widget = new UiWidget(elm); + widget->data1 = args.model; + widget->data2 = args.getvalue; + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + if (var) { + UiList* list = (UiList*)var->value; + list->update = ui_simple_list_update; + list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; + list->obj = widget; + + ui_simple_list_update(list, 0); + } + + if (args.onselection) { + ui_callback onselection = args.onselection; + void* cbdata = args.onselectiondata; + listview.SelectionChanged([onselection, cbdata, obj](IInspectable const& sender, RoutedEventArgs evtargs) { + std::vector<int> selectedRows = ui_create_listview_selection(sender.as<ListView>()); + + UiListSelection selection; + selection.rows = selectedRows.data(); + selection.count = selectedRows.size(); + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = 0; + onselection(&evt, cbdata); + }); + } + if (args.onactivate) { + ui_callback cb = args.onactivate; + void* cbdata = args.onactivatedata; + listview.ItemClick([cb, cbdata, obj](IInspectable const& sender, RoutedEventArgs evtargs) { + std::vector<int> selectedRows = ui_create_listview_selection(sender.as<ListView>()); + UiListSelection selection; + selection.rows = selectedRows.data(); + selection.count = selectedRows.size(); + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = 0; + cb(&evt, cbdata); + }); + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(listview, false); + + return widget; +} + + +UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create listview and toolkit wrapper + ComboBox combobox = ComboBox(); + + UIElement elm = combobox; + UiWidget* widget = new UiWidget(elm); + widget->data1 = args.model; + widget->data2 = args.getvalue; + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + if (var) { + UiList* list = (UiList*)var->value; + list->update = ui_simple_list_update; + list->getselection = ui_dropdown_getselection; + list->setselection = ui_dropdown_setselection; + list->obj = widget; + ui_simple_list_update(list, 0); + } + + if (args.onactivate) { + ui_callback cb = args.onactivate; + void* cbdata = args.onactivatedata; + combobox.SelectionChanged([cb, cbdata, obj](IInspectable const& sender, RoutedEventArgs evtargs) { + int selectedrow = sender.as<ComboBox>().SelectedIndex(); + UiListSelection selection; + selection.count = 1; + selection.rows = &selectedrow; + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = selectedrow; + cb(&evt, cbdata); + }); + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(combobox, false); + + return widget; +} + +UiListSelection ui_listview_getselection(UiList *list) { + UiWidget *widget = (UiWidget*)list->obj; + ListView listview = widget->uielement.as<ListView>(); + std::vector<int> selectedRows = ui_create_listview_selection(listview); + + UiListSelection selection = { NULL, 0 }; + if (selectedRows.size() > 0) { + selection.count = selectedRows.size(); + int *data = selectedRows.data(); + selection.rows = (int*)calloc(selection.count, sizeof(int)); + memcpy(selection.rows, data, selection.count); + } + + return selection; +} + +void ui_listview_setselection(UiList *list, UiListSelection selection) { + UiWidget* widget = (UiWidget*)list->obj; + if (selection.count > 0) { + ListView listview = widget->uielement.as<ListView>(); + listview.SelectedIndex(selection.rows[0]); + } +} + +UiListSelection ui_dropdown_getselection(UiList *list) { + UiWidget* widget = (UiWidget*)list->obj; + ComboBox cb = widget->uielement.as<ComboBox>(); + int index = cb.SelectedIndex(); + UiListSelection selection = { NULL, 0 }; + if (index >= 0) { + selection.rows = (int*)calloc(1, sizeof(int)); + selection.count = 1; + selection.rows[0] = index; + } + return selection; +} + +void ui_dropdown_setselection(UiList *list, UiListSelection selection) { + UiWidget* widget = (UiWidget*)list->obj; + if (selection.count > 0) { + ComboBox cb = widget->uielement.as<ComboBox>(); + cb.SelectedIndex(selection.rows[0]); + } +} + +UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs args) { + UiObject* current = uic_current_obj(obj); + + // create listview and toolkit wrapper + BreadcrumbBar bcbar = BreadcrumbBar(); + + UIElement elm = bcbar; + UiWidget* widget = new UiWidget(elm); + widget->data1 = args.model; + widget->data2 = args.getvalue; + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + if (var) { + UiList* list = (UiList*)var->value; + list->update = ui_breadcrumbbar_update; + list->obj = widget; + ui_breadcrumbbar_update(list, 0); + } + + if (args.onactivate) { + ui_callback cb = args.onactivate; + void* cbdata = args.onactivatedata; + bcbar.ItemClicked([cb, cbdata, obj](IInspectable const& sender, BreadcrumbBarItemClickedEventArgs evtargs) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = evtargs.Index(); + cb(&evt, cbdata); + }); + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(bcbar, false); + + return widget; +} + +static void* getstrvalue(void* elm, int ignore) { + return elm; +} + +void ui_simple_list_update(UiList* list, int i) { + UiWidget* widget = (UiWidget*)list->obj; + UiModel* model = (UiModel*)widget->data1; + ui_getvaluefunc getvalue = (ui_getvaluefunc)widget->data2; + ItemsControl listview = widget->uielement.as<ItemsControl>(); + auto items = listview.Items(); + + // priority: getvalue, model.getvalue, getstrvalue (fallback) + if (getvalue == nullptr) { + if (model && model->getvalue) { + getvalue = model->getvalue; + } else { + getvalue = getstrvalue; + } + } + + // add list elements to listview.Items + items.Clear(); + void* elm = list->first(list); + while (elm) { + char* value = (char*)getvalue(elm, 0); + wchar_t* wstr = str2wstr(value, nullptr); + items.Append(box_value(wstr)); + free(wstr); + + elm = list->next(list); + } +} + +extern "C" void ui_breadcrumbbar_update(UiList * list, int i) { + UiWidget* widget = (UiWidget*)list->obj; + UiModel* model = (UiModel*)widget->data1; + ui_getvaluefunc getvalue = (ui_getvaluefunc)widget->data2; + + // priority: getvalue, model.getvalue, getstrvalue (fallback) + if (getvalue == nullptr) { + if (model && model->getvalue) { + getvalue = model->getvalue; + } + else { + getvalue = getstrvalue; + } + } + + BreadcrumbBar bar = widget->uielement.as<BreadcrumbBar>(); + + Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> items { winrt::single_threaded_vector<Windows::Foundation::IInspectable>() }; + void* elm = list->first(list); + while (elm) { + char* value = (char*)getvalue(elm, 0); + wchar_t* wstr = str2wstr(value, nullptr); + items.Append(box_value(wstr)); + free(wstr); + + elm = list->next(list); + } + + bar.ItemsSource(items); +} + + +std::vector<int> ui_create_listview_selection(ListView listview) { + std::vector<int> selection; + int p = 0; + auto ranges = listview.SelectedRanges(); + for (auto range : ranges) { + int begin = range.FirstIndex(); + int end = range.LastIndex(); + for (int i = begin; i <= end; i++) { + selection.push_back(i); + } + } + return selection; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/list.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/tree.h" +#include "toolkit.h" + +#include "../ui/container.h" + + +extern "C" void ui_simple_list_update(UiList * list, int i); + +extern "C" void ui_breadcrumbbar_update(UiList * list, int i); + +std::vector<int> ui_create_listview_selection(winrt::Microsoft::UI::Xaml::Controls::ListView listview); + +extern "C" UiListSelection ui_listview_getselection(UiList *list); +extern "C" void ui_listview_setselection(UiList *list, UiListSelection selection); + +extern "C" UiListSelection ui_dropdown_getselection(UiList *list); +extern "C" void ui_dropdown_setselection(UiList *list, UiListSelection selection);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/packages.config Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Windows.CppWinRT" version="2.0.240405.15" targetFramework="native" /> + <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.240803.1" targetFramework="native" /> + <package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.1742" targetFramework="native" /> + <package id="Microsoft.WindowsAppSDK" version="1.5.241001000" targetFramework="native" /> +</packages> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/pch.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#include "pch.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/pch.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation and Contributors. +// Licensed under the MIT License. + +#pragma once +#include <windows.h> +#include <unknwn.h> +#include <restrictederrorinfo.h> +#include <hstring.h> + +// Undefine GetCurrentTime macro to prevent +// conflict with Storyboard::GetCurrentTime +#undef GetCurrentTime + +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.ApplicationModel.Activation.h> +#include <winrt/Microsoft.UI.Composition.h> +#include <winrt/Microsoft.UI.Windowing.h> +#include <winrt/Microsoft.UI.Xaml.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h> +#include <winrt/Microsoft.UI.Xaml.Data.h> +#include <winrt/Microsoft.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Markup.h> +#include <winrt/Microsoft.UI.Xaml.Media.h> +#include <winrt/Microsoft.UI.Xaml.Media.Imaging.h> +#include <winrt/Microsoft.UI.Xaml.Navigation.h> +#include <winrt/Microsoft.UI.Xaml.Shapes.h> +#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h> +#include <winrt/Microsoft.UI.Dispatching.h> +#include <winrt/Windows.ApplicationModel.DataTransfer.h> +#include <wil/cppwinrt_helpers.h> +#include <winrt/Microsoft.UI.Xaml.Input.h> +#include <winrt/Microsoft.UI.Input.h> +#include <winrt/Windows.UI.Core.h> +#include <winrt/Windows.ApplicationModel.h> +#include <winrt/Windows.Storage.Pickers.h> + +#include <winrt\Microsoft.UI.Dispatching.h> + +#include <winrt/Windows.Storage.Streams.h> + +#include <Microsoft.UI.Xaml.Window.h> + +#include <shobjidl_core.h>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/readme.txt Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,27 @@ +======================================================================== + winui Project Overview +======================================================================== + +This project demonstrates how to get started writing WinUI3 apps directly +with standard C++, using the Windows App SDK and C++/WinRT packages and +XAML compiler support to generate implementation headers from interface +(IDL) files. These headers can then be used to implement the local +Windows Runtime classes referenced in the app's XAML pages. + +Steps: +1. Create an interface (IDL) file to define any local Windows Runtime + classes referenced in the app's XAML pages. +2. Build the project once to generate implementation templates under + the "Generated Files" folder, as well as skeleton class definitions + under "Generated Files\sources". +3. Use the skeleton class definitions for reference to implement your + Windows Runtime classes. + +======================================================================== +Learn more about Windows App SDK here: +https://docs.microsoft.com/windows/apps/windows-app-sdk/ +Learn more about WinUI3 here: +https://docs.microsoft.com/windows/apps/winui/winui3/ +Learn more about C++/WinRT here: +http://aka.ms/cppwinrt/ +========================================================================
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/stock.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,5 @@ + + +#include "pch.h" + +#include "stock.h" \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/stock.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,32 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/stock.h" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/table.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,648 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "table.h" +#include "container.h" +#include "util.h" +#include "icons.h" + +#include "../common/context.h" +#include "../common/object.h" +#include "../common/types.h" + +#include <winrt/Microsoft.UI.Xaml.Data.h> +#include <winrt/Microsoft.UI.Xaml.Media.h> +#include <winrt/Microsoft.UI.Xaml.Input.h> +#include <winrt/Windows.UI.Core.h> +#include <winrt/Windows.ApplicationModel.h> +#include <winrt/Windows.ApplicationModel.DataTransfer.h> + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; +using namespace winrt::Microsoft::UI::Xaml::Media; +using namespace winrt::Windows::UI::Xaml::Input; + +static UINT ui_double_click_time = GetDoubleClickTime(); + +extern "C" void reg_table_destructor(UiContext * ctx, UiTable * table) { + // TODO: +} + +static void textblock_set_str(TextBlock& t, const char* str) { + if (str) { + wchar_t* wstr = str2wstr(str, nullptr); + t.Text(winrt::hstring(wstr)); + free(wstr); + } +} + +static void textblock_set_int(TextBlock& t, int i) { + wchar_t buf[16]; + swprintf(buf, 16, L"%d", i); + t.Text(winrt::hstring(buf)); +} + +UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args) { + if (!args.model) { + return nullptr; + } + + UiObject* current = uic_current_obj(obj); + + // create widgets and wrapper obj + ScrollViewer scrollW = ScrollViewer(); + Grid grid = Grid(); + scrollW.Content(grid); + UiTable* uitable = new UiTable(obj, scrollW, grid); + reg_table_destructor(current->ctx, uitable); + + uitable->getvalue = args.model->getvalue ? args.model->getvalue : args.getvalue; + uitable->onselection = args.onselection; + uitable->onselectiondata = args.onselectiondata; + uitable->onactivate = args.onactivate; + uitable->onactivatedata = args.onactivatedata; + uitable->ondragstart = args.ondragstart; + uitable->ondragstartdata = args.ondragstartdata; + uitable->ondragcomplete = args.ondragcomplete; + uitable->ondrop = args.ondrop; + uitable->ondropdata = args.ondropsdata; + + // grid styling + winrt::Windows::UI::Color bg = { 255, 255, 255, 255 }; // test color + SolidColorBrush brush = SolidColorBrush(bg); + grid.Background(brush); + + // add columns from args.model + uitable->add_header(args.model); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + if (var) { + UiList* list = (UiList*)var->value; + list->update = ui_table_update; + list->getselection = ui_table_selection; + list->obj = uitable; + uitable->update(list, 0); + } + + // create toolkit wrapper object and register destructor + UIElement elm = scrollW; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + + // add scrollW to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(scrollW, false); + + return widget; +} + +extern "C" void ui_table_update(UiList * list, int i) { + UiTable* table = (UiTable*)list->obj; + table->clear(); + table->update(list, i); +} + +extern "C" UiListSelection ui_table_selection(UiList * list) { + UiTable* table = (UiTable*)list->obj; + return table->uiselection(); +} + +UiTable::UiTable(UiObject *obj, winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid) { + this->obj = obj; + + this->scrollw = scrollw; + this->grid = grid; + + winrt::Windows::UI::Color highlightBg = { 255, 234, 234, 234 }; + highlightBrush = SolidColorBrush(highlightBg); + + winrt::Windows::UI::Color defaultBg = { 0, 0, 0, 0 }; // default + defaultBrush = SolidColorBrush(defaultBg); + + winrt::Windows::UI::Color selectedBg = { 255, 204, 232, 255 }; // test color + selectedBrush = SolidColorBrush(selectedBg); + + winrt::Windows::UI::Color selectedFg = { 255, 0, 90, 158 }; // test color + selectedBorderBrush = SolidColorBrush(selectedFg); + + grid.KeyDown( + winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& args) { + // key event for hanling the table cursor or enter + }) + ); +} + +UiTable::~UiTable() { + ui_model_free(NULL, model); +} + +void UiTable::add_header(UiModel* model) { + this->model = ui_model_copy(NULL, model); + + GridLength gl; + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + + // add header row definition + auto headerRowDef = RowDefinition(); + headerRowDef.Height(gl); + grid.RowDefinitions().Append(headerRowDef); + + winrt::Windows::UI::Color borderColor = { 63, 0, 0, 0 }; + SolidColorBrush borderBrush = SolidColorBrush(borderColor); + + + for (int i = 0; i < model->columns;i++) { + char* title = model->titles[i]; + UiModelType type = model->types[i]; + + // add grid column definition + auto colDef = ColumnDefinition(); + colDef.Width(gl); + grid.ColumnDefinitions().Append(colDef); + + // header column border + Border headerBorder = Border(); + Thickness border = { 0,0,1,0 }; + headerBorder.BorderThickness(border); + headerBorder.BorderBrush(borderBrush); + + // add text + auto hLabel = TextBlock(); + textblock_set_str(hLabel, title); + Thickness cellpadding = { 10,4,4,4 }; + hLabel.Padding(cellpadding); + hLabel.VerticalAlignment(VerticalAlignment::Stretch); + + // event handler for highlighting and column resizing + headerBorder.PointerPressed( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + // the last column doesn't need resize capabilities + if (i + 1 < model->columns) { + double width = headerBorder.ActualWidth(); + auto point = args.GetCurrentPoint(headerBorder); + auto position = point.Position(); + if (position.X + 4 >= width) { + this->resize = true; + this->resizedCol = headerBorder; + } + } + }) + ); + headerBorder.PointerReleased( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + this->resize = false; + }) + ); + headerBorder.PointerMoved( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + if (this->resize) { + auto point = args.GetCurrentPoint(this->resizedCol); + auto position = point.Position(); + if (position.X > 1) { + this->resizedCol.Width(position.X); + } + } + }) + ); + headerBorder.PointerEntered( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + // TODO: background + }) + ); + headerBorder.PointerExited( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + // TODO: background + }) + ); + + + + // add controls + headerBorder.Child(hLabel); + + grid.SetColumn(headerBorder, i); + grid.SetRow(headerBorder, 0); + grid.Children().Append(headerBorder); + + UiTableColumn h; + h.header = headerBorder; + header.push_back(h); + } + + maxrows = 1; +} + +static ULONG64 getsystime() { + SYSTEMTIME st; + GetSystemTime(&st); + return st.wYear * 10000000000000 + + st.wMonth * 100000000000 + + st.wDay * 1000000000 + + st.wHour * 10000000 + + st.wMinute * 100000 + + st.wSecond * 1000 + + st.wMilliseconds; +} + +void UiTable::update(UiList* list, int i) { + if (getvalue == nullptr) { + return; + } + + Thickness b1 = { 1, 1, 0, 1 }; // first col + Thickness b2 = { 0, 1, 0, 1 }; // middle + Thickness b3 = { 0, 1, 1, 1 }; // last col + + GridLength gl; + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + + // iterate model + int row = 1; + void* elm = list->first(list); + while (elm) { + if (row >= maxrows) { + auto rowdef = RowDefinition(); + rowdef.Height(gl); + grid.RowDefinitions().Append(rowdef); + maxrows = row; + } + + Thickness cellpadding = { 10,0,4,0 }; + + // model column, usually the same as col, however UI_ICON_TEXT uses two columns in the model + int model_col = 0; + for (int col = 0; col < header.size(); col++, model_col++) { + // create ui elements with the correct cell border + // dependeing on the column + Border cellBorder = Border(); + cellBorder.Background(defaultBrush); + cellBorder.BorderBrush(defaultBrush); + if (col == 0) { + cellBorder.BorderThickness(b1); + } + else if (col + 1 == header.size()) { + cellBorder.BorderThickness(b3); + } + else { + cellBorder.BorderThickness(b2); + } + + // dnd + if (ondragstart) { + cellBorder.CanDrag(true); + cellBorder.DragStarting([this](IInspectable const& sender, DragStartingEventArgs args) { + UiDnD dnd; + dnd.evttype = 0; + dnd.dndstartargs = args; + dnd.dndcompletedargs = { nullptr }; + dnd.drageventargs = { nullptr }; + dnd.data = args.Data(); + + UiEvent evt; + evt.obj = this->obj; + evt.window = evt.obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &dnd; + evt.intval = 0; + + this->ondragstart(&evt, this->ondragstartdata); + }); + cellBorder.DropCompleted([this](IInspectable const& sender, DropCompletedEventArgs args) { + UiDnD dnd; + dnd.evttype = 1; + dnd.dndstartargs = { nullptr }; + dnd.dndcompletedargs = args; + dnd.drageventargs = { nullptr }; + dnd.data = { nullptr }; + + UiEvent evt; + evt.obj = this->obj; + evt.window = evt.obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &dnd; + evt.intval = 0; + + if (this->ondragcomplete) { + this->ondragcomplete(&evt, this->ondragcompletedata); + } + }); + } + if (ondrop) { + cellBorder.AllowDrop(true); + cellBorder.Drop(DragEventHandler([this](winrt::Windows::Foundation::IInspectable const& sender, DragEventArgs const& args){ + UiDnD dnd; + dnd.evttype = 2; + dnd.dndstartargs = { nullptr }; + dnd.dndcompletedargs = { nullptr }; + dnd.drageventargs = args; + dnd.dataview = args.DataView(); + + UiEvent evt; + evt.obj = this->obj; + evt.window = evt.obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &dnd; + evt.intval = 0; + + this->ondrop(&evt, this->ondropdata); + })); + cellBorder.DragOver(DragEventHandler([this](winrt::Windows::Foundation::IInspectable const& sender, DragEventArgs const& args){ + args.AcceptedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy); + })); + } + + // set the cell value + // depending on the type, we create different cell controls + UiModelType type = model->types[col]; + switch (type) { + case UI_STRING_FREE: + case UI_STRING: { + TextBlock cell = TextBlock(); + cell.Padding(cellpadding); + cell.VerticalAlignment(VerticalAlignment::Stretch); + char *val = (char*)getvalue(elm, model_col); + textblock_set_str(cell, val); + cellBorder.Child(cell); + if (type == UI_STRING_FREE && val) { + free(val); + } + + break; + } + case UI_INTEGER: { + TextBlock cell = TextBlock(); + cell.Padding(cellpadding); + cell.VerticalAlignment(VerticalAlignment::Stretch); + int *value = (int*)getvalue(elm, model_col); + if (value) { + textblock_set_int(cell, *value); + } + cellBorder.Child(cell); + break; + } + case UI_ICON: { + UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col); + if (iconConstr) { + IconElement icon = iconConstr->getIcon(); + cellBorder.Child(icon); + } + break; + } + case UI_ICON_TEXT_FREE: + case UI_ICON_TEXT: { + StackPanel cellPanel = StackPanel(); + cellPanel.Spacing(2); + cellPanel.Padding(cellpadding); + cellPanel.VerticalAlignment(VerticalAlignment::Stretch); + + cellPanel.Orientation(Orientation::Horizontal); + UiIcon* iconConstr = (UiIcon*)getvalue(elm, model_col++); + char* str = (char*)getvalue(elm, model_col); + if (iconConstr) { + IconElement icon = iconConstr->getIcon(); + cellPanel.Children().Append(icon); + } + TextBlock cell = TextBlock(); + textblock_set_str(cell, str); + cellPanel.Children().Append(cell); + cellBorder.Child(cellPanel); + if (type == UI_ICON_TEXT_FREE && str) { + free(str); + } + break; + } + } + + // event handler + cellBorder.PointerPressed( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + winrt::Windows::System::VirtualKeyModifiers modifiers = args.KeyModifiers(); + bool update_selection = true; + + if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Control) { + // add/remove current row + if (!is_row_selected(row)) { + row_background(row, selectedBrush, selectedBorderBrush); + selection.push_back(row); + } + else { + row_background(row, highlightBrush, highlightBrush); + remove_from_selection(row); + } + } + else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None || selection.size() == 0) { + // no modifier or shift is pressed but there is no selection + if (selection.size() > 0) { + change_rows_bg(selection, defaultBrush, defaultBrush); + } + + row_background(row, selectedBrush, selectedBorderBrush); + selection = { row }; + if (modifiers == winrt::Windows::System::VirtualKeyModifiers::None) { + SYSTEMTIME st; + GetSystemTime(&st); + + ULONG64 now = getsystime(); + ULONG64 tdiff = now - lastPointerPress; + if (tdiff < ui_double_click_time && onactivate != nullptr) { + // two pointer presse events in short time and we have an onactivate handler + update_selection = false; // we don't want an additional selection event + lastPointerPress = 0; // reset double-click + + int selectedrow = row - 1; // subtract header row + + UiListSelection selection; + selection.count = 1; + selection.rows = &selectedrow; + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selection; + evt.intval = selectedrow; + onactivate(&evt, onactivatedata); + } + else { + lastPointerPress = now; + } + } + } + else if (modifiers == winrt::Windows::System::VirtualKeyModifiers::Shift) { + // select everything between the first selection and the current row + std::sort(selection.begin(), selection.end()); + int first = selection.front(); + int last = row; + if (first > row) { + last = first; + first = row; + } + + // clear previous selection + change_rows_bg(selection, defaultBrush, defaultBrush); + + // create new selection + std::vector<int> newselection; + for (int s = first; s <= last; s++) { + newselection.push_back(s); + } + selection = newselection; + change_rows_bg(selection, selectedBrush, selectedBorderBrush); + } + + if (update_selection) { + call_handler(onselection, onselectiondata); + } + }) + ); + cellBorder.PointerReleased( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + + }) + ); + cellBorder.PointerEntered( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + if (!is_row_selected(row)) { + row_background(row, highlightBrush, highlightBrush); + } + }) + ); + cellBorder.PointerExited( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + if (!is_row_selected(row)) { + row_background(row, defaultBrush, defaultBrush); + } + }) + ); + + grid.SetColumn(cellBorder, col); + grid.SetRow(cellBorder, row); + grid.Children().Append(cellBorder); + } + + row++; + elm = list->next(list); + } +} + +void UiTable::clear() { + for (int i = grid.Children().Size()-1; i >= 0; i--) { + FrameworkElement elm = grid.Children().GetAt(i).as<FrameworkElement>(); + int child_row = grid.GetRow(elm); + if (child_row > 0) { + grid.Children().RemoveAt(i); + } + } + + // TODO: should we clean row definitions? +} + +void UiTable::row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush) { + Thickness b1 = { 1, 1, 0, 1 }; // first col + Thickness b2 = { 0, 1, 0, 1 }; // middle + Thickness b3 = { 0, 1, 1, 1 }; // last col + + for (auto child : grid.Children()) { + FrameworkElement elm = child.as<FrameworkElement>(); + int child_row = grid.GetRow(elm); + if (child_row == row) { + Border b = elm.as<Border>(); + b.Background(brush); + b.BorderBrush(borderBrush); + } + } +} + +void UiTable::change_rows_bg(std::vector<int> rows, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush) { + std::for_each(rows.cbegin(), rows.cend(), [&](const int& row) {row_background(row, brush, borderBrush); }); +} + +bool UiTable::is_row_selected(int row) { + return std::find(selection.begin(), selection.end(), row) != selection.end() ? true : false; +} + +void UiTable::remove_from_selection(int row) { + selection.erase(std::remove(selection.begin(), selection.end(), row), selection.end()); + selection.shrink_to_fit(); +} + +UiListSelection UiTable::uiselection() { + std::sort(selection.begin(), selection.end()); + + UiListSelection selobj; + selobj.count = selection.size(); + selobj.rows = nullptr; + if (selobj.count > 0) { + selobj.rows = (int*)calloc(selobj.count, sizeof(int)); + memcpy(selobj.rows, selection.data(), selobj.count * sizeof(int)); + for (int i = 0; i < selobj.count; i++) { + selobj.rows[i]--; + } + } + return selobj; +} + +void UiTable::call_handler(ui_callback cb, void* cbdata) { + if (!cb) { + return; + } + + UiListSelection selobj = uiselection(); + + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = &selobj; + evt.intval = 0; + cb(&evt, cbdata); + + if (selobj.rows) { + free(selobj.rows); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/table.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/tree.h" +#include "toolkit.h" +#include "dnd.h" + +#include "../ui/container.h" + + +typedef struct UiTableColumn { + winrt::Microsoft::UI::Xaml::Controls::Border header; + +} UiTableColumn; + +typedef struct UiTable { + winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollw; + winrt::Microsoft::UI::Xaml::Controls::Grid grid; + winrt::Microsoft::UI::Xaml::Media::SolidColorBrush defaultBrush; + winrt::Microsoft::UI::Xaml::Media::SolidColorBrush highlightBrush; + winrt::Microsoft::UI::Xaml::Media::SolidColorBrush selectedBrush; + winrt::Microsoft::UI::Xaml::Media::SolidColorBrush selectedBorderBrush; + + winrt::Microsoft::UI::Xaml::Controls::Border resizedCol{ nullptr }; + bool resize = false; + + UiObject* obj; + ui_callback onactivate; + void* onactivatedata; + ui_callback onselection; + void* onselectiondata; + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropdata; + UiModel* model = nullptr; + std::vector<UiTableColumn> header; + ui_getvaluefunc getvalue = nullptr; + int maxrows = 0; + int lastSelection = 0; + ULONG64 lastPointerPress = 0; + std::vector<int> selection; + + UiTable(UiObject *obj, winrt::Microsoft::UI::Xaml::Controls::ScrollViewer scrollW, winrt::Microsoft::UI::Xaml::Controls::Grid grid); + + ~UiTable(); + + void add_header(UiModel* model); + + void update(UiList* list, int i); + + void clear(); + + void row_background(int row, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush); + + void change_rows_bg(std::vector<int> rows, winrt::Microsoft::UI::Xaml::Media::Brush brush, winrt::Microsoft::UI::Xaml::Media::Brush borderBrush); + + bool is_row_selected(int row); + + void remove_from_selection(int row); + + UiListSelection uiselection(); + + void call_handler(ui_callback cb, void *cbdata); +} UiTable; + +extern "C" void ui_table_update(UiList * list, int i); + +extern "C" UiListSelection ui_table_selection(UiList * list);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/text.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,609 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "text.h" + +#include "../common/context.h" +#include "../common/object.h" + +#include <cx/string.h> +#include <cx/allocator.h> + +#include "util.h" +#include "container.h" + + + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Microsoft::UI::Xaml::Media; +using namespace winrt::Microsoft::UI::Xaml::Controls::Primitives; +using namespace winrt::Windows::UI::Xaml::Input; + + +UIEXPORT UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textarea and toolkit wrapper + TextBox textarea = TextBox(); + textarea.AcceptsReturn(true); + ScrollViewer::SetVerticalScrollBarVisibility(textarea, ScrollBarVisibility::Auto); + UIElement elm = textarea; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_TEXT); + if (var) { + UiText* value = (UiText*)var->value; + value->obj = widget; + value->undomgr = NULL; + value->set = ui_textarea_set; + value->get = ui_textarea_get; + value->getsubstr = ui_textarea_getsubstr; + value->insert = ui_textarea_insert; + value->setposition = ui_textarea_setposition; + value->position = ui_textarea_position; + value->selection = ui_textarea_selection; + value->length = ui_textarea_length; + value->remove = ui_textarea_remove; + } + + // add textarea to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(textarea, true); + + return widget; +} + +UIEXPORT UIWIDGET ui_textarea_gettextwidget(UIWIDGET textarea) { + return textarea; +} + +UIEXPORT void ui_text_undo(UiText *value) { + +} + +UIEXPORT void ui_text_redo(UiText *value) { + +} + +// -------------------------- getter/setter for textarea UiText -------------------------- + +char* ui_wtext_get(UiText *text, std::wstring &value) { + if (text->value.ptr) { + text->value.free(text->value.ptr); + } + + text->value.ptr = wchar2utf8(value.c_str(), value.length()); + text->value.free = free; + + return text->value.ptr; +} + +std::wstring ui_wtext_set(UiText *text, const char* value) { + if (text->value.ptr) { + text->value.free(text->value.ptr); + } + + text->value.ptr = _strdup(value); + text->value.free = free; + + int len; + wchar_t* wstr = str2wstr(value, &len); + std::wstring s(wstr); + free(wstr); + + return s; +} + +extern "C" char* ui_textarea_get(UiText *text) { + UiWidget* widget = (UiWidget*)text->obj; + TextBox box = widget->uielement.as<TextBox>(); + std::wstring wstr(box.Text()); + return ui_wtext_get(text, wstr); +} + +extern "C" void ui_textarea_set(UiText *text, const char *newvalue) { + UiWidget* widget = (UiWidget*)text->obj; + TextBox box = widget->uielement.as<TextBox>(); + box.Text(ui_wtext_set(text, newvalue)); +} + +extern "C" char* ui_textarea_getsubstr(UiText *text, int begin, int end) { + return NULL; +} + +extern "C" void ui_textarea_insert(UiText *text, int pos, char *str) { + +} + +extern "C" void ui_textarea_setposition(UiText *text, int pos) { + +} + +extern "C" int ui_textarea_position(UiText *text) { + return 0; +} + +extern "C" void ui_textarea_selection(UiText *text, int *begin, int *end) { + +} + +extern "C" int ui_textarea_length(UiText *text) { + return 0; +} + +extern "C" void ui_textarea_remove(UiText *text, int begin, int end) { + +} + + + + +UIWIDGET ui_textfield_create(UiObject* obj, UiTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textbox and toolkit wrapper + TextBox textfield = TextBox(); + UIElement elm = textfield; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = widget; + value->get = ui_textfield_get; + value->set = ui_textfield_set; + + // listener for notifying observers + // TODO: + } + + // add textfield to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(textfield, false); + + return widget; +} + +UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs args) { + return ui_textfield_create(obj, args); +} + +UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + // create textbox and toolkit wrapper + PasswordBox textfield = PasswordBox(); + UIElement elm = textfield; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(current->ctx, widget); + ui_set_widget_groups(current->ctx, widget, args.groups); + + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = widget; + value->get = ui_passwordfield_get; + value->set = ui_passwordfield_set; + + // listener for notifying observers + // TODO: + } + + // add textfield to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(textfield, false); + + return widget; +} + + +// -------------------------- getter/setter for textfield UiString -------------------------- + +char* ui_wstring_get(UiString* str, std::wstring &value) { + if (str->value.ptr) { + str->value.free(str->value.ptr); + } + + str->value.ptr = wchar2utf8(value.c_str(), value.length()); + str->value.free = free; + + return str->value.ptr; +} + +std::wstring ui_wstring_set(UiString* str, const char* value) { + if (str->value.ptr) { + str->value.free(str->value.ptr); + } + + str->value.ptr = _strdup(value); + str->value.free = free; + + int len; + wchar_t* wstr = str2wstr(value, &len); + std::wstring s(wstr); + free(wstr); + + return s; +} + +char* ui_textfield_get(UiString * str) { + UiWidget* widget = (UiWidget*)str->obj; + TextBox box = widget->uielement.as<TextBox>(); + std::wstring wstr(box.Text()); + return ui_wstring_get(str, wstr); +} + +void ui_textfield_set(UiString * str, const char* newvalue) { + UiWidget* widget = (UiWidget*)str->obj; + TextBox box = widget->uielement.as<TextBox>(); + box.Text(ui_wstring_set(str, newvalue)); +} + + +char* ui_passwordfield_get(UiString * str) { + UiWidget* widget = (UiWidget*)str->obj; + PasswordBox box = widget->uielement.as<PasswordBox>(); + std::wstring wstr(box.Password()); + return ui_wstring_get(str, wstr); +} + +void ui_passwordfield_set(UiString * str, const char* newvalue) { + UiWidget* widget = (UiWidget*)str->obj; + PasswordBox box = widget->uielement.as<PasswordBox>(); + box.Password(ui_wstring_set(str, newvalue)); +} + + +// ------------------------ path textfield -------------------------------------- + +extern "C" static void destroy_ui_pathtextfield(void* ptr) { + UiPathTextField* pb = (UiPathTextField*)ptr; + delete pb; +} + +static void ui_context_add_pathtextfield_destructor(UiContext* ctx, UiPathTextField* pb) { + cxMempoolRegister(ctx->mp, pb, destroy_ui_pathtextfield); +} + +static void ui_pathtextfield_clear(StackPanel& buttons) { + for (int i = buttons.Children().Size() - 1; i >= 0; i--) { + buttons.Children().RemoveAt(i); + } +} + +static void ui_pathfield_free_pathelms(UiPathElm* elms, size_t nelm) { + if (!elms) { + return; + } + for (int i = 0; i < nelm; i++) { + UiPathElm e = elms[i]; + free(e.name); + free(e.path); + } + free(elms); +} + +UiPathTextField::~UiPathTextField() { + ui_pathfield_free_pathelms(this->current_path, this->current_path_nelms); +} + +static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) { + cxstring *pathelms; + size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms); + + if (nelm == 0) { + *ret_nelm = 0; + return nullptr; + } + + UiPathElm* elms = (UiPathElm*)calloc(nelm, sizeof(UiPathElm)); + size_t n = nelm; + int j = 0; + for (int i = 0; i < nelm; i++) { + cxstring c = pathelms[i]; + if (c.length == 0) { + if (i == 0) { + c.length = 1; + } + else { + n--; + continue; + } + } + + cxmutstr m = cx_strdup(c); + elms[j].name = m.ptr; + elms[j].name_len = m.length; + + size_t elm_path_len = c.ptr + c.length - full_path; + cxmutstr elm_path = cx_strdup(cx_strn(full_path, elm_path_len)); + elms[j].path = elm_path.ptr; + elms[j].path_len = elm_path.length; + + j++; + } + *ret_nelm = n; + + return elms; +} + +int ui_pathtextfield_update(UiPathTextField* pb, const char *full_path) { + Grid grid = pb->grid; + + ui_pathelm_func getpathelm = pb->getpathelm; + void* getpathelmdata = pb->getpathelmdata; + + size_t full_path_len = full_path ? strlen(full_path) : 0; + + size_t nelm = 0; + UiPathElm* path_elm = getpathelm(full_path, full_path_len, &nelm, getpathelmdata); + if (!path_elm) { + return 1; + } + + // hide textbox, show button panel + pb->textbox.Visibility(Visibility::Collapsed); + pb->buttons.Visibility(Visibility::Visible); + + // clear old buttons + ui_pathtextfield_clear(pb->buttons); + + ui_pathfield_free_pathelms(pb->current_path, pb->current_path_nelms); + pb->current_path = path_elm; + pb->current_path_nelms = nelm; + + // add new buttons + int j = 0; + for (int i = 0; i < nelm;i++) { + UiPathElm elm = path_elm[i]; + wchar_t* wstr = str2wstr_len(elm.name, elm.name_len, nullptr); + Button button = Button(); + button.Content(box_value(wstr)); + free(wstr); + + if (pb->onactivate) { + button.Click([pb, j, elm](IInspectable const& sender, RoutedEventArgs) { + // copy elm.path because it could be a non-terminated string + cxmutstr elmpath = cx_strdup(cx_strn(elm.path, elm.path_len)); + + UiEvent evt; + evt.obj = pb->obj; + evt.window = evt.obj->window; + evt.document = evt.obj->ctx->document; + evt.eventdata = elmpath.ptr; + evt.intval = j; + pb->onactivate(&evt, pb->onactivatedata); + + free(elmpath.ptr); + }); + } + + Thickness t = { 0, 0, 1, 0 }; + CornerRadius c = { 0 ,0, 0, 0 }; + button.BorderThickness(t); + button.CornerRadius(c); + + pb->buttons.Children().Append(button); + + j++; + } + + return 0; +} + +char* ui_path_textfield_get(UiString * str) { + UiPathTextField* widget = (UiPathTextField*)str->obj; + TextBox box = widget->textbox; + std::wstring wstr(box.Text()); + return ui_wstring_get(str, wstr); +} + +void ui_path_textfield_set(UiString* str, const char* newvalue) { + UiPathTextField* widget = (UiPathTextField*)str->obj; + TextBox box = widget->textbox; + box.Text(ui_wstring_set(str, newvalue)); + ui_pathtextfield_update(widget, newvalue); +} + +UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs args) { + UiObject* current = uic_current_obj(obj); + + // create view and toolkit wrapper + Border pathbar = Border(); + + IInspectable bgRes = Application::Current().Resources().Lookup(box_value(L"TextControlBackground")); + IInspectable borderThicknessRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderThemeThickness")); + IInspectable borderBrushRes = Application::Current().Resources().Lookup(box_value(L"TextControlBorderBrush")); + // IInspectable cornerRes = Application::Current().Resources().Lookup(box_value(L"TextControlCornerRadius")); + + Brush bgBrush = unbox_value<Brush>(bgRes); + Thickness border = unbox_value<Thickness>(borderThicknessRes); + Brush borderBrush = unbox_value<Brush>(borderBrushRes); + CornerRadius cornerRadius = { 4, 4, 4, 4 }; //unbox_value<CornerRadius>(cornerRes); + + pathbar.Background(bgBrush); + pathbar.BorderBrush(borderBrush); + pathbar.BorderThickness(border); + pathbar.CornerRadius(cornerRadius); + + Grid content = Grid(); + pathbar.Child(content); + + GridLength gl; + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + content.ColumnDefinitions().Append(coldef); + + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + + ColumnDefinition coldef2 = ColumnDefinition(); + coldef2.Width(gl); + content.ColumnDefinitions().Append(coldef2); + + TextBox pathTextBox = TextBox(); + Thickness t = { 0, 0, 0, 0 }; + CornerRadius c = { 0 ,0, 0, 0 }; + pathTextBox.BorderThickness(t); + //pathTextBox.CornerRadius(c); + + + pathTextBox.HorizontalAlignment(HorizontalAlignment::Stretch); + content.SetColumn(pathTextBox, 0); + content.SetColumnSpan(pathTextBox, 2); + + content.Children().Append(pathTextBox); + + // stackpanel for buttons + StackPanel buttons = StackPanel(); + buttons.Orientation(Orientation::Horizontal); + buttons.Visibility(Visibility::Collapsed); + content.SetColumn(buttons, 0); + content.Children().Append(buttons); + + TextBlock filler = TextBlock(); + filler.VerticalAlignment(VerticalAlignment::Stretch); + //filler.Text(winrt::hstring(L"hello filler")); + + filler.HorizontalAlignment(HorizontalAlignment::Stretch); + filler.VerticalAlignment(VerticalAlignment::Stretch); + content.SetColumn(filler, 1); + content.Children().Append(filler); + + filler.PointerPressed( + winrt::Microsoft::UI::Xaml::Input::PointerEventHandler( + [=](IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::PointerRoutedEventArgs const& args) { + pathTextBox.Visibility(Visibility::Visible); + buttons.Visibility(Visibility::Collapsed); + filler.Visibility(Visibility::Collapsed); + pathTextBox.SelectionStart(pathTextBox.Text().size()); + pathTextBox.SelectionLength(0); + pathTextBox.Focus(FocusState::Keyboard); + }) + ); + + //pathTextBox.Visibility(Visibility::Collapsed); + + UiPathTextField* uipathbar = new UiPathTextField; + ui_context_add_pathtextfield_destructor(current->ctx, uipathbar); + uipathbar->grid = content; + uipathbar->buttons = buttons; + uipathbar->textbox = pathTextBox; + uipathbar->filler = filler; + uipathbar->obj = obj; + uipathbar->getpathelm = args.getpathelm ? args.getpathelm : default_pathelm_func; + uipathbar->getpathelmdata = args.getpathelmdata; + uipathbar->onactivate = args.onactivate; + uipathbar->onactivatedata = args.onactivatedata; + uipathbar->ondragstart = args.ondragstart; + uipathbar->ondragstartdata = args.ondragstartdata; + uipathbar->ondragcomplete = args.ondragcomplete; + uipathbar->ondragcompletedata = args.ondragcompletedata; + uipathbar->ondrop = args.ondrop; + uipathbar->ondropdata = args.ondropsdata; + + + pathTextBox.KeyDown( + winrt::Microsoft::UI::Xaml::Input::KeyEventHandler( + [=](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Input::KeyRoutedEventArgs const& e) { + auto key = e.Key(); + bool showButtons = false; + bool update = false; + if (key == Windows::System::VirtualKey::Escape) { + showButtons = true; + } + else if (key == Windows::System::VirtualKey::Enter) { + showButtons = true; + update = true; + } + + if (showButtons) { + pathTextBox.Visibility(Visibility::Collapsed); + buttons.Visibility(Visibility::Visible); + filler.Visibility(Visibility::Visible); + if (update) { + std::wstring value(pathTextBox.Text()); + char* full_path = wchar2utf8(value.c_str(), value.length()); + + if (!ui_pathtextfield_update(uipathbar, full_path)) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = full_path; + evt.intval = -1; + args.onactivate(&evt, args.onactivatedata); + } + + free(full_path); + } + + //buttons.Focus(FocusState::Keyboard); + } + }) + ); + + + UIElement elm = pathbar; + UiWidget* widget = new UiWidget(elm); + widget->data1 = uipathbar; + ui_context_add_widget_destructor(current->ctx, widget); + + // bind var + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_STRING); + if (var) { + UiString* value = (UiString*)var->value; + value->obj = uipathbar; + value->get = ui_path_textfield_get; + value->set = ui_path_textfield_set; + } + + // add listview to current container + UI_APPLY_LAYOUT1(current, args); + + current->container->Add(pathbar, false); + + return widget; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/text.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/text.h" +#include "toolkit.h" + +#include "../ui/container.h" + +struct UiPathTextField { + winrt::Microsoft::UI::Xaml::Controls::Grid grid = { nullptr }; + winrt::Microsoft::UI::Xaml::Controls::StackPanel buttons = { nullptr }; + winrt::Microsoft::UI::Xaml::Controls::TextBox textbox = { nullptr }; + winrt::Microsoft::UI::Xaml::Controls::TextBlock filler = { nullptr }; + + ~UiPathTextField(); + + UiPathElm* current_path = nullptr; + size_t current_path_nelms = 0; + + UiObject* obj; + + ui_pathelm_func getpathelm; + void* getpathelmdata; + + ui_callback onactivate; + void* onactivatedata; + + ui_callback ondragstart; + void* ondragstartdata; + ui_callback ondragcomplete; + void* ondragcompletedata; + ui_callback ondrop; + void* ondropdata; +}; + +char* ui_wtext_get(UiText *text, std::wstring &value); +std::wstring ui_wtext_set(UiText *text, const char* value); + +char* ui_wstring_get(UiString* str, std::wstring& value); +std::wstring ui_wstring_set(UiString* str, const char* value); + +extern "C" char* ui_textarea_get(UiText *text); +extern "C" void ui_textarea_set(UiText *text, const char *newvalue); +extern "C" char* ui_textarea_getsubstr(UiText*, int, int); +extern "C" void ui_textarea_insert(UiText*, int, char*); +extern "C" void ui_textarea_setposition(UiText*,int); +extern "C" int ui_textarea_position(UiText*); +extern "C" void ui_textarea_selection(UiText*, int*, int*); +extern "C" int ui_textarea_length(UiText*); +extern "C" void ui_textarea_remove(UiText*, int, int); + +extern "C" char* ui_textfield_get(UiString *str); +extern "C" void ui_textfield_set(UiString *str, const char *newvalue); + +extern "C" char* ui_passwordfield_get(UiString * str); +extern "C" void ui_passwordfield_set(UiString * str, const char* newvalue); + +extern "C" char* ui_path_textfield_get(UiString * str); +extern "C" void ui_path_textfield_set(UiString * str, const char* newvalue);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/toolkit.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,382 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + +#include "toolkit.h" + +#include <cx/allocator.h> +#include <cx/mempool.h> + +#include "../common/context.h" +#include "../common/document.h" +#include "../common/toolbar.h" +#include "../common/properties.h" + +#include "icons.h" + +#include "MainWindow.xaml.h" + +#include "App.xaml.h" + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::XamlTypeInfo; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace Windows::UI::Core; + +static const char* application_name; + +static ui_callback startup_func; +static void* startup_data; + +static ui_callback open_func; +void* open_data; + +static ui_callback exit_func; +void* exit_data; + +static ui_callback appclose_fnc; + +static void* appclose_udata; + + +static UiObject* active_window; + +static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = { nullptr }; + +void ui_app_run_startup() { + uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread(); + + if (startup_func) { + startup_func(NULL, startup_data); + } +} + +class App : public ApplicationT<App, IXamlMetadataProvider> { +public: + void OnLaunched(LaunchActivatedEventArgs const&) { + Resources().MergedDictionaries().Append(XamlControlsResources()); + if (startup_func) { + startup_func(NULL, startup_data); + } + + //auto window = make<winui::implementation::MainWindow>(); + //window.Activate(); + } + IXamlType GetXamlType(TypeName const& type) { + return provider.GetXamlType(type); + } + IXamlType GetXamlType(hstring const& fullname) { + return provider.GetXamlType(fullname); + } + com_array<XmlnsDefinition> GetXmlnsDefinitions() { + return provider.GetXmlnsDefinitions(); + } +private: + XamlControlsXamlMetaDataProvider provider; +}; + +UiWidget::UiWidget(winrt::Microsoft::UI::Xaml::UIElement& elm) : uielement(elm) {} + +extern "C" void destroy_ui_window_wrapper(void* ptr) { + UiWindow* win = (UiWindow*)ptr; + delete win; +} + +extern "C" void destroy_ui_widget_wrapper(void* ptr) { + UiWidget* widget = (UiWidget*)ptr; + delete widget; +} + +extern "C" void destroy_ui_container_wrapper(void* ptr) { + UiContainer* ctn = (UiContainer*)ptr; + delete ctn; +} + +void ui_context_add_window_destructor(UiContext* ctx, UiWindow* win) { + cxMempoolRegister(ctx->mp, win, destroy_ui_window_wrapper); +} + +void ui_context_add_widget_destructor(UiContext* ctx, UiWidget* widget) { + cxMempoolRegister(ctx->mp, widget, destroy_ui_widget_wrapper); +} + +void ui_context_add_container_destructor(UiContext* ctx, UiContainer *container) { + cxMempoolRegister(ctx->mp, container, destroy_ui_container_wrapper); +} + + +UiEvent ui_create_int_event(UiObject* obj, int64_t i) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = i; + return evt; +} + + +#include <MddBootstrap.h> + +void ui_appsdk_bootstrap(void) { + const UINT32 majorMinorVersion{ 0x00010002 }; + PCWSTR versionTag{ L"" }; + const PACKAGE_VERSION minVersion{}; + + const HRESULT hr = MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion); + if (FAILED(hr)) { + exit(102); + } +} + +void ui_init(const char* appname, int argc, char** argv) { + application_name = appname; + + //ui_appsdk_bootstrap(); + + uic_init_global_context(); + uic_docmgr_init(); + uic_menu_init(); + uic_toolbar_init(); + + uic_load_app_properties(); +} + +const char* ui_appname() { + return application_name; +} + +void ui_onstartup(ui_callback f, void* userdata) { + startup_func = f; + startup_data = userdata; +} + +void ui_onopen(ui_callback f, void* userdata) { + open_func = f; + open_data = userdata; +} + +void ui_onexit(ui_callback f, void* userdata) { + exit_func = f; + exit_data = userdata; +} + +void ui_main() { + /* + init_apartment(); + //Application::Start([](auto&&) {make<App>(); }); + + ::winrt::Microsoft::UI::Xaml::Application::Start( + [](auto&&) + { + ::winrt::make<::winrt::winui::implementation::App>(); + }); + */ + { + void (WINAPI * pfnXamlCheckProcessRequirements)(); + auto module = ::LoadLibrary(L"Microsoft.ui.xaml.dll"); + if (module) + { + pfnXamlCheckProcessRequirements = reinterpret_cast<decltype(pfnXamlCheckProcessRequirements)>(GetProcAddress(module, "XamlCheckProcessRequirements")); + if (pfnXamlCheckProcessRequirements) + { + (*pfnXamlCheckProcessRequirements)(); + } + + ::FreeLibrary(module); + } + } + + winrt::init_apartment(winrt::apartment_type::single_threaded); + ::winrt::Microsoft::UI::Xaml::Application::Start( + [](auto&&) + { + ::winrt::make<::winrt::winui::implementation::App>(); + }); +} + +class UiWin { +public: + Window window; +}; + +void ui_show(UiObject* obj) { + if (obj->wobj) { + obj->wobj->window.Activate(); + } else if(obj->widget && obj->widget->Show) { + obj->widget->Show(); + } +} + +void ui_close(UiObject* obj) { + if (obj->wobj) { + obj->wobj->window.Close(); + } +} + +static void ui_job_thread(UiJob* job) { + if (!job->job_func(job->job_data) && job->finish_callback) { + bool isQueued = uiDispatcherQueue.TryEnqueue([job]() + { + UiEvent event; + event.obj = job->obj; + event.window = job->obj->window; + event.document = job->obj->ctx->document; + event.intval = 0; + event.eventdata = NULL; + job->finish_callback(&event, job->finish_data); + delete job; + }); + if (!isQueued) { + // TODO: error or try again? + exit(-1); + } + } + else { + delete job; + } +} + +UIEXPORT void ui_job(UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { + UiJob* job = new UiJob; + job->obj = obj; + job->job_func = tf; + job->job_data = td; + job->finish_callback = f; + job->finish_data = fd; + + std::thread jobThread(ui_job_thread, job); + jobThread.detach(); +} + +UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td) { + bool isQueued = uiDispatcherQueue.TryEnqueue([tf, td]() + { + (void)tf(td); + }); + if (!isQueued) { + // TODO: error or try again? + exit(-1); + } +} + +static UiJob kill_job; // &kill_job indicates to stop the thread + +static void ui_threadpool_run(UiThreadpool* pool) { + for (;;) { + UiJob* job = pool->GetJob(); + if (job == &kill_job) { + return; + } + else if (job) { + ui_job_thread(job); + } + } +} + +UiThreadpool::UiThreadpool(int nthreads) { + for (int i = 0; i < nthreads; i++) { + std::thread thread(ui_threadpool_run, this); + thread.detach(); + } +} + +void UiThreadpool::EnqueueJob(UiJob* job) +{ + std::unique_lock<std::mutex> lock(mutex); + queue.push(job); + lock.unlock(); + condition.notify_one(); +} + +UiJob* UiThreadpool::GetJob() { + std::unique_lock<std::mutex> lock(mutex); + + UiJob* job = nullptr; + while (!job) { + if (queue.empty()) { + condition.wait(lock); + continue; + } + else + { + job = queue.front(); + queue.pop(); + } + } + + return job; +} + +UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads) { + return new UiThreadpool(nthreads); +} + +UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool) { + // TODO +} + +UIEXPORT void ui_threadpool_job(UiThreadpool* pool, UiObject* obj, ui_threadfunc tf, void* td, ui_callback f, void* fd) { + UiJob* job = new UiJob; + job->obj = obj; + job->job_func = tf; + job->job_data = td; + job->finish_callback = f; + job->finish_data = fd; + pool->EnqueueJob(job); +} + + + +void ui_set_widget_groups(UiContext *ctx, UIWIDGET widget, const int *groups) { + if(!groups) { + return; + } + size_t ngroups = uic_group_array_size(groups); + ui_set_widget_ngroups(ctx, widget, groups, ngroups); +} + +void ui_set_widget_ngroups(UiContext *ctx, UIWIDGET widget, const int *groups, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); + ui_set_enabled(widget, FALSE); + } +} + + +UIEXPORT void ui_set_enabled(UIWIDGET widget, int enabled) { + Control ctrl = widget->uielement.as<Control>(); + if (ctrl) { + ctrl.IsEnabled(enabled); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/toolkit.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "../ui/toolkit.h" + +#include <queue> +#include <mutex> +#include <condition_variable> + +typedef struct UiJob { + UiObject* obj; + ui_threadfunc job_func; + void* job_data; + ui_callback finish_callback; + void* finish_data; +} UiJob; + +struct UiThreadpool +{ + std::queue<UiJob*> queue; + std::mutex mutex; + std::condition_variable condition; + + UiThreadpool(int nthreads); + + void EnqueueJob(UiJob* job); + + UiJob* GetJob(); +}; + +typedef void(*ui_eventfunc)(void*, void*); + +void ui_app_run_startup(); + +extern "C" void destroy_ui_window_wrapper(void* ptr); +extern "C" void destroy_ui_widget_wrapper(void* ptr); + +void ui_context_add_window_destructor(UiContext* ctx, UiWindow* win); +void ui_context_add_widget_destructor(UiContext* ctx, UiWidget* widget); +void ui_context_add_container_destructor(UiContext* ctx, UiContainer *container); + +UiEvent ui_create_int_event(UiObject* obj, int64_t i); + +void ui_set_widget_groups(UiContext *ctx, UIWIDGET widget, const int *groups); +void ui_set_widget_ngroups(UiContext *ctx, UIWIDGET widget, const int *groups, size_t ngroups);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/util.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,46 @@ +#include "pch.h" + +#include "util.h" + +#include <stdlib.h> + + +wchar_t* str2wstr(const char* str, int* newlen) { + size_t len = strlen(str); + + return str2wstr_len(str, len, newlen); +} + +wchar_t* str2wstr_len(const char* str, size_t len, int* newlen) { + wchar_t* wstr = (wchar_t*)calloc(len + 1, sizeof(wchar_t)); + int wlen = MultiByteToWideChar( + CP_UTF8, + 0, + str, + len, + wstr, + len + 1 + ); + if (newlen) { + *newlen = wlen; + } + wstr[wlen] = 0; + + return wstr; +} + +char* wchar2utf8(const wchar_t* wstr, size_t wlen) { + size_t maxlen = wlen * 4; + char* ret = (char*)malloc(maxlen + 1); + int ret_len = WideCharToMultiByte( + CP_UTF8, + 0, + wstr, + wlen, + ret, + maxlen, + NULL, + NULL); + ret[ret_len] = 0; + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/util.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,9 @@ +#pragma once + +#include <stdlib.h> + +wchar_t* str2wstr(const char* str, int* newlen); + +wchar_t* str2wstr_len(const char* str, size_t len, int* newlen); + +char* wchar2utf8(const wchar_t* wstr, size_t wlen);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/window.cpp Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,637 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pch.h" + + +#include "window.h" + +#include "appmenu.h" +#include "commandbar.h" +#include "container.h" +#include "util.h" +#include "button.h" + +#include "../common/context.h" +#include "../common/object.h" + +#include <stdlib.h> + +#include <cx/mempool.h> + +#include "MainWindow.xaml.h" + + +#include <Windows.h> +#include <shobjidl.h> +#include <iostream> + +using namespace winrt; +using namespace Microsoft::UI::Xaml; +using namespace Microsoft::UI::Xaml::Controls; +using namespace Microsoft::UI::Xaml::Controls::Primitives; +using namespace Microsoft::UI::Xaml::XamlTypeInfo; +using namespace Microsoft::UI::Xaml::Markup; +using namespace Windows::UI::Xaml::Interop; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Storage::Pickers; + +UiWindow::UiWindow(winrt::Microsoft::UI::Xaml::Window& win) : window(win) {} + +UiObject* ui_window(const char* title, void* window_data) { + UiObject* obj = ui_simple_window(title, window_data); + + /* + if (uic_get_menu_list()) { + // create/add menubar + MenuBar mb = ui_create_menubar(obj); + mb.VerticalAlignment(VerticalAlignment::Top); + obj->container->Add(mb, false); + } + */ + + if (uic_toolbar_isenabled()) { + // create a grid for the toolbar: ColumnDefinitions="Auto, *, Auto" + Grid toolbar_grid = Grid(); + GridLength gl; + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + + ColumnDefinition coldef0 = ColumnDefinition(); + coldef0.Width(gl); + toolbar_grid.ColumnDefinitions().Append(coldef0); + + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + ColumnDefinition coldef1 = ColumnDefinition(); + coldef1.Width(gl); + toolbar_grid.ColumnDefinitions().Append(coldef1); + + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + ColumnDefinition coldef2 = ColumnDefinition(); + coldef2.Width(gl); + toolbar_grid.ColumnDefinitions().Append(coldef2); + + // rowdef + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + RowDefinition rowdef = RowDefinition(); + rowdef.Height(gl); + toolbar_grid.RowDefinitions().Append(rowdef); + + + // create commandbar + CxList* def_l = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList* def_c = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList* def_r = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + bool addappmenu = true; + if (cxListSize(def_r) > 0) { + CommandBar toolbar_r = ui_create_toolbar(obj, def_r, addappmenu); + toolbar_grid.SetColumn(toolbar_r, 2); + toolbar_grid.SetRow(toolbar_r, 0); + toolbar_grid.Children().Append(toolbar_r); + addappmenu = false; + } + if (cxListSize(def_c) > 0) { + CommandBar toolbar_c = ui_create_toolbar(obj, def_c, addappmenu); + toolbar_c.HorizontalAlignment(HorizontalAlignment::Center); + toolbar_grid.SetColumn(toolbar_c, 1); + toolbar_grid.SetRow(toolbar_c, 0); + toolbar_grid.Children().Append(toolbar_c); + addappmenu = false; + } + if (cxListSize(def_l) > 0) { + CommandBar toolbar_l = ui_create_toolbar(obj, def_l, addappmenu); + toolbar_grid.SetColumn(toolbar_l, 0); + toolbar_grid.SetRow(toolbar_l, 0); + toolbar_grid.Children().Append(toolbar_l); + } + + toolbar_grid.VerticalAlignment(VerticalAlignment::Top); + obj->container->Add(toolbar_grid, false); + } + + return obj; +} + +UIEXPORT UiObject* ui_simple_window(const char *title, void *window_data) { + CxMempool* mp = cxBasicMempoolCreate(256); + UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject)); + + obj->ctx = uic_context(obj, mp); + obj->window = window_data; + + Window window = Window(); + //Window window = make<winui::implementation::MainWindow>(); + + winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///MainWindow.xaml" }; + Application::LoadComponent(window, resourceLocator, ComponentResourceLocation::Nested); + + window.ExtendsContentIntoTitleBar(true); + + Grid grid = Grid(); + window.Content(grid); + + StackPanel titleBar = StackPanel(); + Thickness titleBarPadding = { 10, 5, 5, 10 }; + titleBar.Padding(titleBarPadding); + titleBar.Orientation(Orientation::Horizontal); + TextBlock titleLabel = TextBlock(); + titleBar.Children().Append(titleLabel); + + if (title) { + wchar_t* wtitle = str2wstr(title, nullptr); + window.Title(wtitle); + titleLabel.Text(hstring(wtitle)); + free(wtitle); + } + + window.SetTitleBar(titleBar); + + obj->wobj = new UiWindow(window); + ui_context_add_window_destructor(obj->ctx, obj->wobj); + + window.Closed([obj](IInspectable const& sender, WindowEventArgs) { + if (obj->ctx->close_callback) { + UiEvent evt; + evt.obj = obj; + evt.document = obj->ctx->document; + evt.window = obj->window; + evt.eventdata = NULL; + evt.intval = 0; + obj->ctx->close_callback(&evt, obj->ctx->close_data); + } else { + ui_context_destroy(obj->ctx); + } + }); + + obj->container = new UiBoxContainer(grid, UI_BOX_CONTAINER_VBOX, 0, 0); + + titleBar.VerticalAlignment(VerticalAlignment::Top); + obj->container->Add(titleBar, false); + + obj->window = window_data; + + return obj; +} + +static void dialog_button_add_callback(ContentDialog dialog, Button button, int num, UiObject *obj, ui_callback onclick, void *onclickdata) { + button.Click([dialog, num, obj, onclick, onclickdata](IInspectable const& sender, RoutedEventArgs) { + if (onclick) { + UiEvent evt; + evt.obj = obj; + evt.window = obj->window; + evt.document = obj->ctx->document; + evt.eventdata = nullptr; + evt.intval = num; + onclick(&evt, onclickdata); + } + dialog.Hide(); + }); +} + +UIEXPORT UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args) { + UiWindow *window = parent->wobj; + if (!window) { + return NULL; + } + + CxMempool* mp = cxBasicMempoolCreate(256); + UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject)); + + obj->ctx = uic_context(obj, mp); + + ContentDialog dialog = ContentDialog(); + UIElement elm = dialog; + UiWidget* widget = new UiWidget(elm); + ui_context_add_widget_destructor(obj->ctx, widget); + obj->widget = widget; + + if (args.title) { + wchar_t* wtitle = str2wstr(args.title, nullptr); + dialog.Title(box_value(wtitle)); + free(wtitle); + } + + + Grid dialogContent = Grid(); + GridLength gl; + + // content row + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + RowDefinition rowdef0 = RowDefinition(); + rowdef0.Height(gl); + dialogContent.RowDefinitions().Append(rowdef0); + + // button row + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + RowDefinition rowdef1 = RowDefinition(); + rowdef1.Height(gl); + dialogContent.RowDefinitions().Append(rowdef1); + + // coldef + gl.Value = 1; + gl.GridUnitType = GridUnitType::Star; + ColumnDefinition coldef = ColumnDefinition(); + coldef.Width(gl); + dialogContent.ColumnDefinitions().Append(coldef); + + // content + Grid grid = Grid(); + grid.SetRow(grid, 0); + grid.SetColumn(grid, 0); + dialogContent.Children().Append(grid); + obj->container = new UiBoxContainer(grid, UI_BOX_CONTAINER_VBOX, 0, 0); + + // buttons + Grid buttons = Grid(); + Thickness btnsMargin = { (double)0, (double)10, (double)0, (double)0 }; + buttons.Margin(btnsMargin); + + RowDefinition btnrowdef = RowDefinition(); + gl.Value = 0; + gl.GridUnitType = GridUnitType::Auto; + btnrowdef.Height(gl); + buttons.RowDefinitions().Append(btnrowdef); + + gl.Value = 1; + gl.GridUnitType = GridUnitType::Auto; + int c = 0; + if (args.lbutton1) { + ColumnDefinition bcoldef = ColumnDefinition(); + bcoldef.Width(gl); + buttons.ColumnDefinitions().Append(bcoldef); + + Button btn = Button(); + ui_set_button_label(btn, args.lbutton1, NULL, NULL, UI_LABEL_TEXT); + Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; + btn.Margin(margin); + btn.HorizontalAlignment(HorizontalAlignment::Stretch); + dialog_button_add_callback(dialog, btn, 1, obj, args.onclick, args.onclickdata); + + buttons.SetRow(btn, 0); + buttons.SetColumn(btn, c++); + buttons.Children().Append(btn); + } + if (args.lbutton2) { + ColumnDefinition bcoldef = ColumnDefinition(); + bcoldef.Width(gl); + buttons.ColumnDefinitions().Append(bcoldef); + + Button btn = Button(); + ui_set_button_label(btn, args.lbutton2, NULL, NULL, UI_LABEL_TEXT); + Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; + btn.Margin(margin); + btn.HorizontalAlignment(HorizontalAlignment::Stretch); + dialog_button_add_callback(dialog, btn, 2, obj, args.onclick, args.onclickdata); + + buttons.SetRow(btn, 0); + buttons.SetColumn(btn, c++); + buttons.Children().Append(btn); + } + if (args.rbutton3) { + ColumnDefinition bcoldef = ColumnDefinition(); + bcoldef.Width(gl); + buttons.ColumnDefinitions().Append(bcoldef); + + Button btn = Button(); + ui_set_button_label(btn, args.rbutton3, NULL, NULL, UI_LABEL_TEXT); + Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; + btn.Margin(margin); + btn.HorizontalAlignment(HorizontalAlignment::Stretch); + dialog_button_add_callback(dialog, btn, 3, obj, args.onclick, args.onclickdata); + + buttons.SetRow(btn, 0); + buttons.SetColumn(btn, c++); + buttons.Children().Append(btn); + } + if (args.rbutton4) { + ColumnDefinition bcoldef = ColumnDefinition(); + bcoldef.Width(gl); + buttons.ColumnDefinitions().Append(bcoldef); + + Button btn = Button(); + ui_set_button_label(btn, args.rbutton4, NULL, NULL, UI_LABEL_TEXT); + Thickness margin = { (double)5, (double)5, (double)5, (double)5 }; + btn.Margin(margin); + btn.HorizontalAlignment(HorizontalAlignment::Stretch); + dialog_button_add_callback(dialog, btn, 4, obj, args.onclick, args.onclickdata); + + buttons.SetRow(btn, 0); + buttons.SetColumn(btn, c++); + buttons.Children().Append(btn); + } + + dialogContent.SetRow(buttons, 1); + dialogContent.SetColumn(buttons, 0); + dialogContent.Children().Append(buttons); + + + dialog.Content(dialogContent); + dialog.XamlRoot(window->window.Content().XamlRoot()); + + obj->widget->Show = [dialog]() { + dialog.ShowAsync(); + }; + + return obj; +} + +void ui_window_size(UiObject *obj, int width, int height) { + UIWINDOW win = obj->wobj; + if (win) { + winrt::Windows::Graphics::SizeInt32 wsize; + wsize.Width = width; + wsize.Height = height; + win->window.AppWindow().Resize(wsize); + } +} + + + + +static Windows::Foundation::IAsyncAction create_dialog_async(UiObject *obj, UiDialogArgs args) { + UiObject* current = uic_current_obj(obj); + Window parentWindow = current->wobj->window; + + ContentDialog dialog = ContentDialog(); + dialog.XamlRoot(parentWindow.Content().XamlRoot()); + + if (args.title) { + wchar_t *str = str2wstr(args.title, nullptr); + dialog.Title(winrt::box_value(str)); + free(str); + } + + TextBox textfield{ nullptr }; + PasswordBox password{ nullptr }; + if(args.input || args.password) { + StackPanel panel = StackPanel(); + panel.Orientation(Orientation::Vertical); + if (args.content) { + wchar_t *str = str2wstr(args.content, nullptr); + TextBlock label = TextBlock(); + label.Text(str); + panel.Children().Append(label); + free(str); + } + + Thickness margin = { 0, 5, 0, 0 }; + if (args.password) { + password = PasswordBox(); + password.Margin(margin); + panel.Children().Append(password); + } else { + textfield = TextBox(); + textfield.Margin(margin); + panel.Children().Append(textfield); + } + + panel.Margin(margin); + + dialog.Content(panel); + + } else { + if (args.content) { + wchar_t *str = str2wstr(args.content, nullptr); + dialog.Content(winrt::box_value(str)); + free(str); + } + } + + if (args.button1_label) { + wchar_t *str = str2wstr(args.button1_label, nullptr); + dialog.PrimaryButtonText(winrt::hstring(str)); + free(str); + dialog.DefaultButton(ContentDialogButton::Primary); + } + if (args.button2_label) { + wchar_t *str = str2wstr(args.button2_label, nullptr); + dialog.SecondaryButtonText(winrt::hstring(str)); + free(str); + } + if (args.closebutton_label) { + wchar_t *str = str2wstr(args.closebutton_label, nullptr); + dialog.CloseButtonText(winrt::hstring(str)); + free(str); + } + + ContentDialogResult result = co_await dialog.ShowAsync(); + + if (args.result) { + UiEvent evt; + evt.obj = current; + evt.document = current->ctx->document; + evt.window = current->window; + evt.eventdata = NULL; + evt.intval = 0; + if (result == ContentDialogResult::Primary) { + evt.intval = 1; + } else if (result == ContentDialogResult::Secondary) { + evt.intval = 2; + } + + if (args.password) { + std::wstring wstr(password.Password()); + char *text = wchar2utf8(wstr.c_str(), wstr.length()); + evt.eventdata = text; + } else if (args.input) { + std::wstring wstr(textfield.Text()); + char *text = wchar2utf8(wstr.c_str(), wstr.length()); + evt.eventdata = text; + } + + args.result(&evt, args.resultdata); + + if (evt.eventdata) { + free(evt.eventdata); + } + } +} + +UIEXPORT void ui_dialog_create(UiObject *obj, UiDialogArgs args) { + create_dialog_async(obj, args); +} + + + +// --------------------------------------- File Dialog --------------------------------------- + +static void filedialog_callback( + UiObject *obj, + ui_callback file_selected_callback, + void *cbdata, + winrt::Windows::Foundation::Collections::IVectorView<winrt::Windows::Storage::StorageFile> result) +{ + UiFileList flist; + flist.nfiles = result.Size(); + flist.files = new char*[flist.nfiles]; + + int i = 0; + for (auto const& file : result) { + winrt::hstring path = file.Path(); + flist.files[i++] = wchar2utf8(path.c_str(), path.size()); + } + + UiEvent evt; + evt.obj = obj; + evt.document = obj->ctx->document; + evt.window = obj->window; + evt.eventdata = &flist; + evt.intval = 0; + file_selected_callback(&evt, cbdata); + + for (int i = 0; i < flist.nfiles;i++) { + free(flist.files[i]); + } + delete[] flist.files; +} + +static Windows::Foundation::IAsyncAction open_filedialog_async(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + FileOpenPicker openFileDialog = FileOpenPicker(); + auto initializeWithWindow { openFileDialog.as<::IInitializeWithWindow>() + }; + + HWND hwnd{ nullptr }; + winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd)); + + initializeWithWindow->Initialize(hwnd); + + openFileDialog.FileTypeFilter().Append(L"*"); + + if ((mode & UI_FILEDIALOG_SELECT_MULTI) == UI_FILEDIALOG_SELECT_MULTI) { + auto files = co_await openFileDialog.PickMultipleFilesAsync(); + filedialog_callback(obj, file_selected_callback, cbdata, files); + } else { + auto file = co_await openFileDialog.PickSingleFileAsync(); + auto files = single_threaded_vector<winrt::Windows::Storage::StorageFile>(); + files.Append(file); + filedialog_callback(obj, file_selected_callback, cbdata, files.GetView()); + } +} + +static Windows::Foundation::IAsyncAction save_filedialog_async(UiObject *obj, char *name, ui_callback file_selected_callback, void *cbdata) { + IFileSaveDialog *saveFileDialog; + + HRESULT hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, IID_IFileSaveDialog, reinterpret_cast<void**>(&saveFileDialog)); + if (FAILED(hr)) + { + co_return; + } + + if (name) { + wchar_t *wname = str2wstr(name, NULL); + saveFileDialog->SetFileName(wname); + free(wname); + free(name); + } + + + hr = saveFileDialog->Show(NULL); + if (SUCCEEDED(hr)) { + IShellItem *item; + hr = saveFileDialog->GetResult(&item); + if (SUCCEEDED(hr)) { + PWSTR wpath; + hr = item->GetDisplayName(SIGDN_FILESYSPATH, &wpath); + + if (SUCCEEDED(hr)) { + char *path = wchar2utf8(wpath, lstrlen(wpath)); + CoTaskMemFree(wpath); + + UiFileList flist; + flist.nfiles = 1; + flist.files = new char*[1]; + flist.files[0] = path; + + UiEvent evt; + evt.obj = obj; + evt.document = obj->ctx->document; + evt.window = obj->window; + evt.eventdata = &flist; + evt.intval = 0; + file_selected_callback(&evt, cbdata); + + free(path); + delete[] flist.files; + } + item->Release(); + } + } + + // cleanup + saveFileDialog->Release(); +} + +static Windows::Foundation::IAsyncAction folderdialog_async(UiObject *obj, ui_callback file_selected_callback, void *cbdata) { + FolderPicker folderPicker = FolderPicker(); + auto initializeWithWindow { folderPicker.as<::IInitializeWithWindow>() + }; + + HWND hwnd{ nullptr }; + winrt::check_hresult(obj->wobj->window.as<IWindowNative>()->get_WindowHandle(&hwnd)); + + initializeWithWindow->Initialize(hwnd); + + folderPicker.FileTypeFilter().Append(L"*"); + + auto folder = co_await folderPicker.PickSingleFolderAsync(); + if (folder) { + winrt::hstring hpath = folder.Path(); + char *cpath = wchar2utf8(hpath.c_str(), hpath.size()); + + UiFileList flist; + flist.nfiles = 1; + flist.files = &cpath; + + UiEvent evt; + evt.obj = obj; + evt.document = obj->ctx->document; + evt.window = obj->window; + evt.eventdata = &flist; + evt.intval = 0; + file_selected_callback(&evt, cbdata); + } +} + +UIEXPORT void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { + if ((mode & UI_FILEDIALOG_SELECT_FOLDER) == UI_FILEDIALOG_SELECT_FOLDER) { + folderdialog_async(obj, file_selected_callback, cbdata); + } else { + open_filedialog_async(obj, mode, file_selected_callback, cbdata); + } +} + +UIEXPORT void ui_savefiledialog(UiObject *obj, const char *name, ui_callback file_selected_callback, void *cbdata) { + char *n = name ? _strdup(name) : NULL; + save_filedialog_async(obj, n, file_selected_callback, cbdata); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/window.h Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,44 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "toolkit.h" + +#include "../ui/window.h" + +#include <Windows.h> +#undef GetCurrentTime +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.UI.Xaml.Interop.h> +#include <winrt/Microsoft.UI.Xaml.Controls.h> +#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h> +#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h> +#include <winrt/Microsoft.UI.Xaml.Markup.h> + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/winui.vcxproj Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,311 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.props')" /> + <Import Project="..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props')" /> + <Import Project="..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" /> + <PropertyGroup Label="Globals"> + <CppWinRTOptimized>true</CppWinRTOptimized> + <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge> + <MinimalCoreWin>true</MinimalCoreWin> + <ProjectGuid>{59f97886-bf49-4b3f-9ef6-fa7a84f3ab56}</ProjectGuid> + <ProjectName>winui</ProjectName> + <RootNamespace>winui</RootNamespace> + <!-- + $(TargetName) should be same as $(RootNamespace) so that the produced binaries (.exe/.pri/etc.) + have a name that matches the .winmd + --> + <TargetName>$(RootNamespace)</TargetName> + <DefaultLanguage>de-DE</DefaultLanguage> + <MinimumVisualStudioVersion>16.0</MinimumVisualStudioVersion> + <AppContainerApplication>false</AppContainerApplication> + <AppxPackage>false</AppxPackage> + <ApplicationType>Windows Store</ApplicationType> + <ApplicationTypeRevision>10.0</ApplicationTypeRevision> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion> + <UseWinUI>true</UseWinUI> + <EnableMsixTooling>true</EnableMsixTooling> + <WindowsPackageType>None</WindowsPackageType> + <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + <DesktopCompatible>true</DesktopCompatible> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ItemDefinitionGroup> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> + <ClCompile> + <PreprocessorDefinitions>_DEBUG;DISABLE_XAML_GENERATED_MAIN__;UI_WINUI;UI_WINUI_PCH;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)..\..\ucx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard_C Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdc17</LanguageStandard_C> + <RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">MultiThreadedDebugDLL</RuntimeLibrary> + </ClCompile> + <Link> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">shell32.lib;gdi32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> + <ClCompile> + <PreprocessorDefinitions>DISABLE_XAML_GENERATED_MAIN__;UI_WINUI;UI_WINUI_PCH;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)..\..\ucx;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <SDLCheck Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</SDLCheck> + <LanguageStandard_C Condition="'$(Configuration)|$(Platform)'=='Release|x64'">stdc17</LanguageStandard_C> + </ClCompile> + <Link> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">shell32.lib;gdi32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup Condition="'$(WindowsPackageType)'!='None' and Exists('Package.appxmanifest')"> + <AppxManifest Include="Package.appxmanifest"> + <SubType>Designer</SubType> + </AppxManifest> + </ItemGroup> + <ItemGroup> + <Manifest Include="app.manifest" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\common\context.h" /> + <ClInclude Include="..\common\document.h" /> + <ClInclude Include="..\common\menu.h" /> + <ClInclude Include="..\common\object.h" /> + <ClInclude Include="..\common\properties.h" /> + <ClInclude Include="..\common\toolbar.h" /> + <ClInclude Include="..\common\types.h" /> + <ClInclude Include="..\common\ucx_properties.h" /> + <ClInclude Include="..\ui\button.h" /> + <ClInclude Include="..\ui\container.h" /> + <ClInclude Include="..\ui\display.h" /> + <ClInclude Include="..\ui\dnd.h" /> + <ClInclude Include="..\ui\entry.h" /> + <ClInclude Include="..\ui\graphics.h" /> + <ClInclude Include="..\ui\icons.h" /> + <ClInclude Include="..\ui\image.h" /> + <ClInclude Include="..\ui\menu.h" /> + <ClInclude Include="..\ui\properties.h" /> + <ClInclude Include="..\ui\range.h" /> + <ClInclude Include="..\ui\stock.h" /> + <ClInclude Include="..\ui\text.h" /> + <ClInclude Include="..\ui\toolbar.h" /> + <ClInclude Include="..\ui\toolkit.h" /> + <ClInclude Include="..\ui\tree.h" /> + <ClInclude Include="..\ui\ui.h" /> + <ClInclude Include="..\ui\window.h" /> + <ClInclude Include="appmenu.h" /> + <ClInclude Include="button.h" /> + <ClInclude Include="commandbar.h" /> + <ClInclude Include="condvar.h" /> + <ClInclude Include="container.h" /> + <ClInclude Include="dnd.h" /> + <ClInclude Include="icons.h" /> + <ClInclude Include="image.h" /> + <ClInclude Include="label.h" /> + <ClInclude Include="list.h" /> + <ClInclude Include="pch.h" /> + <ClInclude Include="App.xaml.h"> + <DependentUpon>App.xaml</DependentUpon> + </ClInclude> + <ClInclude Include="MainWindow.xaml.h"> + <DependentUpon>MainWindow.xaml</DependentUpon> + </ClInclude> + <ClInclude Include="stock.h" /> + <ClInclude Include="table.h" /> + <ClInclude Include="text.h" /> + <ClInclude Include="toolkit.h" /> + <ClInclude Include="util.h" /> + <ClInclude Include="window.h" /> + </ItemGroup> + <ItemGroup> + <ApplicationDefinition Include="App.xaml" /> + <Page Include="MainWindow.xaml" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\common\context.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\document.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\menu.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\object.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\properties.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\toolbar.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\types.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="..\common\ucx_properties.c"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> + </ClCompile> + <ClCompile Include="appmenu.cpp" /> + <ClCompile Include="button.cpp" /> + <ClCompile Include="commandbar.cpp" /> + <ClCompile Include="condvar.cpp" /> + <ClCompile Include="container.cpp" /> + <ClCompile Include="dnd.cpp" /> + <ClCompile Include="icons.cpp" /> + <ClCompile Include="image.cpp" /> + <ClCompile Include="label.cpp" /> + <ClCompile Include="list.cpp" /> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="App.xaml.cpp"> + <DependentUpon>App.xaml</DependentUpon> + </ClCompile> + <ClCompile Include="MainWindow.xaml.cpp"> + <DependentUpon>MainWindow.xaml</DependentUpon> + </ClCompile> + <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="stock.cpp" /> + <ClCompile Include="table.cpp" /> + <ClCompile Include="text.cpp" /> + <ClCompile Include="toolkit.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="window.cpp" /> + </ItemGroup> + <ItemGroup> + <Midl Include="App.idl"> + <SubType>Code</SubType> + <DependentUpon>App.xaml</DependentUpon> + </Midl> + <Midl Include="MainWindow.idl"> + <SubType>Code</SubType> + <DependentUpon>MainWindow.xaml</DependentUpon> + </Midl> + </ItemGroup> + <ItemGroup> + <Text Include="readme.txt"> + <DeploymentContent>false</DeploymentContent> + </Text> + </ItemGroup> + <ItemGroup> + <Image Include="Assets\LockScreenLogo.scale-200.png" /> + <Image Include="Assets\SplashScreen.scale-200.png" /> + <Image Include="Assets\Square150x150Logo.scale-200.png" /> + <Image Include="Assets\Square44x44Logo.scale-200.png" /> + <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" /> + <Image Include="Assets\StoreLogo.png" /> + <Image Include="Assets\Wide310x150Logo.scale-200.png" /> + </ItemGroup> + <!-- + Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging + Tools extension to be activated for this project even if the Windows App SDK Nuget + package has not yet been restored. + --> + <ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> + <ProjectCapability Include="Msix" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\make\vs\ucx\ucx.vcxproj"> + <Project>{27da0164-3475-43e2-a1a4-a5d07d305749}</Project> + </ProjectReference> + </ItemGroup> + <!-- + Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution + Explorer "Package and Publish" context menu entry to be enabled for this project even if + the Windows App SDK Nuget package has not yet been restored. + --> + <PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> + <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> + <IntDir>..\..\build\vs\winui\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <OutDir>$(SolutionDir)..\..\build\vs\$(Platform)\$(Configuration)\</OutDir> + <IntDir>..\..\build\vs\winui\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" /> + <Import Project="..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets')" /> + <Import Project="..\..\make\vs\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\make\vs\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> + <Import Project="..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.props'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.WindowsAppSDK.1.5.241001000\build\native\Microsoft.WindowsAppSDK.targets'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.props'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.Windows.CppWinRT.2.0.240405.15\build\native\Microsoft.Windows.CppWinRT.targets'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.Windows.ImplementationLibrary.1.0.240803.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.props'))" /> + <Error Condition="!Exists('..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\make\vs\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.1742\build\Microsoft.Windows.SDK.BuildTools.targets'))" /> + </Target> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/winui.vcxproj.filters Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ApplicationDefinition Include="App.xaml" /> + </ItemGroup> + <ItemGroup> + <Page Include="MainWindow.xaml" /> + </ItemGroup> + <ItemGroup> + <Midl Include="App.idl" /> + <Midl Include="MainWindow.idl" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="pch.cpp" /> + <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="appmenu.cpp" /> + <ClCompile Include="button.cpp" /> + <ClCompile Include="commandbar.cpp" /> + <ClCompile Include="container.cpp" /> + <ClCompile Include="list.cpp" /> + <ClCompile Include="table.cpp" /> + <ClCompile Include="text.cpp" /> + <ClCompile Include="toolkit.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="window.cpp" /> + <ClCompile Include="stock.cpp" /> + <ClCompile Include="icons.cpp" /> + <ClCompile Include="label.cpp" /> + <ClCompile Include="dnd.cpp" /> + <ClCompile Include="condvar.cpp" /> + <ClCompile Include="image.cpp" /> + <ClCompile Include="..\common\context.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\document.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\menu.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\object.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\properties.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\toolbar.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\types.c"> + <Filter>common</Filter> + </ClCompile> + <ClCompile Include="..\common\ucx_properties.c"> + <Filter>common</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h" /> + <ClInclude Include="appmenu.h" /> + <ClInclude Include="button.h" /> + <ClInclude Include="commandbar.h" /> + <ClInclude Include="container.h" /> + <ClInclude Include="list.h" /> + <ClInclude Include="table.h" /> + <ClInclude Include="text.h" /> + <ClInclude Include="toolkit.h" /> + <ClInclude Include="util.h" /> + <ClInclude Include="window.h" /> + <ClInclude Include="..\ui\button.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\container.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\display.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\dnd.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\entry.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\graphics.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\image.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\menu.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\properties.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\range.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\stock.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\text.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\toolbar.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\toolkit.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\tree.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\ui.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\ui\window.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="stock.h" /> + <ClInclude Include="icons.h" /> + <ClInclude Include="label.h" /> + <ClInclude Include="dnd.h" /> + <ClInclude Include="condvar.h" /> + <ClInclude Include="image.h" /> + <ClInclude Include="..\ui\icons.h"> + <Filter>public</Filter> + </ClInclude> + <ClInclude Include="..\common\context.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\document.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\menu.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\object.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\properties.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\toolbar.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\types.h"> + <Filter>common</Filter> + </ClInclude> + <ClInclude Include="..\common\ucx_properties.h"> + <Filter>common</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Image Include="Assets\Wide310x150Logo.scale-200.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\StoreLogo.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\Square150x150Logo.scale-200.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\Square44x44Logo.scale-200.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\SplashScreen.scale-200.png"> + <Filter>Assets</Filter> + </Image> + <Image Include="Assets\LockScreenLogo.scale-200.png"> + <Filter>Assets</Filter> + </Image> + </ItemGroup> + <ItemGroup> + <Filter Include="Assets"> + <UniqueIdentifier>{59f97886-bf49-4b3f-9ef6-fa7a84f3ab56}</UniqueIdentifier> + </Filter> + <Filter Include="public"> + <UniqueIdentifier>{2b58fe46-d27b-4335-b63c-13ec2204fa24}</UniqueIdentifier> + </Filter> + <Filter Include="common"> + <UniqueIdentifier>{35c88d5c-b36f-41b3-86c0-784227845ae9}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <Text Include="readme.txt" /> + </ItemGroup> + <ItemGroup> + <Manifest Include="app.manifest" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" /> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/winui/winui.vcxproj.user Sat Jan 04 16:38:48 2025 +0100 @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup /> +</Project> \ No newline at end of file