]> uap-core.de Git - note.git/commitdiff
fix moving notebooks to other groups
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 18 Jan 2026 15:07:47 +0000 (16:07 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 18 Jan 2026 15:07:47 +0000 (16:07 +0100)
application/nbconfig.c
application/nbconfig.h
application/store.c
application/store.h
application/tests/test-store.c

index a2d4064ad02ce34322708df682850e797b27f483..aa93411587f2cf5f40153ede68725646b5639c31 100644 (file)
@@ -38,10 +38,16 @@ static UiBool notebooklist_move_inprogress = FALSE;
 static int notebooklist_move_pos0 = 0;
 static int notebooklist_move_pos1 = 0;
 
-static void* reslist_getvalue(void *data, int col) {
-    Resource *resource = data;
+static void* reslist_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+    NotebookConfigDialog *wdata = userdata;
+    Resource *resource = elm;
     switch(col) {
         case 0: {
+            if(resource == wdata->selected_group && wdata->selected_group_name) {
+                return wdata->selected_group_name;
+            } else if(resource == wdata->selected_notebook && wdata->selected_notebook_name) {
+                return wdata->selected_notebook_name;
+            }
             return resource->displayname ? resource->displayname : resource->nodename;
         }
         case 1: {
@@ -58,6 +64,15 @@ static void* repolist_get_value(void *data, int col) {
     return repo->name;
 }
 
+void nbconfig_update_list(NotebookConfigDialog *wdata, CxList *list, int index) {
+    if(list == wdata->groups) {
+        ui_list_update_row(wdata->tab1_groups, index);
+        ui_list_update_row(wdata->tab2_groups, index);
+    } else if(list == wdata->notebooks) {
+        ui_list_update_row(wdata->tab2_notebooks, index);
+    }
+}
+
 void nbconfig_update_lists(NotebookConfigDialog *wdata) {
     ui_list_clear(wdata->tab1_groups);
     ui_list_clear(wdata->tab2_groups);
@@ -114,13 +129,14 @@ static void nbconfig_tab1_load_group(NotebookConfigDialog *wdata, Resource *res)
 
 static void nbconfig_tab2_load_notebook(NotebookConfigDialog *wdata, Resource *res) {
     Notebook *nb = res->notebook;
-    CxHashKey key = cx_hash_key(&res->resource_id, sizeof(res->resource_id));
-    Resource *parent = cxMapGet(wdata->notebook_parents, key);
     
     ui_set(wdata->tab2_notebook_name, res->nodename);
-    if(parent) {
-        size_t index = cxListFind(wdata->groups, parent);
-        ui_list_setselection(wdata->tab2_groups, index);
+    CxIterator i = cxListIterator(wdata->groups);
+    cx_foreach(Resource *, parent, i) {
+        if(parent->resource_id == res->parent_id) {
+            ui_list_setselection(wdata->tab2_groups, (int)i.index);
+            break;
+        }
     }
     
     wdata->selected_notebook = res;
@@ -142,21 +158,9 @@ static int nbconfig_list_activate_event(UiEvent *event, UiList *list, void **lis
 }
 
 
-static void group_created(UiEvent *event, int64_t newid, int error, void *userdata) {
-    Resource *res = userdata;
-    NoteStore *store = note_store_get();
-    if(store->root) {
-        if(!store->root->children) {
-            store->root->children = cxArrayListCreate(store->mp->allocator, CX_STORE_POINTERS, 8);
-        }
-        cxListAdd(store->root->children, res);
-        note_store_groups_updated();
-    }
-}
-
 static void group_saved(UiEvent *event, int error, void *userdata) {
     if(error) {
-        fprintf(stderr, "Error: note_store_save_resource failed\n");
+        fprintf(stderr, "Error: note_store_save_notebook_async failed\n");
     }
 }
 
@@ -166,20 +170,18 @@ static void nbconfig_group_save(NotebookConfigDialog *nbconfig) {
         return;
     }
     
-    Resource *dup = cxMapGet(nbconfig->current_names, res->nodename);
+    Resource *dup = cxMapGet(nbconfig->current_names, nbconfig->selected_group_name);
     if(dup) {
-        fprintf(stderr, "Name %s already in use\n", res->nodename);
+        fprintf(stderr, "Name %s already in use\n", nbconfig->selected_group_name);
         // TODO: show error in UI
-        // TODO: restore old name
         return;
     }
     
-    if(res->resource_id == 0) {
-        note_store_new_notebook_async(application_global_obj(), res, group_created, res);
-    } else {
-        note_store_update_resource_async(application_global_obj(), res, group_saved, res);
-        note_store_groups_updated();
-    }
+    NoteStore *store = note_store_get();
+    cxFree(store->mp->allocator, nbconfig->selected_group->nodename);
+    nbconfig->selected_group->nodename = cx_strdup_a(store->mp->allocator, nbconfig->selected_group_name).ptr;
+    
+    note_store_save_notebook_async(application_global_obj(), res, 0, group_saved, res);
 }
 
 static void nbconfig_grouplist_onselect(UiEvent *event, void *userdata) {
@@ -283,22 +285,17 @@ static void nbconfig_grouplist_name_changed(UiEvent *event, void *userdata) {
         return;
     }
     NotebookConfigDialog *wdata = event->window;
-    NoteStore *store = note_store_get();
+    UiContext *ctx = wdata->obj->ctx;
     
     wdata->valuechange = TRUE;
     
     if(wdata->selected_group) {
         char *input = ui_get(wdata->tab1_group_name);
         
-        cxFree(store->mp->allocator, wdata->selected_group->nodename);
-        wdata->selected_group->nodename = cx_strdup_a(store->mp->allocator, cx_str(input)).ptr;
+        ui_free(ctx, wdata->selected_group_name);
+        wdata->selected_group_name = ui_strdup(ctx, input);
         
-        UiListSelection sel = ui_list_getselection(wdata->tab1_groups);
-        nbconfig_update_lists(wdata); // TODO: update only single row
-        if(sel.count > 0) {
-            ui_list_setselection(wdata->tab1_groups, sel.rows[0]);
-        }
-        ui_listselection_free(sel);
+        nbconfig_update_list(wdata, wdata->groups, cxListFind(wdata->groups, wdata->selected_group));
         
         Resource *dup = cxMapGet(wdata->current_names, input);
         if(dup) {
@@ -310,22 +307,9 @@ static void nbconfig_grouplist_name_changed(UiEvent *event, void *userdata) {
     wdata->valuechange = FALSE;
 }
 
-static void notebook_created(UiEvent *event, int64_t newid, int error, void *userdata) {
-    NoteStore *store = note_store_get();
-    Resource *res = userdata;
-    Resource *parent = note_store_get_notebook_by_id(store, res->parent_id);
-    if(parent) {
-        if(!parent->children) {
-            parent->children = cxArrayListCreate(store->mp->allocator, CX_STORE_POINTERS, 8);
-        }
-        cxListAdd(parent->children, res);
-        note_store_groups_updated();
-    }
-}
-
 static void notebook_saved(UiEvent *event, int error, void *userdata) {
     if(error) {
-        fprintf(stderr, "Error: note_store_save_resource failed\n");
+        fprintf(stderr, "Error: note_store_save_notebook_async failed\n");
     }
 }
 
@@ -335,6 +319,20 @@ static void nbconfig_notebook_save(NotebookConfigDialog *nbconfig) {
         return;
     }
     
+    // check if the notebook name is unique
+    Resource *dup = cxMapGet(nbconfig->current_names, nbconfig->selected_notebook_name);
+    if(dup) {
+        fprintf(stderr, "Name %s already in use\n", nbconfig->selected_notebook_name);
+        // TODO: show error in UI
+        return;
+    }
+    
+    // update name
+    NoteStore *store = note_store_get();
+    cxFree(store->mp->allocator, nbconfig->selected_notebook->nodename);
+    nbconfig->selected_notebook->nodename = cx_strdup_a(store->mp->allocator, nbconfig->selected_notebook_name).ptr;
+    
+    // update parent
     UiListSelection sel = ui_list_getselection(nbconfig->tab2_groups);
     if(sel.count == 0) {
         return; // shouldn't happen, maybe remove this when ui_list_getselectedindex exists
@@ -344,14 +342,10 @@ static void nbconfig_notebook_save(NotebookConfigDialog *nbconfig) {
         return; //shouldn't happen
     }
     ui_listselection_free(sel);
+    int64_t prev_parent_id = res->parent_id;
     res->parent_id = parent->resource_id;
     
-    if(res->resource_id == 0) {
-        note_store_new_notebook_async(application_global_obj(), res, notebook_created, res);
-    } else {
-        note_store_update_resource_async(application_global_obj(), res, notebook_saved, res);
-        note_store_groups_updated();
-    }
+    note_store_save_notebook_async(application_global_obj(), res, prev_parent_id, notebook_saved, res);
 }
 
 static void nbconfig_notebooklist_onselect(UiEvent *event, void *userdata) {
@@ -366,9 +360,8 @@ static void nbconfig_notebooklist_onselect(UiEvent *event, void *userdata) {
     if(nbconfig_list_activate_event(event, wdata->tab2_notebooks, (void**)&res)) {
         return;
     }
-    nbconfig_tab2_load_notebook(wdata, res);
-    
-    wdata->selected_group = res;
+    wdata->selected_notebook = res;
+    nbconfig_tab2_load_notebook(wdata, res);  
       
     nbconfig_build_current_names_map(wdata, res);
 }
@@ -376,17 +369,23 @@ static void nbconfig_notebooklist_onselect(UiEvent *event, void *userdata) {
 static void nbconfig_notebooklist_add(UiEvent *event, void *userdata) {
     NotebookConfigDialog *wdata = event->window;
     NoteStore *store = note_store_get();
+    UiContext *ctx = wdata->obj->ctx;
+    
+    Resource *parent = cxListAt(wdata->groups, 0);
     
     Resource *notebook = cxCalloc(store->mp->allocator, 1, sizeof(Resource));
     notebook->notebook = cxCalloc(store->mp->allocator, 1, sizeof(Notebook));
-    
-    if(wdata->selected_notebook) {
-        // TODO
+    if(parent) {
+        notebook->parent_id = parent->resource_id;
     }
+    cxListAdd(wdata->notebooks, notebook);
     
     wdata->selected_notebook = notebook;
+    ui_free(ctx, wdata->selected_notebook_name);
+    wdata->selected_notebook_name = ui_strdup(ctx, "New");
+    nbconfig_update_lists(wdata);
     
-    ui_set(wdata->tab2_notebook_name, "");
+    ui_set(wdata->tab2_notebook_name, "New");
     ui_list_setselection(wdata->tab2_repositories, 0);
     ui_list_setselection(wdata->tab2_types, 0);
 }
@@ -397,24 +396,22 @@ static void nbconfig_notebooklist_name_changed(UiEvent *event, void *userdata) {
     }
     
     NotebookConfigDialog *wdata = event->window;
-    NoteStore *store = note_store_get();
+    UiContext *ctx = wdata->obj->ctx;
     
     wdata->valuechange = TRUE;
     
     if(wdata->selected_notebook) {
-        UiBool add_notebook = FALSE;
-        if(!wdata->selected_notebook->nodename) {
-            add_notebook = TRUE;
-        } else {
-            cxFree(store->mp->allocator, wdata->selected_notebook->nodename);
-        }
+        char *input = ui_get(wdata->tab2_notebook_name);
+        ui_free(ctx, wdata->selected_notebook_name);
+        wdata->selected_notebook_name = ui_strdup(ctx, input);
         
-        wdata->selected_notebook->nodename = cx_strdup_a(store->mp->allocator, cx_str(ui_get(wdata->tab2_notebook_name))).ptr;
+        nbconfig_update_list(wdata, wdata->notebooks, cxListFind(wdata->notebooks, wdata->selected_notebook));
         
-        if(add_notebook) {
-            cxListAdd(wdata->notebooks, wdata->selected_notebook);
+        Resource *dup = cxMapGet(wdata->current_names, input);
+        if(dup) {
+            fprintf(stderr, "Name %s already in use\n", input);
+            // TODO: show error in UI
         }
-        nbconfig_update_lists(wdata); // TODO: update only single row
     }
     
     wdata->valuechange = FALSE;
@@ -596,7 +593,6 @@ void notebook_config_dialog(void) {
     wdata->repositories = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
     wdata->groups = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
     wdata->notebooks = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
-    wdata->notebook_parents = cxHashMapCreate(a, CX_STORE_POINTERS, 32);
     
     wdata->current_names = cxHashMapCreate(a, CX_STORE_POINTERS, 16);
     
@@ -612,11 +608,6 @@ void notebook_config_dialog(void) {
         CxIterator k = cxListIterator(res->children);
         cx_foreach(Resource *, nb, k) {
             cxListAdd(wdata->notebooks, nb);
-            CxHashKey key = cx_hash_key(&nb->resource_id, sizeof(nb->resource_id));
-            cxMapPut(wdata->notebook_parents, key, res);
-            if(!notebook1) {
-                notebook1 = nb;
-            }
         }
     }
     nbconfig_update_lists(wdata);
@@ -634,7 +625,7 @@ void notebook_config_dialog(void) {
             ui_tab(obj, "Groups") {
                 ui_hbox(obj, .margin = 10, .spacing = 10, .fill = TRUE) {
                     ui_vbox(obj, .fill = FALSE) {
-                        ui_listview(obj, .list = wdata->tab1_groups, .getvalue = reslist_getvalue, .fill = TRUE, .onselection = nbconfig_grouplist_onselect);
+                        ui_listview(obj, .list = wdata->tab1_groups, .getvalue2 = reslist_getvalue, .getvalue2data = wdata, .fill = TRUE, .onselection = nbconfig_grouplist_onselect);
                         ui_hbox(obj, .fill = FALSE) {
                             ui_button(obj, .icon = UI_ICON_NEW_FOLDER, .onclick = nbconfig_grouplist_add);
                             ui_button(obj, .icon = UI_ICON_DELETE, .onclick = nbconfig_grouplist_delete);
@@ -661,7 +652,7 @@ void notebook_config_dialog(void) {
             ui_tab(obj, "Notebooks") {
                 ui_hbox(obj, .margin = 10, .spacing = 10, .fill = TRUE) {
                     ui_vbox(obj) {
-                        ui_listview(obj, .list = wdata->tab2_notebooks, .getvalue = reslist_getvalue, .fill = TRUE, .onselection = nbconfig_notebooklist_onselect);
+                        ui_listview(obj, .list = wdata->tab2_notebooks, .getvalue2 = reslist_getvalue, .getvalue2data = wdata, .fill = TRUE, .onselection = nbconfig_notebooklist_onselect);
                         ui_hbox(obj) {
                             ui_button(obj, .icon = UI_ICON_NEW_FOLDER, .onclick = nbconfig_notebooklist_add);
                             ui_button(obj, .icon = UI_ICON_DELETE, .onclick = nbconfig_notebooklist_delete);
@@ -672,7 +663,7 @@ void notebook_config_dialog(void) {
 
                     ui_grid(obj, .columnspacing = 10, .rowspacing = 10, .fill = TRUE, .def_vfill = TRUE) {
                         ui_rlabel(obj, .label = "Group");
-                        ui_dropdown(obj, .list = wdata->tab2_groups, .getvalue = reslist_getvalue);
+                        ui_dropdown(obj, .list = wdata->tab2_groups, .getvalue2 = reslist_getvalue, .getvalue2data = wdata);
                         ui_newline(obj);
                         
                         ui_rlabel(obj, .label = "Name");
index d7d917f116acd5b2e4b6c516ff3cabaa66ed43b5..942a8995a9ce6abaec28664007bc20cf1e187808 100644 (file)
@@ -43,7 +43,6 @@ typedef struct NotebookConfigDialog {
     CxList     *repositories;
     CxList     *groups;
     CxList     *notebooks;
-    CxMap      *notebook_parents;
     
     UiList     *tab1_groups;
     UiString   *tab1_group_name;
@@ -75,6 +74,7 @@ typedef struct NotebookConfigDialog {
     UiBool valuechange;
 } NotebookConfigDialog;
     
+void nbconfig_update_list(NotebookConfigDialog *wdata, CxList *list, int index);
 void nbconfig_update_lists(NotebookConfigDialog *wdata);
 
 void nbconfig_build_current_names_map(NotebookConfigDialog *wdata, Resource *res);
index 4c47bf82c0a558c9df972814ffb0bb37ac7ecef3..7484d4006f8aeb408e3f51fd1bd69d4b3412e0c9 100644 (file)
     "(?, ?, ?) returning resource_id;"
 
 #define SQL_RESOURCE_SAVE \
-    "update resources set nodename = ?, displayname = ?, contenttype = ? where resource_id = ? ;"
+    "update resources set nodename = ?, displayname = ?, contenttype = ?, parent_id = ? where resource_id = ? ;"
 
 #define SQL_NOTEBOOK_NEW \
     "insert into notebooks(resource_id, position) values " \
@@ -1050,8 +1050,9 @@ static int qthr_update_resource(ExecJob *job) {
     } else {
         dbuQuerySetParamNull(q, 3);
     }
+    dbuQuerySetParamInt64(q, 4, res->parent_id);
     
-    dbuQuerySetParamInt64(q, 4, res->resource_id);
+    dbuQuerySetParamInt64(q, 5, res->resource_id);
     
     if(dbuQueryExec(q)) {
         job->error = 1;
@@ -1088,6 +1089,7 @@ void note_store_update_resource_async(UiObject *obj, Resource *res, execresult_f
 typedef struct SaveResourceJob {
     NoteStore *store;
     Resource *res;
+    int64_t prev_parent_id;
     execresult_func resultcb;
     void *userdata;
 } SaveResourceJob;
@@ -1095,6 +1097,20 @@ typedef struct SaveResourceJob {
 static void uithr_save_resource_finished(UiEvent *event, int error, void *userdata) {
     SaveResourceJob *job = userdata;
     if(!error) {
+        if(job->prev_parent_id != 0 && job->prev_parent_id != job->res->parent_id) {
+            // move resource to new parent
+            NoteStore *store = note_store_get();
+            Resource *old_parent = note_store_get_notebook_by_id(store, job->prev_parent_id);
+            Resource *new_parent = note_store_get_notebook_by_id(store, job->res->parent_id);
+            if(old_parent && new_parent && old_parent->children) {
+                size_t i = cxListFind(old_parent->children, job->res);
+                cxListRemove(old_parent->children, i);
+                if(!new_parent->children) {
+                    new_parent->children = cxArrayListCreate(store->mp->allocator, CX_STORE_POINTERS, 8);
+                }
+                cxListAdd(new_parent->children, job->res);
+            }
+        }
         note_store_groups_updated();
     }
     if(job->resultcb) {
@@ -1118,10 +1134,11 @@ static void uithr_save_new_resource_finished(UiEvent *event, int64_t newid, int
     uithr_save_resource_finished(event, error, userdata);
 }
 
-void note_store_save_notebook_async(UiObject *obj, Resource *res, execresult_func resultcb, void *userdata) {
+void note_store_save_notebook_async(UiObject *obj, Resource *res, int64_t prev_parent_id, execresult_func resultcb, void *userdata) {
     SaveResourceJob *job = malloc(sizeof(SaveResourceJob));
     job->store = note_store_get();
     job->res = res;
+    job->prev_parent_id = prev_parent_id;
     job->resultcb = resultcb;
     job->userdata = userdata;
     if(res->resource_id == 0) {
index f1b39398aeef6950a7217e379459642e1d8e7db7..319e9c3ce66a609b5c9822d04a1e0d003e6d8e8f 100644 (file)
@@ -140,7 +140,7 @@ void note_store_new_resource_async(UiObject *obj, Resource *res, createresult_fu
 
 void note_store_update_resource_async(UiObject *obj, Resource *res, execresult_func resultcb, void *userdata);
 
-void note_store_save_notebook_async(UiObject *obj, Resource *res, execresult_func resultcb, void *userdata);
+void note_store_save_notebook_async(UiObject *obj, Resource *res, int64_t prev_parent_id, execresult_func resultcb, void *userdata);
 
 void note_store_delete_empty_collection_async(
         UiObject *obj,
index c8fc54d4d80962b3764952762edf33c09dee14e7..f48d39d4659aafa4f629ffa8b601307c6c89620c 100644 (file)
@@ -290,7 +290,7 @@ CX_TEST(test_note_store_save_notebook_async) {
         res0->nodename = cx_strdup_a(store->mp->allocator, "test_save_notebook_res0").ptr;
         
         int error = -1;
-        note_store_save_notebook_async(obj, res0, test_save_notebook_result, &error);
+        note_store_save_notebook_async(obj, res0, 0, test_save_notebook_result, &error);
         ui_exec_buffered_mainthread_calls_wait(3);
         
         CX_TEST_ASSERT(error == 0);
@@ -314,7 +314,7 @@ CX_TEST(test_note_store_save_notebook_async) {
         
         ui_exec_buffered_mainthread_calls();
         error = -1;
-        note_store_save_notebook_async(obj, res0, test_save_notebook_result, &error);
+        note_store_save_notebook_async(obj, res0, 0, test_save_notebook_result, &error);
         ui_exec_buffered_mainthread_calls_wait(3);
         
         CX_TEST_ASSERT(error == 0);