}
-#endif
\ No newline at end of file
+#endif
}
}
+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);
+ }
+}
CX_TEST(test_user_settings_is_valid);
CX_TEST(test_note_store_reload);
-
+CX_TEST(test_note_store_new_resource_async);
#ifdef __cplusplus
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);
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) {
return;
}
-
char *dir = ui_configfile(NULL);
if(!dir) {
return;
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;
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));
}
call.func = func;
call.data = data;
cxListAdd(mainthread_call_buffer, &call);
+ pthread_cond_signal(&mc_buffer_available);
pthread_mutex_unlock(&mc_buffer_mutex);
}
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) {
}
}
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));
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);
#include "toolkit.h"
#include "toolbar.h"
+#include "window.h"
#include "icon.h"
#include "../common/document.h"
#include "../common/properties.h"
#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);
+ }
}
#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))
#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))
#include "container.h"
#include "headerbar.h"
#include "button.h"
+#include "window.h"
static int nwindows = 0;
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) {
}
}
- uic_context_prepare_close(obj->ctx);
obj->ref--;
if(obj->ref > 0) {
#if GTK_CHECK_VERSION(2, 18, 0)
#endif
return TRUE;
} else {
+ uic_context_prepare_close(obj->ctx);
return FALSE;
}
}
--- /dev/null
+/*
+ * 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 */
+
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);
#include "../common/document.h"
#include "../common/properties.h"
#include "../common/app.h"
+#include "../common/threadpool.h"
#include <cx/buffer.h>
#include <X11/Intrinsic.h>
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++;
}
}
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
}
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);