URI: 
       tImplement pasting (well, sort of) - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 271d268ba17415be9808bb1da36389036b4eb95d
   DIR parent e5841801447358ed8f2f57e3dc0adc2873fb500e
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Mon,  1 Nov 2021 13:17:15 +0100
       
       Implement pasting (well, sort of)
       
       Diffstat:
         M keys_basic.c                        |     121 ++++++++++++++++++++++++-------
         M keys_basic_config.h                 |       2 ++
       
       2 files changed, 97 insertions(+), 26 deletions(-)
       ---
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -25,7 +25,8 @@
        #include "keys_command.h"
        #include "keys_basic_config.h"
        
       -/* this is supposed to be global for all buffers */
       +/* note: this is supposed to be global for all buffers */
       +int paste_buffer_line_based = 0;
        static txtbuf *paste_buffer = NULL;
        static int last_lines_scrolled = -1;
        
       t@@ -91,7 +92,7 @@ static struct key_stack_elem *peek_key_stack(void);
        static struct key_stack_elem *pop_key_stack(void);
        void clear_key_stack(void);
        
       -static void move_cursor_left_right(ledit_buffer *buffer, int dir);
       +static void move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index);
        static void move_cursor_up_down(ledit_buffer *buffer, int dir);
        static void push_num(int num);
        static void key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
       t@@ -99,6 +100,7 @@ static void get_new_line_softline(
            ledit_buffer *buffer, int cur_line, int cur_index,
            int movement, int *new_line_ret, int *new_softline_ret
        );
       +static void move_cursor_logically(ledit_buffer *buffer, int movement_dir, int allow_illegal_index);
        
        /* FIXME: move to common */
        static void
       t@@ -304,7 +306,7 @@ delete_range(
        }
        
        static void
       -insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, int start_group) {
       +insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, int new_line, int new_index, int start_group) {
                if (len < 0)
                        len = strlen(text);
                /* FIXME: this is kind of hacky... */
       t@@ -314,14 +316,22 @@ insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, int 
                cur_range.byte1 = buffer->cur_index;
                del_range.line1 = line;
                del_range.byte1 = index;
       +        int cur_line, cur_index;
                ledit_buffer_insert_text_with_newlines(
                    buffer, line, index, text, len,
       -            &buffer->cur_line, &buffer->cur_index
       +            &cur_line, &cur_index
                );
       -        cur_range.line2 = buffer->cur_line;
       -        cur_range.byte2 = buffer->cur_index;
       -        del_range.line2 = buffer->cur_line;
       -        del_range.byte2 = buffer->cur_index;
       +        /* this is mainly for pasting, where the new line and index
       +           should not be at the end of the pasted text */
       +        if (new_line >= 0 && new_index >= 0) {
       +                cur_range.line2 = buffer->cur_line = new_line;
       +                cur_range.byte2 = buffer->cur_index = new_index;
       +        } else {
       +                cur_range.line2 = buffer->cur_line = cur_line;
       +                cur_range.byte2 = buffer->cur_index = cur_index;
       +        }
       +        del_range.line2 = cur_line;
       +        del_range.byte2 = cur_index;
                ledit_push_undo_insert(
                    buffer->undo, &ins_buf, del_range, cur_range, start_group, buffer->common->mode
                );
       t@@ -335,6 +345,7 @@ delete_selection(ledit_buffer *buffer) {
                            buffer->sel.line1, buffer->sel.byte1,
                            buffer->sel.line2, buffer->sel.byte2
                        );
       +                paste_buffer_line_based = 0;
                        /* FIXME: maybe just set this to the current cursor pos? */
                        buffer->sel.line1 = buffer->sel.line2 = -1;
                        buffer->sel.byte1 = buffer->sel.byte2 = -1;
       t@@ -629,8 +640,53 @@ key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
                    buffer->cur_line, buffer->cur_index,
                    line, char_pos
                );
       +        paste_buffer_line_based = line_based;
       +        ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +        finalize_repetition_stack();
       +}
       +
       +/* FIXME: don't use the pango functions directly so normalize_and_set_pango_text is
       +   always called properly */
       +static struct action
       +paste_normal(ledit_buffer *buffer, char *text, int len) {
       +        (void)text;
       +        (void)len;
       +        if (!paste_buffer) {
       +                ledit_window_show_message(buffer->window, "Nothing to paste", -1);
       +                discard_repetition_stack();
       +                return (struct action){ACTION_NONE, NULL};
       +        }
       +        if (paste_buffer_line_based) {
       +                int x, softline;
       +                ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       +                ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
       +                pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &softline, &x);
       +                PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline);
       +                insert_text(
       +                    buffer, buffer->cur_line, sl->start_index + sl->length,
       +                    "\n", -1, buffer->cur_line, buffer->cur_index, 1
       +                );
       +                /* remove trailing newline if it exists - this may be hacky */
       +                int text_len = paste_buffer->len;
       +                if (paste_buffer->text[paste_buffer->len-1] == '\n') {
       +                        text_len--;
       +                        paste_buffer->text[paste_buffer->len-1] = '\0';
       +                }
       +                insert_text(
       +                    buffer, buffer->cur_line + 1, 0,
       +                    paste_buffer->text, text_len, buffer->cur_line + 1, 0, 0
       +                );
       +        } else {
       +                /* must allow illegal index so text can be pasted at end of line */
       +                move_cursor_logically(buffer, 1, 1);
       +                insert_text(
       +                    buffer, buffer->cur_line, buffer->cur_index,
       +                    paste_buffer->text, paste_buffer->len, buffer->cur_line, buffer->cur_index, 1
       +                );
       +        }
                ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
                finalize_repetition_stack();
       +        return (struct action){ACTION_NONE, NULL};
        }
        
        static struct action
       t@@ -794,7 +850,7 @@ delete_key(ledit_buffer *buffer, char *text, int len) {
        }
        
        static void
       -move_cursor_left_right(ledit_buffer *buffer, int dir) {
       +move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
                int num = 1;
                struct key_stack_elem *e = pop_key_stack();
                if (e != NULL) {
       t@@ -839,7 +895,9 @@ move_cursor_left_right(ledit_buffer *buffer, int dir) {
                /* when in normal mode, the cursor cannot be at the very end
                   of the line because it's always covering a character */
                if (new_index >= cur_line->len) {
       -                if (buffer->common->mode == NORMAL && (e == NULL || e->motion_cb == NULL)) {
       +                if (!allow_illegal_index &&
       +                    buffer->common->mode == NORMAL &&
       +                    (e == NULL || e->motion_cb == NULL)) {
                                new_index = last_index;
                        } else {
                                /* FIXME: I guess this is unnecessary */
       t@@ -868,7 +926,7 @@ static struct action
        cursor_left(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        move_cursor_left_right(buffer, -1);
       +        move_cursor_left_right(buffer, -1, 0);
                return (struct action){ACTION_NONE, NULL};
        }
        
       t@@ -876,7 +934,7 @@ static struct action
        cursor_right(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        move_cursor_left_right(buffer, 1);
       +        move_cursor_left_right(buffer, 1, 0);
                return (struct action){ACTION_NONE, NULL};
        }
        
       t@@ -887,7 +945,7 @@ return_key(ledit_buffer *buffer, char *text, int len) {
                int start_group = 1;
                if (delete_selection(buffer))
                        start_group = 0;
       -        insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, start_group);
       +        insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, -1, -1, start_group);
                /* FIXME: these aren't needed, right? This only works in insert mode
                 * anyways, so there's nothing to wipe */
                /* ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       t@@ -954,6 +1012,28 @@ pango_layout_get_direction(PangoLayout *layout, int index) {
        }
        #endif
        
       +static void
       +move_cursor_logically(ledit_buffer *buffer, int movement_dir, int allow_illegal_index) {
       +        PangoDirection dir = PANGO_DIRECTION_RTL;
       +        int tmp_index = buffer->cur_index;
       +        ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
       +        if (buffer->cur_index >= cur_line->len)
       +                tmp_index--;
       +        if (tmp_index >= 0)
       +                dir = pango_layout_get_direction(cur_line->layout, tmp_index);
       +        if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
       +                if (movement_dir < 0)
       +                        move_cursor_left_right(buffer, 1, allow_illegal_index);
       +                else
       +                        move_cursor_left_right(buffer, -1, allow_illegal_index);
       +        } else {
       +                if (movement_dir < 0)
       +                        move_cursor_left_right(buffer, -1, allow_illegal_index);
       +                else
       +                        move_cursor_left_right(buffer, 1, allow_illegal_index);
       +        }
       +}
       +
        static struct action
        escape_key(ledit_buffer *buffer, char *text, int len) {
                (void)text;
       t@@ -968,18 +1048,7 @@ escape_key(ledit_buffer *buffer, char *text, int len) {
                } else {
                        ledit_buffer_set_mode(buffer, NORMAL);
                        clear_key_stack();
       -                PangoDirection dir = PANGO_DIRECTION_RTL;
       -                int tmp_index = buffer->cur_index;
       -                ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
       -                if (buffer->cur_index >= cur_line->len)
       -                        tmp_index--;
       -                if (tmp_index >= 0)
       -                        dir = pango_layout_get_direction(cur_line->layout, tmp_index);
       -                if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
       -                        cursor_right(buffer, NULL, 0);
       -                } else {
       -                        cursor_left(buffer, NULL, 0);
       -                }
       +                move_cursor_logically(buffer, -1, 0);
                        if (buffer->sel.line1 != buffer->sel.line2) {
                                int min = buffer->sel.line1 < buffer->sel.line2 ? buffer->sel.line1 : buffer->sel.line2;
                                int max = buffer->sel.line1 > buffer->sel.line2 ? buffer->sel.line1 : buffer->sel.line2;
       t@@ -1217,7 +1286,7 @@ redo(ledit_buffer *buffer, char *text, int len) {
        static struct action
        insert_mode_insert_text(ledit_buffer *buffer, char *text, int len) {
                delete_selection(buffer);
       -        insert_text(buffer, buffer->cur_line, buffer->cur_index, text, len, 1);
       +        insert_text(buffer, buffer->cur_line, buffer->cur_index, text, len, -1, -1, 1);
                return (struct action){ACTION_NONE, NULL};
        }
        
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -64,6 +64,7 @@ static struct action scroll_with_cursor_down(ledit_buffer *buffer, char *text, i
        static struct action scroll_lines_up(ledit_buffer *buffer, char *text, int len);
        static struct action scroll_lines_down(ledit_buffer *buffer, char *text, int len);
        static struct action move_to_line(ledit_buffer *buffer, char *text, int len);
       +static struct action paste_normal(ledit_buffer *buffer, char *text, int len);
        
        /* FIXME: maybe sort these and use binary search
           -> but that would mess with the catch-all keys */
       t@@ -121,6 +122,7 @@ static struct key keys_en[] = {
                {"d",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_lines_down},
                {"u",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_lines_up},
                {"G",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &move_to_line},
       +        {"p",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &paste_normal},
                {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
        };