From 579daae890feece295b85dfd55cfea07283885e2 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Tue, 18 Mar 2025 20:57:30 +0100 Subject: [PATCH] implement note deletion --- application/application.c | 24 ++++++++++++++++++++ application/application.h | 2 +- application/menu.c | 16 ++++++++++++- application/menu.h | 3 +++ application/note.c | 20 ++++++++++++++++ application/note.h | 5 +++- application/notebook.c | 30 ++++++++++++++++++++++++ application/notebook.h | 1 + application/store.c | 48 +++++++++++++++++++++++++++++++++++++++ application/store.h | 3 +++ application/window.c | 4 +++- 11 files changed, 152 insertions(+), 4 deletions(-) diff --git a/application/application.c b/application/application.c index 6e5f441..745782b 100644 --- a/application/application.c +++ b/application/application.c @@ -31,12 +31,14 @@ #include "store.h" #include "menu.h" #include "notebook.h" +#include "note.h" #include "editor.h" #include #include #include +#include void application_init() { menu_init(); @@ -130,3 +132,25 @@ void action_note_new(UiEvent *event, void *data) { NotebookModel *notebook = event->document; notebookmodel_new_note(notebook); } + + +static void delete_result(UiEvent *event, void *data) { + NotebookModel *notebook = event->document; + if(event->intval) { + notebookmodel_delete_note(notebook); + } +} + +void action_note_delete(UiEvent *event, void *data) { + NotebookModel *notebook = event->document; + Note *note = notebook->current_note; + cxmutstr msg = cx_asprintf("Delete note %s?", note_get_title(note)); + ui_dialog( + event->obj, + .title = "Delete Note", + .content = msg.ptr, + .button1_label = "Delete", + .closebutton_label = "Cancel", + .result = delete_result); + free(msg.ptr); +} \ No newline at end of file diff --git a/application/application.h b/application/application.h index ac28c5c..00a06fc 100644 --- a/application/application.h +++ b/application/application.h @@ -155,7 +155,7 @@ void action_go_back(UiEvent *event, void *data); void action_go_forward(UiEvent *event, void *data); void action_note_new(UiEvent *event, void *data); - +void action_note_delete(UiEvent *event, void *data); #ifdef __cplusplus } diff --git a/application/menu.c b/application/menu.c index fd89f9a..d2d1ae0 100644 --- a/application/menu.c +++ b/application/menu.c @@ -28,8 +28,17 @@ #include "menu.h" +static UiMenuBuilder* notelist_context_menu; + void menu_init() { - + ui_contextmenu(¬elist_context_menu) { + ui_menuitem(.label = "New Note", .onclick = action_note_new); + ui_menuitem(.label = "Delete", .onclick = action_note_delete); + } +} + +void menu_cleanup() { + ui_menubuilder_free(notelist_context_menu); } void toolbar_init() { @@ -41,3 +50,8 @@ void toolbar_init() { ui_toolbar_add_default("GoForward", UI_TOOLBAR_LEFT); ui_toolbar_add_default("AddNote", UI_TOOLBAR_LEFT); } + + +UiMenuBuilder* get_notelist_context_menu() { + return notelist_context_menu; +} diff --git a/application/menu.h b/application/menu.h index 66b7e57..806dd2d 100644 --- a/application/menu.h +++ b/application/menu.h @@ -38,8 +38,11 @@ extern "C" { #endif void menu_init(); +void menu_cleanup(); void toolbar_init(); +UiMenuBuilder* get_notelist_context_menu(); + #ifdef __cplusplus } diff --git a/application/note.c b/application/note.c index 24ae291..52f0b7e 100644 --- a/application/note.c +++ b/application/note.c @@ -176,3 +176,23 @@ void note_text_style_set_code(NoteModel *note, UiBool enabled) { ui_set(note->textnote_code, !enabled); } } + + +const char* note_get_title(Note *note) { + return note->title ? note->title : note->name; +} + +void note_destroy(const CxAllocator *a, Note *note) { + cxFree(a, note->name); + cxFree(a, note->title); + cxFree(a, note->lastmodified); + cxFree(a, note->creationdate); + cxFree(a, note->contenttype); + cxFree(a, note->created_by); + cxFree(a, note->content.ptr); + cxFree(a, note->bin_content.ptr); + + if(note->model) { + // TODO: destroy model->context + } +} diff --git a/application/note.h b/application/note.h index fcaab32..c2db417 100644 --- a/application/note.h +++ b/application/note.h @@ -42,7 +42,7 @@ typedef struct TextNoteParagraphStyles { } TextNoteParagraphStyles; NoteModel *notemodel_current(UiObject *obj); - + NoteModel* notemodel_create(const CxAllocator *note_allocator); void notemodel_set_note(NoteModel *model, Note *note); @@ -60,6 +60,9 @@ void note_text_style_set_underline(NoteModel *note, UiBool enabled); void note_text_style_set_code(NoteModel *note, UiBool enabled); +const char* note_get_title(Note *note); +void note_destroy(const CxAllocator *a, Note *note); + #ifdef __cplusplus } #endif diff --git a/application/notebook.c b/application/notebook.c index 55cdad2..b2ba831 100644 --- a/application/notebook.c +++ b/application/notebook.c @@ -32,6 +32,8 @@ #include "note.h" #include "editor.h" +#include + NotebookModel* notebookmodel_create() { NotebookModel *model = ui_document_new(sizeof(NotebookModel)); model->ctx = ui_document_context(model); @@ -195,6 +197,34 @@ void notebookmodel_new_note(NotebookModel *model) { editor_load_markdown(new_note->model->text, cx_mutstrn("", 0)); } +typedef struct NoteDeleteOp { + NotebookModel *notebook; + Note *note; +} NoteDeleteOp; + +static void note_deleted(UiEvent *event, int error, void *userdata) { + NotebookModel *notebook = event->document; + NoteDeleteOp *op = userdata; + + CxList *notes = op->notebook->notes->data; + size_t index = cxListFind(notes, op->note); + if(cxListIndexValid(notes, index)) { + cxListRemove(notes, index); + ui_list_update(op->notebook->notes); + note_destroy(op->notebook->current_notes_pool->allocator, op->note); + } else { + fprintf(stderr, "Error: cannot remove deleted note from list\n"); + } +} + +void notebookmodel_delete_note(NotebookModel *model) { + NoteDeleteOp *op = malloc(sizeof(NoteDeleteOp)); + op->notebook = model; + op->note = model->current_note; + note_store_delete_async(model->window->obj, op->note, TRUE, note_deleted, op); +} + + /* * Add the current state (notebook + note + ui options) to the navstack. * If the user navigated through the navstack and the current pos is not the diff --git a/application/notebook.h b/application/notebook.h index 9ed6cbe..ec7178d 100644 --- a/application/notebook.h +++ b/application/notebook.h @@ -52,6 +52,7 @@ void notebookmodel_detach_current_note(NotebookModel *model); Note* notebookmodel_get_note_by_id(NotebookModel *model, int64_t note_id, size_t *index); void notebookmodel_new_note(NotebookModel *model); +void notebookmodel_delete_note(NotebookModel *model); void notebookmodel_add2navstack(NotebookModel *model); diff --git a/application/store.c b/application/store.c index 97fb59b..3d6758f 100644 --- a/application/store.c +++ b/application/store.c @@ -66,6 +66,10 @@ "content = ? " \ "where note_id = ? ;" +#define SQL_NOTE_MOVE_TO_TRASH "update notes set parent_id = ? where note_id = ? ;" + +#define SQL_NOTE_DELETE "delete from notes where note_id = ? ;" + static DBUConnection *connection; static UiThreadpool *queue; @@ -313,6 +317,7 @@ void note_store_reload() { NoteStore *store = cxMalloc(a, sizeof(NoteStore)); store->mp = mp; store->root = NULL; + store->trash = NULL; // key: collection_id value: Collection* CxMap *collection_map = cxHashMapCreate(NULL, CX_STORE_POINTERS, cxListSize(collections) + 16); @@ -554,3 +559,46 @@ void note_store_save_note_async(UiObject *obj, Note *note, execresult_func resul job->error = 0; ui_threadpool_job(queue, obj, (ui_threadfunc)qthr_save_note, job, (ui_callback)uithr_save_note_finished, job); } + + +typedef struct DeleteNoteJob { + int64_t note_id; + UiBool move_to_trash; + execresult_func resultcb; + void *userdata; + int error; +} DeleteNoteJob; + +static int qthr_delete_note(DeleteNoteJob *job) { + DBUQuery *q = connection->createQuery(connection, NULL); + if(job->move_to_trash && current_store->trash) { + dbuQuerySetSQL(q, SQL_NOTE_MOVE_TO_TRASH); + dbuQuerySetParamInt64(q, 1, current_store->trash->collection_id); + dbuQuerySetParamInt64(q, 2, job->note_id); + } else { + dbuQuerySetSQL(q, SQL_NOTE_DELETE); + dbuQuerySetParamInt64(q, 1, job->note_id); + } + if(dbuQueryExec(q)) { + job->error = 1; + } + dbuQueryFree(q); + return 0; +} + +static void uithr_save_delete_finished(UiEvent *event, DeleteNoteJob *job) { + if(job->resultcb) { + job->resultcb(event, job->error, job->userdata); + } + free(job); +} + +void note_store_delete_async(UiObject *obj, Note *note, UiBool move_to_trash, execresult_func resultcb, void *userdata) { + DeleteNoteJob *job = malloc(sizeof(DeleteNoteJob)); + job->note_id = note->note_id; + job->move_to_trash = move_to_trash; + job->resultcb = resultcb; + job->userdata = userdata; + job->error = 0; + ui_threadpool_job(queue, obj, (ui_threadfunc)qthr_delete_note, job, (ui_callback)uithr_save_delete_finished, job); +} diff --git a/application/store.h b/application/store.h index 1c96af7..629b3b3 100644 --- a/application/store.h +++ b/application/store.h @@ -43,6 +43,7 @@ extern "C" { typedef struct NoteStore { CxMempool *mp; Collection *root; + Collection *trash; } NoteStore; typedef struct AsyncListResult { @@ -89,6 +90,8 @@ void note_store_get_note_content_async(UiObject *obj, const CxAllocator *a, int6 void note_store_new_note_async(UiObject *obj, Note *note, execresult_func resultcb, void *userdata); void note_store_save_note_async(UiObject *obj, Note *note, execresult_func resultcb, void *userdata); +void note_store_delete_async(UiObject *obj, Note *note, UiBool move_to_trash, execresult_func resultcb, void *userdata); + #ifdef __cplusplus } #endif diff --git a/application/window.c b/application/window.c index 779a6cc..a1da5c9 100644 --- a/application/window.c +++ b/application/window.c @@ -32,6 +32,7 @@ #include "notebook.h" #include "editor.h" #include "note.h" +#include "menu.h" #include #include @@ -60,7 +61,8 @@ void window_create() { UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING_FREE, "Last Modified", -1); model->columnsize[0] = -1; model->getvalue = window_notelist_getvalue; - ui_table(obj, .model = model, .varname = "notes", .multiselection = TRUE, .onselection = action_note_selected, .onactivate = action_note_activated); + + ui_table(obj, .model = model, .varname = "notes", .contextmenu = get_notelist_context_menu(), .multiselection = TRUE, .onselection = action_note_selected, .onactivate = action_note_activated); // splitpane right: content ui_tabview_w(obj, wdata->document_tabview, .tabview = UI_TABVIEW_INVISIBLE, .varname = "note_type") { -- 2.43.5