URI: 
       tFix various problems - 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 8c56c0d034a6e99583be2d9926946997c8fa82bb
   DIR parent b4c5e772118e3f198989732e3aee1146ef9b5217
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Wed, 22 Dec 2021 21:44:07 +0100
       
       Fix various problems
       
       * Sort of clean up key types
       * Change cursor type over text area
       * Fix cache invalidation
       * Make 'G' work with selections
       * Improve themes
       
       Diffstat:
         M keys_basic.c                        |      38 +++++++++++++++++++------------
         M keys_basic_config.h                 |     226 ++++++++++++++-----------------
         M keys_command.c                      |       2 +-
         M ledit.1                             |       2 ++
         M ledit.c                             |       1 +
         M theme.c                             |       7 +++++++
         M theme.h                             |       5 +++++
         M theme_config.h                      |       5 +++--
         M view.c                              |      11 +++++++++--
         M window.c                            |      44 ++++++++++++++++----------------
         M window.h                            |       1 +
       
       11 files changed, 174 insertions(+), 168 deletions(-)
       ---
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -147,8 +147,8 @@ push_key_stack(void) {
                        key_stack.alloc = new_alloc;
                }
                e = &key_stack.stack[key_stack.len];
       -        e->key = KEY_NONE;
       -        e->followup = KEY_NONE;
       +        e->key = KEY_INVALID;
       +        e->followup = KEY_INVALID;
                e->motion_cb = NULL;
                e->count = 0;
                key_stack.len++;
       t@@ -629,7 +629,10 @@ move_to_line(ledit_view *view, char *text, size_t len) {
                        ledit_line *ll = buffer_get_line(view->buffer, line - 1);
                        cb(view, line - 1, ll->len, KEY_MOTION_LINE);
                } else {
       -                view_wipe_line_cursor_attrs(view, view->cur_line);
       +                if (view->mode == NORMAL)
       +                        view_wipe_line_cursor_attrs(view, view->cur_line);
       +                else
       +                        view_set_selection(view, view->sel.line1, view->sel.byte1, line - 1, 0);
                        view->cur_line = line - 1;
                        view->cur_index = 0;
                        int text_w, text_h;
       t@@ -642,7 +645,8 @@ move_to_line(ledit_view *view, char *text, size_t len) {
                            vl->y_offset + h > view->display_offset + text_h) {
                                view_scroll(view, vl->y_offset - text_h / 2);
                        }
       -                view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
       +                if (view->mode == NORMAL)
       +                        view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
                        discard_repetition_stack();
                }
                return (struct action){ACTION_NONE, NULL};
       t@@ -932,7 +936,7 @@ change(ledit_view *view, char *text, size_t len) {
                                return err_invalid_key(view);
                        } else {
                                struct key_stack_elem *e = push_key_stack();
       -                        e->key = KEY_MOTION; /* ? */
       +                        e->key = KEY_MOTIONALLOWED;
                                e->count = num;
                                e->motion_cb = &change_cb;
                        }
       t@@ -1010,7 +1014,7 @@ yank(ledit_view *view, char *text, size_t len) {
                                clear_key_stack();
                        } else if (cb == NULL) {
                                struct key_stack_elem *e = push_key_stack();
       -                        e->key = KEY_MOTION;
       +                        e->key = KEY_MOTIONALLOWED;
                                e->count = num;
                                e->motion_cb = &yank_cb;
                        } else {
       t@@ -1116,7 +1120,7 @@ delete(ledit_view *view, char *text, size_t len) {
                                return err_invalid_key(view);
                        } else {
                                struct key_stack_elem *e = push_key_stack();
       -                        e->key = KEY_MOTION; /* ? */
       +                        e->key = KEY_MOTIONALLOWED;
                                e->count = num;
                                e->motion_cb = &delete_cb;
                        }
       t@@ -2231,7 +2235,7 @@ toggle_hard_line_based(ledit_view *view, char *text, size_t len) {
        }
        
        static struct action
       -handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned int key_state, int lang_index, int *found) {
       +handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned int key_state, int lang_index, int *found, enum key_type *type) {
                struct key *cur_keys = keys[lang_index].keys;
                int num_keys = keys[lang_index].num_keys;
                struct key_stack_elem *e = peek_key_stack();
       t@@ -2249,18 +2253,21 @@ handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned in
                        if (cur_keys[i].text) {
                                if (len > 0 &&
                                    (cur_keys[i].modes & view->mode) &&
       -                            (!e || (e->key & cur_keys[i].prev_keys)) &&
       +                            (cur_keys[i].prev_keys == KEY_ANY || (!e && (cur_keys[i].prev_keys & KEY_NONE)) || (e && (e->key & cur_keys[i].prev_keys))) &&
                                     ((!strncmp(cur_keys[i].text, key_text, len) &&
                                       match_key(cur_keys[i].mods, key_state & ~ShiftMask)) ||
                                      cur_keys[i].text[0] == '\0')) {
                                        /* FIXME: seems a bit hacky to remove shift, but it
                                           is needed to make keys that use shift match */
       +                                *type = cur_keys[i].type;
                                        *found = 1;
                                        return cur_keys[i].func(view, key_text, len);
                                }
                        } else if ((cur_keys[i].modes & view->mode) &&
       -                            cur_keys[i].keysym == sym &&
       -                            match_key(cur_keys[i].mods, key_state)) {
       +                           cur_keys[i].keysym == sym &&
       +                           (cur_keys[i].prev_keys == KEY_ANY || (!e && (cur_keys[i].prev_keys & KEY_NONE)) || (e && (e->key & cur_keys[i].prev_keys))) &&
       +                           match_key(cur_keys[i].mods, key_state)) {
       +                        *type = cur_keys[i].type;
                                *found = 1;
                                return cur_keys[i].func(view, key_text, len);
                        }
       t@@ -2284,12 +2291,13 @@ repeat_command(ledit_view *view, char *text, size_t len) {
                        return (struct action){ACTION_NONE, NULL};
                }
                int found;
       +        enum key_type type;
                repetition_stack.replaying = 1;
                for (int i = 0; i < num; i++) {
                        unwind_repetition_stack();
                        struct repetition_stack_elem *e = get_cur_repetition_stack_elem();
                        while (e) {
       -                        (void)handle_key(view, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found);
       +                        (void)handle_key(view, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found, &type);
                                advance_repetition_stack();
                                e = get_cur_repetition_stack_elem();
                        }
       t@@ -2322,14 +2330,14 @@ basic_key_handler(ledit_view *view, XEvent *event, int lang_index) {
                int found = 0;
                int msg_shown = view->window->message_shown;
                view->window->message_shown = 0; /* FIXME: this is hacky */
       -        struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found);
       +        enum key_type type;
       +        struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found, &type);
                if (found && n > 0 && !view->window->message_shown)
                        window_hide_message(view->window);
                else if (msg_shown)
                        view->window->message_shown = msg_shown;
        
       -        /* FIXME: add attribute for this to keys - this doesn't take e.g. cursor keys into account! */
       -        if (found && n > 0)
       +        if (found && (type & KEY_ENSURE_CURSOR_SHOWN))
                        view_ensure_cursor_shown(view);
                if (!found && n > 0)
                        window_show_message(view->window, "Invalid key", -1);
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -2,17 +2,21 @@
         * These are all the regular keys used in normal, visual, and insert mode.
         */
        
       -/* FIXME: these aren't really used properly */
       +/*
       + * Note: The key types are currently very inconsistent and don't always make
       + * sense. This will hopefully be fixed sometime. (FIXME)
       + */
       +
        enum key_type {
       -        KEY_NONE = 0,
       -        KEY_MISC = 1,
       -        KEY_CHAR = 2,
       +        KEY_INVALID = 0,
       +        KEY_NONE = 2,
                KEY_MOTION_CHAR = 4,
                KEY_MOTION_LINE = 8,
                KEY_MOTION = 4|8,
       -        KEY_NUMBER = 16,
       -        KEY_NUMBERALLOWED = 32,
       -        KEY_ACTIONCALLBACK = 64,
       +        KEY_MOTIONALLOWED = 16,
       +        KEY_NUMBER = 32,
       +        KEY_NUMBERALLOWED = 64,
       +        KEY_ENSURE_CURSOR_SHOWN = 128, /* jump to cursor if it is off screen */
                KEY_ANY = 0xFF
        };
        
       t@@ -20,9 +24,9 @@ struct key {
                char *text;                                          /* for keys that correspond with text */
                unsigned int mods;                                   /* modifier mask */
                KeySym keysym;                                       /* for other keys, e.g. arrow keys */
       -        ledit_mode modes;                               /* modes in which this keybinding is functional */
       +        ledit_mode modes;                                    /* modes in which this keybinding is functional */
                enum key_type prev_keys;                             /* allowed previous keys */
       -        enum key_type key_types;                             /* key types - used to determine if the key is allowed */
       +        enum key_type type;                                  /* type of key - mainly used for ensure_cursor_shown */
                struct action (*func)(ledit_view *, char *, size_t); /* callback function */
        };
        
       t@@ -102,133 +106,103 @@ static struct action toggle_hard_line_based(ledit_view *view, char *text, size_t
        
        /* FIXME: maybe sort these and use binary search
           -> but that would mess with the catch-all keys */
       -/* FIXME: sort out the key types */
        static struct key keys_en[] = {
       -        {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace},
       -        {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left},
       -        {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right},
       -        {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up},
       -        {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down},
       -        {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key},
       -        {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key},
       -        {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ANY, &escape_key},
       -        {"i",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_insert},
       -        {"h",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       -        {"l",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       -        {"j",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       -        {"k",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       -        {"h",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       -        {"t",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &toggle_hard_line_based},
       -        {NULL,  0, XK_space, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       -        {"j",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       -        {"n",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       -        {"p",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       -        {"0",  0, 0, NORMAL|VISUAL, ~KEY_NUMBER, KEY_ANY, &cursor_to_beginning},
       -        {"0",  0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_NUMBER, &push_0},
       -        {"1",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_1},
       -        {"2",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_2},
       -        {"3",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_3},
       -        {"4",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_4},
       -        {"5",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_5},
       -        {"6",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_6},
       -        {"7",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_7},
       -        {"8",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_8},
       -        {"9",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_9},
       -        {"x",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &delete_chars_forwards},
       -        {"X",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &delete_chars_backwards},
       -        {"d",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &delete},
       -        {"y",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &yank},
       -        {"Y",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &yank_lines},
       -        {"c",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &change},
       -        {"v",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual},
       -        {"o",  0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end},
       -        {"c",  ControlMask, 0, VISUAL, KEY_ANY, KEY_ANY, &clipcopy},
       -        {"v",  ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste},
       -        {"g",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &show_line},
       -        {":",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_commandedit},
       -        {"?",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_searchedit_backward},
       -        {"/",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_searchedit_forward},
       -        {"n",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &key_search_next},
       -        {"N",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &key_search_prev},
       -        {"u",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &undo},
       -        {"U",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &redo},
       -        {".",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &repeat_command}, /* FIXME: only allow after finished key sequence */
       -        {"z",  ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &undo},
       -        {"y",  ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &redo}, /* FIXME: this is confusing with ctrl+y in normal mode */
       -        {"b",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &screen_up},
       -        {"f",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &screen_down},
       -        {"e",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_with_cursor_down},
       -        {"y",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_with_cursor_up},
       -        {"d",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_lines_down},
       -        {"u",  ControlMask, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &scroll_lines_up},
       -        {"$",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &move_to_eol},
       -        {"w",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &next_word},
       -        {"e",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &next_word_end},
       -        {"W",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &next_bigword},
       -        {"E",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &next_bigword_end},
       -        {"b",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &prev_word},
       -        {"B",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &prev_bigword},
       -        {"G",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &move_to_line},
       -        {"J",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &join_lines},
       -        {"I",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &insert_at_beginning},
       -        {"p",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &paste_normal},
       -        {"P",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &paste_normal_backwards},
       -        {"A",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_after_eol},
       -        {"a",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_after_cursor},
       -        {"O",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_line_above},
       -        {"o",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_line_below},
       -        {"m",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &mark_line},
       -        {"'",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &jump_to_mark},
       -        {"C",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &change_to_eol},
       -        {"D",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &delete_to_eol},
       -        {"r",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &replace},
       -        {"^",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_first_non_ws},
       -        {"t",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_forwards},
       -        {"T",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_backwards},
       -        {"f",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_char_forwards},
       -        {"F",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_char_backwards},
       -        {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
       +        {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &backspace},
       +        {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
       +        {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
       +        {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
       +        {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
       +        {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &return_key},
       +        {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete_key},
       +        {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &escape_key},
       +        {"i",  0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_insert},
       +        {"h",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
       +        {"l",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
       +        {"j",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
       +        {"k",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
       +        {"h",  ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
       +        {"t",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_line_based},
       +        {NULL,  0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
       +        {"j",  ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
       +        {"n",  ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
       +        {"p",  ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
       +        {"0",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_beginning},
       +        {"0",  0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push_0},
       +        {"1",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_1},
       +        {"2",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_2},
       +        {"3",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_3},
       +        {"4",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_4},
       +        {"5",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_5},
       +        {"6",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_6},
       +        {"7",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_7},
       +        {"8",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_8},
       +        {"9",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_9},
       +        {"x",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_forwards},
       +        {"X",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_backwards},
       +        {"d",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &delete},
       +        {"y",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &yank},
       +        {"Y",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &yank_lines},
       +        {"c",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &change},
       +        {"v",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual},
       +        {"o",  0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selection_end},
       +        {"c",  ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clipcopy},
       +        {"v",  ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clippaste},
       +        {"g",  ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &show_line},
       +        {":",  0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &enter_commandedit},
       +        {"?",  0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_backward},
       +        {"/",  0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_forward},
       +        {"n",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_next},
       +        {"N",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_prev},
       +        {"u",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &undo},
       +        {"U",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &redo},
       +        {".",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &repeat_command}, /* FIXME: only allow after finished key sequence */
       +        {"z",  ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo},
       +        {"y",  ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo}, /* FIXME: this is confusing with ctrl+y in normal mode */
       +        {"b",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_up},
       +        {"f",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_down},
       +        {"e",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_down},
       +        {"y",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_up},
       +        {"d",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_down},
       +        {"u",  ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_up},
       +        {"$",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_eol},
       +        {"w",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word},
       +        {"e",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word_end},
       +        {"W",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword},
       +        {"E",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword_end},
       +        {"b",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_word},
       +        {"B",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_bigword},
       +        {"G",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_line},
       +        {"J",  0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &join_lines},
       +        {"I",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beginning},
       +        {"p",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal},
       +        {"P",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_backwards},
       +        {"A",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_eol},
       +        {"a",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_cursor},
       +        {"O",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_above},
       +        {"o",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_below},
       +        {"m",  0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line},
       +        {"'",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &jump_to_mark},
       +        {"C",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eol},
       +        {"D",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eol},
       +        {"r",  0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace},
       +        {"^",  0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_first_non_ws},
       +        {"t",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_forwards},
       +        {"T",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_backwards},
       +        {"f",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_forwards},
       +        {"F",  0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_backwards},
       +        {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
        };
        
        static struct key keys_de[] = {
       -        {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
       +        {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
        };
        
        static struct key keys_ur[] = {
       -        {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace},
       -        {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left},
       -        {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right},
       -        {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up},
       -        {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down},
       -        {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key},
       -        {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key},
       -        {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key},
       -        {"ی",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert},
       -        {"ح",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       -        {"ل",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       -        {"ج",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       -        {"ک",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       -        {"0",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning},
       -        {"چ",  ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy},
       -        {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
       +        {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
        };
        
        static struct key keys_hi[] = {
       -        {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace},
       -        {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left},
       -        {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right},
       -        {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up},
       -        {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down},
       -        {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key},
       -        {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key},
       -        {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key},
       -        {"ि",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert},
       -        {"ह",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_left},
       -        {"ल",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_right},
       -        {"ज",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_down},
       -        {"क",  0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor_up},
       -        {"0",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning},
       -        {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text}
       +        {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
        };
        
        GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_ur, keys_hi);
   DIR diff --git a/keys_command.c b/keys_command.c
       t@@ -307,7 +307,7 @@ substitute_all_remaining(ledit_view *view) {
                }
                if (min_line < view->lines_num)
                        buffer_recalc_all_views_from_line(view->buffer, min_line);
       -        window_show_message_fmt(view->window, "Replaced %d occurrences", sub_state.num);
       +        window_show_message_fmt(view->window, "Replaced %d occurrence(s)", sub_state.num);
                /* this doesn't need to be added to the undo stack since it's called on undo/redo anyways */
                view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index);
                view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
   DIR diff --git a/ledit.1 b/ledit.1
       t@@ -1,6 +1,8 @@
        .\" TODO:
        .\" paste buffer line vs. char oriented
        .\" bigword, etc.
       +.\" commands - no good way for mapping found
       +.\" difference between unicode char and grapheme
        .\"
        .\" WARNING: Some parts of this are stolen shamelessly from OpenBSD's
        .\" vi(1) manpage!
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -91,6 +91,7 @@ mainloop(void) {
                                        change_kbd = 1;
                                        continue;
                                }
       +                        /* FIXME: why None? */
                                if (XFilterEvent(&event, None))
                                        continue;
                                ledit_view *view = NULL;
   DIR diff --git a/theme.c b/theme.c
       t@@ -13,13 +13,18 @@ theme_create(ledit_common *common) {
                ledit_theme *theme = ledit_malloc(sizeof(ledit_theme));
                theme->scrollbar_width = SCROLLBAR_WIDTH;
                theme->scrollbar_step = SCROLLBAR_STEP;
       +        theme->text_font = TEXT_FONT;
                theme->text_size = TEXT_SIZE;
                theme->text_fg_hex = TEXT_FG;
                theme->text_bg_hex = TEXT_BG;
       +        theme->bar_fg_hex = BAR_FG;
       +        theme->bar_bg_hex = BAR_BG;
                theme->scrollbar_fg_hex = SCROLLBAR_FG;
                theme->scrollbar_bg_hex = SCROLLBAR_BG;
                XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_FG, &theme->text_fg);
                XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_BG, &theme->text_bg);
       +        XftColorAllocName(common->dpy, common->vis, common->cm, BAR_FG, &theme->bar_fg);
       +        XftColorAllocName(common->dpy, common->vis, common->cm, BAR_BG, &theme->bar_bg);
                XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_FG, &theme->scrollbar_fg);
                XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_BG, &theme->scrollbar_bg);
                return theme;
       t@@ -29,6 +34,8 @@ void
        theme_destroy(ledit_common *common, ledit_theme *theme) {
                XftColorFree(common->dpy, common->vis, common->cm, &theme->text_fg);
                XftColorFree(common->dpy, common->vis, common->cm, &theme->text_bg);
       +        XftColorFree(common->dpy, common->vis, common->cm, &theme->bar_fg);
       +        XftColorFree(common->dpy, common->vis, common->cm, &theme->bar_bg);
                XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_fg);
                XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_bg);
                free(theme);
   DIR diff --git a/theme.h b/theme.h
       t@@ -10,10 +10,15 @@ typedef struct {
                int text_size;
                XftColor text_fg;
                XftColor text_bg;
       +        XftColor bar_fg;
       +        XftColor bar_bg;
                XftColor scrollbar_fg;
                XftColor scrollbar_bg;
       +        const char *text_font;
                const char *text_fg_hex;
                const char *text_bg_hex;
       +        const char *bar_fg_hex;
       +        const char *bar_bg_hex;
                const char *scrollbar_fg_hex;
                const char *scrollbar_bg_hex;
        } ledit_theme;
   DIR diff --git a/theme_config.h b/theme_config.h
       t@@ -1,8 +1,9 @@
       -/* FIXME: different bg for bottom bar */
       -/* FIXME: configure font here */
       +static const char *TEXT_FONT = "Monospace";
        static const int TEXT_SIZE = 12;
        static const char *TEXT_FG = "#000000";
        static const char *TEXT_BG = "#FFFFFF";
       +static const char *BAR_FG = "#000000";
       +static const char *BAR_BG = "#CCCCCC";
        
        /* FIXME: give in units other than pixels */
        static const int SCROLLBAR_WIDTH = 10;
   DIR diff --git a/view.c b/view.c
       t@@ -229,6 +229,10 @@ view_notify_delete_text(ledit_view *view, size_t line, size_t index, size_t len)
        
        void
        view_notify_append_line(ledit_view *view, size_t line) {
       +        cache_invalidate_from_line(
       +            view->cache, line + 1, view,
       +            &invalidate_pixmap_line_helper, &invalidate_layout_line_helper
       +        );
                resize_and_move_line_gap(view, add_sz(view->lines_num, 1), line + 1);
                if (line < view->cur_line)
                        view->cur_line++;
       t@@ -1756,11 +1760,12 @@ static void
        view_button_handler(void *data, XEvent *event) {
                size_t l, b;
                ledit_view *view= (ledit_view *)data;
       -        int x = event->xbutton.x;
       -        int y = event->xbutton.y;
       +        int x, y;
                int snap;
                switch (event->type) {
                case ButtonPress:
       +                x = event->xbutton.x;
       +                y = event->xbutton.y;
                        snap = view->mode == NORMAL ? 0 : 1;
                        view_xy_to_line_byte(view, x, y, snap,  &l, &b);
                        view->selecting = 1;
       t@@ -1780,6 +1785,8 @@ view_button_handler(void *data, XEvent *event) {
                        view->selecting = 0;
                        break;
                case MotionNotify:
       +                x = event->xmotion.x;
       +                y = event->xmotion.y;
                        if (view->selecting) {
                                y = y >= 0 ? y : 0;
                                view_xy_to_line_byte(view, x, y, 1, &l, &b);
   DIR diff --git a/window.c b/window.c
       t@@ -8,6 +8,7 @@
        #include <X11/Xlib.h>
        #include <X11/Xatom.h>
        #include <X11/Xutil.h>
       +#include <X11/cursorfont.h>
        #include <pango/pangoxft.h>
        #include <X11/extensions/Xdbe.h>
        
       t@@ -122,8 +123,8 @@ redraw_line_text(ledit_window *window) {
                pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
                pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
                draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
       -        XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->line_w, window->bb->line_h);
       -        pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->text_fg, window->bb->line, 0, 0);
       +        XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->bar_bg, 0, 0, window->bb->line_w, window->bb->line_h);
       +        pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->bar_fg, window->bb->line, 0, 0);
                recalc_text_size(window);
                window->redraw = 1;
        }
       t@@ -231,15 +232,8 @@ window_delete_bottom_bar_char(ledit_window *window, int dir) {
                        );
                        window->bb->line_len -= (byte - window->bb->line_cur_pos);
                }
       -        /* FIXME: move to separate function */
                window->bb->line_text[window->bb->line_len] = '\0';
       -        pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
       -        pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
       -        draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
       -        XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->line_w, window->bb->line_h);
       -        pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->text_fg, window->bb->line, 0, 0);
       -        recalc_text_size(window);
       -        window->redraw = 1;
       +        redraw_line_text(window);
        }
        
        void
       t@@ -358,8 +352,8 @@ window_set_mode(ledit_window *window, ledit_mode mode) {
                free(final_text);
                pango_layout_get_pixel_size(window->bb->mode, &window->bb->mode_w, &window->bb->mode_h);
                draw_grow(window, window->bb->mode_draw, window->bb->mode_w, window->bb->mode_h);
       -        XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->text_bg, 0, 0, window->bb->mode_w, window->bb->mode_h);
       -        pango_xft_render_layout(window->bb->mode_draw->xftdraw, &window->theme->text_fg, window->bb->mode, 0, 0);
       +        XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->bar_bg, 0, 0, window->bb->mode_w, window->bb->mode_h);
       +        pango_xft_render_layout(window->bb->mode_draw->xftdraw, &window->theme->bar_fg, window->bb->mode, 0, 0);
                recalc_text_size(window);
                window->redraw = 1;
        }
       t@@ -545,7 +539,7 @@ window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
                /* FIXME: FocusChangeMask? */
                window->wattrs.event_mask = KeyPressMask |
                    ExposureMask | VisibilityChangeMask | StructureNotifyMask |
       -            ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
       +            PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
                window->xwin = XCreateWindow(
                    common->dpy, DefaultRootWindow(common->dpy), 0, 0,
                    window->w, window->h, 0, common->depth,
       t@@ -567,8 +561,7 @@ window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
                window->fontmap = pango_xft_get_font_map(common->dpy, common->screen);
                window->context = pango_font_map_create_context(window->fontmap);
        
       -        /* FIXME: theme font name */
       -        window->font = pango_font_description_from_string("Monospace");
       +        window->font = pango_font_description_from_string(theme->text_font);
                pango_font_description_set_size(window->font, theme->text_size * PANGO_SCALE);
        
                window->wm_delete_msg = XInternAtom(common->dpy, "WM_DELETE_WINDOW", False);
       t@@ -577,7 +570,6 @@ window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
                window->common = common;
                window->theme = theme;
        
       -        /* FIXME: theme font */
                window->bb = ledit_malloc(sizeof(bottom_bar));
                window->bb->mode = pango_layout_new(window->context);
                pango_layout_set_font_description(window->bb->mode, window->font);
       t@@ -619,6 +611,7 @@ window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
                if (window->xtarget == None)
                        window->xtarget = XA_STRING;
        
       +        window->cursor_text = XCreateFontCursor(window->common->dpy, XC_xterm);
                /*
                ledit_clear_window(window);
                ledit_redraw_window(window);
       t@@ -692,7 +685,7 @@ window_redraw(ledit_window *window) {
                            window->common->dpy, window->drawable, window->gc,
                            window->w - t->scrollbar_width, 0, t->scrollbar_width, window->text_h
                        );
       -                XSetForeground(window->common->dpy, window->gc, t->text_fg.pixel);
       +                XSetForeground(window->common->dpy, window->gc, t->scrollbar_fg.pixel);
                        double scroll_h, scroll_y;
                        get_scroll_pos_height(window, &scroll_y, &scroll_h);
                        XFillRectangle(
       t@@ -701,8 +694,7 @@ window_redraw(ledit_window *window) {
                            t->scrollbar_width, (int)round(scroll_h)
                        );
                }
       -        XSetForeground(window->common->dpy, window->gc, t->text_bg.pixel);
       -        /* FIXME: allow different color for bar */
       +        XSetForeground(window->common->dpy, window->gc, t->bar_bg.pixel);
                XFillRectangle(
                    window->common->dpy, window->drawable, window->gc,
                    0, window->text_h,
       t@@ -716,7 +708,7 @@ window_redraw(ledit_window *window) {
                            0, window->text_h
                        );
                } else if (window->bottom_text_shown) {
       -                XSetForeground(window->common->dpy, window->gc, t->text_fg.pixel);
       +                XSetForeground(window->common->dpy, window->gc, t->bar_fg.pixel);
                        /* move input method position to cursor and draw cursor */
                        PangoRectangle strong, weak;
                        pango_layout_get_cursor_pos(
       t@@ -1027,6 +1019,14 @@ window_register_resize(ledit_window *window, XEvent *event) {
        
        void
        window_register_motion(ledit_window *window, XEvent *event) {
       +        /* cursor should always change, even if time has not elapsed */
       +        int x = event->xmotion.x;
       +        int y = event->xmotion.y;
       +        /* FIXME: avoid these calls if nothing has changed */
       +        if (x < window->text_w && y < window->text_h)
       +                XDefineCursor(window->common->dpy, window->xwin, window->cursor_text);
       +        else
       +                XDefineCursor(window->common->dpy, window->xwin, None);
                window->last_motion_event = *event;
                window->last_motion_valid = 1;
        }
       t@@ -1084,8 +1084,8 @@ window_drag_motion(ledit_window *window, XEvent *event) {
                if (window->scroll_dragging) {
                        double scroll_h, scroll_y;
                        get_scroll_pos_height(window, &scroll_y, &scroll_h);
       -                scroll_y += event->xbutton.y - window->scroll_grab_handle;
       -                window->scroll_grab_handle = event->xbutton.y;
       +                scroll_y += event->xmotion.y - window->scroll_grab_handle;
       +                window->scroll_grab_handle = event->xmotion.y;
                        set_scroll_pos(window, scroll_y);
                        window->redraw = 1;
                } else  {
   DIR diff --git a/window.h b/window.h
       t@@ -53,6 +53,7 @@ typedef struct {
                bottom_bar *bb;          /* encapsulates the text at the bottom */
                int redraw;              /* whether something has changed and the window has to be redrawn */
                ledit_mode mode;         /* mode of the view - a bit ugly to duplicate this here... */
       +        Cursor cursor_text;      /* text cursor shown when cursor is over text area */
        
                /* stuff for filtering events so not too many have to be handled */
                struct timespec last_scroll;