]> uap-core.de Git - note.git/commitdiff
add test_note_store_new_resource_async main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 12 Jan 2026 20:33:00 +0000 (21:33 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 12 Jan 2026 20:33:00 +0000 (21:33 +0100)
16 files changed:
application/gtk-image.c
application/tests/test-store.c
application/tests/test-store.h
application/tests/testmain.c
ui/common/object.c
ui/common/properties.c
ui/common/threadpool.c
ui/gtk/text.c
ui/gtk/toolkit.c
ui/gtk/toolkit.h
ui/gtk/window.c
ui/gtk/window.h [new file with mode: 0644]
ui/motif/text.c
ui/motif/toolkit.c
ui/ui/text.h
ui/ui/toolkit.h

index 9f796b3bee00d608c65328c581789da32fde4913..1b01e416005d01c8ec57fe8124367aa48581800f 100644 (file)
@@ -94,4 +94,4 @@ GtkWidget* embedded_image_create(EmbeddedWidget *em, GdkPixbuf *pix) {
 }
 
 
-#endif
\ No newline at end of file
+#endif
index 33e9b2ccef3cef0faddc7bfbc9a79c3a3892d47f..ad931e29bb295d5ca38be4889f9964cf61462fd0 100644 (file)
@@ -137,3 +137,45 @@ CX_TEST(test_note_store_reload) {
     }
 }
 
+static void test_new_resource_result(UiEvent *event, int64_t newid, int error, void *userdata) {
+    int64_t *ptr = userdata;
+    ptr[0] = newid;
+    ptr[1] = error;
+}
+
+CX_TEST(test_note_store_new_resource_async) {
+    CX_TEST_DO {
+        UiObject *obj = ui_dummy_object();
+        
+        NoteStore *store = note_store_get();
+        CX_TEST_ASSERT(store);
+        
+        Resource res0 = { 0 };
+        res0.nodename = "test_note_store_new_resource_async";
+        res0.parent_id = store->root->resource_id;
+        res0.iscollection = TRUE;
+        
+        int64_t result[2] = { 0, -1 };
+        note_store_new_resource_async(obj, &res0, test_new_resource_result, result);
+        ui_exec_buffered_mainthread_calls_wait(3);
+        
+        CX_TEST_ASSERT(result[0] > 0);
+        CX_TEST_ASSERT(result[1] == 0);
+        
+        CX_TEST_ASSERT(!note_store_reload());
+        store = note_store_get();
+        
+        CX_TEST_ASSERT(store && store->root && store->root->children);
+        int res_found = 0;
+        CxIterator i = cxListIterator(store->root->children);
+        cx_foreach(Resource *, resource, i) {
+            if(!cx_strcmp(resource->nodename, res0.nodename)) {
+                res_found = 1;
+                break;
+            }
+        }
+        CX_TEST_ASSERT(res_found);
+        
+        ui_close(obj);
+    }
+}
index 92dabd578140ae3057ff38c0383d7eedc9dfb015..f4c1b316e6fd04461c791179243d3a07ea258873 100644 (file)
@@ -40,7 +40,7 @@ CX_TEST(test_note_store_create_default);
 CX_TEST(test_user_settings_is_valid);
 
 CX_TEST(test_note_store_reload);
-
+CX_TEST(test_note_store_new_resource_async);
 
 
 #ifdef __cplusplus
index bc272e14b796812b3c4425970f24ba67830442b7..79a4207bd30f28d40e9a2f3701f070e15df20004 100644 (file)
@@ -48,12 +48,16 @@ int main(int argc, char **argv) {
     ui_init(NULL, 0, NULL);
     register_types();
     
+    // required for testing async functions, that use ui_call_mainthread
+    ui_buffer_mainthread_calls(TRUE);
+    
     CxTestSuite *suite = cx_test_suite_new("note");
     
     cx_test_register(suite, test_init_note_store);
     cx_test_register(suite, test_note_store_create_default);
     cx_test_register(suite, test_user_settings_is_valid);
     cx_test_register(suite, test_note_store_reload);
+    cx_test_register(suite, test_note_store_new_resource_async);
     
     cx_test_register(suite, test_parse_markdown_para);
     cx_test_register(suite, test_parse_markdown_formatting_simple);
index f1582307ef28081cd2889fac28f2fa6c8964720b..9b197d7edb235905c4579f07721859bed49c8dfc 100644 (file)
@@ -60,6 +60,12 @@ void ui_register_object_destruction_callback(ui_object_callback func, void *user
     cxListAdd(destruction_callbacks, &cb);
 }
 
+UiObject* ui_dummy_object(void) {
+    UiObject *obj = uic_object_new_toplevel();
+    obj->ref = 1;
+    return obj;
+}
+
 void uic_object_created(UiObject *obj) {
     CxIterator i = cxListIterator(creation_callbacks);
     cx_foreach(objcallback *, cb, i) {
index bedc918793d62061bde27bebbe6703ac8e7d516b..29c8ce60f47ec64f988abdbeb0c26e31ec6787ef 100644 (file)
@@ -234,7 +234,6 @@ void uic_load_app_properties() {
         return;
     }
     
-    
     char *dir = ui_configfile(NULL);
     if(!dir) {
         return;
index 5d3b72adee40f53fb1b84b3e5ddf4c2f630d4d59..0710dcd48aa4cfd2674de020072f0207a93439a7 100644 (file)
@@ -42,6 +42,7 @@ static threadpool_job kill_job;
 
 
 static pthread_mutex_t mc_buffer_mutex;
+static pthread_cond_t  mc_buffer_available;
 static CxList *mainthread_call_buffer;
 static volatile int mainthread_call_buffered = 0;
 
@@ -52,6 +53,7 @@ typedef struct UiMainCall {
 
 void uic_init_threads(void) {
     pthread_mutex_init(&mc_buffer_mutex, NULL);
+    pthread_cond_init(&mc_buffer_available, NULL);
     mainthread_call_buffer = cxLinkedListCreate(NULL, sizeof(UiMainCall));
 }
 
@@ -65,6 +67,7 @@ void uic_add_buffered_mainthread_call(ui_threadfunc func, void *data) {
     call.func = func;
     call.data = data;
     cxListAdd(mainthread_call_buffer, &call);
+    pthread_cond_signal(&mc_buffer_available);
     pthread_mutex_unlock(&mc_buffer_mutex);
 }
 
@@ -75,8 +78,8 @@ void ui_buffer_mainthread_calls(UiBool enable_buffering) {
         ui_exec_buffered_mainthread_calls();
     }
 }
-void ui_exec_buffered_mainthread_calls(void) {
-    pthread_mutex_lock(&mc_buffer_mutex);
+
+static void exec_buffered_calls(void) {
     CxIterator i = cxListIterator(mainthread_call_buffer);
     cx_foreach(UiMainCall *, call, i) {
         if(call->func) {
@@ -84,10 +87,34 @@ void ui_exec_buffered_mainthread_calls(void) {
         }
     }
     cxListClear(mainthread_call_buffer);
-    pthread_mutex_unlock(&mc_buffer_mutex);
 }
 
+void ui_exec_buffered_mainthread_calls(void) {
+    pthread_mutex_lock(&mc_buffer_mutex);
+    exec_buffered_calls();
+    pthread_mutex_unlock(&mc_buffer_mutex);
+}
 
+UIEXPORT void ui_exec_buffered_mainthread_calls_wait(int timeout) {
+    struct timespec ts;
+    if(timeout > 0) {
+        clock_gettime(CLOCK_REALTIME, &ts);
+        ts.tv_sec += timeout;
+    }
+    
+    pthread_mutex_lock(&mc_buffer_mutex);
+    while(cxListSize(mainthread_call_buffer) == 0) {
+        if(timeout > 0) {
+            if(pthread_cond_timedwait(&mc_buffer_available, &mc_buffer_mutex, &ts)) {
+                break;
+            }
+        } else {
+            pthread_cond_wait(&mc_buffer_available, &mc_buffer_mutex);
+        }
+    }
+    exec_buffered_calls();
+    pthread_mutex_unlock(&mc_buffer_mutex);
+}
 
 UiThreadpool* threadpool_new(int min, int max) {
     UiThreadpool *pool = malloc(sizeof(UiThreadpool));
index 0bcd54df3e4c3eb071e718be050bf731c6f20512..0c352192e1f7b193148ca6c24fd32ed5f3447426 100644 (file)
@@ -687,6 +687,41 @@ UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args) {
     return create_textfield(obj, FALSE, TRUE, args);
 }
 
+void ui_textfield_focus(UIWIDGET textfield) {
+    gtk_widget_grab_focus(textfield);
+}
+
+void ui_textfield_focus_without_selecting(UIWIDGET textfield) {
+#if GTK_CHECK_VERSION(3, 16, 0)
+    gtk_entry_grab_focus_without_selecting(GTK_ENTRY(textfield));
+#else
+    gtk_widget_grab_focus(textfield);
+#endif
+}
+
+void ui_textfield_set_selection(UIWIDGET textfield, int begin, int end) {
+    ENTRY_SET_SELECTION(textfield, begin, end);
+}
+
+void ui_textfield_select_all(UIWIDGET textfield) {
+    ENTRY_SET_SELECTION(textfield, 0, -1);
+}
+
+void ui_textfield_set_editable(UIWIDGET textfield, UiBool editable) {
+    ENTRY_SET_EDITABLE(textfield, editable);
+}
+
+UiBool ui_textfield_is_editable(UIWIDGET textfield) {
+    ENTRY_IS_EDITABLE(textfield);
+}
+
+void ui_textfield_set_position(UIWIDGET textfield, int pos) {
+    ENTRY_SET_POSITION(textfield, pos);
+}
+
+int ui_textfield_get_position(UIWIDGET textfield) {
+    return ENTRY_GET_POSITION(textfield);
+}
 
 void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) {
     free(textfield);
index d775f3b08613724e2ea7cba291a7d9f9cb2a7a0b..9009bcaa5268a721d8defd9a29e2b9a246a9e40d 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "toolkit.h"
 #include "toolbar.h"
+#include "window.h"
 #include "icon.h"
 #include "../common/document.h"
 #include "../common/properties.h"
@@ -145,27 +146,33 @@ GtkApplication* ui_get_application() {
 #endif
 
 void ui_show(UiObject *obj) {
-    gboolean visible = gtk_widget_is_visible(obj->widget);
-    
+    gboolean visible = FALSE;
     uic_check_state_widgets(obj->ctx);
+    if(obj->widget) {
+        visible = gtk_widget_is_visible(obj->widget);
 #if GTK_MAJOR_VERSION >= 4
-    gtk_window_present(GTK_WINDOW(obj->widget));
+        gtk_window_present(GTK_WINDOW(obj->widget));
 #elif GTK_MAJOR_VERSION <= 3
-    gtk_widget_show_all(obj->widget);
+        gtk_widget_show_all(obj->widget);
 #endif
-    
+    }
+      
     if(!visible) {
         obj->ref++;
     }
 }
 
 void ui_close(UiObject *obj) {
-    uic_context_prepare_close(obj->ctx);
+    uic_context_prepare_close(obj->ctx); // TODO: should this be moved to the close event handler?
+    if(obj->widget) {
 #if GTK_CHECK_VERSION(4, 0, 0)
-    gtk_window_close(GTK_WINDOW(obj->widget));
+        gtk_window_close(GTK_WINDOW(obj->widget));
 #else
-    gtk_widget_destroy(obj->widget);
+        gtk_widget_destroy(obj->widget);
 #endif
+    } else {
+        ui_window_close_request(obj);
+    }
 }
 
 
index 0e421b9c996906306e14664b32f5c8ec396634a4..e16f3614806e243ca48f9049eb55da26f8aacce5 100644 (file)
@@ -65,6 +65,11 @@ extern "C" {
 #define BOX_REMOVE(box, child) gtk_box_remove(GTK_BOX(box), child)
 #define ENTRY_SET_TEXT(entry, text) gtk_editable_set_text(GTK_EDITABLE(entry), text)
 #define ENTRY_GET_TEXT(entry) gtk_editable_get_text(GTK_EDITABLE(entry))
+#define ENTRY_SET_SELECTION(entry, begin, end) gtk_editable_select_region(GTK_EDITABLE(entry), begin, end) 
+#define ENTRY_SET_EDITABLE(entry, editable) gtk_editable_set_editable(GTK_EDITABLE(entry), editable)
+#define ENTRY_IS_EDITABLE(entry) gtk_editable_get_editable(GTK_EDITABLE(entry))
+#define ENTRY_SET_POSITION(entry, pos) gtk_editable_set_position(GTK_EDITABLE(entry), pos)
+#define ENTRY_GET_POSITION(entry) gtk_editable_get_position(GTK_EDITABLE(entry))
 #define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new()
 #define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), child)
 #define SCROLLEDWINDOW_GET_CHILD(sw) gtk_scrolled_window_get_child(GTK_SCROLLED_WINDOW(sw))
@@ -90,6 +95,11 @@ extern "C" {
 #define BOX_REMOVE(box, child) gtk_container_remove(GTK_CONTAINER(box), child)
 #define ENTRY_SET_TEXT(entry, text) gtk_entry_set_text(GTK_ENTRY(entry), text)
 #define ENTRY_GET_TEXT(entry) gtk_entry_get_text(GTK_ENTRY(entry))
+#define ENTRY_SET_SELECTION(entry, begin, end) gtk_editable_select_region(GTK_EDITABLE(entry), begin, end)
+#define ENTRY_SET_EDITABLE(entry, editable) gtk_editable_set_editable(GTK_EDITABLE(entry), editable)
+#define ENTRY_IS_EDITABLE(entry) gtk_editable_get_editable(GTK_EDITABLE(entry))
+#define ENTRY_SET_POSITION(entry, pos) gtk_editable_set_position(GTK_EDITABLE(entry), pos)
+#define ENTRY_GET_POSITION(entry) gtk_editable_get_position(GTK_EDITABLE(entry))
 #define SCROLLEDWINDOW_NEW() gtk_scrolled_window_new(NULL, NULL)
 #define SCROLLEDWINDOW_SET_CHILD(sw, child) gtk_container_add(GTK_CONTAINER(sw), child)
 #define SCROLLEDWINDOW_GET_CHILD(sw) gtk_bin_get_child(GTK_BIN(sw))
index 10463914976923d0a62a3c8d9689a74ffd2d7950..fc5e7b27ca7ad9d342d332e29201d50b31c2a057 100644 (file)
@@ -44,6 +44,7 @@
 #include "container.h"
 #include "headerbar.h"
 #include "button.h"
+#include "window.h"
 
 static int nwindows = 0;
 
@@ -80,7 +81,7 @@ void ui_exit_event(GtkWidget *widget, gpointer data) {
     g_idle_add(ui_window_destroy, data);
 }
 
-static gboolean ui_window_close_request(UiObject *obj) {
+gboolean ui_window_close_request(UiObject *obj) {
     if(obj->widget) {
         void *appwindow = g_object_get_data(G_OBJECT(obj->widget), "ui.appwindow");
         if(appwindow) {
@@ -111,7 +112,6 @@ static gboolean ui_window_close_request(UiObject *obj) {
         }
     }
     
-    uic_context_prepare_close(obj->ctx);
     obj->ref--;
     if(obj->ref > 0) {
 #if GTK_CHECK_VERSION(2, 18, 0)
@@ -121,6 +121,7 @@ static gboolean ui_window_close_request(UiObject *obj) {
 #endif
         return TRUE;
     } else {
+        uic_context_prepare_close(obj->ctx);
         return FALSE;
     }
 }
diff --git a/ui/gtk/window.h b/ui/gtk/window.h
new file mode 100644 (file)
index 0000000..1b85fd9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include "toolkit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+gboolean ui_window_close_request(UiObject *obj);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINDOW_H */
+
index b2c5389eaa3f2da819b1788e1821607856d11fd6..09841df67dd6029ce19e5fd88b498b03349a3c71 100644 (file)
@@ -492,6 +492,39 @@ UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args) {
     return create_textfield(obj, args, FALSE, FALSE);
 }
 
+void ui_textfield_focus(UIWIDGET textfield) {
+    ui_textfield_focus_without_selecting(textfield);
+    ui_textfield_select_all(textfield);
+}
+
+void ui_textfield_focus_without_selecting(UIWIDGET textfield) {
+    XmProcessTraversal(textfield, XmTRAVERSE_CURRENT);
+}
+
+void ui_textfield_set_selection(UIWIDGET textfield, int begin, int end) {
+    XmTextSetSelection(textfield, begin, end, 0);
+}
+
+void ui_textfield_select_all(UIWIDGET textfield) {
+    XmTextSetSelection(textfield, 0, XmTextGetLastPosition(textfield), 0);
+}
+
+void ui_textfield_set_editable(UIWIDGET textfield, UiBool editable) {
+    XmTextFieldSetEditable(textfield, editable);
+}
+
+UiBool ui_textfield_is_editable(UIWIDGET textfield) {
+    return XmTextFieldGetEditable(textfield);
+}
+
+void ui_textfield_set_position(UIWIDGET textfield, int pos) {
+    XmTextFieldSetInsertionPosition(textfield, pos);
+}
+
+int ui_textfield_get_position(UIWIDGET textfield) {
+    return (int)XmTextFieldGetInsertionPosition(textfield);
+}
+
 char* ui_textfield_get(UiString *str) {
     if(str->value.free) {
         str->value.free(str->value.ptr);
index da7ab6ec822308087fdfdec13db0e527766d15e5..d70c25f80e3a456f93b810e0efc47fec06f022e5 100644 (file)
@@ -39,6 +39,7 @@
 #include "../common/document.h"
 #include "../common/properties.h"
 #include "../common/app.h"
+#include "../common/threadpool.h"
 #include <cx/buffer.h>
 
 #include <X11/Intrinsic.h>
@@ -155,8 +156,12 @@ void ui_secondary_event_loop(int *loop) {
 
 void ui_show(UiObject *obj) {
     uic_check_state_widgets(obj->ctx);
-    if(!XtIsRealized(obj->widget)) {
-        XtRealizeWidget(obj->widget);
+    if(obj->widget) {
+        if(!XtIsRealized(obj->widget)) {
+            XtRealizeWidget(obj->widget);
+            obj->ref++;
+        }
+    } else {
         obj->ref++;
     }
 }
index c102f0b36ce20b74160aa1b2494b599c099dd92c..4e9265913606c4c4b5600ad61caaf5e61b2ed56e 100644 (file)
@@ -155,6 +155,15 @@ UIEXPORT UIWIDGET ui_frameless_textfield_create(UiObject* obj, UiTextFieldArgs *
 UIEXPORT UIWIDGET ui_passwordfield_create(UiObject* obj, UiTextFieldArgs *args);
 
 UIEXPORT UIWIDGET ui_path_textfield_create(UiObject* obj, UiPathTextFieldArgs *args);
+
+UIEXPORT void ui_textfield_focus(UIWIDGET textfield);
+UIEXPORT void ui_textfield_focus_without_selecting(UIWIDGET textfield);
+UIEXPORT void ui_textfield_set_selection(UIWIDGET textfield, int begin, int end);
+UIEXPORT void ui_textfield_select_all(UIWIDGET textfield);
+UIEXPORT void ui_textfield_set_editable(UIWIDGET textfield, UiBool editable);
+UIEXPORT UiBool ui_textfield_is_editable(UIWIDGET textfield);
+UIEXPORT void ui_textfield_set_position(UIWIDGET textfield, int pos);
+UIEXPORT int ui_textfield_get_position(UIWIDGET textfield);
         
 #ifdef __cplusplus
 }
index 6c0152b94546d4f7f4615bb2d24eaed535df9e57..fff2dda4486550ded23b0339ad4c13720922c1fc 100644 (file)
@@ -554,11 +554,14 @@ UIEXPORT void ui_app_quit(void);
 UIEXPORT void ui_show(UiObject *obj);
 UIEXPORT void ui_close(UiObject *obj);
 
+UIEXPORT UiObject* ui_dummy_object(void);
+
 UIEXPORT void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd);
 UIEXPORT void ui_call_mainthread(ui_threadfunc tf, void* td);
 
 UIEXPORT void ui_buffer_mainthread_calls(UiBool enable_buffering);
 UIEXPORT void ui_exec_buffered_mainthread_calls(void);
+UIEXPORT void ui_exec_buffered_mainthread_calls_wait(int timeout);
 
 UIEXPORT UiThreadpool* ui_threadpool_create(int nthreads);
 UIEXPORT void ui_threadpool_destroy(UiThreadpool* pool);