ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ledit.git (over tor)
Log | Files | Refs | README | LICENSE

view.h (18899B)


      1 /*
      2  * A view consists of a window and everything necessary for displaying
      3  * the contents of the associated buffer in that window.
      4  */
      5 
      6 #ifndef _LEDIT_VIEW_H_
      7 #define _LEDIT_VIEW_H_
      8 
      9 #include <stddef.h>
     10 #include "common.h"
     11 #include "txtbuf.h"
     12 #include "window.h"
     13 #include "cache.h"
     14 #include "uglycrap.h"
     15 
     16 typedef struct ledit_view ledit_view;
     17 
     18 #include "buffer.h"
     19 
     20 enum action_type {
     21         ACTION_NONE,   /* pass next key to basic key handler */
     22         ACTION_GRABKEY /* pass next key to given callback */
     23 };
     24 
     25 /* struct that is returned by key handlers to tell the
     26    main event manager what key handler to call next */
     27 struct action {
     28         enum action_type type;
     29         struct action (*callback)(ledit_view *view, unsigned int key_state, KeySym sym, char *buf, int n, int lang_index);
     30 };
     31 
     32 typedef struct {
     33 	ledit_view *view;          /* parent view */
     34 	int w;                     /* width in pixels */
     35 	int h;                     /* height in pixels */
     36 	long y_offset;             /* pixel offset starting at the top of the file */
     37 	size_t cache_pixmap_index; /* index of pixmap in cache, or -1 if not assigned */
     38 	size_t cache_layout_index; /* index of pango layout in cache, or -1 if not assigned */
     39 	int softlines;             /* number of softlines - cached from PangoLayout */
     40 	size_t cursor_index;       /* cursor index (for highlight in normal mode) */
     41 	char cursor_index_valid;   /* whether cursor index is valid */
     42 	char cache_pixmap_valid;   /* whether cache_pixmap_index is valid */
     43 	char cache_layout_valid;   /* whether cache_layout_index is valid */
     44 	char dirty;                /* whether line needs to be rendered before being drawn */
     45 	char text_dirty;           /* whether the text in the PangoLayout needs to be
     46 	                            * updated before the layout is rendered */
     47 	char highlight_dirty;      /* whether highlight (cursor or selection) needs to be
     48 	                            * updated still in the PangoLayout before rendering */
     49 	char h_dirty;              /* whether height needs to be recalculated */
     50 } ledit_view_line;
     51 
     52 struct ledit_view {
     53 	ledit_buffer *buffer;     /* parent buffer */
     54 	ledit_window *window;     /* window showing this view */
     55 	ledit_cache *cache;       /* cache for pixmaps and pango layouts */
     56 	ledit_view_line *lines;   /* array of lines, stored as gap buffer */
     57 	char *lock_text;          /* text to show if view is locked, i.e. no edits allowed */
     58 	/* current command type - used by key handler in keys_command.c */
     59 	command_mode cur_command_type;
     60 	struct action cur_action; /* current action to execute on key press */
     61 	size_t lines_cap;         /* size of lines array */
     62 	size_t lines_gap;         /* position of gap for line gap buffer */
     63 	size_t lines_num;         /* number of lines */
     64 	size_t cur_line;          /* current line */
     65 	size_t cur_index;         /* current byte index in line */
     66 	long total_height;        /* total pixel height of all lines */
     67 	long display_offset;      /* current pixel offset of viewport */
     68 	ledit_range sel;          /* current selection */
     69 	ledit_mode mode;          /* current mode of this view */
     70 	char destroy;             /* whether the view should be destroyed at the next possible time */
     71 	char selecting;           /* whether user is currently selecting text with mouse */
     72 	char button2_pressed;     /* whether button 2 (middle button) is pressed */
     73 	char sel_valid;           /* whether there is currently a valid selection */
     74 	char redraw;              /* whether something has changed so the view needs to be redrawn */
     75 };
     76 
     77 enum delete_mode {
     78 	DELETE_CHAR,     /* delete an exact line and byte range */
     79 	DELETE_SOFTLINE, /* delete a range of complete softlines */
     80 	DELETE_HARDLINE  /* delete a range of complete hardlines */
     81 };
     82 
     83 /*
     84  * Set the mode of the view.
     85  * This changes the mode group of the associated buffer's
     86  * undo stack and changes the mode display in the window.
     87  */
     88 void view_set_mode(ledit_view *view, ledit_mode mode);
     89 
     90 /*
     91  * Create a view with associated buffer 'buffer'
     92  * The initial mode, line, and byte position are given, respectively,
     93  * by 'mode', 'line', and 'pos'.
     94  */
     95 ledit_view *view_create(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos);
     96 
     97 /*
     98  * Lock a view.
     99  * Views are locked for instance when substitution with confirmation is
    100  * being performed in another view to avoid an inconsistent state.
    101  * This currently only sets the lock text - commands using the view need
    102  * to make sure to check that it isn't locked.
    103  */
    104 void view_lock(ledit_view *view, char *text);
    105 
    106 /*
    107  * Unlock a view.
    108  */
    109 void view_unlock(ledit_view *view);
    110 
    111 /*
    112  * Get the view line at the given index.
    113  * The returned line is only valid until the next
    114  * action that appends or deletes line entries.
    115  */
    116 /* ledit_view_line *view_get_line(ledit_view *view, size_t index); */
    117 
    118 /* This is very hacky - it's so the actual function calling view_get_line is returned in the assertion message.
    119  * There probably is a better way to do this.
    120  */
    121 #define view_get_line(view, index) (ledit_assert((index) < (view)->lines_num), (index) < (view)->lines_gap ? &(view)->lines[index] : &(view)->lines[(index) + (view)->lines_cap - (view)->lines_num])
    122 
    123 /*
    124  * These notification functions are called by the buffer when text
    125  * is changed in order to keep all views in sync.
    126  */
    127 
    128 /*
    129  * Notify the view that 'len' bytes of text have been inserted at
    130  * line 'line' and byte position 'index'.
    131  * This marks the line as dirty, adjusts the cursor position, if it
    132  * is on the same line, and sets the selection to just the current
    133  * cursor position, if there was a valid selection.
    134  * The line heights and offsets are not recalculated.
    135  */
    136 void view_notify_insert_text(ledit_view *view, size_t line, size_t index, size_t len);
    137 
    138 /*
    139  * Notify the view that 'len' bytes of text have been deleted
    140  * starting at line 'line' and byte position 'index'.
    141  * This marks the line as dirty, adjusts the cursor position, if it
    142  * is on the same line, and sets the selection to just the current
    143  * cursor position, if there was a valid selection.
    144  * The line heights and offsets are not recalculated.
    145  */
    146 void view_notify_delete_text(ledit_view *view, size_t line, size_t index, size_t len);
    147 
    148 /*
    149  * Notify the view that a line has been appended after line 'line'.
    150  * This adjusts the cursor position if necessary and sets the selection
    151  * to just the current cursor position, if there was a valid selection.
    152  * The line heights and offsets are not recalculated.
    153  */
    154 void view_notify_append_line(ledit_view *view, size_t line);
    155 
    156 /*
    157  * Notify the view that all lines from 'index1' to 'index2' (inclusive)
    158  * have been deleted.
    159  * This adjusts the cursor position if necessary and sets the selection
    160  * to just the current cursor position, if there was a valid selection.
    161  * The line heights and offsets are not recalculated.
    162  */
    163 void view_notify_delete_lines(ledit_view *view, size_t index1, size_t index2);
    164 
    165 /*
    166  * Destroy a view and its window.
    167  */
    168 void view_destroy(ledit_view *view);
    169 
    170 /*
    171  * Perform cleanup of global data.
    172  */
    173 void view_cleanup(void);
    174 
    175 /*
    176  * Set a cursor highlight on the character at line 'line' and
    177  * byte position 'index'.
    178  */
    179 void view_set_line_cursor_attrs(ledit_view *view, size_t line, size_t index);
    180 
    181 /*
    182  * Remove cursor highlight from line 'line'.
    183  */
    184 void view_wipe_line_cursor_attrs(ledit_view *view, size_t line);
    185 
    186 /*
    187  * Recalculate the height of line 'line'.
    188  * If it has changed, the offsets of all following lines are recalculated.
    189  */
    190 void view_recalc_line(ledit_view *view, size_t line);
    191 
    192 /*
    193  * Recalculate the height and offset of all lines starting at 'line'.
    194  * If 'line' is 0, the offset is set to 0.
    195  */
    196 void view_recalc_from_line(ledit_view *view, size_t line);
    197 
    198 /*
    199  * Shortcut for recalculating all lines starting at 0.
    200  */
    201 void view_recalc_all_lines(ledit_view *view);
    202 
    203 /*
    204  * The cursor movement functions here logically belong to the buffer,
    205  * but they use various unicode attributes that pango exposes, so
    206  * they have to be here as long as no separate library for unicode
    207  * processing is used.
    208  */
    209 
    210 /*
    211  * Get the line and byte position of the cursor position 'num' positions
    212  * after the position at line 'line' and byte position 'byte'. The new
    213  * position is returned in 'line_ret' and 'byte_ret'.
    214  * If multiline is non-zero, the new position may be on a different line.
    215  * Otherwise, it is at most the length of the given line.
    216  * A newline counts as one position.
    217  */
    218 void view_next_cursor_pos(
    219     ledit_view *view,
    220     size_t line, size_t byte,
    221     int num, int multiline,
    222     size_t *line_ret, size_t *byte_ret
    223 );
    224 
    225 /*
    226  * Get the line and byte position of the cursor position 'num' positions
    227  * before the position at line 'line' and byte position 'byte'. The new
    228  * position is returned in 'line_ret' and 'byte_ret'.
    229  * If multiline is non-zero, the new position may be on a different line.
    230  * Otherwise, it is never earlier than position 0 on the given line.
    231  * A newline counts as one position.
    232  */
    233 void view_prev_cursor_pos(
    234     ledit_view *view,
    235     size_t line, size_t byte,
    236     int num, int multiline,
    237     size_t *line_ret, size_t *byte_ret
    238 );
    239 
    240 /*
    241  * The next 6 functions all return a line, a byte position, and a
    242  * "real byte position". In the case of the "*prev*" functions, it
    243  * is actually the same as the normal byte position (it is just
    244  * returned to keep the interface the same), but in the case of the
    245  * "*next*" functions, it can be  different. For instance, when
    246  * moving forward to the end of a word, the normal byte index will
    247  * be before the last cursor of the word (i.e. the position of the
    248  * cursor highlight in normal mode), while the real byte index is
    249  * right after the word, so it can be used for deleting to the end
    250  * of the word.
    251  */
    252 
    253 /*
    254  * Words are defined by unicode semantics (as interpreted by pango),
    255  * while bigwords are simply blocks of non-whitespace characters.
    256  */
    257 
    258 void view_next_word(
    259     ledit_view *view, size_t line, size_t byte, int num_repeat,
    260     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    261 );
    262 void view_next_word_end(
    263     ledit_view *view, size_t line, size_t byte, int num_repeat,
    264     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    265 );
    266 void view_next_bigword(
    267     ledit_view *view, size_t line, size_t byte, int num_repeat,
    268     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    269 );
    270 void view_next_bigword_end(
    271     ledit_view *view, size_t line, size_t byte, int num_repeat,
    272     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    273 );
    274 void view_prev_word(
    275     ledit_view *view, size_t line, size_t byte, int num_repeat,
    276     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    277 );
    278 void view_prev_bigword(
    279     ledit_view *view, size_t line, size_t byte, int num_repeat,
    280     size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret
    281 );
    282 
    283 /*
    284  * Get the byte position of the next non-whitespace character starting at
    285  * 'byte' (including 'byte' itself), or the length of the line if there is
    286  * no further non-whitespace character.
    287  */
    288 size_t view_line_next_non_whitespace(ledit_view *view, size_t line, size_t byte);
    289 
    290 /*
    291  * Get the byte boundary of the softline at line 'line' and byte position 'pos'.
    292  */
    293 void view_get_pos_softline_bounds(
    294     ledit_view *view, size_t line, size_t pos,
    295     size_t *start_byte_ret, size_t *end_byte_ret
    296 );
    297 
    298 /*
    299  * Get the byte boundary of the softline with index 'softline'
    300  * in the hardline 'line'.
    301  */
    302 void view_get_softline_bounds(
    303     ledit_view *view, size_t line, int softline,
    304     size_t *start_byte_ret, size_t *end_byte_ret
    305 );
    306 
    307 /*
    308  * Get the number of softlines in line 'line'.
    309  */
    310 int view_get_softline_count(ledit_view *view, size_t line);
    311 
    312 /*
    313  * Get the softline index at hardline 'line' and byte position 'pos'.
    314  */
    315 int view_pos_to_softline(ledit_view *view, size_t line, size_t pos);
    316 
    317 /*
    318  * Get the pixel position and height of the cursor on hardline 'line'
    319  * at byte position 'pos'.
    320  */
    321 void view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int *x_ret, int *y_ret, int *h_ret);
    322 
    323 /*
    324  * Get the byte index of the cursor if it is moved visually 'movement'
    325  * positions from the position as line 'line' and byte position 'pos',
    326  * where a negative number is to the left and a positive number to the
    327  * right. 
    328  * If 'prev_index_ret' is not NULL, the previous valid cursor position
    329  * is written to it - this is used in normal mode to move back one
    330  * position if the cursor is at the end of the line. For some reason,
    331  * using 'view_get_legal_normal_pos' doesn't work here. I still need
    332  * to figure out why.
    333  */
    334 size_t view_move_cursor_visually(ledit_view *view, size_t line, size_t pos, int movement, size_t *prev_index_ret);
    335 
    336 /*
    337  * Convert a line index and byte position to an x position and softline
    338  * index. The x position is in pango units, not pixels.
    339  * In normal mode, the middle of the character is returned instead of the
    340  * beginning.
    341  */
    342 void view_pos_to_x_softline(ledit_view *view, size_t line, size_t pos, int *x_ret, int *softline_ret);
    343 
    344 /*
    345  * Convert a line index, softline index, and x position (in pango units,
    346  * not pixels) to a byte position.
    347  * In insert mode, the returned byte position is the closest cursor
    348  * position. In normal mode, it is simply the beginning of the character
    349  * (or, rather, grapheme) that the x position was on, regardless of where
    350  * on that character the position was.
    351  */
    352 size_t view_x_softline_to_pos(ledit_view *view, size_t line, int x, int softline);
    353 
    354 /*
    355  * Get a legal normal mode position, i.e. move back one cursor position
    356  * if 'pos' is at the very end of the line.
    357  */
    358 size_t view_get_legal_normal_pos(ledit_view *view, size_t line, size_t pos);
    359 
    360 /*
    361  * Delete a range according to a delete_mode.
    362  * The line and byte indeces do not need to be sorted (in fact, they often
    363  * shouldn't be, as shown in the next sentence).
    364  * If 'delmode' is DELETE_HARDLINE or DELETE_SOFTLINE, 'line_index1' and
    365  * 'byte_index1' are used to determine where the cursor should be after
    366  * the deletion. This new position is written to 'new_line_ret' and
    367  * 'new_byte_ret'.
    368  * If 'delmode' is DELETE_SOFTLINE, the byte indeces are additionally used
    369  * to determine which softlines the range bounds are on.
    370  * Both line deletion modes make sure that there is at least one line left
    371  * in the buffer afterwards, although it may have its text deleted.
    372  * If 'delmode' is DELETE_CHAR, the exact specified range is deleted, and
    373  * the new line and byte are simply the beginning of the range.
    374  * The deleted text is written to 'text_ret'.
    375  * In normal mode, the new cursor index is always at a valid normal mode
    376  * position.
    377  * All return arguments may be NULL.
    378  * The deletion is added to the undo stack with 'start_undo_group'
    379  * specifying whether a new undo group should be started.
    380  * The start cursor position for the undo stack is taken directly from
    381  * the view's current position. The end cursor position is the same as
    382  * what is returned in 'new_line_ret' and 'new_byte_ret', except that it
    383  * is set before calling 'view_get_legal_normal_pos'.
    384  * If different cursor positions are needed, call
    385  * 'undo_change_last_cur_range' afterwards.
    386  * This function does not recalculate the line heights or offsets.
    387  */
    388 void view_delete_range_base(
    389     ledit_view *view,
    390     enum delete_mode delmode, int start_undo_group,
    391     size_t line_index1, size_t byte_index1,
    392     size_t line_index2, size_t byte_index2,
    393     size_t *new_line_ret, size_t *new_byte_ret,
    394     txtbuf *text_ret
    395 );
    396 
    397 /*
    398  * Same as 'view_delete_range_base', but the line heights and offsets of
    399  * all views are recalculated afterwards.
    400  */
    401 void view_delete_range(
    402     ledit_view *view,
    403     enum delete_mode delmode, int start_undo_group,
    404     size_t line_index1, size_t byte_index1,
    405     size_t line_index2, size_t byte_index2,
    406     size_t *new_line_ret, size_t *new_byte_ret,
    407     txtbuf *text_ret
    408 );
    409 
    410 /*
    411  * Resize the size of the textview, i.e. resize the line widths
    412  * and update all scrolling related data.
    413  * 'data' is the view, but is given as a void pointer so the
    414  * function can be used as a callback.
    415  */
    416 void view_resize_textview(void *data);
    417 
    418 /*
    419  * Scroll to the given pixel offset.
    420  * The given offset is sanity-checked so new illegal positions can result.
    421  */
    422 void view_scroll(ledit_view *view, long new_offset);
    423 
    424 /*
    425  * Get the position nearest to 'line' and 'byte' that is currently shown
    426  * on screen.
    427  */
    428 void view_get_nearest_legal_pos(
    429     ledit_view *view,
    430     size_t line, size_t byte,
    431     /*int snap_to_nearest, int snap_middle, FIXME: take these parameters */
    432     size_t *line_ret, size_t *byte_ret
    433 );
    434 
    435 /*
    436  * Convert pixel coordinates to line and byte indeces. The pixel coordinates
    437  * are relative to the current textview.
    438  * If 'snap_to_nearest' is set, but grapheme boundary nearest to the position
    439  * is returned. Otherwise, the start position of the grapheme under the position
    440  * is returned.
    441  */
    442 void view_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest, size_t *line_ret, size_t *byte_ret);
    443 
    444 /*
    445  * Scroll so that the given cursor position is at the very top of the view.
    446  * Note that this may not be entirely true since the final position is
    447  * sanity-checked to be within the scroll bounds.
    448  */
    449 void view_scroll_to_pos_top(ledit_view *view, size_t line, size_t byte);
    450 
    451 /*
    452  * Scroll so that the given cursor position is at the very bottom of the view.
    453  * Note that this may not be entirely true since the final position is
    454  * sanity-checked to be within the scroll bounds.
    455  */
    456 void view_scroll_to_pos_bottom(ledit_view *view, size_t line, size_t byte);
    457 
    458 /*
    459  * Scroll so that the current cursor position is visible on screen.
    460  */
    461 void view_ensure_cursor_shown(ledit_view *view);
    462 
    463 /*
    464  * Clear the selection.
    465  */
    466 void view_wipe_selection(ledit_view *view);
    467 
    468 /*
    469  * Set the selection to the given range.
    470  * The range does not need to be sorted.
    471  */
    472 void view_set_selection(ledit_view *view, size_t line1, size_t byte1, size_t line2, size_t byte2);
    473 
    474 /*
    475  * Redraw the view.
    476  * This only redraws if the redraw bit of the view or window are set.
    477  * This should all be set automatically.
    478  */
    479 void view_redraw(ledit_view *view, size_t lang_index);
    480 /* FIXME: kind of ugly to pass lang_index here, but the window needs it - maybe store centrally somewhere? */
    481 
    482 /*
    483  * Perform up to num undo steps.
    484  * The cursor position of the view is set to the stored position
    485  * in the undo stack.
    486  * The line heights and offsets are recalculated.
    487  */
    488 void view_undo(ledit_view *view, int num);
    489 
    490 /*
    491  * Perform up to num redo steps.
    492  * The cursor position of the view is set to the stored position
    493  * in the undo stack.
    494  * The line heights and offsets are recalculated.
    495  */
    496 void view_redo(ledit_view *view, int num);
    497 
    498 /*
    499  * Paste the X11 clipboard at the current cursor position.
    500  */
    501 void view_paste_clipboard(ledit_view *view);
    502 
    503 /*
    504  * Paste the X11 primary selection at the current cursor position.
    505  */
    506 void view_paste_primary(ledit_view *view);
    507 
    508 #endif