*/
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);
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) {
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));
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));
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);
}
}
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");
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);
}
}
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);
}
}
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);
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) {
}
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);
}
/*
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);
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));
}
}
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);
}
}
// 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);
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
#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
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
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;
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
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
) {
allocator->cl->free(allocator->data, mem);
}
+
+void cxFreeDefault(void *mem) {
+ cxDefaultAllocator->cl->free(cxDefaultAllocator->data, mem);
+}
#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);
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));
}
}
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);
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);
}
}
}
-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;
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(
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;
}
}
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(
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.
*/
#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"
#include "common.h"
#include "allocator.h"
+#include "string.h"
#ifdef __cplusplus
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
*/
#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 {
};
/** 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;
/**
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.
*
* 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
*
* @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()
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.
*
* @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()
*/
/**
* 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.
*
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.
*
*
* 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.
*
*
* @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
/**
* 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.
#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)
#include "string.h"
#include "buffer.h"
#include "array_list.h"
+#include "map.h"
#include <string.h>
*/
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.
*/
*/
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.
*/
/**
* 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);
};
/**
* The literal type if the type is #CX_JSON_LITERAL.
*/
CxJsonLiteral literal;
- } value;
+ };
};
/**
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.
* 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.
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.
*
/**
* 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
*/
*/
#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.
*
*/
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;
}
/**
*/
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;
}
/**
*/
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;
}
/**
*/
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;
}
/**
*/
cx_attr_nonnull
CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
- return value->value.literal == CX_JSON_TRUE;
+ return value->literal == CX_JSON_TRUE;
}
/**
*/
cx_attr_nonnull
CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
- return value->value.array.array_size;
+ return value->array.data_size;
}
/**
*/
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.
*
* @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.
#include "map.h"
#include "buffer.h"
-#include <stdio.h>
-#include <string.h>
-
#ifdef __cplusplus
extern "C" {
#endif
* The character, when appearing at the end of a line, continues that line.
* This is '\' by default.
*/
- /**
- * Reserved for future use.
- */
char continuation;
};
*/
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,
};
/**
*/
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.
*
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"
* 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.
*
*/
cx_tree_node_create_func node_create;
- /**
- * An optional simple destructor for the tree nodes.
- */
- cx_destructor_func simple_destructor;
-
- /**
- * An optional advanced destructor for the tree nodes.
- */
- cx_destructor_func2 advanced_destructor;
-
- /**
- * The pointer to additional data that is passed to the advanced destructor.
- */
- void *destructor_data;
-
/**
* A function to compare two nodes.
*/
*/
cx_tree_search_data_func search_data;
- /**
- * The number of currently stored elements.
- */
- size_t size;
-
/**
* Offset in the node struct for the parent pointer.
*/
*/
#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;
}
}
}
}
- 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) {
}
// 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
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};
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) {
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
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
}
}
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) {
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:
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
}
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);
}
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
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: {
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
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;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_NUMBER;
- v->value.number = num;
+ v->number = num;
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_INTEGER;
- v->value.integer = num;
+ v->integer = num;
return v;
}
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;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_LITERAL;
- v->value.literal = lit;
+ v->literal = lit;
return v;
}
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) {
}
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,
CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
return (CxJsonWriter) {
- true,
true,
6,
use_spaces,
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)) {
// 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);
}
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: {
// 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 ,
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 {
}
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);
+}
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);
}
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
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;
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;
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;
#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',
'\\',
};
// 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
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;
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;
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;
}
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;
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;
}
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;
}
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;
}
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;
}
if (tree->root != NULL) {
cxTreeClear(tree);
}
- cxFree(tree->allocator, tree);
+ cxFree(tree->collection.allocator, tree);
}
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) {
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;
}
}
size_t cxTreeSize(CxTree *tree) {
- return tree->size;
+ return tree->collection.size;
}
size_t cxTreeDepth(CxTree *tree) {
if (loc_last_child >= 0) tree_last_child(node) = NULL;
// the tree now has one member less
- tree->size--;
+ tree->collection.size--;
return 0;
}
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(
) {
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;
);
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;
}
*/
#import "toolkit.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
@interface ListDataSource : NSObject <NSTableViewDataSource>
#import "toolkit.h"
#import "Container.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
#import "ListDataSource.h"
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;
@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;
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/threadpool.h"
+#include "../common/app.h"
#import "image.h"
#import "menu.h"
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 ------------------- */
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;
}
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) {
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) {
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) {
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;
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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 */
+
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);
}
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);
}
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) {
free((void*)args->icon);
free((void*)args->tooltip);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
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) {
free((void*)args->label);
free((void*)args->icon);
free((void*)args->tooltip);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
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) {
free((void*)args->icon);
free((void*)args->tooltip);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
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) {
free((void*)args->label);
free((void*)args->uri);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
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) {
}
free(args->static_elements);
}
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
free((void*)args->style_class);
free((void*)args->varname);
free((void*)args->sublists);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
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);
}
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);
}
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);
}
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);
}
#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"
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);
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);
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);
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);
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);
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);
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);
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);
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);
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
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;
}
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;
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) {
// 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);
}
}
-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;
}
}
-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) {
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;
+}
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 {
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);
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;
UiBool bound;
};
-struct UiGroupWidget {
+struct UiStateWidget {
void *widget;
ui_enablefunc enable;
- int *groups;
- int numgroups;
+ int *states;
+ int numstates;
};
struct UiDestroyHandler {
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);
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
}
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;
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);
}
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);
}
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);
}
}
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);
}
case UI_MENU_RADIO_ITEM: {
UiMenuRadioItem *i = (UiMenuRadioItem*)item;
- free(i->groups);
+ free(i->states);
free(i->label);
free(i->icon);
free(i->varname);
char *label;
char *icon;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuCheckItem {
char *varname;
ui_callback callback;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuRadioItem {
char *varname;
ui_callback callback;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuItemList {
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);
* 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;
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);
// 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;
return NULL;
}
+
+#endif
#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
typedef struct UiMessageHandler UiMessageHandler;
-typedef void(*msg_received_callback)(cxmutstr msg);
+typedef void(*msg_received_callback)(cxstring msg);
struct UiMessageHandler {
int (*start)(UiMessageHandler *handler);
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);
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)
#include <cx/buffer.h>
#include <cx/hash_map.h>
-#include "ucx_properties.h"
+#include <cx/properties.h>
static CxMap *application_properties;
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) {
#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) {
// 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);
#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;
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;
}
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);
}
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);
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);
}
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) {
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
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
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
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
#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"
UiModel* ui_model(UiContext *ctx, ...) {
UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+ info->ctx = ctx;
va_list ap;
va_start(ap, 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*));
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++;
}
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));
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);
}
UIEXPORT void ui_listselection_free(UiListSelection selection) {
- if (selection.rows) {
- free(selection.rows);
- }
+ free(selection.rows);
}
UIEXPORT UiStr ui_str(char *cstr) {
#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;
}
}
}
+
+// 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);
+}
#include "../ui/toolkit.h"
#include "../ui/text.h"
+#include <cx/string.h>
+
#ifdef __cplusplus
extern "C" {
#endif
*/
void ui_get_window_default_width(int *width, int *height);
+cxmutstr ui_escape_string(cxstring str);
+
#ifdef __cplusplus
}
#define UIC_WRAPPER_H
#include "../ui/toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#ifdef __cplusplus
extern "C" {
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);
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);
}
}
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);
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);
}
}
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);
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) {
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) {
}
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);
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) {
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);
}
int close;
};
+typedef struct UiCustomContainer {
+ UiContainerPrivate container;
+ UiObject *obj;
+ ui_addwidget_func add;
+ void *userdata;
+} UiCustomContainer;
+
typedef struct UiBoxContainer {
UiContainerPrivate container;
UiSubContainerType type;
#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);
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;
}
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);
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);
#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
#include "container.h"
#include "menu.h"
+#include "widget.h"
#include "../common/context.h"
#include "../common/object.h"
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();
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;
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;
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();
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 {
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;
}
}
- 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
}
}
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;
}
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);
}
}
-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);
}
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);
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);
}
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);
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) {
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);
// 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);
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
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);
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);
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);
}
}
}
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 };
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) {
// 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);
//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);
}
-/* --------------------------- 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);
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) {
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*));
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) {
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]);
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);
* 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>
extern "C" {
#endif
+typedef struct UiListView UiListView;
typedef struct UiColData UiColData;
#if GTK_CHECK_VERSION(4, 10, 0)
} 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;
CxMap *bound_rows;
GListStore *liststore;
GtkSelectionModel *selectionmodel;
- UiColData *columns;
+ int *columns;
int numcolumns;
+ UiColData coldata;
PangoAttrList *current_row_attributes;
#else
int style_offset;
void *onsavedata;
UiListSelection selection;
-} UiListView;
-
-struct UiColData {
- UiListView *listview;
- int model_column;
- int data_column;
};
typedef struct UiTreeEventData {
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);
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);
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);
}
#endif
-#endif /* TREE_H */
+#endif /* LIST_H */
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);
}
}
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);
}
#include "text.h"
#include "container.h"
+#include "widget.h"
#include <cx/printf.h>
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;
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(
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;
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);
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);
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);
}
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(
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) {
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);
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/threadpool.h"
+#include "../common/app.h"
#include <cx/string.h>
#include <cx/printf.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 ui_callback appclose_fnc;
static void *appclose_udata;
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) {
}
static void app_shutdown(GtkApplication *app, gpointer userdata) {
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
ui_app_save_settings();
}
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) {
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
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
}
}
-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);
}
}
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);
}
}
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);
#include "container.h"
#include "webview.h"
+#include "widget.h"
#ifdef UI_WEBVIEW
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));
}
}
- 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);
#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;
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
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
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
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();
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);
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);
}
-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) {
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));
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;
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;
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);
}
}
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);
}
}
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) {
event->userdata = args->onchangedata;
event->observers = NULL;
event->var = var;
- event->value = args->enable_group;
+ event->value = args->enable_state;
XtAddCallback(
button,
XmNvalueChangedCallback,
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;
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;
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(
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) {
}
}
-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);
/* ------------------------------- Drop Down ------------------------------- */
-static void ui_dropdown_selection(
+static void ui_dropdown_selection_changed(
Widget w,
UiListView *listview,
XmComboBoxCallbackStruct *cb)
}
}
-UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject* obj, UiListArgs *args) {
Arg xargs[16];
int n = 0;
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;
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(
XtAddCallback(
widget,
XmNselectionCallback,
- (XtCallbackProc)ui_dropdown_selection,
+ (XtCallbackProc)ui_dropdown_selection_changed,
listview);
return widget;
}
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);
+}
#define LIST_H
#include "toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "../common/context.h"
#ifdef __cplusplus
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);
#include "menu.h"
#include "button.h"
#include "toolkit.h"
-#include "stock.h"
#include "container.h"
#include "../common/context.h"
#include "../common/menu.h"
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) {
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) {
MOTIF_OBJPRE = $(OBJ_DIR)$(MOTIF_SRC_DIR)
MOTIFOBJ = toolkit.o
-MOTIFOBJ += stock.o
MOTIFOBJ += window.o
MOTIFOBJ += widget.o
MOTIFOBJ += container.o
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;
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));
#include "toolbar.h"
#include "button.h"
-#include "stock.h"
#include "list.h"
#include <cx/hash_map.h>
#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>
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;
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);
}
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++;
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);
}
}
}
-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;
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) {
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);
hasStretchedWidget = true;
}
current = widget;
+ hasChild = true;
}
UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
}
+/* ------------------------------ 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) {
#include <QTabWidget>
#include <QStackedWidget>
#include <QSplitter>
+#include <QGroupBox>
#define ui_obj_container(obj) (UiContainerPrivate*)((UiContainerX*)obj->container_end)->container
public:
QBoxLayout *box;
bool hasStretchedWidget = false;
+ bool singleChild = false;
+ bool hasChild = false;
QSpacerItem *space;
QBoxLayout::Direction direction;
#include <QTreeView>
#include <QTreeWidgetItem>
#include <QListView>
+#include <QComboBox>
extern "C" void* ui_strmodel_getvalue(void *elm, int column) {
return column == 0 ? elm : NULL;
return NULL;
}
-UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
UiContainerPrivate *ctn = ui_obj_container(obj);
QListView *view = new QListView();
return view;
}
-UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
UiContainerPrivate *ctn = ui_obj_container(obj);
QTreeView *view = new QTreeView();
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;
+}
#ifndef TREE_H
#define TREE_H
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "model.h"
#include <QTableView>
#include "../common/menu.h"
#include "../ui/properties.h"
#include "../ui/window.h"
-#include "stock.h"
#include "container.h"
}
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);
}
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()));
}
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;
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;
#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;
}
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;
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;
+ }
}
#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>
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);
SOURCES += window.cpp
SOURCES += menu.cpp
SOURCES += toolbar.cpp
-SOURCES += stock.cpp
SOURCES += container.cpp
SOURCES += text.cpp
SOURCES += model.cpp
HEADERS += window.h
HEADERS += menu.h
HEADERS += toolbar.h
-HEADERS += stock.h
HEADERS += container.h
HEADERS += text.h
HEADERS += model.h
#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);
#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;
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;
#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();
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) {
ui_callback onclick;
void *onclickdata;
- const int *groups;
+ const int *states;
} UiButtonArgs;
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 {
UiBool nofollow;
UiLinkType type;
- const int *groups;
+ const int *states;
} UiLinkButtonArgs;
#define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } )
#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))
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);
ui_callback onchange;
void* onchangedata;
- const int *groups;
+ const int *states;
} UiSpinBoxArgs;
int margin_bottom;
int colspan;
int rowspan;
+ int width;
+ int height;
const char *name;
const char *style_class;
--- /dev/null
+/*
+ * 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 */
+
ui_callback onclick;
void* onclickdata;
- const int* groups;
+ const int* states;
} UiMenuItemArgs;
typedef struct UiMenuToggleItemArgs {
ui_callback onchange;
void* onchangedata;
- const int* groups;
+ const int* nstates;
} UiMenuToggleItemArgs;
typedef struct UiMenuItemListArgs {
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);
ui_callback onchange;
void *onchangedata;
- const int *groups;
+ const int *states;
} UiTextAreaArgs;
typedef struct UiTextFieldArgs {
ui_callback onactivate;
void *onactivatedata;
- const int *groups;
+ const int *states;
} UiTextFieldArgs;
typedef struct UiPathElmRet {
int margin_bottom;
int colspan;
int rowspan;
+ int width;
const char *name;
const char *style_class;
ui_callback onclick;
void* onclickdata;
- const int *groups;
+ const int *states;
const int *visibility_states;
} UiToolbarItemArgs;
ui_callback onchange;
void *onchangedata;
- const int *groups;
+ const int *states;
const int *visibility_states;
} UiToolbarToggleItemArgs;
#elif UI_SERVER
-#define UIWIDGET void*
-#define UIWINDOW void*
+typedef struct UiWidget UiWidget;
+
+#define UIWIDGET UiWidget*
+#define UIWINDOW UiWidget*
#define UIMENU void*
#endif
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);
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);
#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"
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__ } )
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;
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__ });
}\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
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
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
}\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
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
.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
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
}\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
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
#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
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
--- /dev/null
+/*\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
--- /dev/null
+/*\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
WIN32OBJ += grid.obj
WIN32OBJ += text.obj
WIN32OBJ += list.obj
+WIN32OBJ += menu.obj
TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%)
TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c)
#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) {
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;
DispatchMessage(&msg);
}
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
uic_store_app_properties();
}
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;
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
+}
#include <stdlib.h>
#include "win32.h"
+#include "menu.h"
static W32WidgetClass w32_toplevel_widget_class = {
.eventproc = ui_window_widget_event,
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;
}
}
-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);
}
\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
\r
#pragma once\r
\r
-#include "../ui/tree.h"\r
+#include "../ui/list.h"\r
#include "toolkit.h"\r
#include "dnd.h"\r
\r
#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
\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
\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
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
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
<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
<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