#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(
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));
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);
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,
}
}
+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) {
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) {
}
+
+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);
}
}
-
+/*
+ * 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) {
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);
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) {
}
}
+/*
+ * 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);
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;
+}
#include "store.h"
#include "notebook.h"
#include "editor.h"
+#include "note.h"
#include <cx/array_list.h>
#include <cx/hash_map.h>
}
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");
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);
+ }
+}