]> uap-core.de Git - note.git/commitdiff
implement case insensitive and backwards search
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 31 Jan 2026 17:20:31 +0000 (18:20 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sat, 31 Jan 2026 17:20:31 +0000 (18:20 +0100)
application/application.c
application/application.h
application/note.c
application/note.h
application/notebook.c
application/notebook.h
application/window.c
application/window.h

index 29ef4ada50976dbdade6ba4c3841add44328a00f..d8736a4bf2a263fef04cea2d94e35ee7b6f79023 100644 (file)
@@ -178,11 +178,9 @@ void action_note_delete(UiEvent *event, void *data) {
 }
 
 void action_find(UiEvent *event, void *data) {
-    ui_set_state(event->obj->ctx, APP_STATE_NOTE_FIND);
-    ui_unset_state(event->obj->ctx, APP_STATE_NOTE_REPLACE);
+    window_show_searchbar(event->window, FALSE);
 }
 
 void action_replace(UiEvent *event, void *data) {
-    ui_set_state(event->obj->ctx, APP_STATE_NOTE_FIND);
-    ui_set_state(event->obj->ctx, APP_STATE_NOTE_REPLACE);
+    window_show_searchbar(event->window, TRUE);
 }
index 2957a2ee95c6ea30ba902bf0be65bb9601289a08..8dedf352bde5d93acac2efe09aa6a23bd8b023f2 100644 (file)
@@ -66,6 +66,8 @@ typedef struct MainWindow {
     UIWIDGET textview;
     UIWIDGET attachments;
     
+    UIWIDGET searchbar_textfield;
+    
     /*
      * is the note list visible (splitpane child 0)
      */
index a2a506fc523ff15733c1718ecc6fd81e9cd0d6c2..3781a45c365082e0949c19260e82181123149bf3 100644 (file)
@@ -266,7 +266,7 @@ void note_insert_image(NoteModel *note, const char *filepath) {
 void note_update_title(NotebookModel *notebook, Note *note) {
     NoteModel *m = note->model;
     
-    int index = notebookmode_get_note_index(notebook, note);
+    int index = notebookmodel_get_note_index(notebook, note);
     if(index < 0 && !m->new_note) {
         return;
     }
@@ -316,3 +316,109 @@ Attachment* note_get_attachment(Resource *note, const char *path) {
     }
     return NULL;
 }
+
+
+/* ----------------------------- Search/Replace ---------------------------- */
+
+cxstring text_search_strcasestr(cxstring haystack, cxstring needle) {
+    if(needle.length == 0) {
+        return haystack;
+    }
+    
+    for(size_t i=0;i<haystack.length;i++) {
+        cxstring str = cx_strn(haystack.ptr+i, haystack.length-i);
+        cxstring str2 = str;
+        if(str2.length > needle.length) {
+            str2.length = needle.length;
+        }
+        if(!cx_strcasecmp(str2, needle)) {
+            return str;
+        }
+    }
+    return (cxstring){NULL,0};
+}
+
+int text_search(
+        cxstring text,
+        cxstring search,
+        int pos,
+        bool backwards,
+        bool case_sensitive,
+        bool regex,
+        int *result_begin,
+        int *result_end)
+{
+    cxstring result = (cxstring){NULL,0};
+    if(!backwards) {
+        cxstring subtext = cx_strsubs(text, pos);
+        if(!regex) {
+            result = case_sensitive ? cx_strstr(subtext, search) : text_search_strcasestr(subtext, search);
+        } else {
+            // regex search
+            // TODO
+        }
+    } else {
+        // use text_search in forward-mode as long as result_end < pos
+        int begin, end;
+        int prev_begin = -1;
+        int prev_end = 0;
+        cxstring subtext = text;
+        while(text_search(subtext, search, prev_end, FALSE, case_sensitive, regex, &begin, &end)) {
+            if(end > pos) {
+                break;
+            }
+            prev_begin = begin;
+            prev_end = end;
+        }
+        if(prev_begin >= 0) {
+            result = cx_strn(text.ptr + prev_begin, prev_end-prev_begin);
+        }
+    }
+    if(result.ptr) {
+        *result_begin = (int)(result.ptr - text.ptr);
+        *result_end = *result_begin + search.length;
+        return 1;
+    }
+    return 0;
+}
+
+static int get_search_start_pos(NoteModel *note) {
+    int pos = note->text->position(note->text);
+    int sel_begin, sel_end;
+    note->text->selection(note->text, &sel_begin, &sel_end);
+    if(sel_end >= 0) {
+        pos = sel_end;
+    }
+    return pos;
+}
+
+static void note_search(NoteModel *note, bool backwards) {
+    cxstring searchstr = cx_str(ui_get(note->search));
+    if(searchstr.length == 0) {
+        return;
+    }
+    
+    cxstring text = cx_str(ui_get(note->text));
+    int pos = get_search_start_pos(note);
+
+    bool cs = (bool)ui_get(note->search_cs);
+    bool regex = (bool)ui_get(note->search_regex);
+    
+    int begin, end;
+    if(text_search(text, searchstr, pos, backwards, regex, cs, &begin, &end)) {
+        note->text->setposition(note->text, end);
+        note->text->setselection(note->text, begin, end);
+    }
+}
+
+void note_search_next(NoteModel *note) {
+    note_search(note, FALSE);
+}
+
+void note_search_prev(NoteModel *note) {
+    note_search(note, TRUE);
+}
+
+void note_replace(NoteModel *note) {
+    
+}
index 948664729a539045da8c41f5f0fc4cb9f9eaed89..f7be3599180cd56f19d54679f57cb8a2d8a1c8dc 100644 (file)
@@ -70,6 +70,22 @@ void note_destroy(const CxAllocator *a, Note *note);
 
 Attachment* note_get_attachment(Resource *note, const char *path);
 
+cxstring text_search_strcasestr(cxstring haystack, cxstring needle);
+
+int text_search(
+        cxstring text,
+        cxstring search,
+        int pos,
+        bool backwards,
+        bool case_sensitive,
+        bool regex,
+        int *result_begin,
+        int *result_end);
+
+void note_search_next(NoteModel *note);
+void note_search_prev(NoteModel *note);
+void note_replace(NoteModel *note);
+
 #ifdef __cplusplus
 }
 #endif
index 37a9e9f9360b3156d2161ea96a00d1fd237998d9..6cf0d8b555fd5bf184d704fe8214d6505003abf0 100644 (file)
@@ -296,7 +296,7 @@ void notebookmodel_add2navstack(NotebookModel *model) {
     */
 }
 
-int notebookmode_get_note_index(NotebookModel *model, Note *note) {
+int notebookmodel_get_note_index(NotebookModel *model, Note *note) {
     CxList *list = model->notes->data;
     size_t index = cxListFind(list, note);
     return cxListIndexValid(list, index) ? index : -1;
index 89ea350f127135f9eabfebb33fce52a2b502e8e0..21a293700efee014cdeda88a7e4673f38152d3b8 100644 (file)
@@ -56,7 +56,7 @@ void notebookmodel_delete_note(NotebookModel *model);
 
 void notebookmodel_add2navstack(NotebookModel *model);
 
-int notebookmode_get_note_index(NotebookModel *model, Note *note);
+int notebookmodel_get_note_index(NotebookModel *model, Note *note);
 
 
 #ifdef __cplusplus
index 61a693f4a179385ce01f08742254edb5a4476fca..4811571e9a579a02684401b89eaff06508cfeb95 100644 (file)
@@ -120,9 +120,9 @@ void window_create() {
                     
                     ui_grid(obj, .margin = 4, .columnspacing = 4, .rowspacing = 4, .def_hfill = TRUE, .def_vfill = TRUE, .visibility_states = UI_GROUPS(APP_STATE_NOTE_FIND)) {
                         ui_rlabel(obj, .label = "Find");
-                        ui_textfield(obj, .hexpand = TRUE, .onactivate = ui_searchbar_next, .varname = "search");
-                        ui_button(obj, .label = "Previous");
-                        ui_button(obj, .label = "Next", .onclick = ui_searchbar_next);
+                        wdata->searchbar_textfield = ui_textfield(obj, .hexpand = TRUE, .onactivate = action_searchbar_next, .varname = "search");
+                        ui_button(obj, .label = "Previous", .onclick = action_searchbar_prev);
+                        ui_button(obj, .label = "Next", .onclick = action_searchbar_next);
                         ui_checkbox(obj, .label = "Case Sensitive", .varname = "search_cs");
                         ui_checkbox(obj, .label = "Regex", .varname = "search_regex");
                         ui_button(obj, .icon = "window-close", .onclick = action_searchbar_close);
@@ -295,6 +295,17 @@ void window_store_groups_updated(NoteStore *store, MainWindow *window) {
     update_sublists(window->obj->ctx, window->notebooks);
 }
 
+void window_show_searchbar(MainWindow *window, UiBool replace) {
+    UiContext *ctx = window->obj->ctx;
+    ui_set_state(ctx, APP_STATE_NOTE_FIND);
+    if(replace) {
+        ui_set_state(ctx, APP_STATE_NOTE_REPLACE);
+    } else {
+        ui_unset_state(ctx, APP_STATE_NOTE_REPLACE);
+    }
+    ui_textfield_focus(window->searchbar_textfield);
+}
+
 
 typedef struct NotebookCreatedResult {
     MainWindow *window;
@@ -564,26 +575,16 @@ void action_searchbar_close(UiEvent *event, void *userdata) {
     ui_unset_state(event->obj->ctx, APP_STATE_NOTE_REPLACE);
 }
 
-void ui_searchbar_next(UiEvent *event, void *userdata) {
+void action_searchbar_next(UiEvent *event, void *userdata) {
     NoteModel *note = notemodel_current(event->obj);
-    if(!note) {
-        return;
-    }
-    
-    cxstring searchstr = cx_str(ui_get(note->search));
-    if(searchstr.length == 0) {
-        return;
+    if(note) {
+        note_search_next(note);
     }
-    
-    cxstring text = cx_str(ui_get(note->text));
-    int pos = note->text->position(note->text);
-    cxstring subtext = cx_strsubs(text, pos);
-    
-    cxstring result = cx_strstr(subtext, searchstr);
-    if(result.ptr) {
-        size_t result_pos = result.ptr - text.ptr;
-        size_t result_end = result_pos + searchstr.length;
-        note->text->setposition(note->text, result_end);
-        note->text->setselection(note->text, (int)result_pos, (int)result_end);
+}
+
+void action_searchbar_prev(UiEvent *event, void *userdata) {
+    NoteModel *note = notemodel_current(event->obj);
+    if(note) {
+        note_search_prev(note);
     }
 }
index c65d25f2fe6be887c2d3c21063f3e9e001083fdb..fe1e1ee3cc586fcb41707358c3c7ec1b301889f9 100644 (file)
@@ -69,6 +69,8 @@ void* window_notelist_getvalue(void *data, int col);
 // note store listener funcs
 void window_store_groups_updated(NoteStore *store, MainWindow *window);
 
+void window_show_searchbar(MainWindow *window, UiBool replace);
+
 
 void action_notebook_add(UiEvent *event, void *userdata);
 void action_notebook_config(UiEvent *event, void *userdata);
@@ -88,7 +90,8 @@ void action_textnote_insertlist(UiEvent *event, void *userdata);
 void action_textnote_insertimg(UiEvent *event, void *userdata);
 
 void action_searchbar_close(UiEvent *event, void *userdata);
-void ui_searchbar_next(UiEvent *event, void *userdata);
+void action_searchbar_next(UiEvent *event, void *userdata);
+void action_searchbar_prev(UiEvent *event, void *userdata);