From dfcef48a830e6703c9e105adc468b24b06cbdab4 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 10 Mar 2025 22:22:41 +0100 Subject: [PATCH] add editor_get_markdown for converting a GtkTextBuffer to markdown, implement heading and strong --- application/application.c | 3 + application/editor.h | 2 + application/gtk-text.c | 124 ++++++++++++++++++++++++++++++++++++++ application/gtk-text.h | 5 ++ application/note.c | 4 +- 5 files changed, 136 insertions(+), 2 deletions(-) diff --git a/application/application.c b/application/application.c index 16e53d6..6e5f441 100644 --- a/application/application.c +++ b/application/application.c @@ -31,6 +31,7 @@ #include "store.h" #include "menu.h" #include "notebook.h" +#include "editor.h" #include #include @@ -40,6 +41,8 @@ void application_init() { menu_init(); toolbar_init(); + + editor_global_init(); } void application_startup(UiEvent *event, void *data) { diff --git a/application/editor.h b/application/editor.h index 246ba1b..a026be7 100644 --- a/application/editor.h +++ b/application/editor.h @@ -107,9 +107,11 @@ MDDocLinear mddoc_linearization(MDDoc *doc); // platform specific implementation // (gtk-text.c) +void editor_global_init(); void editor_init_textview(UIWIDGET textview); void editor_init_textbuf(UiText *text); void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles); +cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a); #ifdef __cplusplus } diff --git a/application/gtk-text.c b/application/gtk-text.c index 9d9aa9d..688f14a 100644 --- a/application/gtk-text.c +++ b/application/gtk-text.c @@ -29,6 +29,25 @@ #include "gtk-text.h" #include "editor.h" +#include +#include + +static CxMap *markdown_tags; + +void editor_global_init() { + markdown_tags = cxHashMapCreateSimple(sizeof(MDTag)); + + cxMapPut(markdown_tags, EDITOR_STYLE_PARAGRAPH, &((MDTag){NULL, NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING1, &((MDTag){"# ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING2, &((MDTag){"## ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING3, &((MDTag){"### ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING4, &((MDTag){"#### ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING5, &((MDTag){"##### ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_HEADING6, &((MDTag){"###### ", NULL})); + cxMapPut(markdown_tags, EDITOR_STYLE_STRONG, &((MDTag){"**", "**"})); +} + + void editor_init_textview(UIWIDGET textview) { gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); @@ -119,3 +138,108 @@ void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) { } } } + + +static CxMap* tags2map(GSList *tags) { + CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); + while(tags) { + GtkTextTag *t = tags->data; + GValue name_value = G_VALUE_INIT; + g_object_get_property(G_OBJECT(t), "name", &name_value); + const char *name = g_value_get_string(&name_value); + if(name) { + cxMapPut(map, name, t); + } + tags = tags->next; + } + return map; +} + +static CxMap* map_clone(CxMap *map) { + CxMap *newmap = cxHashMapCreateSimple(CX_STORE_POINTERS); + CxMapIterator i = cxMapIterator(map); + cx_foreach(CxMapEntry *, entry, i) { + cxMapPut(newmap, *entry->key, entry->value); + } + return newmap; +} + +static void map_subtract(CxMap *map, CxMap *sub) { + CxMapIterator i = cxMapIterator(sub); + cx_foreach(CxMapEntry *, entry, i) { + cxMapRemove(map, *entry->key); + } +} + +cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { + char *str = ui_get(text); + + + CxBuffer out; + cxBufferInit(&out, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND); + + GtkTextBuffer *buffer = text->data1; + + CxMap *prev_tags = cxHashMapCreateSimple(CX_STORE_POINTERS); + + GtkTextIter start; + GtkTextIter iter; + gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0); + start = iter; + while(gtk_text_iter_forward_to_tag_toggle(&iter, NULL)) { + gchar *text = gtk_text_buffer_get_text(buffer, &start, &iter, TRUE); + + GSList *tags = gtk_text_iter_get_tags(&start); + CxMap *begin_tags = tags2map(tags); + g_slist_free(tags); + + // subtract prev_tags from begin_tags to get a map of all new tags + CxMap *new_tags = map_clone(begin_tags); + CxMap *begin_tags2 = map_clone(begin_tags); + map_subtract(new_tags, prev_tags); + cxMapFree(prev_tags); + prev_tags = begin_tags; + + // check all new tags and add tag prefix code + CxMapIterator i = cxMapIterator(new_tags); + cx_foreach(CxMapEntry *, entry, i) { + MDTag *t = cxMapGet(markdown_tags, *entry->key); + if(t && t->begin) { + cxBufferPutString(&out, t->begin); + } + } + cxMapFree(new_tags); + + // add content + printf("range: {%s}\n\n", text); + cxBufferPutString(&out, text); + + // get all tags that ended here + tags = gtk_text_iter_get_tags(&iter); + CxMap *end_tags = tags2map(tags); + g_slist_free(tags); + + // check end tags and add tag suffix + map_subtract(begin_tags2, end_tags); + i = cxMapIterator(begin_tags2); + cx_foreach(CxMapEntry *, entry, i) { + MDTag *t = cxMapGet(markdown_tags, *entry->key); + if(t && t->end) { + cxBufferPutString(&out, t->end); + } + } + cxMapFree(begin_tags2); + cxMapFree(end_tags); + + g_free(text); + start = iter; + } + + + cxBufferTerminate(&out); + + printf("new markdown doc: \n\n%s\n\n", out.space); + + cxmutstr ret = cx_mutstrn(out.space, out.size); + return ret; +} diff --git a/application/gtk-text.h b/application/gtk-text.h index 55591b3..f09d4db 100644 --- a/application/gtk-text.h +++ b/application/gtk-text.h @@ -34,6 +34,11 @@ #ifdef __cplusplus extern "C" { #endif + +typedef struct MDTag { + const char *begin; + const char *end; +} MDTag; void init_textbuf(GtkTextBuffer *buf); void init_tagtable(GtkTextTagTable *table); diff --git a/application/note.c b/application/note.c index e571e30..165c77a 100644 --- a/application/note.c +++ b/application/note.c @@ -93,7 +93,6 @@ void note_save(UiObject *obj, NotebookModel *notebook, Note *note) { NoteModel *m = note->model; char *title = ui_get(m->title); - char *content = ui_get(m->text); const CxAllocator *a = notebook->current_notes_pool->allocator; cxFree(a, note->title); @@ -102,8 +101,9 @@ void note_save(UiObject *obj, NotebookModel *notebook, Note *note) { cxFree(a, note->name); note->name = cx_strdup_a(a, cx_str(title)).ptr; + cxmutstr content = editor_get_markdown(m->text, a); cxFree(a, note->content.ptr); - note->content = cx_strdup_a(a, cx_str(content)); + note->content = content; if(note->note_id == 0) { // new note -- 2.43.5