cxMempoolFree(mp);
+ note_store_reload();
+
window_create();
}
typedef struct MainWindow {
UiList *notebooks;
- UiList *sources;
-} MainWindow;
+
+ UiList *test1;
+ UiList *test2;
+} MainWindow;
+
void application_startup(UiEvent *event, void *data);
#include <sys/stat.h>
#include <cx/printf.h>
+#include <cx/array_list.h>
+#include <cx/hash_map.h>
#include "types.h"
#include "store_sqlite.h"
#include "../dbutils/dbutils/db.h"
#include "../dbutils/dbutils/sqlite.h"
+#include "cx/mempool.h"
+
+#define SQL_NOTEBOOK_STRUCTURE "with recursive cols as (\n" \
+ " select 1 as depth, '' as path, * from collections where collection_id = ?\n" \
+ " union all\n" \
+ " select p.depth+1 as depth, concat(p.path, '/', c.name), c.* from collections c\n" \
+ " inner join cols p on c.parent_id = p.collection_id\n" \
+ " where p.depth < 3\n" \
+ ")\n" \
+ "select * from cols\n" \
+ "order by path;"
+
+
static DBUConnection *connection;
static int64_t settings_default_repository_id;
static int64_t settings_default_node_id;
+static NoteStore *current_store;
+
/*
* Creates a connection to the note database and initializes tables if necessary
*
return err;
}
+
+
+/*
+ * Reloads the NoteStore structure. The previous NoteStore* pointer will be
+ * invalid after this call
+ */
+void note_store_reload() {
+ // load notebook/collection structure
+ CxMempool *mp = cxMempoolCreate(64, NULL);
+ const CxAllocator *a = mp->allocator;
+ DBUQuery *q = connection->createQuery(connection, NULL);
+ dbuQuerySetSQL(q, SQL_NOTEBOOK_STRUCTURE);
+ dbuQuerySetParamInt64(q, 1, settings_default_node_id);
+ DBUObjectBuilder *builder = dbuObjectBuilder(collection_class, q, mp->allocator);
+ CxList *collections = dbuObjectBuilderGetList(builder);
+ dbuObjectBuilderDestroy(builder);
+
+ if(!collections) {
+ fprintf(stderr, "Error: cannot query note store\n");
+ cxMempoolFree(mp);
+ return;
+ }
+
+ // NoteStore root object
+ NoteStore *store = cxMalloc(a, sizeof(NoteStore));
+ store->mp = mp;
+ store->root = NULL;
+
+ // key: collection_id value: Collection*
+ CxMap *collection_map = cxHashMapCreate(NULL, CX_STORE_POINTERS, cxListSize(collections) + 16);
+
+ // convert result list to tree
+ CxIterator i = cxListIterator(collections);
+ cx_foreach(Collection *, col, i) {
+ CxHashKey key = cx_hash_key(&col->collection_id, sizeof(int64_t));
+ cxMapPut(collection_map, key, col);
+ if(i.index == 0) {
+ store->root = col;
+ continue;
+ }
+ if(col->parent_id != 0) {
+ CxHashKey parent_key = cx_hash_key(&col->parent_id, sizeof(int64_t));
+ Collection *parent = cxMapGet(collection_map, parent_key);
+ if(parent) {
+ if(!parent->children) {
+ parent->children = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 8);
+ }
+ cxListAdd(parent->children, col);
+ }
+ }
+ }
+
+ cxMapFree(collection_map);
+
+ if(current_store) {
+ cxMempoolFree(current_store->mp);
+ }
+
+ current_store = store;
+}
+
+NoteStore* note_store_get() {
+ return current_store;
+}
#include "application.h"
#include "types.h"
#include <cx/list.h>
+#include <cx/mempool.h>
#ifdef __cplusplus
extern "C" {
#define NOTES_DB_FILE "notes.db"
+typedef struct NoteStore {
+ CxMempool *mp;
+ Collection *root;
+} NoteStore;
+
int init_note_store();
CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, const char *user, const char *profile);
int note_store_create_default(const char *host, const char *user);
+void note_store_reload();
+
+NoteStore* note_store_get();
+
#ifdef __cplusplus
}
dbuClassAdd(usersettings_class, UserSettings, default_collection_id);
dbuClassAdd(usersettings_class, UserSettings, created_by);
+ collection_class = dbuRegisterClass(ctx, "collections", Collection, collection_id);
+ dbuClassAdd(collection_class, Collection, parent_id);
+ dbuClassAdd(collection_class, Collection, repository_id);
+ dbuClassAdd(collection_class, Collection, name);
+ dbuClassAdd(collection_class, Collection, display_name);
+ dbuClassAdd(collection_class, Collection, type);
}
\ No newline at end of file
char *name;
char *display_name;
char *type;
+
+ CxList *children;
};
extern DBUClass *usersettings_class;
#include "window.h"
#include "application.h"
+#include "store.h"
+
+#include <cx/array_list.h>
void window_create() {
UiObject *obj = ui_sidebar_window("note", NULL);
MainWindow *wdata = window_init_data(obj);
+ /*
UiSubList sublists[] = {
{ .value = wdata->notebooks, .header = "Notebooks" },
{ .value = wdata->sources, .header = "Sources", .separator = TRUE }
};
+ */
ui_sidebar(obj) {
- ui_sourcelist(obj, .sublists = sublists, .numsublists = 2, .getvalue = window_sidebar_getvalue, .fill = UI_ON);
+ ui_sourcelist(obj, .dynamic_sublist = wdata->notebooks, .getvalue = window_sidebar_getvalue, .fill = UI_ON);
ui_hbox(obj, .spacing = 2, .fill = UI_OFF) {
ui_button(obj, .icon = "folder-new-symbolic", .style_class = "flat");
}
MainWindow *wdata = ui_calloc(obj->ctx, 1, sizeof(MainWindow));
obj->window = wdata;
- wdata->notebooks = ui_list_new(obj->ctx, "notebooks");
- wdata->sources = ui_list_new(obj->ctx, "source");
+ wdata->notebooks = ui_list_new(obj->ctx, NULL);
+ wdata->test1 = ui_list_new(obj->ctx, NULL);
+ wdata->test2 = ui_list_new(obj->ctx, NULL);
+
+ ui_list_append(wdata->test1, "Test1");
+ ui_list_append(wdata->test1, "Test2");
+ ui_list_append(wdata->test1, "Test3");
+
+ ui_list_append(wdata->test2, "Test2 X1");
+ ui_list_append(wdata->test2, "Test2 X2");
+
+ UiSubList *s1 = calloc(1, sizeof(UiSubList));
+ UiSubList *s2 = calloc(1, sizeof(UiSubList));
+
+ s1->value = wdata->test1;
+ s2->value = wdata->test2;
+
+ s1->header = "Test 1";
+ s2->header = "Test 2";
+
+ //ui_list_append(wdata->notebooks, s1);
+ //ui_list_append(wdata->notebooks, s2);
+ update_sublists(obj->ctx, wdata->notebooks);
return wdata;
}
void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item) {
+ Collection *notebook = rowdata;
+ item->label = strdup(notebook->display_name ? notebook->display_name : notebook->name);
+
+}
+
+static void sublist_free(UiContext *ctx, UiSubList *sublist) {
+ ui_list_free(sublist->value);
+ free((char*)sublist->header);
+ free(sublist);
+}
+
+/*
+ * Converts the notestore to a list of sublists
+ * The sublists list will be cleared and filled with UiSubList elements
+ */
+void update_sublists(UiContext *ctx, UiList *sublists) {
+ NoteStore *notestore = note_store_get();
+ // The NoteStore is a tree in theory, however, currently the depth
+ // is limited to 3
+ // All direct children of root will be converted to a UiSubList
+
+ CxList *list = sublists->data;
+
+ // save previous elements
+ CxList *delete_list = cxArrayListCreateSimple(CX_STORE_POINTERS, cxListSize(list));
+ CxIterator i = cxListIterator(list);
+ cx_foreach(UiSubList *, sublist, i) {
+ cxListAdd(delete_list, sublist);
+ }
+ cxListClear(list);
+
+ if(!notestore->root) {
+ fprintf(stderr, "Error: no NoteStore root\n");
+ return; // weird, no root, shouldn't happen
+ }
+
+ if(!notestore->root->children) {
+ return;
+ }
+
+ i = cxListIterator(notestore->root->children);
+ cx_foreach(Collection *, col, i) {
+ UiSubList *sublist = calloc(1, sizeof(UiSubList));
+ sublist->header = strdup(col->display_name ? col->display_name : col->name);
+ sublist->value = ui_list_new(ctx, NULL);
+ sublist->userdata = col;
+ ui_list_append(sublists, sublist);
+
+ if(col->children) {
+ CxIterator j = cxListIterator(col->children);
+ cx_foreach(Collection *, nb, j) {
+ ui_list_append(sublist->value, nb);
+ }
+ }
+ }
+
+ // now we can free the list
+ delete_list->collection.advanced_destructor = (cx_destructor_func2)sublist_free;
+ delete_list->collection.destructor_data = ctx;
+ cxListFree(delete_list);
}
void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item);
+void update_sublists(UiContext *ctx, UiList *sublists);
+
#ifdef __cplusplus
}
}
#endif
+
+static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *sublist) {
+ UiListBoxSubList uisublist;
+ uisublist.var = uic_widget_var(
+ uilistbox->obj->ctx,
+ uilistbox->obj->ctx,
+ sublist->value,
+ sublist->varname,
+ UI_VAR_LIST);
+ uisublist.numitems = 0;
+ uisublist.header = sublist->header ? strdup(sublist->header) : NULL;
+ uisublist.separator = sublist->separator;
+ uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ uisublist.listbox = uilistbox;
+ uisublist.userdata = sublist->userdata;
+ uisublist.index = cxListSize(sublists);
+
+ // bind UiList
+ UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1);
+ UiList *list = uisublist.var->value;
+ if(list) {
+ list->obj = sublist_ptr;
+ list->update = ui_listbox_list_update;
+ }
+
+ cxListAdd(sublists, &uisublist);
+}
+
UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
UiObject* current = uic_current_obj(obj);
uilistbox->sublists->collection.destructor_data = obj;
uilistbox->first_row = NULL;
- if(args.numsublists == 0 && args.sublists) {
- args.numsublists = INT_MAX;
- }
- for(int i=0;i<args.numsublists;i++) {
- UiSubList sublist = args.sublists[i];
- if(!sublist.varname && !sublist.value) {
- break;
+ if(args.sublists) {
+ // static sublist initalization
+ if(args.numsublists == 0 && args.sublists) {
+ args.numsublists = INT_MAX;
+ }
+ for(int i=0;i<args.numsublists;i++) {
+ UiSubList sublist = args.sublists[i];
+ if(!sublist.varname && !sublist.value) {
+ break;
+ }
+
+ add_sublist(uilistbox, uilistbox->sublists, &sublist);
}
- UiListBoxSubList uisublist;
- uisublist.var = uic_widget_var(
- obj->ctx,
- current->ctx,
- sublist.value,
- sublist.varname,
- UI_VAR_LIST);
- uisublist.numitems = 0;
- uisublist.header = sublist.header ? strdup(sublist.header) : NULL;
- uisublist.separator = sublist.separator;
- uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- uisublist.listbox = uilistbox;
- uisublist.userdata = sublist.userdata;
- uisublist.index = i;
-
- cxListAdd(uilistbox->sublists, &uisublist);
-
- // bind UiList
- UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-1);
- UiList *list = uisublist.var->value;
- if(list) {
- list->obj = sublist_ptr;
- list->update = ui_listbox_list_update;
+ // fill items
+ ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
+ } else {
+ UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.dynamic_sublist, args.varname, UI_VAR_LIST);
+ if(var) {
+ UiList *list = var->value;
+ list->obj = uilistbox;
+ list->update = ui_listbox_dynamic_update;
+
+ ui_listbox_dynamic_update(list, 0);
}
}
- // fill items
- ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
// register uilistbox for both widgets, so it doesn't matter which
// widget is used later
return scroll_area;
}
+void ui_listbox_dynamic_update(UiList *list, int x) {
+ UiListBox *uilistbox = list->obj;
+
+ // unbind/free previous list vars
+ CxIterator i = cxListIterator(uilistbox->sublists);
+ cx_foreach(UiListBoxSubList *, s, i) {
+ if(s->var) {
+ UiList *sl = s->var->value;
+ sl->obj = NULL;
+ sl->update = NULL;
+ if(s->var->type == UI_VAR_SPECIAL) {
+ ui_free(s->var->from_ctx, s->var);
+ }
+ }
+ }
+
+ cxListFree(uilistbox->sublists);
+ CxList *new_sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), list->count(list));
+ uilistbox->sublists = new_sublists;
+
+ UiSubList *sublist = list->first(list);
+ while(sublist) {
+ add_sublist(uilistbox, new_sublists, sublist);
+ sublist = list->next(list);
+ }
+
+ ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
+}
+
void ui_listbox_update(UiListBox *listbox, int from, int to) {
CxIterator i = cxListIterator(listbox->sublists);
size_t pos = 0;
UiListSelection ui_combobox_getselection(UiList *list);
void ui_combobox_setselection(UiList *list, UiListSelection selection);
+void ui_listbox_dynamic_update(UiList *list, int i);
void ui_listbox_update(UiListBox *listbox, int from, int to);
void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index);
void ui_listbox_list_update(UiList *list, int i);
UIEXPORT UiList* ui_list_new(UiContext *ctx, char *name);
+UIEXPORT void ui_list_free(UiList *list);
UIEXPORT void* ui_list_first(UiList *list);
UIEXPORT void* ui_list_next(UiList *list);
UIEXPORT void* ui_list_get(UiList *list, int i);
const int *groups;
/*
- * list of sublists
+ * 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;
/*
*/
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