]> uap-core.de Git - note.git/commitdiff
implement loader for notebook structure and use the sidebar to show all available...
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 17 Feb 2025 21:32:33 +0000 (22:32 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 17 Feb 2025 21:32:33 +0000 (22:32 +0100)
12 files changed:
application/application.c
application/application.h
application/store.c
application/store.h
application/types.c
application/types.h
application/window.c
application/window.h
ui/gtk/list.c
ui/gtk/list.h
ui/ui/toolkit.h
ui/ui/tree.h

index 65f27577e96cb8584e4d6e9ece1d6e126a2dbf62..340f796a629e604ffa87bc099b1f8dd2f641205f 100644 (file)
@@ -86,5 +86,7 @@ void application_startup(UiEvent *event, void *data) {
     
     cxMempoolFree(mp);
     
+    note_store_reload();
+    
     window_create();
 }
index acae80e12a1f1603589bfa46d7125a93d7cdaa72..ee8806c212130e9a0372451ecd2f8e36ad47b9d7 100644 (file)
@@ -40,9 +40,12 @@ extern "C" {
     
 typedef struct MainWindow {
     UiList *notebooks;
-    UiList *sources;
-} MainWindow;
     
+    
+    UiList *test1;
+    UiList *test2;
+} MainWindow;
+   
 void application_startup(UiEvent *event, void *data);
 
 
index 3e64e969e6d2e030d0ec1991360855b84438dd6f..740548019a1b03acb55deb60b3199b9849f48a81 100644 (file)
 #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;
 
@@ -50,6 +65,8 @@ static char *settings_profile_name;
 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
  * 
@@ -256,3 +273,67 @@ int note_store_create_default(const char *host, const char *user) {
     
     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;
+}
index 691363113bd243a1558aa67aafe9bdfd4278c283..a556989e1bd570685bf60ef5481e8abe18af05cc 100644 (file)
@@ -32,6 +32,7 @@
 #include "application.h"
 #include "types.h"
 #include <cx/list.h>
+#include <cx/mempool.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -39,6 +40,11 @@ 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);
@@ -60,6 +66,10 @@ void note_store_set_settings(
 
 int note_store_create_default(const char *host, const char *user);
 
+void note_store_reload();
+
+NoteStore* note_store_get();
+
 
 #ifdef __cplusplus
 }
index 2498b45e07e604e64ce8965e9fc0fec9958a3cb9..a6fc2ee1a1e7fa07172d67673cbb7e4ea88a54f1 100644 (file)
@@ -49,4 +49,10 @@ void register_types() {
     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
index c82629ce052feb0199992e8ae0b96a82019c263b..f6de2d8dfbb3c9603e59b71a40866fa6ab94f057 100644 (file)
@@ -66,6 +66,8 @@ struct Collection {
     char       *name;
     char       *display_name;
     char       *type;
+    
+    CxList     *children;
 };
 
 extern DBUClass *usersettings_class;
index a152bb56d4fe6b4d520dbaa8a214d7a5cedff291..a5a44aa19015b3874c43f2c044a5e8b75e659c13 100644 (file)
 
 #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");
         }
@@ -68,12 +73,93 @@ MainWindow* window_init_data(UiObject *obj) {
     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);
 }
index 71594a40a951c802c148905f930bedd6c968c102..9373298bb90e79f23fde15bf869cc8d188e8f9eb 100644 (file)
@@ -42,6 +42,8 @@ MainWindow* window_init_data(UiObject *obj);
 
 void window_sidebar_getvalue(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item);
 
+void update_sublists(UiContext *ctx, UiList *sublists);
+
 
 #ifdef __cplusplus
 }
index a4347910e02a9d867ed7915ea9f18698906ec2bd..c0e5467587c7504ed3cb44d1e4446596289f7afb 100644 (file)
@@ -1566,6 +1566,34 @@ static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
 }
 #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);
     
@@ -1603,42 +1631,32 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
     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
@@ -1663,6 +1681,35 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
     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;
index d9ba1b22496f0bcda8866fc61914b1e2acba5fce..1ff4029e3acd52ab23dd392bf97d2691030b7025 100644 (file)
@@ -161,6 +161,7 @@ void ui_combobox_modelupdate(UiList *list, int i);
 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);
index 66973c384eec87f07d00c0630617b788e1b9a66b..dd4a7d1522fdc4097b215b8a1efa640da6ec1968 100644 (file)
@@ -534,6 +534,7 @@ UIEXPORT void ui_notify_evt(UiObserver *observer, UiEvent *event);
 
 
 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);
index eb652f022711a86fc25e7f1218ef4f078f63b6c7..589d2a1fddd10daff42f17e080a61f06f2e92801 100644 (file)
@@ -183,11 +183,14 @@ struct UiSourceListArgs {
     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;
     /*
@@ -197,6 +200,17 @@ struct UiSourceListArgs {
      */
     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