]> uap-core.de Git - note.git/commitdiff
implement text style changes via toolbar buttons
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 13 Mar 2025 19:59:18 +0000 (20:59 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 13 Mar 2025 19:59:18 +0000 (20:59 +0100)
12 files changed:
application/application.h
application/editor.c
application/editor.h
application/gtk-text.c
application/gtk-text.h
application/note.c
application/note.h
application/window.c
application/window.h
ui/common/types.c
ui/gtk/button.c
ui/ui/toolkit.h

index 1c6d1e288479458a591445eb440f6ff59190699b..ac28c5c1334476ff1ce9d1af014c44ca8b8e7f45 100644 (file)
@@ -137,6 +137,11 @@ struct NoteModel {
      */
     UiList *textnote_para;
     
+    UiInteger *textnote_strong;
+    UiInteger *textnote_emphasis;
+    UiInteger *textnote_underline;
+    UiInteger *textnote_code;
+    
     bool modified;
 };
    
index 179d4ea19c11e404d20ea2cecaf8e2e3a1816e9a..93289afda437785cd15d17ca9b224e664d1c4fe1 100644 (file)
@@ -273,6 +273,7 @@ static const char* node_style(MDNode *n) {
         case MD_SPAN_EM: return EDITOR_STYLE_EMPHASIS;
         case MD_SPAN_STRONG: return EDITOR_STYLE_STRONG;
         case MD_SPAN_A: return EDITOR_STYLE_LINK;
+        case MD_SPAN_CODE: return EDITOR_STYLE_CODE;
     }
     return NULL;
 }
@@ -316,6 +317,7 @@ static const char* paragraph_style(MDPara *p) {
                 case 6: return EDITOR_STYLE_HEADING6;
             }
         }
+        case MD_BLOCK_CODE: return EDITOR_STYLE_CODE_BLOCK;
         default: return EDITOR_STYLE_PARAGRAPH;
     }
 }
index 15dc2ab91bb41844ff146a8093a0e0176b17975b..11c798f81e314c2fe51b5f155a4c3305d3dc4825 100644 (file)
 extern "C" {
 #endif
 
-#define EDITOR_STYLE_PARAGRAPH "para"
-#define EDITOR_STYLE_HEADING1  "heading1"
-#define EDITOR_STYLE_HEADING2  "heading2"
-#define EDITOR_STYLE_HEADING3  "heading3"
-#define EDITOR_STYLE_HEADING4  "heading4"
-#define EDITOR_STYLE_HEADING5  "heading5"
-#define EDITOR_STYLE_HEADING6  "heading6"
-#define EDITOR_STYLE_QUOTE     "quote"
-#define EDITOR_STYLE_CODE      "code"
-#define EDITOR_STYLE_EMPHASIS  "emphasis"
-#define EDITOR_STYLE_STRONG    "strong"
-#define EDITOR_STYLE_LINK      "link"
+#define EDITOR_STYLE_PARAGRAPH  "para"
+#define EDITOR_STYLE_HEADING1   "heading1"
+#define EDITOR_STYLE_HEADING2   "heading2"
+#define EDITOR_STYLE_HEADING3   "heading3"
+#define EDITOR_STYLE_HEADING4   "heading4"
+#define EDITOR_STYLE_HEADING5   "heading5"
+#define EDITOR_STYLE_HEADING6   "heading6"
+#define EDITOR_STYLE_QUOTE      "quote"
+#define EDITOR_STYLE_CODE_BLOCK "code_block"
+#define EDITOR_STYLE_EMPHASIS   "emphasis"
+#define EDITOR_STYLE_STRONG     "strong"
+#define EDITOR_STYLE_CODE       "code"
+#define EDITOR_STYLE_LINK       "link"
     
     
 #define MD_MAX_DEPTH 50
@@ -60,6 +61,8 @@ typedef struct MDNode MDNode;
 typedef struct MDDocLinear MDDocLinear;
 typedef struct MDDocStyleSection MDDocStyleSection;
 
+typedef struct MDActiveStyles MDActiveStyles;
+
 struct MDPara {
     MD_BLOCKTYPE type;
     MDNode *content;
@@ -95,6 +98,14 @@ struct MDDocStyleSection {
     const char *style;
     const char *link;
 };
+
+struct MDActiveStyles {
+    const char *paragraph;
+    int paragraph_index;
+    UiBool emphasis;
+    UiBool strong;
+    UiBool code;
+};
     
 void editor_init(UiText *text);
     
@@ -116,6 +127,8 @@ void editor_init_textbuf(UiText *text);
 void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles);
 cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a);
 
+UiBool editor_set_style(UiText *text, const char *style, UiBool enabled);
+
 #ifdef __cplusplus
 }
 #endif
index e0b7f5c70a08174bd7cc0187eb223f02009b5c5f..4702038b8094739f460ec9cb63d6ad0d81d3a819 100644 (file)
 
 #include "gtk-text.h"
 #include "editor.h"
+#include "note.h"
 
 #include <cx/buffer.h>
 #include <cx/hash_map.h>
 
+// workaround for netbeans bug
+#define g_object_set g_object_set
+#define g_object_get_property g_object_get_property
+#define g_object_set_data g_object_set_data
+#define g_object_get_data g_object_get_data
+
 static CxMap *markdown_tags;
 
 static void editor_button_released_cb(
@@ -41,6 +48,12 @@ static void editor_button_released_cb(
         double y,
         NoteEditor *editor);
 
+static void editor_set_cursor_cb(
+        GtkTextBuffer *buffer,
+        const GtkTextIter *location,
+        GtkTextMark *mark,
+        NoteEditor *editor);
+
 void editor_global_init() {
     markdown_tags = cxHashMapCreateSimple(sizeof(MDTag));
     
@@ -52,9 +65,10 @@ void editor_global_init() {
     cxMapPut(markdown_tags, EDITOR_STYLE_HEADING5, &((MDTag){"##### ", NULL}));
     cxMapPut(markdown_tags, EDITOR_STYLE_HEADING6, &((MDTag){"###### ", NULL}));
     cxMapPut(markdown_tags, EDITOR_STYLE_STRONG, &((MDTag){"**", "**"}));
+    cxMapPut(markdown_tags, EDITOR_STYLE_EMPHASIS, &((MDTag){"*", "*"}));
+    cxMapPut(markdown_tags, EDITOR_STYLE_CODE, &((MDTag){"`", "`"}));
 }
 
-
 void editor_init_textview(UiObject *obj, UIWIDGET textview) {
     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR);
     gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 16);
@@ -67,11 +81,15 @@ void editor_init_textview(UiObject *obj, UIWIDGET textview) {
     
     g_object_set_data(G_OBJECT(textview), "editor", editor);
     
+    // gesture event controller is used for handling clicks on links
     GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new());
-    g_signal_connect(controller, "released", G_CALLBACK (editor_button_released_cb), editor);
+    g_signal_connect(controller, "released", G_CALLBACK(editor_button_released_cb), editor);
     gtk_widget_add_controller(textview, controller);
 }
 
+/*
+ * handle clicks inside the textview and follow links when possible
+ */
 static void editor_button_released_cb(
         GtkGestureClick *gesture,
         guint n_press,
@@ -105,6 +123,46 @@ static void editor_button_released_cb(
     }
 }
 
+static void editor_set_cursor_cb(
+        GtkTextBuffer *buffer,
+        const GtkTextIter *location,
+        GtkTextMark *mark,
+        NoteEditor *editor)
+{
+    const char *mark_name = gtk_text_mark_get_name(mark);
+    if(!mark_name || strcmp(mark_name, "insert")) {
+        return;
+    }
+    
+    GtkTextIter pos;
+    // get current tags
+    // when the buffer has a selection, get the tags from the selection start
+    // without a selection, get the tags at the cursor position
+    if(gtk_text_buffer_get_has_selection(buffer)) {
+        GtkTextIter end;
+        gtk_text_buffer_get_selection_bounds(buffer, &pos, &end);
+    } else {
+        GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
+        gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark);
+    }
+    
+    MDActiveStyles styles = { 0 };
+    GSList *tags = gtk_text_iter_get_tags(&pos);
+    while(tags) {
+        GtkTextTag *tag = tags->data;
+        set_style_cb set_style = (set_style_cb)g_object_get_data(G_OBJECT(tag), "set_style");
+        if(set_style) {
+            set_style(&styles, tag);
+        }
+        tags = tags->next;
+    }
+    
+    NoteModel *note = notemodel_current(editor->obj);
+    if(note) {
+        note_update_current_style(note, &styles);
+    }
+}
+
 void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos) {
     GSList *tags = gtk_text_iter_get_tags(pos);
     while(tags) {
@@ -134,6 +192,15 @@ void editor_init_textbuf(UiText *text) {
         init_textbuf(buf);
         g_object_set_data(G_OBJECT(buf), "md", text);
     }
+    
+    GtkTextView *textview = text->obj;
+    if(!textview) {
+        return;
+    }
+    NoteEditor *editor = g_object_get_data(G_OBJECT(textview), "editor");
+    if(editor) {
+        g_signal_connect(buf, "mark-set", G_CALLBACK(editor_set_cursor_cb), editor);
+    }
 }
 
 void init_textbuf(GtkTextBuffer *buf) {
@@ -143,58 +210,140 @@ void init_textbuf(GtkTextBuffer *buf) {
     
 }
 
+
+static void tagstyle_paragraph(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_PARAGRAPH;
+    style->paragraph_index = 0;
+}
+
+static void tagstyle_heading1(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING1;
+    style->paragraph_index = 3;
+}
+
+static void tagstyle_heading2(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING2;
+    style->paragraph_index = 4;
+}
+
+static void tagstyle_heading3(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING3;
+    style->paragraph_index = 5;
+}
+
+static void tagstyle_heading4(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING4;
+    style->paragraph_index = 6;
+}
+
+static void tagstyle_heading5(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING5;
+    style->paragraph_index = 7;
+}
+
+static void tagstyle_heading6(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_HEADING6;
+    style->paragraph_index = 8;
+}
+
+static void tagstyle_quote(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_QUOTE;
+    style->paragraph_index = 2;
+}
+
+static void tagstyle_codeblock(MDActiveStyles *style, GtkTextTag *tag) {
+    style->paragraph = EDITOR_STYLE_CODE_BLOCK;
+    style->paragraph_index = 1;
+}
+
+static void tagstyle_emphasis(MDActiveStyles *style, GtkTextTag *tag) {
+    style->emphasis = TRUE;
+}
+
+static void tagstyle_strong(MDActiveStyles *style, GtkTextTag *tag) {
+    style->strong = TRUE;
+}
+
+static void tagstyle_code(MDActiveStyles *style, GtkTextTag *tag) {
+    style->code = TRUE;
+}
+
+static void tagstyle_link(MDActiveStyles *style, GtkTextTag *tag) {
+    
+}
+
 void init_tagtable(GtkTextTagTable *table) {
     printf("init_tagtable\n");
     GtkTextTag *tag;
     
     tag = gtk_text_tag_new(EDITOR_STYLE_PARAGRAPH);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_paragraph);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING1);
     g_object_set(tag, "scale", 1.5, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading1);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING2);
     g_object_set(tag, "scale", 1.4, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading2);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING3);
     g_object_set(tag, "scale", 1.3, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading3);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING4);
     g_object_set(tag, "scale", 1.2, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading4);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING5);
     g_object_set(tag, "scale", 1.1, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading5);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_HEADING6);
     g_object_set(tag, "scale", 1.1, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading6);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_QUOTE);
     g_object_set(tag, "left-margin", 20, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_quote);
     gtk_text_tag_table_add(table, tag);
     
-    tag = gtk_text_tag_new(EDITOR_STYLE_CODE);
+    tag = gtk_text_tag_new(EDITOR_STYLE_CODE_BLOCK);
     g_object_set(tag, "family", "Monospace", "paragraph-background", "#080808", NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_codeblock);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_EMPHASIS);
     g_object_set(tag, "style", PANGO_STYLE_ITALIC, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_emphasis);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_STRONG);
     g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_strong);
+    gtk_text_tag_table_add(table, tag);
+    
+    tag = gtk_text_tag_new(EDITOR_STYLE_CODE);
+    g_object_set(tag, "family", "Monospace", "background", "#efefef", NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_code);
     gtk_text_tag_table_add(table, tag);
     
     tag = gtk_text_tag_new(EDITOR_STYLE_LINK);
     g_object_set(tag, "foreground", "blue", NULL);
+    g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_link);
     gtk_text_tag_table_add(table, tag);
 }
 
+/*
+ * Applies all styles from the MDDocStyleSection list to the text buffer
+ */
 void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) {
     GtkTextBuffer *buffer = text->data1;
     GtkTextTagTable *tagtable = gtk_text_buffer_get_tag_table(buffer);
@@ -220,7 +369,11 @@ void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) {
     }
 }
 
-
+/*
+ * Creates a map from a GtkTextTag list
+ * key: tag name
+ * value: GtkTextTag* 
+ */
 static CxMap* tags2map(GSList *tags) {
     CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS);
     while(tags) {
@@ -236,6 +389,10 @@ static CxMap* tags2map(GSList *tags) {
     return map;
 }
 
+/*
+ * Clones a map (that stores pointers)
+ * The values are not cloned
+ */
 static CxMap* map_clone(CxMap *map) {
     CxMap *newmap = cxHashMapCreateSimple(CX_STORE_POINTERS);
     CxMapIterator i = cxMapIterator(map);
@@ -245,6 +402,9 @@ static CxMap* map_clone(CxMap *map) {
     return newmap;
 }
 
+/*
+ * Removes all sub entries from map
+ */
 static void map_subtract(CxMap *map, CxMap *sub) {
     CxMapIterator i = cxMapIterator(sub);
     cx_foreach(CxMapEntry *, entry, i) {
@@ -252,6 +412,9 @@ static void map_subtract(CxMap *map, CxMap *sub) {
     }
 }
 
+/*
+ * Converts the UiText text buffer to markdown
+ */
 cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { 
     CxBuffer out;
     cxBufferInit(&out, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND);
@@ -321,3 +484,33 @@ cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) {
     cxmutstr ret = cx_mutstrn(out.space, out.size);
     return ret;
 }
+
+/*
+ * Apply a non-paragraph style to the current selection
+ */
+UiBool editor_set_style(UiText *text, const char *style, UiBool enabled) {
+    GtkTextBuffer *buffer = text->data1;
+    if(!buffer) {
+        fprintf(stderr, "Error: editor_set_style: no text buffer\n");
+        return FALSE;
+    }
+    
+    if(!gtk_text_buffer_get_has_selection(buffer)) {
+        return FALSE;
+    }
+    GtkTextIter begin, end;
+    gtk_text_buffer_get_selection_bounds(buffer, &begin, &end);
+    
+    if(enabled) {
+        gtk_text_buffer_apply_tag_by_name(buffer, style, &begin, &end);
+    } else {
+        GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), style);
+        if(tag) {
+            gtk_text_buffer_remove_tag(buffer, tag, &begin, &end);
+        } else {
+            fprintf(stderr, "Error: tag %s not found\n", style);
+        }
+    }
+    
+    return TRUE;
+}
index 57e69a90ddaa9645f317f831e950bbee68a07569..3f25b254b3615af16b8e8a11a2a3077ad50b93ab 100644 (file)
@@ -50,6 +50,8 @@ typedef struct TextLink {
     int type;
 } TextLink;
 
+typedef void (*set_style_cb)(MDActiveStyles *style, GtkTextTag *tag);
+
 void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos);
 
 void init_textbuf(GtkTextBuffer *buf);
index 165c77a76b5f5a593ee4e28d6cad694b7acbbf8a..4f3bd3cd0f7e3fb87ce3cf09b9db141042da5baa 100644 (file)
 #include "store.h"
 #include "editor.h"
 
+NoteModel *notemodel_current(UiObject *obj) {
+    MainWindow *win = obj->window;
+    if(win->current_notebook && win->current_notebook->current_note) {
+        return win->current_notebook->current_note->model;
+    }
+    return NULL;
+}
+
 NoteModel* notemodel_create(const CxAllocator *note_allocator) {
     NoteModel *model = ui_document_new(sizeof(NoteModel));
     model->ctx = ui_document_context(model);
@@ -57,6 +65,11 @@ NoteModel* notemodel_create(const CxAllocator *note_allocator) {
     ui_list_append(model->textnote_para, "Heading 5");
     ui_list_append(model->textnote_para, "Heading 6");
     
+    model->textnote_strong = ui_int_new(model->ctx, "note_textnote_strong");
+    model->textnote_emphasis = ui_int_new(model->ctx, "note_textnote_emphasis");
+    model->textnote_underline = ui_int_new(model->ctx, "note_textnote_underline");
+    model->textnote_code = ui_int_new(model->ctx, "note_textnote_code");
+    
     return model;
 }
 
@@ -114,3 +127,34 @@ void note_save(UiObject *obj, NotebookModel *notebook, Note *note) {
         note_store_save_note_async(obj, note, NULL, NULL);
     }
 }
+
+void note_update_current_style(NoteModel *note, MDActiveStyles *style) {
+    ui_list_setselection(note->textnote_para, style->paragraph_index);
+    
+    ui_set(note->textnote_strong, style->strong);
+    ui_set(note->textnote_emphasis, style->emphasis);
+    //ui_set(note->textnote_underline = style->underline);
+    ui_set(note->textnote_code, style->code);
+}
+
+void note_text_style_set_strong(NoteModel *note, UiBool enabled) {
+    if(!editor_set_style(note->text, EDITOR_STYLE_STRONG, enabled)) {
+        ui_set(note->textnote_strong, !enabled);
+    }
+}
+
+void note_text_style_set_emphasis(NoteModel *note, UiBool enabled) {
+    if(!editor_set_style(note->text, EDITOR_STYLE_EMPHASIS, enabled)) {
+        ui_set(note->textnote_emphasis, !enabled);
+    }
+}
+
+void note_text_style_set_underline(NoteModel *note, UiBool enabled) {
+    
+}
+
+void note_text_style_set_code(NoteModel *note, UiBool enabled) {
+    if(!editor_set_style(note->text, EDITOR_STYLE_CODE, enabled)) {
+        ui_set(note->textnote_code, !enabled);
+    }
+}
index 30a003bbd4e564aa207a82577a0e205955ffde05..8e2bce86166c27a6bd066629e7bb35d8b2ab4661 100644 (file)
 #define NOTE_H
 
 #include "application.h"
+#include "editor.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+NoteModel *notemodel_current(UiObject *obj);
+    
 NoteModel* notemodel_create(const CxAllocator *note_allocator);
 void notemodel_set_note(NoteModel *model, Note *note);
 
@@ -43,6 +46,13 @@ void note_load_content(UiObject *obj, NotebookModel *notebook, Note *note);
 
 void note_save(UiObject *obj, NotebookModel *notebook, Note *note);
 
+void note_update_current_style(NoteModel *note, MDActiveStyles *style);
+
+void note_text_style_set_strong(NoteModel *note, UiBool enabled);
+void note_text_style_set_emphasis(NoteModel *note, UiBool enabled);
+void note_text_style_set_underline(NoteModel *note, UiBool enabled);
+void note_text_style_set_code(NoteModel *note, UiBool enabled);
+
 
 #ifdef __cplusplus
 }
index fec346bed57e6fa36ce8772a7778084f6f4454b1..6a4d0e305a1737696c11de3cb6594b188b5fa448 100644 (file)
@@ -31,6 +31,7 @@
 #include "store.h"
 #include "notebook.h"
 #include "editor.h"
+#include "note.h"
 
 #include <cx/array_list.h>
 #include <cx/hash_map.h>
@@ -75,9 +76,10 @@ void window_create() {
                     }
                     ui_hbox(obj, .style_class = "note_toolbar", .margin = 10, .spacing = 4, .fill = UI_OFF) {
                         ui_combobox(obj, .varname = "note_textnote_para");
-                        ui_button(obj, .icon = "format-text-bold");
-                        ui_button(obj, .icon = "format-text-italic");
-                        ui_button(obj, .icon = "format-text-underline");
+                        ui_togglebutton(obj, .icon = "format-text-bold", .varname = "note_textnote_strong", .onchange = action_textnote_style_strong);
+                        ui_togglebutton(obj, .icon = "format-text-italic", .varname = "note_textnote_emphasis", .onchange = action_textnote_style_emphasis);
+                        ui_togglebutton(obj, .icon = "format-text-underline", .varname = "note_textnote_underline", .onchange = action_textnote_style_underline);
+                        ui_togglebutton(obj, .label = "code", .varname = "note_textnote_code", .onchange = action_textnote_style_code);
                         ui_button(obj, .icon = "view-list-bullet");
                         ui_button(obj, .icon = "view-list-ordered");
                         ui_button(obj, .icon = "insert-image");
@@ -297,3 +299,47 @@ void action_note_activated(UiEvent *event, void *userdata) {
         window_notelist_setvisible(window, TRUE);
     }
 }
+
+void action_textnote_style_strong(UiEvent *event, void *userdata) {
+    if(event->set) {
+        return; // only handle user interactions, not events triggered by ui_set
+    }
+    MainWindow *window = event->window;
+    NotebookModel *notebook = window->current_notebook;
+    if(notebook && notebook->current_note && notebook->current_note->model) {
+        note_text_style_set_strong(notebook->current_note->model, event->intval);
+    }
+}
+
+void action_textnote_style_emphasis(UiEvent *event, void *userdata) {
+    if(event->set) {
+        return;
+    }
+    MainWindow *window = event->window;
+    NotebookModel *notebook = window->current_notebook;
+    if(notebook && notebook->current_note && notebook->current_note->model) {
+        note_text_style_set_emphasis(notebook->current_note->model, event->intval);
+    }
+}
+
+void action_textnote_style_underline(UiEvent *event, void *userdata) {
+    if(event->set) {
+        return;
+    }
+    MainWindow *window = event->window;
+    NotebookModel *notebook = window->current_notebook;
+    if(notebook && notebook->current_note && notebook->current_note->model) {
+        note_text_style_set_underline(notebook->current_note->model, event->intval);
+    }
+}
+
+void action_textnote_style_code(UiEvent *event, void *userdata) {
+    if(event->set) {
+        return;
+    }
+    MainWindow *window = event->window;
+    NotebookModel *notebook = window->current_notebook;
+    if(notebook && notebook->current_note && notebook->current_note->model) {
+        note_text_style_set_code(notebook->current_note->model, event->intval);
+    }
+}
index 2f8f42f52cee5cad7f0baf884037e28ad25000c3..ed470acbc5fb6551f66671455cbe5a38499c45d6 100644 (file)
@@ -56,7 +56,10 @@ void action_notebook_selected(UiEvent *event, void *userdata);
 void action_note_selected(UiEvent *event, void *userdata);
 void action_note_activated(UiEvent *event, void *userdata);
 
-
+void action_textnote_style_strong(UiEvent *event, void *userdata);
+void action_textnote_style_emphasis(UiEvent *event, void *userdata);
+void action_textnote_style_underline(UiEvent *event, void *userdata);
+void action_textnote_style_code(UiEvent *event, void *userdata);
 
 
 
index fe12110801e44708f89e91c636ce1da3f8085dbc..1fdb1b9d13c98bcac663968540d4c69e9b781a5b 100644 (file)
@@ -306,7 +306,9 @@ UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, char *name) {
 void ui_int_set(UiInteger* i, int64_t value) {
     if (i) {
         if (i->set) {
+            ui_setop_enable(TRUE);
             i->set(i, value);
+            ui_setop_enable(FALSE);
         } else {
             i->value = value;
         }
@@ -324,7 +326,9 @@ int64_t ui_int_get(UiInteger* i) {
 void ui_double_set(UiDouble* d, double value) {
     if (d) {
         if (d->set) {
+            ui_setop_enable(TRUE);
             d->set(d, value);
+            ui_setop_enable(FALSE);
         } else {
             d->value = value;
         }
@@ -343,7 +347,9 @@ double ui_double_get(UiDouble* d) {
 void ui_string_set(UiString* s, const char* value) {
     if (s) {
         if (s->set) {
+            ui_setop_enable(TRUE);
             s->set(s, value);
+            ui_setop_enable(FALSE);
         } else {
             if(s->value.free) {
                 s->value.free(s->value.ptr);
@@ -371,7 +377,9 @@ char* ui_string_get(UiString* s) {
 void ui_text_set(UiText* s, const char* value) {
     if (s) {
         if (s->set) {
+            ui_setop_enable(TRUE);
             s->set(s, value);
+            ui_setop_enable(FALSE);
         } else {
             if(s->value.free) {
                 s->value.free(s->value.ptr);
@@ -616,3 +624,15 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser
     destr->observer = observer;
     cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor);
 }
+
+
+static int ui_set_op = 0;
+
+void ui_setop_enable(int set) {
+    ui_set_op = set;
+}
+
+int ui_get_setop(void) {
+    return ui_set_op;
+}
+
index 5123e2736c98d6658e34441d67594dde297a2f51..b26a13056e951a3e8c88a13f9546ee396b612fa1 100644 (file)
@@ -114,6 +114,7 @@ void ui_button_clicked(GtkWidget *widget, UiEventData *event) {
     e.document = event->obj->ctx->document;
     e.eventdata = NULL;
     e.intval = event->value;
+    e.set = ui_get_setop();
     event->callback(&e, event->userdata);
 }
 
@@ -136,7 +137,8 @@ void ui_toggled_obs(void *widget, UiVarEventData *event) {
     e.window = event->obj->window;
     e.document = event->obj->ctx->document;
     e.eventdata = event->var->value;
-    e.intval = i->get(i);  
+    e.intval = i->get(i);
+    e.set = ui_get_setop();
     
     ui_notify_evt(i->observers, &e);
 }
@@ -148,6 +150,7 @@ static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) {
     e.document = event->obj->ctx->document;
     e.eventdata = NULL;
     e.intval = gtk_toggle_button_get_active(widget);
+    e.set = ui_get_setop();
     event->callback(&e, event->userdata);    
 }
 
@@ -275,7 +278,7 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr
     UiObject* current = uic_current_obj(obj);
     
     ui_setup_togglebutton(
-            current,
+            obj,
             widget,
             args.label,
             args.icon,
@@ -318,6 +321,7 @@ static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) {
     e.document = event->obj->ctx->document;
     e.eventdata = NULL;
     e.intval = gtk_check_button_get_active(widget);
+    e.set = ui_get_setop();
     event->callback(&e, event->userdata);    
 }
 
@@ -388,6 +392,7 @@ static void radiobutton_toggled(void *widget, UiEventData *event) {
     e.document = event->obj->ctx->document;
     e.eventdata = NULL;
     e.intval = RADIOBUTTON_GET_ACTIVE(widget);
+    e.set = ui_get_setop();
     event->callback(&e, event->userdata);    
 }
 
index f3d3c62cb6928649c7c81bdda4bafbb5facd4fd7..732473ca8dbb8d900e8fd5e22dca515d10a97ba3 100644 (file)
@@ -288,6 +288,7 @@ struct UiEvent {
     void     *window;
     void     *eventdata;
     int      intval;
+    int      set;
 };
 
 struct UiMouseEvent {
@@ -594,6 +595,9 @@ UIEXPORT void ui_condvar_wait(UiCondVar *var);
 UIEXPORT void ui_condvar_signal(UiCondVar *var, void *data, int intdata);
 UIEXPORT void ui_condvar_destroy(UiCondVar *var);
 
+UIEXPORT void ui_setop_enable(int set);
+UIEXPORT int ui_get_setop(void);
+
 #ifdef __cplusplus
 }
 #endif