]> uap-core.de Git - note.git/commitdiff
fix build with newest toolkit version main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 13 Dec 2025 14:43:55 +0000 (15:43 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 13 Dec 2025 14:43:55 +0000 (15:43 +0100)
109 files changed:
application/attachment.c
application/gtk-text.c
application/menu.c
application/nbconfig.c
application/note.c
application/notebook.c
application/window.c
ucx/allocator.c
ucx/buffer.c
ucx/cx/allocator.h
ucx/cx/buffer.h
ucx/cx/common.h
ucx/cx/json.h
ucx/cx/properties.h
ucx/cx/tree.h
ucx/json.c
ucx/kv_list.c
ucx/properties.c
ucx/tree.c
ui/cocoa/ListDataSource.h
ui/cocoa/list.h
ui/cocoa/list.m
ui/cocoa/toolkit.m
ui/cocoa/window.m
ui/common/app.c [new file with mode: 0644]
ui/common/app.h [new file with mode: 0644]
ui/common/args.c
ui/common/args.h
ui/common/context.c
ui/common/context.h
ui/common/menu.c
ui/common/menu.h
ui/common/message.c
ui/common/message.h
ui/common/objs.mk
ui/common/properties.c
ui/common/threadpool.c
ui/common/toolbar.c
ui/common/toolbar.h
ui/common/types.c
ui/common/utils.c
ui/common/utils.h
ui/common/wrapper.h
ui/gtk/button.c
ui/gtk/container.c
ui/gtk/container.h
ui/gtk/entry.c
ui/gtk/graphics.c
ui/gtk/headerbar.c
ui/gtk/headerbar.h
ui/gtk/image.c
ui/gtk/list.c
ui/gtk/list.h
ui/gtk/menu.c
ui/gtk/text.c
ui/gtk/toolbar.c
ui/gtk/toolkit.c
ui/gtk/toolkit.h
ui/gtk/webview.c
ui/gtk/widget.c
ui/gtk/widget.h
ui/gtk/window.c
ui/motif/button.c
ui/motif/entry.c
ui/motif/list.c
ui/motif/list.h
ui/motif/menu.c
ui/motif/objs.mk
ui/motif/text.c
ui/motif/toolbar.c
ui/motif/toolkit.c
ui/motif/window.c
ui/qt/container.cpp
ui/qt/container.h
ui/qt/list.cpp
ui/qt/list.h
ui/qt/menu.cpp
ui/qt/model.cpp
ui/qt/model.h
ui/qt/qt5.pro
ui/qt/toolbar.cpp
ui/qt/toolkit.cpp
ui/qt/window.cpp
ui/ui/button.h
ui/ui/container.h
ui/ui/entry.h
ui/ui/image.h
ui/ui/list.h [new file with mode: 0644]
ui/ui/menu.h
ui/ui/properties.h
ui/ui/text.h
ui/ui/toolbar.h
ui/ui/toolkit.h
ui/ui/ui.h
ui/ui/webview.h
ui/ui/window.h
ui/win32/list.c
ui/win32/list.h
ui/win32/menu.c [new file with mode: 0644]
ui/win32/menu.h [new file with mode: 0644]
ui/win32/objs.mk
ui/win32/toolkit.c
ui/win32/window.c
ui/winui/list.h
ui/winui/table.h
ui/winui/toolkit.cpp
ui/winui/window.cpp
ui/winui/winui.vcxproj
ui/winui/winui.vcxproj.filters

index c94dfe3b8168e34eb0cc025620a546553d78443b..00ad2702164bab2526ef41d8e8a333a10940c705 100644 (file)
@@ -168,7 +168,7 @@ void action_attachment_clicked(UiEvent *event, void *userdata) {
  */
 UiObject* attachment_window_create(Note *note, Attachment *selected_attachment) {
     cxmutstr title = cx_asprintf("%s - attachments", note_get_title(note->resource));
-    UiObject *obj = ui_simple_window(title.ptr, NULL);
+    UiObject *obj = ui_simple_window(title.ptr);
     free(title.ptr);
     
     AttachmentWindow *wdata = attachment_window_create_data(obj, note);
index dbc0ce759400b3205f630b7a715aa2d9e549ffbf..bd25a2de59f00fd99306166979bd038f4c622441 100644 (file)
@@ -395,7 +395,7 @@ static void editor_attach_image(NoteEditor *editor, GdkPixbuf *pixbuf, char *att
     ui_list_update(model->attachments);
     
     // TODO: can we move this to note_add_attachment or somewhere else?
-    ui_set_group(wdata->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
+    ui_set_state(wdata->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
 }
 
 static gboolean editor_dnd_add_file(NoteEditor *editor, GFile *file) {
index f23b4f508c457dd766f6da54804ab844733dfc9f..96eb8478d75ac2f5600e13a02407c95848cf2dda 100644 (file)
@@ -44,7 +44,7 @@ void menu_cleanup() {
 void toolbar_init() {
     ui_toolbar_item("GoBack", .icon = UI_ICON_GO_BACK, .onclick = action_go_back);
     ui_toolbar_item("GoForward", .icon = UI_ICON_GO_FORWARD, .onclick = action_go_forward);
-    ui_toolbar_item("AddNote", .icon = UI_ICON_ADD, .onclick = action_note_new, .groups = UI_GROUPS(APP_STATE_NOTEBOOK_SELECTED));
+    ui_toolbar_item("AddNote", .icon = UI_ICON_ADD, .onclick = action_note_new, .states = UI_GROUPS(APP_STATE_NOTEBOOK_SELECTED));
     
     ui_toolbar_item("GoBack2", .icon = UI_ICON_GO_BACK, .onclick = action_go_back, .visibility_states = UI_GROUPS(APP_STATE_HIDE_NOTELIST));
     ui_toolbar_item("GoForward2", .icon = UI_ICON_GO_FORWARD, .onclick = action_go_forward, .visibility_states = UI_GROUPS(APP_STATE_HIDE_NOTELIST));
index 5486ac749caba9678e62a6fdc515ae8389a85cb2..c61a108b61142c43e341c15b39e38e93144eca15 100644 (file)
@@ -398,7 +398,7 @@ void notebook_config_dialog(void) {
     NoteStore *store = note_store_get();
     // TODO: check store->root and show different dialog, when root is missing
     
-    UiObject *obj = ui_simple_window("Notebooks", NULL);
+    UiObject *obj = ui_simple_window("Notebooks");
     const CxAllocator *a = ui_allocator(obj->ctx);
     
     NotebookConfigDialog *wdata = ui_malloc(obj->ctx, sizeof(NotebookConfigDialog));
@@ -475,11 +475,11 @@ void notebook_config_dialog(void) {
                         ui_newline(obj);
 
                         ui_rlabel(obj, .label = "Default Repository");
-                        ui_combobox(obj, .list = wdata->tab1_repositories);
+                        ui_dropdown(obj, .list = wdata->tab1_repositories);
                         ui_newline(obj);
                         
                         ui_rlabel(obj, .label = "Default Notebook Type");
-                        ui_combobox(obj, .list = wdata->tab1_types);
+                        ui_dropdown(obj, .list = wdata->tab1_types);
                         ui_newline(obj);
                     }
                 }
@@ -498,7 +498,7 @@ void notebook_config_dialog(void) {
 
                     ui_grid(obj, .columnspacing = 10, .rowspacing = 10, .fill = TRUE, .def_vfill = TRUE) {
                         ui_rlabel(obj, .label = "Group");
-                        ui_combobox(obj, .list = wdata->tab2_groups, .getvalue = reslist_getvalue);
+                        ui_dropdown(obj, .list = wdata->tab2_groups, .getvalue = reslist_getvalue);
                         ui_newline(obj);
                         
                         ui_rlabel(obj, .label = "Name");
@@ -506,11 +506,11 @@ void notebook_config_dialog(void) {
                         ui_newline(obj);
 
                         ui_rlabel(obj, .label = "Repository");
-                        ui_combobox(obj, .list = wdata->tab1_repositories);
+                        ui_dropdown(obj, .list = wdata->tab1_repositories);
                         ui_newline(obj);
                         
                         ui_rlabel(obj, .label = "Type");
-                        ui_combobox(obj, .list = wdata->tab2_types);
+                        ui_dropdown(obj, .list = wdata->tab2_types);
                         ui_newline(obj);
                     }
                 }
@@ -543,7 +543,7 @@ void notebook_config_dialog(void) {
                         ui_newline(obj);
                         
                         ui_rlabel(obj, .label = "Encryption Key");
-                        ui_combobox(obj, .list = wdata->tab3_repo_encryption_key);
+                        ui_dropdown(obj, .list = wdata->tab3_repo_encryption_key);
                         
                     }
                 }
index de5214fda1d1d9516ba3ce8ab76fe2ab0f048ac1..372bc4d02f2174287debcc561d3391c89d21903e 100644 (file)
@@ -122,7 +122,7 @@ static void note_loading_completed(UiObject *obj, LoadNoteContent *op) {
         ui_list_update(note->model->attachments);
         
         if(ui_list_count(note->model->attachments) > 0) {
-            ui_set_group(obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
+            ui_set_state(obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
         }
         
         editor_load_markdown(note, wdata->textview, note->resource->content);
index 2b08f422666e2657e2079320d6d1f06c2d331a17..79fdca6d29f851ea92e079425d51e99e795717c8 100644 (file)
@@ -146,16 +146,16 @@ void notebookmodel_attach_note(NotebookModel *model, Note *note) {
     ui_attach_document(model->ctx, note->model);
     // TODO: this is only a workaround and should be removed when
     //       sub-document groups are supported
-    ui_set_group(model->window->obj->ctx, APP_STATE_NOTE_SELECTED);
+    ui_set_state(model->window->obj->ctx, APP_STATE_NOTE_SELECTED);
     model->current_note = note;
     
     ui_list_update(note->model->attachments);
     
     // TODO: replace when getting groups from sub-ctx works
     if(ui_list_count(note->model->attachments) == 0) {
-        ui_unset_group(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
+        ui_unset_state(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
     } else {
-        ui_set_group(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
+        ui_set_state(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
     }
     
     if(!note->resource->content_loaded) {
@@ -180,7 +180,7 @@ void notebookmodel_detach_current_note(NotebookModel *model) {
         }
         model->current_note = NULL;
     }
-    ui_unset_group(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
+    ui_unset_state(model->window->obj->ctx, APP_STATE_NOTE_HAS_ATTACHMENTS);
 }
 
 /*
index cba82f8a83c2ac01963dea988226d03742030561..84d272e1121098e853d155d9d9abd6c91e4331c3 100644 (file)
@@ -85,11 +85,11 @@ void window_create() {
                 ui_vbox(obj, .fill = TRUE) {
                     ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .def_vfill = TRUE) {
                         //ui_label(obj, .label = "Title", .vfill = TRUE);
-                        ui_textfield(obj, .varname = "note_title", .onchange = action_note_title_changed, .hexpand = TRUE, .hfill = TRUE, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED));
+                        ui_textfield(obj, .varname = "note_title", .onchange = action_note_title_changed, .hexpand = TRUE, .hfill = TRUE, .states = UI_GROUPS(APP_STATE_NOTE_SELECTED));
                         ui_newline(obj);
                     }
                     ui_hbox(obj, .style_class = "note_toolbar", .margin = 10, .spacing = 4) {
-                        ui_combobox(obj, .varname = "note_textnote_para", .onactivate = action_textnote_paragraph);
+                        ui_dropdown(obj, .varname = "note_textnote_para", .onactivate = action_textnote_paragraph);
                         ui_togglebutton(obj, .icon = "format-text-bold", .varname = "note_textnote_strong", .onchange = action_textnote_style_strong);
                         ui_togglebutton(obj, .icon = "format-text-italic", .varname = "note_textnote_emphasis", .onchange = action_textnote_style_emphasis);
                         ui_togglebutton(obj, .icon = "format-text-underline", .varname = "note_textnote_underline", .onchange = action_textnote_style_underline);
@@ -112,8 +112,8 @@ void window_create() {
                     ui_customwidget(obj, editor_gtk4_workaround, wdata, .hfill = TRUE);
 #endif
                     ui_set_visible(wdata->attachments, FALSE);
-                    ui_widget_set_groups(obj->ctx, wdata->attachments, (ui_enablefunc)ui_set_visible, APP_STATE_NOTE_HAS_ATTACHMENTS, -1);
-                    wdata->textview = ui_textarea(obj, .varname = "note_text", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON);
+                    ui_widget_set_states(obj->ctx, wdata->attachments, (ui_enablefunc)ui_set_visible, APP_STATE_NOTE_HAS_ATTACHMENTS, -1);
+                    wdata->textview = ui_textarea(obj, .varname = "note_text", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .states = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON);
                     editor_init_textview(obj, ui_textarea_gettextwidget(wdata->textview));
                 } 
             }
@@ -143,9 +143,9 @@ void window_notelist_setvisible(MainWindow *window, UiBool visible) {
     ui_splitview_window_set_visible(window->obj, 0, visible);
     window->notelist_isvisible = visible;
     if(visible) {
-        ui_unset_group(window->obj->ctx, APP_STATE_HIDE_NOTELIST);
+        ui_unset_state(window->obj->ctx, APP_STATE_HIDE_NOTELIST);
     } else {
-        ui_set_group(window->obj->ctx, APP_STATE_HIDE_NOTELIST);
+        ui_set_state(window->obj->ctx, APP_STATE_HIDE_NOTELIST);
     }
 }
 
@@ -350,7 +350,7 @@ void action_notebook_add(UiEvent *event, void *userdata) {
     // Dialog UI
     ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .def_vfill = TRUE, .fill = TRUE) {
         ui_rlabel(obj, .label = "Group");
-        ui_combobox(obj, .list = wdata->groups, .getvalue = nnd_group_value, .hfill = TRUE, .hexpand = TRUE);
+        ui_dropdown(obj, .list = wdata->groups, .getvalue = nnd_group_value, .hfill = TRUE, .hexpand = TRUE);
         ui_newline(obj);
         
         ui_newline(obj);
@@ -385,7 +385,7 @@ void action_notebook_selected(UiEvent *event, void *userdata) {
     Resource *collection = data->row_data;
     
     printf("notebook selected: %s\n", collection->nodename);
-    ui_set_group(event->obj->ctx, APP_STATE_NOTEBOOK_SELECTED);
+    ui_set_state(event->obj->ctx, APP_STATE_NOTEBOOK_SELECTED);
     
     if(window->current_notebook && window->current_notebook->collection == collection) {
         return; // notebook already selected
index 323b14b59e04df862440b946562d186333f61efe..bb01129fcc08353adf63dcc3813d0032135c5430 100644 (file)
 #include <errno.h>
 #include <string.h>
 
+#ifdef _WIN32
+#include <Windows.h>
+#include <sysinfoapi.h>
+unsigned long cx_system_page_size(void) {
+    static unsigned long ps = 0;
+    if (ps == 0) {
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ps = (unsigned long) sysinfo.dwPageSize;
+    }
+    return ps;
+}
+#else
+#include <unistd.h>
+unsigned long cx_system_page_size(void) {
+    static unsigned long ps = 0;
+    if (ps == 0) {
+        long sc = sysconf(_SC_PAGESIZE);
+        if (sc < 0) {
+            // fallback for systems which do not report a value here
+            ps = 4096; // LCOV_EXCL_LINE
+        } else {
+            ps = (unsigned long) sc;
+        }
+    }
+    return ps;
+}
+#endif
+
 static void *cx_malloc_stdlib(
         cx_attr_unused void *d,
         size_t n
@@ -79,6 +108,11 @@ int cx_reallocate_(
         void **mem,
         size_t n
 ) {
+    if (n == 0) {
+        free(*mem);
+        *mem = NULL;
+        return 0;
+    }
     void *nmem = realloc(*mem, n);
     if (nmem == NULL) {
         return 1; // LCOV_EXCL_LINE
@@ -93,6 +127,11 @@ int cx_reallocatearray_(
         size_t nmemb,
         size_t size
 ) {
+    if (nmemb == 0 || size == 0) {
+        free(*mem);
+        *mem = NULL;
+        return 0;
+    }
     size_t n;
     if (cx_szmul(nmemb, size, &n)) {
         errno = EOVERFLOW;
@@ -156,6 +195,11 @@ int cxReallocate_(
         void **mem,
         size_t n
 ) {
+    if (n == 0) {
+        cxFree(allocator, *mem);
+        *mem = NULL;
+        return 0;
+    }
     void *nmem = allocator->cl->realloc(allocator->data, *mem, n);
     if (nmem == NULL) {
         return 1; // LCOV_EXCL_LINE
@@ -171,6 +215,11 @@ int cxReallocateArray_(
         size_t nmemb,
         size_t size
 ) {
+    if (nmemb == 0 || size == 0) {
+        cxFree(allocator, *mem);
+        *mem = NULL;
+        return 0;
+    }
     void *nmem = cxReallocArray(allocator, *mem, nmemb, size);
     if (nmem == NULL) {
         return 1; // LCOV_EXCL_LINE
@@ -194,3 +243,7 @@ void cxFree(
 ) {
     allocator->cl->free(allocator->data, mem);
 }
+
+void cxFreeDefault(void *mem) {
+    cxDefaultAllocator->cl->free(cxDefaultAllocator->data, mem);
+}
index a652abc5bc388b385f2040fee64cd1634b5e8d89..a727ee4ae06709ceeb5d26469dfb713fd7e01d60 100644 (file)
 #include <string.h>
 #include <errno.h>
 
-#ifdef _WIN32
-#include <Windows.h>
-#include <sysinfoapi.h>
-static unsigned long system_page_size(void) {
-    static unsigned long ps = 0;
-    if (ps == 0) {
-        SYSTEM_INFO sysinfo;
-        GetSystemInfo(&sysinfo);
-        ps = sysinfo.dwPageSize;
-    }
-    return ps;
-}
-#else
-#include <unistd.h>
-static unsigned long system_page_size(void) {
-    static unsigned long ps = 0;
-    if (ps == 0) {
-        long sc = sysconf(_SC_PAGESIZE);
-        if (sc < 0) {
-            // fallback for systems which do not report a value here
-            ps = 4096; // LCOV_EXCL_LINE
-        } else {
-            ps = (unsigned long) sc;
-        }
-    }
-    return ps;
-}
-#endif
-
 static int buffer_copy_on_write(CxBuffer* buffer) {
     if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
     void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
@@ -95,29 +66,18 @@ int cxBufferInit(
         buffer->bytes = space;
     }
     buffer->capacity = capacity;
+    buffer->max_capacity = SIZE_MAX;
     buffer->size = 0;
     buffer->pos = 0;
 
-    buffer->flush = NULL;
-
-    return 0;
-}
-
-int cxBufferEnableFlushing(
-    CxBuffer *buffer,
-    CxBufferFlushConfig config
-) {
-    buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
-    if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
-    memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
     return 0;
 }
 
 void cxBufferDestroy(CxBuffer *buffer) {
-    if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
+    if ((buffer->flags & (CX_BUFFER_FREE_CONTENTS | CX_BUFFER_DO_NOT_FREE))
+            == CX_BUFFER_FREE_CONTENTS) {
         cxFree(buffer->allocator, buffer->bytes);
     }
-    cxFreeDefault(buffer->flush);
     memset(buffer, 0, sizeof(CxBuffer));
 }
 
@@ -239,9 +199,12 @@ bool cxBufferEof(const CxBuffer *buffer) {
 }
 
 int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
-    if (newcap <= buffer->capacity) {
+    if (newcap == buffer->capacity) {
         return 0;
     }
+    if (newcap > buffer->max_capacity) {
+        return -1;
+    }
     const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
     if (buffer->flags & force_copy_flags) {
         void *newspace = cxMalloc(buffer->allocator, newcap);
@@ -254,42 +217,57 @@ int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
         return 0;
     } else if (cxReallocate(buffer->allocator,
                      (void **) &buffer->bytes, newcap) == 0) {
+        buffer->flags |= CX_BUFFER_FREE_CONTENTS;
         buffer->capacity = newcap;
+        if (buffer->size > newcap) {
+            buffer->size = newcap;
+        }
         return 0;
     } else {
         return -1; // LCOV_EXCL_LINE
     }
 }
 
-static size_t cx_buffer_calculate_minimum_capacity(size_t mincap) {
-    unsigned long pagesize = system_page_size();
-    // if page size is larger than 64 KB - for some reason - truncate to 64 KB
-    if (pagesize > 65536) pagesize = 65536;
-    if (mincap < pagesize) {
-        // when smaller as one page, map to the next power of two
-        mincap--;
-        mincap |= mincap >> 1;
-        mincap |= mincap >> 2;
-        mincap |= mincap >> 4;
-        // last operation only needed for pages larger 4096 bytes
-        // but if/else would be more expensive than just doing this
-        mincap |= mincap >> 8;
-        mincap++;
-    } else {
-        // otherwise, map to a multiple of the page size
-        mincap -= mincap % pagesize;
-        mincap += pagesize;
-        // note: if newcap is already page aligned,
-        // this gives a full additional page (which is good)
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity) {
+    if (capacity < buffer->capacity) {
+        return -1;
     }
-    return mincap;
+    buffer->max_capacity = capacity;
+    return 0;
 }
 
 int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
     if (newcap <= buffer->capacity) {
         return 0;
     }
-    newcap = cx_buffer_calculate_minimum_capacity(newcap);
+    if (newcap > buffer->max_capacity) {
+        return -1;
+    }
+    if (newcap < buffer->max_capacity) {
+        unsigned long pagesize = cx_system_page_size();
+        // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+        if (pagesize > 65536) pagesize = 65536;
+        if (newcap < pagesize) {
+            // when smaller as one page, map to the next power of two
+            newcap--;
+            newcap |= newcap >> 1;
+            newcap |= newcap >> 2;
+            newcap |= newcap >> 4;
+            // last operation only needed for pages larger 4096 bytes
+            // but if/else would be more expensive than just doing this
+            newcap |= newcap >> 8;
+            newcap++;
+        } else {
+            // otherwise, map to a multiple of the page size
+            newcap -= newcap % pagesize;
+            newcap += pagesize;
+            // note: if newcap is already page aligned,
+            // this gives a full additional page (which is good)
+        }
+        if (newcap > buffer->max_capacity) {
+            newcap = buffer->max_capacity;
+        }
+    }
     return cxBufferReserve(buffer, newcap);
 }
 
@@ -315,60 +293,15 @@ void cxBufferShrink(
     }
 }
 
-static size_t cx_buffer_flush_helper(
-        const CxBuffer *buffer,
-        const unsigned char *src,
-        size_t size,
-        size_t nitems
-) {
-    // flush data from an arbitrary source
-    // does not need to be the buffer's contents
-    size_t max_items = buffer->flush->blksize / size;
-    size_t fblocks = 0;
-    size_t flushed_total = 0;
-    while (nitems > 0 && fblocks < buffer->flush->blkmax) {
-        fblocks++;
-        size_t items = nitems > max_items ? max_items : nitems;
-        size_t flushed = buffer->flush->wfunc(
-            src, size, items, buffer->flush->target);
-        if (flushed > 0) {
-            flushed_total += flushed;
-            src += flushed * size;
-            nitems -= flushed;
-        } else {
-            // if no bytes can be flushed out anymore, we give up
-            break;
-        }
-    }
-    return flushed_total;
-}
-
-static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
-    // flush the current contents of the buffer
-    unsigned char *space = buffer->bytes;
-    size_t remaining = buffer->pos / size;
-    size_t flushed_total = cx_buffer_flush_helper(
-        buffer, space, size, remaining);
-
-    // shift the buffer left after flushing
-    // IMPORTANT: up to this point, copy on write must have been
-    // performed already, because we can't do error handling here
-    cxBufferShiftLeft(buffer, flushed_total*size);
-
-    return flushed_total;
-}
-
-size_t cxBufferFlush(CxBuffer *buffer) {
-    if (buffer_copy_on_write(buffer)) return 0;
-    return cx_buffer_flush_impl(buffer, 1);
-}
-
 size_t cxBufferWrite(
         const void *ptr,
         size_t size,
         size_t nitems,
         CxBuffer *buffer
 ) {
+    // trivial case
+    if (size == 0 || nitems == 0) return 0;
+
     // optimize for easy case
     if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
         if (buffer_copy_on_write(buffer)) return 0;
@@ -380,107 +313,52 @@ size_t cxBufferWrite(
         return nitems;
     }
 
-    size_t len, total_flushed = 0;
-cx_buffer_write_retry:
+    size_t len;
     if (cx_szmul(size, nitems, &len)) {
         errno = EOVERFLOW;
-        return total_flushed;
+        return 0;
     }
     if (buffer->pos > SIZE_MAX - len) {
         errno = EOVERFLOW;
-        return total_flushed;
+        return 0;
     }
+    const size_t required = buffer->pos + len;
 
-    size_t required = buffer->pos + len;
-    bool perform_flush = false;
+    // check if we need to auto-extend
     if (required > buffer->capacity) {
         if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
-            if (buffer->flush != NULL) {
-                size_t newcap = cx_buffer_calculate_minimum_capacity(required);
-                if (newcap > buffer->flush->threshold) {
-                    newcap = buffer->flush->threshold;
-                }
-                if (cxBufferReserve(buffer, newcap)) {
-                    return total_flushed; // LCOV_EXCL_LINE
-                }
-                if (required > newcap) {
-                    perform_flush = true;
-                }
-            } else {
-                if (cxBufferMinimumCapacity(buffer, required)) {
-                    return total_flushed; // LCOV_EXCL_LINE
-                }
-            }
-        } else {
-            if (buffer->flush != NULL) {
-                perform_flush = true;
-            } else {
-                // truncate data, if we can neither extend nor flush
-                len = buffer->capacity - buffer->pos;
-                if (size > 1) {
-                    len -= len % size;
-                }
-                nitems = len / size;
+            size_t newcap = required < buffer->max_capacity
+                    ? required : buffer->max_capacity;
+            if (cxBufferMinimumCapacity(buffer, newcap)) {
+                return 0; // LCOV_EXCL_LINE
             }
         }
     }
 
+    // check again and truncate data if capacity is still not enough
+    if (required > buffer->capacity) {
+        len = buffer->capacity - buffer->pos;
+        if (size > 1) {
+            len -= len % size;
+        }
+        nitems = len / size;
+    }
+
     // check here and not above because of possible truncation
     if (len == 0) {
-        return total_flushed;
+        return 0;
     }
 
     // check if we need to copy
     if (buffer_copy_on_write(buffer)) return 0;
 
     // perform the operation
-    if (perform_flush) {
-        size_t items_flushed;
-        if (buffer->pos == 0) {
-            // if we don't have data in the buffer, but are instructed
-            // to flush, it means that we are supposed to relay the data
-            items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
-            if (items_flushed == 0) {
-                // we needed to relay data, but could not flush anything
-                // i.e. we have to give up to avoid endless trying
-                return 0;
-            }
-            nitems -= items_flushed;
-            total_flushed += items_flushed;
-            if (nitems > 0) {
-                ptr = ((unsigned char*)ptr) + items_flushed * size;
-                goto cx_buffer_write_retry;
-            }
-            return total_flushed;
-        } else {
-            items_flushed = cx_buffer_flush_impl(buffer, size);
-            if (items_flushed == 0) {
-                // flush target is full, let's try to truncate
-                size_t remaining_space;
-                if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
-                    remaining_space = buffer->flush->threshold > buffer->pos
-                                          ? buffer->flush->threshold - buffer->pos
-                                          : 0;
-                } else {
-                    remaining_space = buffer->capacity > buffer->pos
-                                          ? buffer->capacity - buffer->pos
-                                          : 0;
-                }
-                nitems = remaining_space / size;
-                if (nitems == 0) {
-                    return total_flushed;
-                }
-            }
-            goto cx_buffer_write_retry;
-        }
-    } else {
-        memcpy(buffer->bytes + buffer->pos, ptr, len);
-        buffer->pos += len;
-        if (buffer->pos > buffer->size) {
-            buffer->size = buffer->pos;
-        }
-        return total_flushed + nitems;
+    memcpy(buffer->bytes + buffer->pos, ptr, len);
+    buffer->pos += len;
+    if (buffer->pos > buffer->size) {
+        buffer->size = buffer->pos;
     }
+    return nitems;
 }
 
 size_t cxBufferAppend(
@@ -489,20 +367,13 @@ size_t cxBufferAppend(
         size_t nitems,
         CxBuffer *buffer
 ) {
-    size_t pos = buffer->pos;
-    size_t append_pos = buffer->size;
-    buffer->pos = append_pos;    
-    size_t written = cxBufferWrite(ptr, size, nitems, buffer);
-    // the buffer might have been flushed
-    // we must compute a possible delta for the position
-    // expected: pos = append_pos + written
-    // -> if this is not the case, there is a delta
-    size_t delta = append_pos + written*size - buffer->pos;
-    if (delta > pos) {
-        buffer->pos = 0;
-    } else {
-        buffer->pos = pos - delta;
-    }
+    // trivial case
+    if (size == 0 || nitems == 0) return 0;
+
+    const size_t pos = buffer->pos;
+    buffer->pos = buffer->size;
+    const size_t written = cxBufferWrite(ptr, size, nitems, buffer);
+    buffer->pos = pos;
     return written;
 }
 
@@ -520,19 +391,35 @@ int cxBufferPut(
 }
 
 int cxBufferTerminate(CxBuffer *buffer) {
-    if (0 == cxBufferPut(buffer, 0)) {
-        buffer->size = buffer->pos - 1;
-        return 0;
+    // try to extend / shrink the buffer
+    if (buffer->pos >= buffer->capacity) {
+        if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == 0) {
+            return -1;
+        }
+        if (cxBufferReserve(buffer, buffer->pos + 1)) {
+            return -1; // LCOV_EXCL_LINE
+        }
     } else {
-        return -1;
+        buffer->size = buffer->pos;
+        cxBufferShrink(buffer, 1);
+        // set the capacity explicitly, in case shrink was skipped due to CoW
+        buffer->capacity = buffer->size + 1;
     }
+
+    // check if we are still on read-only memory
+    if (buffer_copy_on_write(buffer)) return -1;
+
+    // write the terminator and exit
+    buffer->space[buffer->pos] = '\0';
+    return 0;
 }
 
-size_t cxBufferPutString(
-        CxBuffer *buffer,
-        const char *str
-) {
-    return cxBufferWrite(str, 1, strlen(str), buffer);
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str) {
+    return cxBufferWrite(str.ptr, 1, str.length, buffer);
+}
+
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str) {
+    return cxBufferAppend(str.ptr, 1, str.length, buffer);
 }
 
 size_t cxBufferRead(
index a69ef07f06117b9c2d8f7d7ce736591c3b509ab0..11750f374c308dd2c59c8fa694739be6e0aa5368 100644 (file)
@@ -145,6 +145,17 @@ typedef void (*cx_destructor_func2)(void *data, void *memory);
 typedef void*(cx_clone_func)(void *target, const void *source,
                              const CxAllocator *allocator, void *data);
 
+/**
+ * Returns the system's memory page size.
+ *
+ * If the page size cannot be retrieved from the system,
+ * a default of 4096 bytes is assumed.
+ *
+ * @return the system's memory page size in bytes
+ */
+cx_attr_nodiscard
+CX_EXPORT unsigned long cx_system_page_size(void);
+
 /**
  * Reallocate a previously allocated block and changes the pointer in-place,
  * if necessary.
@@ -428,9 +439,9 @@ CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
  */
 #define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__)
 /**
- * Convenience macro that invokes cxFree() with the cxDefaultAllocator.
+ * Convenience function that invokes cxFree() with the cxDefaultAllocator.
  */
-#define cxFreeDefault(...) cxFree(cxDefaultAllocator, __VA_ARGS__)
+CX_EXPORT void cxFreeDefault(void *mem);
 
 #ifdef __cplusplus
 } // extern "C"
index 70b5667a3c4140a2f94e90d27cbec31f1d6b6178..4efa52cb2e911d6337a13dfb6964575e3a50e104 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "common.h"
 #include "allocator.h"
+#include "string.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -88,6 +89,13 @@ extern "C" {
  */
 #define CX_BUFFER_COPY_ON_EXTEND 0x08
 
+/**
+ * If this flag is enabled, the buffer will never free its contents regardless of #CX_BUFFER_FREE_CONTENTS.
+ *
+ * This is useful, for example, when you want to keep a pointer to the data after destroying the buffer.
+ */
+#define CX_BUFFER_DO_NOT_FREE 0x10
+
 /**
  * Function pointer for cxBufferWrite that is compatible with cx_write_func.
  * @see cx_write_func
@@ -99,59 +107,6 @@ extern "C" {
  */
 #define cxBufferReadFunc  ((cx_read_func) cxBufferRead)
 
-/**
- * Configuration for automatic flushing.
- */
-struct cx_buffer_flush_config_s {
-    /**
-     * The buffer may not extend beyond this threshold before starting to flush.
-     *
-     * Only used when the buffer uses #CX_BUFFER_AUTO_EXTEND.
-     * The threshold will be the maximum capacity the buffer is extended to
-     * before flushing.
-     */
-    size_t threshold;
-    /**
-     * The block size for the elements to flush.
-     */
-    size_t blksize;
-    /**
-     * The maximum number of blocks to flush in one cycle.
-     *
-     * @attention While it is guaranteed that cxBufferFlush() will not flush
-     * more blocks, this is not necessarily the case for cxBufferWrite().
-     * After performing a flush cycle, cxBufferWrite() will retry the write
-     * operation and potentially trigger another flush cycle, until the
-     * flush target accepts no more data.
-     */
-    size_t blkmax;
-
-    /**
-     * The target for the write function.
-     */
-    void *target;
-
-    /**
-     * The write-function used for flushing.
-     * If NULL, the flushed content gets discarded.
-     */
-    cx_write_func wfunc;
-};
-
-/**
- * Type alias for the flush configuration struct.
- *
- * @code
- * struct cx_buffer_flush_config_s {
- *     size_t threshold;
- *     size_t blksize;
- *     size_t blkmax;
- *     void *target;
- *     cx_write_func wfunc;
- * };
- * @endcode
- */
-typedef struct cx_buffer_flush_config_s CxBufferFlushConfig;
 
 /** Structure for the UCX buffer data. */
 struct cx_buffer_s {
@@ -168,16 +123,12 @@ struct cx_buffer_s {
     };
     /** The allocator to use for automatic memory management. */
     const CxAllocator *allocator;
-    /**
-     * Optional flush configuration
-     *
-     * @see cxBufferEnableFlushing()
-     */
-    CxBufferFlushConfig *flush;
     /** Current position of the buffer. */
     size_t pos;
     /** Current capacity (i.e. maximum size) of the buffer. */
     size_t capacity;
+    /** Maximum capacity that this buffer may grow to. */
+    size_t max_capacity;
     /** Current size of the buffer content. */
     size_t size;
     /**
@@ -230,23 +181,6 @@ cx_attr_nonnull_arg(1)
 CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity,
         const CxAllocator *allocator, int flags);
 
-/**
- * Configures the buffer for flushing.
- *
- * Flushing can happen automatically when data is written
- * to the buffer (see cxBufferWrite()) or manually when
- * cxBufferFlush() is called.
- *
- * @param buffer the buffer
- * @param config the flush configuration
- * @retval zero success
- * @retval non-zero failure
- * @see cxBufferFlush()
- * @see cxBufferWrite()
- */
-cx_attr_nonnull
-CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config);
-
 /**
  * Destroys the buffer contents.
  *
@@ -443,6 +377,8 @@ CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
  * Ensures that the buffer has the required capacity.
  *
  * If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is larger, the buffer is shrunk and superfluous
+ * content is discarded.
  *
  * This function will reserve no more bytes than requested, in contrast to
  * cxBufferMinimumCapacity(), which may reserve more bytes to improve the
@@ -450,7 +386,7 @@ CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
  *
  * @param buffer the buffer
  * @param capacity the required capacity for this buffer
- * @retval zero the capacity was already sufficient or successfully increased
+ * @retval zero on success
  * @retval non-zero on allocation failure
  * @see cxBufferShrink()
  * @see cxBufferMinimumCapacity()
@@ -458,6 +394,25 @@ CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
 cx_attr_nonnull
 CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
 
+/**
+ * Limits the buffer's capacity.
+ *
+ * If the current capacity is already larger, this function fails and returns
+ * non-zero.
+ *
+ * The capacity limit will affect auto-extension features, as well as future
+ * calls to cxBufferMinimumCapacity() and cxBufferReserve().
+ *
+ * @param buffer the buffer
+ * @param capacity the maximum allowed capacity for this buffer
+ * @retval zero the limit is applied
+ * @retval non-zero the new limit is smaller than the current capacity
+ * @see cxBufferReserve()
+ * @see cxBufferMinimumCapacity()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
+
 /**
  * Ensures that the buffer has a minimum capacity.
  *
@@ -471,6 +426,7 @@ CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
  * @param capacity the minimum required capacity for this buffer
  * @retval zero the capacity was already sufficient or successfully increased
  * @retval non-zero on allocation failure
+ * @see cxBufferMaximumCapacity()
  * @see cxBufferReserve()
  * @see cxBufferShrink()
  */
@@ -500,33 +456,13 @@ CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
 /**
  * Writes data to a CxBuffer.
  *
- * If automatic flushing is not enabled, the data is simply written into the
- * buffer at the current position, and the position of the buffer is increased
- * by the number of bytes written.
- *
- * 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.
+ * If auto-extension is enabled, the buffer's capacity is automatically
+ * increased when it is not large enough to hold all data.
+ * By default, the capacity grows indefinitely, unless limited with
+ * cxBufferMaximumCapacity().
+ * When auto-extension fails, this function writes no data and returns zero.
  *
- * If, after flushing, the number of items that shall be written still exceeds
- * the capacity or flush threshold, this function tries to write all items directly
- * to the flush target, if possible.
- *
- * The number returned by this function is the number of elements from
- * @c ptr that could be written to either the flush target or the buffer.
- * That means it does @em not include the number of items that were already in
- * the buffer and were also flushed during the process.
- *
- * @attention
- * When @p size is larger than one and the contents of the buffer are not aligned
- * with @p size, flushing stops after all complete items have been flushed, leaving
- * the misaligned part in the buffer.
- * Afterward, this function only writes as many items as possible to the buffer.
+ * The position of the buffer is moved alongside the written data.
  *
  * @note The signature is compatible with the fwrite() family of functions.
  *
@@ -565,62 +501,6 @@ cx_attr_nonnull
 CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
-/**
- * Performs a single flush-run on the specified buffer.
- *
- * Does nothing when the position in the buffer is zero.
- * Otherwise, the data until the current position minus
- * one is considered for flushing.
- * Note carefully that flushing will never exceed the
- * current @em position, even when the size of the
- * buffer is larger than the current position.
- *
- * One flush run will try to flush @c blkmax many
- * blocks of size @c blksize until either the @p buffer
- * has no more data to flush or the write function
- * used for flushing returns zero.
- *
- * The buffer is shifted left for that many bytes
- * the flush operation has successfully flushed.
- *
- * @par Example 1
- * Assume you have a buffer with size 340 and you are
- * at position 200. The flush configuration is
- * @c blkmax=4 and @c blksize=64 .
- * Assume that the entire flush operation is successful.
- * All 200 bytes on the left-hand-side from the current
- * position are written.
- * That means the size of the buffer is now 140 and the
- * position is zero.
- *
- * @par Example 2
- * Same as Example 1, but now the @c blkmax is 1.
- * The size of the buffer is now 276, and the position is 136.
- *
- * @par Example 3
- * Same as Example 1, but now assume the flush target
- * only accepts 100 bytes before returning zero.
- * That means the flush operation manages to flush
- * one complete block and one partial block, ending
- * up with a buffer with size 240 and position 100.
- *
- * @remark Just returns zero when flushing was not enabled with
- * cxBufferEnableFlushing().
- *
- * @remark When the buffer uses copy-on-write, the memory
- * is copied first, before attempting any flush.
- * This is, however, considered an erroneous use of the
- * buffer because it makes little sense to put
- * readonly data into an UCX buffer for flushing instead
- * of writing it directly to the target.
- *
- * @param buffer the buffer
- * @return the number of successfully flushed bytes
- * @see cxBufferEnableFlushing()
- */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer);
-
 /**
  * Reads data from a CxBuffer.
  *
@@ -645,8 +525,9 @@ CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
  *
  * 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 the buffer extension fails, @c EOF is returned.
+ * the buffer capacity is extended, unless a limit set by
+ * cxBufferMaximumCapacity() is reached.
+ * If the feature is disabled or the buffer extension fails, @c EOF is returned.
  *
  * On successful writing, the position of the buffer is increased.
  *
@@ -655,8 +536,8 @@ CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
  *
  * @param buffer the buffer to write to
  * @param c the character to write
- * @return the byte that has been written or @c EOF when the end of the stream is
- * reached, and automatic extension is not enabled or not possible
+ * @return the byte that has been written or @c EOF when the end of the
+ * stream is reached, and automatic extension is not enabled or not possible
  * @see cxBufferTerminate()
  */
 cx_attr_nonnull
@@ -665,28 +546,61 @@ CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
 /**
  * Writes a terminating zero to a buffer at the current position.
  *
- * If successful, sets the size to the current position and advances the position by one.
+ * If successful, also sets the size to the current position and shrinks the buffer.
  *
  * The purpose of this function is to have the written data ready to be used as
  * a C string with the buffer's size being the length of that string.
  *
  * @param buffer the buffer to write to
  * @return zero, if the terminator could be written, non-zero otherwise
+ * @see cxBufferShrink()
  */
 cx_attr_nonnull
 CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
 
 /**
- * Writes a string to a buffer.
+ * Internal function - do not use.
  *
- * This is a convenience function for <code>cxBufferWrite(str, 1, strlen(str), buffer)</code>.
+ * @param buffer the buffer
+ * @param str the string
+ * @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Writes a string to a buffer with cxBufferWrite().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferWrite()
+ * @see cx_strcast()
+ */
+#define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str))
+
+/**
+ * Internal function - do not use.
  *
  * @param buffer the buffer
- * @param str the zero-terminated string
+ * @param str the string
  * @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Appends a string to a buffer with cxBufferAppend().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferAppend()
+ * @see cx_strcast()
  */
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str);
+#define cxBufferAppendString(buffer, str) cx_buffer_append_string(buffer, cx_strcast(str))
 
 /**
  * Gets a character from a buffer.
index 92175857102f499a9205ce976df7e0e7b4b3581b..953edb5540ab631a8052a2aecd967abff444f590 100644 (file)
 #define UCX_COMMON_H
 
 /** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR   3
+#define UCX_VERSION_MAJOR   4
 
 /** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR   1
+#define UCX_VERSION_MINOR   0
 
 /** Version constant which ensures to increase monotonically. */
 #define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
index ab5af4f0bb8f189fd58ad21abeb54054f81d48da..c70a7f7a0141402d3f30d6cc0c2904cc72ce6974 100644 (file)
@@ -41,6 +41,7 @@
 #include "string.h"
 #include "buffer.h"
 #include "array_list.h"
+#include "map.h"
 
 #include <string.h>
 
@@ -188,9 +189,10 @@ typedef struct cx_json_value_s CxJsonValue;
  */
 typedef struct cx_json_array_s CxJsonArray;
 /**
- * Type alias for the JSON object struct.
+ * Type alias for the map representing a JSON object.
+ * The map contains pointers of type @c CxJsonValue.
  */
-typedef struct cx_json_object_s CxJsonObject;
+typedef CxMap* CxJsonObject;
 /**
  * Type alias for a JSON string.
  */
@@ -208,11 +210,6 @@ typedef double CxJsonNumber;
  */
 typedef enum cx_json_literal CxJsonLiteral;
 
-/**
- * Type alias for a key/value pair in a JSON object.
- */
-typedef struct cx_json_obj_value_s CxJsonObjValue;
-
 /**
  * JSON array structure.
  */
@@ -220,35 +217,7 @@ struct cx_json_array_s {
     /**
      * The array data.
      */
-    CX_ARRAY_DECLARE(CxJsonValue*, array);
-};
-
-/**
- * JSON object structure.
- */
-struct cx_json_object_s {
-    /**
-     * The key/value entries.
-     */
-    CX_ARRAY_DECLARE(CxJsonObjValue, values);
-    /**
-     * The original indices to reconstruct the order in which the members were added.
-     */
-    size_t *indices;
-};
-
-/**
- * Structure for a key/value entry in a JSON object.
- */
-struct cx_json_obj_value_s {
-    /**
-     * The key (or name in JSON terminology) of the value.
-     */
-    cxmutstr name;
-    /**
-     * The value.
-     */
-    CxJsonValue *value;
+    CX_ARRAY_DECLARE(CxJsonValue*, data);
 };
 
 /**
@@ -295,7 +264,7 @@ struct cx_json_value_s {
          * The literal type if the type is #CX_JSON_LITERAL.
          */
         CxJsonLiteral literal;
-    } value;
+    };
 };
 
 /**
@@ -349,11 +318,11 @@ struct cx_json_s {
     CxJsonValue *parsed;
 
     /**
-     * A pointer to an intermediate state of a currently parsed object member.
+     * The name of a not yet completely parsed object member.
      *
      * Never access this value manually.
      */
-    CxJsonObjValue uncompleted_member;
+    cxmutstr uncompleted_member_name;
 
     /**
      * State stack.
@@ -438,10 +407,6 @@ struct cx_json_writer_s {
      * Set true to enable pretty output.
      */
     bool pretty;
-    /**
-     * Set false to output the members in the order in which they were added.
-     */
-    bool sort_members;
     /**
      * The maximum number of fractional digits in a number value.
      * The default value is 6 and values larger than 15 are reduced to 15.
@@ -508,6 +473,33 @@ cx_attr_nonnull_arg(1, 2, 3)
 CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
         cx_write_func wfunc, const CxJsonWriter* settings);
 
+
+/**
+ * Produces a compact string representation of the specified JSON value.
+ *
+ * @param value the JSON value
+ * @param allocator the allocator for the string
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterCompact()
+ * @see cxJsonToPrettyString()
+ */
+cx_attr_nonnull_arg(1)
+CX_EXPORT cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator);
+
+/**
+ * Produces a pretty string representation of the specified JSON value.
+ *
+ * @param value the JSON value
+ * @param allocator the allocator for the string
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterPretty()
+ * @see cxJsonToString()
+ */
+cx_attr_nonnull_arg(1)
+CX_EXPORT cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator);
+
 /**
  * Initializes the JSON interface.
  *
@@ -530,8 +522,8 @@ CX_EXPORT void cxJsonDestroy(CxJson *json);
 /**
  * Destroys and re-initializes the JSON interface.
  *
- * You might want to use this to reset the parser after
- * encountering a syntax error.
+ * You must use this to reset the parser after encountering a syntax error
+ * if you want to continue using it.
  *
  * @param json the JSON interface
  */
@@ -592,6 +584,36 @@ CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
  */
 #define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str))
 
+
+/**
+ * Internal function - use cxJsonFromString() instead.
+ *
+ * @param allocator the allocator for the JSON value
+ * @param str the string to parse
+ * @param value a pointer where the JSON value shall be stored to
+ * @return status code
+ */
+cx_attr_nonnull_arg(3)
+CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+            cxstring str, CxJsonValue **value);
+
+/**
+ * Parses a string into a JSON value.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the JSON value
+ * @param str (any string) the string to parse
+ * @param value (@c CxJsonValue**) a pointer where the JSON value shall be stored to
+ * @retval CX_JSON_NO_ERROR success
+ * @retval CX_JSON_NO_DATA the string was empty or blank
+ * @retval CX_JSON_INCOMPLETE_DATA the string unexpectedly ended
+ * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed
+ * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for the CxJsonValue failed
+ * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
+ * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
+ */
+#define cxJsonFromString(allocator, str, value) \
+        cx_json_from_string(allocator, cx_strcast(str), value)
+
 /**
  * Creates a new (empty) JSON object.
  *
@@ -1077,7 +1099,7 @@ CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
  */
 cx_attr_nonnull
 CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
-    return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL;
+    return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL;
 }
 
 /**
@@ -1094,7 +1116,7 @@ CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
  */
 cx_attr_nonnull
 CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
-    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE;
+    return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE;
 }
 
 /**
@@ -1111,7 +1133,7 @@ CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
  */
 cx_attr_nonnull
 CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
-    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE;
+    return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE;
 }
 
 /**
@@ -1124,7 +1146,7 @@ CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
  */
 cx_attr_nonnull
 CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
-    return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL;
+    return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL;
 }
 
 /**
@@ -1202,7 +1224,7 @@ CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
  */
 cx_attr_nonnull
 CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
-    return value->value.literal == CX_JSON_TRUE;
+    return value->literal == CX_JSON_TRUE;
 }
 
 /**
@@ -1216,7 +1238,7 @@ CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
  */
 cx_attr_nonnull
 CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
-    return value->value.array.array_size;
+    return value->array.data_size;
 }
 
 /**
@@ -1277,14 +1299,14 @@ CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
  */
 cx_attr_nonnull
 CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
-    return value->value.object.values_size;
+    return cxCollectionSize(value->object);
 }
 
 /**
- * Returns an iterator over the JSON object members.
+ * Returns a map iterator over the JSON object members.
  *
- * The iterator yields values of type @c CxJsonObjValue* which
- * contain the name and value of the member.
+ * The iterator yields values of type @c CxMapEntry* which
+ * contain the name and the @c CxJsonObjValue* of the member.
  *
  * If the @p value is not a JSON object, the behavior is undefined.
  *
@@ -1293,7 +1315,7 @@ CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
  * @see cxJsonIsObject()
  */
 cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value);
+CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value);
 
 /**
  * Internal function, do not use.
index 95a4bbef0d5d15545e7d0f18e0b3a89b94d96aa1..8301275d406e41d84aed8c0f253c4b9a9577511c 100644 (file)
@@ -41,9 +41,6 @@
 #include "map.h"
 #include "buffer.h"
 
-#include <stdio.h>
-#include <string.h>
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -80,9 +77,6 @@ struct cx_properties_config_s {
      * The character, when appearing at the end of a line, continues that line.
      * This is '\' by default.
      */
-    /**
-     * Reserved for future use.
-     */
     char continuation;
 };
 
@@ -141,23 +135,16 @@ enum cx_properties_status {
      */
     CX_PROPERTIES_BUFFER_ALLOC_FAILED,
     /**
-     * Initializing the properties source failed.
-     *
-     * @see cx_properties_read_init_func
+     * A file operation failed.
+     * Only for cxPropertiesLoad().
+     * It is system-specific if errno is set.
      */
-    CX_PROPERTIES_READ_INIT_FAILED,
+    CX_PROPERTIES_FILE_ERROR,
     /**
-     * Reading from a properties source failed.
-     *
-     * @see cx_properties_read_func
+     * A map operation failed.
+     * Only for cxPropertiesLoad().
      */
-    CX_PROPERTIES_READ_FAILED,
-    /**
-     * Sinking a k/v-pair failed.
-     *
-     * @see cx_properties_sink_func
-     */
-    CX_PROPERTIES_SINK_FAILED,
+    CX_PROPERTIES_MAP_ERROR,
 };
 
 /**
@@ -190,134 +177,6 @@ struct cx_properties_s {
  */
 typedef struct cx_properties_s CxProperties;
 
-
-/**
- * Typedef for a properties sink.
- */
-typedef struct cx_properties_sink_s CxPropertiesSink;
-
-/**
- * A function that consumes a k/v-pair in a sink.
- *
- * The sink could be a map, and the sink function would be calling
- * a map function to store the k/v-pair.
- *
- * @param prop the properties interface that wants to sink a k/v-pair
- * @param sink the sink
- * @param key the key
- * @param value the value
- * @retval zero success
- * @retval non-zero sinking the k/v-pair failed
- */
-typedef int(*cx_properties_sink_func)(
-        CxProperties *prop,
-        CxPropertiesSink *sink,
-        cxstring key,
-        cxstring value
-);
-
-/**
- * Defines a sink for k/v-pairs.
- */
-struct cx_properties_sink_s {
-    /**
-     * The sink object.
-     */
-    void *sink;
-    /**
-     * Optional custom data.
-     */
-    void *data;
-    /**
-     * A function for consuming k/v-pairs into the sink.
-     */
-    cx_properties_sink_func sink_func;
-};
-
-
-/**
- * Typedef for a properties source.
- */
-typedef struct cx_properties_source_s CxPropertiesSource;
-
-/**
- * A function that reads data from a source.
- *
- * When the source is depleted, implementations SHALL provide an empty
- * string in the @p target and return zero.
- * A non-zero return value is only permitted in case of an error.
- *
- * The meaning of the optional parameters is implementation-dependent.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @param target a string buffer where the read data shall be stored
- * @retval zero success
- * @retval non-zero reading the data failed
- */
-typedef int(*cx_properties_read_func)(
-        CxProperties *prop,
-        CxPropertiesSource *src,
-        cxstring *target
-);
-
-/**
- * A function that may initialize additional memory for the source.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @retval zero initialization was successful
- * @retval non-zero otherwise
- */
-typedef int(*cx_properties_read_init_func)(
-        CxProperties *prop,
-        CxPropertiesSource *src
-);
-
-/**
- * A function that cleans memory initialized by the read_init_func.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- */
-typedef void(*cx_properties_read_clean_func)(
-        CxProperties *prop,
-        CxPropertiesSource *src
-);
-
-/**
- * Defines a properties source.
- */
-struct cx_properties_source_s {
-    /**
-     * The source object.
-     *
-     * For example, a file stream or a string.
-     */
-    void *src;
-    /**
-     * Optional additional data pointer.
-     */
-    void *data_ptr;
-    /**
-     * Optional size information.
-     */
-    size_t data_size;
-    /**
-     * A function that reads data from the source.
-     */
-    cx_properties_read_func read_func;
-    /**
-     * Optional function that may prepare the source for reading data.
-     */
-    cx_properties_read_init_func read_init_func;
-    /**
-     * Optional function that cleans additional memory allocated by the
-     * read_init_func.
-     */
-    cx_properties_read_clean_func read_clean_func;
-};
-
 /**
  * Initialize a properties interface.
  *
@@ -465,100 +324,83 @@ cx_attr_nonnull cx_attr_nodiscard
 CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
 
 /**
- * Creates a properties sink for an UCX map.
- *
- * The values stored in the map will be pointers to freshly allocated,
- * zero-terminated C strings (@c char*), which means the @p map should have been
- * created with #CX_STORE_POINTERS.
- *
- * The cxDefaultAllocator will be used unless you specify a custom
- * allocator in the optional @c data field of the returned sink.
- *
- * @param map the map that shall consume the k/v-pairs.
- * @return the sink
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will reserve with `cxPropertiesUseStack()`.
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map);
+CX_EXPORT extern const unsigned cx_properties_load_buf_size;
 
 /**
- * Creates a properties source based on an UCX string.
- *
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will use to read contents from the file.
  */
-cx_attr_nodiscard
-CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str);
+CX_EXPORT extern const unsigned cx_properties_load_fill_size;
 
 /**
- * Creates a properties source based on C string with the specified length.
+ * Internal function - use cxPropertiesLoad() instead.
  *
- * @param str the string
- * @param len the length
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param config the parser config
+ * @param allocator the allocator for the values
+ * @param filename the file name
+ * @param target the target map
+ * @return status code
  */
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
+cx_attr_nonnull_arg(4)
+CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
+        const CxAllocator *allocator, cxstring filename, CxMap *target);
 
 /**
- * Creates a properties source based on a C string.
+ * Loads properties from a file and inserts them into a map.
  *
- * The length will be determined with strlen(), so the string MUST be
- * zero-terminated.
+ * Entries are added to the map, possibly overwriting existing entries.
  *
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
- */
-cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str);
-
-/**
- * Creates a properties source based on an FILE.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
  *
- * @param file the file
- * @param chunk_size how many bytes may be read in one operation
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
  *
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param config the parser config
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
+ * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
+ * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
+ * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoadDefault()
  */
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1)
-CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
-
+#define cxPropertiesLoad(config, allocator, filename, target) \
+    cx_properties_load(config, allocator, cx_strcast(filename), target)
 
 /**
- * Loads properties data from a source and transfers it to a sink.
+ * Loads properties from a file and inserts them into a map with a default config.
  *
- * This function tries to read as much data from the source as possible.
- * When the source was completely consumed and at least on k/v-pair was found,
- * the return value will be #CX_PROPERTIES_NO_ERROR.
- * When the source was consumed but no k/v-pairs were found, the return value
- * will be #CX_PROPERTIES_NO_DATA.
- * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA
- * is returned. In that case you should call this function again with the same
- * sink and either an updated source or the same source if the source is able to
- * yield the missing data.
+ * Entries are added to the map, possibly overwriting existing entries.
  *
- * The other result codes apply, according to their description.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
  *
- * @param prop the properties interface
- * @param sink the sink
- * @param source the source
- * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found
- * @retval CX_PROPERTIES_READ_INIT_FAILED initializing the source failed
- * @retval CX_PROPERTIES_READ_FAILED reading from the source failed
- * @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed
- * @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs
- * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
+ *
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
  * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
  * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
  * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoad()
  */
-cx_attr_nonnull
-CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop,
-        CxPropertiesSink sink, CxPropertiesSource source);
+#define cxPropertiesLoadDefault(allocator, filename, target) \
+    cx_properties_load(cx_properties_config_default, allocator, cx_strcast(filename), target)
+
 
 #ifdef __cplusplus
 } // extern "C"
index e4d10a8f13cb647e0a31b1af11b1f459b85bae1b..23ab58e7f717a3237e4183a4795f03047f5f09ef 100644 (file)
@@ -643,16 +643,12 @@ struct cx_tree_node_base_s {
  * Structure for holding the base data of a tree.
  */
 struct cx_tree_s {
+    CX_COLLECTION_BASE;
     /**
      * The tree class definition.
      */
     const cx_tree_class *cl;
 
-    /**
-     * Allocator to allocate new nodes.
-     */
-    const CxAllocator *allocator;
-
     /**
      * A pointer to the root node.
      *
@@ -670,21 +666,6 @@ struct cx_tree_s {
      */
     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.
      */
@@ -695,11 +676,6 @@ struct cx_tree_s {
      */
     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.
      */
index eb5fc941f0bfe13733d5ac767db2cccd6e6b8fea..967356b708b20976b67822913bd0d1bd315fc865 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include "cx/json.h"
+#include "cx/kv_list.h"
 
 #include <string.h>
 #include <assert.h>
 
 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
 
-static int json_cmp_objvalue(const void *l, const void *r) {
-    const CxJsonObjValue *left = l;
-    const CxJsonObjValue *right = r;
-    return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
-}
-
-static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
-    assert(obj->type == CX_JSON_OBJECT);
-    CxJsonObjValue kv_dummy;
-    kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
-    return cx_array_binary_search(
-            obj->value.object.values,
-            obj->value.object.values_size,
-            sizeof(CxJsonObjValue),
-            &kv_dummy,
-            json_cmp_objvalue
-    );
-}
-
-static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
-    assert(objv->type == CX_JSON_OBJECT);
-    const CxAllocator * const al = objv->allocator;
-    CxJsonObject *obj = &(objv->value.object);
-
-    // determine the index where we need to insert the new member
-    size_t index = cx_array_binary_search_sup(
-        obj->values,
-        obj->values_size,
-        sizeof(CxJsonObjValue),
-        &member, json_cmp_objvalue
-    );
-
-    // is the name already present?
-    if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) {
-        // free the original value
-        cx_strfree_a(al, &obj->values[index].name);
-        cxJsonValueFree(obj->values[index].value);
-        // replace the item
-        obj->values[index] = member;
-
-        // nothing more to do
-        return 0;
-    }
-
-    // determine the old capacity and reserve for one more element
-    CxArrayReallocator arealloc = cx_array_reallocator(al, NULL);
-    size_t oldcap = obj->values_capacity;
-    if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1;
-
-    // check the new capacity, if we need to realloc the index array
-    size_t newcap = obj->values_capacity;
-    if (newcap > oldcap) {
-        if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
-            return 1; // LCOV_EXCL_LINE
-        }
-    }
-
-    // check if append or insert
-    if (index < obj->values_size) {
-        // move the other elements
-        memmove(
-            &obj->values[index+1],
-            &obj->values[index],
-            (obj->values_size - index) * sizeof(CxJsonObjValue)
-        );
-        // increase indices for the moved elements
-        for (size_t i = 0; i < obj->values_size ; i++) {
-            if (obj->indices[i] >= index) {
-                obj->indices[i]++;
-            }
-        }
-    }
-
-    // insert the element and set the index
-    obj->values[index] = member;
-    obj->indices[obj->values_size] = index;
-    obj->values_size++;
-
-    return 0;
-}
-
 static void token_destroy(CxJsonToken *token) {
     if (token->allocated) {
         cx_strfree(&token->content);
+        token->allocated = false;
     }
 }
 
@@ -307,7 +228,9 @@ static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
         }
     }
 
-    if (ttype != CX_JSON_NO_TOKEN) {
+    if (ttype == CX_JSON_NO_TOKEN) {
+        return CX_JSON_NO_DATA;
+    } else {
         // uncompleted token
         size_t uncompleted_len = json->buffer.size - token_part_start;
         if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
@@ -334,9 +257,8 @@ static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
         }
         // advance the buffer position - we saved the stuff in the uncompleted token
         json->buffer.pos += uncompleted_len;
+        return CX_JSON_INCOMPLETE_DATA;
     }
-
-    return CX_JSON_INCOMPLETE_DATA;
 }
 
 // converts a Unicode codepoint to utf8
@@ -473,7 +395,7 @@ static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) {
     return result;
 }
 
-static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
     // note: this function produces the string without enclosing quotes
     // the reason is that we don't want to allocate memory just for that
     CxBuffer buf = {0};
@@ -519,11 +441,27 @@ static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
             cxBufferPut(&buf, c);
         }
     }
-    if (!all_printable) {
-        str = cx_mutstrn(buf.space, buf.size);
+    cxmutstr ret;
+    if (all_printable) {
+        // don't copy the string when we don't need to escape anything
+        ret = cx_mutstrn((char*)str.ptr, str.length);
+    } else {
+        ret = cx_mutstrn(buf.space, buf.size);
     }
     cxBufferDestroy(&buf);
-    return str;
+    return ret;
+}
+
+static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
+    // TODO: we might want to add a comparator that is sorting the elements by their key
+    CxMap *map = cxKvListCreateAsMap(allocator, NULL, CX_STORE_POINTERS);
+    if (map == NULL) return NULL; // LCOV_EXCL_LINE
+    cxDefineDestructor(map, cxJsonValueFree);
+    return map;
+}
+
+static void json_free_object_map(CxJsonObject obj) {
+    cxMapFree(obj);
 }
 
 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
@@ -534,14 +472,11 @@ static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
     v->type = type;
     v->allocator = json->allocator;
     if (type == CX_JSON_ARRAY) {
-        cx_array_initialize_a(json->allocator, v->value.array.array, 16);
-        if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
+        cx_array_initialize_a(json->allocator, v->array.data, 16);
+        if (v->array.data == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
     } else if (type == CX_JSON_OBJECT) {
-        cx_array_initialize_a(json->allocator, v->value.object.values, 16);
-        v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t));
-        if (v->value.object.values == NULL ||
-            v->value.object.indices == NULL)
-            goto create_json_value_exit_error; // LCOV_EXCL_LINE
+        v->object = json_create_object_map(json->allocator);
+        if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
     }
 
     // add the new value to a possible parent
@@ -550,17 +485,17 @@ static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
         assert(parent != NULL);
         if (parent->type == CX_JSON_ARRAY) {
             CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL);
-            if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) {
+            if (cx_array_simple_add_a(&value_realloc, parent->array.data, v)) {
                 goto create_json_value_exit_error; // LCOV_EXCL_LINE
             }
         } else if (parent->type == CX_JSON_OBJECT) {
             // the member was already created after parsing the name
-            assert(json->uncompleted_member.name.ptr != NULL);
-            json->uncompleted_member.value = v;
-            if (json_add_objvalue(parent, json->uncompleted_member))  {
+            // store the pointer of the uncompleted value in the map
+            assert(json->uncompleted_member_name.ptr != NULL);
+            if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
                 goto create_json_value_exit_error; // LCOV_EXCL_LINE
             }
-            json->uncompleted_member.name = (cxmutstr) {NULL, 0};
+            cx_strfree_a(json->allocator, &json->uncompleted_member_name);
         } else {
             assert(false); // LCOV_EXCL_LINE
         }
@@ -624,10 +559,8 @@ void cxJsonDestroy(CxJson *json) {
     }
     cxJsonValueFree(json->parsed);
     json->parsed = NULL;
-    if (json->uncompleted_member.name.ptr != NULL) {
-        cx_strfree_a(json->allocator, &json->uncompleted_member.name);
-        json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL};
-    }
+    token_destroy(&json->uncompleted);
+    cx_strfree_a(json->allocator, &json->uncompleted_member_name);
 }
 
 void cxJsonReset(CxJson *json) {
@@ -720,7 +653,7 @@ static enum cx_json_status json_parse(CxJson *json) {
                 if (str.ptr == NULL) {
                     return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
                 }
-                vbuf->value.string = str;
+                vbuf->string = str;
                 return_rec(CX_JSON_NO_ERROR);
             }
             case CX_JSON_TOKEN_INTEGER:
@@ -730,11 +663,11 @@ static enum cx_json_status json_parse(CxJson *json) {
                     return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
                 }
                 if (type == CX_JSON_INTEGER) {
-                    if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) {
+                    if (cx_strtoi64(token.content, &vbuf->integer, 10)) {
                         return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
                     }
                 } else {
-                    if (cx_strtod(token.content, &vbuf->value.number)) {
+                    if (cx_strtod(token.content, &vbuf->number)) {
                         // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
                         return_rec(CX_JSON_FORMAT_ERROR_NUMBER);  // LCOV_EXCL_LINE
                     }
@@ -746,11 +679,11 @@ static enum cx_json_status json_parse(CxJson *json) {
                     return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
                 }
                 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) {
-                    vbuf->value.literal = CX_JSON_TRUE;
+                    vbuf->literal = CX_JSON_TRUE;
                 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) {
-                    vbuf->value.literal = CX_JSON_FALSE;
+                    vbuf->literal = CX_JSON_FALSE;
                 } else {
-                    vbuf->value.literal = CX_JSON_NULL;
+                    vbuf->literal = CX_JSON_NULL;
                 }
                 return_rec(CX_JSON_NO_ERROR);
             }
@@ -786,8 +719,8 @@ static enum cx_json_status json_parse(CxJson *json) {
             if (name.ptr == NULL) {
                 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
             }
-            assert(json->uncompleted_member.name.ptr == NULL);
-            json->uncompleted_member.name = name;
+            assert(json->uncompleted_member_name.ptr == NULL);
+            json->uncompleted_member_name = name;
             assert(json->vbuf_size > 0);
 
             // next state
@@ -860,29 +793,54 @@ CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
     return result;
 }
 
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+            cxstring str, CxJsonValue **value) {
+    *value = &cx_json_value_nothing;
+    CxJson parser;
+    cxJsonInit(&parser, allocator);
+    if (cxJsonFill(&parser, str)) {
+        // LCOV_EXCL_START
+        cxJsonDestroy(&parser);
+        return CX_JSON_BUFFER_ALLOC_FAILED;
+        // LCOV_EXCL_STOP
+    }
+    CxJsonStatus status = cxJsonNext(&parser, value);
+    // check if we consume the total string
+    CxJsonValue *chk_value = NULL;
+    CxJsonStatus chk_status = CX_JSON_NO_DATA;
+    if (status == CX_JSON_NO_ERROR) {
+        chk_status = cxJsonNext(&parser, &chk_value);
+    }
+    cxJsonDestroy(&parser);
+    if (chk_status == CX_JSON_NO_DATA) {
+        return status;
+    } else {
+        cxJsonValueFree(*value);
+        // if chk_value is nothing, the free is harmless
+        cxJsonValueFree(chk_value);
+        *value = &cx_json_value_nothing;
+        return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
+    }
+
+}
+
 void cxJsonValueFree(CxJsonValue *value) {
     if (value == NULL || value->type == CX_JSON_NOTHING) return;
     switch (value->type) {
         case CX_JSON_OBJECT: {
-            CxJsonObject obj = value->value.object;
-            for (size_t i = 0; i < obj.values_size; i++) {
-                cxJsonValueFree(obj.values[i].value);
-                cx_strfree_a(value->allocator, &obj.values[i].name);
-            }
-            cxFree(value->allocator, obj.values);
-            cxFree(value->allocator, obj.indices);
+            json_free_object_map(value->object);
             break;
         }
         case CX_JSON_ARRAY: {
-            CxJsonArray array = value->value.array;
-            for (size_t i = 0; i < array.array_size; i++) {
-                cxJsonValueFree(array.array[i]);
+            CxJsonArray array = value->array;
+            for (size_t i = 0; i < array.data_size; i++) {
+                cxJsonValueFree(array.data[i]);
             }
-            cxFree(value->allocator, array.array);
+            cxFree(value->allocator, array.data);
             break;
         }
         case CX_JSON_STRING: {
-            cxFree(value->allocator, value->value.string.ptr);
+            cxFree(value->allocator, value->string.ptr);
             break;
         }
         default: {
@@ -898,15 +856,8 @@ CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) {
     if (v == NULL) return NULL;
     v->allocator = allocator;
     v->type = CX_JSON_OBJECT;
-    cx_array_initialize_a(allocator, v->value.object.values, 16);
-    if (v->value.object.values == NULL) { // LCOV_EXCL_START
-        cxFree(allocator, v);
-        return NULL;
-        // LCOV_EXCL_STOP
-    }
-    v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t));
-    if (v->value.object.indices == NULL) { // LCOV_EXCL_START
-        cxFree(allocator, v->value.object.values);
+    v->object = json_create_object_map(allocator);
+    if (v->object == NULL) { // LCOV_EXCL_START
         cxFree(allocator, v);
         return NULL;
         // LCOV_EXCL_STOP
@@ -920,8 +871,8 @@ CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) {
     if (v == NULL) return NULL;
     v->allocator = allocator;
     v->type = CX_JSON_ARRAY;
-    cx_array_initialize_a(allocator, v->value.array.array, 16);
-    if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; }
+    cx_array_initialize_a(allocator, v->array.data, 16);
+    if (v->array.data == NULL) { cxFree(allocator, v); return NULL; }
     return v;
 }
 
@@ -931,7 +882,7 @@ CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) {
     if (v == NULL) return NULL;
     v->allocator = allocator;
     v->type = CX_JSON_NUMBER;
-    v->value.number = num;
+    v->number = num;
     return v;
 }
 
@@ -941,7 +892,7 @@ CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) {
     if (v == NULL) return NULL;
     v->allocator = allocator;
     v->type = CX_JSON_INTEGER;
-    v->value.integer = num;
+    v->integer = num;
     return v;
 }
 
@@ -953,7 +904,7 @@ CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
     v->type = CX_JSON_STRING;
     cxmutstr s = cx_strdup_a(allocator, str);
     if (s.ptr == NULL) { cxFree(allocator, v); return NULL; }
-    v->value.string = s;
+    v->string = s;
     return v;
 }
 
@@ -963,7 +914,7 @@ CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit
     if (v == NULL) return NULL;
     v->allocator = allocator;
     v->type = CX_JSON_LITERAL;
-    v->value.literal = lit;
+    v->literal = lit;
     return v;
 }
 
@@ -1042,24 +993,14 @@ int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count)
     CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL);
     assert(arr->type == CX_JSON_ARRAY);
     return cx_array_simple_copy_a(&value_realloc,
-            arr->value.array.array,
-            arr->value.array.array_size,
+            arr->array.data,
+            arr->array.data_size,
             val, count
     );
 }
 
 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
-    cxmutstr k = cx_strdup_a(obj->allocator, name);
-    if (k.ptr == NULL) return -1;
-    CxJsonObjValue kv = {k, child};
-    if (json_add_objvalue(obj, kv)) {
-        // LCOV_EXCL_START
-        cx_strfree_a(obj->allocator, &k);
-        return 1;
-        // LCOV_EXCL_STOP
-    } else {
-        return 0;
-    }
+    return cxMapPut(obj->object, name, child);
 }
 
 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
@@ -1105,98 +1046,84 @@ CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLite
 }
 
 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
-    if (index >= value->value.array.array_size) {
+    if (index >= value->array.data_size) {
         return &cx_json_value_nothing;
     }
-    return value->value.array.array[index];
+    return value->array.data[index];
 }
 
 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
-    if (index >= value->value.array.array_size) {
+    if (index >= value->array.data_size) {
         return NULL;
     }
-    CxJsonValue *ret = value->value.array.array[index];
+    CxJsonValue *ret = value->array.data[index];
     // TODO: replace with a low level cx_array_remove()
-    size_t count = value->value.array.array_size - index - 1;
+    size_t count = value->array.data_size - index - 1;
     if (count > 0) {
-        memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
+        memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*));
     }
-    value->value.array.array_size--;
+    value->array.data_size--;
     return ret;
 }
 
 char *cxJsonAsString(const CxJsonValue *value) {
-    return value->value.string.ptr;
+    return value->string.ptr;
 }
 
 cxstring cxJsonAsCxString(const CxJsonValue *value) {
-    return cx_strcast(value->value.string);
+    return cx_strcast(value->string);
 }
 
 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
-    return value->value.string;
+    return value->string;
 }
 
 double cxJsonAsDouble(const CxJsonValue *value) {
     if (value->type == CX_JSON_INTEGER) {
-        return (double) value->value.integer;
+        return (double) value->integer;
     } else {
-        return value->value.number;
+        return value->number;
     }
 }
 
 int64_t cxJsonAsInteger(const CxJsonValue *value) {
     if (value->type == CX_JSON_INTEGER) {
-        return value->value.integer;
+        return value->integer;
     } else {
-        return (int64_t) value->value.number;
+        return (int64_t) value->number;
     }
 }
 
 CxIterator cxJsonArrIter(const CxJsonValue *value) {
     return cxIteratorPtr(
-        value->value.array.array,
-        value->value.array.array_size,
+        value->array.data,
+        value->array.data_size,
         true // arrays need to keep order
     );
 }
 
-CxIterator cxJsonObjIter(const CxJsonValue *value) {
-    return cxIterator(
-        value->value.object.values,
-        sizeof(CxJsonObjValue),
-        value->value.object.values_size,
-        true // TODO: objects do not always need to keep order
-    );
+CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
+    return cxMapIterator(value->object);
 }
 
 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
-    size_t index = json_find_objvalue(value, name);
-    if (index >= value->value.object.values_size) {
+    CxJsonValue *v = cxMapGet(value->object, name);
+    if (v == NULL) {
         return &cx_json_value_nothing;
     } else {
-        return value->value.object.values[index].value;
+        return v;
     }
 }
 
 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
-    size_t index = json_find_objvalue(value, name);
-    if (index >= value->value.object.values_size) {
-        return NULL;
-    } else {
-        CxJsonObjValue kv = value->value.object.values[index];
-        cx_strfree_a(value->allocator, &kv.name);
-        // TODO: replace with cx_array_remove() / cx_array_remove_fast()
-        value->value.object.values_size--;
-        memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
-        return kv.value;
-    }
+    CxJsonValue *v = NULL;
+    cxMapRemoveAndGet(value->object, name, &v);
+    return v;
 }
 
 CxJsonWriter cxJsonWriterCompact(void) {
     return (CxJsonWriter) {
         false,
-        true,
         6,
         false,
         4,
@@ -1206,7 +1133,6 @@ CxJsonWriter cxJsonWriterCompact(void) {
 
 CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
     return (CxJsonWriter) {
-        true,
         true,
         6,
         use_spaces,
@@ -1272,14 +1198,8 @@ int cx_json_write_rec(
                 expected++;
             }
             depth++;
-            size_t elem_count = value->value.object.values_size;
-            for (size_t look_idx = 0; look_idx < elem_count; look_idx++) {
-                // get the member either via index array or directly
-                size_t elem_idx = settings->sort_members
-                                      ? look_idx
-                                      : value->value.object.indices[look_idx];
-                CxJsonObjValue *member = &value->value.object.values[elem_idx];
-
+            CxMapIterator member_iter = cxJsonObjIter(value);
+            cx_foreach(const CxMapEntry *, member, member_iter) {
                 // possible indentation
                 if (settings->pretty) {
                     if (cx_json_writer_indent(target, wfunc, settings, depth)) {
@@ -1289,26 +1209,27 @@ int cx_json_write_rec(
 
                 // the name
                 actual += wfunc("\"", 1, 1, target);
-                cxmutstr name = escape_string(member->name, settings->escape_slash);
+                cxstring key = cx_strn(member->key->data, member->key->len);
+                cxmutstr name = escape_string(key, settings->escape_slash);
                 actual += wfunc(name.ptr, 1, name.length, target);
-                if (name.ptr != member->name.ptr) {
-                    cx_strfree(&name);
-                }
                 actual += wfunc("\"", 1, 1, target);
                 const char *obj_name_sep = ": ";
                 if (settings->pretty) {
                     actual += wfunc(obj_name_sep, 1, 2, target);
-                    expected += 4 + member->name.length;
+                    expected += 4 + name.length;
                 } else {
                     actual += wfunc(obj_name_sep, 1, 1, target);
-                    expected += 3 + member->name.length;
+                    expected += 3 + name.length;
+                }
+                if (name.ptr != key.ptr) {
+                    cx_strfree(&name);
                 }
 
                 // the value
                 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
 
                 // end of object-value
-                if (look_idx < elem_count - 1) {
+                if (member_iter.index < member_iter.elem_count - 1) {
                     const char *obj_value_sep = ",\n";
                     if (settings->pretty) {
                         actual += wfunc(obj_value_sep, 1, 2, target);
@@ -1361,13 +1282,14 @@ int cx_json_write_rec(
         }
         case CX_JSON_STRING: {
             actual += wfunc("\"", 1, 1, target);
-            cxmutstr str = escape_string(value->value.string, settings->escape_slash);
+            cxmutstr str = escape_string(cx_strcast(value->string),
+                settings->escape_slash);
             actual += wfunc(str.ptr, 1, str.length, target);
-            if (str.ptr != value->value.string.ptr) {
+            actual += wfunc("\"", 1, 1, target);
+            expected += 2 + str.length;
+            if (str.ptr != value->string.ptr) {
                 cx_strfree(&str);
             }
-            actual += wfunc("\"", 1, 1, target);
-            expected += 2 + value->value.string.length;
             break;
         }
         case CX_JSON_NUMBER: {
@@ -1375,7 +1297,7 @@ int cx_json_write_rec(
             // because of the way how %g is defined, we need to
             // double the precision and truncate ourselves
             precision = 1 + (precision > 15 ? 30 : 2 * precision);
-            snprintf(numbuf, 40, "%.*g", precision, value->value.number);
+            snprintf(numbuf, 40, "%.*g", precision, value->number);
             char *dot, *exp;
             unsigned char max_digits;
             // find the decimal separator and hope that it's one of . or ,
@@ -1439,17 +1361,17 @@ int cx_json_write_rec(
             break;
         }
         case CX_JSON_INTEGER: {
-            snprintf(numbuf, 32, "%" PRIi64, value->value.integer);
+            snprintf(numbuf, 32, "%" PRIi64, value->integer);
             size_t len = strlen(numbuf);
             actual += wfunc(numbuf, 1, len, target);
             expected += len;
             break;
         }
         case CX_JSON_LITERAL: {
-            if (value->value.literal == CX_JSON_TRUE) {
+            if (value->literal == CX_JSON_TRUE) {
                 actual += wfunc("true", 1, 4, target);
                 expected += 4;
-            } else if (value->value.literal == CX_JSON_FALSE) {
+            } else if (value->literal == CX_JSON_FALSE) {
                 actual += wfunc("false", 1, 5, target);
                 expected += 5;
             } else {
@@ -1487,3 +1409,35 @@ int cxJsonWrite(
     }
     return cx_json_write_rec(target, value, wfunc, settings, 0);
 }
+
+static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
+    if (allocator == NULL) allocator = cxDefaultAllocator;
+    CxBuffer buffer;
+    if (cxBufferInit(&buffer, NULL, 128, allocator,
+        CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
+        return (cxmutstr){NULL, 0};
+    }
+    if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0)
+            || cxBufferTerminate(&buffer)) {
+        // LCOV_EXCL_START
+        buffer.flags &= ~CX_BUFFER_DO_NOT_FREE;
+        cxBufferDestroy(&buffer);
+        return (cxmutstr){NULL, 0};
+        // LCOV_EXCL_STOP
+    } else {
+        cxmutstr str = cx_mutstrn(buffer.space, buffer.size);
+        cxBufferDestroy(&buffer);
+        return str;
+    }
+
+}
+
+cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator) {
+    CxJsonWriter writer = cxJsonWriterCompact();
+    return cx_json_to_string(value, allocator, &writer);
+}
+
+cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator) {
+    CxJsonWriter writer = cxJsonWriterPretty(true);
+    return cx_json_to_string(value, allocator, &writer);
+}
index 3d1a78315a9b983e9381152ab76a6c3ec6d65b59..d6375d9c40f6942d788daff404437f89cffa3aec 100644 (file)
@@ -285,6 +285,7 @@ static struct cx_iterator_s cx_kvl_iterator(
 
 static void cx_kvl_map_deallocate(struct cx_map_s *map) {
     cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    cx_kv_list_update_destructors(kv_list);
     kv_list->map_methods->deallocate(map);
     kv_list->list_methods->deallocate(&kv_list->list.base);
 }
@@ -296,41 +297,7 @@ static void cx_kvl_map_clear(struct cx_map_s *map) {
     kv_list->map_methods->clear(map);
 }
 
-static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
-    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
-    // if the hash has not yet been computed, do it now
-    if (key.hash == 0) {
-        cx_hash_murmur(&key);
-    }
-
-    // reserve memory in the map first
-    void **map_data = kv_list->map_methods->put(map, key, NULL);
-    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
-
-    // insert the data into the list (which most likely destroys the sorted property)
-    kv_list->list.base.collection.sorted = false;
-    void *node_data = kv_list->list_methods->insert_element(
-        &kv_list->list.base, kv_list->list.base.collection.size,
-        kv_list->list.base.collection.store_pointer ? &value : value);
-    if (node_data == NULL) { // LCOV_EXCL_START
-        // non-destructively remove the key again
-        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
-        return NULL;
-    } // LCOV_EXCL_STOP
-
-    // write the node pointer to the map entry
-    *map_data = node_data;
-
-    // copy the key to the node data
-    CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
-    *key_ptr = key;
-
-    // we must return node_data here and not map_data,
-    // because the node_data is the actual element of this collection
-    return node_data;
-}
-
-void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+static void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
     cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
     void *node_data = kv_list->map_methods->get(map, key);
     if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
@@ -338,7 +305,7 @@ void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
     return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
 }
 
-int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
     cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
 
     void *node_data;
@@ -381,6 +348,43 @@ int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
     return 0;
 }
 
+static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+    // if the hash has not yet been computed, do it now
+    if (key.hash == 0) {
+        cx_hash_murmur(&key);
+    }
+
+    // remove any existing element first
+    cx_kvl_map_remove(map, key, NULL);
+
+    // now reserve new memory in the map
+    void **map_data = kv_list->map_methods->put(map, key, NULL);
+    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
+
+    // insert the data into the list (which most likely destroys the sorted property)
+    kv_list->list.base.collection.sorted = false;
+    void *node_data = kv_list->list_methods->insert_element(
+        &kv_list->list.base, kv_list->list.base.collection.size,
+        kv_list->list.base.collection.store_pointer ? &value : value);
+    if (node_data == NULL) { // LCOV_EXCL_START
+        // non-destructively remove the key again
+        kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+        return NULL;
+    } // LCOV_EXCL_STOP
+
+    // write the node pointer to the map entry
+    *map_data = node_data;
+
+    // copy the key to the node data
+    CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+    *key_ptr = key;
+
+    // we must return node_data here and not map_data,
+    // because the node_data is the actual element of this collection
+    return node_data;
+}
+
 static void *cx_kvl_iter_current_entry(const void *it) {
     const CxMapIterator *iter = it;
     return (void*)&iter->entry;
@@ -455,7 +459,7 @@ static bool cx_kvl_iter_valid(const void *it) {
     return iter->elem != NULL;
 }
 
-CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
     CxMapIterator iter = {0};
 
     iter.type = type;
index 8f15f9654aa0c9f7a585d9104db43409c19e1618..c8cdc5cc14839915a6013124a1a710a052138a6b 100644 (file)
 #include "cx/properties.h"
 
 #include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
 
 const CxPropertiesConfig cx_properties_config_default = {
-        '=',
-        '#',
-        '\0',
-        '\0',
+    '=',
+    '#',
+    '\0',
+    '\0',
     '\\',
 };
 
@@ -94,13 +97,30 @@ CxPropertiesStatus cxPropertiesNext(
 
     // a pointer to the buffer we want to read from
     CxBuffer *current_buffer = &prop->input;
-
+    
+    char comment1 = prop->config.comment1;
+    char comment2 = prop->config.comment2;
+    char comment3 = prop->config.comment3;
+    char delimiter = prop->config.delimiter;
+    char continuation = prop->config.continuation;
+    
     // check if we have rescued data
     if (!cxBufferEof(&prop->buffer)) {
         // check if we can now get a complete line
         cxstring input = cx_strn(prop->input.space + prop->input.pos,
             prop->input.size - prop->input.pos);
         cxstring nl = cx_strchr(input, '\n');
+        while (nl.length > 0) {
+            // check for line continuation
+            char previous = nl.ptr > input.ptr ? nl.ptr[-1] : prop->buffer.space[prop->buffer.size-1];
+            if (previous == continuation) {
+                // this nl is a line continuation, check the next newline
+                nl = cx_strchr(cx_strsubs(nl, 1), '\n');
+            } else {
+                break;
+            }
+        }
+        
         if (nl.length > 0) {
             // we add as much data to the rescue buffer as we need
             // to complete the line
@@ -127,12 +147,7 @@ CxPropertiesStatus cxPropertiesNext(
             return CX_PROPERTIES_INCOMPLETE_DATA;
         }
     }
-
-    char comment1 = prop->config.comment1;
-    char comment2 = prop->config.comment2;
-    char comment3 = prop->config.comment3;
-    char delimiter = prop->config.delimiter;
-
+   
     // get one line and parse it
     while (!cxBufferEof(current_buffer)) {
         const char *buf = current_buffer->space + current_buffer->pos;
@@ -145,6 +160,7 @@ CxPropertiesStatus cxPropertiesNext(
         size_t delimiter_index = 0;
         size_t comment_index = 0;
         bool has_comment = false;
+        bool has_continuation = false;
 
         size_t i = 0;
         char c = 0;
@@ -159,6 +175,9 @@ CxPropertiesStatus cxPropertiesNext(
                 if (delimiter_index == 0 && !has_comment) {
                     delimiter_index = i;
                 }
+            } else if (delimiter_index > 0 && c == continuation && i+1 < len && buf[i+1] == '\n') {
+                has_continuation = true;
+                i++;
             } else if (c == '\n') {
                 break;
             }
@@ -223,10 +242,53 @@ CxPropertiesStatus cxPropertiesNext(
             k = cx_strtrim(k);
             val = cx_strtrim(val);
             if (k.length > 0) {
+                current_buffer->pos += i + 1; 
+                assert(current_buffer->pos <= current_buffer->size);
+                assert(current_buffer != &prop->buffer || current_buffer->pos == current_buffer->size);
+                
+                if (has_continuation) {
+                    char *ptr = (char*)val.ptr;
+                    if (current_buffer != &prop->buffer) {
+                        // move value to the rescue buffer
+                        if (prop->buffer.space == NULL) {
+                            cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
+                        }
+                        prop->buffer.size = 0;
+                        prop->buffer.pos = 0;
+                        if (cxBufferWrite(val.ptr, 1, val.length, &prop->buffer) != val.length) {
+                            return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+                        }
+                        val.ptr = prop->buffer.space;
+                        ptr = prop->buffer.space;
+                    }
+                    // value.ptr is now inside the rescue buffer and we can
+                    // remove the continuation character from the value
+                    bool trim = false;
+                    size_t x = 0;
+                    for(size_t j=0;j<val.length;j++) {
+                        c = ptr[j];
+                        if (j+1 < val.length && c == '\\' && ptr[j+1] == '\n') {
+                            // skip continuation and newline character
+                            j++;
+                            trim = true; // enable trim in the next line
+                            continue;
+                        }
+                        if (j > x) {
+                            if (trim) {
+                                if (isspace((unsigned char)c)) {
+                                    continue;
+                                }
+                                trim = false;
+                            }
+                            ptr[x] = c;
+                        }
+                        x++;
+                    }
+                    val.length = x;
+                }
                 *key = k;
                 *value = val;
-                current_buffer->pos += i + 1;
-                assert(current_buffer->pos <= current_buffer->size);
+                
                 return CX_PROPERTIES_NO_ERROR;
             } else {
                 return CX_PROPERTIES_INVALID_EMPTY_KEY;
@@ -241,180 +303,96 @@ CxPropertiesStatus cxPropertiesNext(
     return CX_PROPERTIES_NO_DATA;
 }
 
-static int cx_properties_sink_map(
-        cx_attr_unused CxProperties *prop,
-        CxPropertiesSink *sink,
-        cxstring key,
-        cxstring value
-) {
-    CxMap *map = sink->sink;
-    CxAllocator *alloc = sink->data;
-    cxmutstr v = cx_strdup_a(alloc, value);
-    int r = cxMapPut(map, key, v.ptr);
-    if (r != 0) cx_strfree_a(alloc, &v);
-    return r;
-}
-
-CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
-    CxPropertiesSink sink;
-    sink.sink = map;
-    sink.data = (void*) cxDefaultAllocator;
-    sink.sink_func = cx_properties_sink_map;
-    return sink;
-}
-
-static int cx_properties_read_string(
-        CxProperties *prop,
-        CxPropertiesSource *src,
-        cxstring *target
-) {
-    if (prop->input.space == src->src) {
-        // when the input buffer already contains the string
-        // we have nothing more to provide
-        target->length = 0;
-    } else {
-        target->ptr = src->src;
-        target->length = src->data_size;
+#ifndef CX_PROPERTIES_LOAD_FILL_SIZE
+#define CX_PROPERTIES_LOAD_FILL_SIZE 1024
+#endif
+const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE;
+#ifndef CX_PROPERTIES_LOAD_BUF_SIZE
+#define CX_PROPERTIES_LOAD_BUF_SIZE 256
+#endif
+const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE;
+
+CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
+        const CxAllocator *allocator, cxstring filename, CxMap *target) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
     }
-    return 0;
-}
-
-static int cx_properties_read_file(
-        cx_attr_unused CxProperties *prop,
-        CxPropertiesSource *src,
-        cxstring *target
-) {
-    target->ptr = src->data_ptr;
-    target->length = fread(src->data_ptr, 1, src->data_size, src->src);
-    return ferror((FILE*)src->src);
-}
-
-static int cx_properties_read_init_file(
-        cx_attr_unused CxProperties *prop,
-        CxPropertiesSource *src
-) {
-    src->data_ptr = cxMallocDefault(src->data_size);
-    if (src->data_ptr == NULL) return 1;
-    return 0;
-}
 
-static void cx_properties_read_clean_file(
-        cx_attr_unused CxProperties *prop,
-        CxPropertiesSource *src
-) {
-    cxFreeDefault(src->data_ptr);
-}
-
-CxPropertiesSource cxPropertiesStringSource(cxstring str) {
-    CxPropertiesSource src;
-    src.src = (void*) str.ptr;
-    src.data_size = str.length;
-    src.data_ptr = NULL;
-    src.read_func = cx_properties_read_string;
-    src.read_init_func = NULL;
-    src.read_clean_func = NULL;
-    return src;
-}
-
-CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) {
-    CxPropertiesSource src;
-    src.src = (void*) str;
-    src.data_size = len;
-    src.data_ptr = NULL;
-    src.read_func = cx_properties_read_string;
-    src.read_init_func = NULL;
-    src.read_clean_func = NULL;
-    return src;
-}
-
-CxPropertiesSource cxPropertiesCstrSource(const char *str) {
-    CxPropertiesSource src;
-    src.src = (void*) str;
-    src.data_size = strlen(str);
-    src.data_ptr = NULL;
-    src.read_func = cx_properties_read_string;
-    src.read_init_func = NULL;
-    src.read_clean_func = NULL;
-    return src;
-}
-
-CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) {
-    CxPropertiesSource src;
-    src.src = file;
-    src.data_size = chunk_size;
-    src.data_ptr = NULL;
-    src.read_func = cx_properties_read_file;
-    src.read_init_func = cx_properties_read_init_file;
-    src.read_clean_func = cx_properties_read_clean_file;
-    return src;
-}
+    // sanity check for the map
+    const bool use_cstring = cxCollectionStoresPointers(target);
+    if (!use_cstring && cxCollectionElementSize(target) != sizeof(cxmutstr)) {
+        return CX_PROPERTIES_MAP_ERROR;
+    }
 
-CxPropertiesStatus cxPropertiesLoad(
-        CxProperties *prop,
-        CxPropertiesSink sink,
-        CxPropertiesSource source
-) {
-    assert(source.read_func != NULL);
-    assert(sink.sink_func != NULL);
+    // create a duplicate to guarantee zero-termination
+    cxmutstr fname = cx_strdup(filename);
+    if (fname.ptr == NULL) {
+        return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+    }
 
-    // initialize reader
-    if (source.read_init_func != NULL) {
-        if (source.read_init_func(prop, &source)) {
-            return CX_PROPERTIES_READ_INIT_FAILED;  // LCOV_EXCL_LINE
-        }
+    // open the file
+    FILE *f = fopen(fname.ptr, "r");
+    if (f == NULL) {
+        cx_strfree(&fname);
+        return CX_PROPERTIES_FILE_ERROR;
     }
 
-    // transfer the data from the source to the sink
+    // initialize the parser
+    char linebuf[cx_properties_load_buf_size];
+    char fillbuf[cx_properties_load_fill_size];
     CxPropertiesStatus status;
-    CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA;
-    bool found = false;
+    CxProperties parser;
+    cxPropertiesInit(&parser, config);
+    cxPropertiesUseStack(&parser, linebuf, cx_properties_load_buf_size);
+
+    // read/fill/parse loop
+    status = CX_PROPERTIES_NO_DATA;
+    size_t keys_found = 0;
     while (true) {
-        // read input
-        cxstring input;
-        if (source.read_func(prop, &source, &input)) { // LCOV_EXCL_START
-            status = CX_PROPERTIES_READ_FAILED;
+        size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f);
+        if (ferror(f)) {
+            status = CX_PROPERTIES_FILE_ERROR;
             break;
-        } // LCOV_EXCL_STOP
-
-        // no more data - break
-        if (input.length == 0) {
-            if (found) {
-                // something was found, check the last kv_status
-                if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) {
-                    status = CX_PROPERTIES_INCOMPLETE_DATA;
-                } else {
-                    status = CX_PROPERTIES_NO_ERROR;
-                }
-            } else {
-                // nothing found
-                status = CX_PROPERTIES_NO_DATA;
-            }
+        }
+        if (r == 0) {
             break;
         }
-
-        // set the input buffer and read the k/v-pairs
-        cxPropertiesFill(prop, input);
-
-        do {
-            cxstring key, value;
-            kv_status = cxPropertiesNext(prop, &key, &value);
-            if (kv_status == CX_PROPERTIES_NO_ERROR) {
-                found = true;
-                if (sink.sink_func(prop, &sink, key, value)) {
-                    kv_status = CX_PROPERTIES_SINK_FAILED;  // LCOV_EXCL_LINE
+        if (cxPropertiesFilln(&parser, fillbuf, r)) {
+            status = CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+            break;
+        }
+        cxstring key, value;
+        while (true) {
+            status = cxPropertiesNext(&parser, &key, &value);
+            if (status != CX_PROPERTIES_NO_ERROR) {
+                break;
+            } else {
+                cxmutstr v = cx_strdup_a(allocator, value);
+                if (v.ptr == NULL) {
+                    status = CX_PROPERTIES_MAP_ERROR;
+                    break;
+                }
+                void *mv = use_cstring ? (void*)v.ptr : &v;
+                if (cxMapPut(target, key, mv)) {
+                    cx_strfree(&v);
+                    status = CX_PROPERTIES_MAP_ERROR;
+                    break;
                 }
+                keys_found++;
             }
-        } while (kv_status == CX_PROPERTIES_NO_ERROR);
-
-        if (kv_status > CX_PROPERTIES_OK) {
-            status = kv_status;
+        }
+        if (status > CX_PROPERTIES_OK) {
             break;
         }
     }
 
-    if (source.read_clean_func != NULL) {
-        source.read_clean_func(prop, &source);
+    // cleanup and exit
+    fclose(f);
+    cxPropertiesDestroy(&parser);
+    cx_strfree(&fname);
+    if (status == CX_PROPERTIES_NO_DATA && keys_found > 0) {
+        return CX_PROPERTIES_NO_ERROR;
+    } else {
+        return status;
     }
-
-    return status;
 }
index 8be9b6d775bd883585f07942c871690f56eaff82..3905cc0ec9acdcb1c6c5d665f2da6dceebd120ab 100644 (file)
@@ -734,15 +734,15 @@ static int cx_tree_default_insert_element(
         if (node == NULL) return 1;  // LCOV_EXCL_LINE
         cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
         tree->root = node;
-        tree->size = 1;
+        tree->collection.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++;
+        tree->collection.size++;
     } else {
-        cxFree(tree->allocator, node);
+        cxFree(tree->collection.allocator, node);
     }
     return result;
 }
@@ -767,9 +767,9 @@ static size_t cx_tree_default_insert_many(
     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;
+    tree->collection.size += ins;
     if (ins < n) {
-        cxFree(tree->allocator, failed);
+        cxFree(tree->collection.allocator, failed);
     }
     return ins;
 }
@@ -818,24 +818,21 @@ CxTree *cxTreeCreate(const CxAllocator *allocator,
     assert(search_func != NULL);
     assert(search_data_func != NULL);
 
-    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
     if (tree == NULL) return NULL;  // LCOV_EXCL_LINE
 
     tree->cl = &cx_tree_default_class;
-    tree->allocator = allocator;
+    tree->collection.allocator = allocator;
     tree->node_create = create_func;
     tree->search = search_func;
     tree->search_data = search_data_func;
-    tree->simple_destructor = NULL;
-    tree->advanced_destructor = (cx_destructor_func2) cxFree;
-    tree->destructor_data = (void *) allocator;
+    tree->collection.advanced_destructor = (cx_destructor_func2) cxFree;
+    tree->collection.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;
 }
@@ -845,7 +842,7 @@ void cxTreeFree(CxTree *tree) {
     if (tree->root != NULL) {
         cxTreeClear(tree);
     }
-    cxFree(tree->allocator, tree);
+    cxFree(tree->collection.allocator, tree);
 }
 
 CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
@@ -856,39 +853,33 @@ CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
     }
     assert(root != NULL);
 
-    CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+    CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
     if (tree == NULL) return NULL;  // LCOV_EXCL_LINE
 
     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->collection.allocator = 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 = root;
-    tree->size = cxTreeSubtreeSize(tree, root);
+    tree->collection.size = cxTreeSubtreeSize(tree, root);
     return tree;
 }
 
 void cxTreeSetParent(CxTree *tree, void *parent, void *child) {
     size_t loc_parent = tree->loc_parent;
     if (tree_parent(child) == NULL) {
-        tree->size++;
+        tree->collection.size++;
     }
     cx_tree_link(parent, child, cx_tree_node_layout(tree));
 }
 
 void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
     cx_tree_link(parent, child, cx_tree_node_layout(tree));
-    tree->size++;
+    tree->collection.size++;
 }
 
 int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
@@ -896,7 +887,7 @@ int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
     if (node == NULL) return 1; // LCOV_EXCL_LINE
     cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
     cx_tree_link(parent, node, cx_tree_node_layout(tree));
-    tree->size++;
+    tree->collection.size++;
     return 0;
 }
 
@@ -948,7 +939,7 @@ size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) {
 }
 
 size_t cxTreeSize(CxTree *tree) {
-    return tree->size;
+    return tree->collection.size;
 }
 
 size_t cxTreeDepth(CxTree *tree) {
@@ -1002,7 +993,7 @@ int cxTreeRemoveNode(
     if (loc_last_child >= 0) tree_last_child(node) = NULL;
 
     // the tree now has one member less
-    tree->size--;
+    tree->collection.size--;
 
     return 0;
 }
@@ -1010,12 +1001,12 @@ int cxTreeRemoveNode(
 void cxTreeRemoveSubtree(CxTree *tree, void *node) {
     if (node == tree->root) {
         tree->root = NULL;
-        tree->size = 0;
+        tree->collection.size = 0;
         return;
     }
     size_t subtree_size = cxTreeSubtreeSize(tree, node);
     cx_tree_unlink(node, cx_tree_node_layout(tree));
-    tree->size -= subtree_size;
+    tree->collection.size -= subtree_size;
 }
 
 int cxTreeDestroyNode(
@@ -1025,12 +1016,7 @@ int cxTreeDestroyNode(
 ) {
     int result = cxTreeRemoveNode(tree, node, relink_func);
     if (result == 0) {
-        if (tree->simple_destructor) {
-            tree->simple_destructor(node);
-        }
-        if (tree->advanced_destructor) {
-            tree->advanced_destructor(tree->destructor_data, node);
-        }
+        cx_invoke_destructor(tree, node);
         return 0;
     } else {
         return result;
@@ -1045,15 +1031,10 @@ void cxTreeDestroySubtree(CxTree *tree, void *node) {
     );
     cx_foreach(void *, child, iter) {
         if (iter.exiting) {
-            if (tree->simple_destructor) {
-                tree->simple_destructor(child);
-            }
-            if (tree->advanced_destructor) {
-                tree->advanced_destructor(tree->destructor_data, child);
-            }
+            cx_invoke_destructor(tree, child);
         }
     }
-    tree->size -= iter.counter;
+    tree->collection.size -= iter.counter;
     if (node == tree->root) {
         tree->root = NULL;
     }
index 4dc142fc3caf00cadb6c036246f4a14cf870aa10..439cae1a5bf975a2d7736bf8843dce0d5daa5557 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #import "toolkit.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
 
 @interface ListDataSource : NSObject <NSTableViewDataSource>
 
index c498de5408b74f7a36fb5a8c02648ba272e5455e..6a3b174dec9a35da1dc96d71fb443688d005bbea 100644 (file)
@@ -28,7 +28,7 @@
 
 #import "toolkit.h"
 #import "Container.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
 
 #import "ListDataSource.h"
 
index e776eeb4067abac359f7fcedbee117843d7acfd4..98d4a6fa9d4f38cb5c9a2f2f702cef370418a4e7 100644 (file)
@@ -179,7 +179,7 @@ UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
         
         ListDataSource *dataSource = [[ListDataSource alloc] init:cols var:var getvalue:getvalue getvaluedata:getvaluedata];
         if(model) {
-            dataSource.model = ui_model_copy(obj->ctx, model);
+            dataSource.model = model;
         }
         
         tableview.dataSource = dataSource;
@@ -263,7 +263,7 @@ void ui_tableview_setselection(UiList *list, UiListSelection selection) {
 
 @end
 
-UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject* obj, UiListArgs *args) {
     NSComboBox *dropdown = [[NSComboBox alloc] init];
     dropdown.editable = NO;
     
index fd97b2c2fa787a49132f247c6aa2e719ad5eb269..c0e9edff00451902f89af3e37c9db80ffe7404f9 100644 (file)
@@ -33,6 +33,7 @@
 #include "../common/menu.h"
 #include "../common/toolbar.h"
 #include "../common/threadpool.h"
+#include "../common/app.h"
 
 #import "image.h"
 #import "menu.h"
@@ -46,13 +47,6 @@ static const char *application_name;
 static int app_argc;
 static const char **app_argv;
 
-static ui_callback   startup_func;
-static void          *startup_data;
-static ui_callback   open_func;
-static void          *open_data;
-static ui_callback   exit_func;
-static void          *exit_data;
-
 static UiBool        exit_on_shutdown;
 
 /* ------------------- App Init / Event Loop functions ------------------- */
@@ -85,21 +79,6 @@ 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_app_exit_on_shutdown(UiBool exitapp) {
     exit_on_shutdown = exitapp;
 }
@@ -111,9 +90,7 @@ void ui_cocoa_onstartup(void) {
     e.document = NULL;
     e.eventdata = NULL;
     e.intval = 0;
-    if(startup_func) {
-        startup_func(&e, startup_data);
-    }
+    uic_application_startup(&e);
 }
 
 void ui_cocoa_onopen(const char *file) {
@@ -123,9 +100,7 @@ void ui_cocoa_onopen(const char *file) {
     e.document = NULL;
     e.eventdata = NULL;
     e.intval = 0;
-    if(open_func) {
-        open_func(&e, open_data);
-    }
+    uic_application_open(&e);
 }
 
 void ui_cocoa_onexit(void) {
@@ -135,9 +110,7 @@ void ui_cocoa_onexit(void) {
     e.document = NULL;
     e.eventdata = NULL;
     e.intval = 0;
-    if(exit_func) {
-        exit_func(&e, exit_data);
-    }
+    uic_application_exit(&e);
 }
 
 void ui_main(void) {
index c4efb6b40cdcf12272b758a72d0747736d2032c6..a3fe0890fe9da612168fd43a8a844b32855aff7f 100644 (file)
@@ -67,21 +67,18 @@ static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOO
     return obj;
 }
 
-UiObject* ui_window(const char *title, void *window_data) {
+UiObject* ui_window(const char *title) {
     UiObject *obj = create_window(title, FALSE, FALSE, FALSE);
-    obj->window = window_data;
     return obj;
 }
 
-UiObject* ui_simple_window(const char *title, void *window_data) {
+UiObject* ui_simple_window(const char *title) {
     UiObject *obj = create_window(title, TRUE, FALSE, FALSE);
-    obj->window = window_data;
     return obj;
 }
 
-UiObject* ui_sidebar_window(const char *title, void *window_data) {
+UiObject* ui_sidebar_window(const char *title) {
     UiObject *obj = create_window(title, FALSE, TRUE, FALSE);
-    obj->window = window_data;
     return obj;
 }
 
diff --git a/ui/common/app.c b/ui/common/app.c
new file mode 100644 (file)
index 0000000..9674599
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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 "app.h"
+
+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;
+
+
+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 uic_application_startup(UiEvent *event) {
+    if(startup_func) {
+        startup_func(event, startup_data);
+    }
+}
+
+void uic_application_open(UiEvent *event) {
+    if(open_func) {
+        open_func(event, open_data);
+    }
+}
+
+void uic_application_exit(UiEvent *event) {
+    if(exit_func) {
+        exit_func(event, exit_data);
+    }
+}
diff --git a/ui/common/app.h b/ui/common/app.h
new file mode 100644 (file)
index 0000000..02356a9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 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_APP_H
+#define UIC_APP_H
+
+#include "../ui/toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void uic_application_startup(UiEvent *event);
+void uic_application_open(UiEvent *event);
+void uic_application_exit(UiEvent *event);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UIC_APP_H */
+
index 796919ba834fd4797e69ff2935d0ca4c5cf1b770..2ef732e53afc72cc5ec6ff0874f680b62a9be367 100644 (file)
@@ -175,10 +175,10 @@ void ui_dialogwindow_args_free(UiDialogWindowArgs *args) {
     free((void*)args->lbutton2);
     free((void*)args->rbutton3);
     free((void*)args->rbutton4);
-    free((void*)args->lbutton1_groups);
-    free((void*)args->lbutton2_groups);
-    free((void*)args->rbutton3_groups);
-    free((void*)args->rbutton4_groups);
+    free((void*)args->lbutton1_states);
+    free((void*)args->lbutton2_states);
+    free((void*)args->rbutton3_states);
+    free((void*)args->rbutton4_states);
     free(args);
 }
 
@@ -310,16 +310,16 @@ void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclick
     args->onclickdata = onclickdata;
 }
 
-void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_toolbar_item_args_set_states(UiToolbarItemArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 void ui_toolbar_item_args_free(UiToolbarItemArgs *args) {
     free((void*)args->label);
     free((void*)args->icon);
     free((void*)args->tooltip);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -355,10 +355,10 @@ void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args,
     args->onchangedata = onchangedata;
 }
 
-void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_toolbar_toggleitem_args_set_states(UiToolbarToggleItemArgs *args,int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) {
@@ -366,7 +366,7 @@ void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) {
     free((void*)args->icon);
     free((void*)args->tooltip);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -1383,10 +1383,10 @@ void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata){
     args->onclickdata = onclickdata;
 }
 
-void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_button_args_set_states(UiButtonArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_button_args_free(UiButtonArgs *args) {
@@ -1395,7 +1395,7 @@ void ui_button_args_free(UiButtonArgs *args) {
     free((void*)args->label);
     free((void*)args->icon);
     free((void*)args->tooltip);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -1502,14 +1502,14 @@ void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value) {
     args->value = value;
 }
 
-void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) {
-    args->enable_group = group;
+void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state) {
+    args->enable_state = state;
 }
 
-void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_toggle_args_set_states(UiToggleArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_toggle_args_free(UiToggleArgs *args) {
@@ -1519,7 +1519,7 @@ void ui_toggle_args_free(UiToggleArgs *args) {
     free((void*)args->icon);
     free((void*)args->tooltip);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -1633,10 +1633,10 @@ void ui_linkbutton_args_set_value(UiLinkButtonArgs *args, UiString *value) {
     args->value = value;
 }
 
-void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_linkbutton_args_set_states(UiLinkButtonArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
@@ -1645,7 +1645,7 @@ void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
     free((void*)args->label);
     free((void*)args->uri);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -1815,10 +1815,10 @@ void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder)
     args->contextmenu = menubuilder;
 }
 
-void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_list_args_set_states(UiListArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_list_args_free(UiListArgs *args) {
@@ -1831,7 +1831,7 @@ void ui_list_args_free(UiListArgs *args) {
         }
         free(args->static_elements);
     }
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -1971,7 +1971,7 @@ void ui_sourcelist_args_free(UiSourceListArgs *args) {
     free((void*)args->style_class);
     free((void*)args->varname);
     free((void*)args->sublists);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -2071,17 +2071,17 @@ void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value) {
     args->value = value;
 }
 
-void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_textarea_args_set_states(UiTextAreaArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_textarea_args_free(UiTextAreaArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -2191,17 +2191,17 @@ void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value) {
     args->value = value;
 }
 
-void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_textfield_args_set_states(UiTextFieldArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_textfield_args_free(UiTextFieldArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -2314,17 +2314,17 @@ void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value) {
     args->rangevalue = value;
 }
 
-void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_spinbox_args_set_states(UiSpinBoxArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_spinbox_args_free(UiSpinBoxArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
@@ -2414,17 +2414,17 @@ void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value) {
     args->value = value;
 }
 
-void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) {
-    args->groups = calloc(numstates+1, sizeof(int));
-    memcpy((void*)args->groups, states, numstates * sizeof(int));
-    ((int*)args->groups)[numstates] = -1;
+void ui_webview_args_set_states(UiWebviewArgs *args, int *states, int numstates) {
+    args->states = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->states, states, numstates * sizeof(int));
+    ((int*)args->states)[numstates] = -1;
 }
 
 void ui_webview_args_free(UiWebviewArgs *args) {
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
-    free((void*)args->groups);
+    free((void*)args->states);
     free(args);
 }
 
index ab6647178e475549a9caa5c1038386e641d985f6..41d33f6e4adfca0ff3938b57bf0127a1adc17747 100644 (file)
@@ -36,7 +36,7 @@
 #include "../ui/entry.h"
 #include "../ui/menu.h"
 #include "../ui/toolbar.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "../ui/text.h"
 #include "../ui/webview.h"
 #include "../ui/widget.h"
@@ -107,7 +107,7 @@ UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char
 UIEXPORT void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip);
 UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata);
-UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_item_args_set_states(UiToolbarItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args);
 
 UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void);
@@ -117,7 +117,7 @@ UIEXPORT void ui_toolbar_toggleitem_args_set_tooltip(UiToolbarToggleItemArgs *ar
 UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata);
-UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_toggleitem_args_set_states(UiToolbarToggleItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args);
 
 UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void);
@@ -346,7 +346,7 @@ UIEXPORT void ui_button_args_set_tooltip(UiButtonArgs *args, const char *tooltip
 UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype);
 UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback);
 UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata);
-UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_button_args_set_states(UiButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_button_args_free(UiButtonArgs *args);
 
 UIEXPORT UiToggleArgs* ui_toggle_args_new(void);
@@ -373,8 +373,8 @@ UIEXPORT void ui_toggle_args_set_onchange(UiToggleArgs *args, ui_callback callba
 UIEXPORT void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata);
 UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname);
 UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value);
-UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group);
-UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates);
+UIEXPORT void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state);
+UIEXPORT void ui_toggle_args_set_states(UiToggleArgs *args, int *states, int numstates);
 UIEXPORT void ui_toggle_args_free(UiToggleArgs *args);
 
 UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void);
@@ -401,7 +401,7 @@ UIEXPORT void ui_linkbutton_args_set_onclick(UiLinkButtonArgs *args, ui_callback
 UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata);
 UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value);
 UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type);
-UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_linkbutton_args_set_states(UiLinkButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args);
 
 UIEXPORT UiListArgs* ui_list_args_new(void);
@@ -443,7 +443,7 @@ UIEXPORT void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave)
 UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection);
 UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder);
-UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates);
+UIEXPORT void ui_list_args_set_states(UiListArgs *args, int *states, int numstates);
 UIEXPORT void ui_list_args_free(UiListArgs *args);
 
 UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void);
@@ -495,7 +495,7 @@ UIEXPORT void ui_textarea_args_set_onchange(UiTextAreaArgs *args, ui_callback ca
 UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata);
 UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname);
 UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value);
-UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates);
+UIEXPORT void ui_textarea_args_set_states(UiTextAreaArgs *args, int *states, int numstates);
 UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args);
 
 UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void);
@@ -520,7 +520,7 @@ UIEXPORT void ui_textfield_args_set_onactivate(UiTextFieldArgs *args, ui_callbac
 UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata);
 UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname);
 UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value);
-UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates);
+UIEXPORT void ui_textfield_args_set_states(UiTextFieldArgs *args, int *states, int numstates);
 UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args);
 
 UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void);
@@ -549,7 +549,7 @@ UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varna
 UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value);
 UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value);
 UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value);
-UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates);
+UIEXPORT void ui_spinbox_args_set_states(UiSpinBoxArgs *args, int *states, int numstates);
 UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args);
 
 UIEXPORT UiWebviewArgs* ui_webview_args_new(void);
@@ -570,7 +570,7 @@ UIEXPORT void ui_webview_args_set_name(UiWebviewArgs *args, const char *name);
 UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname);
 UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname);
 UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value);
-UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates);
+UIEXPORT void ui_webview_args_set_states(UiWebviewArgs *args, int *states, int numstates);
 UIEXPORT void ui_webview_args_free(UiWebviewArgs *args);
 
 #ifdef __cplusplus
index 40c1544d4276c8fffbc5a8368a6552a66f0eb3c9..bd1d8558d46d13f0f85febd3b6dece6fdd8f730a 100644 (file)
@@ -64,8 +64,8 @@ UiContext* uic_context(UiObject *toplevel, CxMempool *mp) {
     ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
     
     ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, 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->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget));
+    ctx->states = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
     
     ctx->attach_document = uic_context_attach_document;
     ctx->detach_document2 = uic_context_detach_document;
@@ -92,11 +92,17 @@ void uic_context_add_destructor(UiContext *ctx, cx_destructor_func func, void *d
 }
 
 void uic_context_prepare_close(UiContext *ctx) {
-    cxListClear(ctx->groups);
-    cxListClear(ctx->group_widgets);
+    cxListClear(ctx->states);
+    cxListClear(ctx->state_widgets);
 }
 
 void uic_context_attach_document(UiContext *ctx, void *document) {
+    if(ctx->single_document_mode) {
+        if(ctx->document) {
+            uic_context_detach_document(ctx, ctx->document);
+        }
+    }
+    
     cxListAdd(ctx->documents, document);
     ctx->document = document;
     
@@ -202,6 +208,14 @@ UiVar* uic_get_var(UiContext *ctx, const char *name) {
     return ctx_getvar(ctx, key);
 }
 
+UiVar* uic_get_var_t(UiContext *ctx,const char *name, UiVarType type) {
+    UiVar *var = uic_get_var(ctx, name);
+    if(var && var->type == type) {
+        return var;
+    }
+    return NULL;
+}
+
 UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
     UiVar *var = uic_get_var(ctx, name);
     if(var) {
@@ -485,6 +499,14 @@ void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value)
 
 // public API
 
+void* ui_context_get_document(UiContext *ctx) {
+    return ctx->document;
+}
+
+void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable) {
+    ctx->single_document_mode = enable;
+}
+
 void ui_attach_document(UiContext *ctx, void *document) {
     uic_context_attach_document(ctx, document);
 }
@@ -511,48 +533,48 @@ UiContext* ui_context_parent(UiContext *ctx) {
 }
 
 
-void ui_set_group(UiContext *ctx, int group) {
-    if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) {
-        cxListAdd(ctx->groups, &group);
+void ui_set_state(UiContext *ctx, int state) {
+    if(!cxListIndexValid(ctx->states, cxListFind(ctx->states, &state))) {
+        cxListAdd(ctx->states, &state);
     }
     
     // enable/disable group widgets
-    uic_check_group_widgets(ctx);
+    uic_check_state_widgets(ctx);
 }
 
-void ui_unset_group(UiContext *ctx, int group) {
-    int i = cxListFind(ctx->groups, &group);
+void ui_unset_state(UiContext *ctx, int state) {
+    int i = cxListFind(ctx->states, &state);
     if(i != -1) {
-        cxListRemove(ctx->groups, i);
+        cxListRemove(ctx->states, i);
     }
     
     // enable/disable group widgets
-    uic_check_group_widgets(ctx);
+    uic_check_state_widgets(ctx);
 }
 
-int* ui_active_groups(UiContext *ctx, int *ngroups) {
-    *ngroups = cxListSize(ctx->groups);
-    return cxListAt(ctx->groups, 0);
+int* ui_active_states(UiContext *ctx, int *nstates) {
+    *nstates = cxListSize(ctx->states);
+    return cxListAt(ctx->states, 0);
 }
 
-void uic_check_group_widgets(UiContext *ctx) {
+void uic_check_state_widgets(UiContext *ctx) {
     int ngroups = 0;
-    int *groups = ui_active_groups(ctx, &ngroups);
+    int *groups = ui_active_states(ctx, &ngroups);
     
-    CxIterator i = cxListIterator(ctx->group_widgets);
-    cx_foreach(UiGroupWidget *, gw, i) {
-        char *check = calloc(1, gw->numgroups);
+    CxIterator i = cxListIterator(ctx->state_widgets);
+    cx_foreach(UiStateWidget *, gw, i) {
+        char *check = calloc(1, gw->numstates);
         
         for(int i=0;i<ngroups;i++) {
-            for(int k=0;k<gw->numgroups;k++) {
-                if(groups[i] == gw->groups[k]) {
+            for(int k=0;k<gw->numstates;k++) {
+                if(groups[i] == gw->states[k]) {
                     check[k] = 1;
                 }
             }
         }
         
         int enable = 1;
-        for(int i=0;i<gw->numgroups;i++) {
+        for(int i=0;i<gw->numstates;i++) {
             if(check[i] == 0) {
                 enable = 0;
                 break;
@@ -563,70 +585,70 @@ void uic_check_group_widgets(UiContext *ctx) {
     }
 }
 
-void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
     if(enable == NULL) {
         enable = (ui_enablefunc)ui_set_enabled;
     }
-    // get groups
-    CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+    // get states
+    CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
     va_list ap;
     va_start(ap, enable);
-    int group;
-    while((group = va_arg(ap, int)) != -1) {
-        cxListAdd(groups, &group);
+    int state;
+    while((state = va_arg(ap, int)) != -1) {
+        cxListAdd(states, &state);
     }
     va_end(ap);
     
-    uic_add_group_widget(ctx, widget, enable, groups);
+    uic_add_state_widget(ctx, widget, enable, states);
     
-    cxListFree(groups);
+    cxListFree(states);
 }
 
-void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) {
+void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates) {
     if(enable == NULL) {
         enable = (ui_enablefunc)ui_set_enabled;
     }
-    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
-    for(int i=0;i<ngroups;i++) {
-        cxListAdd(ls, groups+i);
+    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), nstates);
+    for(int i=0;i<nstates;i++) {
+        cxListAdd(ls, states+i);
     }
-    uic_add_group_widget(ctx, widget, enable, ls);
+    uic_add_state_widget(ctx, widget, enable, ls);
     cxListFree(ls);
 }
 
 void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates) {
-    ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+    ui_widget_set_states2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
 }
 
-size_t uic_group_array_size(const int *groups) {
+size_t uic_state_array_size(const int *states) {
     int i;
-    for(i=0;groups[i] >= 0;i++) { }
+    for(i=0;states[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_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states) {
+    uic_add_state_widget_i(ctx, widget, enable, cxListAt(states, 0), cxListSize(states));
 }
 
-void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) {
+void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates) {
     const CxAllocator *a = ctx->allocator;
-    UiGroupWidget gw;
+    UiStateWidget gw;
     
     gw.widget = widget;
     gw.enable = enable;
-    gw.numgroups = numgroups;
-    gw.groups = cxCalloc(a, numgroups, sizeof(int));
+    gw.numstates = numstates;
+    gw.states = cxCalloc(a, numstates, sizeof(int));
     
-    // copy groups
-    if(groups) {
-        memcpy(gw.groups, groups, gw.numgroups * sizeof(int));
+    // copy states
+    if(states) {
+        memcpy(gw.states, states, gw.numstates * sizeof(int));
     }
     
-    cxListAdd(ctx->group_widgets, &gw);
+    cxListAdd(ctx->state_widgets, &gw);
 }
 
-void uic_remove_group_widget(UiContext *ctx, void *widget) {
-    (void)cxListFindRemove(ctx->group_widgets, widget);
+void uic_remove_state_widget(UiContext *ctx, void *widget) {
+    (void)cxListFindRemove(ctx->state_widgets, widget);
 }
 
 UIEXPORT void *ui_allocator(UiContext *ctx) {
@@ -671,3 +693,33 @@ void  ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) {
 void  ui_set_destructor(void *mem, ui_destructor_func destr) {
     cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
 }
+
+UiInteger* ui_get_int_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_INTEGER);
+    return var ? var->value : NULL;
+}
+
+UiDouble* ui_get_double_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_DOUBLE);
+    return var ? var->value : NULL;
+}
+
+UiString* ui_get_string_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_STRING);
+    return var ? var->value : NULL;
+}
+
+UiText* ui_get_text_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_TEXT);
+    return var ? var->value : NULL;
+}
+
+UiRange* ui_get_range_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_RANGE);
+    return var ? var->value : NULL;
+}
+
+UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) {
+    UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC);
+    return var ? var->value : NULL;
+}
index 15e7700386ceb573cf2935a65f7f4b33374a9f3d..5efcc2ee50d8a2d0a11494dc012a46c623463d5d 100644 (file)
@@ -43,7 +43,7 @@ extern "C" {
 typedef struct UiVar            UiVar;
 typedef struct UiListPtr        UiListPtr;
 typedef struct UiListVar        UiListVar;
-typedef struct UiGroupWidget    UiGroupWidget;
+typedef struct UiStateWidget    UiStateWidget;
 typedef struct UiDestroyHandler UiDestroyHandler;
 
 typedef enum UiVarType {
@@ -69,8 +69,8 @@ struct UiContext {
     
     CxMap         *vars;
     
-    CxList        *groups; // int list
-    CxList        *group_widgets; // UiGroupWidget list
+    CxList        *states; // int list
+    CxList        *state_widgets; // UiGroupWidget list
     
     void (*attach_document)(UiContext *ctx, void *document);
     void (*detach_document2)(UiContext *ctx, void *document); 
@@ -84,8 +84,10 @@ struct UiContext {
     GtkAccelGroup *accel_group;
 #endif 
 #endif
-
-
+    
+    // allow only one document to be attached
+    // attaching a document will automatically detach the current document
+    UiBool single_document_mode;
     
     ui_callback   close_callback;
     void          *close_data;
@@ -100,11 +102,11 @@ struct UiVar {
     UiBool    bound;
 };
 
-struct UiGroupWidget {
+struct UiStateWidget {
     void          *widget;
     ui_enablefunc enable;
-    int           *groups;
-    int           numgroups;
+    int           *states;
+    int           numstates;
 };
 
 struct UiDestroyHandler {
@@ -128,6 +130,7 @@ void uic_context_detach_context(UiContext *ctx, UiContext *doc_ctx); // TODO
 void uic_context_detach_all(UiContext *ctx);
 
 UiVar* uic_get_var(UiContext *ctx, const char *name);
+UiVar* uic_get_var_t(UiContext *ctx, const char *name, UiVarType type);
 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);
@@ -143,11 +146,11 @@ const char *uic_type2str(UiVarType type);
 
 void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value);
 
-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, 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);
+size_t uic_state_array_size(const int *states);
+void uic_check_state_widgets(UiContext *ctx);
+void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states);
+void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates);
+void uic_remove_state_widget(UiContext *ctx, void *widget);
 
 #ifdef __cplusplus
 }
index b0cfc46bbe59052c37abde53c0616815aea4a55d..64c49c19ffcf702349b528b74fe7209378723471 100644 (file)
@@ -89,20 +89,20 @@ 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) {
+int* uic_copy_states(const int* states, size_t *nstates) {
+    *nstates = 0;
+    if (!states) {
         return NULL;
     }
 
     size_t n;
-    for (n = 0; groups[n] > -1; n++) { }
+    for (n = 0; states[n] > -1; n++) { }
 
-    if (ngroups > 0) {
+    if (nstates > 0) {
         int* newarray = calloc(n+1, sizeof(int));
-        memcpy(newarray, groups, n * sizeof(int));
+        memcpy(newarray, states, n * sizeof(int));
         newarray[n] = -1;
-        *ngroups = n;
+        *nstates = n;
         return newarray;
     }
     return NULL;
@@ -152,7 +152,7 @@ void ui_menuitem_create(UiMenuItemArgs *args) {
     item->icon = nl_strdup(args->icon);
     item->userdata = args->onclickdata;
     item->callback = args->onclick;
-    item->groups = uic_copy_groups(args->groups, &item->ngroups);
+    item->states = uic_copy_states(args->states, &item->nstates);
 
     add_item((UiMenuItemI*)item);
 }
@@ -179,7 +179,7 @@ void ui_menu_toggleitem_create(UiMenuToggleItemArgs *args) {
     item->varname = nl_strdup(args->varname);
     item->userdata = args->onchangedata;
     item->callback = args->onchange;
-    item->groups = uic_copy_groups(args->groups, &item->ngroups);
+    item->states = uic_copy_states(args->nstates, &item->nstates);
     
     add_item((UiMenuItemI*)item);
 }
@@ -196,7 +196,7 @@ void ui_menu_radioitem_create(UiMenuToggleItemArgs *args) {
     item->varname = nl_strdup(args->varname);
     item->userdata = args->onchangedata;
     item->callback = args->onchange;
-    item->groups = uic_copy_groups(args->groups, &item->ngroups);
+    item->states = uic_copy_states(args->nstates, &item->nstates);
 
     add_item((UiMenuItemI*)item);
 }
@@ -296,14 +296,14 @@ static void free_menuitem(UiMenuItemI *item) {
         }
         case UI_MENU_ITEM: {
             UiMenuItem *i = (UiMenuItem*)item;
-            free(i->groups);
+            free(i->states);
             free(i->label);
             free(i->icon);
             break;
         }
         case UI_MENU_CHECK_ITEM: {
             UiMenuCheckItem *i = (UiMenuCheckItem*)item;
-            free(i->groups);
+            free(i->states);
             free(i->label);
             free(i->icon);
             free(i->varname);
@@ -311,7 +311,7 @@ static void free_menuitem(UiMenuItemI *item) {
         }
         case UI_MENU_RADIO_ITEM: {
             UiMenuRadioItem *i = (UiMenuRadioItem*)item;
-            free(i->groups);
+            free(i->states);
             free(i->label);
             free(i->icon);
             free(i->varname);
index f44f6b4f9504333acdae7bc1fa6debe34e88cca6..9aea5789962c531fddfc7e618f7307197a1c275f 100644 (file)
@@ -79,8 +79,8 @@ struct UiMenuItem {
     char           *label;
     char           *icon;
     void           *userdata;
-    int            *groups;
-    size_t         ngroups;
+    int            *states;
+    size_t         nstates;
 };
 
 struct UiMenuCheckItem {
@@ -90,8 +90,8 @@ struct UiMenuCheckItem {
     char           *varname;
     ui_callback    callback;
     void           *userdata;
-    int            *groups;
-    size_t         ngroups;
+    int            *states;
+    size_t         nstates;
 };
 
 struct UiMenuRadioItem {
@@ -101,8 +101,8 @@ struct UiMenuRadioItem {
     char           *varname;
     ui_callback    callback;
     void           *userdata;
-    int            *groups;
-    size_t         ngroups;
+    int            *states;
+    size_t         nstates;
 };
 
 struct UiMenuItemList {
@@ -129,7 +129,7 @@ UiMenu* uic_get_menu_list(void);
 
 void uic_add_menu_to_stack(UiMenu* menu);
 
-int* uic_copy_groups(const int* groups, size_t *ngroups);
+int* uic_copy_states(const int* states, size_t *nstates);
 
 void uic_set_tmp_eventdata(void *eventdata, int type);
 void* uic_get_tmp_eventdata(void);
index 921b1a6695fae729b7fc021fadf044278bc22be6..30ef304b3bd0452878a623d74a367eb843198f82 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef _WIN32
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
 #include "message.h"
 
+int uic_message_send_(UiMessageHandler *handler, cxstring msg) {
+    return handler->send(handler, msg);
+}
+
 UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback) {
     UiSimpleMessageHandler *handler = malloc(sizeof(UiSimpleMessageHandler));
     handler->handler.start = uic_simple_msg_handler_start;
@@ -77,6 +83,9 @@ int uic_simple_msg_handler_stop(UiMessageHandler *handler) {
 int uic_simple_msg_handler_send(UiMessageHandler *handler, cxstring msg) {
     UiSimpleMessageHandler *sh = (UiSimpleMessageHandler*)handler;
     pthread_mutex_lock(&sh->queue_lock);
+    char header[32];
+    snprintf(header, 32, "%zu\n", msg.length);
+    cxBufferPutString(sh->outbuf, header);
     cxBufferWrite(msg.ptr, 1, msg.length, sh->outbuf);
     pthread_cond_signal(&sh->available);
     pthread_mutex_unlock(&sh->queue_lock);
@@ -114,8 +123,9 @@ void* uic_simple_msg_handler_in_thread(void *data) {
                     // message complete
                     //fprintf(stderr, "send: %.*s\n", (int)msg_size, msg);
                     if(handler->handler.callback) {
-                        handler->handler.callback(cx_mutstrn(msg, msg_size));
+                        handler->handler.callback(cx_strn(msg, msg_size));
                     }
+                    free(msg);
                     msg = NULL;
                     msg_size = 0;
                     msg_pos = 0;
@@ -200,3 +210,5 @@ void* uic_simple_msg_handler_out_thread(void *data) {
     
     return NULL;
 }
+
+#endif
index 00c83b7f47ec3715be57a76213bfcf60b8a127af..83cb135ec223d846193f019cf788d6e4bb2eafbc 100644 (file)
 #ifndef UIC_MESSAGE_H
 #define UIC_MESSAGE_H
 
+
 #include <cx/string.h>
 #include <cx/json.h>
 #include <cx/buffer.h>
 
+#ifndef _WIN32
 #include <pthread.h>
+#endif
 
 
 #ifdef __cplusplus
@@ -43,7 +46,7 @@ extern "C" {
 
 typedef struct UiMessageHandler UiMessageHandler;
 
-typedef void(*msg_received_callback)(cxmutstr msg);
+typedef void(*msg_received_callback)(cxstring msg);
 
 struct UiMessageHandler {
     int (*start)(UiMessageHandler *handler);
@@ -57,15 +60,20 @@ typedef struct UiSimpleMessageHandler {
     UiMessageHandler handler;
     int in;
     int out;
+#ifndef _WIN32
     pthread_t in_thread;
     pthread_t out_thread;
     pthread_mutex_t queue_lock;
     pthread_mutex_t avlbl_lock;
     pthread_cond_t  available;
+#endif
     CxBuffer *outbuf;
     int stop;
 } UiSimpleMessageHandler;
 
+int uic_message_send_(UiMessageHandler *handler, cxstring msg);
+#define uic_message_send(handler, msg) uic_message_send_(handler, cx_strcast(msg))
+
 UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback callback);
 int uic_simple_msg_handler_start(UiMessageHandler *handler);
 int uic_simple_msg_handler_stop(UiMessageHandler *handler);
index 2d01bda1f5af24951670b6e285ebe3cd2d88746b..e69125caa6865580629a16a70b5cf06e5ce2eea5 100644 (file)
@@ -34,10 +34,10 @@ COMMON_OBJ += document$(OBJ_EXT)
 COMMON_OBJ += object$(OBJ_EXT)
 COMMON_OBJ += container$(OBJ_EXT)
 COMMON_OBJ += types$(OBJ_EXT)
+COMMON_OBJ += app$(OBJ_EXT)
 COMMON_OBJ += properties$(OBJ_EXT)
 COMMON_OBJ += menu$(OBJ_EXT)
 COMMON_OBJ += toolbar$(OBJ_EXT)
-COMMON_OBJ += ucx_properties$(OBJ_EXT)
 COMMON_OBJ += threadpool$(OBJ_EXT)
 COMMON_OBJ += condvar$(OBJ_EXT)
 COMMON_OBJ += args$(OBJ_EXT)
index 28676fb08fe84c06d1e5c501351a7fbdc3d75496..138be626729064873236cc78ff8657684b333132 100644 (file)
@@ -44,7 +44,7 @@
 #include <cx/buffer.h>
 
 #include <cx/hash_map.h>
-#include "ucx_properties.h"
+#include <cx/properties.h>
 
 static CxMap *application_properties;
 static CxMap *language;
@@ -54,8 +54,27 @@ static CxMap *language;
 static char *locales_dir;
 static char *pixmaps_dir;
 
+static UiBool use_xdg_config_home = TRUE;
+
 #endif
 
+static UiBool load_on_startup = TRUE;
+static void *properties_data = NULL;
+static size_t properties_data_length = 0;
+
+void ui_load_properties_file_on_startup(UiBool enable) {
+    load_on_startup = enable;
+}
+
+void ui_set_properties_data(const char *str, size_t len) {
+    if(str && len > 0) {
+        properties_data = malloc(len);
+        memcpy(properties_data, str, len);
+    } else {
+        properties_data = NULL;
+        properties_data_length = 0;
+    }
+}
 
 char* ui_getappdir(void) {
     if(ui_appname() == NULL) {
@@ -73,6 +92,8 @@ char* ui_getappdir(void) {
 #define UI_ENV_HOME "USERPROFILE"
 #endif
 
+#define UI_XDG_CONFIG_HOME_VAR "XDG_CONFIG_HOME"
+
 char* ui_configfile(const char *name) {
     const char *appname = ui_appname();
     if(!appname) {
@@ -102,8 +123,23 @@ char* ui_configfile(const char *name) {
     // on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/
     cxBufferPutString(&buf, "AppData\\Local\\");
 #else
-    // app dir is $HOME/.$APPNAME/
-    cxBufferPut(&buf, '.');
+    if(use_xdg_config_home) {
+        // app dir is $HOME/.config/$APPNAME/
+        char *xdghome = getenv(UI_XDG_CONFIG_HOME_VAR);
+        size_t xdghomelen = xdghome ? strlen(xdghome) : 0;
+        if(xdghome && xdghomelen > 0) {
+            cxBufferSeek(&buf, 0, SEEK_SET);
+            cxBufferPutString(&buf, xdghome);
+            if(xdghome[xdghomelen-1] != '/') {
+                cxBufferPut(&buf, '/');
+            }
+        } else {
+            cxBufferPutString(&buf, ".config/");
+        }
+    } else {
+        cxBufferPut(&buf, '.');
+    }
+    
 #endif
     cxBufferPutString(&buf, appname);
     cxBufferPut(&buf, UI_PATH_SEPARATOR);
@@ -126,10 +162,53 @@ static int ui_mkdir(char *path) {
 #endif
 } 
 
+static int load_properties(FILE *file, CxMap *map) {
+    CxProperties prop;
+    cxPropertiesInitDefault(&prop);
+    char buf[8192];
+    size_t r;
+    CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+    while((r = fread(buf, 1, 8192, file)) > 0) {
+        cxPropertiesFilln(&prop, buf, r);
+        cxstring key;
+        cxstring value;
+        while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+            cxMapPut(map, key, cx_strdup(value).ptr);
+        }
+        if(status > CX_PROPERTIES_OK) {
+            break;
+        }
+    }
+    return status <= CX_PROPERTIES_NO_DATA ? 0 : 1;
+}
+
 void uic_load_app_properties() {
     application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
     application_properties->collection.simple_destructor = free;
     
+    if(!load_on_startup) {
+        if(properties_data) {
+            CxProperties prop;
+            cxPropertiesInitDefault(&prop);
+            
+            CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+            cxPropertiesFilln(&prop, properties_data, properties_data_length);
+            
+            cxstring key;
+            cxstring value;
+            while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+                cxMapPut(application_properties, key, cx_strdup(value).ptr);
+            }
+            
+            if(status > CX_PROPERTIES_NO_DATA) {
+                fprintf(stderr, "Error: cannot load properties: %d\n", (int)status);
+            } else {
+                cxMapRehash(application_properties);
+            }
+        }
+        return;
+    }
+    
     if(!ui_appname()) {
         // applications without name cannot load app properties
         return;
@@ -139,9 +218,37 @@ void uic_load_app_properties() {
     if(!dir) {
         return;
     }
+    size_t len = strlen(dir);
+    if(len < 2) {
+        return;
+    }
     if(ui_mkdir(dir)) {
+        if(errno == ENOENT) {
+            char *parent = strdup(dir);
+            for(int i=len-2;i>=0;i--) {
+                if(parent[i] == '/') {
+                    parent[i] = 0;
+                    break;
+                }
+            }
+            // try creating the parent
+            int err = ui_mkdir(parent);
+            if(err) {
+                fprintf(stderr, "Error: Cannot create directory %s: %s\n", parent, strerror(errno));
+                free(parent);
+                free(dir);
+                return;
+            }
+            free(parent);
+            
+            // try dir again
+            if(!ui_mkdir(dir)) {
+                errno = EEXIST; // success
+            }
+        }
+        
         if(errno != EEXIST) {
-            fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir);
+            fprintf(stderr, "Error: Cannot create directory %s: %s\n", dir, strerror(errno));
             free(dir);
             return;
         }
@@ -159,8 +266,8 @@ void uic_load_app_properties() {
         return;
     }
     
-    if(ucx_properties_load(application_properties, file)) {
-        fprintf(stderr, "Ui Error: Cannot load application properties.\n");
+    if(load_properties(file, application_properties)) {
+        fprintf(stderr, "Error: Cannot load application properties.\n");
     }
     
     fclose(file);
@@ -181,11 +288,13 @@ int uic_store_app_properties() {
     }
     
     int ret = 0;
-    if(ucx_properties_store(application_properties, file)) {
-        fprintf(stderr, "Ui Error: Cannot store application properties.\n");
-        ret = 1;
+    CxMapIterator i = cxMapIterator(application_properties);
+    cx_foreach(CxMapEntry *, entry, i) {
+        fprintf(file, "%.*s = %s\n", (int)entry->key->len, (char*)entry->key->data, (char*)entry->value);
     }
     
+    cxMapRehash(application_properties);
+    
     fclose(file);
     free(path);
     
@@ -313,8 +422,8 @@ int uic_load_language_file(const char *path) {
         return 1;
     }
     
-    if(ucx_properties_load(lang, file)) {
-        fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path);
+    if(load_properties(file, lang)) {
+        fprintf(stderr, "Error: Cannot parse language file: %s.\n", path);
     }
     
     fclose(file);
index a66af6219c3005ca635e11ebd2438d342ee87568..3b4c096509861abc266c59e5698daf5aa16d342e 100644 (file)
@@ -156,7 +156,12 @@ UiQueue* ui_queue_create(void) {
 }
 
 void ui_queue_free(UiQueue *queue) {
-    // TODO
+    // The queue must be empty, we could free UiQueueElm,
+    // but not the payload data
+    pthread_mutex_destroy(&queue->lock);
+    pthread_mutex_destroy(&queue->avlbl_lock);
+    pthread_cond_destroy(&queue->available);
+    free(queue);
 }
 
 void ui_queue_put(UiQueue *queue, void *data) {
index f237290dd0419275d0cd8bf78458b8c08fc95e17..eed1246e0d634c5bc7ca3dfbead09f29035940e2 100644 (file)
@@ -57,15 +57,15 @@ static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups,
     newargs.tooltip = nl_strdup(args->tooltip);\r
     newargs.onclick = args->onclick;\r
     newargs.onclickdata = args->onclickdata;\r
-    newargs.groups = uic_copy_groups(args->groups, ngroups);\r
-    newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+    newargs.states = uic_copy_states(args->states, ngroups);\r
+    newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
     return newargs;\r
 }\r
 \r
 void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) {\r
     UiToolbarItem* item = malloc(sizeof(UiToolbarItem));\r
     item->item.type = UI_TOOLBAR_ITEM;\r
-    item->args = itemargs_copy(args, &item->ngroups, &item->nvstates);\r
+    item->args = itemargs_copy(args, &item->nstates, &item->nvstates);\r
     cxMapPut(toolbar_items, name, item);\r
 }\r
 \r
@@ -78,15 +78,15 @@ static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args
     newargs.varname = nl_strdup(args->varname);\r
     newargs.onchange = args->onchange;\r
     newargs.onchangedata = args->onchangedata;\r
-    newargs.groups = uic_copy_groups(args->groups, ngroups);\r
-    newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+    newargs.states = uic_copy_states(args->states, ngroups);\r
+    newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
     return newargs;\r
 }\r
 \r
 void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) {\r
     UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem));\r
     item->item.type = UI_TOOLBAR_TOGGLEITEM;\r
-    item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates);\r
+    item->args = toggleitemargs_copy(args, &item->nstates, &item->nvstates);\r
     cxMapPut(toolbar_items, name, item);\r
 }\r
 \r
@@ -95,7 +95,7 @@ static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args, size_t *nvstates
     newargs.label = nl_strdup(args->label);\r
     newargs.icon = nl_strdup(args->icon);\r
     newargs.tooltip = nl_strdup(args->tooltip);\r
-    newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+    newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
     return newargs;\r
 }\r
 \r
index 1ac1d553759fa14936bb1751429d81584a188462..9ae60f07cb5b0c04a0cd70b2ae0bcd70441ae49f 100644 (file)
@@ -62,14 +62,14 @@ struct UiToolbarItemI {
 struct UiToolbarItem {\r
     UiToolbarItemI item;\r
     UiToolbarItemArgs args;\r
-    size_t ngroups;\r
+    size_t nstates;\r
     size_t nvstates;\r
 };\r
 \r
 struct UiToolbarToggleItem {\r
     UiToolbarItemI item;\r
     UiToolbarToggleItemArgs args;\r
-    size_t ngroups;\r
+    size_t nstates;\r
     size_t nvstates;\r
 };\r
 \r
index 53fbbec1f4ce41b28a41d7a8d2b00ecef31b3bac..e8680b785f4de29de1cf801b663ece55ab241923 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <cx/list.h>
 #include <cx/array_list.h>
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "types.h"
 #include "context.h"
 #include "../ui/image.h"
@@ -212,6 +212,7 @@ typedef struct {
 
 UiModel* ui_model(UiContext *ctx, ...) {
     UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+    info->ctx = ctx;
     
     va_list ap;
     va_start(ap, ctx);
@@ -251,8 +252,18 @@ UiModel* ui_model(UiContext *ctx, ...) {
 
 #define UI_MODEL_DEFAULT_ALLOC_SIZE 16
 
+
+static void model_notify_observer(UiModel *model, int insert, int delete) {
+    UiModelChangeObserver *obs = model->observer;
+    while(obs) {
+        obs->update(model, obs->userdata, insert, delete);
+        obs = obs->next;
+    }
+}
+
 UiModel* ui_model_new(UiContext *ctx) {
     UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+    info->ctx = ctx;
     info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE;
     info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType));
     info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*));
@@ -260,16 +271,21 @@ UiModel* ui_model_new(UiContext *ctx) {
     return info;
 }
 
-void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) {
+void ui_model_add_column(UiModel *model, UiModelType type, const char *title, int width) {
+    UiContext *ctx = model->ctx;
     if(model->columns >= model->alloc) {
         model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE;
         model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType));
         model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*));
         model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int));
     }
-    model->types[model->columns] = type;
-    model->titles[model->columns] = ui_strdup(ctx, title);
-    model->columnsize[model->columns] = width;
+    int index = model->columns;
+    model->types[index] = type;
+    model->titles[index] = ui_strdup(ctx, title);
+    model->columnsize[index] = width;
+    
+    model_notify_observer(model, index, -1);
+    
     model->columns++;
 }
 
@@ -277,6 +293,7 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) {
     const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
 
     UiModel* newmodel = cxMalloc(a, sizeof(UiModel));
+    newmodel->ctx = ctx;
     *newmodel = *model;
 
     newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType));
@@ -292,11 +309,67 @@ UiModel* ui_model_copy(UiContext *ctx, UiModel* model) {
     return newmodel;
 }
 
-void ui_model_free(UiContext *ctx, UiModel *mi) {
-    const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+void ui_model_ref(UiModel *model) {
+    model->ref++;
+}
+
+void ui_model_unref(UiModel *model) { 
+    if(--model->ref == 0) {
+        ui_model_free(model);
+    }
+}
+
+void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data) {
+    UiModelChangeObserver *observer = ui_malloc(model->ctx, sizeof(UiModelChangeObserver));
+    observer->update = update;
+    observer->userdata = data;
+    observer->next = NULL;
+    
+    if(model->observer) {
+        UiModelChangeObserver *last = model->observer;
+        while(last->next) {
+            last = last->next;
+        }
+        last->next = observer;
+    } else {
+        model->observer = observer;
+    }
+}
+
+void ui_model_remove_observer(UiModel *model, void *data) {
+    if(model->observer) {
+        UiModelChangeObserver *obs = model->observer;
+        UiModelChangeObserver *prev = NULL;
+        while(obs) {
+            if(obs->userdata == data) {
+                // remove
+                if(prev) {
+                    prev->next = obs->next;
+                } else {
+                    model->observer = obs->next;
+                }
+                // free
+                ui_free(model->ctx, obs);
+                break;
+            }
+            prev = obs;
+            obs = obs->next;
+        }
+    }
+}
+
+void ui_model_free(UiModel *mi) {
+    UiContext *ctx = mi->ctx;
+    const CxAllocator* a = ctx->allocator;
     for(int i=0;i<mi->columns;i++) {
         ui_free(ctx, mi->titles[i]);
     }
+    UiModelChangeObserver *obs = mi->observer;
+    while(obs) {
+        UiModelChangeObserver *n = obs->next;
+        cxFree(a, obs);
+        obs = n;
+    }
     cxFree(a, mi->types);
     cxFree(a, mi->titles);
     cxFree(a, mi->columnsize);
@@ -735,9 +808,7 @@ UIEXPORT void ui_list_setselection(UiList *list, int index) {
 }
 
 UIEXPORT void ui_listselection_free(UiListSelection selection) {
-    if (selection.rows) {
-        free(selection.rows);
-    }
+    free(selection.rows);
 }
 
 UIEXPORT UiStr ui_str(char *cstr) {
index 5e01239b0290e15b5f9e18c1bdfdb9c9fbd978d7..8d3b5ddce50f0157fa31926cb065a5f3185c4e5c 100644 (file)
 #include "utils.h"
 #include "properties.h"
 
+#include <stdio.h>
+#include <string.h>
+
 #include <cx/string.h>
+#include <cx/buffer.h>
 
 UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
     cxstring *pathelms;
@@ -83,3 +87,65 @@ void ui_get_window_default_width(int *width, int *height) {
         }
     }
 }
+
+// from UCX json.c
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
+    // note: this function produces the string without enclosing quotes
+    // the reason is that we don't want to allocate memory just for that
+    CxBuffer buf = {0};
+
+    bool all_printable = true;
+    for (size_t i = 0; i < str.length; i++) {
+        unsigned char c = str.ptr[i];
+        bool escape = c < 0x20 || c == '\\' || c == '"'
+            || (escape_slash && c == '/');
+
+        if (all_printable && escape) {
+            size_t capa = str.length + 32;
+            char *space = cxMallocDefault(capa);
+            if (space == NULL) return cx_mutstrn(NULL, 0);
+            cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+            cxBufferWrite(str.ptr, 1, i, &buf);
+            all_printable = false;
+        }
+        if (escape) {
+            cxBufferPut(&buf, '\\');
+            if (c == '\"') {
+                cxBufferPut(&buf, '\"');
+            } else if (c == '\n') {
+                cxBufferPut(&buf, 'n');
+            } else if (c == '\t') {
+                cxBufferPut(&buf, 't');
+            } else if (c == '\r') {
+                cxBufferPut(&buf, 'r');
+            } else if (c == '\\') {
+                cxBufferPut(&buf, '\\');
+            } else if (c == '/') {
+                cxBufferPut(&buf, '/');
+            } else if (c == '\f') {
+                cxBufferPut(&buf, 'f');
+            } else if (c == '\b') {
+                cxBufferPut(&buf, 'b');
+            } else {
+                char code[6];
+                snprintf(code, sizeof(code), "u%04x", (unsigned int) c);
+                cxBufferPutString(&buf, code);
+            }
+        } else if (!all_printable) {
+            cxBufferPut(&buf, c);
+        }
+    }
+    cxmutstr ret;
+    if (all_printable) {
+        // don't copy the string when we don't need to escape anything
+        ret = cx_mutstrn((char*)str.ptr, str.length);
+    } else {
+        ret = cx_mutstrn(buf.space, buf.size);
+    }
+    cxBufferDestroy(&buf);
+    return ret;
+}
+
+cxmutstr ui_escape_string(cxstring str) {
+    return escape_string(str, FALSE);
+}
index 592ad4b107d464284170007d02d50e578d6ef04c..94f3e890df344c409323f711091b12d294eee651 100644 (file)
@@ -31,6 +31,8 @@
 #include "../ui/toolkit.h"
 #include "../ui/text.h"
 
+#include <cx/string.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -43,6 +45,8 @@ UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* re
  */
 void ui_get_window_default_width(int *width, int *height);
 
+cxmutstr ui_escape_string(cxstring str);
+
 
 #ifdef __cplusplus
 }
index 6ce7c9e236cb55ba8e1f7a377c83f9fbe9d3a9ee..37f2f38a9f4de88d7dc584e2b178a5bc0bac1b57 100644 (file)
@@ -30,7 +30,7 @@
 #define UIC_WRAPPER_H
 
 #include "../ui/toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
 
 #ifdef __cplusplus
 extern "C" {
index 6db5141b63ee8d714bb50c44b64fce5ad06a33ac..cf911cffbd865b4b70f5ea77dee0127e4b8b8e58 100644 (file)
@@ -106,7 +106,7 @@ GtkWidget* ui_create_button(
 UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
     GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, 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_set_widget_states(obj->ctx, button, args->states);
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, button, &layout);
@@ -174,9 +174,9 @@ static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) {
 
 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);
+        ui_set_state(event->obj->ctx, event->value);
     } else {
-        ui_unset_group(event->obj->ctx, event->value);
+        ui_unset_state(event->obj->ctx, event->value);
     }
 }
 
@@ -308,9 +308,9 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr
             args->value,
             args->onchange,
             args->onchangedata,
-            args->enable_group);
+            args->enable_state);
     ui_set_name_and_style(widget, args->name, args->style_class);
-    ui_set_widget_groups(obj->ctx, widget, args->groups);
+    ui_set_widget_states(obj->ctx, widget, args->states);
     
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
@@ -351,9 +351,9 @@ static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) {
 
 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);
+        ui_set_state(event->obj->ctx, event->value);
     } else {
-        ui_unset_group(event->obj->ctx, event->value);
+        ui_unset_state(event->obj->ctx, event->value);
     }
 }
 
@@ -370,10 +370,10 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
             args->onchange,
             args->onchangedata,
             (ui_toggled_func)ui_checkbox_enable_state,
-            args->enable_group);
+            args->enable_state);
     
     ui_set_name_and_style(widget, args->name, args->style_class);
-    ui_set_widget_groups(obj->ctx, widget, args->groups);
+    ui_set_widget_states(obj->ctx, widget, args->states);
     
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
@@ -419,7 +419,7 @@ static void switch_changed(
 UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
     GtkWidget *widget = gtk_switch_new();
     ui_set_name_and_style(widget, args->name, args->style_class);
-    ui_set_widget_groups(obj->ctx, widget, args->groups);
+    ui_set_widget_states(obj->ctx, widget, args->states);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     if(var) {
@@ -541,7 +541,7 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
     
     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);
+    ui_set_widget_states(obj->ctx, rbutton, args->states);
     if(rgroup) {
 #if GTK_MAJOR_VERSION >= 4
         if(rg) {
@@ -895,7 +895,7 @@ UIWIDGET ui_linkbutton_create(UiObject *obj, UiLinkButtonArgs *args) {
     }
     
     ui_set_name_and_style(button, args->name, args->style_class);
-    ui_set_widget_groups(obj->ctx, button, args->groups);
+    ui_set_widget_states(obj->ctx, button, args->states);
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, button, &layout);
index 16f7c216a002cc4453089e44f1ff85b673ab493a..a8df7e8c338a972ab7801b68b2d413674cc47d42 100644 (file)
@@ -112,6 +112,21 @@ GtkWidget* ui_subcontainer_create(
     return add;
 }
 
+/* -------------------- Custom Container -------------------- */
+
+void ui_custom_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+    UiCustomContainer *custom = (UiCustomContainer*)ct;
+    custom->add(custom->obj, ct->widget, widget, custom->userdata);
+}
+
+void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata) {
+    UiCustomContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiCustomContainer));
+    container->container.add = ui_custom_container_add;
+    container->container.widget = widget;
+    container->add = add_child;
+    container->userdata = userdata;
+    uic_object_push_container(obj, (UiContainerX*)container);
+}
 
 /* -------------------- Box Container -------------------- */
 UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
@@ -985,6 +1000,8 @@ void ui_headerbar_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLay
             GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
             hb->centerbox = box;
             UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box);
+            UI_HEADERBAR_SHOW_TITLE_WIDGET(ct->widget, TRUE);
+            UI_HEADERBAR_SETTINGS(ct->widget);
         }
         BOX_ADD(hb->centerbox, widget);
     }
index 2f2510c51a3a505fc66400d71aeef7e80dd46f6e..f224aadd0736034a28f4cedd8c2c9581e83dff10 100644 (file)
@@ -61,6 +61,13 @@ struct UiContainerPrivate {
     int close;
 };
 
+typedef struct UiCustomContainer {
+    UiContainerPrivate container;
+    UiObject *obj;
+    ui_addwidget_func add;
+    void *userdata;
+} UiCustomContainer;
+
 typedef struct UiBoxContainer {
     UiContainerPrivate container;
     UiSubContainerType type;
index b411191cae49057d08a708463a81d830c608cf7a..9e61e4e109b40f09e4877381cf7966bf84ab32d2 100644 (file)
@@ -79,7 +79,7 @@ UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
 #endif
     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);
+    ui_set_widget_states(obj->ctx, spin, args->states);
     
     if(args->width > 0) {
         gtk_widget_set_size_request(spin, args->width, -1);
index 33fd186ae63c885f8f9fa8c7192d0ef2148ee496..3ae10256ffe353e88e20d49662568c63063df37e 100644 (file)
@@ -107,14 +107,14 @@ UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) {
             widget,
             "draw",
             G_CALLBACK(draw_callback),
-            NULL);
+            drawingarea);
 #endif
     
     g_signal_connect(
-                widget,
-                "destroy",
-                G_CALLBACK(destroy_drawingarea),
-                drawingarea);
+            widget,
+            "destroy",
+            G_CALLBACK(destroy_drawingarea),
+            drawingarea);
     
     return widget;
 }
index 0a9bd3e7adc51de04a44ad02a61f2979f6b826f9..ef1d9c7e151fa79cf0203d2906eb32dab19031b0 100644 (file)
@@ -164,7 +164,7 @@ void ui_add_headerbar_item(
         enum UiToolbarPos pos)
 {
     GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE);
-    ui_set_widget_groups(obj->ctx, button, item->args.groups);
+    ui_set_widget_states(obj->ctx, button, item->args.states);
     ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
     WIDGET_ADD_CSS_CLASS(button, "flat");
     headerbar_add(headerbar, box, button, pos);
@@ -178,7 +178,7 @@ void ui_add_headerbar_toggleitem(
         enum UiToolbarPos pos)
 {
     GtkWidget *button = gtk_toggle_button_new();
-    ui_set_widget_groups(obj->ctx, button, item->args.groups);
+    ui_set_widget_states(obj->ctx, button, item->args.states);
     ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
     WIDGET_ADD_CSS_CLASS(button, "flat");
     ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0);
index 93028ea515f7cbfac7aa9f6645b8de64540e5b83..392eae7fb581045a5e9cfe7fd3d88dcba864fa8e 100644 (file)
@@ -46,15 +46,20 @@ extern "C" {
 #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)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) adw_header_bar_set_show_title(ADW_HEADER_BAR(h), b)
+#define UI_HEADERBAR_SETTINGS(h) adw_header_bar_set_centering_policy(ADW_HEADER_BAR(h), ADW_CENTERING_POLICY_LOOSE)
 #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)
+#define UI_HEADERBAR_SETTINGS(h)
 #if GTK_MAJOR_VERSION >= 4
 #define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
 #else
 #define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
 #endif
 #endif
     
index e60dbdd46deaca14043eff25cefaa765d7294ea2..8181126301707af9236baa3e9d096c49920f1f79 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "container.h"
 #include "menu.h"
+#include "widget.h"
 #include "../common/context.h"
 #include "../common/object.h"
 
@@ -67,8 +68,14 @@ UIWIDGET ui_imageviewer_create(UiObject *obj, UiImageViewerArgs *args) {
     GtkWidget *drawingarea = gtk_drawing_area_new();
     GtkWidget *toplevel;
     GtkWidget *widget = drawingarea;
-      
-    gtk_widget_set_size_request(drawingarea, 100, 100);
+    
+    int width = args->width;
+    int height = args->height;
+    if(width == 0 && height == 0) {
+        width = 100;
+        height = 100;
+    }
+    ui_widget_size_request(drawingarea, width, height);
     
 #if GTK_MAJOR_VERSION < 4
     GtkWidget *eventbox = gtk_event_box_new();
index 8e2d21730ac101a03f53bbc1b1f94e9f2cf4d366..7a08ffccd764f455e4073c58bbb6648dce901eb4 100644 (file)
@@ -80,6 +80,7 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
     memset(tableview, 0, sizeof(UiListView));
     tableview->obj = obj;
     tableview->model = args->model;
+    tableview->multiselection = args->multiselection;
     tableview->onactivate = args->onactivate;
     tableview->onactivatedata = args->onactivatedata;
     tableview->onselection = args->onselection;
@@ -98,6 +99,11 @@ static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
     tableview->onsave = args->onsave;
     tableview->onsavedata = args->onsavedata;
     
+#if GTK_CHECK_VERSION(4, 0, 0)
+    tableview->coldata.listview = tableview;
+    tableview->coldata.column = 0;
+#endif
+    
     if(args->getvalue2) {
         tableview->getvalue = args->getvalue2;
         tableview->getvaluedata = args->getvalue2data;
@@ -200,7 +206,7 @@ static void cell_entry_activate(
 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiModel *model = col->listview->model;
-    UiModelType type = model->types[col->model_column];
+    UiModelType type = model->types[col->column];
     if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
         GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
         GtkWidget *image = gtk_image_new();
@@ -281,16 +287,17 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
     UiColData *col = userdata;
     UiList *list = col->listview->var ? col->listview->var->value : NULL;
     UiListView *listview = col->listview;
+    int datacolumn = listview->columns[col->column];
      
     ObjWrapper *obj = gtk_list_item_get_item(item);
     UiModel *model = col->listview->model;
-    UiModelType type = model->types[col->model_column];
+    UiModelType type = model->types[col->column];
     
     // cache the GtkListItem
     CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
     UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
     if(row) {
-        if(row->items[col->model_column] == NULL) {
+        if(row->items[col->column] == NULL) {
             row->bound++;
         }
     } else {
@@ -298,10 +305,10 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
         cxMapPut(listview->bound_rows, row_key, row);
         row->bound = 1;
     }
-    row->items[col->model_column] = item;
+    row->items[col->column] = item;
     
     UiBool freevalue = FALSE;
-    void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
+    void *data = listview->getvalue(list, obj->data, obj->i, datacolumn, listview->getvaluedata, &freevalue);
     GtkWidget *child = gtk_list_item_get_child(item);
     
     PangoAttrList *attributes = NULL;
@@ -319,7 +326,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
             }
         }
         
-        int style_col = col->data_column;
+        int style_col = datacolumn;
         if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
             style_col++; // col->data_column is the icon, we need the next col for the label
         }
@@ -363,7 +370,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
             
         }
         case UI_ICON_TEXT_FREE: {
-            void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue);
+            void *data2 = listview->getvalue(list, obj->data, obj->i, datacolumn+1, listview->getvaluedata, &freevalue);
             if(type == UI_ICON_TEXT_FREE) {
                 freevalue = TRUE;
             }
@@ -387,7 +394,7 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
             if(entry) {
                 entry->listview = col->listview;
                 entry->row = obj->i;
-                entry->col = col->data_column;
+                entry->col = datacolumn;
                 entry->previous_value = strdup(data);
             }
             ENTRY_SET_TEXT(child, data);
@@ -405,13 +412,13 @@ static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, g
     }
 }
 
-static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
+static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {  
     ObjWrapper *obj = gtk_list_item_get_item(item);
     UiListView *listview = col->listview;
     CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
     UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
     if(row) {
-        row->items[col->model_column] = NULL;
+        row->items[col->column] = NULL;
         row->bound--;
         if(row->bound == 0) {
             cxMapRemove(listview->bound_rows, row_key);
@@ -457,17 +464,16 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     }
     
     listview->numcolumns = 1;
-    listview->columns = malloc(sizeof(UiColData));
-    listview->columns->listview = listview;
-    listview->columns->data_column = 0;
-    listview->columns->model_column = 0;
+    listview->columns = malloc(sizeof(int));
+    listview->columns[0] = 0;
     
     listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
     listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
      
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
-    g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
-    g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+    g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata);
+    g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata);
+    g_signal_connect(factory, "unbind", G_CALLBACK(column_factory_unbind), &listview->coldata);
     
     GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
     GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory);
@@ -540,7 +546,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     return scroll_area;
 }
 
-UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
     // to simplify things and share code with ui_tableview_create, we also
     // use a UiModel for the listview
     UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
@@ -554,17 +560,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
     }
     
     listview->numcolumns = 1;
-    listview->columns = malloc(sizeof(UiColData));
-    listview->columns->listview = listview;
-    listview->columns->data_column = 0;
-    listview->columns->model_column = 0;
+    listview->columns = malloc(sizeof(int));
+    listview->columns[0] = 0;
     
     listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
     listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
     
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
-    g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
-    g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+    g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata);
+    g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata);
     
     GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL);
     gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory);
@@ -591,8 +595,8 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
         
         list->obj = listview;
         list->update = ui_listview_update2;
-        list->getselection = ui_combobox_getselection;
-        list->setselection = ui_combobox_setselection;
+        list->getselection = ui_dropdown_getselection;
+        list->setselection = ui_dropdown_setselection;
         
         ui_update_liststore(ls, list);
     } else if (args->static_elements && args->static_nelm > 0) {
@@ -619,10 +623,34 @@ void ui_listview_select(UIWIDGET listview, int index) {
     gtk_selection_model_select_item(model, index, TRUE);
 }
     
-void ui_combobox_select(UIWIDGET dropdown, int index) {
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
     gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index);
 }
 
+static void add_column(UiListView *tableview, int index) {
+    UiModel *model = tableview->model;
+    
+    UiColData *col = malloc(sizeof(UiColData));
+    col->listview = tableview;
+    col->column = index;
+    
+    GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
+    g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
+    g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
+    g_object_set_data_full(G_OBJECT(factory), "coldata", col, (GDestroyNotify)free);
+
+    GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[index], factory);
+    gtk_column_view_column_set_resizable(column, true);
+    gtk_column_view_insert_column(GTK_COLUMN_VIEW(tableview->widget), index, column);
+
+    int size = model->columnsize[index];
+    if(size > 0) {
+        gtk_column_view_column_set_fixed_width(column, size);
+    } else if(size < 0) {
+        gtk_column_view_column_set_expand(column, TRUE);
+    }
+}
+
 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
     //g_list_store_append(ls, v1);
@@ -650,9 +678,13 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     
     // create columns from UiModel
     UiModel *model = args->model;
-    int columns = model ? model->columns : 0;
+    int columns = 0;
+    if(model) {
+        columns = model->columns;
+        ui_model_add_observer(model, ui_listview_update_model, tableview);
+    }
     
-    tableview->columns = calloc(columns, sizeof(UiColData));
+    tableview->columns = calloc(columns, sizeof(int));
     tableview->numcolumns = columns;
     
     tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
@@ -660,30 +692,14 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     
     int addi = 0;
     for(int i=0;i<columns;i++) {
-        tableview->columns[i].listview = tableview;
-        tableview->columns[i].model_column = i;
-        tableview->columns[i].data_column = i+addi;
+        tableview->columns[i] = i+addi;
         
         if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
             // icon+text has 2 data columns
             addi++;
         }
         
-        GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
-        UiColData *col = &tableview->columns[i]; 
-        g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
-       g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
-        
-        GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
-        gtk_column_view_column_set_resizable(column, true);
-        gtk_column_view_append_column(GTK_COLUMN_VIEW(view), column);
-        
-        int size = model->columnsize[i];
-        if(size > 0) {
-            gtk_column_view_column_set_fixed_width(column, size);
-        } else if(size < 0) {
-            gtk_column_view_column_set_expand(column, TRUE);
-        }
+        add_column(tableview, i);
     }
     
     // bind listview to list
@@ -734,6 +750,49 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     return scroll_area;
 }
 
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index) {
+    UiListView *listview = userdata;
+    if(insert_index >= listview->numcolumns) {
+        listview->numcolumns = insert_index+1;
+        listview->columns = realloc(listview->columns, listview->numcolumns * sizeof(UiColData));
+    }
+    
+    gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), NULL);
+    cxMapClear(listview->bound_rows);
+    
+    if(insert_index) {
+        int prev = 0;
+        if(insert_index > 0) {
+            prev = listview->columns[insert_index-1];
+        }
+        listview->columns[insert_index] = prev+1;
+        add_column(listview, insert_index);
+        
+        if(insert_index+1 < listview->numcolumns) {
+            // the data index of trailing columns must be adjusted
+            UiModelType type = model->types[insert_index];
+            int add = 1;
+            if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+                add++;
+            }
+            for(int i=insert_index+1;i<listview->numcolumns;i++) {
+                listview->columns[i] += add;
+            }
+        }
+    } // TODO: delete_index
+    
+    GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
+    GtkSelectionModel *selection_model = create_selection_model(listview, ls, listview->multiselection);
+    gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), selection_model);
+    listview->selectionmodel = selection_model;
+    listview->liststore = ls;
+    
+    if(listview->var) {
+        UiList *list = listview->var->value;
+        ui_list_update(list);
+    }
+}
+
 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
     UiListSelection sel = { 0, NULL };
     GtkBitset *bitset = gtk_selection_model_get_selection(model);
@@ -862,6 +921,7 @@ void ui_listview_update2(UiList *list, int i) {
     UiListView *view = list->obj;
     view->current_row = -1;
     if(i < 0) {
+        cxMapClear(view->bound_rows);
         ui_update_liststore(view->liststore, list);
     } else {
         void *value = list->get(list, i);
@@ -874,9 +934,12 @@ void ui_listview_update2(UiList *list, int i) {
             CxHashKey row_key = cx_hash_key(&i, sizeof(int));
             UiRowItems *row = cxMapGet(view->bound_rows, row_key);
             if(row) {
+                UiColData coldata;
+                coldata.listview = view;
                 for(int c=0;c<view->numcolumns;c++) {
                     if(row->items[c] != NULL) {
-                        column_factory_bind(NULL, row->items[c], &view->columns[c]);
+                        coldata.column = c;
+                        column_factory_bind(NULL, row->items[c], &coldata);
                     }
                 }
             }
@@ -916,7 +979,7 @@ void ui_listview_setselection2(UiList *list, UiListSelection selection) {
     ui_setop_enable(FALSE);
 }
 
-UiListSelection ui_combobox_getselection(UiList *list) {
+UiListSelection ui_dropdown_getselection(UiList *list) {
     UiListView *view = list->obj;
     guint selection = gtk_drop_down_get_selected(GTK_DROP_DOWN(view->widget));
     UiListSelection sel = { 0, NULL };
@@ -928,7 +991,7 @@ UiListSelection ui_combobox_getselection(UiList *list) {
     return sel;
 }
 
-void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
     ui_setop_enable(TRUE);
     UiListView *view = list->obj;
     if(selection.count > 0) {
@@ -1157,7 +1220,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     // 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);
+    ui_set_widget_states(obj->ctx, view, args->states);
     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);
@@ -1273,7 +1336,7 @@ void ui_listview_select(UIWIDGET listview, int index) {
     //g_object_unref(path);
 }
     
-void ui_combobox_select(UIWIDGET dropdown, int index) {
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
     gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
 }
 
@@ -1515,16 +1578,16 @@ void ui_listview_setselection(UiList *list, UiListSelection selection) {
 
 
 
-/* --------------------------- ComboBox ---------------------------  */
+/* --------------------------- Dropdown ---------------------------  */
 
-UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
     GtkWidget *combobox = gtk_combo_box_new();
     if(args->width > 0) {
         gtk_widget_set_size_request(combobox, args->width, -1);
     }
     
     ui_set_name_and_style(combobox, args->name, args->style_class);
-    ui_set_widget_groups(obj->ctx, combobox, args->groups);
+    ui_set_widget_states(obj->ctx, combobox, args->states);
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, combobox, &layout);
@@ -1545,8 +1608,8 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
     if(var) {
         listview->var = var;
         list->update = ui_combobox_modelupdate;
-        list->getselection = ui_combobox_getselection;
-        list->setselection = ui_combobox_setselection;
+        list->getselection = ui_dropdown_getselection;
+        list->setselection = ui_dropdown_setselection;
         list->obj = listview;
         list->update(list, -1);
     } else if(args->static_nelm > 0) {
@@ -1623,7 +1686,7 @@ void ui_combobox_modelupdate(UiList *list, int i) {
     g_object_unref(store);
 }
 
-UiListSelection ui_combobox_getselection(UiList *list) {
+UiListSelection ui_dropdown_getselection(UiList *list) {
     UiListView *combobox = list->obj;
     UiListSelection ret;
     ret.rows = malloc(sizeof(int*));
@@ -1632,7 +1695,7 @@ UiListSelection ui_combobox_getselection(UiList *list) {
     return ret;
 }
 
-void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
     ui_setop_enable(TRUE);
     UiListView *combobox = list->obj;
     if(selection.count > 0) {
@@ -2046,6 +2109,10 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) {
     if(v->var) {
         ui_destroy_boundvar(v->obj->ctx, v->var);
     }
+    if(v->model) {
+        ui_model_remove_observer(v->model, v);
+        ui_model_unref(v->model);
+    }
     if(v->elements) {
         for(int i=0;i<v->nelm;i++) {
             free(v->elements[i]);
@@ -2184,7 +2251,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
     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_set_widget_states(obj->ctx, listbox, args->states);
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, scroll_area, &layout);
index 635ac58f258635bc0d533d8dab99b37755134953..ef828d47131e48aeeb34dff6950a7aaa6aba9ac8 100644 (file)
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef TREE_H
-#define        TREE_H
+#ifndef LIST_H
+#define        LIST_H
 
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "toolkit.h"
 
 #include <cx/array_list.h>
@@ -38,6 +38,7 @@
 extern "C" {
 #endif
     
+typedef struct UiListView UiListView;
 typedef struct UiColData UiColData;
 
 #if GTK_CHECK_VERSION(4, 10, 0)
@@ -47,11 +48,17 @@ typedef struct UiRowItems {
 } UiRowItems;
 #endif
 
-typedef struct UiListView {
+struct UiColData {
+    UiListView *listview;
+    int column;
+};
+
+struct UiListView {
     UiObject          *obj;
     GtkWidget         *widget;
     UiVar             *var;
     UiModel           *model;
+    UiBool            multiselection;
     ui_getvaluefunc2  getvalue;
     void              *getvaluedata;
     ui_getstylefunc   getstyle;
@@ -65,8 +72,9 @@ typedef struct UiListView {
     CxMap             *bound_rows;
     GListStore        *liststore;
     GtkSelectionModel *selectionmodel;
-    UiColData         *columns;
+    int               *columns;
     int               numcolumns;
+    UiColData         coldata;
     PangoAttrList     *current_row_attributes;
 #else
     int               style_offset;
@@ -85,12 +93,6 @@ typedef struct UiListView {
     void              *onsavedata;
     UiListSelection   selection;
     
-} UiListView;
-
-struct UiColData {
-    UiListView *listview;
-    int model_column;
-    int data_column;
 };
 
 typedef struct UiTreeEventData {
@@ -136,6 +138,7 @@ struct UiListBox {
 void ui_update_liststore(GListStore *liststore, UiList *list);
 void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm);
 
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index);
 void ui_listview_update2(UiList *list, int i);
 UiListSelection ui_listview_getselection2(UiList *list);
 void ui_listview_setselection2(UiList *list, UiListSelection selection);
@@ -155,6 +158,7 @@ UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks
 
 GtkWidget* ui_get_tree_widget(UIWIDGET widget);
 
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index);
 void ui_listview_update(UiList *list, int i);
 UiListSelection ui_listview_getselection(UiList *list);
 void ui_listview_setselection(UiList *list, UiListSelection selection);
@@ -186,8 +190,8 @@ UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui
 GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, 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);
+UiListSelection ui_dropdown_getselection(UiList *list);
+void ui_dropdown_setselection(UiList *list, UiListSelection selection);
 
 void ui_listbox_dynamic_update(UiList *list, int i);
 void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel);
@@ -205,5 +209,5 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user
 }
 #endif
 
-#endif /* TREE_H */
+#endif /* LIST_H */
 
index 74597ae6462f5ffdea62f85ce0471a1c9b2e8ee4..44d32ca4989e6b7a97131a582b65bb3140f9e3e6 100644 (file)
@@ -129,11 +129,11 @@ void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObje
     
     gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
     
-    if(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);
-        cxListFree(groups);
+    if(i->states) {
+        CxList *states = cxArrayListCreateSimple(sizeof(int), i->nstates);
+        cxListAddArray(states, i->states, i->nstates);
+        uic_add_state_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, states);
+        cxListFree(states);
     }
 }
 
@@ -462,10 +462,10 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject
     g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
     g_object_unref(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);
+    if(i->states) {
+        CxList *groups = cxArrayListCreateSimple(sizeof(int), i->nstates);
+        cxListAddArray(groups, i->states, i->nstates);
+        uic_add_state_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
         cxListFree(groups);
     }
     
index ccace09b1001e1f28123834a9c2da26e63b89958..cb52e4a90363a8191180552512ca4ac26e8bf7e9 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "text.h"
 #include "container.h"
+#include "widget.h"
 
 #include <cx/printf.h>
 
@@ -53,9 +54,9 @@ static void selection_handler(
         int sel = gtk_text_buffer_get_selection_bounds (buf, &begin, &end);
         if(sel != textview->last_selection_state) {
             if(sel) {
-                ui_set_group(textview->ctx, UI_GROUP_SELECTION);
+                ui_set_state(textview->ctx, UI_GROUP_SELECTION);
             } else {
-                ui_unset_group(textview->ctx, UI_GROUP_SELECTION);
+                ui_unset_state(textview->ctx, UI_GROUP_SELECTION);
             }
         }
         textview->last_selection_state = sel;
@@ -112,7 +113,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
     
     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);
+    ui_set_widget_states(obj->ctx, text_area, args->states);
     
     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_area), GTK_WRAP_WORD_CHAR);
     g_signal_connect(
@@ -144,17 +145,7 @@ UIWIDGET ui_textarea_create(UiObject *obj, UiTextAreaArgs *args) {
             GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS  
     SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area);
     
-    if(args->width > 0 || args->height > 0) {
-        int width = args->width;
-        int height = args->height;
-        if(width == 0) {
-            width = -1;
-        }
-        if(height == 0) {
-            height = -1;
-        }
-        gtk_widget_set_size_request(scroll_area, width, height);
-    }
+    ui_widget_size_request(scroll_area, args->width, args->height);
     
     // font and padding
     //PangoFontDescription *font;
@@ -612,7 +603,7 @@ void ui_text_redo(UiText *value) {
 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);
+    ui_set_widget_states(obj->ctx, textfield, args->states);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
     
@@ -966,6 +957,10 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
     pathtf->stack = gtk_stack_new();
     gtk_widget_set_name(pathtf->stack, "path-textfield-box");
     
+    if(args->width > 0) {
+        gtk_widget_set_size_request(pathtf->stack, args->width, -1);
+    }
+    
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, pathtf->stack, &layout);
@@ -1137,6 +1132,10 @@ UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args) {
             G_CALLBACK(ui_path_textfield_destroy),
             pathtf);
     
+    if(args->width > 0) {
+        gtk_widget_set_size_request(eventbox, args->width, -1);
+    }
+    
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, eventbox, &layout);
index 64b221ab7307bd8f529f55cda6c81442ffd37e5c..0e195ff9046cd28482e2805d0c9f77aa5575e11d 100644 (file)
@@ -139,7 +139,7 @@ void add_toolitem_widget(GtkToolbar *tb, UiToolbarItem *item, UiObject *obj) {
     }
     gtk_tool_item_set_is_important(button, TRUE);
     
-    ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+    ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates);
     
     if(item->args.onclick) {
         UiEventData *event = cxMalloc(
@@ -181,7 +181,7 @@ void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObj
     if(item->args.tooltip) {
         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
     }
-    ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+    ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER);
     if(var) {
@@ -394,7 +394,7 @@ void add_headerbar_item_widget(GtkHeaderBar *hb, UiToolbarItem *item, UiObject *
     if(item->args.icon) {
         ui_button_set_icon_name(button, item->args.icon);
     }
-    ui_set_widget_groups(obj->ctx, button, item->args.groups);
+    ui_set_widget_states(obj->ctx, button, item->args.states);
     
     gtk_header_bar_pack_start(hb, button);
     
index 3c5557ddedc36edc5fc4a31b346b9723dd69005e..e0a30c2ab697c4f535db78eb41a87f76c3413bba 100644 (file)
@@ -39,6 +39,7 @@
 #include "../common/menu.h"
 #include "../common/toolbar.h"
 #include "../common/threadpool.h"
+#include "../common/app.h"
 
 #include <cx/string.h>
 #include <cx/printf.h>
@@ -51,13 +52,6 @@ UI_APPLICATION app;
 
 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;
 
@@ -95,30 +89,13 @@ 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_app_exit_on_shutdown(UiBool exitapp) {
     exit_on_shutdown = exitapp;
 }
 
 #ifdef UI_APPLICATION
 static void app_startup(GtkApplication* app, gpointer userdata) {
-    if(startup_func) {
-        startup_func(NULL, startup_data);
-    }
+    uic_application_startup(NULL);
 }
 
 static void app_activate(GtkApplication* app, gpointer userdata) {
@@ -126,9 +103,7 @@ static void app_activate(GtkApplication* app, gpointer userdata) {
 }
 
 static void app_shutdown(GtkApplication *app, gpointer userdata) {
-    if(exit_func) {
-        exit_func(NULL, exit_data);
-    }
+    uic_application_exit(NULL);
     ui_app_save_settings();
 }
 
@@ -148,13 +123,9 @@ void ui_main() {
     
     free(appid.ptr);
 #else
-    if(startup_func) {
-        startup_func(NULL, startup_data);
-    }
+    uic_application_startup(NULL);
     gtk_main();
-    if(exit_func) {
-        exit_func(NULL, exit_data);
-    }
+    uic_application_exit(NULL);
     ui_app_save_settings();
 #endif
     if(exit_on_shutdown) {
@@ -175,7 +146,7 @@ GtkApplication* ui_get_application() {
 void ui_show(UiObject *obj) {
     gboolean visible = gtk_widget_is_visible(obj->widget);
     
-    uic_check_group_widgets(obj->ctx);
+    uic_check_state_widgets(obj->ctx);
 #if GTK_MAJOR_VERSION >= 4
     gtk_window_present(GTK_WINDOW(obj->widget));
 #elif GTK_MAJOR_VERSION <= 3
@@ -268,9 +239,9 @@ void ui_set_visible(UIWIDGET widget, UiBool visible) {
     gtk_widget_set_visible(widget, visible);
 #else
     if(visible) {
-        gtk_widget_set_no_show_all(widget, FALSE);
         gtk_widget_show_all(widget);
     } else {
+        gtk_widget_set_no_show_all(widget, FALSE);
         gtk_widget_hide(widget);
     }
 #endif
@@ -534,17 +505,17 @@ void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *styl
     }
 }
 
-void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) {
-    if(!groups) {
+void ui_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states) {
+    if(!states) {
         return;
     }
-    size_t ngroups = uic_group_array_size(groups);
-    ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+    size_t nstates = uic_state_array_size(states);
+    ui_set_widget_nstates(ctx, widget, states, nstates);
 }
 
-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);
+void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates) {
+    if(nstates > 0) {
+        uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, states, nstates);
         ui_set_enabled(widget, FALSE);
     }
 }
@@ -553,14 +524,14 @@ void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const in
     if(!states) {
         return;
     }
-    size_t nstates = uic_group_array_size(states);
+    size_t nstates = uic_state_array_size(states);
     ui_set_widget_nvisibility_states(ctx, widget, states, nstates);
 }
 
 
 void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) {
     if(ngroups > 0) {
-        uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups);
+        uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups);
         ui_set_visible(widget, FALSE);
     }
 }
index cfa25bb34e23fbb5e282e8abdd8fe7cf2dee1ac9..8d1ee573e5ad881b911077538004ba5ae5e555d3 100644 (file)
@@ -178,8 +178,8 @@ GtkApplication* ui_get_application();
 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_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states);
+void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates);
 void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states);
 void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups);
 
index f8f57185a091be6a607b389c0bbba4f567742266..6a1f319f5add611c0bb94789570812d9788509f7 100644 (file)
@@ -30,6 +30,7 @@
 #include "container.h"
 
 #include "webview.h"
+#include "widget.h"
 
 #ifdef UI_WEBVIEW
 
@@ -38,6 +39,8 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
     
     ui_set_name_and_style(webview, args->name, args->style_class);
     
+    ui_widget_size_request(webview, args->width, args->height);
+    
     UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
     if(var) {
         WebViewData *data = malloc(sizeof(WebViewData));
@@ -57,7 +60,7 @@ UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) {
         }
     }
     
-    ui_set_widget_groups(obj->ctx, webview, args->groups);
+    ui_set_widget_states(obj->ctx, webview, args->states);
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, webview, &layout);
index 06ef1b2406395fd91e7c168aca136329c48b2dd2..95cbb121c75e34d95b402c541ebd03b905d5156e 100644 (file)
 
 #include "../common/object.h"
 
-UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+void ui_widget_size_request(UIWIDGET w, int width, int height) {
+    if(width > 0 || height > 0) {
+        if(width == 0) {
+            width = -1;
+        }
+        if(height == 0) {
+            height = -1;
+        }
+        gtk_widget_set_size_request(w, width, height);
+    }
+}
+
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
     UIWIDGET widget = create_widget(obj, args, userdata);
     
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
index 57c7314d3cc2fe91290817fd6092b73a36e74a23..7ee6ed4704f7003aa4c7b94b2303280f6050c30d 100644 (file)
 extern "C" {
 #endif
 
-
+/*
+ * Sets a widget width/height.
+ * 
+ * If wdith or height is 0, the dimension is not changed
+ */
+void ui_widget_size_request(UIWIDGET w, int width, int height);
 
 
 #ifdef __cplusplus
index 4b103c30859fbb4b0d485031b9ffeb71b6a3dfc3..2081de29c9879278d47a7eb95fdc8dc0bc9185fb 100644 (file)
@@ -142,7 +142,7 @@ static void save_window_splitview_pos(GtkWidget *widget, void *unused) {
     ui_set_property("ui.window.splitview.pos", buf);
 }
 
-static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) {
+static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitview, UiBool simple) {
     UiObject *obj = uic_object_new_toplevel();
    
 #ifdef UI_LIBADWAITA
@@ -153,8 +153,6 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
     obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 #endif
     
-    obj->window = window_data;
-    
 #if GTK_CHECK_VERSION(4, 0, 0)
     obj->ctx->action_map = G_ACTION_MAP(obj->widget);
 #endif
@@ -197,6 +195,21 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
             obj);
 #endif
     
+    int splitview_pos = 0;
+    if(splitview) {
+        const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
+        splitview_pos= splitview_window_default_pos;
+        if(splitview_pos < 0) {
+            splitview_pos = window_width / 2;
+        }
+        if(splitview_pos_str && splitview_window_use_prop) {
+            int sv_pos = atoi(splitview_pos_str);
+            if(sv_pos > 0) {
+                splitview_pos = sv_pos;
+            }
+        }
+    }
+    
     GtkWidget *vbox = ui_gtk_vbox_new(0);
 #ifdef UI_LIBADWAITA
     GtkWidget *toolbar_view = adw_toolbar_view_new();
@@ -215,18 +228,7 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
                 G_CALLBACK(save_window_splitview_pos),
                 NULL);
         
-        const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
-        int pos = splitview_window_default_pos;
-        if(pos < 0) {
-            pos = window_width / 2;
-        }
-        if(splitview_pos_str && splitview_window_use_prop) {
-            int splitview_pos = atoi(splitview_pos_str);
-            if(splitview_pos > 0) {
-                pos = splitview_pos;
-            }
-        }
-        gtk_paned_set_position(GTK_PANED(content), pos);
+        gtk_paned_set_position(GTK_PANED(content), splitview_pos);
         
         GtkWidget *right_panel = adw_toolbar_view_new();
         GtkWidget *right_vbox = ui_gtk_vbox_new(0);
@@ -348,6 +350,7 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
         
         if(splitview) {
             GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+            gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos);
             gtk_paned_add2(GTK_PANED(paned), content_paned);
             
             GtkWidget *right_content_box = ui_gtk_vbox_new(0);
@@ -389,20 +392,20 @@ static UiObject* create_window(const char *title, void *window_data, UiBool side
 }
 
 
-UiObject* ui_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, FALSE, FALSE);
+UiObject* ui_window(const char *title) {
+    return create_window(title, FALSE, FALSE, FALSE);
 }
 
-UiObject *ui_sidebar_window(const char *title, void *window_data) {
-    return create_window(title, window_data, TRUE, FALSE, FALSE);
+UiObject *ui_sidebar_window(const char *title) {
+    return create_window(title, TRUE, FALSE, FALSE);
 }
 
-UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
-    return create_window(title, NULL, sidebar, TRUE, FALSE);
+UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
+    return create_window(title, sidebar, TRUE, FALSE);
 }
 
-UiObject* ui_simple_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, FALSE, TRUE);
+UiObject* ui_simple_window(const char *title) {
+    return create_window(title, FALSE, FALSE, TRUE);
 }
 
 void ui_window_size(UiObject *obj, int width, int height) {
index 6393e761d0edc7c5271e16234b8c1b32b8f1587e..54b5ed2427a590f10f7f3369aa450ca2b254c807 100644 (file)
@@ -61,7 +61,7 @@ UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs *args) {
     XtManageChild(button);
     ui_container_add(ctn, button);
     
-    ui_set_widget_groups(obj->ctx, button, args->groups);
+    ui_set_widget_groups(obj->ctx, button, args->states);
     
     if(args->onclick) {
         UiEventData *eventdata = malloc(sizeof(UiEventData));
@@ -118,9 +118,9 @@ UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs *args) {
     XtManageChild(button);
     ui_container_add(ctn, button);
     
-    ui_set_widget_groups(obj->ctx, button, args->groups);
+    ui_set_widget_groups(obj->ctx, button, args->states);
     
-    ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+    ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_state);
     
     XmStringFree(label);
     return button;
@@ -146,9 +146,9 @@ UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs *args) {
     XtManageChild(button);
     ui_container_add(ctn, button);
     
-    ui_set_widget_groups(obj->ctx, button, args->groups);
+    ui_set_widget_groups(obj->ctx, button, args->states);
     
-    ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+    ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_state);
     
     XmStringFree(label);
     return button;
@@ -162,9 +162,9 @@ static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButton
     if(event->value > 0) {
         // button in configured to enable/disable states
         if(tb->set) {
-            ui_set_group(event->obj->ctx, event->value);
+            ui_set_state(event->obj->ctx, event->value);
         } else {
-            ui_unset_group(event->obj->ctx, event->value);
+            ui_unset_state(event->obj->ctx, event->value);
         }
     }
     
@@ -249,9 +249,9 @@ static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonC
     if(event->value > 0) {
         // button in configured to enable/disable states
         if(tb->set) {
-            ui_set_group(event->obj->ctx, event->value);
+            ui_set_state(event->obj->ctx, event->value);
         } else {
-            ui_unset_group(event->obj->ctx, event->value);
+            ui_unset_state(event->obj->ctx, event->value);
         }
     }
     
@@ -365,7 +365,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
     XtManageChild(button);
     ui_container_add(ctn, button);
     
-    ui_set_widget_groups(obj->ctx, button, args->groups);
+    ui_set_widget_groups(obj->ctx, button, args->states);
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
     if(var) {
@@ -402,7 +402,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
     event->userdata = args->onchangedata;
     event->observers = NULL;
     event->var = var;
-    event->value = args->enable_group;
+    event->value = args->enable_state;
     XtAddCallback(
             button,
             XmNvalueChangedCallback,
index d21a400980eb770f5763cf83f7f7bff4ac1fe9b2..e8ce311ccafbfecbf88901e2a11e4e2dc80f68b7 100644 (file)
@@ -86,7 +86,7 @@ UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
     XtManageChild(spinbox);
     ui_container_add(ctn, spinbox);
     
-    ui_set_widget_groups(obj->ctx, spinbox, args->groups);
+    ui_set_widget_groups(obj->ctx, spinbox, args->states);
     
     WidgetList children;
     Cardinal num_children;
index b1e96eeac493e57270ff3dd0e40d3b8c1392889c..619b48c91608141ee83a944fc601350cef752e88 100644 (file)
@@ -95,6 +95,8 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     listview->onselection = args->onselection;
     listview->onselectiondata = args->onselectiondata;
     
+    char **static_elements = args->static_elements;
+    size_t static_nelm = args->static_nelm;
     if(var) {
         UiList *list = var->value;
         list->obj = listview;
@@ -102,6 +104,23 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
         list->getselection = ui_listview_getselection;
         list->setselection = ui_listview_setselection;
         ui_listview_update(list, 0);
+    } else if(static_elements && static_nelm > 0) {
+        XmStringTable items = calloc(static_nelm, sizeof(XmString));
+        for(int i=0;i<static_nelm;i++) {
+            items[i] = XmStringCreateLocalized(static_elements[i]);
+        }
+        XtVaSetValues(
+                listview->widget,
+                XmNitems, items,
+                XmNitemCount,
+                static_nelm,
+                NULL);
+        for (int i=0;i<static_nelm;i++) {
+            XmStringFree(items[i]);
+        }
+        free(items);
+        listview->getvalue = getvalue_wrapper;
+        listview->getvaluedata = ui_strmodel_getvalue;
     }
     
     XtAddCallback(
@@ -118,19 +137,36 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     XtAddCallback(
                 widget,
                 XmNextendedSelectionCallback,
-                (XtCallbackProc)ui_listview_selection,
+                (XtCallbackProc)ui_listview_selection_changed,
                 listview);
     XtAddCallback(
                 widget,
                 XmNsingleSelectionCallback,
-                (XtCallbackProc)ui_listview_selection,
+                (XtCallbackProc)ui_listview_selection_changed,
                 listview);
     
     return widget;
 }
 
+void ui_listview_select(UIWIDGET listview, int index) {
+    XmListDeselectAllItems(listview);
+    XmListSelectPos(listview, index+1, False);
+}
+
+int ui_listview_selection(UIWIDGET listview) {
+    int *selpositions = NULL;
+    int numpos = 0;
+    XtVaGetValues(listview, XmNselectedPositions, &selpositions, XmNselectedPositionCount, &numpos, NULL);
+    return numpos > 0 ? selpositions[0] : -1;
+}
+
 void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d) {
-    // TODO
+    ui_listselection_free(listview->current_selection);
+    if(listview->model) {
+        ui_model_remove_observer(listview->model, listview);
+        ui_model_unref(listview->model);
+    }
+    free(listview);
 }
 
 static void list_callback(UiObject *obj, UiListSelection sel, ui_callback callback, void *userdata) {
@@ -167,7 +203,7 @@ void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *
     }
 }
 
-void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb) {
+void ui_listview_selection_changed(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);
@@ -249,7 +285,7 @@ void* ui_strmodel_getvalue(void *elm, int column) {
 
 /* ------------------------------- Drop Down ------------------------------- */
 
-static void ui_dropdown_selection(
+static void ui_dropdown_selection_changed(
         Widget w,
         UiListView *listview,
         XmComboBoxCallbackStruct *cb)
@@ -276,7 +312,7 @@ static void ui_dropdown_selection(
     }
 }
 
-UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject* obj, UiListArgs *args) {
     Arg xargs[16];
     int n = 0;
     
@@ -311,6 +347,8 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
     listview->onselection = args->onselection;
     listview->onselectiondata = args->onselectiondata;
     
+    char **static_elements = args->static_elements;
+    size_t static_nelm = args->static_nelm;
     if(var) {
         UiList *list = var->value;
         list->obj = listview;
@@ -318,6 +356,23 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
         list->getselection = ui_dropdown_getselection;
         list->setselection = ui_dropdown_setselection;
         ui_listview_update(list, 0);
+    } else if(static_elements && static_nelm > 0) {
+        XmStringTable items = calloc(static_nelm, sizeof(XmString));
+        for(int i=0;i<static_nelm;i++) {
+            items[i] = XmStringCreateLocalized(static_elements[i]);
+        }
+        XtVaSetValues(
+                listview->widget,
+                XmNitems, items,
+                XmNitemCount,
+                static_nelm,
+                NULL);
+        for (int i=0;i<static_nelm;i++) {
+            XmStringFree(items[i]);
+        }
+        free(items);
+        listview->getvalue = getvalue_wrapper;
+        listview->getvaluedata = ui_strmodel_getvalue;
     }
     
     XtAddCallback(
@@ -328,7 +383,7 @@ UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
     XtAddCallback(
                 widget,
                 XmNselectionCallback,
-                (XtCallbackProc)ui_dropdown_selection,
+                (XtCallbackProc)ui_dropdown_selection_changed,
                 listview);
     
     return widget;
@@ -355,3 +410,12 @@ UiListSelection ui_dropdown_getselection(UiList *list) {
     }
     return sel;
 }
+
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
+    XtVaSetValues(dropdown, XmNselectedPosition, index, NULL);
+}
+
+int ui_dropdown_selection(UIWIDGET dropdown) {
+    int pos = -1;
+    XtVaGetValues(dropdown, XmNselectedPosition, &pos, NULL);
+}
index 04756a2140239a7f86fff469ff77e55f3a7aea7d..328f6dcdf7b09416c916aac83e7126ea1256b9f3 100644 (file)
@@ -30,7 +30,7 @@
 #define        LIST_H
 
 #include "toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "../common/context.h"
 
 #ifdef __cplusplus
@@ -63,7 +63,7 @@ typedef struct UiListView {
 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_selection_changed(Widget w, UiListView *listview, XmListCallbackStruct *cb);
 
 void ui_listview_update(UiList *list, int i);
 UiListSelection ui_listview_getselection(UiList *list);
index f3e1f41d6f06cbb4546bd7e5b8889682591aec32..b6f68badaa2fd003ea22fc4fa5b3f29e2768c722 100644 (file)
@@ -33,7 +33,6 @@
 #include "menu.h"
 #include "button.h"
 #include "toolkit.h"
-#include "stock.h"
 #include "container.h"
 #include "../common/context.h"
 #include "../common/menu.h"
@@ -150,7 +149,7 @@ void add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj)
                 eventdata);
     }
     
-    ui_set_widget_groups(obj->ctx, mitem, it->groups);
+    ui_set_widget_groups(obj->ctx, mitem, it->states);
 }
 
 void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
@@ -182,7 +181,7 @@ void add_checkitem_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
     
     ui_bind_togglebutton(obj, checkbox, it->varname, NULL, it->callback, it->userdata, 0);
     
-    ui_set_widget_groups(obj->ctx, checkbox, it->groups);
+    ui_set_widget_groups(obj->ctx, checkbox, it->states);
 }
 
 void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj) {
index 8d5d0fd9284a5450ddacf6e4a0517ac5cc8a5b0a..d4d2dd6dc94f10c15d57a358a32dd31520a2b282 100644 (file)
@@ -30,7 +30,6 @@ MOTIF_SRC_DIR = ui/motif/
 MOTIF_OBJPRE = $(OBJ_DIR)$(MOTIF_SRC_DIR)
 
 MOTIFOBJ = toolkit.o
-MOTIFOBJ += stock.o
 MOTIFOBJ += window.o
 MOTIFOBJ += widget.o
 MOTIFOBJ += container.o
index 52f4f169663cca77df7e8410abb3fdad17bd8c0f..b2c5389eaa3f2da819b1788e1821607856d11fd6 100644 (file)
@@ -213,9 +213,9 @@ void ui_text_selection_callback(
     int sel = left < right ? 1 : 0;
     if(sel != textarea->last_selection_state) {
         if(sel) {
-            ui_set_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+            ui_set_state(textarea->obj->ctx, UI_GROUP_SELECTION);
         } else {
-            ui_unset_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+            ui_unset_state(textarea->obj->ctx, UI_GROUP_SELECTION);
         }
     }
     textarea->last_selection_state = sel;
@@ -408,7 +408,7 @@ static UIWIDGET create_textfield(UiObject *obj, UiTextFieldArgs *args, int frame
     XtManageChild(textfield);
     ui_container_add(ctn, textfield);
     
-    ui_set_widget_groups(obj->ctx, textfield, args->groups);
+    ui_set_widget_groups(obj->ctx, textfield, args->states);
     
     UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt));
     memset(eventdata, 0, sizeof(UiEventDataExt));
index 2860f13df585fd82b14af5f3432d20d87a28fedb..e9ca682d9d7c1903c31c24a6a87cb91b4c7125ef 100644 (file)
@@ -33,7 +33,6 @@
 
 #include "toolbar.h"
 #include "button.h"
-#include "stock.h"
 #include "list.h"
 
 #include <cx/hash_map.h>
index 17b4726f227b392f88d48fb51aa9da08fc9ff471..fe49df16fb985c554375afb0d3fbc467c526a66b 100644 (file)
 #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 "../common/app.h"
 #include <cx/buffer.h>
 
 #include <X11/Intrinsic.h>
@@ -49,13 +49,6 @@ static Display *display;
 static Widget active_window;
 static const char *application_name;
 
-static ui_callback   startup_func;
-static void          *startup_data;
-static ui_callback   open_func;
-static void          *open_data;
-static ui_callback   exit_func;
-static void          *exit_data;
-
 static ui_callback appclose_fnc;
 static void *appclose_udata;
 
@@ -134,33 +127,14 @@ Display* ui_motif_get_display() {
     return display;
 }
 
-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_app_exit_on_shutdown(UiBool exitapp) {
     exit_on_shutdown = exitapp;
 }
 
 void ui_main() {
-    if(startup_func) {
-        startup_func(NULL, startup_data);
-    }
+    uic_application_startup(NULL);
     XtAppMainLoop(app);
-    if(exit_func) {
-        exit_func(NULL, exit_data);
-    }
+    uic_application_exit(NULL);
     uic_store_app_properties();
     if(exit_on_shutdown) {
         exit(0);
@@ -180,7 +154,7 @@ void ui_secondary_event_loop(int *loop) {
 }
 
 void ui_show(UiObject *obj) {
-    uic_check_group_widgets(obj->ctx);
+    uic_check_state_widgets(obj->ctx);
     if(!XtIsRealized(obj->widget)) {
         XtRealizeWidget(obj->widget);
         obj->ref++;
@@ -357,13 +331,13 @@ void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) {
     if(!groups) {
         return;
     }
-    size_t ngroups = uic_group_array_size(groups);
+    size_t ngroups = uic_state_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);
+        uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
         ui_set_enabled(widget, FALSE);
     }
 }
index 9489f077c96d24ddb4fcbae7acdf3d8d08c2ba81..c8d5b1d4fa2d579895a3cfcb329dc8bdacbf9706 100644 (file)
@@ -74,11 +74,10 @@ static void window_close_handler(Widget window, void *udata, void *cdata) {
 }
 
 
-static UiObject* create_window(const char *title, void *window_data, Boolean simple) {
+static UiObject* create_window(const char *title, Boolean simple) {
     CxMempool *mp = cxMempoolCreateSimple(256);
     const CxAllocator *a = mp->allocator;
     UiObject *obj = uic_object_new_toplevel();
-    obj->window = window_data;
     obj->destroy = ui_window_widget_destroy;
     
     int window_width = window_default_width;
@@ -151,12 +150,12 @@ static UiObject* create_window(const char *title, void *window_data, Boolean sim
     return obj;
 } 
 
-UiObject* ui_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE);
+UiObject* ui_window(const char *title) {
+    return create_window(title, FALSE);
 }
 
-UiObject* ui_simple_window(const char *title, void *window_data) {
-    return create_window(title, window_data, TRUE);
+UiObject* ui_simple_window(const char *title) {
+    return create_window(title, TRUE);
 }
 
 void ui_window_size(UiObject *obj, int width, int height) {
index 245ba19252b6d8edf7473f8764496f9d05ee3757..36e5b46080552e655295c062783952d2b8a5c358 100644 (file)
@@ -82,7 +82,14 @@ void UiBoxContainer::add(QWidget* widget, UiLayout& layout) {
     bool fill = layout.fill;
     if(hasStretchedWidget && fill) {
         fill = false;
-        fprintf(stderr, "UiError: container has 2 filled widgets");
+        fprintf(stderr, "UiError: container has 2 filled widgets\n");
+    }
+    
+    if(singleChild) {
+        fill = true;
+        if(hasChild) {
+            fprintf(stderr, "UiError: single child container already has a child\n");
+        }
     }
     
     uic_layout_setup_margin(&layout);
@@ -103,6 +110,7 @@ void UiBoxContainer::add(QWidget* widget, UiLayout& layout) {
         hasStretchedWidget = true;
     }
     current = widget;
+    hasChild = true;
 }
 
 UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
@@ -239,6 +247,64 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) {
 }
 
 
+/* ------------------------------ Frame ------------------------------ */
+
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+    UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    
+    bool createBox = true;
+    bool singleChild = false;
+    QBoxLayout::Direction dir = QBoxLayout::TopToBottom;
+    QGroupBox *widget = new QGroupBox();
+    if(args->label) {
+        widget->setTitle(args->label);
+    }
+    
+    switch(args->subcontainer) {
+        default: break;
+        case UI_CONTAINER_HBOX: {
+            dir = QBoxLayout::LeftToRight;
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            createBox = false;
+            break;
+        }
+        case UI_CONTAINER_NO_SUB: {
+            singleChild = true;
+        }
+    }
+    
+    if(createBox) {
+        QBoxLayout *box = new QBoxLayout(dir);
+        widget->setLayout(box);
+        
+        UiBoxContainer *container = new UiBoxContainer(box);
+        ui_obj_add_container(obj, container);
+
+        container->singleChild = singleChild;
+    } else {
+        QGridLayout *grid = new QGridLayout();
+        widget->setLayout(grid);
+
+        ui_obj_add_container(obj, new UiGridContainer(
+                grid,
+                args->padding,
+                args->columnspacing,
+                args->rowspacing,
+                false,
+                false,
+                false,
+                false));
+    }
+    
+    
+    ctn->add(widget, layout);
+    
+    return widget;
+}
+
 /* ---------------------------- UiSidebar ---------------------------- */
 
 UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
index 867fc4c57ef1f086a600ad1e9d4a53126d75fbd3..0adf78aac6a4e2d1efae642121bcc1169fae6041 100644 (file)
@@ -39,6 +39,7 @@
 #include <QTabWidget>
 #include <QStackedWidget>
 #include <QSplitter>
+#include <QGroupBox>
 
 #define ui_obj_container(obj) (UiContainerPrivate*)((UiContainerX*)obj->container_end)->container
 
@@ -54,6 +55,8 @@ class UiBoxContainer : public UiContainerPrivate {
 public:
     QBoxLayout            *box;
     bool                  hasStretchedWidget = false;
+    bool                  singleChild = false;
+    bool                  hasChild = false;
     QSpacerItem           *space;
     QBoxLayout::Direction direction;
     
index 94d34269f78d180aee0fd185d3987d154f608052..a2bac52a2ee86f8cbc84d2c16850d3c44686217b 100644 (file)
@@ -32,6 +32,7 @@
 #include <QTreeView>
 #include <QTreeWidgetItem>
 #include <QListView>
+#include <QComboBox>
 
 extern "C" void* ui_strmodel_getvalue(void *elm, int column) {
     return column == 0 ? elm : NULL;
@@ -46,7 +47,7 @@ static void* null_getvalue(UiList *list, void *elm, int row, int col, void *user
     return NULL;
 }
 
-UIWIDGET ui_listview_create(UiObjectobj, UiListArgs *args) {
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
     
     QListView *view = new QListView();
@@ -92,7 +93,7 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     return view;
 }
 
-UIWIDGET ui_table_create(UiObjectobj, UiListArgs *args) {
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
     
     QTreeView *view = new QTreeView();
@@ -141,3 +142,42 @@ UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
     
     return view;
 }
+
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
+    UiContainerPrivate *ctn = ui_obj_container(obj);
+    
+    QComboBox *view = new QComboBox();
+    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ctn->add(view, layout);
+    
+    ui_getvaluefunc2 getvalue = nullptr;
+    void *getvaluedata = nullptr;
+    if(args->getvalue2) {
+        getvalue = args->getvalue2;
+        getvaluedata = args->getvalue2data;
+    } else if(args->getvalue) {
+        getvalue = getvalue_wrapper;
+        getvaluedata = (void*)args->getvalue;
+    } else {
+        getvalue = getvalue_wrapper;
+        getvaluedata = (void*)ui_strmodel_getvalue;
+    }
+    
+    UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+    
+    ListModel *model = new ListModel(obj, view, var, getvalue, getvaluedata);
+    view->setModel(model);
+    
+    if(var) {
+        UiList *list = (UiList*)var->value;
+        list->update = ui_listmodel_update;
+        list->getselection = ui_listmodel_getselection;
+        list->setselection = ui_listmodel_setselection;
+        list->obj = model;
+    }
+    
+    model->setActivationCallback(args->onactivate, args->onactivatedata);
+    model->setSelectionCallback(args->onselection, args->onselectiondata);
+    
+    return view;
+}
index 664b32b5f40bd1aea4bf7c193ba82d5d0b062100..82232140027ec50ff119c0ba1047db0d8c726fe1 100644 (file)
@@ -29,7 +29,7 @@
 #ifndef TREE_H
 #define        TREE_H
 
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "model.h"
 
 #include <QTableView>
index 4dbb7e1ed5191151b0cfe3d15d8716ccedfbdb56..f391753ce0f49af04c4e6fa6d9d912fc1229b536 100644 (file)
@@ -38,7 +38,6 @@
 #include "../common/menu.h"
 #include "../ui/properties.h"
 #include "../ui/window.h"
-#include "stock.h"
 #include "container.h"
 
 
@@ -88,8 +87,8 @@ static UiAction* create_action(
     }
     
     if(states) {
-        size_t nstates = uic_group_array_size(states);
-        uic_add_group_widget_i(obj->ctx, action, (ui_enablefunc)ui_action_enable, states, nstates);
+        size_t nstates = uic_state_array_size(states);
+        uic_add_state_widget_i(obj->ctx, action, (ui_enablefunc)ui_action_enable, states, nstates);
         action->setEnabled(false);
     }
     
@@ -98,7 +97,7 @@ static UiAction* create_action(
 
 void add_menuitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
     UiMenuItem *it = (UiMenuItem*)item;
-    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
     parent->addAction(action);
     QObject::connect(action, SIGNAL(triggered()), action, SLOT(trigger()));
 }
@@ -110,7 +109,7 @@ void add_menuseparator_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject
 void add_checkitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
     UiMenuCheckItem *it = (UiMenuCheckItem*)item;
     
-    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
     parent->addAction(action);
     action->setCheckable(true);
     action->prepare_event = ui_checkableaction_prepare_event;
@@ -130,7 +129,7 @@ void add_checkitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj
 void add_radioitem_widget(QMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
     UiMenuRadioItem *it = (UiMenuRadioItem*)item;
     
-    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+    UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
     parent->addAction(action);
     action->setCheckable(true);
     action->prepare_event = ui_actiongroup_prepare_event;
index 7b991344beff7e6b0bbff9b312bf8cf3ae2bc9c3..361da2ff36ef7d132cfa9dad05b68c4ab4eda6ba 100644 (file)
 #include "model.h"
 
 
-ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata){
+ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata) {
     this->obj = obj;
-    this->view = view;
+    this->listview = view;
+    this->combobox = nullptr;
+    this->var = var;
+    this->getvalue = getvalue;
+    this->getvaluedata = getvaluedata;
+    this->onactivate = nullptr;
+    this->onactivatedata = nullptr;
+    this->onselection = nullptr;
+    this->onselectiondata = nullptr;
+}
+
+ListModel::ListModel(UiObject *obj, QComboBox *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata) {
+    this->obj = obj;
+    this->combobox = view;
+    this->listview = nullptr;
     this->var = var;
     this->getvalue = getvalue;
     this->getvaluedata = getvaluedata;
@@ -85,7 +99,12 @@ QVariant ListModel::data(const QModelIndex &index, int role) const {
 }
 
 void ListModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
-    UiListSelection sel = ui_selection_model_to_selection(view->selectionModel());
+    UiListSelection sel;
+    if(listview) {
+        sel = ui_selection_model_to_selection(listview->selectionModel());
+    } else {
+        // TODO
+    }
     
     UiEvent event;
     event.obj = obj;
@@ -257,10 +276,19 @@ void ui_listmodel_setselection(UiList *list, UiListSelection sel) {
         QModelIndex index = model->index(sel.rows[i]);
         selection.select(index, index);
     }
-    model->view->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+    if(model->listview) {
+        model->listview->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+    } else if(model->combobox) {
+        // TODO
+    }
 }
 
 UiListSelection ui_listmodel_getselection(UiList *list) {
     ListModel *model = (ListModel*)list->obj;
-    return ui_selection_model_to_selection(model->view->selectionModel());
+    if(model->listview) {
+        return ui_selection_model_to_selection(model->listview->selectionModel());
+    } else {
+        UiListSelection sel = { 0, NULL };
+        return sel;
+    }
 }
index 8fa513e0cb7deea73d41093e86159fca33748e37..4a69ced416ceb006e89d745f463e6e2c302a218c 100644 (file)
 #define        MODEL_H
 
 #include "toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
 #include "../common/context.h"
 #include <QListView>
 #include <QTreeView>
+#include <QComboBox>
 #include <QAbstractListModel>
 #include <QAbstractTableModel>
 #include <QAbstractItemModel>
@@ -54,9 +55,11 @@ class ListModel : public QAbstractListModel {
 public:
     UiObject    *obj;
     UiVar       *var;
-    QListView   *view;
+    QListView   *listview;
+    QComboBox   *combobox;
     
     ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata);
+    ListModel(UiObject *obj, QComboBox *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata);
     
     void setActivationCallback(ui_callback f, void *userdata);
     void setSelectionCallback(ui_callback f, void *userdata);
index 553ddbdf6463e57095c0d948f43bf9ed130f94c9..c3082a5a0ef3b8b9c9a19656441c781956fd6ea1 100644 (file)
@@ -43,7 +43,6 @@ SOURCES += toolkit.cpp
 SOURCES += window.cpp
 SOURCES += menu.cpp
 SOURCES += toolbar.cpp
-SOURCES += stock.cpp
 SOURCES += container.cpp
 SOURCES += text.cpp
 SOURCES += model.cpp
@@ -59,7 +58,6 @@ HEADERS += toolkit.h
 HEADERS += window.h
 HEADERS += menu.h
 HEADERS += toolbar.h
-HEADERS += stock.h
 HEADERS += container.h
 HEADERS += text.h
 HEADERS += model.h
index 2cb1589aa3c14c6d040cec283f52f2ccbc2de11d..63245b6fa2e2b9745d57824b02374699111de670 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "toolbar.h"
 #include "menu.h"
-#include "stock.h"
 
 static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items);
 static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i);
index c1fa90af7faf2b4a5fcf64bd401aa86b46822c82..bc355d08c56c25b36d66ee5752c2f221b0846746 100644 (file)
 
 #include "toolkit.h"
 #include "window.h"
-#include "stock.h"
 
 #include "../common/document.h"
 #include "../common/properties.h"
 #include "../common/menu.h"
 #include "../common/toolbar.h"
+#include "../common/app.h"
 
 static const char *application_name;
 
-static ui_callback   startup_func;
-static void          *startup_data;
-static ui_callback   open_func;
-static void          *open_data;
-static ui_callback   exit_func;
-static void          *exit_data;
-
 static int is_toplevel_realized = 0;
 
 static int app_argc;
@@ -73,33 +66,14 @@ 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_app_exit_on_shutdown(UiBool exitapp) {
     exit_on_shutdown = exitapp;
 }
 
 void ui_main() {
-    if(startup_func) {
-        startup_func(NULL, startup_data);
-    }
+    uic_application_startup(NULL);
     application->exec();
-    if(exit_func) {
-        exit_func(NULL, exit_data);
-    }
+    uic_application_exit(NULL);
     uic_store_app_properties();
     
     delete application;
index 38570d5655511e7b8a879bd104bb91a474fa5ff6..6e4c1c83af43cd07b8043d06660183566db659ec 100644 (file)
@@ -41,9 +41,8 @@
 #include <QDockWidget>
 #include <QMessageBox>
 
-static UiObject* create_window(const char *title, void *window_data, bool simple, bool sidebar = false) {
+static UiObject* create_window(const char *title, bool simple, bool sidebar = false) {
     UiObject *obj = uic_object_new_toplevel();
-    obj->window = window_data;
     obj->next = NULL;
     
     QMainWindow *window = new QMainWindow();
@@ -73,16 +72,16 @@ static UiObject* create_window(const char *title, void *window_data, bool simple
     return obj;
 }
 
-UiObject* ui_window(const char *title, void *window_data) {
-    return create_window(title, window_data, false);
+UiObject* ui_window(const char *title) {
+    return create_window(title, false);
 }
 
-UiObject* ui_simplewindow(char *title, void *window_data) {
-    return create_window(title, window_data, true);
+UiObject* ui_simple_window(const char *title) {
+    return create_window(title, true);
 }
 
-UiObject *ui_sidebar_window(const char *title, void *window_data) {
-    return create_window(title, window_data, false, true);
+UiObject* ui_sidebar_window(const char *title) {
+    return create_window(title, false, true);
 }
 
 void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
index 693f7309fed15e57102ce35046091eee4d5c131f..55367f19fc7e54e34c09738c469edf0fbaf91ce5 100644 (file)
@@ -65,7 +65,7 @@ typedef struct UiButtonArgs {
     ui_callback onclick;
     void *onclickdata;
     
-    const int *groups;
+    const int *states;
 } UiButtonArgs;
 
 typedef struct UiToggleArgs {
@@ -93,9 +93,9 @@ typedef struct UiToggleArgs {
     const char *varname;
     ui_callback onchange;
     void *onchangedata;
-    int enable_group;
+    int enable_state;
     
-    const int *groups;
+    const int *states;
 } UiToggleArgs;
 
 typedef struct UiLinkButtonArgs {
@@ -124,7 +124,7 @@ typedef struct UiLinkButtonArgs {
     UiBool nofollow;
     UiLinkType type;
     
-    const int *groups;
+    const int *states;
 } UiLinkButtonArgs;
  
 #define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } )
index 0bc42b702a06008308746ee59d0f0e8a6c74fbf7..dc2aa299ebef95b7e26fc502c1edbb28bd58bffc 100644 (file)
@@ -312,13 +312,14 @@ struct UiContainerX {
 #define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 
-
 #define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_grid_w(obj, w, ...) for(w = ui_grid_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_tabview_w(obj, w, ...) for(w = ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_scrolledwindow_w(obj, w, ...) for(w = ui_scrolledwindow_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 
+#define ui_custom_container(ob, widget, addfunc, data) for(ui_custom_container_create(obj, widget, addfunc, data);ui_container_finish(obj);ui_container_begin_close(obj))
+
 #define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
@@ -369,6 +370,9 @@ UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBo
 
 UIEXPORT void ui_newline(UiObject *obj);
 
+typedef void(*ui_addwidget_func)(UiObject *obj, UIWIDGET parent, UIWIDGET child, void *userdata);
+UIEXPORT void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata);
+
 // TODO
 UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj);
 UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view);
index 6dd6a0a5983572a16aa4810db9c6887c5d8b2dc2..9ee26d3b1e5a02cab83240cb61febc84ba55b7c1 100644 (file)
@@ -65,7 +65,7 @@ typedef struct UiSpinBoxArgs {
     ui_callback onchange;
     void* onchangedata;
     
-    const int *groups;
+    const int *states;
 } UiSpinBoxArgs;
 
 
index 77d923fe49967317f353aafb84a41067399e4989..0730f7569d6bacd06c1d1b889146c955f6afbebe 100644 (file)
@@ -58,6 +58,8 @@ typedef struct UiImageViewerArgs {
     int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
+    int height;
     const char *name;
     const char *style_class;
 
diff --git a/ui/ui/list.h b/ui/ui/list.h
new file mode 100644 (file)
index 0000000..7cfbfbb
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * 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 UI_TREE_H
+#define        UI_TREE_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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,
+    UI_STRING_EDITABLE,
+    UI_BOOL_EDITABLE
+} UiModelType;
+
+typedef struct UiCellValue {
+    union {
+        const char *string;
+        int64_t i;
+        UiBool b;
+    };
+    UiModelType type;
+} UiCellValue;
+
+typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata);
+
+typedef void (*ui_model_update_func)(UiModel *model, void *userdata, int insert_index, int delete_index);
+
+typedef struct UiModelChangeObserver UiModelChangeObserver;
+struct UiModelChangeObserver {
+    ui_model_update_func update;
+    void *userdata;
+    UiModelChangeObserver *next;
+};
+
+struct UiModel {
+    UiContext *ctx;
+    
+    /*
+     * number of columns
+     */
+    int columns;
+    
+    /*
+     * current allocation size (internal)
+     */
+    int alloc;
+    
+    /*
+     * array of column types
+     * array length is the number of columns
+     */
+    UiModelType *types;
+    
+    /*
+     * array of column titles
+     * array length is the number of columns
+     */
+    char **titles;
+    
+    /*
+     * array of column size hints
+     */
+    int *columnsize;
+    
+    /*
+     * Model change observers, that will be called when
+     * columns are added or removed
+     */
+    UiModelChangeObserver *observer;
+    
+    /*
+     * reference counter
+     */
+    int ref;
+};
+
+struct UiListCallbacks {
+    /*
+     * selection callback
+     */
+    ui_callback activate;
+    
+    /*
+     * cursor callback
+     */
+    ui_callback selection;
+    
+    /*
+     * userdata for all callbacks
+     */
+    void *userdata;
+};
+
+struct UiListArgs {
+    UiBool fill;
+    UiBool hexpand;
+    UiBool vexpand;
+    UiBool hfill;
+    UiBool vfill;
+    UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
+    int colspan;
+    int rowspan;
+    int width;
+    int height;
+
+    const char *name;
+    const char *style_class;
+    UiList *list;
+    const char* varname;
+    UiModel *model;
+    char **static_elements;
+    size_t static_nelm;
+    ui_getvaluefunc getvalue;
+    ui_getvaluefunc2 getvalue2;
+    void *getvalue2data;
+    ui_getstylefunc getstyle;
+    void *getstyledata;
+    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;
+    UiBool multiselection;
+    UiMenuBuilder *contextmenu;
+    ui_list_savefunc onsave;
+    void *onsavedata;
+    
+    const int *states;
+};
+
+typedef void (*ui_sublist_getvalue_func)(UiList *list, void *sublist_userdata, void *rowdata, int index, UiSubListItem *item, void *userdata);
+
+struct UiSubList {
+    UiList *value;
+    const char *varname;
+    const char *header;
+    UiBool separator;
+    void *userdata;
+};
+
+typedef struct UiSubListEventData {
+    UiList *list;
+    int    sublist_index;
+    int    row_index;
+    void   *row_data;
+    void   *sublist_userdata;
+    void   *event_data;
+} UiSubListEventData;
+
+/*
+ * 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;
+    UiMenuBuilder *button_menu;
+    char          *badge;
+    void          *eventdata;
+};
+
+struct UiSourceListArgs {
+    UiBool fill;
+    UiBool hexpand;
+    UiBool vexpand;
+    UiBool hfill;
+    UiBool vfill;
+    UiBool override_defaults;
+    int margin;
+    int margin_left;
+    int margin_right;
+    int margin_top;
+    int margin_bottom;
+    int colspan;
+    int rowspan;
+    int width;
+    int height;
+    const char *name;
+    const char *style_class;
+    
+    const int *states;
+    
+    /*
+     * static 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
+     * 
+     * sublists can be NULL, in which case sublists are dynamically loaded
+     * from dynamic_sublist/varname
+     */
+    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;
+    
+    /*
+     * list value, that contains UiSubList* elements
+     */
+    UiList *dynamic_sublist;
+    
+    /*
+     * load sublists dynamically from a variable with the specified name
+     */
+    const char *varname;
+    
+    
+    /*
+     * callback for each list item, that should fill all necessary
+     * UiSubListItem fields
+     */
+    ui_sublist_getvalue_func getvalue;
+    
+    /*
+     * getvalue_func userdata
+     */
+    void *getvaluedata;
+    
+    /*
+     * is a sublist header a selectable item
+     */
+    UiBool header_is_item;
+    
+    /*
+     * 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;
+    
+    UiMenuBuilder *contextmenu;
+};
+
+#define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ }
+#define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} }
+
+
+/*
+ * Creates an UiModel, that specifies columns for a table widget.
+ *
+ * For each column a column type (UiModelType) and a title string
+ * (char*) must be specified. The column list must be terminated
+ * with -1.
+ *
+ * UiModel *model = ui_model(ctx, UI_STRING, "Column 1", UI_STRING, "Column 2", -1);
+ */
+UIEXPORT UiModel* ui_model(UiContext *ctx, ...);
+UIEXPORT UiModel* ui_model_new(UiContext *ctx);
+UIEXPORT void ui_model_add_column(UiModel *model, UiModelType type, const char *title, int width);
+UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model);
+UIEXPORT void ui_model_ref(UiModel *model);
+UIEXPORT void ui_model_unref(UiModel *model);
+UIEXPORT void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data);
+UIEXPORT void ui_model_remove_observer(UiModel *model, void *data);
+UIEXPORT void ui_model_free(UiModel *mi);
+
+#define ui_listview(obj, ...) ui_listview_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_table(obj, ...) ui_table_create(obj, &(UiListArgs) { __VA_ARGS__ } )
+#define ui_dropdown(obj, ...) ui_dropdown_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__ } )
+
+UIEXPORT UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args);
+UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject *obj, UiListArgs *args);
+
+UIEXPORT void ui_listview_select(UIWIDGET listview, int index);
+UIEXPORT int ui_listview_selection(UIWIDGET listview);
+UIEXPORT void ui_dropdown_select(UIWIDGET dropdown, int index);
+UIEXPORT int ui_dropdown_selection(UIWIDGET dropdown);
+
+UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args);
+
+UIEXPORT void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon);
+UIEXPORT void ui_sublist_item_set_label(UiSubListItem *item, const char *label);
+UIEXPORT void ui_sublist_item_set_button_icon(UiSubListItem *item, const char *button_icon);
+UIEXPORT void ui_sublist_item_set_button_label(UiSubListItem *item, const char *button_label);
+UIEXPORT void ui_sublist_item_set_button_menu(UiSubListItem *item, UiMenuBuilder *menu);
+UIEXPORT void ui_sublist_item_set_badge(UiSubListItem *item, const char *badge);
+UIEXPORT void ui_sublist_item_set_eventdata(UiSubListItem *item, void *eventdata);
+
+
+
+/*
+ * Only relevant for some language bindings
+ */
+typedef void(*ui_sourcelist_update_func)(void);
+
+/*
+ * The sourcelist update callback is called after any source list
+ * sublist update is completed
+ */
+UIEXPORT void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TREE_H */
+
index 9dde9b4546bca50c1b7d60793d8b009b06dedd1e..43cca7e7f6ab80e39a945356b0c8db06fb320f9e 100644 (file)
@@ -43,7 +43,7 @@ typedef struct UiMenuItemArgs {
        ui_callback onclick;
        void* onclickdata;
 
-       const int* groups;
+       const int* states;
 } UiMenuItemArgs;
 
 typedef struct UiMenuToggleItemArgs {
@@ -54,7 +54,7 @@ typedef struct UiMenuToggleItemArgs {
        ui_callback onchange;
        void* onchangedata;
 
-       const int* groups;
+       const int* nstates;
 } UiMenuToggleItemArgs;
 
 typedef struct UiMenuItemListArgs {
index ffb10ddbb00242f288965b20649b15de5db7b3e6..57112b7513fdb4a3ed954b4d79376d3e02b61cbe 100644 (file)
@@ -35,6 +35,9 @@
 extern "C" {
 #endif
 
+void ui_load_properties_file_on_startup(UiBool enable);
+void ui_set_properties_data(const char *str, size_t len);
+    
 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);
index ac5a2c1d3a16fb645fafbc08fea94e15f820e218..c102f0b36ce20b74160aa1b2494b599c099dd92c 100644 (file)
@@ -59,7 +59,7 @@ typedef struct UiTextAreaArgs {
     ui_callback onchange;
     void *onchangedata;
     
-    const int *groups;
+    const int *states;
 } UiTextAreaArgs;
     
 typedef struct UiTextFieldArgs {
@@ -87,7 +87,7 @@ typedef struct UiTextFieldArgs {
     ui_callback onactivate;
     void *onactivatedata;
     
-    const int *groups;
+    const int *states;
 } UiTextFieldArgs;
 
 typedef struct UiPathElmRet {
@@ -115,6 +115,7 @@ typedef struct UiPathTextFieldArgs {
     int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
     const char *name;
     const char *style_class;
 
index 26a79e68e38a7a85265452fa7ca330b0f115d144..2e230df495810275d9a65fc0313a5ff40e05224e 100644 (file)
@@ -44,7 +44,7 @@ typedef struct UiToolbarItemArgs {
     ui_callback onclick;
     void* onclickdata;
 
-    const int *groups;
+    const int *states;
     const int *visibility_states;
 } UiToolbarItemArgs;
 
@@ -57,7 +57,7 @@ typedef struct UiToolbarToggleItemArgs {
     ui_callback onchange;
     void *onchangedata;
 
-    const int *groups;
+    const int *states;
     const int *visibility_states;
 } UiToolbarToggleItemArgs;
 
index 89751d26bdc7d8a44e6dfe2c3f111e02b7b365f9..628e3e8907ec5f69efc13c490a1552bb677b1df0 100644 (file)
@@ -148,8 +148,10 @@ public:
 
 #elif UI_SERVER
 
-#define UIWIDGET void*
-#define UIWINDOW void*
+typedef struct UiWidget UiWidget;
+
+#define UIWIDGET UiWidget*
+#define UIWINDOW UiWidget*
 #define UIMENU   void*
 
 #endif
@@ -565,16 +567,18 @@ UIEXPORT void* ui_get_subdocument(void *document);               // deprecated
 
 UIEXPORT UiContext* ui_document_context(void *doc);
 
+UIEXPORT void* ui_context_get_document(UiContext *ctx);
+UIEXPORT void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable);
 UIEXPORT void ui_attach_document(UiContext *ctx, void *document);
 UIEXPORT void ui_detach_document(UiContext *ctx, void *document);
 
-UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
-UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups);
+UIEXPORT void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
+UIEXPORT void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates);
 UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates);
 
-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);
+UIEXPORT void ui_set_state(UiContext *ctx, int state);
+UIEXPORT void ui_unset_state(UiContext *ctx, int state);
+UIEXPORT int* ui_active_states(UiContext *ctx, int *nstates);
     
 UIEXPORT void* ui_allocator(UiContext *ctx);
 UIEXPORT void* ui_cx_mempool(UiContext *ctx);
@@ -633,6 +637,13 @@ 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 UiInteger* ui_get_int_var(UiContext *ctx, const char *name);
+UIEXPORT UiDouble* ui_get_double_var(UiContext *ctx, const char *name);
+UIEXPORT UiString* ui_get_string_var(UiContext *ctx, const char *name);
+UIEXPORT UiText* ui_get_text_var(UiContext *ctx, const char *name);
+UIEXPORT UiRange* ui_get_range_var(UiContext *ctx, const char *name);
+UIEXPORT UiGeneric* ui_get_generic_var(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);
index 57c802593c1b7b261ffbe242afd86a711789b6a2..472ca74dd4ffe36f15a2d5d5d7b82be7e975db1b 100644 (file)
 #include "menu.h"
 #include "toolbar.h"
 #include "window.h"
-#include "stock.h"
 #include "button.h"
 #include "text.h"
 #include "properties.h"
-#include "tree.h"
+#include "list.h"
 #include "graphics.h"
 #include "entry.h"
 #include "range.h"
index 9636165f22bda227e84245327b98bcd0512279a6..d5a243ddb9d48aca1ddb928e42684dde547952f1 100644 (file)
@@ -60,13 +60,15 @@ typedef struct UiWebviewArgs {
     int margin_bottom;
     int colspan;
     int rowspan;
+    int width;
+    int height;
     const char *name;
     const char *style_class;
     
     UiGeneric *value;
     const char *varname;
     
-    const int* groups;
+    const int* states;
 } UiWebviewArgs;
 
 #define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } )
index 3619b3a0102aef270b6829b9c0b389ff9586120c..d100016f225e77a3aad7c0d2da792bc8715ae513 100644 (file)
@@ -61,10 +61,10 @@ typedef struct UiDialogWindowArgs {
     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;
+    const int *lbutton1_states;
+    const int *lbutton2_states;
+    const int *rbutton3_states;
+    const int *rbutton4_states;
     int default_button;
     int width;
     int height;
@@ -72,10 +72,10 @@ typedef struct UiDialogWindowArgs {
     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_window(const char *title);
+UIEXPORT UiObject *ui_sidebar_window(const char *title);
 UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar);
-UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_simple_window(const char *title);
 UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args);
 
 #define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ });
index 56264585ace58de826bd2cd84a9eacada89b5fb4..2681a2e41dcc96c473aec16347cfe5455bd0b2f3 100644 (file)
@@ -134,7 +134,7 @@ static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) {
         }\r
     } else {\r
         UiModel *model = ui_model_new(obj->ctx);\r
-        ui_model_add_column(obj->ctx, model, UI_STRING, "Test", -1);\r
+        ui_model_add_column(model, UI_STRING, "Test", -1);\r
         listview->model = model;\r
         numcolumns = 1;\r
     }\r
@@ -166,18 +166,30 @@ static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) {
         UiList *list = listview->var->value;\r
         list->obj = listview;\r
         list->update = ui_listview_update;\r
-        list->getselection = ui_listview_getselection;\r
-        list->setselection = ui_listview_setselection;\r
+        list->getselection = ui_listview_getselection_impl;\r
+        list->setselection = ui_listview_setselection_impl;\r
 \r
         ui_listview_update(list, -1);\r
+    } else if (!table && args->static_elements && args->static_nelm > 0) {\r
+        char **static_elements = args->static_elements;\r
+        size_t static_nelm = args->static_nelm;\r
+        LVITEM item;\r
+        item.mask = LVIF_TEXT;\r
+        item.iSubItem = 0;\r
+        for (int i=0;i<static_nelm;i++) {\r
+            item.iItem = i;\r
+            item.pszText = static_elements[i];\r
+            ListView_InsertItem(hwnd, &item);\r
+        }\r
+        listview->getvalue = strmodel_getvalue;\r
+        listview->getvaluedata = NULL;\r
     }\r
 \r
     return (W32Widget*)listview;\r
 }\r
 \r
-static UiListSelection listview_get_selection(UiListView *listview) {\r
+static UiListSelection listview_get_selection2(HWND hwnd) {\r
     UiListSelection sel = { 0, NULL };\r
-    HWND hwnd = listview->widget.hwnd;\r
 \r
     CX_ARRAY_DECLARE(int, indices);\r
     cx_array_initialize(indices, 8);\r
@@ -195,6 +207,11 @@ static UiListSelection listview_get_selection(UiListView *listview) {
     return sel;\r
 }\r
 \r
+static UiListSelection listview_get_selection(UiListView *listview) {\r
+    HWND hwnd = listview->widget.hwnd;\r
+    return listview_get_selection2(hwnd);\r
+}\r
+\r
 // listview class event proc\r
 int ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {\r
     UiListView *listview = (UiListView*)widget;\r
@@ -327,12 +344,12 @@ void ui_listview_update(UiList *list, int row) {
     }\r
 }\r
 \r
-UiListSelection ui_listview_getselection(UiList *list) {\r
+UiListSelection ui_listview_getselection_impl(UiList *list) {\r
     UiListView *listview = (UiListView*)list->obj;\r
     return listview_get_selection(listview);\r
 }\r
 \r
-void ui_listview_setselection(UiList *list, UiListSelection selection) {\r
+void ui_listview_setselection_impl(UiList *list, UiListSelection selection) {\r
 \r
 }\r
 \r
@@ -346,6 +363,20 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     return listview_create(obj, args, TRUE);\r
 }\r
 \r
+void ui_listview_select(UIWIDGET listview, int index) {\r
+\r
+}\r
+\r
+int ui_listview_selection(UIWIDGET listview) {\r
+    W32Widget *w = (W32Widget*)listview;\r
+    UiListSelection sel = listview_get_selection2(w->hwnd);\r
+    int index = -1;\r
+    if (sel.count > 0) {\r
+        index = sel.rows[0];\r
+    }\r
+    free(sel.rows);\r
+    return index;\r
+}\r
 \r
 /* ------------------------------------ DropDown ------------------------------------*/\r
 \r
@@ -357,7 +388,7 @@ static W32WidgetClass dropdown_widget_class = {
     .destroy  = w32_widget_default_destroy\r
 };\r
 \r
-UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {\r
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {\r
     HINSTANCE hInstance = GetModuleHandle(NULL);\r
     UiContainerPrivate *container = ui_obj_container(obj);\r
     HWND parent = ui_container_get_parent(container);\r
@@ -383,13 +414,20 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
         UiList *list = dropdown->var->value;\r
         list->obj = dropdown;\r
         list->update = ui_dropdown_update;\r
-        list->getselection = ui_dropdown_getselection;\r
-        list->setselection = ui_dropdown_setselection;\r
+        list->getselection = ui_dropdown_getselection_impl;\r
+        list->setselection = ui_dropdown_setselection_impl;\r
 \r
         ui_dropdown_update(list, -1);\r
+    } else if (args->static_elements && args->static_nelm > 0) {\r
+        char **static_elements = args->static_elements;\r
+        size_t static_nelm = args->static_nelm;\r
+        for (int i=0;i<static_nelm;i++) {\r
+            SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)static_elements[i]);\r
+        }\r
+        dropdown->getvalue = strmodel_getvalue;\r
+        dropdown->getvaluedata = NULL;\r
     }\r
 \r
-\r
     return (W32Widget*)dropdown;\r
 }\r
 \r
@@ -437,7 +475,7 @@ void ui_dropdown_update(UiList *list, int row) {
     }\r
 }\r
 \r
-UiListSelection ui_dropdown_getselection(UiList *list) {\r
+UiListSelection ui_dropdown_getselection_impl(UiList *list) {\r
     UiListSelection sel = { 0, NULL };\r
     UiListView *listview = (UiListView*)list->obj;\r
     int index = (int)SendMessage(listview->widget.hwnd, CB_GETCURSEL, 0, 0);\r
@@ -449,7 +487,15 @@ UiListSelection ui_dropdown_getselection(UiList *list) {
     return sel;\r
 }\r
 \r
-void ui_dropdown_setselection(UiList *list, UiListSelection selection) {\r
+void ui_dropdown_setselection_impl(UiList *list, UiListSelection selection) {\r
     UiListView *listview = (UiListView*)list->obj;\r
     SendMessage(listview->widget.hwnd, CB_SETCURSEL, 0, 0);\r
 }\r
+\r
+void ui_dropdown_select(UIWIDGET dropdown, int index) {\r
+    SendMessage(dropdown->hwnd, CB_SETCURSEL, 0, 0);\r
+}\r
+\r
+int ui_dropdown_selection(UIWIDGET dropdown) {\r
+    return SendMessage(dropdown->hwnd, CB_GETCURSEL, 0, 0);\r
+}\r
index 7b1d36b385f9a6f04fbcc32f823a89196532c16e..3074ed40369f29c121f6d18f68423d7c1c7bb317 100644 (file)
@@ -30,7 +30,7 @@
 #define LIST_H\r
 \r
 #include "toolkit.h"\r
-#include "../ui/tree.h"\r
+#include "../ui/list.h"\r
 #include "win32.h"\r
 #include <commctrl.h>\r
 \r
@@ -66,15 +66,15 @@ int ui_listview_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam
 W32Size ui_listview_get_preferred_size(W32Widget *widget);\r
 \r
 void ui_listview_update(UiList *list, int row);\r
-UiListSelection ui_listview_getselection(UiList *list);\r
-void ui_listview_setselection(UiList *list, UiListSelection selection);\r
+UiListSelection ui_listview_getselection_impl(UiList *list);\r
+void ui_listview_setselection_impl(UiList *list, UiListSelection selection);\r
 \r
 int ui_dropdown_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
 W32Size ui_dropdown_get_preferred_size(W32Widget *widget);\r
 \r
 void ui_dropdown_update(UiList *list, int row);\r
-UiListSelection ui_dropdown_getselection(UiList *list);\r
-void ui_dropdown_setselection(UiList *list, UiListSelection selection);\r
+UiListSelection ui_dropdown_getselection_impl(UiList *list);\r
+void ui_dropdown_setselection_impl(UiList *list, UiListSelection selection);\r
 \r
 #ifdef __cplusplus\r
 }\r
diff --git a/ui/win32/menu.c b/ui/win32/menu.c
new file mode 100644 (file)
index 0000000..c825866
--- /dev/null
@@ -0,0 +1,97 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2024 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#include "menu.h"\r
+\r
+static ui_menu_add_f createMenuItem[] = {\r
+    /* UI_MENU                 */ ui_add_menu,\r
+    /* UI_MENU_ITEM            */ ui_add_menu_item,\r
+    /* UI_MENU_CHECK_ITEM      */ ui_add_menu_checkitem,\r
+    /* UI_MENU_RADIO_ITEM      */ ui_add_menu_radioitem,\r
+    /* UI_MENU_ITEM_LIST       */ ui_add_menu_list,\r
+    /* UI_MENU_CHECKITEM_LIST  */ ui_add_menu_checklist,\r
+    /* UI_MENU_RADIOITEM_LIST  */ ui_add_menu_radiolist,\r
+    /* UI_MENU_SEPARATOR       */ ui_add_menu_separator\r
+};\r
+\r
+\r
+HMENU ui_create_main_menu(UiObject *obj) {\r
+    UiMenu *menu = uic_get_menu_list();\r
+    if (!menu) {\r
+        return NULL;\r
+    }\r
+\r
+    HMENU hMenu = CreateMenu();\r
+    ui_add_menu(hMenu, 0, &menu->item, obj);\r
+\r
+\r
+\r
+    return hMenu;\r
+}\r
+\r
+void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+    UiMenu *menu = (UiMenu*)item;\r
+    HMENU hMenu = CreatePopupMenu();\r
+    AppendMenu(parent, MF_POPUP, (UINT_PTR)hMenu, menu->label);\r
+\r
+    int i = 0;\r
+    UiMenuItemI *child = menu->items_begin;\r
+    while (child) {\r
+        createMenuItem[child->type](hMenu, i++, child, obj);\r
+        child = child->next;\r
+    }\r
+}\r
+\r
+void ui_add_menu_item(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+    UiMenuItem *i = (UiMenuItem*)item;\r
+    AppendMenu(parent, MF_STRING, 0, i->label);\r
+}\r
+\r
+void ui_add_menu_checkitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
+\r
+void ui_add_menu_radioitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
+\r
+void ui_add_menu_list(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
+\r
+void ui_add_menu_checklist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
+\r
+void ui_add_menu_radiolist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
+\r
+void ui_add_menu_separator(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+\r
+}\r
diff --git a/ui/win32/menu.h b/ui/win32/menu.h
new file mode 100644 (file)
index 0000000..13bbf0c
--- /dev/null
@@ -0,0 +1,57 @@
+/*\r
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.\r
+ *\r
+ * Copyright 2025 Olaf Wintermann. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above copyright\r
+ *      notice, this list of conditions and the following disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above copyright\r
+ *      notice, this list of conditions and the following disclaimer in the\r
+ *      documentation and/or other materials provided with the distribution.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+\r
+#ifndef MENU_H\r
+#define MENU_H\r
+\r
+#include "toolkit.h"\r
+#include "../ui/menu.h"\r
+#include "../common/menu.h"\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+typedef void(*ui_menu_add_f)(HMENU, int, UiMenuItemI*, UiObject*);\r
+\r
+HMENU ui_create_main_menu(UiObject *obj);\r
+\r
+void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_item(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_checkitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_radioitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_list(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_checklist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_radiolist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+void ui_add_menu_separator(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif //MENU_H
\ No newline at end of file
index 85a6fb337774327b87c72ec3a288242e1b98cbeb..c23935447b489bd074b2227ebfe1ae066876173e 100644 (file)
@@ -39,6 +39,7 @@ WIN32OBJ += button.obj
 WIN32OBJ += grid.obj
 WIN32OBJ += text.obj
 WIN32OBJ += list.obj
+WIN32OBJ += menu.obj
 
 TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%)
 TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c)
index f73cc5bbcf6dc21e0820a626bee07de62dc873e6..2ed15357d890c355b7bd9daf1f17b2b5fe8877c9 100644 (file)
@@ -35,6 +35,7 @@
 #include "../common/toolbar.h"
 #include "../common/document.h"
 #include "../common/properties.h"
+#include "../common/app.h"
 
 #include "../ui/widget.h"
 
 
 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 HFONT ui_font = NULL;
 
 void ui_init(const char *appname, int argc, char **argv) {
@@ -91,25 +85,8 @@ 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() {
-    if(startup_func) {
-        startup_func(NULL, startup_data);
-    }
+    uic_application_startup(NULL);
 
     // event loop
     MSG msg;
@@ -118,9 +95,7 @@ void ui_main() {
         DispatchMessage(&msg);
     }
 
-    if(exit_func) {
-        exit_func(NULL, exit_data);
-    }
+    uic_application_exit(NULL);
     uic_store_app_properties();
 }
 
@@ -161,7 +136,7 @@ LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
         case WM_SIZE: {
             int width  = LOWORD(lParam);
             int height = HIWORD(lParam);
-            if (widget->layout) {
+            if (widget && widget->layout) {
                 widget->layout(widget->layoutmanager, width, height);
             }
             break;
@@ -169,4 +144,8 @@ LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
         default: break;//return DefWindowProc(hwnd, uMsg, wParam, lParam);
     }
     return DefWindowProc(hwnd, uMsg, wParam, lParam);;
-}
\ No newline at end of file
+}
+
+void ui_call_mainthread(ui_threadfunc tf, void* td) {
+    // TODO
+}
index 6fbe7af1f0bd1a169cc0672d7b6ff5d43bb87c2a..1f661d791204cd4b209cd63ec7603c6fd16675d4 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdlib.h>
 
 #include "win32.h"
+#include "menu.h"
 
 static W32WidgetClass w32_toplevel_widget_class = {
        .eventproc = ui_window_widget_event,
@@ -54,9 +55,9 @@ static const char *mainWindowClass = "UiMainWindow";
 
 
 void ui_window_init(void) {
-       hInstance = GetModuleHandle(NULL);
-       
-       WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
+    hInstance = GetModuleHandle(NULL);
+
+    WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
     wc.lpfnWndProc = ui_default_eventproc;
     wc.hInstance = hInstance;
     wc.lpszClassName = mainWindowClass;
@@ -69,50 +70,59 @@ void ui_window_init(void) {
     }
 }
 
-static UiObject* create_window(const char *title, void *window_data, bool simple) {
+static UiObject* create_window(const char *title, bool simple) {
     UiObject *obj = uic_object_new_toplevel();
-    obj->window = window_data;
 
-       HWND hwnd = CreateWindowExA(
-                       0,
-                       "UiMainWindow",
-                       title,
-                       WS_OVERLAPPEDWINDOW,
+    HWND hwnd = CreateWindowExA(
+            0,
+            "UiMainWindow",
+            title,
+            WS_OVERLAPPEDWINDOW,
+            CW_USEDEFAULT,
             CW_USEDEFAULT,
-                       CW_USEDEFAULT,
-                       800,
-                       600,
+            800,
+            600,
+            NULL,
             NULL,
-                       NULL,
-                       hInstance,
-                       NULL);
+            hInstance,
+            NULL);
+
+    if (!simple) {
+        HMENU menubar = ui_create_main_menu(obj);
+        if (menubar) {
+            SetMenu(hwnd, menubar);
+        }
+    }
 
     UpdateWindow(hwnd);
 
-       UiContainerX *container = ui_box_container_create(obj, hwnd, UI_BOX_VERTICAL, 0, INSETS_ZERO);
-       uic_object_push_container(obj, container);
-       UiBoxContainer *box = (UiBoxContainer*)container;
+    UiContainerX *container = ui_box_container_create(obj, hwnd, UI_BOX_VERTICAL, 0, INSETS_ZERO);
+    uic_object_push_container(obj, container);
+    UiBoxContainer *box = (UiBoxContainer*)container;
 
-       UiWindow *widget = w32_widget_create(&w32_toplevel_widget_class, hwnd, sizeof(UiWindow));
-       widget->obj = obj;
-       widget->widget.layout = (W32LayoutFunc)ui_grid_layout;
-       widget->widget.layoutmanager = box->layout;
-       obj->widget = (W32Widget*)widget;
-       obj->ref = 1;
+    UiWindow *widget = w32_widget_create(&w32_toplevel_widget_class, hwnd, sizeof(UiWindow));
+    widget->obj = obj;
+    widget->widget.layout = (W32LayoutFunc)ui_grid_layout;
+    widget->widget.layoutmanager = box->layout;
+    obj->widget = (W32Widget*)widget;
+    obj->ref = 1;
 
-       return obj;
+    return obj;
 }
 
-UiObject *ui_window(const char *title, void *window_data) {
-       return create_window(title, window_data, FALSE);
+UiObject *ui_window(const char *title) {
+    return create_window(title, FALSE);
 }
 
+UiObject *ui_simple_window(const char *title) {
+    return create_window(title, TRUE);
+}
 
 int ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-       //UiWindow *window = (UiWindow*)widget;
-       return 0;
+    //UiWindow *window = (UiWindow*)widget;
+    return 0;
 }
 
 void ui_window_widget_show(W32Widget *w, BOOLEAN show) {
-       ShowWindow(w->hwnd, show ? SW_SHOWNORMAL : SW_HIDE);
+    ShowWindow(w->hwnd, show ? SW_SHOWNORMAL : SW_HIDE);
 }
index bbb63f6b869f3737ddaa915c03adeb203d0fa39f..5027368d026cd0e45a48052cc1521dfdbcdd1303 100644 (file)
@@ -28,7 +28,7 @@
 \r
 #pragma once\r
 \r
-#include "../ui/tree.h"\r
+#include "../ui/list.h"\r
 #include "toolkit.h"\r
 \r
 #include "../ui/container.h"\r
index 51fa97d6ddd9ba92f72085bc5127c23c339e5566..c6959ec489a8c4c51565ee04f3a894742e299dec 100644 (file)
@@ -28,7 +28,7 @@
 \r
 #pragma once\r
 \r
-#include "../ui/tree.h"\r
+#include "../ui/list.h"\r
 #include "toolkit.h"\r
 #include "dnd.h"\r
 \r
index 1844cc28671d1d7d570e7b8adf582ab07c4a0921..42e2b8db3bec2ad61b25b532fe4e446029d17dea 100644 (file)
@@ -37,6 +37,7 @@
 #include "../common/document.h"\r
 #include "../common/toolbar.h"\r
 #include "../common/properties.h"\r
+#include "../common/app.h"\r
 \r
 #include "icons.h"\r
 \r
@@ -55,15 +56,6 @@ using namespace Windows::UI::Core;
 \r
 static const char* application_name;\r
 \r
-static ui_callback   startup_func;\r
-static void* startup_data;\r
-\r
-static ui_callback   open_func;\r
-void* open_data;\r
-\r
-static ui_callback   exit_func;\r
-void* exit_data;\r
-\r
 static ui_callback   appclose_fnc;\r
 \r
 static void* appclose_udata;\r
@@ -75,19 +67,14 @@ static winrt::Microsoft::UI::Dispatching::DispatcherQueue uiDispatcherQueue = {
 \r
 void ui_app_run_startup() {\r
        uiDispatcherQueue = winrt::Microsoft::UI::Dispatching::DispatcherQueue::GetForCurrentThread();\r
-       \r
-       if (startup_func) {\r
-               startup_func(NULL, startup_data);\r
-       }\r
+       uic_application_startup(NULL);\r
 }\r
 \r
 class App : public ApplicationT<App, IXamlMetadataProvider> {\r
 public:\r
        void OnLaunched(LaunchActivatedEventArgs const&) {\r
                Resources().MergedDictionaries().Append(XamlControlsResources());\r
-               if (startup_func) {\r
-                       startup_func(NULL, startup_data);\r
-               }\r
+               uic_application_startup(NULL);\r
 \r
                //auto window = make<winui::implementation::MainWindow>();\r
                //window.Activate();\r
@@ -175,21 +162,6 @@ const char* ui_appname() {
        return application_name;\r
 }\r
 \r
-void ui_onstartup(ui_callback f, void* userdata) {\r
-       startup_func = f;\r
-       startup_data = userdata;\r
-}\r
-\r
-void ui_onopen(ui_callback f, void* userdata) {\r
-       open_func = f;\r
-       open_data = userdata;\r
-}\r
-\r
-void ui_onexit(ui_callback f, void* userdata) {\r
-       exit_func = f;\r
-       exit_data = userdata;\r
-}\r
-\r
 void ui_main() {\r
        /*\r
        init_apartment();\r
index 4285e092b930b1ee0d41efc03f7563e6a4011966..fcb01d1f3c39015f676ba2024eb5f2ed473fa53b 100644 (file)
@@ -68,89 +68,88 @@ extern "C" static void ui_window_widget_destroy(UiObject *obj) {
        obj->wobj->window.Close();\r
 }\r
 \r
-UiObject* ui_window(const char* title, void* window_data) {\r
-       UiObject* obj = ui_simple_window(title, window_data);\r
-\r
-       /*\r
-       if (uic_get_menu_list()) {\r
-               // create/add menubar\r
-               MenuBar mb = ui_create_menubar(obj);\r
-               mb.VerticalAlignment(VerticalAlignment::Top);\r
-               obj->container->Add(mb, false);\r
-       }\r
-       */\r
-\r
-       if (uic_toolbar_isenabled()) {\r
-               // create a grid for the toolbar: ColumnDefinitions="Auto, *, Auto"\r
-               Grid toolbar_grid = Grid();\r
-               GridLength gl;\r
-               gl.Value = 0;\r
-               gl.GridUnitType = GridUnitType::Auto;\r
-\r
-               ColumnDefinition coldef0 = ColumnDefinition();\r
-               coldef0.Width(gl);\r
-               toolbar_grid.ColumnDefinitions().Append(coldef0);\r
-\r
-               gl.Value = 1;\r
-               gl.GridUnitType = GridUnitType::Star;\r
-               ColumnDefinition coldef1 = ColumnDefinition();\r
-               coldef1.Width(gl);\r
-               toolbar_grid.ColumnDefinitions().Append(coldef1);\r
-\r
-               gl.Value = 0;\r
-               gl.GridUnitType = GridUnitType::Auto;\r
-               ColumnDefinition coldef2 = ColumnDefinition();\r
-               coldef2.Width(gl);\r
-               toolbar_grid.ColumnDefinitions().Append(coldef2);\r
-\r
-               // rowdef\r
-               gl.Value = 0;\r
-               gl.GridUnitType = GridUnitType::Auto;\r
-               RowDefinition rowdef = RowDefinition();\r
-               rowdef.Height(gl);\r
-               toolbar_grid.RowDefinitions().Append(rowdef);\r
-\r
-\r
-               // create commandbar\r
-               CxList* def_l = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);\r
-               CxList* def_c = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);\r
-               CxList* def_r = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);\r
-\r
-               bool addappmenu = true;\r
-               if (cxListSize(def_r) > 0) {\r
-                       CommandBar toolbar_r = ui_create_toolbar(obj, def_r, addappmenu);\r
-                       toolbar_grid.SetColumn(toolbar_r, 2);\r
-                       toolbar_grid.SetRow(toolbar_r, 0);\r
-                       toolbar_grid.Children().Append(toolbar_r);\r
-                       addappmenu = false;\r
-               }\r
-               if (cxListSize(def_c) > 0) {\r
-                       CommandBar toolbar_c = ui_create_toolbar(obj, def_c, addappmenu);\r
-                       toolbar_c.HorizontalAlignment(HorizontalAlignment::Center);\r
-                       toolbar_grid.SetColumn(toolbar_c, 1);\r
-                       toolbar_grid.SetRow(toolbar_c, 0);\r
-                       toolbar_grid.Children().Append(toolbar_c);\r
-                       addappmenu = false;\r
-               }\r
-               if (cxListSize(def_l) > 0) {\r
-                       CommandBar toolbar_l = ui_create_toolbar(obj, def_l, addappmenu);\r
-                       toolbar_grid.SetColumn(toolbar_l, 0);\r
-                       toolbar_grid.SetRow(toolbar_l, 0);\r
-                       toolbar_grid.Children().Append(toolbar_l);\r
-               }\r
-\r
-               toolbar_grid.VerticalAlignment(VerticalAlignment::Top);\r
-               obj->container->Add(toolbar_grid, false);\r
-       }\r
-\r
-       return obj;\r
+UiObject* ui_window(const char* title) {\r
+    UiObject* obj = ui_simple_window(title);\r
+\r
+    /*\r
+    if (uic_get_menu_list()) {\r
+            // create/add menubar\r
+            MenuBar mb = ui_create_menubar(obj);\r
+            mb.VerticalAlignment(VerticalAlignment::Top);\r
+            obj->container->Add(mb, false);\r
+    }\r
+    */\r
+\r
+    if (uic_toolbar_isenabled()) {\r
+        // create a grid for the toolbar: ColumnDefinitions="Auto, *, Auto"\r
+        Grid toolbar_grid = Grid();\r
+        GridLength gl;\r
+        gl.Value = 0;\r
+        gl.GridUnitType = GridUnitType::Auto;\r
+\r
+        ColumnDefinition coldef0 = ColumnDefinition();\r
+        coldef0.Width(gl);\r
+        toolbar_grid.ColumnDefinitions().Append(coldef0);\r
+\r
+        gl.Value = 1;\r
+        gl.GridUnitType = GridUnitType::Star;\r
+        ColumnDefinition coldef1 = ColumnDefinition();\r
+        coldef1.Width(gl);\r
+        toolbar_grid.ColumnDefinitions().Append(coldef1);\r
+\r
+        gl.Value = 0;\r
+        gl.GridUnitType = GridUnitType::Auto;\r
+        ColumnDefinition coldef2 = ColumnDefinition();\r
+        coldef2.Width(gl);\r
+        toolbar_grid.ColumnDefinitions().Append(coldef2);\r
+\r
+        // rowdef\r
+        gl.Value = 0;\r
+        gl.GridUnitType = GridUnitType::Auto;\r
+        RowDefinition rowdef = RowDefinition();\r
+        rowdef.Height(gl);\r
+        toolbar_grid.RowDefinitions().Append(rowdef);\r
+\r
+\r
+        // create commandbar\r
+        CxList* def_l = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);\r
+        CxList* def_c = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);\r
+        CxList* def_r = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);\r
+\r
+        bool addappmenu = true;\r
+        if (cxListSize(def_r) > 0) {\r
+            CommandBar toolbar_r = ui_create_toolbar(obj, def_r, addappmenu);\r
+            toolbar_grid.SetColumn(toolbar_r, 2);\r
+            toolbar_grid.SetRow(toolbar_r, 0);\r
+            toolbar_grid.Children().Append(toolbar_r);\r
+            addappmenu = false;\r
+        }\r
+        if (cxListSize(def_c) > 0) {\r
+            CommandBar toolbar_c = ui_create_toolbar(obj, def_c, addappmenu);\r
+            toolbar_c.HorizontalAlignment(HorizontalAlignment::Center);\r
+            toolbar_grid.SetColumn(toolbar_c, 1);\r
+            toolbar_grid.SetRow(toolbar_c, 0);\r
+            toolbar_grid.Children().Append(toolbar_c);\r
+            addappmenu = false;\r
+        }\r
+        if (cxListSize(def_l) > 0) {\r
+            CommandBar toolbar_l = ui_create_toolbar(obj, def_l, addappmenu);\r
+            toolbar_grid.SetColumn(toolbar_l, 0);\r
+            toolbar_grid.SetRow(toolbar_l, 0);\r
+            toolbar_grid.Children().Append(toolbar_l);\r
+        }\r
+\r
+        toolbar_grid.VerticalAlignment(VerticalAlignment::Top);\r
+        obj->container->Add(toolbar_grid, false);\r
+    }\r
+\r
+    return obj;\r
 }\r
 \r
-UIEXPORT UiObject* ui_simple_window(const char *title, void *window_data) {\r
+UIEXPORT UiObject* ui_simple_window(const char *title) {\r
        UiObject* obj = uic_object_new_toplevel();\r
 \r
        obj->ctx = uic_context(obj, mp);\r
-       obj->window = window_data;\r
 \r
        Window window = Window();\r
        //Window window = make<winui::implementation::MainWindow>();\r
index 9f72b38647a02d0e4eab0b4a15569e7a7c0995f4..6caa289022f13694cdb9ef3eb9f5ecd44d286c38 100644 (file)
     <ClInclude Include="..\ui\text.h" />\r
     <ClInclude Include="..\ui\toolbar.h" />\r
     <ClInclude Include="..\ui\toolkit.h" />\r
-    <ClInclude Include="..\ui\tree.h" />\r
+    <ClInclude Include="..\ui\list.h" />\r
     <ClInclude Include="..\ui\ui.h" />\r
     <ClInclude Include="..\ui\window.h" />\r
     <ClInclude Include="appmenu.h" />\r
index 7340b7fdcc8aaf8b58081cb1170e85cb6dd05e09..eb8ea019fa3e529dff5761d3aafdc9b2563a0f09 100644 (file)
     <ClInclude Include="..\ui\toolkit.h">\r
       <Filter>public</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="..\ui\tree.h">\r
+    <ClInclude Include="..\ui\list.h">\r
       <Filter>public</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\ui\ui.h">\r