commit 9bdc877acf81ae270c68cacad2e70b9860f0832e
parent 6c98800389dc02e20954a157675ac67fb02b7f8c
Author: lumidify <nobody@lumidify.org>
Date: Sun, 28 Nov 2021 21:06:33 +0100
Add support for multiple views
Warning: There are still a lot of bugs.
Diffstat:
M | IDEAS | | | 6 | +++--- |
M | Makefile | | | 6 | ++++-- |
A | QUIRKS | | | 9 | +++++++++ |
A | TODO | | | 2 | ++ |
M | buffer.c | | | 2267 | +++++++++---------------------------------------------------------------------- |
M | buffer.h | | | 418 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | cache.c | | | 94 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | cache.h | | | 66 | ++++++++++++++++++++++++++++++++++++------------------------------ |
A | cleanup.h | | | 3 | +++ |
M | common.h | | | 9 | ++++----- |
M | keys_basic.c | | | 1483 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | keys_basic.h | | | 3 | +-- |
M | keys_basic_config.h | | | 160 | ++++++++++++++++++++++++++++++++++++++++---------------------------------------- |
M | keys_command.c | | | 232 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | keys_command.h | | | 17 | +++-------------- |
M | keys_command_config.h | | | 32 | ++++++++++++++++---------------- |
M | ledit.c | | | 196 | ++++++++++++++++++++++++------------------------------------------------------- |
A | macros.h | | | 10 | ++++++++++ |
M | memory.c | | | 114 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
M | memory.h | | | 32 | ++++++++++++++++++++++++++++++++ |
M | pango-compat.c | | | 8 | ++++++-- |
M | pango-compat.h | | | 5 | +++-- |
M | search.c | | | 74 | +++++++++++++++++++++++++++++++++++++------------------------------------- |
M | search.h | | | 4 | ++-- |
M | undo.c | | | 32 | ++++++++++++++++++++++++-------- |
M | undo.h | | | 8 | ++++---- |
M | util.h | | | 2 | ++ |
A | view.c | | | 2096 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | view.h | | | 158 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | window.c | | | 179 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
M | window.h | | | 37 | ++++++++++++++++++++++++++++++------- |
31 files changed, 4305 insertions(+), 3457 deletions(-)
diff --git a/IDEAS b/IDEAS
@@ -1,5 +1,5 @@
-* Important: use less memory (maybe don't keep all pango layouts around
- all the time)
-* allow editing same file in multiple places at same time (like in acme)
+* draw cursor as outline rectangle when window not active
+* implement '!' command by just opening a separate terminal
+* basic macros
* add different (more basic) text backend
* https://drewdevault.com/2021/06/27/You-cant-capture-the-nuance.html
diff --git a/Makefile b/Makefile
@@ -11,6 +11,7 @@ MAN1 = ${BIN:=.1}
OBJ = \
buffer.o \
+ view.o \
cache.o \
keys.o \
keys_basic.o \
@@ -26,8 +27,8 @@ OBJ = \
pango-compat.o
HDR = \
- action.h \
buffer.h \
+ view.h \
cache.h \
common.h \
keys.h \
@@ -40,6 +41,7 @@ HDR = \
undo.h \
util.h \
window.h \
+ cleanup.h \
pango-compat.h
CFLAGS_LEDIT = -g -Wall -Wextra -D_POSIX_C_SOURCE=200809L `pkg-config --cflags x11 xkbfile pangoxft xext`
@@ -47,7 +49,7 @@ LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm
all: ${BIN}
-ledit.o : config.h
+ledit.o window.o : config.h
theme.o : theme_config.h
keys_basic.o : keys_basic_config.h
keys_command.o : keys_command_config.h
diff --git a/QUIRKS b/QUIRKS
@@ -0,0 +1,9 @@
+* Undo with multiple views:
+ Since a new mode group is started each time insert is entered, when text
+ is typed in one view in insert, then in another view, and then again in
+ the first one, the last two inserts will be undone in one go since both
+ views were in insert already. I'm not sure how to make this more logical,
+ though.
+ Maybe it could be "improved" by also saving view in undo stack, but that
+ would cause problems because views can be added and removed, and it would
+ maybe not even be more logical.
diff --git a/TODO b/TODO
@@ -0,0 +1,2 @@
+* Load file in background so text is already shown while still
+ loading the rest of a big file.
diff --git a/buffer.c b/buffer.c
@@ -1,12 +1,14 @@
/* FIXME: shrink buffers when text length less than a fourth of the size */
-/* FIXME: also cache PangoLayouts since keeping them around isn't really of much use? */
/* FIXME: handle all undo within buffer to keep it consistent */
+/* FIXME: maybe use separate unicode grapheme library so all functions
+ that need grapheme boundaries can be included here instead of in the views */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
+#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -34,52 +36,45 @@
* normalized. This is a bit ugly, but oh well.
*/
-static PangoAttrList *basic_attrs = NULL;
+/*
+ * Note: "line gap buffer" refers to the gap buffer containing
+ * all lines, not to the gap buffer of the text in one line.
+ */
-/*static void err_text_dirty(ledit_buffer *buffer, int line);*/
-static void move_text_gap(ledit_line *line, int index);
-static void resize_and_move_text_gap(ledit_line *line, int min_size, int index);
-static char *strchr_len(char *text, char c, long len);
-static void init_line(ledit_buffer *buffer, ledit_line *line);
-static void delete_line_section_base(ledit_buffer *buffer, int line, int start, int length);
-static void swap(int *a, int *b);
-static void copy_selection_to_x_primary(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2);
+/*
+ * Move the gap of a line so it is at byte position 'index'
+ */
+static void move_text_gap(ledit_line *line, size_t index);
/*
- * Assign a cache index to line and set text and highlight of the pango layout.
+ * Move the gap of the line gap buffer to 'index'.
*/
-static void set_pango_text_and_highlight(ledit_buffer *buffer, int line);
+static void move_line_gap(ledit_buffer *buffer, size_t index);
/*
- * Get the pango layout for line.
- * This first assigns a cache index (by calling set_pango_text_and_highlight).
+ * Resize the line so it can hold at least 'min_size' bytes and move the gap
+ * to byte position 'index'.
*/
-static PangoLayout *get_pango_layout(ledit_buffer *buffer, int line);
+static void resize_and_move_text_gap(ledit_line *line, size_t min_size, size_t index);
/*
- * Get an attribute list for a text highlight between the given range.
+ * Resize the line gap buffer so it can hold at least 'min_size' lines and
+ * move the gap to line at position 'index'.
*/
-static PangoAttrList *get_pango_attributes(ledit_buffer *buffer, int start_byte, int end_byte);
+static void resize_and_move_line_gap(ledit_buffer *buffer, size_t min_size, size_t index);
/*
- * Set the attributes for a PangoLayout belonging to the given line index.
- * If the line is part of the buffer's selection, the selection is set.
- * If that is not the case but cursor_index is set for the line, the character
- * at that position is highlighted (this is used for the normal mode cursor).
- * Otherwise, the default attributes (basic_attrs) are set.
+ * Similar to strchr, but the length of the text is given separately
*/
-static void set_line_layout_attrs(ledit_buffer *buffer, int line, PangoLayout *layout);
+static char *strchr_len(char *text, char c, size_t len);
-static int line_visible_callback(void *data, int line);
-static void set_pixmap_line_helper(void *data, int line, int index);
-static void set_layout_line_helper(void *data, int line, int index);
+/*
+ * Initialize a line with default values for its struct members.
+ */
+static void init_line(ledit_buffer *buffer, ledit_line *line);
-void
-ledit_buffer_set_mode(ledit_buffer *buffer, enum ledit_mode mode) {
- buffer->common->mode = mode;
- ledit_window_set_mode(buffer->window, mode);
- ledit_change_mode_group(buffer->undo);
-}
+static void marklist_destroy(ledit_buffer_marklist *marklist);
+static ledit_buffer_marklist *marklist_create(void);
static void
marklist_destroy(ledit_buffer_marklist *marklist) {
@@ -91,7 +86,7 @@ marklist_destroy(ledit_buffer_marklist *marklist) {
}
void
-ledit_buffer_insert_mark(ledit_buffer *buffer, char *mark, int len, int line, int byte) {
+ledit_buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t line, size_t byte) {
ledit_buffer_marklist *marklist = buffer->marklist;
for (size_t i = 0; i < marklist->len; i++) {
if (!strncmp(mark, marklist->marks[i].text, len)) {
@@ -123,51 +118,89 @@ marklist_create(void) {
}
ledit_buffer *
-ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *window) {
- if (basic_attrs == NULL) {
- basic_attrs = pango_attr_list_new();
- #if PANGO_VERSION_CHECK(1, 44, 0)
- PangoAttribute *no_hyphens = pango_attr_insert_hyphens_new(FALSE);
- pango_attr_list_insert(basic_attrs, no_hyphens);
- #endif
- }
-
+ledit_buffer_create(ledit_common *common) {
ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer));
- buffer->cache = cache_create(common->dpy);
+ buffer->common = common;
buffer->undo = ledit_undo_stack_create();
- buffer->theme = theme;
buffer->marklist = marklist_create();
- ledit_window_set_scroll_callback(window, &ledit_buffer_scroll_handler, buffer);
- ledit_window_set_button_callback(window, &ledit_buffer_button_handler, buffer);
- buffer->common = common;
- buffer->window = window;
- buffer->lines = NULL;
buffer->filename = NULL;
+ buffer->lines = NULL;
buffer->lines_num = 0;
buffer->lines_cap = 0;
- buffer->selecting = 0;
- buffer->cur_line = 0;
- buffer->cur_index = 0;
- /* FIXME: trailing currently not used */
- buffer->trailing = 0;
- buffer->trailing_bytes = 0;
- buffer->end_of_soft_line = 0;
- buffer->total_height = 0;
- buffer->display_offset = 0;
- buffer->sel.line1 = buffer->sel.byte1 = -1;
- buffer->sel.line2 = buffer->sel.byte2 = -1;
-
- ledit_buffer_append_line_base(buffer, -1, -1);
- ledit_buffer_recalc_all_lines(buffer);
+ buffer->lines_gap = 0;
+ buffer->views = NULL;
+ buffer->views_num = 0;
+ buffer->hard_line_based = 1;
+
+ /* add one empty line to buffer */
+ resize_and_move_line_gap(buffer, 1, 0);
+ buffer->lines_num++;
+ buffer->lines_gap++;
+ ledit_line *ll = ledit_buffer_get_line(buffer, 0);
+ init_line(buffer, ll);
return buffer;
}
+static void
+set_view_hard_line_text(ledit_buffer *buffer, ledit_view *view) {
+ char *text = buffer->hard_line_based ? "|HL" : "|SL";
+ ledit_window_set_mode_extra_text(view->window, text);
+}
+
+void
+ledit_buffer_set_hard_line_based(ledit_buffer *buffer, int hl) {
+ buffer->hard_line_based = hl;
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ set_view_hard_line_text(buffer, buffer->views[i]);
+ }
+}
+
+void
+ledit_buffer_add_view(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size_t line, size_t pos) {
+ size_t new_num = buffer->views_num + 1;
+ if (new_num <= buffer->views_num)
+ err_overflow();
+ buffer->views = ledit_reallocarray(buffer->views, new_num, sizeof(ledit_view *));
+ buffer->views[buffer->views_num] = view_create(buffer, theme, mode, line, pos);
+ set_view_hard_line_text(buffer, buffer->views[buffer->views_num]);
+ buffer->views_num = new_num;
+}
+
+/* FIXME: error checking */
+void
+ledit_buffer_remove_view(ledit_buffer *buffer, ledit_view *view) {
+ size_t i = 0;
+ int found = 0;
+ for (; i < buffer->views_num; i++) {
+ if (buffer->views[i] == view) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ view_destroy(buffer->views[i]);
+ memmove(
+ buffer->views + i,
+ buffer->views + i + 1,
+ (buffer->views_num - i - 1) * sizeof(ledit_view *)
+ );
+ ledit_reallocarray(buffer->views, --buffer->views_num, sizeof(ledit_view *));
+ }
+}
+
+void
+ledit_buffer_recalc_all_views_from_line(ledit_buffer *buffer, size_t line) {
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_recalc_from_line(buffer->views[i], line);
+ }
+}
+
/* FIXME: don't generate extra blank line at end! */
/* WARNING: errstr must be copied as soon as possible! */
int
-ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, char **errstr) {
+ledit_buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errstr) {
long len;
int off = 0;
ledit_line *ll;
@@ -224,7 +257,7 @@ ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr)
file = fopen(filename, "w");
if (!file) goto error;
clearerr(file);
- for (int i = 0; i < buffer->lines_num; i++) {
+ for (size_t i = 0; i < buffer->lines_num; i++) {
ll = ledit_buffer_get_line(buffer, i);
ledit_buffer_normalize_line(ll);
if (fprintf(file, "%s\n", ll->text) < 0) goto errorclose;
@@ -245,11 +278,10 @@ errorclose:
void
ledit_buffer_destroy(ledit_buffer *buffer) {
ledit_line *l;
- for (int i = 0; i < buffer->lines_num; i++) {
+ for (size_t i = 0; i < buffer->lines_num; i++) {
l = ledit_buffer_get_line(buffer, i);
free(l->text);
}
- cache_destroy(buffer->cache);
ledit_undo_stack_destroy(buffer->undo);
free(buffer->lines);
if (buffer->filename)
@@ -259,12 +291,6 @@ ledit_buffer_destroy(ledit_buffer *buffer) {
}
void
-ledit_buffer_cleanup(void) {
- if (basic_attrs)
- pango_attr_list_unref(basic_attrs);
-}
-
-void
ledit_buffer_normalize_line(ledit_line *line) {
if (line->gap < line->len) {
memmove(
@@ -275,122 +301,17 @@ ledit_buffer_normalize_line(ledit_line *line) {
line->gap = line->len;
}
/* FIXME: check if enough space, just to be sure */
+ assert(line->len < line->cap);
line->text[line->len] = '\0';
}
-#if 0
-static void
-err_text_dirty(ledit_buffer *buffer, int line) {
- fprintf(
- stderr,
- "WARNING: Line had text_dirty or h_dirty attribute "
- "set when rendering. Fix your code!\n"
- );
- ledit_buffer_recalc_from_line(buffer, line);
-}
-#endif
-
-#if 0
-static void
-set_line_selection(ledit_buffer *buffer, int line, int start_byte, int end_byte) {
- ledit_line *l = ledit_buffer_get_line(buffer, line);
- if (l->text_dirty)
- err_text_dirty(buffer, line);
- /* FIXME: Are these guaranteed to be range 0-65535? */
- XRenderColor fg = buffer->theme->text_fg.color;
- XRenderColor bg = buffer->theme->text_bg.color;
- PangoAttribute *attr0 = pango_attr_background_new(fg.red, fg.green, fg.blue);
- PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg.blue);
- attr0->start_index = start_byte;
- attr0->end_index = end_byte;
- attr1->start_index = start_byte;
- attr1->end_index = end_byte;
- PangoAttrList *list = pango_attr_list_new();
- pango_attr_list_insert(list, attr0);
- pango_attr_list_insert(list, attr1);
- #if PANGO_VERSION_CHECK(1, 44, 0)
- PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
- pango_attr_list_insert(list, attr2);
- #endif
- pango_layout_set_attributes(l->layout, list);
- pango_attr_list_unref(list);
- l->dirty = 1;
-}
-#endif
-
-static PangoAttrList *
-get_pango_attributes(ledit_buffer *buffer, int start_byte, int end_byte) {
- XRenderColor fg = buffer->theme->text_fg.color;
- XRenderColor bg = buffer->theme->text_bg.color;
- PangoAttribute *attr0 = pango_attr_background_new(fg.red, fg.green, fg.blue);
- PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg.blue);
- attr0->start_index = start_byte;
- attr0->end_index = end_byte;
- attr1->start_index = start_byte;
- attr1->end_index = end_byte;
- PangoAttrList *list = pango_attr_list_new();
- pango_attr_list_insert(list, attr0);
- pango_attr_list_insert(list, attr1);
- #if PANGO_VERSION_CHECK(1, 44, 0)
- PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
- pango_attr_list_insert(list, attr2);
- #endif
- return list;
-}
-
-/* this takes layout directly to possibly avoid infinite recursion */
-static void
-set_line_layout_attrs(ledit_buffer *buffer, int line, PangoLayout *layout) {
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- ledit_range sel = ll->parent_buffer->sel;
- PangoAttrList *list = NULL;
- if (sel.line1 < line && sel.line2 > line) {
- list = get_pango_attributes(buffer, 0, ll->len);
- } else if (sel.line1 == line && sel.line2 == line) {
- int start = sel.byte1, end = sel.byte2;
- if (start > end)
- swap(&start, &end);
- list = get_pango_attributes(buffer, start, end);
- } else if (sel.line1 == line && sel.line2 > line) {
- list = get_pango_attributes(buffer, sel.byte1, ll->len);
- } else if (sel.line1 < line && sel.line2 == line) {
- list = get_pango_attributes(buffer, 0, sel.byte2);
- } else if (ll->cursor_index >= 0 && ll->cursor_index < ll->len) {
- /* FIXME: does just adding one really do the right thing? */
- list = get_pango_attributes(buffer, ll->cursor_index, ll->cursor_index + 1);
- }
- if (list != NULL) {
- pango_layout_set_attributes(layout, list);
- pango_attr_list_unref(list);
- } else {
- pango_layout_set_attributes(layout, basic_attrs);
- }
- ll->highlight_dirty = 0;
-}
-
-void
-ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) {
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- ll->cursor_index = index;
- ll->highlight_dirty = 1;
- ll->dirty = 1;
-}
-
-void
-ledit_buffer_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) {
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- ll->cursor_index = -1;
- ll->highlight_dirty = 1;
- ll->dirty = 1;
-}
-
/* FIXME: To simplify this a bit, maybe just copy text to txtbuf first and
then insert it in one go instead of having this complex logic */
void
ledit_buffer_insert_text_from_line(
ledit_buffer *buffer,
- int dst_line, int dst_index,
- int src_line, int src_index, int src_len,
+ size_t dst_line, size_t dst_index,
+ size_t src_line, size_t src_index, size_t src_len,
txtbuf *text_ret) {
ledit_buffer_insert_text_from_line_base(
buffer, dst_line, dst_index, src_line, src_index, src_len, text_ret
@@ -398,19 +319,17 @@ ledit_buffer_insert_text_from_line(
ledit_buffer_recalc_line(buffer, dst_line);
}
-/* FIXME: check if there can be bugs when a newline is inserted in some way other
+/* FIXME: check if there can be bugs when a newline is inserted in some way
other than pasting or pressing enter */
void
ledit_buffer_insert_text_from_line_base(
ledit_buffer *buffer,
- int dst_line, int dst_index,
- int src_line, int src_index, int src_len,
+ size_t dst_line, size_t dst_index,
+ size_t src_line, size_t src_index, size_t src_len,
txtbuf *text_ret) {
assert(dst_line != src_line);
ledit_line *ll = ledit_buffer_get_line(buffer, src_line);
- if (src_len == -1)
- src_len = ll->len - src_index;
if (text_ret != NULL) {
txtbuf_grow(text_ret, src_len);
text_ret->len = src_len;
@@ -467,91 +386,66 @@ ledit_buffer_insert_text_from_line_base(
}
}
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_notify_insert_text(buffer->views[i], dst_line, dst_index, src_len);
+ }
}
static void
-move_text_gap(ledit_line *line, int index) {
- if (index > line->gap) {
- /* move piece between end of original gap and
- index to beginning of original gap */
- memmove(
- line->text + line->gap,
- line->text + line->gap + line->cap - line->len,
- index - line->gap
- );
- } else if (index < line->gap) {
- /* move piece between index and original gap to
- end of original gap */
- memmove(
- line->text + index + line->cap - line->len,
- line->text + index,
- line->gap - index
- );
- }
- line->gap = index;
+move_text_gap(ledit_line *line, size_t index) {
+ /* yes, I know sizeof(char) == 1 anyways */
+ move_gap(
+ line->text, sizeof(char), index,
+ line->gap, line->cap, line->len,
+ &line->gap
+ );
+}
+
+static void
+move_line_gap(ledit_buffer *buffer, size_t index) {
+ move_gap(
+ buffer->lines, sizeof(ledit_line), index,
+ buffer->lines_gap, buffer->lines_cap, buffer->lines_num,
+ &buffer->lines_gap
+ );
}
-/* This is almost certainly premature optimization and maybe
- not optimization at all. */
/* FIXME: add "final" versions of the functions that include the
normalization, i.e. if they have to move the gap anyways, they
just move it to the end */
+
static void
-resize_and_move_text_gap(ledit_line *line, int min_size, int index) {
- int gap_size = line->cap - line->len;
- /* FIXME: read up on what the best values are here */
- line->cap = line->cap * 2 > min_size ? line->cap * 2 : min_size;
- line->text = ledit_realloc(line->text, line->cap);
- if (index > line->gap) {
- /* move piece between end of original gap and index to
- beginning of original gap */
- memmove(
- line->text + line->gap,
- line->text + line->gap + gap_size,
- index - line->gap
- );
- /* move piece after index to end of buffer */
- memmove(
- line->text + line->cap - (line->len - index),
- line->text + index + gap_size,
- line->len - index
- );
- } else if (index < line->gap) {
- /* move piece after original gap to end of buffer */
- memmove(
- line->text + line->cap - (line->len - line->gap),
- line->text + line->gap + gap_size,
- line->len - line->gap
- );
- /* move piece between index and original gap to end */
- memmove(
- line->text + line->cap - line->len + index,
- line->text + index,
- line->gap - index
- );
- } else {
- /* move piece after original gap to end of buffer */
- memmove(
- line->text + line->cap - (line->len - line->gap),
- line->text + line->gap + gap_size,
- line->len - line->gap
- );
- }
- line->gap = index;
+resize_and_move_text_gap(ledit_line *line, size_t min_size, size_t index) {
+ /* yes, I know sizeof(char) == 1 anyways */
+ line->text = resize_and_move_gap(
+ line->text, sizeof(char),
+ line->gap, line->cap, line->len,
+ min_size, index,
+ &line->gap, &line->cap
+ );
+}
+
+static void
+resize_and_move_line_gap(ledit_buffer *buffer, size_t min_size, size_t index) {
+ buffer->lines = resize_and_move_gap(
+ buffer->lines, sizeof(ledit_line),
+ buffer->lines_gap, buffer->lines_cap, buffer->lines_num,
+ min_size, index,
+ &buffer->lines_gap, &buffer->lines_cap
+ );
}
void
-ledit_buffer_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, int len) {
+ledit_buffer_insert_text(ledit_buffer *buffer, size_t line_index, size_t index, char *text, size_t len) {
ledit_buffer_insert_text_base(buffer, line_index, index, text, len);
ledit_buffer_recalc_line(buffer, line_index);
}
void
-ledit_buffer_insert_text_base(ledit_buffer *buffer, int line_index, int index, char *text, int len) {
- ledit_line *line = &buffer->lines[line_index];
- if (len == -1)
- len = strlen(text);
+ledit_buffer_insert_text_base(ledit_buffer *buffer, size_t line_index, size_t index, char *text, size_t len) {
+ ledit_line *line = ledit_buffer_get_line(buffer, line_index);
/* \0 is not included in line->len */
+ /* FIXME: this if should be redundant now because resize_and_move... includes a check */
if (line->len + len + 1 > line->cap || line->text == NULL)
resize_and_move_text_gap(line, line->len + len + 1, index);
else
@@ -560,24 +454,15 @@ ledit_buffer_insert_text_base(ledit_buffer *buffer, int line_index, int index, c
memcpy(line->text + index, text, len);
line->gap += len;
line->len += len;
- line->dirty = 1;
- line->text_dirty = 1;
- line->h_dirty = 1;
-}
-
-/* FIXME: implement this - if it is known that this is the last insertion,
- move gap to end immediately if the gap needs to be enlarged anyways to
- avoid another copy before rendering */
-/*
-void
-ledit_buffer_insert_text_final(ledit_buffer *buffer, int line_index, int index, char *text, int len) {
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_notify_insert_text(buffer->views[i], line_index, index, len);
+ }
}
-*/
/* FIXME: this isn't optimized like the standard version, but whatever */
static char *
-strchr_len(char *text, char c, long len) {
- for (long i = 0; i < len; i++) {
+strchr_len(char *text, char c, size_t len) {
+ for (size_t i = 0; i < len; i++) {
if (text[i] == c)
return text + i;
}
@@ -585,23 +470,16 @@ strchr_len(char *text, char c, long len) {
}
/* FIXME: make these functions that call recalc* also be final as described above */
-/* FIXME: Sort out integer types.
- -> len is long here mainly because that's what ftell(3) returns and it sort of
- makes sense since a file can be very long (although ledit probably won't work
- with such long files anyways). The individual lines have to use int anyways
- because of pango.
- Maybe len isn't needed anyways? It might be possible to enforce that text
- just always has to be null-terminated. */
void
ledit_buffer_insert_text_with_newlines(
ledit_buffer *buffer,
- int line_index, int index,
- char *text, long len,
- int *end_line_ret, int *end_char_ret) {
- int end;
+ size_t line_index, size_t index,
+ char *text, size_t len,
+ size_t *end_line_ret, size_t *end_byte_ret) {
+ size_t end;
ledit_buffer_insert_text_with_newlines_base(
buffer, line_index, index, text, len,
- &end, end_char_ret
+ &end, end_byte_ret
);
if (end_line_ret)
*end_line_ret = end;
@@ -611,23 +489,22 @@ ledit_buffer_insert_text_with_newlines(
ledit_buffer_recalc_from_line(buffer, line_index);
}
-/* FIXME: Check for integer overflow when casting to int */
+/* FIXME: also look for \r */
void
ledit_buffer_insert_text_with_newlines_base(
ledit_buffer *buffer,
- int line_index, int index,
- char *text, long len,
- int *end_line_ret, int *end_char_ret) {
- if (len == -1)
- len = strlen(text);
- long rem_len = len;
+ size_t line_index, size_t index,
+ char *text, size_t len,
+ size_t *end_line_ret, size_t *end_byte_ret) {
+ size_t rem_len = len;
char *cur, *last = text;
- int cur_line = line_index;
- int cur_index = index;
+ size_t cur_line = line_index;
+ size_t cur_index = index;
/* FIXME: strchr_len isn't really needed when the lines are normalized anyways */
while ((cur = strchr_len(last, '\n', rem_len)) != NULL) {
- /* FIXME: inefficient because there's no gap buffer yet */
- ledit_buffer_append_line_base(buffer, cur_line, cur_index);
+ /* FIXME: this is probably inefficient, but I don't have time to
+ think about it right now */
+ ledit_buffer_append_line_base(buffer, cur_line, cur_index, 1);
ledit_buffer_insert_text_base(buffer, cur_line, cur_index, last, cur - last);
cur_index = 0;
cur_line++;
@@ -638,256 +515,122 @@ ledit_buffer_insert_text_with_newlines_base(
ledit_buffer_insert_text_base(buffer, cur_line, cur_index, last, text + len - last);
if (end_line_ret)
*end_line_ret = cur_line;
- if (end_char_ret)
- *end_char_ret = cur_index + text + len - last;
-}
-
-/* FIXME: is this even needed? */
-static int
-line_visible_callback(void *data, int line) {
- return ledit_buffer_line_visible((ledit_buffer*)data, line);
-}
-
-/* FIXME: standardize variable names (line/line_index, etc.) */
-void
-ledit_buffer_render_line(ledit_buffer *buffer, int line_index) {
- /* FIXME: check for <= 0 on size */
- ledit_line *ll = ledit_buffer_get_line(buffer, line_index);
- assert(!ll->h_dirty); /* FIXME */
- PangoLayout *layout = get_pango_layout(buffer, line_index);
- if (ll->cache_pixmap_index == -1) {
- cache_assign_pixmap_index(
- buffer->cache, line_index, buffer,
- &line_visible_callback, &set_pixmap_line_helper
- );
- }
- cache_pixmap *pix = cache_get_pixmap(buffer->cache, ll->cache_pixmap_index);
- /* FIXME: sensible default pixmap sizes here */
- if (pix->pixmap == None || pix->draw == NULL) {
- pix->pixmap = XCreatePixmap(
- buffer->common->dpy, buffer->window->drawable,
- ll->w + 10, ll->h + 10, buffer->common->depth
- );
- pix->w = ll->w + 10;
- pix->h = ll->h + 10;
- pix->draw = XftDrawCreate(
- buffer->common->dpy, pix->pixmap,
- buffer->common->vis, buffer->common->cm
- );
- } else if (pix->w < ll->w || pix->h < ll->h) {
- int new_w = ll->w > pix->w ? ll->w + 10 : pix->w + 10;
- int new_h = ll->h > pix->h ? ll->h + 10 : pix->h + 10;
- XFreePixmap(buffer->common->dpy, pix->pixmap);
- pix->pixmap = XCreatePixmap(
- buffer->common->dpy, buffer->window->drawable,
- new_w, new_h, buffer->common->depth
- );
- pix->w = new_w;
- pix->h = new_h;
- XftDrawChange(pix->draw, pix->pixmap);
- }
- XftDrawRect(pix->draw, &buffer->theme->text_bg, 0, 0, ll->w, ll->h);
- pango_xft_render_layout(pix->draw, &buffer->theme->text_fg, layout, 0, 0);
- ll->dirty = 0;
+ if (end_byte_ret)
+ *end_byte_ret = cur_index + text + len - last;
}
static void
init_line(ledit_buffer *buffer, ledit_line *line) {
- int text_w, text_h;
line->parent_buffer = buffer;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
line->gap = 0;
line->cap = 2; /* arbitrary */
line->text = ledit_malloc(line->cap);
line->text[0] = '\0';
line->len = 0;
- line->cache_pixmap_index = -1;
- line->cache_layout_index = -1;
- line->dirty = 1;
- line->text_dirty = 1;
- line->highlight_dirty = 1;
- line->h_dirty = 1;
- line->w = line->h = 0;
- line->cursor_index = -1;
- line->softlines = 1;
- line->w = text_w;
- line->h = 0;
- line->y_offset = 0;
}
void
-ledit_buffer_append_line(ledit_buffer *buffer, int line_index, int text_index) {
- ledit_buffer_append_line_base(buffer, line_index, text_index);
+ledit_buffer_append_line(ledit_buffer *buffer, size_t line_index, size_t text_index, int break_text) {
+ ledit_buffer_append_line_base(buffer, line_index, text_index, break_text);
ledit_buffer_recalc_from_line(buffer, line_index);
}
/* FIXME: error checking (index out of bounds, etc.) */
void
-ledit_buffer_append_line_base(ledit_buffer *buffer, int line_index, int text_index) {
- if (buffer->lines_num >= buffer->lines_cap) {
- buffer->lines_cap *= 2;
- if (buffer->lines_cap == 0)
- buffer->lines_cap = 2;
- buffer->lines = ledit_realloc(
- buffer->lines, buffer->lines_cap * sizeof(ledit_line)
- );
- }
- memmove(
- buffer->lines + line_index + 2,
- buffer->lines + line_index + 1,
- (buffer->lines_num - (line_index + 1)) * sizeof(ledit_line)
- );
+ledit_buffer_append_line_base(ledit_buffer *buffer, size_t line_index, size_t text_index, int break_text) {
+ size_t new_len = buffer->lines_num + 1;
+ if (new_len <= buffer->lines_num)
+ err_overflow();
+ size_t insert_index = line_index + 1;
+ if (insert_index <= line_index)
+ err_overflow();
+ resize_and_move_line_gap(buffer, new_len, insert_index);
buffer->lines_num++;
+ buffer->lines_gap++;
ledit_line *new_l = ledit_buffer_get_line(buffer, line_index + 1);
init_line(buffer, new_l);
- if (text_index != -1) {
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_notify_append_line(buffer->views[i], line_index);
+ }
+ if (break_text) {
ledit_line *l = ledit_buffer_get_line(buffer, line_index);
ledit_buffer_insert_text_from_line_base(
buffer, line_index + 1, 0,
- line_index, text_index, -1, NULL
+ line_index, text_index, l->len - text_index, NULL
);
- delete_line_section_base(
+ ledit_buffer_delete_line_section_base(
buffer, line_index,
text_index, l->len - text_index
);
}
}
-/* this is very weird and ugly with the recalc */
+/* FIXME: set offset to 0 when recalculating first line? */
void
-ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int index2) {
+ledit_buffer_delete_line_entries(ledit_buffer *buffer, size_t index1, size_t index2) {
ledit_buffer_delete_line_entries_base(buffer, index1, index2);
ledit_buffer_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0);
}
-static void
-set_pixmap_line_helper(void *data, int line, int index) {
- ledit_line *ll = ledit_buffer_get_line((ledit_buffer *)data, line);
- ll->cache_pixmap_index = index;
-}
-
-static void
-set_layout_line_helper(void *data, int line, int index) {
- ledit_line *ll = ledit_buffer_get_line((ledit_buffer *)data, line);
- ll->cache_layout_index = index;
-}
-
/* IMPORTANT: ledit_buffer_recalc_from_line needs to be called sometime after this! */
void
-ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int index2) {
+ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t index2) {
ledit_line *l;
- /* FIXME: make sure this is always true */
- /* FIXME: Ummm... what is that assert supposed to do? */
+ assert (index2 >= index1);
+ /* it isn't allowed to delete all lines */
assert(index2 - index1 != buffer->lines_num);
- cache_invalidate_from_line(
- buffer->cache, index1, buffer,
- &set_pixmap_line_helper, &set_layout_line_helper
- );
- for (int i = index1; i <= index2; i++) {
+ for (size_t i = index1; i <= index2; i++) {
l = ledit_buffer_get_line(buffer, i);
free(l->text);
}
- /* FIXME: gap buffer */
- if (index2 < buffer->lines_num - 1) {
- memmove(
- buffer->lines + index1, buffer->lines + index2 + 1,
- (buffer->lines_num - index2 - 1) * sizeof(ledit_line)
- );
- }
+ move_line_gap(buffer, index1);
buffer->lines_num -= index2 - index1 + 1;
- /* force a recalc of the lines */
- if (index1 == 0) {
- l = ledit_buffer_get_line(buffer, 0);
- l->y_offset = 0;
- l->h_dirty = 1;
- } else {
- l = ledit_buffer_get_line(buffer, index1 - 1);
- l->h_dirty = 1;
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_notify_delete_lines(buffer->views[i], index1, index2);
}
}
void
-ledit_buffer_delete_line_entry(ledit_buffer *buffer, int index) {
+ledit_buffer_delete_line_entry(ledit_buffer *buffer, size_t index) {
ledit_buffer_delete_line_entries(buffer, index, index);
}
void
-ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int index) {
+ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, size_t index) {
ledit_buffer_delete_line_entries_base(buffer, index, index);
}
-/* FIXME: use some sort of gap buffer (that would make this function
- slightly more useful...) */
ledit_line *
-ledit_buffer_get_line(ledit_buffer *buffer, int index) {
- assert(index >= 0 && index < buffer->lines_num);
- return &buffer->lines[index];
+ledit_buffer_get_line(ledit_buffer *buffer, size_t index) {
+ assert(index < buffer->lines_num);
+ return index < buffer->lines_gap ?
+ &buffer->lines[index] :
+ &buffer->lines[index + buffer->lines_cap - buffer->lines_num];
}
-/* set text of pango layout if dirty and recalculate height of line
- * - if height hasn't changed, nothing further is done
- * - if height has changed, offset of all following lines is changed */
void
-ledit_buffer_recalc_line(ledit_buffer *buffer, int line) {
- ledit_line *l = ledit_buffer_get_line(buffer, line);
- if (l->text_dirty)
- set_pango_text_and_highlight(buffer, line);
-
- /* if height changed, set height of current line
- * and adjust offsets of all lines following it */
- if (l->h_dirty) {
- l->h_dirty = 0;
- long off = l->y_offset + l->h;
- for (int i = line + 1; i < buffer->lines_num; i++) {
- l = ledit_buffer_get_line(buffer, i);
- l->y_offset = off;
- off += l->h;
- }
- buffer->total_height = off;
+ledit_buffer_recalc_line(ledit_buffer *buffer, size_t line) {
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_recalc_line(buffer->views[i], line);
}
}
-/* set text of pango layout and recalculate height
- * and offset for all lines starting at 'line' */
void
-ledit_buffer_recalc_from_line(ledit_buffer *buffer, int line) {
- ledit_line *l = ledit_buffer_get_line(buffer, line);
- long off = l->y_offset;
- for (int i = line; i < buffer->lines_num; i++) {
- l = ledit_buffer_get_line(buffer, i);
- if (l->text_dirty)
- set_pango_text_and_highlight(buffer, i);
- l->h_dirty = 0;
- l->y_offset = off;
- off += l->h;
+ledit_buffer_recalc_from_line(ledit_buffer *buffer, size_t line) {
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_recalc_from_line(buffer->views[i], line);
}
- buffer->total_height = off;
}
-/* short for 'ledit_buffer_recalc_from_line' starting at line 0 */
void
ledit_buffer_recalc_all_lines(ledit_buffer *buffer) {
- /* force first line to offset 0, just in case */
- ledit_line *l = ledit_buffer_get_line(buffer, 0);
- l->y_offset = 0;
- ledit_buffer_recalc_from_line(buffer, 0);
-}
-
-int
-ledit_buffer_line_visible(ledit_buffer *buffer, int index) {
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- ledit_line *l = ledit_buffer_get_line(buffer, index);
- return l->y_offset < buffer->display_offset + text_h &&
- l->y_offset + l->h > buffer->display_offset;
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_recalc_all_lines(buffer->views[i]);
+ }
}
-/* get needed length of text range, including newlines
- * - NUL is not included
- * - if the last range ends at the end of a line, the newline is *not* included
- * - the range must be sorted already */
size_t
-ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2) {
+ledit_buffer_textlen(ledit_buffer *buffer, size_t line1, size_t byte1, size_t line2, size_t byte2) {
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
size_t len = 0;
ledit_line *ll = ledit_buffer_get_line(buffer, line1);
@@ -896,7 +639,7 @@ ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int
} else {
/* + 1 for newline */
len = ll->len - byte1 + byte2 + 1;
- for (int i = line1 + 1; i < line2; i++) {
+ for (size_t i = line1 + 1; i < line2; i++) {
ll = ledit_buffer_get_line(buffer, i);
len += ll->len + 1;
}
@@ -910,10 +653,6 @@ ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int
only done when it is re-rendered (and thus normalized because
of pango's requirements). If a more efficient rendering
backend is added, it would be good to optimize this, though. */
-/* copy text range into given buffer
- * - dst is null-terminated
- * - dst must be large enough to contain the text and NUL
- * - the range must be sorted already */
void
ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2) {
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
@@ -944,17 +683,12 @@ ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, in
}
}
-/* copy text range into given buffer and resize it if necessary
- * - *dst is reallocated and *alloc adjusted if the text doesn't fit
- * - *dst is null-terminated
- * - the range must be sorted already
- * - returns the length of the text, not including the NUL */
void
ledit_buffer_copy_text_to_txtbuf(
ledit_buffer *buffer,
txtbuf *buf,
- int line1, int byte1,
- int line2, int byte2) {
+ size_t line1, size_t byte1,
+ size_t line2, size_t byte2) {
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
size_t len = ledit_buffer_textlen(buffer, line1, byte1, line2, byte2);
txtbuf_grow(buf, len + 1);
@@ -965,11 +699,11 @@ ledit_buffer_copy_text_to_txtbuf(
/* get char with logical index i from line */
#define LINE_CHAR(line, i) ((i) < (line)->gap ? (line)->text[i] : (line)->text[i + (line)->cap - (line)->len])
-int
-ledit_line_prev_utf8(ledit_line *line, int index) {
+size_t
+ledit_line_prev_utf8(ledit_line *line, size_t index) {
if (index <= 0)
return 0;
- int i = index - 1;
+ size_t i = index - 1;
/* find valid utf8 char - this probably needs to be improved */
/* FIXME: don't go off end or beginning */
while (i > 0 && ((LINE_CHAR(line, i) & 0xC0) == 0x80))
@@ -977,11 +711,11 @@ ledit_line_prev_utf8(ledit_line *line, int index) {
return i;
}
-int
-ledit_line_next_utf8(ledit_line *line, int index) {
+size_t
+ledit_line_next_utf8(ledit_line *line, size_t index) {
if (index >= line->len)
return line->len;
- int i = index + 1;
+ size_t i = index + 1;
while (i < line->len && ((LINE_CHAR(line, i) & 0xC0) == 0x80))
i++;
return i;
@@ -990,11 +724,11 @@ ledit_line_next_utf8(ledit_line *line, int index) {
/* Warning: this is very inefficient! */
/* FIXME: at least attempt to be more efficient by starting from the beginning
or end based on approximately where in the line the byte is */
-static int
-line_byte_to_char(ledit_line *line, int byte) {
- int c = 0;
- int i = 0;
- int b = byte > line->len ? line->len : byte; /* maybe not necessary */
+size_t
+line_byte_to_char(ledit_line *line, size_t byte) {
+ size_t c = 0;
+ size_t i = 0;
+ size_t b = byte > line->len ? line->len : byte; /* maybe not necessary */
while (i < b) {
c++;
i = ledit_line_next_utf8(line, i);
@@ -1002,466 +736,8 @@ line_byte_to_char(ledit_line *line, int byte) {
return c;
}
-int
-ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int num) {
- int nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- int c = line_byte_to_char(ll, byte);
- int cur_byte = byte;
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- for (int i = 0; i < num; i++) {
- cur_byte = ledit_line_next_utf8(ll, byte);
- for (c++; c < nattrs; c++) {
- if (attrs[c].is_cursor_position)
- break;
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- if (cur_byte >= ll->len)
- break;
- }
- return cur_byte <= ll->len ? cur_byte : ll->len;
-}
-
-int
-ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num) {
- int nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- int c = line_byte_to_char(ll, byte);
- int cur_byte = byte;
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- for (int i = 0; i < num; i++) {
- cur_byte = ledit_line_prev_utf8(ll, cur_byte);
- for (c--; c >= 0; c--) {
- if (attrs[c].is_cursor_position)
- break;
- cur_byte = ledit_line_prev_utf8(ll, cur_byte);
- }
- if (cur_byte <= 0)
- break;
- }
- return cur_byte > 0 ? cur_byte : 0;
-}
-
-static int
-line_next_word(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- int cur_byte = wrapped_line ? byte : ledit_line_next_utf8(ll, byte);
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- for (int i = wrapped_line ? c : c + 1; i < nattrs; i++) {
- if (attrs[i].is_word_start) {
- if (char_ret)
- *char_ret = i;
- if (real_byte_ret)
- *real_byte_ret = cur_byte;
- return cur_byte;
- }
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-static int
-line_prev_word(ledit_buffer *buffer, int line, int byte, int char_index, int *char_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- int cur_byte = ledit_line_prev_utf8(ll, byte);
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- if (c > nattrs)
- return -1;
- for (int i = c - 1; i >= 0; i--) {
- if (attrs[i].is_word_start) {
- if (char_ret)
- *char_ret = i;
- return cur_byte;
- }
- cur_byte = ledit_line_prev_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-static int
-line_prev_bigword(ledit_buffer *buffer, int line, int byte, int char_index, int *char_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- int cur_byte = ledit_line_prev_utf8(ll, byte);
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- int next_cursorb = byte;
- int next_cursorc = c;
- int found_word = 0;
- for (int i = c - 1; i >= 0; i--) {
- if (!found_word && !attrs[i].is_white) {
- found_word = 1;
- } else if (found_word && attrs[i].is_white) {
- if (char_ret)
- *char_ret = next_cursorc;
- return next_cursorb;
- }
- if (found_word && c == 0) {
- if (char_ret)
- *char_ret = 0;
- return 0;
- }
- if (attrs[i].is_cursor_position) {
- next_cursorc = i;
- next_cursorb = cur_byte;
- }
- cur_byte = ledit_line_prev_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-int
-line_next_bigword_end(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- int last_cursorb, last_cursorc;
- if (wrapped_line) {
- last_cursorb = byte;
- last_cursorc = c;
- } else {
- last_cursorb = -1;
- last_cursorc = -1;
- }
- int found_word = 0;
- int cur_byte = byte;
- for (int i = c; i < nattrs; i++) {
- if (last_cursorb != -1 && !found_word && !attrs[i].is_white) {
- found_word = 1;
- } else if (found_word && attrs[i].is_white) {
- if (char_ret)
- *char_ret = last_cursorc;
- if (real_byte_ret)
- *real_byte_ret = cur_byte;
- return last_cursorb;
- }
- if (attrs[i].is_cursor_position) {
- last_cursorc = i;
- last_cursorb = cur_byte;
- }
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-static int
-line_next_word_end(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- int cur_byte = ledit_line_next_utf8(ll, byte);
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- int last_cursorb, last_cursorc;
- if (wrapped_line) {
- last_cursorb = byte;
- last_cursorc = c;
- } else {
- last_cursorb = -1;
- last_cursorc = -1;
- }
- for (int i = c + 1; i < nattrs; i++) {
- if (last_cursorb != -1 && attrs[i].is_word_end) {
- if (char_ret)
- *char_ret = last_cursorc;
- if (real_byte_ret)
- *real_byte_ret = cur_byte;
- return last_cursorb;
- }
- if (attrs[i].is_cursor_position) {
- last_cursorc = i;
- last_cursorb = cur_byte;
- }
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-static int
-line_next_bigword(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (char_index >= 0)
- c = char_index;
- else
- c = line_byte_to_char(ll, byte);
- int cur_byte = byte;
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- int found_ws = wrapped_line;
- for (int i = c; i < nattrs; i++) {
- if (!found_ws && attrs[i].is_white) {
- found_ws = 1;
- } else if (found_ws && !attrs[i].is_white) {
- if (char_ret)
- *char_ret = i;
- if (real_byte_ret)
- *real_byte_ret = cur_byte;
- return cur_byte;
- }
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- return -1;
-}
-
-int
-ledit_line_next_non_whitespace(ledit_buffer *buffer, int line, int byte) {
- int c, nattrs;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- c = line_byte_to_char(ll, byte);
- int cur_byte = byte;
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- for (; c < nattrs; c++) {
- if (!attrs[c].is_white)
- return cur_byte;
- cur_byte = ledit_line_next_utf8(ll, cur_byte);
- }
- return ll->len;
-}
-
-/* FIXME: document that word and bigword are a bit weird because word uses unicode semantics */
-
-#define GEN_NEXT_WORD(name, func) \
-void \
-ledit_buffer_next_##name( \
- ledit_buffer *buffer, \
- int line, int byte, int num_repeat, \
- int *line_ret, int *byte_ret, int *real_byte_ret) { \
- int cur_line = line; \
- int cur_byte = byte; \
- int cur_char = -1; \
- int real_byte = -1; \
- int wrapped_line; \
- for (int i = 0; i < num_repeat; i++) { \
- wrapped_line = 0; \
- while ((cur_byte = func(buffer, cur_line, cur_byte, cur_char, \
- wrapped_line, &cur_char, &real_byte)) == -1 && \
- cur_line < buffer->lines_num - 1) { \
- cur_line++; \
- cur_byte = 0; \
- wrapped_line = 1; \
- } \
- if (cur_byte == -1 && cur_line == buffer->lines_num - 1) \
- break; \
- } \
- if (cur_byte == -1) { \
- *line_ret = buffer->lines_num - 1; \
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->lines_num - 1); \
- *byte_ret = ledit_buffer_get_legal_normal_pos(buffer, buffer->lines_num - 1, ll->len); \
- *real_byte_ret = ll->len; \
- } else { \
- *line_ret = cur_line; \
- *byte_ret = cur_byte; \
- *real_byte_ret = real_byte; \
- } \
-}
-
-#define GEN_PREV_WORD(name, func) \
-void \
-ledit_buffer_prev_##name( \
- ledit_buffer *buffer, \
- int line, int byte, int num_repeat, \
- int *line_ret, int *byte_ret, int *real_byte_ret) { \
- int cur_line = line; \
- int cur_byte = byte; \
- int cur_char = -1; \
- ledit_line *ll = ledit_buffer_get_line(buffer, cur_line); \
- for (int i = 0; i < num_repeat; i++) { \
- while ((cur_byte = func(buffer, cur_line, cur_byte, cur_char, \
- &cur_char)) == -1 && cur_line > 0) { \
- cur_line--; \
- ll = ledit_buffer_get_line(buffer, cur_line); \
- cur_byte = ll->len; \
- } \
- if (cur_byte == -1 && cur_line == 0) \
- break; \
- } \
- if (cur_byte == -1) { \
- *line_ret = 0; \
- *byte_ret = 0; \
- *real_byte_ret = 0; \
- } else { \
- *line_ret = cur_line; \
- *byte_ret = cur_byte; \
- *real_byte_ret = cur_byte; \
- } \
-}
-
-GEN_NEXT_WORD(word, line_next_word)
-GEN_NEXT_WORD(word_end, line_next_word_end)
-GEN_NEXT_WORD(bigword, line_next_bigword)
-GEN_NEXT_WORD(bigword_end, line_next_bigword_end)
-GEN_PREV_WORD(word, line_prev_word)
-GEN_PREV_WORD(bigword, line_prev_bigword)
-
-void
-ledit_buffer_get_pos_softline_bounds(
- ledit_buffer *buffer, int line, int pos,
- int *start_byte_ret, int *end_byte_ret) {
- assert(line >= 0 && line < buffer->lines_num);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- assert(pos >= 0 && pos <= ll->len);
- PangoLayout *layout = get_pango_layout(buffer, line);
- int x, sli;
- pango_layout_index_to_line_x(layout, pos, 0, &sli, &x);
- PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, sli);
- *start_byte_ret = pl->start_index;
- *end_byte_ret = pl->start_index + pl->length;
-}
-
-void
-ledit_buffer_get_softline_bounds(
- ledit_buffer *buffer, int line, int softline,
- int *start_byte_ret, int *end_byte_ret) {
- assert(line >= 0 && line < buffer->lines_num);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- assert(softline < ll->softlines);
- PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, softline);
- *start_byte_ret = pl->start_index;
- *end_byte_ret = pl->start_index + pl->length;
-}
-
-int
-ledit_buffer_get_softline_count(ledit_buffer *buffer, int line) {
- assert(line >= 0 && line < buffer->lines_num);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (ll->text_dirty)
- set_pango_text_and_highlight(buffer, line);
- return ll->softlines;
-}
-
-int
-ledit_buffer_pos_to_softline(ledit_buffer *buffer, int line, int pos) {
- assert(line >= 0 && line < buffer->lines_num);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- assert(pos >= 0 && pos <= ll->len);
- PangoLayout *layout = get_pango_layout(buffer, line);
- int x, sli;
- pango_layout_index_to_line_x(layout, pos, 0, &sli, &x);
- return sli;
-}
-
-void
-ledit_buffer_get_cursor_pixel_pos(ledit_buffer *buffer, int line, int pos, int *x_ret, int *y_ret, int *h_ret) {
- assert(line >= 0 && line < buffer->lines_num);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- assert(pos >= 0 && pos <= ll->len);
- PangoLayout *layout = get_pango_layout(buffer, line);
- PangoRectangle strong, weak;
- pango_layout_get_cursor_pos(layout, 0, &strong, &weak);
- *x_ret = strong.x / PANGO_SCALE;
- *y_ret = strong.y / PANGO_SCALE;
- *h_ret = strong.height / PANGO_SCALE;
-}
-
-/* prev_index_ret is used instead of just calling get_legal_normal_pos
- because weird things happen otherwise
- -> in certain cases, this is still weird because prev_index_ret sometimes
- is not at the end of the line, but this is the best I could come up
- with for now */
-int
-ledit_buffer_move_cursor_visually(ledit_buffer *buffer, int line, int pos, int movement, int *prev_index_ret) {
- /* FIXME: trailing */
- int trailing = 0, tmp_index;
- ledit_line *cur_line = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- int new_index = pos, last_index = pos;
- int dir = 1;
- int num = movement;
- if (movement < 0) {
- dir = -1;
- num = -movement;
- }
- while (num > 0) {
- tmp_index = new_index;
- pango_layout_move_cursor_visually(
- layout, TRUE,
- new_index, trailing, dir,
- &new_index, &trailing
- );
- /* for some reason, this is necessary */
- if (new_index < 0)
- new_index = 0;
- else if (new_index > cur_line->len)
- new_index = cur_line->len;
- num--;
- if (tmp_index != new_index)
- last_index = tmp_index;
- }
- /* FIXME: Allow cursor to be at end of soft line */
- /* we don't currently support a difference between the cursor being at
- the end of a soft line and the beginning of the next line */
- /* FIXME: spaces at end of softlines are weird in normal mode */
- while (trailing > 0) {
- trailing--;
- new_index = ledit_line_next_utf8(cur_line, new_index);
- }
- if (new_index < 0)
- new_index = 0;
- if (prev_index_ret)
- *prev_index_ret = last_index;
- return new_index;
-}
-
-/* FIXME: implement */
-/*
-int
-ledit_line_nearest_cursor_pos(ledit_line *line, int byte) {
-}
-
void
-ledit_line_word_boundaries(ledit_line *line, int byte, int *start_ret, int *end_ret) {
-}
-*/
-
-/* FIXME: no idea why this exists */
-/*
-static void
-delete_line_section(ledit_buffer *buffer, int line, int start, int length) {
- delete_line_section_base(buffer, line, start, length);
- ledit_buffer_recalc_line(buffer, line);
-}
-*/
-
-static void
-delete_line_section_base(ledit_buffer *buffer, int line, int start, int length) {
+ledit_buffer_delete_line_section_base(ledit_buffer *buffer, size_t line, size_t start, size_t length) {
ledit_line *l = ledit_buffer_get_line(buffer, line);
if (start <= l->gap && start + length >= l->gap) {
l->gap = start;
@@ -1480,1092 +756,29 @@ delete_line_section_base(ledit_buffer *buffer, int line, int start, int length)
);
}
l->len -= length;
- l->dirty = 1;
- l->text_dirty = 1;
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_notify_delete_text(buffer->views[i], line, start, length);
+ }
}
-int
-ledit_buffer_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, int dir) {
- int new_index = ledit_buffer_delete_unicode_char_base(buffer, line_index, byte_index, dir);
+size_t
+ledit_buffer_delete_unicode_char(ledit_buffer *buffer, size_t line_index, size_t byte_index, int dir) {
+ size_t new_index = ledit_buffer_delete_unicode_char_base(buffer, line_index, byte_index, dir);
ledit_buffer_recalc_line(buffer, line_index);
return new_index;
}
-int
-ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int byte_index, int dir) {
+size_t
+ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, size_t line_index, size_t byte_index, int dir) {
ledit_line *l = ledit_buffer_get_line(buffer, line_index);
- int new_index = byte_index;
+ size_t new_index = byte_index;
if (dir < 0) {
- int i = ledit_line_prev_utf8(l, byte_index);
- delete_line_section_base(buffer, line_index, i, byte_index - i);
+ size_t i = ledit_line_prev_utf8(l, byte_index);
+ ledit_buffer_delete_line_section_base(buffer, line_index, i, byte_index - i);
new_index = i;
} else {
- int i = ledit_line_next_utf8(l, byte_index);
- delete_line_section_base(buffer, line_index, byte_index, i - byte_index);
+ size_t i = ledit_line_next_utf8(l, byte_index);
+ ledit_buffer_delete_line_section_base(buffer, line_index, byte_index, i - byte_index);
}
return new_index;
}
-
-static void
-set_pango_text_and_highlight(ledit_buffer *buffer, int line) {
- cache_layout *cl;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- int old_index = ll->cache_layout_index;
- if (ll->cache_layout_index < 0) {
- cache_assign_layout_index(
- ll->parent_buffer->cache, line,
- ll->parent_buffer, &set_layout_line_helper
- );
- assert(ll->cache_layout_index >= 0);
- cl = cache_get_layout(ll->parent_buffer->cache, ll->cache_layout_index);
- if (cl->layout == NULL) {
- cl->layout = pango_layout_new(ll->parent_buffer->window->context);
- pango_layout_set_font_description(cl->layout, buffer->window->font);
- pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR);
- }
- } else {
- cl = cache_get_layout(ll->parent_buffer->cache, ll->cache_layout_index);
- }
- if (ll->text_dirty || old_index < 0) {
- ledit_buffer_normalize_line(ll);
- pango_layout_set_text(cl->layout, ll->text, ll->len);
- set_line_layout_attrs(buffer, line, cl->layout);
- /* FIXME: is this guard necessary? */
- ll->softlines = ll->len > 0 ? pango_layout_get_line_count(cl->layout) : 1;
- pango_layout_set_width(cl->layout, ll->w * PANGO_SCALE);
- int w, h;
- pango_layout_get_pixel_size(cl->layout, &w, &h);
- if (h != ll->h) {
- ll->h = h;
- ll->h_dirty = 1;
- }
- ll->text_dirty = 0;
- ll->dirty = 1;
- } else if (ll->highlight_dirty) {
- set_line_layout_attrs(buffer, line, cl->layout);
- }
- ll->highlight_dirty = 0;
-}
-
-static PangoLayout *
-get_pango_layout(ledit_buffer *buffer, int line) {
- set_pango_text_and_highlight(buffer, line);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- cache_layout *cl = cache_get_layout(
- ll->parent_buffer->cache, ll->cache_layout_index
- );
- return cl->layout;
-}
-
-void
-ledit_pos_to_x_softline(ledit_buffer *buffer, int line, int pos, int *x_ret, int *softline_ret) {
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- pango_layout_index_to_line_x(layout, pos, 0, softline_ret, x_ret);
- /* FIXME: do these lines need to be unref'd? */
- PangoLayoutLine *pango_line = pango_layout_get_line_readonly(layout, *softline_ret);
- /* add left margin to x position if line is aligned right */
- if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
- PangoRectangle rect;
- pango_layout_line_get_extents(pango_line, NULL, &rect);
- *x_ret += (ll->w * PANGO_SCALE - rect.width);
- }
- /* if in normal mode, change position to the middle of the
- current rectangle so that moving around won't jump weirdly */
- /* FIXME: also in visual? */
- /* FIXME: this is too much magic for my taste */
- if (ll->parent_buffer->common->mode == NORMAL) {
- PangoRectangle rect;
- pango_layout_index_to_pos(layout, pos, &rect);
- *x_ret += rect.width / 2;
- }
-}
-
-/* FIXME: change this to return int */
-void
-ledit_x_softline_to_pos(ledit_buffer *buffer, int line, int x, int softline, int *pos_ret) {
- int trailing = 0;
- int x_relative = x;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- PangoLayoutLine *pango_line =
- pango_layout_get_line_readonly(layout, softline);
- /* x is absolute, so the margin at the left needs to be subtracted */
- if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
- PangoRectangle rect;
- pango_layout_line_get_extents(pango_line, NULL, &rect);
- x_relative -= (ll->w * PANGO_SCALE - rect.width);
- }
- pango_layout_line_x_to_index(
- pango_line, x_relative, pos_ret, &trailing
- );
- /* if in insert mode, snap to the nearest border between graphemes */
- /* FIXME: add parameter for this instead of checking mode */
- if (ll->parent_buffer->common->mode == INSERT) {
- while (trailing > 0) {
- trailing--;
- *pos_ret = ledit_line_next_utf8(ll, *pos_ret);
- }
- }
-}
-
-int
-ledit_buffer_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) {
- /* move back one grapheme if at end of line */
- int ret = pos;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- if (pos == ll->len && pos > 0) {
- int nattrs;
- PangoLayout *layout = get_pango_layout(buffer, line);
- const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(layout, &nattrs);
- int cur = nattrs - 2;
- ret = ledit_line_prev_utf8(ll, ret);
- while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) {
- cur--;
- ret = ledit_line_prev_utf8(ll, ret);
- }
- }
- return ret;
-}
-
-void
-ledit_buffer_delete_range(
- ledit_buffer *buffer, enum delete_mode delmode,
- int line_index1, int byte_index1,
- int line_index2, int byte_index2,
- int *new_line_ret, int *new_byte_ret,
- ledit_range *final_range_ret, txtbuf *text_ret) {
- ledit_buffer_delete_range_base(
- buffer, delmode,
- line_index1, byte_index1,
- line_index2, byte_index2,
- new_line_ret, new_byte_ret,
- final_range_ret, text_ret
- );
- /* need to start recalculating one line before in case first
- line was deleted and offset is now wrong */
- int min = line_index1 < line_index2 ? line_index1 : line_index2;
- ledit_buffer_recalc_from_line(buffer, min > 0 ? min - 1 : min);
-}
-
-/* Note: line_index* and byte_index* don't need to be sorted */
-/* line_index1, byte_index1 are used as the cursor position in order
- to determine the new cursor position */
-/* FIXME: use at least somewhat sensible variable names */
-/* FIXME: I once noticed a bug where using 'dG' to delete to the end of
- the file caused a line index way larger than buffer->lines_num to be
- given, but I couldn't reproduce this bug */
-void
-ledit_buffer_delete_range_base(
- ledit_buffer *buffer, enum delete_mode delmode,
- int line_index1, int byte_index1,
- int line_index2, int byte_index2,
- int *new_line_ret, int *new_byte_ret,
- ledit_range *final_range_ret, txtbuf *text_ret) {
- /* FIXME: Oh boy, this is nasty */
- /* range line x, range byte x */
- int rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0;
- int new_line = 0, new_byte = 0;
- assert(line_index1 >= 0);
- assert(line_index2 >= 0);
- assert(line_index1 < buffer->lines_num);
- assert(line_index2 < buffer->lines_num);
- /* FIXME: could this be simplified by just calculating the range and then using
- the non-line-based version? */
- if (delmode == DELETE_HARDLINE) {
- int x, sl_useless;
- int l1 = line_index1, l2 = line_index2;
- if (line_index1 > line_index2) {
- l1 = line_index2;
- l2 = line_index1;
- }
- int dell1 = l1, dell2 = l2;
- ledit_line *ll = ledit_buffer_get_line(buffer, line_index1);
- ledit_pos_to_x_softline(buffer, line_index1, byte_index1, &x, &sl_useless);
- if (l1 > 0 && l2 < buffer->lines_num - 1) {
- rgl1 = l1;
- rgb1 = 0;
- rgl2 = l2 + 1;
- rgb2 = 0;
- } else if (l1 > 0) {
- rgl1 = l1 - 1;
- ll = ledit_buffer_get_line(buffer, rgl1);
- rgb1 = ll->len;
- rgl2 = l2;
- ll = ledit_buffer_get_line(buffer, rgl2);
- rgb2 = ll->len;
- } else if (l2 < buffer->lines_num - 1) {
- rgl1 = l1;
- rgb1 = 0;
- rgl2 = l2 + 1;
- rgb2 = 0;
- } else {
- rgl1 = l1;
- rgb1 = 0;
- rgl2 = l2;
- ll = ledit_buffer_get_line(buffer, rgl2);
- rgb2 = ll->len;
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1, rgl2, rgb2
- );
- }
- /* default is dell1 = l1, dell2 = l2 */
- if (l2 < buffer->lines_num - 1) {
- new_line = l1;
- ledit_x_softline_to_pos(
- buffer, l2 + 1,
- x, 0, &new_byte
- );
- } else if (l1 > 0) {
- new_line = l1 - 1;
- ledit_x_softline_to_pos(
- buffer, l1 - 1,
- x, 0, &new_byte
- );
- } else {
- dell1 = l1 + 1;
- dell2 = l2;
- new_line = l1;
- new_byte = 0;
- /* happens when all lines are deleted, so one line has to be cleared */
- ll = ledit_buffer_get_line(buffer, l1);
- delete_line_section_base(
- buffer, l1, 0, ll->len
- );
- }
- if (dell1 <= dell2) {
- ledit_buffer_delete_line_entries_base(buffer, dell1, dell2);
- }
- } else if (delmode == DELETE_SOFTLINE) {
- int x, softline1, softline2;
- ledit_line *line1 = ledit_buffer_get_line(buffer, line_index1);
- ledit_pos_to_x_softline(buffer, line_index1, byte_index1, &x, &softline1);
- if (line_index1 == line_index2) {
- int x_useless;
- PangoLayout *layout = get_pango_layout(buffer, line_index1);
- pango_layout_index_to_line_x(layout, byte_index2, 0, &softline2, &x_useless);
- int l1 = softline1 < softline2 ? softline1 : softline2;
- int l2 = softline1 < softline2 ? softline2 : softline1;
- PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout, l1);
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout, l2);
- /* don't delete entire line if it is the last one remaining */
- if (l1 == 0 && l2 == line1->softlines - 1 && buffer->lines_num > 1) {
- if (line_index1 < buffer->lines_num - 1) {
- /* cursor can be moved to next hard line */
- new_line = line_index1;
- ledit_x_softline_to_pos(
- buffer, line_index1 + 1,
- x, 0, &new_byte
- );
- rgl1 = line_index1;
- rgb1 = 0;
- rgl2 = line_index1 + 1;
- rgb2 = 0;
- } else {
- /* cursor has to be be moved to previous hard line
- because last line in buffer is deleted */
- /* note: logically, line_index1 - 1 must be >= 0 because
- buffer->lines_num > 1 && line_index1 >= buffer->lines_num - 1 */
- new_line = line_index1 - 1;
- ledit_line *prevline = ledit_buffer_get_line(buffer, new_line);
- if (prevline->text_dirty)
- set_pango_text_and_highlight(buffer, new_line);
- ledit_x_softline_to_pos(buffer, new_line, x, prevline->softlines - 1, &new_byte);
- rgl1 = line_index1 - 1;
- rgb1 = prevline->len;
- rgl2 = line_index1;
- rgb2 = line1->len;
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- ledit_buffer_delete_line_entry_base(buffer, line_index1);
- } else {
- assert(pl2->start_index + pl2->length - pl1->start_index >= 0);
- rgl1 = rgl2 = line_index1;
- rgb1 = pl1->start_index;
- rgb2 = pl2->start_index + pl2->length;
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(
- buffer, line_index1, rgb1, rgb2 - rgb1
- );
- if (l2 == line1->softlines - 1 && line_index1 < buffer->lines_num - 1) {
- new_line = line_index1 + 1;
- ledit_x_softline_to_pos(
- buffer, line_index1 + 1,
- x, 0, &new_byte
- );
- } else if (l2 < line1->softlines - 1) {
- new_line = line_index1;
- ledit_x_softline_to_pos(
- buffer, line_index1,
- x, l1, &new_byte
- );
- } else if (l1 > 0) {
- new_line = line_index1;
- ledit_x_softline_to_pos(
- buffer, line_index1,
- x, l1 - 1, &new_byte
- );
- } else {
- /* the line has been emptied and is the last line remaining */
- new_line = 0;
- new_byte = 0;
- }
- }
- } else {
- int x_useless, sl1, sl2;
- int l1, l2, b1, b2;
- if (line_index1 < line_index2) {
- l1 = line_index1;
- b1 = byte_index1;
- l2 = line_index2;
- b2 = byte_index2;
- } else {
- l1 = line_index2;
- b1 = byte_index2;
- l2 = line_index1;
- b2 = byte_index1;
- }
- ledit_line *ll1 = ledit_buffer_get_line(buffer, l1);
- ledit_line *ll2 = ledit_buffer_get_line(buffer, l2);
- PangoLayout *layout1 = get_pango_layout(buffer, l1);
- PangoLayout *layout2 = get_pango_layout(buffer, l2);
- pango_layout_index_to_line_x(layout1, b1, 0, &sl1, &x_useless);
- pango_layout_index_to_line_x(layout2, b2, 0, &sl2, &x_useless);
- PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout1, sl1);
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout2, sl2);
- if (sl1 == 0 && sl2 == ll2->softlines - 1) {
- if (l1 == 0 && l2 == buffer->lines_num - 1) {
- rgl1 = l1;
- rgl2 = l2;
- rgb1 = 0;
- rgb2 = ll2->len;
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(buffer, l1, 0, ll1->len);
- ledit_buffer_delete_line_entries_base(buffer, l1 + 1, l2);
- new_line = 0;
- new_byte = 0;
- } else {
- if (l2 == buffer->lines_num - 1) {
- new_line = l1 - 1;
- ledit_line *new_lline = ledit_buffer_get_line(buffer, new_line);
- if (new_lline->text_dirty)
- set_pango_text_and_highlight(buffer, new_line);
- ledit_x_softline_to_pos(buffer, new_line, x, new_lline->softlines - 1, &new_byte);
- rgl1 = l1 - 1;
- rgb1 = new_lline->len;
- rgl2 = l2;
- rgb2 = ll2->len;
- } else {
- new_line = l1;
- ledit_x_softline_to_pos(
- buffer, l2 + 1, x, 0, &new_byte
- );
- rgl1 = l1;
- rgb1 = 0;
- rgl2 = l2 + 1;
- rgb2 = 0;
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- ledit_buffer_delete_line_entries_base(buffer, l1, l2);
- }
- } else if (sl1 == 0) {
- rgl1 = l1;
- rgb1 = 0;
- rgl2 = l2;
- rgb2 = pl2->start_index + pl2->length;
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(buffer, l2, 0, pl2->start_index + pl2->length);
- new_line = l1;
- ledit_x_softline_to_pos(buffer, l2, x, 0, &new_byte);
- ledit_buffer_delete_line_entries_base(buffer, l1, l2 - 1);
- } else if (sl2 == ll2->softlines - 1) {
- rgl1 = l1;
- rgb1 = pl1->start_index;
- rgl2 = l2;
- rgb2 = ll2->len;
- if (l2 + 1 == buffer->lines_num) {
- new_line = l1;
- ledit_x_softline_to_pos(buffer, l1, x, sl1 - 1, &new_byte);
- } else {
- new_line = l1 + 1;
- ledit_x_softline_to_pos(
- buffer, l2 + 1,
- x, 0, &new_byte
- );
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(buffer, l1, pl1->start_index, ll1->len - pl1->start_index);
- ledit_buffer_delete_line_entries_base(buffer, l1 + 1, l2);
- } else {
- /* FIXME: this could be made nicer by just using the range to
- delete all in one go at the end */
- rgl1 = l1;
- rgb1 = pl1->start_index;
- rgl2 = l2;
- rgb2 = pl2->start_index + pl2->length;
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(buffer, l1, pl1->start_index, ll1->len - pl1->start_index);
- ledit_buffer_insert_text_from_line_base(
- buffer,
- l1, pl1->start_index,
- l2, pl2->start_index + pl2->length,
- ll2->len - (pl2->start_index + pl2->length), NULL
- );
- ledit_buffer_delete_line_entries_base(buffer, l1 + 1, l2);
- new_line = l1;
- set_pango_text_and_highlight(buffer, l1);
- /* it's technically possible that the remaining part of the
- second line is so small that it doesn't generate a new
- softline, so there needs to be a special case - this is
- a bit weird because the cursor will seem to stay on the
- same line, but it now includes the rest of the second line
- (FIXME: this is probably not the best thing to do) */
- ledit_x_softline_to_pos(
- buffer, l1, x, sl1 + 1 < ll1->softlines ? sl1 + 1 : sl1, &new_byte
- );
- }
- }
- } else {
- if (line_index1 == line_index2) {
- rgl1 = rgl2 = line_index1;
- if (byte_index1 < byte_index2) {
- rgb1 = byte_index1;
- rgb2 = byte_index2;
- } else {
- rgb1 = byte_index2;
- rgb2 = byte_index1;
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- delete_line_section_base(buffer, line_index1, rgb1, rgb2 - rgb1);
- new_line = line_index1;
- new_byte = rgb1;
- } else {
- if (line_index1 < line_index2) {
- rgl1 = line_index1;
- rgb1 = byte_index1;
- rgl2 = line_index2;
- rgb2 = byte_index2;
- } else {
- rgl1 = line_index2;
- rgb1 = byte_index2;
- rgl2 = line_index1;
- rgb2 = byte_index1;
- }
- if (text_ret) {
- ledit_buffer_copy_text_to_txtbuf(
- buffer, text_ret,
- rgl1, rgb1,
- rgl2, rgb2
- );
- }
- ledit_line *line1 = ledit_buffer_get_line(buffer, rgl1);
- ledit_line *line2 = ledit_buffer_get_line(buffer, rgl2);
- delete_line_section_base(buffer, rgl1, rgb1, line1->len - rgb1);
- ledit_buffer_insert_text_from_line_base(
- buffer, rgl1, rgb1, rgl2, rgb2, line2->len - rgb2, NULL
- );
- new_line = rgl1;
- new_byte = rgb1;
- ledit_buffer_delete_line_entries_base(buffer, rgl1 + 1, rgl2);
- }
- if (buffer->common->mode == NORMAL)
- new_byte = ledit_buffer_get_legal_normal_pos(buffer, new_line, new_byte);
- }
- if (final_range_ret) {
- final_range_ret->line1 = rgl1;
- final_range_ret->byte1 = rgb1;
- final_range_ret->line2 = rgl2;
- final_range_ret->byte2 = rgb2;
- }
- if (new_line_ret)
- *new_line_ret = new_line;
- if (new_byte_ret)
- *new_byte_ret = new_byte;
-}
-
-/* FIXME: any way to make this more efficient? */
-void
-ledit_buffer_resize_textview(ledit_buffer *buffer) {
- buffer->total_height = 0;
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- for (int i = 0; i < buffer->lines_num; i++) {
- ledit_line *line = ledit_buffer_get_line(buffer, i);
- line->w = text_w;
- line->text_dirty = 1;
- set_pango_text_and_highlight(buffer, i);
- line->y_offset = buffer->total_height;
- line->dirty = 1;
- line->h_dirty = 0;
- buffer->total_height += line->h;
- }
- ledit_window_set_scroll_max(buffer->window, buffer->total_height);
- if (buffer->display_offset > 0 &&
- buffer->display_offset + text_h > buffer->total_height) {
- ledit_buffer_scroll(buffer, buffer->total_height - text_h);
- }
-}
-
-void
-ledit_buffer_scroll(ledit_buffer *buffer, long new_offset) {
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- if (new_offset + text_h > buffer->total_height)
- new_offset = buffer->total_height - text_h;
- if (new_offset < 0)
- new_offset = 0;
- buffer->display_offset = new_offset;
- ledit_window_set_scroll_pos(buffer->window, buffer->display_offset);
-}
-
-/* FIXME: there's gotta be a better/more efficient way to do this... */
-/* FIXME: make sure h_dirty is not set here */
-void
-ledit_buffer_get_nearest_legal_pos(
- ledit_buffer *buffer,
- int line, int byte,
- /*int snap_to_nearest, int snap_middle, FIXME: take these parameters */
- int *line_ret, int *byte_ret) {
- PangoRectangle strong, weak;
- int text_w, text_h;
- int x, sl_useless;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- ledit_line *lline = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- pango_layout_get_cursor_pos(layout, byte, &strong, &weak);
- ledit_pos_to_x_softline(buffer, line, byte, &x, &sl_useless);
- long cursor_y = strong.y / PANGO_SCALE + lline->y_offset;
- PangoRectangle ink, log;
- if (cursor_y < buffer->display_offset) {
- /* search for the hard line covering the top of the screen */
- int hline = line;
- while (lline->y_offset + lline->h <= buffer->display_offset && hline < buffer->lines_num - 1) {
- lline = ledit_buffer_get_line(buffer, ++hline);
- }
- /* the current hard line is now the one at the very top of the screen*/
- layout = get_pango_layout(buffer, hline);
- int num_sl = lline->softlines;
- int cur_y_off = 0;
- int sl_index = -1;
- PangoLayoutLine *sl;
- /* search for first soft line completely on-screen */
- for (int i = 0; i < num_sl; i++) {
- sl = pango_layout_get_line_readonly(layout, i);
- if (cur_y_off + lline->y_offset >= buffer->display_offset) {
- sl_index = i;
- break;
- }
- pango_layout_line_get_pixel_extents(sl, &ink, &log);
- cur_y_off += log.height;
- }
- if (sl_index >= 0) {
- /* we found the correct soft line */
- *line_ret = hline;
- ledit_x_softline_to_pos(buffer, hline, x, sl_index, byte_ret);
- } else if (hline < buffer->lines_num - 1) {
- /* need to move to next hard line */
- *line_ret = hline + 1;
- ledit_x_softline_to_pos(buffer, hline + 1, x, 0, byte_ret);
- } else {
- /* no idea if this can happen, but just fail and use
- the last soft line of the last hard line */
- *line_ret = hline;
- ledit_x_softline_to_pos(buffer, hline, x, num_sl - 1, byte_ret);
- }
- } else if (cursor_y + strong.height / PANGO_SCALE >
- buffer->display_offset + text_h) {
- /* search for the hard line covering the bottom of the screen */
- int hline = line;
- while (lline->y_offset > buffer->display_offset + text_h && hline > 0) {
- lline = ledit_buffer_get_line(buffer, --hline);
- }
- /* the current hard line is now the one at the very bottom of the screen*/
- layout = get_pango_layout(buffer, hline);
- int num_sl = lline->softlines;
- int cur_y_off = 0;
- int sl_index = -1;
- PangoLayoutLine *sl;
- /* search for last soft line completely on-screen */
- for (int i = num_sl - 1; i >= 0; i--) {
- sl = pango_layout_get_line_readonly(layout, i);
- if (lline->y_offset + lline->h - cur_y_off < buffer->display_offset + text_h) {
- sl_index = i;
- break;
- }
- pango_layout_line_get_pixel_extents(sl, &ink, &log);
- cur_y_off += log.height;
- }
- if (sl_index >= 0) {
- /* we found the correct soft line */
- *line_ret = hline;
- ledit_x_softline_to_pos(buffer, hline, x, sl_index, byte_ret);
- } else if (hline > 0) {
- /* need to move to previous hard line */
- *line_ret = hline - 1;
- lline = ledit_buffer_get_line(buffer, hline - 1);
- num_sl = lline->softlines;
- ledit_x_softline_to_pos(buffer, hline - 1, x, num_sl - 1, byte_ret);
- } else {
- /* no idea if this can happen, but just fail and use
- the first soft line of the first hard line */
- *line_ret = hline;
- ledit_x_softline_to_pos(buffer, hline, x, 0, byte_ret);
- }
- }
-}
-
-void
-ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int snap_to_nearest, int *line_ret, int *byte_ret) {
- /* FIXME: store current line offset to speed this up */
- /* FIXME: use y_offset in lines */
- long h = 0;
- double pos = buffer->display_offset + y;
- for (int i = 0; i < buffer->lines_num; i++) {
- ledit_line *line = ledit_buffer_get_line(buffer, i);
- if ((h <= pos && h + line->h > pos) || i == buffer->lines_num - 1) {
- int index, trailing;
- PangoLayout *layout = get_pango_layout(buffer, i);
- /* FIXME: what if i == buffer->lines_num - 1 but pos - h < 0? */
- pango_layout_xy_to_index(
- layout,
- x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
- &index, &trailing
- );
- if (snap_to_nearest) {
- while (trailing > 0) {
- trailing--;
- index = ledit_line_next_utf8(line, index);
- }
- }
- *line_ret = i;
- *byte_ret = index;
- break;
- }
- h += line->h;
- }
-}
-
-static void
-scroll_to_pos(ledit_buffer *buffer, int line, int byte, int top) {
- PangoRectangle strong, weak;
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
- PangoLayout *layout = get_pango_layout(buffer, line);
- pango_layout_get_cursor_pos(layout, byte, &strong, &weak);
- long cursor_y = strong.y / PANGO_SCALE + ll->y_offset;
- if (top) {
- ledit_buffer_scroll(buffer, cursor_y);
- } else {
- ledit_buffer_scroll(buffer, cursor_y - text_h + strong.height / PANGO_SCALE);
- }
-}
-
-void
-ledit_buffer_scroll_to_pos_top(ledit_buffer *buffer, int line, int byte) {
- scroll_to_pos(buffer, line, byte, 1);
-}
-
-void
-ledit_buffer_scroll_to_pos_bottom(ledit_buffer *buffer, int line, int byte) {
- scroll_to_pos(buffer, line, byte, 0);
-}
-
-void
-ledit_buffer_ensure_cursor_shown(ledit_buffer *buffer) {
- PangoRectangle strong, weak;
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- ledit_line *line = ledit_buffer_get_line(buffer, buffer->cur_line);
- PangoLayout *layout = get_pango_layout(buffer, buffer->cur_line);
- pango_layout_get_cursor_pos(
- layout, buffer->cur_index, &strong, &weak
- );
- long cursor_y = strong.y / PANGO_SCALE + line->y_offset;
- if (cursor_y < buffer->display_offset) {
- ledit_buffer_scroll(buffer, cursor_y);
- } else if (cursor_y + strong.height / PANGO_SCALE >
- buffer->display_offset + text_h) {
- ledit_buffer_scroll(buffer, cursor_y - text_h + strong.height / PANGO_SCALE);
- }
-}
-
-static void
-swap(int *a, int *b) {
- int tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-void
-ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte2) {
- if (*line1 > *line2) {
- swap(line1, line2);
- swap(byte1, byte2);
- } else if (*line1 == *line2 && *byte1 > *byte2) {
- swap(byte1, byte2);
- }
-}
-
-/* FIXME: don't reset selection when selection is clicked away */
-/* FIXME: when selecting with mouse, only call this when button is released */
-/* lines and bytes need to be sorted already! */
-static void
-copy_selection_to_x_primary(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2) {
- /* FIXME: let window handle this */
- txtbuf *primary = ledit_window_get_primary_clipboard_buffer();
- ledit_buffer_copy_text_to_txtbuf(buffer, primary, line1, byte1, line2, byte2);
- XSetSelectionOwner(buffer->common->dpy, XA_PRIMARY, buffer->window->xwin, CurrentTime);
- /*
- FIXME
- if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win)
- selclear();
- */
-}
-
-void
-ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2) {
- if (line1 == buffer->sel.line1 && line2 == buffer->sel.line2 &&
- byte1 == buffer->sel.byte1 && byte2 == buffer->sel.byte2) {
- return;
- }
- /* FIXME: maybe check both lines and bytes? */
- if (buffer->sel.line1 >= 0 || line1 >= 0) {
- int l1_new = line1, l2_new = line2;
- int b1_new = byte1, b2_new = byte2;
- ledit_buffer_sort_selection(&l1_new, &b1_new, &l2_new, &b2_new);
- ledit_buffer_sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2);
- /* FIXME: make this a bit nicer and optimize it */
- if (buffer->sel.line1 > l2_new || buffer->sel.line2 < l1_new) {
- for (int i = buffer->sel.line1; i <= buffer->sel.line2; i++) {
- if (i >= 0)
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
- }
- } else {
- for (int i = buffer->sel.line1; i < l1_new; i++) {
- if (i >= 0)
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
- }
- for (int i = buffer->sel.line2; i > l2_new; i--) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
- }
- }
- if (l1_new >= 0 && l2_new >= 0) {
- for (int i = l1_new; i <= l2_new; i++) {
- /* only change the ones that were not already selected */
- if (i <= buffer->sel.line1 || i >= buffer->sel.line2) {
- ledit_line *ll = ledit_buffer_get_line(buffer, i);
- ll->highlight_dirty = 1;
- }
- }
- if (l1_new != l2_new || b1_new != b2_new)
- copy_selection_to_x_primary(buffer, l1_new, b1_new, l2_new, b2_new);
- }
- }
- buffer->sel.line1 = line1;
- buffer->sel.byte1 = byte1;
- buffer->sel.line2 = line2;
- buffer->sel.byte2 = byte2;
-}
-
-void
-ledit_buffer_scroll_handler(void *buffer, long pos) {
- ((ledit_buffer *)buffer)->display_offset = pos;
-}
-
-void
-ledit_buffer_button_handler(void *data, XEvent *event) {
- int l, b;
- ledit_buffer *buffer = (ledit_buffer *)data;
- int x = event->xbutton.x;
- int y = event->xbutton.y;
- int snap;
- switch (event->type) {
- case ButtonPress:
- snap = buffer->common->mode == NORMAL ? 0 : 1;
- ledit_xy_to_line_byte(buffer, x, y, snap, &l, &b);
- buffer->selecting = 1;
- if (buffer->common->mode == NORMAL)
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- buffer->cur_line = l;
- buffer->cur_index = b;
- /* don't set selection yet because the mouse may not be
- dragged, so we don't want to switch to visual (this
- allows setting just the cursor position in normal mode
- without always switching to visual) */
- ledit_buffer_set_selection(buffer, -1, -1, -1, -1);
- if (buffer->common->mode == NORMAL)
- ledit_buffer_set_line_cursor_attrs(buffer, l, b);
- break;
- case ButtonRelease:
- buffer->selecting = 0;
- break;
- case MotionNotify:
- if (buffer->selecting) {
- y = y >= 0 ? y : 0;
- ledit_xy_to_line_byte(buffer, x, y, 1, &l, &b);
- if (buffer->common->mode == NORMAL) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- /* FIXME: return to old mode afterwards? */
- /* should change_mode_group even be called here? */
- ledit_buffer_set_mode(buffer, VISUAL);
- }
- if (buffer->sel.line1 < 0 || buffer->sel.byte1 < 0) {
- /* the selection has just started, so the current
- position is already set to the beginning of the
- selection (see case ButtonPress above) */
- ledit_buffer_set_selection(
- buffer,
- buffer->cur_line, buffer->cur_index, l, b
- );
- } else {
- ledit_buffer_set_selection(
- buffer,
- buffer->sel.line1, buffer->sel.byte1, l, b
- );
- }
- buffer->cur_line = l;
- buffer->cur_index = b;
- }
- break;
- }
-}
-
-void
-ledit_buffer_redraw(ledit_buffer *buffer) {
- int h = 0;
- int cur_line_y = 0;
- int cursor_displayed = 0;
- int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- for (int i = 0; i < buffer->lines_num; i++) {
- ledit_line *line = ledit_buffer_get_line(buffer, i);
- if (h + line->h > buffer->display_offset) {
- /* FIXME: line->text_dirty should not happen here */
- if (line->text_dirty || line->highlight_dirty)
- set_pango_text_and_highlight(buffer, i);
- if (line->dirty || line->cache_pixmap_index == -1) {
- ledit_buffer_render_line(buffer, i);
- }
- int final_y = 0;
- int dest_y = h - buffer->display_offset;
- int final_h = line->h;
- if (h < buffer->display_offset) {
- dest_y = 0;
- final_y = buffer->display_offset - h;
- final_h -= buffer->display_offset - h;
- }
- if (dest_y + final_h > text_h) {
- final_h -= final_y + final_h -
- buffer->display_offset - text_h;
- }
- cache_pixmap *pix = cache_get_pixmap(
- buffer->cache, line->cache_pixmap_index
- );
- XCopyArea(
- buffer->common->dpy, pix->pixmap,
- buffer->window->drawable, buffer->window->gc,
- 0, final_y, line->w, final_h, 0, dest_y
- );
- if (i == buffer->cur_line) {
- cur_line_y = h - buffer->display_offset;
- cursor_displayed = 1;
- }
- if (h + line->h >= buffer->display_offset + text_h)
- break;
- }
- h += line->h;
- }
-
- XSetForeground(buffer->common->dpy, buffer->window->gc, buffer->theme->text_fg.pixel);
- PangoRectangle strong, weak;
- ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
- PangoLayout *layout = get_pango_layout(buffer, buffer->cur_line);
- pango_layout_get_cursor_pos(
- layout, buffer->cur_index, &strong, &weak
- );
- /* FIXME: long, int, etc. */
- int cursor_y = strong.y / PANGO_SCALE + cur_line_y;
- if (cursor_displayed && cursor_y >= 0) {
- if (buffer->common->mode == NORMAL) {
- /* FIXME: figure out if there's a better way to do this */
- /* Seriously, which of the pango folks though it would be a good idea to
- not highlight spaces at the end of soft lines? That is an utterly
- horrible idea. Or am I just too stupid to use it properly? */
- /* FIXME: properly document what is happening here */
-
- int box_x = strong.x / PANGO_SCALE;
- int box_w = 10;
- /* determine where the box should be drawn */
- PangoDirection dir = PANGO_DIRECTION_LTR;
- int tmp_index = buffer->cur_index;
- if (buffer->cur_index >= cur_line->len)
- tmp_index = cur_line->len - 1;
- if (tmp_index >= 0)
- dir = pango_layout_get_direction(layout, tmp_index);
-
- int x, sli;
- pango_layout_index_to_line_x(layout, buffer->cur_index, 0, &sli, &x);
- PangoLayoutLine *sl = pango_layout_get_line_readonly(layout, sli);
- if (dir != sl->resolved_dir) {
- box_w = 3;
- }
- if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
- box_x = box_x - box_w;
- }
-
- if (buffer->cur_index == cur_line->len ||
- (cur_line->text[buffer->cur_index] == ' ' &&
- buffer->cur_index == sl->start_index + sl->length - 1)) {
- XFillRectangle(
- buffer->common->dpy, buffer->window->drawable, buffer->window->gc,
- box_x, cursor_y,
- box_w, strong.height / PANGO_SCALE
- );
- }
- } else if (buffer->common->mode == INSERT || buffer->common->mode == VISUAL) {
- XDrawLine(
- buffer->common->dpy, buffer->window->drawable, buffer->window->gc,
- strong.x / PANGO_SCALE, cursor_y,
- strong.x / PANGO_SCALE,
- (strong.y + strong.height) / PANGO_SCALE + cur_line_y
- );
- }
- }
- /* move input method position */
- if (!ledit_window_bottom_bar_text_shown(buffer->window)) {
- xximspot(
- buffer->window,
- strong.x / PANGO_SCALE,
- (strong.y + strong.height) / PANGO_SCALE + cur_line_y
- );
- }
-}
-
-static void
-undo_insert_helper(void *data, int line, int byte, char *text, int text_len) {
- ledit_buffer_insert_text_with_newlines_base((ledit_buffer *)data, line, byte, text, text_len, NULL, NULL);
-}
-
-static void
-undo_delete_helper(void *data, int line1, int byte1, int line2, int byte2) {
- ledit_buffer_delete_range_base((ledit_buffer *)data, 0, line1, byte1, line2, byte2, NULL, NULL, NULL, NULL);
-}
-
-void
-ledit_buffer_undo(ledit_buffer *buffer) {
- int min_line;
- ledit_undo(
- buffer->undo, buffer->common->mode, buffer, &undo_insert_helper,
- &undo_delete_helper, &buffer->cur_line, &buffer->cur_index, &min_line
- );
- if (buffer->common->mode == NORMAL) {
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
- );
- }
- if (min_line < buffer->lines_num)
- ledit_buffer_recalc_from_line(buffer, min_line > 0 ? min_line - 1 : min_line);
- /* FIXME: show undo message */
-}
-
-void
-ledit_buffer_redo(ledit_buffer *buffer) {
- int min_line;
- ledit_redo(
- buffer->undo, buffer->common->mode, buffer, &undo_insert_helper,
- &undo_delete_helper, &buffer->cur_line, &buffer->cur_index, &min_line
- );
- if (buffer->common->mode == NORMAL) {
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
- );
- }
- if (min_line < buffer->lines_num)
- ledit_buffer_recalc_from_line(buffer, min_line > 0 ? min_line - 1 : min_line);
- /* FIXME: show undo message */
-}
-
-static void
-paste_callback(void *data, char *text, int len) {
- ledit_buffer *buffer = (ledit_buffer *)data;
- txtbuf ins_buf = {.text = text, .len = len, .cap = len};
- ledit_range cur_range, ins_range;
- cur_range.line1 = ins_range.line1 = buffer->cur_line;
- cur_range.byte1 = ins_range.byte1 = buffer->cur_index;
- ledit_buffer_insert_text_with_newlines(
- buffer, buffer->cur_line, buffer->cur_index,
- text, len, &buffer->cur_line, &buffer->cur_index
- );
- cur_range.line2 = ins_range.line2 = buffer->cur_line;
- cur_range.byte2 = ins_range.byte2 = buffer->cur_index;
- ledit_push_undo_insert(
- buffer->undo, &ins_buf, ins_range, cur_range, 1, buffer->common->mode
- );
-}
-
-/* FIXME: guard against buffer being destroyed before paste callback is nulled */
-
-void
-ledit_buffer_paste_clipboard(ledit_buffer *buffer) {
- ledit_window_set_paste_callback(buffer->window, &paste_callback, buffer);
- clipboard_paste_clipboard(buffer->window);
-}
-
-void
-ledit_buffer_paste_primary(ledit_buffer *buffer) {
- ledit_window_set_paste_callback(buffer->window, &paste_callback, buffer);
- clipboard_paste_primary(buffer->window);
-}
diff --git a/buffer.h b/buffer.h
@@ -1,31 +1,23 @@
+#ifndef _LEDIT_BUFFER_H_
+#define _LEDIT_BUFFER_H_
+
typedef struct ledit_buffer ledit_buffer;
+#include "view.h"
+
/* FIXME: size_t for len, etc. */
typedef struct {
ledit_buffer *parent_buffer;
- char *text; /* text, stored as gap buffer */
- int gap; /* position of gap for gap buffer */
- int cap; /* allocated space for text */
- int len; /* actual length of text */
- int w; /* width in pixels */
- int h; /* height in pixels */
- long y_offset; /* pixel offset starting at the top of the file */
- int cache_pixmap_index; /* index of pixmap in cache, or -1 if not assigned */
- int cache_layout_index; /* index of pango layout in cache, or -1 if not assigned */
- int cursor_index; /* cursor index if it should be highlighted, -1 else */
- int softlines; /* number of softlines - cached from PangoLayout */
- char dirty; /* whether line needs to be rendered before being drawn */
- char text_dirty; /* whether the text in the PangoLayout needs to be
- * updated before the layout is rendered */
- char highlight_dirty; /* whether highlight (cursor or selection) needs to be
- * updated still in the PangoLayout before rendering */
- char h_dirty; /* whether height needs to be recalculated */
+ char *text; /* text, stored as gap buffer */
+ size_t gap; /* position of gap for gap buffer */
+ size_t cap; /* allocated space for text */
+ size_t len; /* actual length of text */
} ledit_line;
typedef struct {
char *text;
- int line;
- int byte;
+ size_t line;
+ size_t byte;
} ledit_buffer_mark;
typedef struct {
@@ -35,155 +27,283 @@ typedef struct {
/* TODO: advisory lock on file? also check if modification date changed before writing */
struct ledit_buffer {
- ledit_common *common; /* common stuff, e.g. display, window, etc. */
- ledit_line *lines; /* array of lines */
- ledit_theme *theme;
- char *filename;
- int lines_cap; /* number of lines allocated in array */
- int lines_num; /* number of used lines */
- int cur_line; /* current line */
- int cur_index; /* current byte index in line */
- int trailing; /* used by pango for determining if index is at
- * beginning or end of character */
- int trailing_bytes; /* same thing, but with bytes instead of utf8 characters */
- int end_of_soft_line; /* used to handle special behavior at end end of soft line */
- long total_height; /* total pixel height of all lines */
- long display_offset; /* current pixel offset of viewport */
- int selecting;
- ledit_range sel; /* current selection; all entries -1 if no selection */
- ledit_cache *cache;
- ledit_undo_stack *undo;
- ledit_window *window;
- ledit_buffer_marklist *marklist;
+ ledit_common *common; /* common stuff, e.g. display, etc. */
+ char *filename; /* last opened filename */
+ ledit_undo_stack *undo; /* undo manager */
+ ledit_buffer_marklist *marklist; /* list of mark positions set */
+ ledit_line *lines; /* array of lines */
+ ledit_view **views; /* array of registered views */
+ size_t views_num; /* number of views in array */
+ size_t lines_cap; /* size of lines array */
+ size_t lines_gap; /* position of gap for line gap buffer */
+ size_t lines_num; /* number of lines */
+ int hard_line_based; /* whether operations should work on soft- or hardlines
+ Note that this doesn't actually change any behavior of
+ the buffer functions, it is just stored here so all
+ views can be updated to display it in their status. */
};
-enum delete_mode {
- DELETE_CHAR,
- DELETE_SOFTLINE,
- DELETE_HARDLINE
-};
+/*
+ * Create a new buffer with one empty line
+ */
+ledit_buffer *ledit_buffer_create(ledit_common *common);
+
+/*
+ * Set the hard line mode of the buffer and update the
+ * displayed mode in all views.
+ */
+void ledit_buffer_set_hard_line_based(ledit_buffer *buffer, int hl);
+
+/*
+ * Add a new view to the buffer.
+ */
+void ledit_buffer_add_view(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size_t line, size_t pos);
+
+/*
+ * Remove the given view from the buffer.
+ * Nothing is done if the view does not belong to the buffer.
+ */
+void ledit_buffer_remove_view(ledit_buffer *buffer, ledit_view *view);
+
+/*
+ * Call 'view_recalc_from_line' for all views.
+ */
+void ledit_buffer_recalc_all_views_from_line(ledit_buffer *buffer, size_t line);
-ledit_buffer *ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *window);
-int ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, char **errstr);
+/*
+ * Load a file into the buffer at line 'line'.
+ * If the line is empty, the text is inserted directly.
+ * Otherwise it is appended after the line.
+ * Returns 0 on success and 1 on error. In case of an error, *errstr is filled
+ * with an error message which must be copied as soon as possible because it may
+ * be overwritten by subsequent function calls.
+ */
+int ledit_buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errstr);
+
+/*
+ * Write the buffer to a file.
+ * Returns 0 on success and 1 on error. In case of an error, *errstr is filled
+ * with an error message which must be copied as soon as possible because it may
+ * be overwritten by subsequent function calls.
+ */
int ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr);
+
+/*
+ * Destroy a buffer.
+ */
void ledit_buffer_destroy(ledit_buffer *buffer);
+
+/*
+ * Normalize a line, i.e. move the gap to the end and add '\0'
+ * so the text can be used as a normal string
+ */
void ledit_buffer_normalize_line(ledit_line *line);
-void ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index);
-void ledit_buffer_wipe_line_cursor_attrs(ledit_buffer *buffer, int line);
-void ledit_buffer_render_line(ledit_buffer *buffer, int line_index);
-ledit_line *ledit_buffer_get_line(ledit_buffer *buffer, int index);
-int ledit_buffer_line_visible(ledit_buffer *buffer, int index);
-int ledit_buffer_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos);
-void ledit_pos_to_x_softline(ledit_buffer *buffer, int line, int pos, int *x_ret, int *softline_ret);
-void ledit_x_softline_to_pos(ledit_buffer *buffer, int line, int x, int softline, int *pos_ret);
-int ledit_line_next_utf8(ledit_line *line, int index);
-int ledit_line_prev_utf8(ledit_line *line, int index);
-int ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int num);
-int ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num);
-int ledit_buffer_move_cursor_visually(ledit_buffer *buffer, int line, int pos, int movement, int *prev_index_ret);
-
-void ledit_buffer_next_word(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_next_word_end(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_next_bigword(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_next_bigword_end(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_prev_word(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_prev_bigword(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret);
-void ledit_buffer_get_pos_softline_bounds(ledit_buffer *buffer, int line, int pos, int *start_byte_ret, int *end_byte_ret);
-void ledit_buffer_get_softline_bounds(ledit_buffer *buffer, int line, int softline, int *start_byte_ret, int *end_byte_ret);
-int ledit_buffer_get_softline_count(ledit_buffer *buffer, int line);
-int ledit_buffer_pos_to_softline(ledit_buffer *buffer, int line, int pos);
-void ledit_buffer_get_cursor_pixel_pos(ledit_buffer *buffer, int line, int pos, int *x_ret, int *y_ret, int *h_ret);
-
-size_t ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2);
-void ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2);
-void ledit_buffer_copy_text_to_txtbuf(
+
+/*
+ * Insert 'src_len' bytes from 'src_line' starting at byte position 'src_index'
+ * into line 'dst_line' at byte position 'dst_index'.
+ * 'dst_line' must not be the same as 'src_line'.
+ * If 'text_ret' is not NULL, the copied text is additionally copied into 'text_ret'.
+ * This function does not update the views or normalize the lines, so it should
+ * only be used for efficiency purposes when performing multiple operations.
+ */
+void ledit_buffer_insert_text_from_line_base(
ledit_buffer *buffer,
- txtbuf *buf, /* oh, isn't that a very non-confusing name? */
- int line1, int byte1,
- int line2, int byte2
+ size_t dst_line, size_t dst_index,
+ size_t src_line, size_t src_index, size_t src_len,
+ txtbuf *text_ret
);
-void ledit_buffer_recalc_line(ledit_buffer *buffer, int line);
-void ledit_buffer_recalc_from_line(ledit_buffer *buffer, int line);
-void ledit_buffer_recalc_all_lines(ledit_buffer *buffer);
-/* The following functions all have two versions:
- * - The _base version does not call any recalc functions - this can be used
- * when multiple operations are performed before the next render in order to
- * avoid recalculating everything every time.
- * - The non-base versions call the appropriate recalc function in order to
- * keep everything in a consistent state. */
+/*
+ * Same as ledit_buffer_insert_text_from_line_base, but the views are updated afterwards.
+ */
+void ledit_buffer_insert_text_from_line(
+ ledit_buffer *buffer,
+ size_t dst_line, size_t dst_index,
+ size_t src_line, size_t src_index, size_t src_len,
+ txtbuf *text_ret
+);
-void ledit_buffer_insert_text_base(ledit_buffer *buffer, int line_index, int index, char *text, int len);
-void ledit_buffer_insert_text_with_newlines_base(
+/*
+ * Insert text 'text' with length 'len' at line 'line_index' and byte position 'index'.
+ * The text must not contain newlines.
+ * This function does not update the views or normalize the lines, so it should
+ * only be used for efficiency purposes when performing multiple operations.
+ */
+void ledit_buffer_insert_text_base(
ledit_buffer *buffer,
- int line_index, int index,
- char *text, long len,
- int *end_line_ret, int *end_char_ret
+ size_t line_index, size_t index,
+ char *text, size_t len
);
-void ledit_buffer_append_line_base(ledit_buffer *buffer, int line_index, int text_index);
-void ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int index2);
-void ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int index);
-int ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int byte_index, int dir);
-void ledit_buffer_delete_range_base(
- ledit_buffer *buffer, enum delete_mode delmode,
- int line_index1, int byte_index1,
- int line_index2, int byte_index2,
- int *new_line_ret, int *new_byte_ret,
- ledit_range *final_range_ret, txtbuf *text_ret
+
+/*
+ * Same as ledit_buffer_insert_text_base, but the views are updated afterwards.
+ */
+void ledit_buffer_insert_text(
+ ledit_buffer *buffer,
+ size_t line_index, size_t index,
+ char *text, size_t len
);
-void ledit_buffer_insert_text_from_line_base(
+
+/* Insert text 'text' with length 'len' at line 'line_index' and byte position 'index.
+ * The text may contain newlines.
+ * If end_line_ret is not NULL, the line index at the end of the insertion is
+ * written into it.
+ * If end_byte_ret is not NULL, the byte position at the end of the insertion is
+ * written into it.
+ * This function does not update the views or normalize the lines, so it should
+ * only be used for efficiency purposes when performing multiple operations.
+ */
+void ledit_buffer_insert_text_with_newlines_base(
ledit_buffer *buffer,
- int dst_line, int dst_index,
- int src_line, int src_index, int src_len,
- txtbuf *text_ret
+ size_t line_index, size_t index,
+ char *text, size_t len,
+ size_t *end_line_ret, size_t *end_char_ret
);
-void ledit_buffer_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, int len);
+/*
+ * Same as ledit_buffer_insert_text_with_newlines_base, but the views are updated afterwards.
+ */
void ledit_buffer_insert_text_with_newlines(
ledit_buffer *buffer,
- int line_index, int index,
- char *text, long len,
- int *end_line_ret, int *end_char_ret
+ size_t line_index, size_t index,
+ char *text, size_t len,
+ size_t *end_line_ret, size_t *end_char_ret
);
-void ledit_buffer_append_line(ledit_buffer *buffer, int line_index, int text_index);
-void ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int index2);
-void ledit_buffer_delete_line_entry(ledit_buffer *buffer, int index);
-int ledit_buffer_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, int dir);
-void ledit_buffer_delete_range(
- ledit_buffer *buffer, enum delete_mode delmode,
- int line_index1, int byte_index1,
- int line_index2, int byte_index2,
- int *new_line_ret, int *new_byte_ret,
- ledit_range *final_range_ret, txtbuf *text_ret
-);
-void ledit_buffer_insert_text_from_line(
+
+/*
+ * Append line after line at 'line_index'.
+ * if 'break_text' is not 0, the text on line 'line_index' starting at
+ * byte index 'text_index' is moved to the newly appended line.
+ * The views are notified that a line has been appended, but not told to update
+ * their line heights and offsets.
+ */
+void ledit_buffer_append_line_base(ledit_buffer *buffer, size_t line_index, size_t text_index, int break_text);
+
+/*
+ * Same as ledit_buffer_append_line_base, but the views are told to update
+ * their line heights and offsets afterwards.
+ */
+void ledit_buffer_append_line(ledit_buffer *buffer, size_t line_index, size_t text_index, int break_text);
+
+/*
+ * Delete lines between 'index1' and 'index2' (inclusive).
+ * The views are notified of the deletion but not told to
+ * update their line heights and offsets.
+ */
+void ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t index2);
+
+/*
+ * Same as ledit_buffer_delete_line_entries_base, but the views are told to
+ * update their line heights and offsets.
+ */
+void ledit_buffer_delete_line_entries(ledit_buffer *buffer, size_t index1, size_t index2);
+
+/*
+ * Convenience function to call ledit_buffer_delete_line_entries_base
+ * with two times the same line index.
+ */
+void ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, size_t index);
+
+/*
+ * Convenience function to call ledit_buffer_delete_line_entries
+ * with two times the same line index.
+ */
+void ledit_buffer_delete_line_entry(ledit_buffer *buffer, size_t index);
+
+/*
+ * Get the line at logical index 'index'.
+ * The returned line is only valid until the next
+ * action that appends or deletes line entries.
+ */
+ledit_line *ledit_buffer_get_line(ledit_buffer *buffer, size_t index);
+
+/*
+ * Tell views to recalculate the height of line 'line' and
+ * update the pixel offsets of the following lines.
+ */
+void ledit_buffer_recalc_line(ledit_buffer *buffer, size_t line);
+
+/*
+ * Tell views to recalculate the height for all lines starting at 'line'
+ * where the text_dirty attribute is set and update the pixel offsets of
+ * all lines after 'line'.
+ * Also clear the text_dirty attribute for all lines starting at 'line'.
+ */
+void ledit_buffer_recalc_from_line(ledit_buffer *buffer, size_t line);
+
+/*
+ * Tell views to recalculate all lines.
+ * Also clear the text_dirty attribute for all lines.
+ */
+void ledit_buffer_recalc_all_lines(ledit_buffer *buffer);
+
+/*
+ * Get needed length of text range, including newlines.
+ * - NUL is not included
+ * - if the last range ends at the end of a line, the newline is *not* included
+ * - the range must be sorted already
+ */
+size_t ledit_buffer_textlen(ledit_buffer *buffer, size_t line1, size_t byte1, size_t line2, size_t byte2);
+
+/*
+ * Copy text range into given buffer.
+ * - dst is null-terminated
+ * - dst must be large enough to contain the text and NUL (only use this together with ledit_buffer_textlen)
+ * - the range must be sorted already
+ */
+void ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2);
+
+/*
+ * Copy text range into given buffer and resize it if necessary.
+ * - the range must be sorted already
+ */
+void ledit_buffer_copy_text_to_txtbuf(
ledit_buffer *buffer,
- int dst_line, int dst_index,
- int src_line, int src_index, int src_len,
- txtbuf *text_ret
+ txtbuf *buf, /* oh, isn't that a very non-confusing name? */
+ size_t line1, size_t byte1,
+ size_t line2, size_t byte2
);
-void ledit_buffer_resize_width(ledit_buffer *buffer, int width);
-/* x and y are in pixels, if snap_to_nearest is nonzero, the returned byte
- is at the nearest grapheme boundary, if it is zero, the byte is always
- the beginning of the grapheme under the position */
-void ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int snap_to_nearest, int *line_ret, int *byte_ret);
-void ledit_buffer_get_nearest_legal_pos(ledit_buffer *buffer, int line, int byte, int *line_ret, int *byte_ret);
-void ledit_buffer_ensure_cursor_shown(ledit_buffer *buffer);
-void ledit_buffer_scroll_handler(void *buffer, long pos);
-void ledit_buffer_button_handler(void *data, XEvent *event);
-void ledit_buffer_redraw(ledit_buffer *buffer);
-void ledit_buffer_undo(ledit_buffer *buffer);
-void ledit_buffer_redo(ledit_buffer *buffer);
-void ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2);
-void ledit_buffer_set_mode(ledit_buffer *buffer, enum ledit_mode mode);
-void ledit_buffer_paste_clipboard(ledit_buffer *buffer);
-void ledit_buffer_paste_primary(ledit_buffer *buffer);
-void ledit_buffer_resize_textview(ledit_buffer *buffer);
-void ledit_buffer_scroll(ledit_buffer *buffer, long new_offset);
-void ledit_buffer_scroll_to_pos_top(ledit_buffer *buffer, int line, int byte);
-void ledit_buffer_scroll_to_pos_bottom(ledit_buffer *buffer, int line, int byte);
-/* FIXME: just make generic sort range */
-void ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte2);
-int ledit_line_next_non_whitespace(ledit_buffer *buffer, int line, int byte);
-void ledit_buffer_insert_mark(ledit_buffer *buffer, char *mark, int len, int line, int byte);
+/*
+ * Get the byte index of the previous utf8 character starting at byte index 'index'.
+ */
+size_t ledit_line_next_utf8(ledit_line *line, size_t index);
+
+/*
+ * Get the byte index of the previous utf8 character starting at byte index 'index'.
+ */
+size_t ledit_line_prev_utf8(ledit_line *line, size_t index);
+
+/*
+ * Get the unicode character index of a byte position.
+ */
+size_t line_byte_to_char(ledit_line *line, size_t byte);
+
+/*
+ * Delete the section of line 'line' starting at byte 'start' with length 'length'
+ * and notify the views.
+ * Note that this does not tell the views to recalculate their line heights and offsets.
+ */
+void ledit_buffer_delete_line_section_base(ledit_buffer *buffer, size_t line, size_t start, size_t length);
+
+/*
+ * Delete the unicode char at 'line_index' and 'byte_index' if 'dir' is >= 0,
+ * or the previous char if 'dir' is < 0.
+ * Returns the byte index where the deleted text was located.
+ * This function only notifies the views of the deletion, but does not tell
+ * them to recalculate their line heights and offsets.
+ */
+size_t ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, size_t line_index, size_t byte_index, int dir);
+
+/*
+ * Same as ledit_buffer_delete_unicode_char_base, but the views are updated.
+ */
+size_t ledit_buffer_delete_unicode_char(ledit_buffer *buffer, size_t line_index, size_t byte_index, int dir);
+
+/*
+ * Insert a mark with key 'mark' at line 'line' and byte 'byte'.
+ */
+void ledit_buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t line, size_t byte);
+
+#endif
diff --git a/cache.c b/cache.c
@@ -20,11 +20,13 @@ cache_create(Display *dpy) {
for (size_t i = 0; i < PIXMAP_CACHE_INITIAL_SIZE; i++) {
cache->pixmaps[i].pixmap = None;
cache->pixmaps[i].draw = NULL;
- cache->pixmaps[i].line = -1;
+ cache->pixmaps[i].line = 0;
+ cache->pixmaps[i].valid = 0;
}
for (size_t i = 0; i < LAYOUT_CACHE_SIZE; i++) {
cache->layouts[i].layout = NULL;
- cache->layouts[i].line = -1;
+ cache->layouts[i].line = 0;
+ cache->layouts[i].valid = 0;
}
cache->num_pixmaps = PIXMAP_CACHE_INITIAL_SIZE;
cache->num_layouts = LAYOUT_CACHE_SIZE;
@@ -35,28 +37,30 @@ cache_create(Display *dpy) {
void
cache_flush(
ledit_cache *cache, void *callback_data,
- void (*set_pixmap_line)(void *, int, int),
- void (*set_layout_line)(void *, int, int)) {
+ void (*invalidate_pixmap_line)(void *, size_t),
+ void (*invalidate_layout_line)(void *, size_t)) {
cache_invalidate_from_line(
- cache, 0, callback_data, set_pixmap_line, set_layout_line
+ cache, 0, callback_data, invalidate_pixmap_line, invalidate_layout_line
);
}
void
cache_invalidate_from_line(
- ledit_cache *cache, int start, void *callback_data,
- void (*set_pixmap_line)(void *, int, int),
- void (*set_layout_line)(void *, int, int)) {
+ ledit_cache *cache, size_t start, void *callback_data,
+ void (*invalidate_pixmap_line)(void *, size_t),
+ void (*invalidate_layout_line)(void *, size_t)) {
for (size_t i = 0; i < cache->num_pixmaps; i++) {
if (cache->pixmaps[i].line >= start) {
- set_pixmap_line(callback_data, cache->pixmaps[i].line, -1);
- cache->pixmaps[i].line = -1;
+ invalidate_pixmap_line(callback_data, cache->pixmaps[i].line);
+ cache->pixmaps[i].line = 0;
+ cache->pixmaps[i].valid = 0;
}
}
for (size_t i = 0; i < cache->num_layouts; i++) {
if (cache->layouts[i].line >= start) {
- set_layout_line(callback_data, cache->layouts[i].line, -1);
- cache->layouts[i].line = -1;
+ invalidate_layout_line(callback_data, cache->layouts[i].line);
+ cache->layouts[i].line = 0;
+ cache->layouts[i].valid = 0;
}
}
}
@@ -79,49 +83,44 @@ cache_destroy(ledit_cache *cache) {
}
cache_pixmap *
-cache_get_pixmap(ledit_cache *cache, int index) {
- assert(index >= 0 && (size_t)index < cache->num_pixmaps);
+cache_get_pixmap(ledit_cache *cache, size_t index) {
+ assert(index < cache->num_pixmaps);
return &cache->pixmaps[index];
}
cache_layout *
-cache_get_layout(ledit_cache *cache, int index) {
- assert(index >= 0 && (size_t)index < cache->num_layouts);
+cache_get_layout(ledit_cache *cache, size_t index) {
+ assert(index < cache->num_layouts);
return &cache->layouts[index];
}
-/* FIXME: standardize overflow checking */
-static void
-err_overflow(void) {
- fprintf(stderr, "ERROR: Integer overflow in cache handling.\n");
- exit(1);
-}
-
/* FIXME: decide on int or size_t, but not both */
/* or maybe ssize_t */
+
+/* FIXME: max pixmap cache size */
void
cache_assign_pixmap_index(
- ledit_cache *cache, int line,
+ ledit_cache *cache, size_t line,
void *callback_data,
- int (*line_needed)(void *, int),
- void (*set_pixmap_line)(void *, int, int)) {
- int line_index;
+ int (*line_needed)(void *, size_t),
+ void (*set_pixmap_line)(void *, size_t, size_t),
+ void (*invalidate_pixmap_line)(void *, size_t)) {
+ size_t line_index;
size_t entry_index;
for (size_t i = 0; i <= cache->num_pixmaps; i++) {
entry_index = (i + cache->cur_pixmap_index) % cache->num_pixmaps;
line_index = cache->pixmaps[entry_index].line;
+ int valid = cache->pixmaps[entry_index].valid;
/* replace line when entry isn't assigned or currently assigned line is not visible */
- if (line_index == -1 ||
- (line_index >= 0 &&
- !line_needed(callback_data, line_index))) {
+ if (!valid ||
+ (valid && !line_needed(callback_data, line_index))) {
cache->cur_pixmap_index = (entry_index + 1) % cache->num_pixmaps;
- if (entry_index > INT_MAX)
- err_overflow();
cache_pixmap *pix = &cache->pixmaps[entry_index];
- if (pix->line >= 0)
- set_pixmap_line(callback_data, pix->line, -1);
+ if (pix->valid)
+ invalidate_pixmap_line(callback_data, pix->line);
pix->line = line;
- set_pixmap_line(callback_data, line, (int)entry_index);
+ pix->valid = 1;
+ set_pixmap_line(callback_data, line, entry_index);
return;
}
}
@@ -133,32 +132,31 @@ cache_assign_pixmap_index(
cache->pixmaps = ledit_reallocarray(cache->pixmaps, cache->num_pixmaps * 2, sizeof(cache_pixmap));
entry_index = cache->num_pixmaps;
for (size_t i = cache->num_pixmaps; i < cache->num_pixmaps * 2; i++) {
- cache->pixmaps[i].line = -1;
+ cache->pixmaps[i].line = 0;
+ cache->pixmaps[i].valid = 0;
cache->pixmaps[i].pixmap = None;
cache->pixmaps[i].draw = NULL;
}
cache->num_pixmaps *= 2;
- if (entry_index > INT_MAX)
- err_overflow();
cache_pixmap *pix = &cache->pixmaps[entry_index];
pix->line = line;
- set_pixmap_line(callback_data, line, (int)entry_index);
+ pix->valid = 1;
+ set_pixmap_line(callback_data, line, entry_index);
}
/* FIXME: perhaps use "real" clock cache management, i.e. set a bit on a cache entry
when it is used so it isn't invalidated yet. */
-void
-cache_assign_layout_index(
- ledit_cache *cache, int line,
+void cache_assign_layout_index(
+ ledit_cache *cache, size_t line,
void *callback_data,
- void (*set_layout_line)(void *, int, int)) {
+ void (*set_layout_line)(void *, size_t, size_t),
+ void (*invalidate_layout_line)(void *, size_t)) {
size_t old = cache->cur_layout_index;
cache->cur_layout_index = (cache->cur_layout_index + 1) % cache->num_layouts;
- if (old > INT_MAX)
- err_overflow();
cache_layout *layout = &cache->layouts[old];
- if (layout->line >= 0)
- set_layout_line(callback_data, layout->line, -1);
+ if (layout->valid)
+ invalidate_layout_line(callback_data, layout->line);
+ layout->valid = 1;
layout->line = line;
- set_layout_line(callback_data, line, (int)old);
+ set_layout_line(callback_data, line, old);
}
diff --git a/cache.h b/cache.h
@@ -4,7 +4,7 @@
#define LAYOUT_CACHE_SIZE 40
/*
- * The initial number of pixmas in the cache.
+ * The initial number of pixmaps in the cache.
* The size is increased when more pixmaps are visible
* at the same time than there are entries in the cache.
*/
@@ -13,13 +13,15 @@
typedef struct {
Pixmap pixmap;
XftDraw *draw;
- int w, h; /* width and height of the pixmap */
- int line;/* the line associated with this entry, or -1 if unassigned */
+ int w, h; /* width and height of the pixmap */
+ size_t line; /* the line associated with this entry */
+ int valid; /* whether the entry is assigned to a line */
} cache_pixmap;
typedef struct {
PangoLayout *layout;
- int line; /* the line associated with this entry, or -1 if unassigned */
+ size_t line; /* the line associated with this entry */
+ int valid; /* whether the entry is assigned to a line */
} cache_layout;
typedef struct {
@@ -42,26 +44,26 @@ ledit_cache *cache_create(Display *dpy);
/*
* Reset line index of every cache entry (pixmaps and layouts).
- * set_pixmap_line is called with callback_data as its first argument,
- * a line index which has been removed from the pixmap cache as the
- * second argument, and '-1' as the third argument.
- * set_layout_line is the same, but for the layout cache.
+ * invalidate_pixmap_line is called with callback_data as its first
+ * argument and a line index which has been removed from the pixmap
+ * cache as the second argument.
+ * invalidate_layout_line is the same, but for the layout cache.
*/
void cache_flush(
ledit_cache *cache,
void *callback_data,
- void (*set_pixmap_line)(void *, int, int),
- void (*set_layout_line)(void *, int, int)
+ void (*invalidate_pixmap_line)(void *, size_t),
+ void (*invalidate_layout_line)(void *, size_t)
);
/*
* Like cache_flush, but only line numbers >= start are invalidated.
*/
void cache_invalidate_from_line(
- ledit_cache *cache, int start,
+ ledit_cache *cache, size_t start,
void *callback_data,
- void (*set_pixmap_line)(void *, int, int),
- void (*set_layout_line)(void *, int, int)
+ void (*invalidate_pixmap_line)(void *, size_t),
+ void (*invalidate_layout_line)(void *, size_t)
);
/*
@@ -72,12 +74,12 @@ void cache_destroy(ledit_cache *cache);
/*
* Get the cache_pixmap at index.
*/
-cache_pixmap *cache_get_pixmap(ledit_cache *cache, int index);
+cache_pixmap *cache_get_pixmap(ledit_cache *cache, size_t index);
/*
* Get the cache_layout at index.
*/
-cache_layout *cache_get_layout(ledit_cache *cache, int index);
+cache_layout *cache_get_layout(ledit_cache *cache, size_t index);
/*
* The following two functions have a somewhat cumbersome interface
@@ -94,17 +96,19 @@ cache_layout *cache_get_layout(ledit_cache *cache, int index);
* It is called with callback_data as the first argument and the
* line to be checked as the second argument.
* The line of the cache entry is set to 'line' and if a line was
- * set before, it is reset by calling 'reset_pixmap_line' with
- * 'callback_data' as the first argument, the old line as the second
- * argument, and '-1' as the third argument.
- * Similarly, the cache index of the new line is changed by calling
- * 'set_pixmap_line' with the new cache index as the third argument.
+ * set before, it is reset by calling 'invalidate_pixmap_line' with
+ * 'callback_data' as the first argumentand the old line as the
+ * second argument.
+ * The cache index of the new line is changed by calling 'set_pixmap_line'
+ * with 'callback_data' as the first argument, 'line' as the second
+ * argument, and the new cache index as the third argument.
*/
void cache_assign_pixmap_index(
- ledit_cache *cache, int line,
+ ledit_cache *cache, size_t line,
void *callback_data,
- int (*line_needed)(void *, int),
- void (*set_pixmap_line)(void *, int, int)
+ int (*line_needed)(void *, size_t),
+ void (*set_pixmap_line)(void *, size_t, size_t),
+ void (*invalidate_pixmap_line)(void *, size_t)
);
/*
@@ -112,14 +116,16 @@ void cache_assign_pixmap_index(
* Since it is not clear which layouts are needed more, this just
* uses the next index in a clock fashion.
* The line of the cache entry is set to 'line' and if a line was
- * set before, it is reset by calling 'set_layout_line' with
- * 'callback_data' as the first argument, the old line as the second
- * argument, and '-1' as the third argument.
- * Similarly, the cache index of the new line is changed by calling
- * 'set_layout_line' with the new cache index as the third argument.
+ * set before, it is reset by calling 'invalidate_layout_line' with
+ * 'callback_data' as the first argument and the old line as the
+ * second argument.
+ * The cache index of the new line is changed by calling 'set_layout_line'
+ * with 'callback_data' as the first argument, 'line' as the second
+ * argument, and the new cache index as the third argument.
*/
void cache_assign_layout_index(
- ledit_cache *cache, int line,
+ ledit_cache *cache, size_t line,
void *callback_data,
- void (*set_layout_line)(void *, int, int)
+ void (*set_layout_line)(void *, size_t, size_t),
+ void (*invalidate_layout_line)(void *, size_t)
);
diff --git a/cleanup.h b/cleanup.h
@@ -0,0 +1,3 @@
+/* This is here so it can be called from other places
+ even though the function definition is in ledit.c */
+void ledit_cleanup(void);
diff --git a/common.h b/common.h
@@ -1,8 +1,8 @@
typedef struct {
- int line1;
- int byte1;
- int line2;
- int byte2;
+ size_t line1;
+ size_t byte1;
+ size_t line2;
+ size_t byte2;
} ledit_range;
enum ledit_mode {
@@ -18,5 +18,4 @@ typedef struct {
int screen;
int depth;
int redraw;
- enum ledit_mode mode;
} ledit_common;
diff --git a/keys_basic.c b/keys_basic.c
@@ -1,5 +1,6 @@
-/* FIXME: I guess hard_line_based should be updated for all buffers/windows when that gets supported */
-/* FIXME: cursor isn't shown on spaces at end of softlines */
+/* FIXME: the stacks here are shared for all views which can cause weird
+ behavior, but I'm not sure what would be more logical */
+/* FIXME: cursor isn't shown properly on spaces at end of softlines */
/* FIXME: selection is sometimes not reset when it is "clicked away" */
/* FIXME: use weak cursor */
/* FIXME: spaces at end of soft line are weird in bidi text
@@ -28,10 +29,10 @@
#include "theme.h"
#include "window.h"
#include "buffer.h"
+#include "view.h"
#include "search.h"
#include "keys.h"
-#include "action.h"
#include "keys_basic.h"
#include "keys_command.h"
#include "keys_basic_config.h"
@@ -43,7 +44,7 @@ static int last_lines_scrolled = -1;
struct repetition_stack_elem {
char *key_text;
- int len;
+ size_t len;
KeySym sym;
unsigned int key_state;
int lang_index;
@@ -57,7 +58,7 @@ static struct {
struct repetition_stack_elem *tmp_stack;
} repetition_stack = {0, 0, 0, 0, NULL, 0, 0, NULL};
-typedef void (*motion_callback)(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
+typedef void (*motion_callback)(ledit_view *view, size_t line, size_t char_pos, enum key_type type);
struct key_stack_elem {
enum key_type key;
@@ -68,8 +69,6 @@ struct key_stack_elem {
* the command should operate on lines or chars) */
motion_callback motion_cb;
int count; /* number of repetitions */
- int data1; /* misc. data 1 */
- int data2; /* misc. data 2 */
};
static struct {
@@ -77,7 +76,7 @@ static struct {
struct key_stack_elem *stack;
} key_stack = {0, 0, NULL};
-static struct action (*grab_char_cb)(ledit_buffer *buffer, char *text, int len) = NULL;
+static struct action (*grab_char_cb)(ledit_view *view, char *text, size_t len) = NULL;
static int hard_line_based = 1;
void
@@ -109,24 +108,24 @@ 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, int allow_illegal_index);
-static void move_cursor_up_down(ledit_buffer *buffer, int dir);
+static void move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_index);
+static void move_cursor_up_down(ledit_view *view, int dir);
static void push_num(int num);
-static void delete_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
-static void yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
+static void delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type);
+static void yank_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type);
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
+ ledit_view *view, size_t cur_line, size_t cur_index,
+ int movement, size_t *new_line_ret, int *new_softline_ret
);
-static void move_cursor_logically(ledit_buffer *buffer, int movement_dir, int allow_illegal_index);
-static void change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
-static void push_undo_empty_insert(ledit_buffer *buffer, int line, int index, int start_group);
-static void move_half_screen(ledit_buffer *buffer, int movement);
+static void move_cursor_logically(ledit_view *view, int movement_dir, int allow_illegal_index);
+static void change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type);
+static void push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group);
+static void move_half_screen(ledit_view *view, int movement);
/* FIXME: move to common */
static void
-swap(int *a, int *b) {
- int tmp = *a;
+swap_sz(size_t *a, size_t *b) {
+ size_t tmp = *a;
*a = *b;
*b = tmp;
}
@@ -151,8 +150,6 @@ push_key_stack(void) {
e->followup = KEY_NONE;
e->motion_cb = NULL;
e->count = 0;
- e->data1 = 0;
- e->data2 = 0;
key_stack.len++;
return e;
}
@@ -182,8 +179,8 @@ clear_key_stack(void) {
}
static struct action
-err_invalid_key(ledit_buffer *buffer) {
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+err_invalid_key(ledit_view *view) {
+ ledit_window_show_message(view->window, "Invalid key", -1);
clear_key_stack();
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
@@ -213,8 +210,14 @@ get_key_repeat_and_motion_cb(motion_callback *cb_ret) {
} else if (e->count == 0) {
num = 0;
}
- if (e != NULL)
- num *= (e->count > 0 ? e->count : 1);
+ if (e != NULL) {
+ int new_count = e->count > 0 ? e->count : 1;
+ if (INT_MAX / new_count < num) {
+ /* FIXME: show error */
+ num = INT_MAX;
+ }
+ num *= new_count;
+ }
} else {
num = 0;
}
@@ -329,27 +332,28 @@ finalize_repetition_stack(void) {
down (negative means up, positive means down) */
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) {
+ ledit_view *view, size_t cur_line, size_t cur_index, int movement,
+ size_t *new_line_ret, int *new_softline_ret) {
if (hard_line_based) {
- *new_line_ret = cur_line + movement;
- if (*new_line_ret < 0)
+ if (movement < 0 && (size_t)-movement > cur_line)
*new_line_ret = 0;
- else if (*new_line_ret >= buffer->lines_num)
- *new_line_ret = buffer->lines_num - 1;
+ else
+ *new_line_ret = cur_line + movement;
+ if (*new_line_ret >= view->lines_num)
+ *new_line_ret = view->lines_num - 1;
*new_softline_ret = 0;
} else {
- int softline = ledit_buffer_pos_to_softline(buffer, cur_line, cur_index);
+ int softline = view_pos_to_softline(view, cur_line, cur_index);
if (movement > 0) {
- int softlines = ledit_buffer_get_softline_count(buffer, cur_line);
+ int softlines = view_get_softline_count(view, cur_line);
if (softlines - softline > movement) {
*new_line_ret = cur_line;
*new_softline_ret = softline + movement;
} else {
movement -= (softlines - softline - 1);
- int endline = cur_line + 1;
- while (movement > 0 && endline < buffer->lines_num) {
- softlines = ledit_buffer_get_softline_count(buffer, endline);
+ size_t endline = cur_line + 1;
+ while (movement > 0 && endline < view->lines_num) {
+ softlines = view_get_softline_count(view, endline);
movement -= softlines;
endline++;
}
@@ -368,13 +372,12 @@ get_new_line_softline(
*new_softline_ret = softline + movement;
} else {
movement += softline;
- int endline = cur_line - 1;
- while (movement < 0 && endline >= 0) {
- softlines = ledit_buffer_get_softline_count(buffer, endline);
+ size_t endline = cur_line;
+ while (movement < 0 && endline > 0) {
+ softlines = view_get_softline_count(view, endline-1);
movement += softlines;
endline--;
}
- endline++;
if (movement >= 0) {
*new_softline_ret = movement;
} else {
@@ -389,21 +392,21 @@ get_new_line_softline(
}
}
-/* FIXME: don't overwrite buffer->cur_line, etc. here? */
+/* FIXME: don't overwrite view->cur_line, etc. here? */
static void
delete_range(
- ledit_buffer *buffer,
+ ledit_view *view,
int line_based, int selected,
- int line_index1, int byte_index1,
- int line_index2, int byte_index2,
+ size_t line_index1, size_t byte_index1,
+ size_t line_index2, size_t byte_index2,
int copy_to_buffer) {
(void)selected; /* FIXME */
if (copy_to_buffer && !paste_buffer)
paste_buffer = txtbuf_new();
txtbuf *buf = copy_to_buffer ? paste_buffer : txtbuf_new();
ledit_range cur_range, del_range;
- cur_range.line1 = buffer->cur_line;
- cur_range.byte1 = buffer->cur_index;
+ cur_range.line1 = view->cur_line;
+ cur_range.byte1 = view->cur_index;
enum delete_mode delmode = DELETE_CHAR;
if (line_based) {
if (hard_line_based)
@@ -411,17 +414,17 @@ delete_range(
else
delmode = DELETE_SOFTLINE;
}
- ledit_buffer_delete_range(
- buffer, delmode,
+ view_delete_range(
+ view, delmode,
line_index1, byte_index1,
line_index2, byte_index2,
- &buffer->cur_line, &buffer->cur_index,
+ &view->cur_line, &view->cur_index,
&del_range, buf
);
- cur_range.line2 = buffer->cur_line;
- cur_range.byte2 = buffer->cur_index;
+ cur_range.line2 = view->cur_line;
+ cur_range.byte2 = view->cur_index;
ledit_push_undo_delete(
- buffer->undo, buf, del_range, cur_range, 1, buffer->common->mode
+ view->buffer->undo, buf, del_range, cur_range, 1, view->mode
);
if (!copy_to_buffer)
txtbuf_destroy(buf);
@@ -429,214 +432,214 @@ delete_range(
static void
insert_text(
- ledit_buffer *buffer,
- int line, int index,
- char *text, int len,
- int cur_line1, int cur_index1,
- int cur_line2, int cur_index2, int start_group) {
- if (len < 0)
- len = strlen(text);
+ ledit_view *view,
+ size_t line, size_t index,
+ char *text, size_t len,
+ size_t cur_line1, size_t cur_index1,
+ size_t cur_line2, size_t cur_index2,
+ int set_range_start, int set_range_end, int start_group) {
txtbuf ins_buf = {.text = text, .len = len, .cap = len};
ledit_range cur_range, del_range;
- if (cur_line1 >= 0 && cur_index1 >= 0) {
+ if (set_range_start) {
cur_range.line1 = cur_line1;
cur_range.byte1 = cur_index1;
} else {
- cur_range.line1 = buffer->cur_line;
- cur_range.byte1 = buffer->cur_index;
+ cur_range.line1 = view->cur_line;
+ cur_range.byte1 = view->cur_index;
}
del_range.line1 = line;
del_range.byte1 = index;
- int cur_line, cur_index;
+ size_t cur_line, cur_index;
ledit_buffer_insert_text_with_newlines(
- buffer, line, index, text, len,
+ view->buffer, line, index, text, len,
&cur_line, &cur_index
);
/* this is mainly for pasting, where the new line and index
should not be at the end of the pasted text */
- if (cur_line2 >= 0 && cur_index2 >= 0) {
- cur_range.line2 = buffer->cur_line = cur_line2;
- cur_range.byte2 = buffer->cur_index = cur_index2;
+ if (set_range_end) {
+ cur_range.line2 = view->cur_line = cur_line2;
+ cur_range.byte2 = view->cur_index = cur_index2;
} else {
- cur_range.line2 = buffer->cur_line = cur_line;
- cur_range.byte2 = buffer->cur_index = cur_index;
+ cur_range.line2 = view->cur_line = cur_line;
+ cur_range.byte2 = view->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
+ view->buffer->undo, &ins_buf, del_range, cur_range, start_group, view->mode
);
}
static int
-delete_selection(ledit_buffer *buffer) {
- if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buffer->sel.byte2) {
+delete_selection(ledit_view *view) {
+ if (view->sel_valid && (view->sel.line1 != view->sel.line2 || view->sel.byte1 != view->sel.byte2)) {
delete_range(
- buffer, 0, 0,
- buffer->sel.line1, buffer->sel.byte1,
- buffer->sel.line2, buffer->sel.byte2, 1
+ view, 0, 0,
+ view->sel.line1, view->sel.byte1,
+ view->sel.line2, view->sel.byte2, 1
);
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;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view->sel_valid = 0;
+ view->sel.line1 = view->sel.line2 = 0;
+ view->sel.byte1 = view->sel.byte2 = 0;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
return 1;
}
return 0;
}
static struct action
-delete_chars_forwards(ledit_buffer *buffer, char *text, int len) {
+delete_chars_forwards(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int num = get_key_repeat();
if (num == -1) {
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
return (struct action){ACTION_NONE, NULL};
} else if (num == 0) {
num = 1;
}
- int end_index = ledit_buffer_next_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, num
+ size_t end_index = view_next_cursor_pos(
+ view, view->cur_line, view->cur_index, num
);
delete_range(
- buffer, 0, 0,
- buffer->cur_line, buffer->cur_index,
- buffer->cur_line, end_index, 1
+ view, 0, 0,
+ view->cur_line, view->cur_index,
+ view->cur_line, end_index, 1
);
paste_buffer_line_based = 0;
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-delete_chars_backwards(ledit_buffer *buffer, char *text, int len) {
+delete_chars_backwards(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int num = get_key_repeat();
if (num == -1) {
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
return (struct action){ACTION_NONE, NULL};
} else if (num == 0) {
num = 1;
}
- int start_index = ledit_buffer_prev_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, num
+ size_t start_index = view_prev_cursor_pos(
+ view, view->cur_line, view->cur_index, num
);
delete_range(
- buffer, 0, 0,
- buffer->cur_line, start_index,
- buffer->cur_line, buffer->cur_index, 1
+ view, 0, 0,
+ view->cur_line, start_index,
+ view->cur_line, view->cur_index, 1
);
paste_buffer_line_based = 0;
/* I guess this is technically unnecessary since only
text before the current position is deleted */
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, start_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, start_index
);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
return (struct action){ACTION_NONE, NULL};
}
/* used to set cursor - I guess this is sort of a hack */
static void
-push_undo_empty_insert(ledit_buffer *buffer, int line, int index, int start_group) {
+push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group) {
txtbuf ins_buf = {.text = "", .len = 0, .cap = 0};
ledit_range ins_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
ledit_range cur_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
ledit_push_undo_insert(
- buffer->undo, &ins_buf, ins_range, cur_range, start_group, buffer->common->mode
+ view->buffer->undo, &ins_buf, ins_range, cur_range, start_group, view->mode
);
}
static struct action
-append_line_above(ledit_buffer *buffer, char *text, int len) {
- int start, end;
+append_line_above(ledit_view *view, char *text, size_t len) {
+ size_t start, end;
/* do this here already so the mode group is the same for the newline insertion */
- enter_insert(buffer, text, len);
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
+ enter_insert(view, text, len);
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
if (hard_line_based || start == 0) {
- insert_text(buffer, buffer->cur_line, 0, "\n", -1, -1, -1, buffer->cur_line, 0, 1);
+ insert_text(view, view->cur_line, 0, "\n", 1, 0, 0, view->cur_line, 0, 0, 1, 1);
} else {
- insert_text(buffer, buffer->cur_line, start, "\n\n", -1, -1, -1, buffer->cur_line + 1, 0, 1);
+ /* FIXME: this interface really is horrible */
+ insert_text(view, view->cur_line, start, "\n\n", 2, 0, 0, view->cur_line + 1, 0, 0, 1, 1);
}
return (struct action){ACTION_NONE, NULL};
}
static struct action
-append_line_below(ledit_buffer *buffer, char *text, int len) {
- int start, end;
- enter_insert(buffer, text, len);
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+append_line_below(ledit_view *view, char *text, size_t len) {
+ size_t start, end;
+ enter_insert(view, text, len);
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
if (hard_line_based || end == ll->len) {
- insert_text(buffer, buffer->cur_line, ll->len, "\n", -1, -1, -1, buffer->cur_line + 1, 0, 1);
+ insert_text(view, view->cur_line, ll->len, "\n", 1, 0, 0, view->cur_line + 1, 0, 0, 1, 1);
} else {
- insert_text(buffer, buffer->cur_line, end, "\n\n", -1, -1, -1, buffer->cur_line + 1, 0, 1);
+ insert_text(view, view->cur_line, end, "\n\n", 2, 0, 0, view->cur_line + 1, 0, 0, 1, 1);
}
return (struct action){ACTION_NONE, NULL};
}
static struct action
-append_after_cursor(ledit_buffer *buffer, char *text, int len) {
- enter_insert(buffer, text, len);
+append_after_cursor(ledit_view *view, char *text, size_t len) {
+ enter_insert(view, text, len);
/* make cursor jump back to original position on undo */
- push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1);
- buffer->cur_index = ledit_buffer_next_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, 1
+ push_undo_empty_insert(view, view->cur_line, view->cur_index, 1);
+ view->cur_index = view_next_cursor_pos(
+ view, view->cur_line, view->cur_index, 1
);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-append_after_eol(ledit_buffer *buffer, char *text, int len) {
- int start, end;
- enter_insert(buffer, text, len);
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
+append_after_eol(ledit_view *view, char *text, size_t len) {
+ size_t start, end;
+ enter_insert(view, text, len);
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
/* make cursor jump back to original position on undo */
- push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1);
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ push_undo_empty_insert(view, view->cur_line, view->cur_index, 1);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
if (hard_line_based)
- buffer->cur_index = ll->len;
+ view->cur_index = ll->len;
else
- buffer->cur_index = end;
+ view->cur_index = end;
return (struct action){ACTION_NONE, NULL};
}
static struct action
-move_to_line(ledit_buffer *buffer, char *text, int len) {
+move_to_line(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb = NULL;
int repeat = get_key_repeat_and_motion_cb(&cb);
- int line;
+ size_t line;
if (repeat > 0)
- line = repeat > buffer->lines_num ? buffer->lines_num : repeat;
+ line = (size_t)repeat > view->lines_num ? view->lines_num : (size_t)repeat;
else if (repeat == 0)
- line = buffer->lines_num;
+ line = view->lines_num;
else
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
if (cb != NULL) {
- cb(buffer, line - 1, 0, KEY_MOTION_LINE);
+ cb(view, line - 1, 0, KEY_MOTION_LINE);
} else {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- buffer->cur_line = line - 1;
- buffer->cur_index = 0;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view->cur_line = line - 1;
+ view->cur_index = 0;
int text_w, text_h;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ ledit_view_line *vl = view_get_line(view, view->cur_line);
int x, y, h;
- ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, 0, &x, &y, &h);
+ view_get_cursor_pixel_pos(view, view->cur_line, 0, &x, &y, &h);
/* if cursor is not on screen anymore, move to middle of screen */
- if (ll->y_offset < buffer->display_offset ||
- ll->y_offset + h > buffer->display_offset + text_h) {
- ledit_buffer_scroll(buffer, ll->y_offset - text_h / 2);
+ if (vl->y_offset < view->display_offset ||
+ vl->y_offset + h > view->display_offset + text_h) {
+ view_scroll(view, vl->y_offset - text_h / 2);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
discard_repetition_stack();
}
return (struct action){ACTION_NONE, NULL};
@@ -644,117 +647,117 @@ move_to_line(ledit_buffer *buffer, char *text, int len) {
/* FIXME: should these scrolling functions change behavior when hard_line_based == 1? */
static void
-scroll_lines(ledit_buffer *buffer, int lines, int dir) {
+scroll_lines(ledit_view *view, int lines, int dir) {
if (last_lines_scrolled <= 0 && lines <= 0) {
/* no scroll command yet - scroll half of screen */
- move_half_screen(buffer, dir);
+ move_half_screen(view, dir);
} else {
int x, y, h, sli;
int final_lines, text_w, text_h;
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
- ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, buffer->cur_index, &x, &y, &h);
+ ledit_view_line *vl = view_get_line(view, view->cur_line);
+ view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h);
/* get the middle position of char */
- ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_index, &x, &sli);
- long abs_pos = ll->y_offset + y;
- ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
+ ledit_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &sli);
+ long abs_pos = vl->y_offset + y;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
if (lines > 0)
final_lines = last_lines_scrolled = lines;
else
final_lines = last_lines_scrolled;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
dir < 0 ? -final_lines : final_lines,
- &buffer->cur_line, &sli
+ &view->cur_line, &sli
);
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, buffer->cur_line, sli, &start, &end);
- ll = ledit_buffer_get_line(buffer, buffer->cur_line);
- ledit_x_softline_to_pos(buffer, buffer->cur_line, x, sli, &buffer->cur_index);
- ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, buffer->cur_index, &x, &y, &h);
- long new_abs_pos = ll->y_offset + y;
- ledit_buffer_scroll(buffer, buffer->display_offset + (new_abs_pos - abs_pos));
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ size_t start, end;
+ view_get_softline_bounds(view, view->cur_line, sli, &start, &end);
+ vl = view_get_line(view, view->cur_line);
+ ledit_x_softline_to_pos(view, view->cur_line, x, sli, &view->cur_index);
+ view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h);
+ long new_abs_pos = vl->y_offset + y;
+ view_scroll(view, view->display_offset + (new_abs_pos - abs_pos));
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
}
static struct action
-scroll_lines_up(ledit_buffer *buffer, char *text, int len) {
+scroll_lines_up(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- scroll_lines(buffer, repeat, -1);
+ scroll_lines(view, repeat, -1);
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-scroll_lines_down(ledit_buffer *buffer, char *text, int len) {
+scroll_lines_down(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- scroll_lines(buffer, repeat, 1);
+ scroll_lines(view, repeat, 1);
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static void
-scroll_with_cursor(ledit_buffer *buffer, int movement) {
+scroll_with_cursor(ledit_view *view, int movement) {
int x, y, h;
- ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, buffer->cur_index, &x, &y, &h);
- int pix_movement = movement * h;
- ledit_buffer_scroll(buffer, buffer->display_offset + pix_movement);
- int old_line = buffer->cur_line;
- int old_index = buffer->cur_index;
- ledit_buffer_get_nearest_legal_pos(
- buffer, old_line, old_index,
- &buffer->cur_line, &buffer->cur_index
+ view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h);
+ int pix_movement = movement * h; /* FIXME: overflow */
+ view_scroll(view, view->display_offset + pix_movement);
+ size_t old_line = view->cur_line;
+ size_t old_index = view->cur_index;
+ view_get_nearest_legal_pos(
+ view, old_line, old_index,
+ &view->cur_line, &view->cur_index
);
- if (old_line != buffer->cur_line || old_index != buffer->cur_index) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, old_line);
+ if (old_line != view->cur_line || old_index != view->cur_index) {
+ view_wipe_line_cursor_attrs(view, old_line);
/* if cursor is at top or bottom of screen, snap it to the
very edge to avoid it looking weird */
if (movement > 0) {
- ledit_buffer_scroll_to_pos_top(
- buffer, buffer->cur_line, buffer->cur_index
+ view_scroll_to_pos_top(
+ view, view->cur_line, view->cur_index
);
} else {
- ledit_buffer_scroll_to_pos_bottom(
- buffer, buffer->cur_line, buffer->cur_index
+ view_scroll_to_pos_bottom(
+ view, view->cur_line, view->cur_index
);
}
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
static struct action
-scroll_with_cursor_up(ledit_buffer *buffer, char *text, int len) {
+scroll_with_cursor_up(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- scroll_with_cursor(buffer, -(repeat == 0 ? 1 : repeat));
+ scroll_with_cursor(view, -(repeat == 0 ? 1 : repeat));
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-scroll_with_cursor_down(ledit_buffer *buffer, char *text, int len) {
+scroll_with_cursor_down(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- scroll_with_cursor(buffer, repeat == 0 ? 1 : repeat);
+ scroll_with_cursor(view, repeat == 0 ? 1 : repeat);
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
@@ -765,160 +768,161 @@ scroll_with_cursor_down(ledit_buffer *buffer, char *text, int len) {
(unless the screen is already at the very top or bottom - then it is the other way around) */
/* FIXME: this is a bit weird at the moment */
static void
-move_half_screen(ledit_buffer *buffer, int movement) {
+move_half_screen(ledit_view *view, int movement) {
int w, h;
- ledit_window_get_textview_size(buffer->window, &w, &h);
+ ledit_window_get_textview_size(view->window, &w, &h);
/* FIXME: overflow */
int total = movement * h/2;
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ ledit_view_line *vl = view_get_line(view, view->cur_line);
int cur_x, cur_y, cur_h;
- ledit_buffer_get_cursor_pixel_pos(
- buffer, buffer->cur_line, buffer->cur_index, &cur_x, &cur_y, &cur_h
+ view_get_cursor_pixel_pos(
+ view, view->cur_line, view->cur_index, &cur_x, &cur_y, &cur_h
);
- long real_cur_y = ll->y_offset - buffer->display_offset + cur_y;
+ long real_cur_y = vl->y_offset - view->display_offset + cur_y;
/* new pixel position of cursor */
/* Note: this usually causes at least part of a line of overlap
because ensure_cursor_shown scrolls back a bit if the line
isn't completely shown (this behavior could be changed using
- ledit_buffer_get_nearest_legal_pos) */
+ view_get_nearest_legal_pos) */
int y = movement > 0 ? 0 : h;
int half_screen = abs(movement) % 2 == 1;
if (half_screen) {
/* if only half screens are moved and we are at the beginning or
end, just move the cursor the movement amount instead of
moving it to the very top or bottom */
- if (buffer->display_offset + total <= 0 ||
- buffer->display_offset + total + h >= buffer->total_height) {
+ if (view->display_offset + total <= 0 ||
+ view->display_offset + total + h >= view->total_height) {
y = real_cur_y + total;
}
} else {
- if (buffer->display_offset + total <= 0)
+ if (view->display_offset + total <= 0)
y = 0;
- else if (buffer->display_offset + total + h > buffer->total_height)
+ else if (view->display_offset + total + h > view->total_height)
y = h;
}
if (y < 0)
y = 0;
if (y > h)
y = h;
- ledit_buffer_scroll(buffer, buffer->display_offset + total);
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view_scroll(view, view->display_offset + total);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
/* try to keep current x position of cursor */
int x, softline;
/* FIXME: properly document what uses PANGO_SCALE and what not */
- ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_index, &x, &softline);
+ ledit_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &softline);
ledit_xy_to_line_byte(
- buffer, x / PANGO_SCALE, y, 0,
- &buffer->cur_line, &buffer->cur_index
+ view, x / PANGO_SCALE, y, 0,
+ &view->cur_line, &view->cur_index
);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
static struct action
-screen_up(ledit_buffer *buffer, char *text, int len) {
+screen_up(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- move_half_screen(buffer, -(repeat == 0 ? 2 : repeat*2));
+ move_half_screen(view, -(repeat == 0 ? 2 : repeat*2));
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-screen_down(ledit_buffer *buffer, char *text, int len) {
+screen_down(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int repeat = get_key_repeat();
if (repeat >= 0)
- move_half_screen(buffer, repeat == 0 ? 2 : repeat*2);
+ move_half_screen(view, repeat == 0 ? 2 : repeat*2);
else
- ledit_window_show_message(buffer->window, "Invalid key", -1);
+ ledit_window_show_message(view->window, "Invalid key", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-delete_to_eol(ledit_buffer *buffer, char *text, int len) {
+delete_to_eol(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
if (!key_stack_empty())
- return err_invalid_key(buffer);
- int start, end;
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ return err_invalid_key(view);
+ size_t start, end;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
if (hard_line_based) {
end = ll->len;
} else {
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
}
delete_range(
- buffer, 0, 0,
- buffer->cur_line, buffer->cur_index,
- buffer->cur_line, end, 1
+ view, 0, 0,
+ view->cur_line, view->cur_index,
+ view->cur_line, end, 1
);
paste_buffer_line_based = 0;
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-change_to_eol(ledit_buffer *buffer, char *text, int len) {
+change_to_eol(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
if (!key_stack_empty())
- return err_invalid_key(buffer);
- ledit_buffer_set_mode(buffer, INSERT);
- int start, end;
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ return err_invalid_key(view);
+ view_set_mode(view, INSERT);
+ size_t start, end;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
if (hard_line_based) {
end = ll->len;
} else {
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
}
delete_range(
- buffer, 0, 0,
- buffer->cur_line, buffer->cur_index,
- buffer->cur_line, end, 1
+ view, 0, 0,
+ view->cur_line, view->cur_index,
+ view->cur_line, end, 1
);
paste_buffer_line_based = 0;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
return (struct action){ACTION_NONE, NULL};
}
/* FIXME: clear selection on most commands */
/* FIXME: don't include escape when repeating change with '.'? */
static struct action
-change(ledit_buffer *buffer, char *text, int len) {
+change(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb = NULL;
int num = get_key_repeat_and_motion_cb(&cb);
if (num == -1)
- return err_invalid_key(buffer);
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_mode(buffer, INSERT);
- delete_selection(buffer);
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ return err_invalid_key(view);
+ if (view->mode == VISUAL) {
+ view_set_mode(view, INSERT);
+ delete_selection(view);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
clear_key_stack();
} else {
if (cb == &change_cb) {
int lines = num > 0 ? num : 1;
- int new_line, new_softline;
+ size_t new_line;
+ int new_softline;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
lines - 1, &new_line, &new_softline
);
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &start, &end);
- cb(buffer, new_line, start, KEY_MOTION_LINE);
+ size_t start, end;
+ view_get_softline_bounds(view, new_line, new_softline, &start, &end);
+ cb(view, new_line, start, KEY_MOTION_LINE);
clear_key_stack();
} else if (cb != NULL) {
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
} else {
struct key_stack_elem *e = push_key_stack();
e->key = KEY_MOTION; /* ? */
@@ -930,55 +934,55 @@ change(ledit_buffer *buffer, char *text, int len) {
}
static void
-change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
+change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) {
/* set mode first so the deletion is included in the undo group */
- ledit_buffer_set_mode(buffer, INSERT);
+ view_set_mode(view, INSERT);
int line_based = type == KEY_MOTION_LINE ? 1 : 0;
/* this hackery is needed to avoid deleting the entire last line and
instead leave an empty line - this should be made nicer (FIXME) */
- int pos1 = buffer->cur_index, pos2 = char_pos;
+ size_t pos1 = view->cur_index, pos2 = char_pos;
if (line_based && !hard_line_based) {
- int pos1, pos2, tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &pos1, &tmp);
- ledit_buffer_get_pos_softline_bounds(buffer, line, char_pos, &tmp, &pos2);
+ size_t pos1, pos2, tmp;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &pos1, &tmp);
+ view_get_pos_softline_bounds(view, line, char_pos, &tmp, &pos2);
} else if (line_based && hard_line_based) {
pos1 = 0;
- ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
pos2 = ll->len;
}
/* force line_based to 0 (see comment about hackery above) */
delete_range(
- buffer, 0, 0,
- buffer->cur_line, pos1,
+ view, 0, 0,
+ view->cur_line, pos1,
line, pos2, 1
);
paste_buffer_line_based = line_based;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
}
static struct action
-yank(ledit_buffer *buffer, char *text, int len) {
+yank(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
if (!paste_buffer)
paste_buffer = txtbuf_new();
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_sort_selection(
- &buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2
+ if (view->mode == VISUAL) {
+ view_sort_selection(
+ &view->sel.line1, &view->sel.byte1, &view->sel.line2, &view->sel.byte2
);
ledit_buffer_copy_text_to_txtbuf(
- buffer, paste_buffer,
- buffer->sel.line1, buffer->sel.byte1, buffer->sel.line2, buffer->sel.byte2
+ view->buffer, paste_buffer,
+ view->sel.line1, view->sel.byte1, view->sel.line2, view->sel.byte2
);
paste_buffer_line_based = 0;
- buffer->cur_line = buffer->sel.line1;
- buffer->cur_index = buffer->sel.byte1;
- ledit_buffer_set_selection(buffer, -1, -1, -1, -1);
- ledit_buffer_set_mode(buffer, NORMAL);
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_line = view->sel.line1;
+ view->cur_index = view->sel.byte1;
+ view_wipe_selection(view);
+ view_set_mode(view, NORMAL);
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
clear_key_stack();
} else {
motion_callback cb = NULL;
@@ -986,14 +990,15 @@ yank(ledit_buffer *buffer, char *text, int len) {
if (num == 0)
num = 1;
if (cb == &yank_cb) {
- int new_line, new_softline;
+ size_t new_line;
+ int new_softline;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
num - 1, &new_line, &new_softline
);
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &start, &end);
- cb(buffer, new_line, start, KEY_MOTION_LINE);
+ size_t start, end;
+ view_get_softline_bounds(view, new_line, new_softline, &start, &end);
+ cb(view, new_line, start, KEY_MOTION_LINE);
clear_key_stack();
} else if (cb == NULL) {
struct key_stack_elem *e = push_key_stack();
@@ -1009,61 +1014,62 @@ yank(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-yank_lines(ledit_buffer *buffer, char *text, int len) {
+yank_lines(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int num = get_key_repeat();
if (num == -1)
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
else if (num == 0)
num = 1;
- int new_line, new_softline;
+ size_t new_line;
+ int new_softline;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
num - 1, &new_line, &new_softline
);
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &start, &end);
- yank_cb(buffer, new_line, start, KEY_MOTION_LINE);
+ size_t start, end;
+ view_get_softline_bounds(view, new_line, new_softline, &start, &end);
+ yank_cb(view, new_line, start, KEY_MOTION_LINE);
clear_key_stack();
return (struct action){ACTION_NONE, NULL};
}
static void
-yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
+yank_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) {
int line_based = type == KEY_MOTION_LINE ? 1 : 0;
- int l1 = buffer->cur_line, l2 = line, b1 = buffer->cur_index, b2 = char_pos;
+ size_t l1 = view->cur_line, l2 = line, b1 = view->cur_index, b2 = char_pos;
if (!paste_buffer)
paste_buffer = txtbuf_new();
if (l2 < l1 || (l1 == l2 && b2 < b1)) {
- swap(&l1, &l2);
- swap(&b1, &b2);
+ swap_sz(&l1, &l2);
+ swap_sz(&b1, &b2);
}
if (line_based && !hard_line_based) {
- int start1, end2, tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, l1, b1, &start1, &tmp);
- ledit_buffer_get_pos_softline_bounds(buffer, l2, b2, &tmp, &end2);
- ledit_line *ll = ledit_buffer_get_line(buffer, l2);
- if (end2 == ll->len && l2 < buffer->lines_num - 1) {
+ size_t start1, end2, tmp;
+ view_get_pos_softline_bounds(view, l1, b1, &start1, &tmp);
+ view_get_pos_softline_bounds(view, l2, b2, &tmp, &end2);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, l2);
+ if (end2 == ll->len && l2 < view->lines_num - 1) {
l2++;
end2 = 0;
}
ledit_buffer_copy_text_to_txtbuf(
- buffer, paste_buffer, l1, start1, l2, end2
+ view->buffer, paste_buffer, l1, start1, l2, end2
);
} else if (line_based && hard_line_based) {
- ledit_line *ll = ledit_buffer_get_line(buffer, l2);
- int end = ll->len;
- if (l2 < buffer->lines_num - 1) {
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, l2);
+ size_t end = ll->len;
+ if (l2 < view->lines_num - 1) {
l2++;
end = 0;
}
ledit_buffer_copy_text_to_txtbuf(
- buffer, paste_buffer, l1, 0, l2, end
+ view->buffer, paste_buffer, l1, 0, l2, end
);
} else {
ledit_buffer_copy_text_to_txtbuf(
- buffer, paste_buffer, l1, b1, l2, b2
+ view->buffer, paste_buffer, l1, b1, l2, b2
);
}
paste_buffer_line_based = line_based;
@@ -1071,33 +1077,34 @@ yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
}
static struct action
-delete(ledit_buffer *buffer, char *text, int len) {
+delete(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb = NULL;
int num = get_key_repeat_and_motion_cb(&cb);
if (num == -1)
- return err_invalid_key(buffer);
- if (delete_selection(buffer)) {
- ledit_buffer_set_mode(buffer, NORMAL);
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(buffer, buffer->cur_line, buffer->cur_index);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ return err_invalid_key(view);
+ if (delete_selection(view)) {
+ view_set_mode(view, NORMAL);
+ 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);
clear_key_stack();
} else {
/* FIXME: checking equality of the function pointer may be a bit risky */
if (cb == &delete_cb) {
int lines = num > 0 ? num : 1;
- int new_line, new_softline;
+ size_t new_line;
+ int new_softline;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
lines - 1, &new_line, &new_softline
);
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &start, &end);
- cb(buffer, new_line, start, KEY_MOTION_LINE);
+ size_t start, end;
+ view_get_softline_bounds(view, new_line, new_softline, &start, &end);
+ cb(view, new_line, start, KEY_MOTION_LINE);
clear_key_stack();
} else if (cb != NULL) {
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
} else {
struct key_stack_elem *e = push_key_stack();
e->key = KEY_MOTION; /* ? */
@@ -1110,15 +1117,15 @@ delete(ledit_buffer *buffer, char *text, int len) {
/* FIXME: should this get number of lines to remove or actual end line? */
static void
-delete_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
+delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) {
int line_based = type == KEY_MOTION_LINE ? 1 : 0;
delete_range(
- buffer, line_based, 0,
- buffer->cur_line, buffer->cur_index,
+ view, line_based, 0,
+ view->cur_line, view->cur_index,
line, char_pos, 1
);
paste_buffer_line_based = line_based;
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
finalize_repetition_stack();
}
@@ -1127,30 +1134,30 @@ delete_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
hard lines, which may be unexpected, but the alternatives I could think of are
even weirder */
static struct action
-paste_normal(ledit_buffer *buffer, char *text, int len) {
+paste_normal(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
if (!paste_buffer) {
- ledit_window_show_message(buffer->window, "Nothing to paste", -1);
+ ledit_window_show_message(view->window, "Nothing to paste", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
if (paste_buffer_line_based) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
- int brk = 0;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
+ size_t brk = 0;
if (hard_line_based) {
brk = ll->len;
} else {
- int tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &tmp, &brk);
+ size_t tmp;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &tmp, &brk);
}
insert_text(
- buffer, buffer->cur_line, brk,
- "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1
+ view, view->cur_line, brk,
+ "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 1
);
- int text_len = paste_buffer->len;
- ll = ledit_buffer_get_line(buffer, buffer->cur_line + 1);
+ size_t text_len = paste_buffer->len;
+ ll = ledit_buffer_get_line(view->buffer, view->cur_line + 1);
if (ll->len == 0 && paste_buffer->text[text_len-1] == '\n') {
/* remove trailing newline if it exists and text is already on own line */
text_len--;
@@ -1158,79 +1165,80 @@ paste_normal(ledit_buffer *buffer, char *text, int len) {
} else if (ll->len > 0 && paste_buffer->text[text_len-1] != '\n') {
/* ensure pasted text is on its own hard line */
insert_text(
- buffer, buffer->cur_line + 1, 0,
- "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 0
+ view, view->cur_line + 1, 0,
+ "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 0
);
}
insert_text(
- buffer, buffer->cur_line + 1, 0,
- paste_buffer->text, text_len, -1, -1, buffer->cur_line + 1, 0, 0
+ view, view->cur_line + 1, 0,
+ paste_buffer->text, text_len, 0, 0, view->cur_line + 1, 0, 0, 1, 0
);
} else {
- int old_line = buffer->cur_line;
- int old_index = buffer->cur_index;
+ size_t old_line = view->cur_line;
+ size_t old_index = view->cur_index;
/* must allow illegal index so text can be pasted at end of line */
- move_cursor_logically(buffer, 1, 1);
+ move_cursor_logically(view, 1, 1);
insert_text(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
paste_buffer->text, paste_buffer->len,
- old_line, old_index, buffer->cur_line, buffer->cur_index, 1
+ old_line, old_index, view->cur_line, view->cur_index, 1, 1, 1
);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
finalize_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-paste_normal_backwards(ledit_buffer *buffer, char *text, int len) {
+paste_normal_backwards(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
if (!paste_buffer) {
- ledit_window_show_message(buffer->window, "Nothing to paste", -1);
+ ledit_window_show_message(view->window, "Nothing to paste", -1);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
if (paste_buffer_line_based) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
- int brk = 0;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line);
+ size_t brk = 0;
if (!hard_line_based) {
- int tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &brk, &tmp);
+ size_t tmp;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &brk, &tmp);
}
+ /* FIXME: better interface without these weird int args */
insert_text(
- buffer, buffer->cur_line, brk,
- "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1
+ view, view->cur_line, brk,
+ "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 1
);
- int text_len = paste_buffer->len;
- ll = ledit_buffer_get_line(buffer, buffer->cur_line);
+ size_t text_len = paste_buffer->len;
+ ll = ledit_buffer_get_line(view->buffer, view->cur_line);
if (paste_buffer->text[text_len-1] == '\n') {
/* remove trailing newline if it exists */
text_len--;
paste_buffer->text[text_len] = '\0';
}
- int new_line = buffer->cur_line;
+ size_t new_line = view->cur_line;
if (ll->len > 0) {
/* ensure pasted text is on its own hard line */
insert_text(
- buffer, buffer->cur_line, ll->len,
- "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 0
+ view, view->cur_line, ll->len,
+ "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 0
);
- new_line = buffer->cur_line + 1;
+ new_line = view->cur_line + 1;
}
insert_text(
- buffer, new_line, 0,
- paste_buffer->text, text_len, -1, -1, new_line, 0, 0
+ view, new_line, 0,
+ paste_buffer->text, text_len, 0, 0, new_line, 0, 0, 1, 0
);
} else {
insert_text(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
paste_buffer->text, paste_buffer->len,
- -1, -1, buffer->cur_line, buffer->cur_index, 1
+ 0, 0, view->cur_line, view->cur_index, 0, 1, 1
);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
finalize_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
@@ -1243,14 +1251,22 @@ push_num(int num) {
e->key = KEY_NUMBER;
e->followup = KEY_NUMBER|KEY_NUMBERALLOWED;
}
- /* FIXME: error (overflow) checking */
+ /* FIXME: error messages */
+ if (INT_MAX / 10 < e->count) {
+ clear_key_stack();
+ return;
+ }
e->count *= 10;
+ if (INT_MAX - num < e->count) {
+ clear_key_stack();
+ return;
+ }
e->count += num;
}
static struct action
-push_0(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_0(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(0);
@@ -1258,8 +1274,8 @@ push_0(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_1(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_1(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(1);
@@ -1267,8 +1283,8 @@ push_1(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_2(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_2(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(2);
@@ -1276,8 +1292,8 @@ push_2(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_3(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_3(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(3);
@@ -1285,8 +1301,8 @@ push_3(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_4(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_4(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(4);
@@ -1294,8 +1310,8 @@ push_4(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_5(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_5(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(5);
@@ -1303,8 +1319,8 @@ push_5(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_6(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_6(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(6);
@@ -1312,8 +1328,8 @@ push_6(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_7(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_7(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(7);
@@ -1321,8 +1337,8 @@ push_7(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_8(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_8(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(8);
@@ -1330,8 +1346,8 @@ push_8(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-push_9(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+push_9(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
push_num(9);
@@ -1339,130 +1355,129 @@ push_9(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-backspace(ledit_buffer *buffer, char *text, int len) {
+backspace(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
/* FIXME: don't copy to paste buffer on del_sel here; delete entire grapheme */
- if (delete_selection(buffer)) {
+ if (delete_selection(view)) {
/* NOP */
- } else if (buffer->cur_index == 0) {
- if (buffer->cur_line != 0) {
- ledit_line *l1 = ledit_buffer_get_line(buffer, buffer->cur_line - 1);
- delete_range(buffer, 0, 0, buffer->cur_line - 1, l1->len, buffer->cur_line, 0, 0);
+ } else if (view->cur_index == 0) {
+ if (view->cur_line != 0) {
+ ledit_line *l1 = ledit_buffer_get_line(view->buffer, view->cur_line - 1);
+ delete_range(view, 0, 0, view->cur_line - 1, l1->len, view->cur_line, 0, 0);
}
} else {
- ledit_line *l = ledit_buffer_get_line(buffer, buffer->cur_line);
- int i = ledit_line_prev_utf8(l, buffer->cur_index);
- delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i, 0);
+ ledit_line *l = ledit_buffer_get_line(view->buffer, view->cur_line);
+ int i = ledit_line_prev_utf8(l, view->cur_index);
+ delete_range(view, 0, 0, view->cur_line, view->cur_index, view->cur_line, i, 0);
}
- /* FIXME: This was probably a mistake earlier, right?
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);*/
return (struct action){ACTION_NONE, NULL};
}
static struct action
-delete_key(ledit_buffer *buffer, char *text, int len) {
+delete_key(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
- if (delete_selection(buffer)) {
+ ledit_line *cur_line = ledit_buffer_get_line(view->buffer, view->cur_line);
+ if (delete_selection(view)) {
/* NOP */
- } else if (buffer->cur_index == cur_line->len) {
- if (buffer->cur_line != buffer->lines_num - 1) {
- delete_range(buffer, 0, 0, buffer->cur_line, cur_line->len, buffer->cur_line + 1, 0, 0);
+ } else if (view->cur_index == cur_line->len) {
+ if (view->cur_line != view->lines_num - 1) {
+ delete_range(view, 0, 0, view->cur_line, cur_line->len, view->cur_line + 1, 0, 0);
}
} else {
- int i = ledit_line_next_utf8(cur_line, buffer->cur_index);
- delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i, 0);
+ int i = ledit_line_next_utf8(cur_line, view->cur_index);
+ delete_range(view, 0, 0, view->cur_line, view->cur_index, view->cur_line, i, 0);
}
/* FIXME: This was probably a mistake earlier, right?
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);*/
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);*/
return (struct action){ACTION_NONE, NULL};
}
static struct action
-move_to_eol(ledit_buffer *buffer, char *text, int len) {
+move_to_eol(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num == -1)
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
if (num == 0)
num = 1;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- int new_line, new_softline;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ size_t new_line;
+ int new_softline;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index, num - 1,
+ view, view->cur_line, view->cur_index, num - 1,
&new_line, &new_softline
);
- ledit_line *ll = ledit_buffer_get_line(buffer, new_line);
- int end_index = ll->len;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, new_line);
+ size_t end_index = ll->len;
if (!hard_line_based) {
- int tmp;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &tmp, &end_index);
+ size_t tmp;
+ view_get_softline_bounds(view, new_line, new_softline, &tmp, &end_index);
}
if (cb != NULL) {
- cb(buffer, new_line, end_index, KEY_MOTION_CHAR);
+ cb(view, new_line, end_index, KEY_MOTION_CHAR);
} else {
- buffer->cur_line = new_line;
- buffer->cur_index = end_index;
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_selection(
- buffer,
- buffer->sel.line1, buffer->sel.byte1,
+ view->cur_line = new_line;
+ view->cur_index = end_index;
+ if (view->mode == VISUAL) {
+ view_set_selection(
+ view,
+ view->sel.line1, view->sel.byte1,
new_line, end_index
);
} else {
/* FIXME: this is weird because the cursor is actually on the
next soft line, but the alternative has too many weird effects
with bidi text */
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
return (struct action){ACTION_NONE, NULL};
}
#define GEN_WORD_MOVEMENT(name, func) \
static struct action \
-name(ledit_buffer *buffer, char *text, int len) { \
+name(ledit_view *view, char *text, size_t len) { \
(void)text; \
(void)len; \
motion_callback cb; \
int num = get_key_repeat_and_motion_cb(&cb); \
if (num == -1) \
- return err_invalid_key(buffer); \
+ return err_invalid_key(view); \
if (num == 0) \
num = 1; \
- int new_line, new_index, new_real_index; \
+ size_t new_line, new_index, new_real_index; \
func( \
- buffer, \
- buffer->cur_line, buffer->cur_index, num, \
+ view, \
+ view->cur_line, view->cur_index, num, \
&new_line, &new_index, &new_real_index \
); \
if (cb != NULL) { \
- cb(buffer, new_line, new_real_index, KEY_MOTION_CHAR); \
+ cb(view, new_line, new_real_index, KEY_MOTION_CHAR); \
} else { \
- if (buffer->common->mode == VISUAL) { \
- ledit_buffer_set_selection( \
- buffer, \
- buffer->sel.line1, buffer->sel.byte1, \
+ if (view->mode == VISUAL) { \
+ view_set_selection( \
+ view, \
+ view->sel.line1, view->sel.byte1, \
new_line, new_real_index \
); \
- buffer->cur_line = new_line; \
- buffer->cur_index = new_real_index; \
+ view->cur_line = new_line; \
+ view->cur_index = new_real_index; \
} else { \
- if (new_line != buffer->cur_line) \
- ledit_buffer_wipe_line_cursor_attrs( \
- buffer, buffer->cur_line \
+ if (new_line != view->cur_line) \
+ view_wipe_line_cursor_attrs( \
+ view, view->cur_line \
); \
- buffer->cur_line = new_line; \
- buffer->cur_index = new_index; \
- ledit_buffer_set_line_cursor_attrs( \
- buffer, buffer->cur_line, buffer->cur_index \
+ view->cur_line = new_line; \
+ view->cur_index = new_index; \
+ view_set_line_cursor_attrs( \
+ view, view->cur_line, view->cur_index \
); \
} \
discard_repetition_stack(); \
@@ -1471,33 +1486,33 @@ name(ledit_buffer *buffer, char *text, int len) {
return (struct action){ACTION_NONE, NULL}; \
}
-GEN_WORD_MOVEMENT(next_word, ledit_buffer_next_word)
-GEN_WORD_MOVEMENT(next_word_end, ledit_buffer_next_word_end)
-GEN_WORD_MOVEMENT(next_bigword, ledit_buffer_next_bigword)
-GEN_WORD_MOVEMENT(next_bigword_end, ledit_buffer_next_bigword_end)
-GEN_WORD_MOVEMENT(prev_word, ledit_buffer_prev_word)
-GEN_WORD_MOVEMENT(prev_bigword, ledit_buffer_prev_bigword)
+GEN_WORD_MOVEMENT(next_word, view_next_word)
+GEN_WORD_MOVEMENT(next_word_end, view_next_word_end)
+GEN_WORD_MOVEMENT(next_bigword, view_next_bigword)
+GEN_WORD_MOVEMENT(next_bigword_end, view_next_bigword_end)
+GEN_WORD_MOVEMENT(prev_word, view_prev_word)
+GEN_WORD_MOVEMENT(prev_bigword, view_prev_bigword)
static void
-move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
+move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_index) {
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num == -1)
- (void)err_invalid_key(buffer);
+ (void)err_invalid_key(view);
if (num == 0)
num = 1;
- ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
+ ledit_line *cur_line = ledit_buffer_get_line(view->buffer, view->cur_line);
/* FIXME: standardize interface - num * dir or separately? */
- int last_index;
- int new_index = ledit_buffer_move_cursor_visually(
- buffer, buffer->cur_line, buffer->cur_index, num * dir, &last_index
+ size_t last_index;
+ size_t new_index = view_move_cursor_visually(
+ view, view->cur_line, view->cur_index, num * dir, &last_index
);
/* 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 (!allow_illegal_index &&
- buffer->common->mode == NORMAL && cb == NULL) {
+ view->mode == NORMAL && cb == NULL) {
new_index = last_index;
} else {
/* FIXME: I guess this is unnecessary */
@@ -1505,17 +1520,15 @@ move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
}
}
if (cb != NULL) {
- cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR);
+ cb(view, view->cur_line, new_index, KEY_MOTION_CHAR);
} else {
- buffer->cur_index = new_index;
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_selection(buffer, buffer->sel.line1, buffer->sel.byte1, buffer->sel.line2, new_index);
- } else if (buffer->common->mode == INSERT &&
- (buffer->sel.line1 != buffer->sel.line2 ||
- buffer->sel.byte1 != buffer->sel.byte2)) {
- ledit_buffer_set_selection(buffer, buffer->cur_line, new_index, buffer->cur_line, new_index);
- } else if (buffer->common->mode == NORMAL) {
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view->cur_index = new_index;
+ if (view->mode == VISUAL) {
+ view_set_selection(view, view->sel.line1, view->sel.byte1, view->cur_line, new_index);
+ } else if (view->mode == INSERT && view->sel_valid) {
+ view_wipe_selection(view);
+ } else if (view->mode == NORMAL) {
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
discard_repetition_stack();
}
@@ -1523,133 +1536,118 @@ move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
}
static struct action
-cursor_left(ledit_buffer *buffer, char *text, int len) {
+cursor_left(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- move_cursor_left_right(buffer, -1, 0);
+ move_cursor_left_right(view, -1, 0);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-cursor_right(ledit_buffer *buffer, char *text, int len) {
+cursor_right(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- move_cursor_left_right(buffer, 1, 0);
+ move_cursor_left_right(view, 1, 0);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-return_key(ledit_buffer *buffer, char *text, int len) {
+return_key(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int start_group = 1;
- if (delete_selection(buffer))
+ if (delete_selection(view))
start_group = 0;
- insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, -1, -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);
- buffer->cur_line++;
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
- buffer->cur_index = 0; */
+ insert_text(view, view->cur_line, view->cur_index, "\n", 1, 0, 0, 0, 0, 0, 0, start_group);
return (struct action){ACTION_NONE, NULL};
}
static void
-move_cursor_logically(ledit_buffer *buffer, int movement_dir, int allow_illegal_index) {
+move_cursor_logically(ledit_view *view, int movement_dir, int allow_illegal_index) {
if (movement_dir < 0) {
- buffer->cur_index = ledit_buffer_prev_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, 1
+ view->cur_index = view_prev_cursor_pos(
+ view, view->cur_line, view->cur_index, 1
);
} else {
- buffer->cur_index = ledit_buffer_next_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, 1
+ view->cur_index = view_next_cursor_pos(
+ view, view->cur_line, view->cur_index, 1
);
}
if (!allow_illegal_index) {
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
}
}
static struct action
-escape_key(ledit_buffer *buffer, char *text, int len) {
+escape_key(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
clear_key_stack();
- if (buffer->common->mode == INSERT)
+ if (view->mode == INSERT)
finalize_repetition_stack();
- if (buffer->common->mode == INSERT &&
- (buffer->sel.line1 != buffer->sel.line2 ||
- buffer->sel.byte1 != buffer->sel.byte2)) {
- ledit_buffer_set_mode(buffer, VISUAL);
- } else if (buffer->common->mode != NORMAL) {
- ledit_buffer_set_mode(buffer, NORMAL);
- 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;
- for (int i = min; i <= max; i++) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, i);
- }
- }
- buffer->sel.line1 = buffer->sel.line2 = -1;
- buffer->sel.byte1 = buffer->sel.byte2 = -1;
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ if (view->mode == INSERT && view->sel_valid) {
+ view_set_mode(view, VISUAL);
+ } else if (view->mode != NORMAL) {
+ view_set_mode(view, NORMAL);
+ move_cursor_logically(view, -1, 0);
+ view_wipe_selection(view);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
return (struct action){ACTION_NONE, NULL};
}
static struct action
-enter_insert(ledit_buffer *buffer, char *text, int len) {
+enter_insert(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- if (buffer->common->mode == NORMAL)
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- ledit_buffer_set_mode(buffer, INSERT);
+ if (view->mode == NORMAL)
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view_set_mode(view, INSERT);
clear_key_stack();
return (struct action){ACTION_NONE, NULL};
}
/* FIXME: Check if previous key allows motion command - or should this be checked automatically before? */
static void
-move_cursor_up_down(ledit_buffer *buffer, int dir) {
- int new_line, new_softline;
+move_cursor_up_down(ledit_view *view, int dir) {
+ size_t new_line;
+ int new_softline;
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num == -1)
- (void)err_invalid_key(buffer);
+ (void)err_invalid_key(view);
if (num == 0)
num = 1;
num *= dir;
get_new_line_softline(
- buffer, buffer->cur_line, buffer->cur_index,
+ view, view->cur_line, view->cur_index,
num, &new_line, &new_softline
);
if (cb != NULL) {
- int start, end;
- ledit_buffer_get_softline_bounds(buffer, new_line, new_softline, &start, &end);
- cb(buffer, new_line, start, KEY_MOTION_LINE);
+ size_t start, end;
+ view_get_softline_bounds(view, new_line, new_softline, &start, &end);
+ cb(view, new_line, start, KEY_MOTION_LINE);
} else {
+ /* FIXME: when selecting on last line, moving down moves the cursor back
+ one (when it stays on the same line because it's the last one) */
int lineno, x;
- ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_index, &x, &lineno);
- ledit_x_softline_to_pos(buffer, new_line, x, new_softline, &buffer->cur_index);
- if (buffer->cur_line != new_line)
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- buffer->cur_line = new_line;
-
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_selection(buffer, buffer->sel.line1, buffer->sel.byte1, buffer->cur_line, buffer->cur_index);
- } else if (buffer->common->mode == INSERT &&
- (buffer->sel.line1 != buffer->sel.line2 ||
- buffer->sel.byte1 != buffer->sel.byte2)) {
- ledit_buffer_set_selection(buffer, buffer->cur_line, buffer->cur_index, buffer->cur_line, buffer->cur_index);
- } else if (buffer->common->mode == NORMAL) {
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ ledit_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &lineno);
+ ledit_x_softline_to_pos(view, new_line, x, new_softline, &view->cur_index);
+ if (view->cur_line != new_line)
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view->cur_line = new_line;
+
+ if (view->mode == VISUAL) {
+ view_set_selection(view, view->sel.line1, view->sel.byte1, view->cur_line, view->cur_index);
+ } else if (view->mode == INSERT && view->sel_valid) {
+ view_wipe_selection(view);
+ } else if (view->mode == NORMAL) {
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
}
discard_repetition_stack();
}
@@ -1657,63 +1655,63 @@ move_cursor_up_down(ledit_buffer *buffer, int dir) {
}
static struct action
-cursor_down(ledit_buffer *buffer, char *text, int len) {
+cursor_down(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- move_cursor_up_down(buffer, 1);
+ move_cursor_up_down(view, 1);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-cursor_up(ledit_buffer *buffer, char *text, int len) {
+cursor_up(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- move_cursor_up_down(buffer, -1);
+ move_cursor_up_down(view, -1);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-join_lines(ledit_buffer *buffer, char *text, int len) {
+join_lines(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
int num = get_key_repeat();
if (num == -1)
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
if (num == 0)
num = 1;
int start_group = 1;
ledit_line *ll1;
- int cur_line = buffer->cur_line;
+ size_t cur_line = view->cur_line;
/* FIXME: have a general tmp buf for everyone to use */
txtbuf *buf = txtbuf_new();
- int oldlen;
+ size_t oldlen;
ledit_range cur_range, del_range;
cur_range.line1 = cur_range.line2 = cur_line;
for (int i = 0; i < num; i++) {
- if (cur_line == buffer->lines_num - 1)
+ if (cur_line == view->lines_num - 1)
break;
/* getting cur line again should be unnecessary, but
I'll just leave it in case I change the way lines
are stored later */
- ll1 = ledit_buffer_get_line(buffer, cur_line);
+ ll1 = ledit_buffer_get_line(view->buffer, cur_line);
oldlen = ll1->len;
/* FIXME: truncate whitespace to one space */
- ledit_buffer_delete_range(
- buffer, DELETE_CHAR,
+ view_delete_range(
+ view, DELETE_CHAR,
cur_line, ll1->len, cur_line + 1, 0,
NULL, NULL, &del_range, buf
);
- cur_range.byte1 = buffer->cur_index;
- cur_range.byte2 = buffer->cur_index =
- ledit_buffer_get_legal_normal_pos(buffer, buffer->cur_line, oldlen);
+ cur_range.byte1 = view->cur_index;
+ cur_range.byte2 = view->cur_index =
+ view_get_legal_normal_pos(view, view->cur_line, oldlen);
ledit_push_undo_delete(
- buffer->undo, buf, del_range, cur_range,
- start_group, buffer->common->mode
+ view->buffer->undo, buf, del_range, cur_range,
+ start_group, view->mode
);
start_group = 0;
}
- ledit_buffer_set_line_cursor_attrs(
- buffer, buffer->cur_line, buffer->cur_index
+ view_set_line_cursor_attrs(
+ view, view->cur_line, view->cur_index
);
txtbuf_destroy(buf);
finalize_repetition_stack();
@@ -1721,49 +1719,49 @@ join_lines(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-insert_at_beginning(ledit_buffer *buffer, char *text, int len) {
+insert_at_beginning(ledit_view *view, char *text, size_t len) {
if (!key_stack_empty())
- return err_invalid_key(buffer);
- enter_insert(buffer, text, len);
- int new_index = 0;
+ return err_invalid_key(view);
+ enter_insert(view, text, len);
+ size_t new_index = 0;
if (!hard_line_based) {
- int tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &new_index, &tmp);
+ size_t tmp;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &new_index, &tmp);
}
- push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1);
- buffer->cur_index = new_index;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ push_undo_empty_insert(view, view->cur_line, view->cur_index, 1);
+ view->cur_index = new_index;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len) {
+cursor_to_first_non_ws(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num != 0)
- return err_invalid_key(buffer);
- int new_index = 0;
+ return err_invalid_key(view);
+ size_t new_index = 0;
if (hard_line_based) {
- new_index = ledit_line_next_non_whitespace(buffer, buffer->cur_line, 0);
+ new_index = view_line_next_non_whitespace(view, view->cur_line, 0);
} else {
- int start, end;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
- new_index = ledit_line_next_non_whitespace(buffer, buffer->cur_line, start);
+ size_t start, end;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end);
+ new_index = view_line_next_non_whitespace(view, view->cur_line, start);
/* next non-whitespace might be on next softline */
if (new_index >= end) {
- new_index = ledit_buffer_prev_cursor_pos(
- buffer, buffer->cur_line, end, 1
+ new_index = view_prev_cursor_pos(
+ view, view->cur_line, end, 1
);
}
}
if (cb != NULL) {
- cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR);
+ cb(view, view->cur_line, new_index, KEY_MOTION_CHAR);
} else {
- buffer->cur_index = new_index;
- ledit_buffer_set_line_cursor_attrs(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = new_index;
+ view_set_line_cursor_attrs(
+ view, view->cur_line, view->cur_index
);
discard_repetition_stack();
}
@@ -1771,32 +1769,32 @@ cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-cursor_to_beginning(ledit_buffer *buffer, char *text, int len) {
+cursor_to_beginning(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num != 0)
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
/* FIXME: should anything be done with num? */
- int start_index = 0;
+ size_t start_index = 0;
if (!hard_line_based) {
- int tmp;
- ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start_index, &tmp);
+ size_t tmp;
+ view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start_index, &tmp);
}
if (cb != NULL) {
- cb(buffer, buffer->cur_line, start_index, KEY_MOTION_CHAR);
+ cb(view, view->cur_line, start_index, KEY_MOTION_CHAR);
} else {
- buffer->cur_index = start_index;
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_selection(
- buffer,
- buffer->sel.line1, buffer->sel.byte1,
- buffer->cur_line, buffer->cur_index
+ view->cur_index = start_index;
+ if (view->mode == VISUAL) {
+ view_set_selection(
+ view,
+ view->sel.line1, view->sel.byte1,
+ view->cur_line, view->cur_index
);
} else {
- ledit_buffer_set_line_cursor_attrs(
- buffer, buffer->cur_line, buffer->cur_index
+ view_set_line_cursor_attrs(
+ view, view->cur_line, view->cur_index
);
}
discard_repetition_stack();
@@ -1806,25 +1804,25 @@ cursor_to_beginning(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-enter_visual(ledit_buffer *buffer, char *text, int len) {
+enter_visual(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_buffer_set_mode(buffer, VISUAL);
- buffer->sel.line1 = buffer->sel.line2 = buffer->cur_line;
- buffer->sel.byte1 = buffer->sel.byte2 = buffer->cur_index;
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
+ view_set_mode(view, VISUAL);
+ view->sel.line1 = view->sel.line2 = view->cur_line;
+ view->sel.byte1 = view->sel.byte2 = view->cur_index;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
clear_key_stack(); /* FIXME: error if not empty? */
return (struct action){ACTION_NONE, NULL};
}
static struct action
-switch_selection_end(ledit_buffer *buffer, char *text, int len) {
+switch_selection_end(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- swap(&buffer->sel.line1, &buffer->sel.line2);
- swap(&buffer->sel.byte1, &buffer->sel.byte2);
- buffer->cur_line = buffer->sel.line2;
- buffer->cur_index = buffer->sel.byte2;
+ swap_sz(&view->sel.line1, &view->sel.line2);
+ swap_sz(&view->sel.byte1, &view->sel.byte2);
+ view->cur_line = view->sel.line2;
+ view->cur_index = view->sel.byte2;
return (struct action){ACTION_NONE, NULL};
}
@@ -1832,103 +1830,106 @@ switch_selection_end(ledit_buffer *buffer, char *text, int len) {
#define XK_NO_MOD 0
static struct action
-enter_commandedit(ledit_buffer *buffer, char *text, int len) {
+enter_commandedit(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_window_set_bottom_bar_text(buffer->window, ":", -1);
- ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
- ledit_window_set_bottom_bar_cursor(buffer->window, 1);
- ledit_command_set_type(CMD_EDIT);
- ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
+ ledit_window_set_bottom_bar_text(view->window, ":", -1);
+ ledit_window_set_bottom_bar_min_pos(view->window, 1);
+ ledit_window_set_bottom_bar_cursor(view->window, 1);
+ view->cur_command_type = CMD_EDIT;
+ ledit_window_set_bottom_bar_text_shown(view->window, 1);
discard_repetition_stack();
return (struct action){ACTION_GRABKEY, &ledit_command_key_handler};
}
static struct action
-enter_searchedit_forward(ledit_buffer *buffer, char *text, int len) {
+enter_searchedit_forward(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_window_set_bottom_bar_text(buffer->window, "/", -1);
- ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
- ledit_window_set_bottom_bar_cursor(buffer->window, 1);
- ledit_command_set_type(CMD_EDITSEARCH);
- ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
+ ledit_window_set_bottom_bar_text(view->window, "/", -1);
+ ledit_window_set_bottom_bar_min_pos(view->window, 1);
+ ledit_window_set_bottom_bar_cursor(view->window, 1);
+ view->cur_command_type = CMD_EDITSEARCH;
+ ledit_window_set_bottom_bar_text_shown(view->window, 1);
discard_repetition_stack();
return (struct action){ACTION_GRABKEY, &ledit_command_key_handler};
}
static struct action
-enter_searchedit_backward(ledit_buffer *buffer, char *text, int len) {
+enter_searchedit_backward(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_window_set_bottom_bar_text(buffer->window, "?", -1);
- ledit_window_set_bottom_bar_min_pos(buffer->window, 1);
- ledit_window_set_bottom_bar_cursor(buffer->window, 1);
- ledit_command_set_type(CMD_EDITSEARCHB);
- ledit_window_set_bottom_bar_text_shown(buffer->window, 1);
+ ledit_window_set_bottom_bar_text(view->window, "?", -1);
+ ledit_window_set_bottom_bar_min_pos(view->window, 1);
+ ledit_window_set_bottom_bar_cursor(view->window, 1);
+ view->cur_command_type = CMD_EDITSEARCHB;
+ ledit_window_set_bottom_bar_text_shown(view->window, 1);
discard_repetition_stack();
return (struct action){ACTION_GRABKEY, &ledit_command_key_handler};
}
/* FIXME: differentiate between jumping to line and index like nvi */
static struct action
-mark_line_cb(ledit_buffer *buffer, char *text, int len) {
+mark_line_cb(ledit_view *view, char *text, size_t len) {
grab_char_cb = NULL;
ledit_buffer_insert_mark(
- buffer, text, len, buffer->cur_line, buffer->cur_index
+ view->buffer, text, len, view->cur_line, view->cur_index
);
return (struct action){ACTION_NONE, NULL};
}
-/* FIXME: check that byte is actually in at grapheme boundary */
+/* FIXME: move part of this to buffer.c */
+/* FIXME: check that byte is actually at grapheme boundary */
static struct action
-jump_to_mark_cb(ledit_buffer *buffer, char *text, int len) {
+jump_to_mark_cb(ledit_view *view, char *text, size_t len) {
grab_char_cb = NULL;
- ledit_buffer_marklist *marklist = buffer->marklist;
+ ledit_buffer_marklist *marklist = view->buffer->marklist;
motion_callback cb;
int num = get_key_repeat_and_motion_cb(&cb);
if (num > 0)
- return err_invalid_key(buffer);
- int line = -1, index = -1;
+ return err_invalid_key(view);
+ size_t line = 0, index = 0;
+ int mark_found = 0;
for (size_t i = 0; i < marklist->len; i++) {
if (!strncmp(text, marklist->marks[i].text, len)) {
ledit_line *ll;
ledit_buffer_mark *m = &marklist->marks[i];
- if (m->line >= buffer->lines_num) {
- line = buffer->lines_num - 1;
- ll = ledit_buffer_get_line(buffer, line);
+ if (m->line >= view->lines_num) {
+ line = view->lines_num - 1;
+ ll = ledit_buffer_get_line(view->buffer, line);
index = ll->len;
} else {
line = m->line;
- ll = ledit_buffer_get_line(buffer, m->line);
+ ll = ledit_buffer_get_line(view->buffer, m->line);
if (m->byte >= ll->len)
index = ll->len;
else
index = m->byte;
}
+ mark_found = 1;
break;
}
}
- if (line == -1)
- return err_invalid_key(buffer);
- if (buffer->common->mode == VISUAL) {
- ledit_buffer_set_selection(
- buffer, buffer->sel.line1, buffer->sel.byte1, line, index
+ if (!mark_found)
+ return err_invalid_key(view);
+ if (view->mode == VISUAL) {
+ view_set_selection(
+ view, view->sel.line1, view->sel.byte1, line, index
);
- buffer->cur_line = line;
- buffer->cur_index = index;
+ view->cur_line = line;
+ view->cur_index = index;
} else {
if (cb) {
- cb(buffer, line, index, KEY_MOTION_LINE);
+ cb(view, line, index, KEY_MOTION_LINE);
} else {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- buffer->cur_line = line;
- buffer->cur_index = index;
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view->cur_line = line;
+ view->cur_index = index;
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
- ledit_buffer_set_line_cursor_attrs(
- buffer, buffer->cur_line, buffer->cur_index
+ view_set_line_cursor_attrs(
+ view, view->cur_line, view->cur_index
);
discard_repetition_stack();
}
@@ -1937,8 +1938,8 @@ jump_to_mark_cb(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-mark_line(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+mark_line(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
grab_char_cb = &mark_line_cb;
@@ -1947,8 +1948,8 @@ mark_line(ledit_buffer *buffer, char *text, int len) {
}
static struct action
-jump_to_mark(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+jump_to_mark(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
grab_char_cb = &jump_to_mark_cb;
@@ -1958,125 +1959,129 @@ jump_to_mark(ledit_buffer *buffer, char *text, int len) {
/* FIXME: support visual mode, i.e. change selection to new place? */
static struct action
-key_search_next(ledit_buffer *buffer, char *text, int len) {
+key_search_next(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- search_next(buffer);
+ search_next(view);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-key_search_prev(ledit_buffer *buffer, char *text, int len) {
+key_search_prev(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- search_prev(buffer);
+ search_prev(view);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-show_line(ledit_buffer *buffer, char *text, int len) {
+show_line(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- int textlen = snprintf(NULL, 0, "Line %d of %d", buffer->cur_line + 1, buffer->lines_num);
+ int textlen = snprintf(NULL, 0, "Line %zu of %zu", view->cur_line + 1, view->lines_num);
char *str = ledit_malloc(textlen + 1);
- snprintf(str, textlen + 1, "Line %d of %d", buffer->cur_line + 1, buffer->lines_num);
- ledit_window_show_message(buffer->window, str, textlen);
+ snprintf(str, textlen + 1, "Line %zu of %zu", view->cur_line + 1, view->lines_num);
+ ledit_window_show_message(view->window, str, textlen);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
/* FIXME: return status! */
static struct action
-undo(ledit_buffer *buffer, char *text, int len) {
+undo(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_buffer_set_selection(buffer, 0, 0, 0, 0);
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- ledit_buffer_undo(buffer);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_wipe_selection(view);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view_undo(view);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
finalize_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-redo(ledit_buffer *buffer, char *text, int len) {
+redo(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_buffer_set_selection(buffer, 0, 0, 0, 0);
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- ledit_buffer_redo(buffer);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ view_wipe_selection(view);
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view_redo(view);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
finalize_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
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, -1, -1, -1, 1);
+insert_mode_insert_text(ledit_view *view, char *text, size_t len) {
+ /* FIXME: set cur_index when deleting selection */
+ delete_selection(view);
+ insert_text(view, view->cur_line, view->cur_index, text, len, 0, 0, 0, 0, 0, 0, 1);
return (struct action){ACTION_NONE, NULL};
}
static struct action
-clipcopy(ledit_buffer *buffer, char *text, int len) {
+clipcopy(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- /* FIXME: abstract this through buffer */
- clipboard_primary_to_clipboard(buffer->window);
+ /* FIXME: abstract this through view */
+ clipboard_primary_to_clipboard(view->window);
discard_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
static struct action
-clippaste(ledit_buffer *buffer, char *text, int len) {
+clippaste(ledit_view *view, char *text, size_t len) {
(void)text;
(void)len;
- ledit_buffer_paste_clipboard(buffer);
+ view_paste_clipboard(view);
finalize_repetition_stack();
return (struct action){ACTION_NONE, NULL};
}
/* FIXME: make sure the found position is valid cursor position? */
static int
-search_str_backwards(char *haystack, int hlen, char *needle, int nlen, int start_index) {
- if (start_index < 0 || start_index > hlen)
+search_str_backwards(char *haystack, size_t hlen, char *needle, size_t nlen, size_t start_index, size_t *ret) {
+ if (start_index > hlen)
return -1;
- int new_index = start_index;
- for (new_index--; new_index >= 0; new_index--) {
- if (!strncmp(haystack + new_index, needle, nlen))
- return new_index;
+ size_t new_index = start_index;
+ for (; new_index > 0; new_index--) {
+ if (!strncmp(haystack + new_index - 1, needle, nlen)) {
+ *ret = new_index;
+ return 0;
+ }
}
return -1;
}
static int
-search_str_forwards(char *haystack, int hlen, char *needle, int nlen, int start_index) {
- if (start_index < 0 || start_index >= hlen)
+search_str_forwards(char *haystack, size_t hlen, char *needle, size_t nlen, size_t start_index, size_t *ret) {
+ if (start_index >= hlen)
return -1;
/* duplicate so it is nul-terminated */
char *search_str = ledit_strndup(needle, nlen);
char *res = strstr(haystack + start_index + 1, search_str);
free(search_str);
/* FIXME: is this legal? */
- if (res)
- return (int)(res - haystack);
- else
+ if (res) {
+ *ret = (size_t)(res - haystack);
+ return 0;
+ } else {
return -1;
+ }
}
/* just to make the macro below works for all cases */
/* FIXME: is there a more elegant way to do this? */
-static int
-dummy_cursor_helper(ledit_buffer *buffer, int line, int index, int num) {
- (void)buffer;
+static size_t
+dummy_cursor_helper(ledit_view *view, size_t line, size_t index, int num) {
+ (void)view;
(void)line;
(void)num;
return index;
}
-/* FIXME: call normalize on line first? */
/* FIXME: add checks to functions that current mode is supported */
/* name is the name of the generated pair of functions
@@ -2089,42 +2094,45 @@ dummy_cursor_helper(ledit_buffer *buffer, int line, int index, int num) {
cur_funcv is called to position the cursor in visual mode */
#define GEN_MOVE_TO_CHAR(name, search_func, cur_funcm, cur_funcn, cur_funcv) \
static struct action \
-name##_cb(ledit_buffer *buffer, char *text, int len) { \
+name##_cb(ledit_view *view, char *text, size_t len) { \
motion_callback cb = NULL; \
int num = get_key_repeat_and_motion_cb(&cb); \
if (num == -1) \
- return err_invalid_key(buffer); \
+ return err_invalid_key(view); \
if (num == 0) \
num = 1; \
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line); \
- int new_index = buffer->cur_index; \
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->cur_line); \
+ size_t new_index = view->cur_index; \
+ int ret = -1; \
for (int i = 0; i < num; i++) { \
- new_index = search_func(ll->text, ll->len, text, len, new_index); \
- if (new_index == -1) \
+ if ((ret = search_func( \
+ ll->text, ll->len, text, len, \
+ new_index, &new_index)) == -1) { \
break; \
+ } \
} \
- if (new_index >= 0) { \
+ if (!ret) { \
if (cb != NULL) { \
new_index = cur_funcm( \
- buffer, buffer->cur_line, new_index, 1 \
+ view, view->cur_line, new_index, 1 \
); \
- cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR); \
+ cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); \
} else { \
- if (buffer->common->mode == VISUAL) { \
- buffer->cur_index = cur_funcv( \
- buffer, buffer->cur_line, new_index, 1 \
+ if (view->mode == VISUAL) { \
+ view->cur_index = cur_funcv( \
+ view, view->cur_line, new_index, 1 \
); \
- ledit_buffer_set_selection( \
- buffer, \
- buffer->sel.line1, buffer->sel.byte1, \
- buffer->cur_line, buffer->cur_index \
+ view_set_selection( \
+ view, \
+ view->sel.line1, view->sel.byte1, \
+ view->cur_line, view->cur_index \
); \
} else { \
- buffer->cur_index = cur_funcn( \
- buffer, buffer->cur_line, new_index, 1 \
+ view->cur_index = cur_funcn( \
+ view, view->cur_line, new_index, 1 \
); \
- ledit_buffer_set_line_cursor_attrs( \
- buffer, buffer->cur_line, buffer->cur_index \
+ view_set_line_cursor_attrs( \
+ view, view->cur_line, view->cur_index \
); \
} \
discard_repetition_stack(); \
@@ -2136,8 +2144,8 @@ name##_cb(ledit_buffer *buffer, char *text, int len) {
} \
\
static struct action \
-name(ledit_buffer *buffer, char *text, int len) { \
- (void)buffer; \
+name(ledit_view *view, char *text, size_t len) { \
+ (void)view; \
(void)text; \
(void)len; \
grab_char_cb = &name##_cb; \
@@ -2148,15 +2156,15 @@ name(ledit_buffer *buffer, char *text, int len) {
/* FIXME: dummy_cursor_helper is kind of ugly */
GEN_MOVE_TO_CHAR(
find_next_char_forwards, search_str_forwards,
- dummy_cursor_helper, ledit_buffer_prev_cursor_pos, dummy_cursor_helper
+ dummy_cursor_helper, view_prev_cursor_pos, dummy_cursor_helper
)
GEN_MOVE_TO_CHAR(
find_next_char_backwards, search_str_backwards,
- ledit_buffer_next_cursor_pos, ledit_buffer_next_cursor_pos, ledit_buffer_next_cursor_pos
+ view_next_cursor_pos, view_next_cursor_pos, view_next_cursor_pos
)
GEN_MOVE_TO_CHAR(
find_char_forwards, search_str_forwards,
- ledit_buffer_next_cursor_pos, dummy_cursor_helper, dummy_cursor_helper
+ view_next_cursor_pos, dummy_cursor_helper, dummy_cursor_helper
)
GEN_MOVE_TO_CHAR(
find_char_backwards, search_str_backwards,
@@ -2164,68 +2172,55 @@ GEN_MOVE_TO_CHAR(
)
static struct action
-replace_cb(ledit_buffer *buffer, char *text, int len) {
- int start_index = buffer->cur_index;
+replace_cb(ledit_view *view, char *text, size_t len) {
+ size_t start_index = view->cur_index;
/* FIXME: replace with (key repeat) * text instead of just text */
- int end_index = ledit_buffer_next_cursor_pos(
- buffer, buffer->cur_line, buffer->cur_index, 1
+ size_t end_index = view_next_cursor_pos(
+ view, view->cur_line, view->cur_index, 1
);
delete_range(
- buffer, 0, 0,
- buffer->cur_line, start_index, buffer->cur_line, end_index, 0
+ view, 0, 0,
+ view->cur_line, start_index, view->cur_line, end_index, 0
);
insert_text(
- buffer, buffer->cur_line, start_index, text, len,
- buffer->cur_line, start_index, buffer->cur_line, start_index, 0
+ view, view->cur_line, start_index, text, len,
+ view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0
);
/* this should not be necessary, but just in case */
- buffer->cur_index = ledit_buffer_get_legal_normal_pos(
- buffer, buffer->cur_line, buffer->cur_index
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
);
- push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 0);
+ push_undo_empty_insert(view, view->cur_line, view->cur_index, 0);
grab_char_cb = NULL;
return (struct action){ACTION_NONE, NULL};
}
static struct action
-replace(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+replace(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
int num = get_key_repeat();
if (num != 0)
- return err_invalid_key(buffer);
+ return err_invalid_key(view);
grab_char_cb = &replace_cb;
return (struct action){ACTION_NONE, NULL};
}
-static void
-set_hard_line_based(ledit_buffer *buffer, int hl) {
- hard_line_based = hl;
- char *text = hl ? "|HL" : "|SL";
- ledit_window_set_mode_extra_text(buffer->window, text);
-}
-
static struct action
-toggle_hard_line_based(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+toggle_hard_line_based(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
int num = get_key_repeat();
if (num != 0)
- return err_invalid_key(buffer);
- set_hard_line_based(buffer, !hard_line_based);
+ return err_invalid_key(view);
+ ledit_buffer_set_hard_line_based(view->buffer, !view->buffer->hard_line_based);
return (struct action){ACTION_NONE, NULL};
}
-/* FIXME: this is sort of all over the place and ugly */
-void
-keys_basic_init(ledit_buffer *buffer) {
- set_hard_line_based(buffer, 1);
-}
-
static struct action
-handle_key(ledit_buffer *buffer, char *key_text, int 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) {
struct key *cur_keys = keys[lang_index].keys;
int num_keys = keys[lang_index].num_keys;
struct key_stack_elem *e = peek_key_stack();
@@ -2236,13 +2231,13 @@ handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned i
return (struct action){ACTION_NONE, NULL};
} else if (len > 0 && grab_char_cb) {
*found = 1;
- return grab_char_cb(buffer, key_text, len);
+ return grab_char_cb(view, key_text, len);
}
*found = 0;
for (int i = 0; i < num_keys; i++) {
if (cur_keys[i].text) {
if (len > 0 &&
- (cur_keys[i].modes & buffer->common->mode) &&
+ (cur_keys[i].modes & view->mode) &&
(!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)) ||
@@ -2250,21 +2245,21 @@ handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned i
/* FIXME: seems a bit hacky to remove shift, but it
is needed to make keys that use shift match */
*found = 1;
- return cur_keys[i].func(buffer, key_text, len);
+ return cur_keys[i].func(view, key_text, len);
}
- } else if ((cur_keys[i].modes & buffer->common->mode) &&
+ } else if ((cur_keys[i].modes & view->mode) &&
cur_keys[i].keysym == sym &&
match_key(cur_keys[i].mods, key_state)) {
*found = 1;
- return cur_keys[i].func(buffer, key_text, len);
+ return cur_keys[i].func(view, key_text, len);
}
}
return (struct action){ACTION_NONE, NULL};
}
static struct action
-repeat_command(ledit_buffer *buffer, char *text, int len) {
- (void)buffer;
+repeat_command(ledit_view *view, char *text, size_t len) {
+ (void)view;
(void)text;
(void)len;
int found;
@@ -2273,7 +2268,7 @@ repeat_command(ledit_buffer *buffer, char *text, int len) {
unwind_repetition_stack();
struct repetition_stack_elem *e = get_cur_repetition_stack_elem();
while (e) {
- (void)handle_key(buffer, 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);
advance_repetition_stack();
e = get_cur_repetition_stack_elem();
}
@@ -2284,29 +2279,29 @@ repeat_command(ledit_buffer *buffer, char *text, int len) {
}
struct action
-basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) {
+basic_key_handler(ledit_view *view, XEvent *event, int lang_index) {
char buf[64];
KeySym sym;
int n;
unsigned int key_state = event->xkey.state;
- preprocess_key(buffer->window, event, &sym, buf, sizeof(buf), &n);
+ preprocess_key(view->window, event, &sym, buf, sizeof(buf), &n);
struct repetition_stack_elem *re = push_repetition_stack();
re->key_text = ledit_strndup(buf, (size_t)n);
- re->len = n;
+ re->len = (size_t)n;
re->sym = sym;
re->key_state = key_state;
re->lang_index = lang_index;
/* FIXME: only hide when actually necessary */
- ledit_window_hide_message(buffer->window);
+ ledit_window_hide_message(view->window);
int found = 0;
- struct action act = handle_key(buffer, buf, n, sym, key_state, lang_index, &found);
+ struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found);
/* FIXME: only do this when necessary */
if (found)
- ledit_buffer_ensure_cursor_shown(buffer);
+ view_ensure_cursor_shown(view);
/* FIXME: maybe show error if not found */
return act;
}
diff --git a/keys_basic.h b/keys_basic.h
@@ -1,3 +1,2 @@
-void keys_basic_init(ledit_buffer *buffer);
void basic_key_cleanup(void);
-struct action basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index);
+struct action basic_key_handler(ledit_view *view, XEvent *event, int lang_index);
diff --git a/keys_basic_config.h b/keys_basic_config.h
@@ -13,88 +13,88 @@ enum key_type {
};
struct key {
- char *text; /* for keys that correspond with text */
- unsigned int mods; /* modifier mask */
- KeySym keysym; /* for other keys, e.g. arrow keys */
- enum 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 */
- struct action (*func)(ledit_buffer *, char *, int); /* callback function */
+ char *text; /* for keys that correspond with text */
+ unsigned int mods; /* modifier mask */
+ KeySym keysym; /* for other keys, e.g. arrow keys */
+ enum 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 */
+ struct action (*func)(ledit_view *, char *, size_t); /* callback function */
};
-static struct action backspace(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_left(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_right(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_up(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_down(ledit_buffer *buffer, char *text, int len);
-static struct action return_key(ledit_buffer *buffer, char *text, int len);
-static struct action delete_key(ledit_buffer *buffer, char *text, int len);
-static struct action escape_key(ledit_buffer *buffer, char *text, int len);
-static struct action enter_insert(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_to_beginning(ledit_buffer *buffer, char *text, int len);
-static struct action push_0(ledit_buffer *buffer, char *text, int len);
-static struct action push_1(ledit_buffer *buffer, char *text, int len);
-static struct action push_2(ledit_buffer *buffer, char *text, int len);
-static struct action push_3(ledit_buffer *buffer, char *text, int len);
-static struct action push_4(ledit_buffer *buffer, char *text, int len);
-static struct action push_5(ledit_buffer *buffer, char *text, int len);
-static struct action push_6(ledit_buffer *buffer, char *text, int len);
-static struct action push_7(ledit_buffer *buffer, char *text, int len);
-static struct action push_8(ledit_buffer *buffer, char *text, int len);
-static struct action push_9(ledit_buffer *buffer, char *text, int len);
-static struct action delete(ledit_buffer *buffer, char *text, int len);
-static struct action enter_visual(ledit_buffer *buffer, char *text, int len);
-static struct action switch_selection_end(ledit_buffer *buffer, char *text, int len);
-static struct action clipcopy(ledit_buffer *buffer, char *text, int len);
-static struct action clippaste(ledit_buffer *buffer, char *text, int len);
-static struct action show_line(ledit_buffer *buffer, char *text, int len);
-static struct action enter_commandedit(ledit_buffer *buffer, char *text, int len);
-static struct action enter_searchedit_backward(ledit_buffer *buffer, char *text, int len);
-static struct action enter_searchedit_forward(ledit_buffer *buffer, char *text, int len);
-static struct action key_search_next(ledit_buffer *buffer, char *text, int len);
-static struct action key_search_prev(ledit_buffer *buffer, char *text, int len);
-static struct action undo(ledit_buffer *buffer, char *text, int len);
-static struct action redo(ledit_buffer *buffer, char *text, int len);
-static struct action insert_mode_insert_text(ledit_buffer *buffer, char *text, int len);
-static struct action repeat_command(ledit_buffer *buffer, char *text, int len);
-static struct action screen_up(ledit_buffer *buffer, char *text, int len);
-static struct action screen_down(ledit_buffer *buffer, char *text, int len);
-static struct action scroll_with_cursor_up(ledit_buffer *buffer, char *text, int len);
-static struct action scroll_with_cursor_down(ledit_buffer *buffer, char *text, int len);
-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);
-static struct action paste_normal_backwards(ledit_buffer *buffer, char *text, int len);
-static struct action change(ledit_buffer *buffer, char *text, int len);
-static struct action move_to_eol(ledit_buffer *buffer, char *text, int len);
-static struct action mark_line(ledit_buffer *buffer, char *text, int len);
-static struct action jump_to_mark(ledit_buffer *buffer, char *text, int len);
-static struct action next_word(ledit_buffer *buffer, char *text, int len);
-static struct action next_word_end(ledit_buffer *buffer, char *text, int len);
-static struct action next_bigword(ledit_buffer *buffer, char *text, int len);
-static struct action next_bigword_end(ledit_buffer *buffer, char *text, int len);
-static struct action prev_word(ledit_buffer *buffer, char *text, int len);
-static struct action prev_bigword(ledit_buffer *buffer, char *text, int len);
-static struct action append_after_eol(ledit_buffer *buffer, char *text, int len);
-static struct action append_after_cursor(ledit_buffer *buffer, char *text, int len);
-static struct action append_line_above(ledit_buffer *buffer, char *text, int len);
-static struct action append_line_below(ledit_buffer *buffer, char *text, int len);
-static struct action find_next_char_forwards(ledit_buffer *buffer, char *text, int len);
-static struct action find_next_char_backwards(ledit_buffer *buffer, char *text, int len);
-static struct action find_char_forwards(ledit_buffer *buffer, char *text, int len);
-static struct action find_char_backwards(ledit_buffer *buffer, char *text, int len);
-static struct action change_to_eol(ledit_buffer *buffer, char *text, int len);
-static struct action delete_to_eol(ledit_buffer *buffer, char *text, int len);
-static struct action delete_chars_forwards(ledit_buffer *buffer, char *text, int len);
-static struct action delete_chars_backwards(ledit_buffer *buffer, char *text, int len);
-static struct action yank(ledit_buffer *buffer, char *text, int len);
-static struct action yank_lines(ledit_buffer *buffer, char *text, int len);
-static struct action replace(ledit_buffer *buffer, char *text, int len);
-static struct action cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len);
-static struct action join_lines(ledit_buffer *buffer, char *text, int len);
-static struct action insert_at_beginning(ledit_buffer *buffer, char *text, int len);
-static struct action toggle_hard_line_based(ledit_buffer *buffer, char *text, int len);
+static struct action backspace(ledit_view *view, char *text, size_t len);
+static struct action cursor_left(ledit_view *view, char *text, size_t len);
+static struct action cursor_right(ledit_view *view, char *text, size_t len);
+static struct action cursor_up(ledit_view *view, char *text, size_t len);
+static struct action cursor_down(ledit_view *view, char *text, size_t len);
+static struct action return_key(ledit_view *view, char *text, size_t len);
+static struct action delete_key(ledit_view *view, char *text, size_t len);
+static struct action escape_key(ledit_view *view, char *text, size_t len);
+static struct action enter_insert(ledit_view *view, char *text, size_t len);
+static struct action cursor_to_beginning(ledit_view *view, char *text, size_t len);
+static struct action push_0(ledit_view *view, char *text, size_t len);
+static struct action push_1(ledit_view *view, char *text, size_t len);
+static struct action push_2(ledit_view *view, char *text, size_t len);
+static struct action push_3(ledit_view *view, char *text, size_t len);
+static struct action push_4(ledit_view *view, char *text, size_t len);
+static struct action push_5(ledit_view *view, char *text, size_t len);
+static struct action push_6(ledit_view *view, char *text, size_t len);
+static struct action push_7(ledit_view *view, char *text, size_t len);
+static struct action push_8(ledit_view *view, char *text, size_t len);
+static struct action push_9(ledit_view *view, char *text, size_t len);
+static struct action delete(ledit_view *view, char *text, size_t len);
+static struct action enter_visual(ledit_view *view, char *text, size_t len);
+static struct action switch_selection_end(ledit_view *view, char *text, size_t len);
+static struct action clipcopy(ledit_view *view, char *text, size_t len);
+static struct action clippaste(ledit_view *view, char *text, size_t len);
+static struct action show_line(ledit_view *view, char *text, size_t len);
+static struct action enter_commandedit(ledit_view *view, char *text, size_t len);
+static struct action enter_searchedit_backward(ledit_view *view, char *text, size_t len);
+static struct action enter_searchedit_forward(ledit_view *view, char *text, size_t len);
+static struct action key_search_next(ledit_view *view, char *text, size_t len);
+static struct action key_search_prev(ledit_view *view, char *text, size_t len);
+static struct action undo(ledit_view *view, char *text, size_t len);
+static struct action redo(ledit_view *view, char *text, size_t len);
+static struct action insert_mode_insert_text(ledit_view *view, char *text, size_t len);
+static struct action repeat_command(ledit_view *view, char *text, size_t len);
+static struct action screen_up(ledit_view *view, char *text, size_t len);
+static struct action screen_down(ledit_view *view, char *text, size_t len);
+static struct action scroll_with_cursor_up(ledit_view *view, char *text, size_t len);
+static struct action scroll_with_cursor_down(ledit_view *view, char *text, size_t len);
+static struct action scroll_lines_up(ledit_view *view, char *text, size_t len);
+static struct action scroll_lines_down(ledit_view *view, char *text, size_t len);
+static struct action move_to_line(ledit_view *view, char *text, size_t len);
+static struct action paste_normal(ledit_view *view, char *text, size_t len);
+static struct action paste_normal_backwards(ledit_view *view, char *text, size_t len);
+static struct action change(ledit_view *view, char *text, size_t len);
+static struct action move_to_eol(ledit_view *view, char *text, size_t len);
+static struct action mark_line(ledit_view *view, char *text, size_t len);
+static struct action jump_to_mark(ledit_view *view, char *text, size_t len);
+static struct action next_word(ledit_view *view, char *text, size_t len);
+static struct action next_word_end(ledit_view *view, char *text, size_t len);
+static struct action next_bigword(ledit_view *view, char *text, size_t len);
+static struct action next_bigword_end(ledit_view *view, char *text, size_t len);
+static struct action prev_word(ledit_view *view, char *text, size_t len);
+static struct action prev_bigword(ledit_view *view, char *text, size_t len);
+static struct action append_after_eol(ledit_view *view, char *text, size_t len);
+static struct action append_after_cursor(ledit_view *view, char *text, size_t len);
+static struct action append_line_above(ledit_view *view, char *text, size_t len);
+static struct action append_line_below(ledit_view *view, char *text, size_t len);
+static struct action find_next_char_forwards(ledit_view *view, char *text, size_t len);
+static struct action find_next_char_backwards(ledit_view *view, char *text, size_t len);
+static struct action find_char_forwards(ledit_view *view, char *text, size_t len);
+static struct action find_char_backwards(ledit_view *view, char *text, size_t len);
+static struct action change_to_eol(ledit_view *view, char *text, size_t len);
+static struct action delete_to_eol(ledit_view *view, char *text, size_t len);
+static struct action delete_chars_forwards(ledit_view *view, char *text, size_t len);
+static struct action delete_chars_backwards(ledit_view *view, char *text, size_t len);
+static struct action yank(ledit_view *view, char *text, size_t len);
+static struct action yank_lines(ledit_view *view, char *text, size_t len);
+static struct action replace(ledit_view *view, char *text, size_t len);
+static struct action cursor_to_first_non_ws(ledit_view *view, char *text, size_t len);
+static struct action join_lines(ledit_view *view, char *text, size_t len);
+static struct action insert_at_beginning(ledit_view *view, char *text, size_t len);
+static struct action toggle_hard_line_based(ledit_view *view, char *text, size_t len);
/* FIXME: maybe sort these and use binary search
-> but that would mess with the catch-all keys */
diff --git a/keys_command.c b/keys_command.c
@@ -19,63 +19,77 @@
#include "theme.h"
#include "window.h"
#include "buffer.h"
+#include "view.h"
#include "search.h"
+#include "cleanup.h"
#include "keys.h"
-#include "action.h"
#include "keys_command.h"
#include "keys_command_config.h"
/* FIXME: history for search and commands */
-/* FIXME: THIS WON'T WORK WHEN THERE ARE MULTIPLE BUFFERS! */
-/* this must first be set by caller before jumping to key handler */
-static enum ledit_command_type cur_type;
+static int create_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int close_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid);
+static int handle_cmd(ledit_view *view, char *cmd, size_t len);
-void
-command_key_cleanup(void) {
- /* nothing right now */
+static int
+handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
+ (void)view;
+ (void)cmd;
+ (void)l1;
+ (void)l2;
+ /* FIXME: Implement properly; handle error */
+ char *errstr;
+ if (view->buffer->filename)
+ ledit_buffer_write_to_file(view->buffer, view->buffer->filename, &errstr);
+ return 0;
}
-void
-ledit_command_set_type(enum ledit_command_type type) {
- cur_type = type;
+static int
+handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
+ (void)view;
+ (void)cmd;
+ (void)l1;
+ (void)l2;
+ /* FIXME: ask to save changes */
+ ledit_cleanup();
+ exit(0);
+ return 0;
}
-static int handle_write(ledit_buffer *buffer, char *cmd, int l1, int l2);
-static int handle_quit(ledit_buffer *buffer, char *cmd, int l1, int l2);
-static int handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2);
-static int handle_substitute(ledit_buffer *buffer, char *cmd, int l1, int l2);
-static int parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1_ret, int *line2_ret);
-static int handle_cmd(ledit_buffer *buffer, char *cmd, int len);
-
static int
-handle_write(ledit_buffer *buffer, char *cmd, int l1, int l2) {
- (void)buffer;
+create_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)cmd;
(void)l1;
(void)l2;
- /* FIXME: Implement properly; handle error */
- char *errstr;
- if (buffer->filename)
- ledit_buffer_write_to_file(buffer, buffer->filename, &errstr);
+ ledit_buffer_add_view(view->buffer, view->theme, view->mode, view->cur_line, view->cur_index);
return 0;
}
static int
-handle_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) {
- (void)buffer;
+close_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)cmd;
(void)l1;
(void)l2;
- /* FIXME: Implement */
- exit(1);
+ /* FIXME: This will lead to problems if I add something that
+ requires access to the view after the command is handled. */
+ ledit_buffer_remove_view(view->buffer, view);
+ if (view->buffer->views_num == 0) {
+ ledit_cleanup();
+ exit(0);
+ }
return 0;
}
static int
-handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) {
- (void)buffer;
+handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
+ (void)view;
(void)cmd;
(void)l1;
(void)l2;
@@ -84,8 +98,8 @@ handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) {
}
static int
-handle_substitute(ledit_buffer *buffer, char *cmd, int l1, int l2) {
- (void)buffer;
+handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
+ (void)view;
(void)cmd;
(void)l1;
(void)l2;
@@ -101,11 +115,13 @@ enum cmd_type {
static const struct {
char *cmd;
enum cmd_type type;
- int (*handler)(ledit_buffer *buffer, char *cmd, int l1, int l2);
+ int (*handler)(ledit_view *view, char *cmd, size_t l1, size_t l2);
} cmds[] = {
{"wq", CMD_OPTIONAL_RANGE, &handle_write_quit},
{"w", CMD_OPTIONAL_RANGE, &handle_write},
{"q", CMD_NORMAL, &handle_quit},
+ {"v", CMD_NORMAL, &create_view},
+ {"c", CMD_NORMAL, &close_view},
{"s", CMD_OPTIONAL_RANGE, &handle_substitute}
};
@@ -117,7 +133,7 @@ $ last line
/* FIXME: ACTUALLY USE LEN!!! */
static int
-parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1_ret, int *line2_ret) {
+parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid) {
(void)len;
enum {
START_LINENO = 1,
@@ -125,28 +141,34 @@ parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1
IN_RANGE = 4,
IN_LINENO = 8
} s = START_LINENO | START_RANGE;
- int l1 = -1, l2 = -1;
+ size_t l1 = 0, l2 = 0;
+ *l1_valid = 0;
+ *l2_valid = 0;
char *c = cmd;
for (; *c != '\0'; c++) {
if (isdigit(*c)) {
/* FIXME: integer overflow */
if (s & IN_LINENO) {
- if (l2 != -1)
+ if (*l2_valid) {
l2 = l2 * 10 + (*c - '0');
- else
+ } else {
l1 = l1 * 10 + (*c - '0');
+ *l1_valid = 1;
+ }
} else if ((s & START_LINENO) && (s & START_RANGE)) {
l1 = *c - '0';
+ *l1_valid = 1;
s &= ~START_RANGE;
s &= ~START_LINENO;
s |= IN_RANGE | IN_LINENO;
} else if ((s & START_LINENO)) {
l2 = *c - '0';
+ *l2_valid = 1;
s &= ~START_LINENO;
s |= IN_LINENO;
}
} else if (*c == ',' && !(s & START_RANGE)) {
- if (l1 != -1 && l2 != -1) {
+ if (*l1_valid && *l2_valid) {
return 1;
} else {
s |= START_LINENO;
@@ -155,17 +177,21 @@ parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1
} else if (*c == '%') {
if (s & START_RANGE) {
l1 = 1;
- l2 = buffer->lines_num;
+ l2 = view->lines_num;
+ *l1_valid = *l2_valid = 1;
break;
} else {
return 1;
}
} else if (*c == '$') {
if (s & START_LINENO) {
- if (l1 == -1)
- l1 = buffer->lines_num;
- else
- l2 = buffer->lines_num;
+ if (!*l1_valid) {
+ l1 = view->lines_num;
+ *l1_valid = 1;
+ } else {
+ l2 = view->lines_num;
+ *l2_valid = 1;
+ }
s &= ~START_LINENO;
s &= ~IN_LINENO;
} else {
@@ -175,7 +201,7 @@ parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1
break;
}
}
- if ((l1 == -1 || l2 == -1) && !(s & START_RANGE))
+ if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE))
return 1;
*cmd_ret = c;
*line1_ret = l1;
@@ -184,196 +210,196 @@ parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *line1
}
static int
-handle_cmd(ledit_buffer *buffer, char *cmd, int len) {
- if (len < 0)
- len = strlen(cmd);
+handle_cmd(ledit_view *view, char *cmd, size_t len) {
if (len < 1)
return 0;
char *c;
- int l1, l2;
+ size_t l1, l2;
+ int l1_valid, l2_valid;
/* FIXME: show error msg here */
- if (parse_range(buffer, cmd, len, &c, &l1, &l2))
+ if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid))
return 0;
- int range_given = l1 != -1 && l2 != -1;
+ int range_given = l1_valid && l2_valid;
for (size_t i = 0; i < LENGTH(cmds); i++) {
if (!strncmp(cmds[i].cmd, c, strlen(cmds[i].cmd)) &&
(!range_given || cmds[i].type == CMD_OPTIONAL_RANGE)) {
- return cmds[i].handler(buffer, c, l1, l2);
+ return cmds[i].handler(view, c, l1, l2);
}
}
return 0;
}
static int
-substitute_yes(ledit_buffer *buffer, char *key_text, int len) {
- (void)buffer;
+substitute_yes(ledit_view *view, char *key_text, size_t len) {
+ (void)view;
(void)key_text;
(void)len;
return 1;
}
static int
-substitute_yes_all(ledit_buffer *buffer, char *key_text, int len) {
- (void)buffer;
+substitute_yes_all(ledit_view *view, char *key_text, size_t len) {
+ (void)view;
(void)key_text;
(void)len;
return 0;
}
static int
-substitute_no(ledit_buffer *buffer, char *key_text, int len) {
- (void)buffer;
+substitute_no(ledit_view *view, char *key_text, size_t len) {
+ (void)view;
(void)key_text;
(void)len;
return 1;
}
static int
-substitute_no_all(ledit_buffer *buffer, char *key_text, int len) {
- (void)buffer;
+substitute_no_all(ledit_view *view, char *key_text, size_t len) {
+ (void)view;
(void)key_text;
(void)len;
return 0;
}
static int
-edit_insert_text(ledit_buffer *buffer, char *key_text, int len) {
- ledit_window_insert_bottom_bar_text(buffer->window, key_text, len);
+edit_insert_text(ledit_view *view, char *key_text, size_t len) {
+ /* FIXME: overflow */
+ ledit_window_insert_bottom_bar_text(view->window, key_text, len);
ledit_window_set_bottom_bar_cursor(
- buffer->window, ledit_window_get_bottom_bar_cursor(buffer->window) + len
+ view->window, ledit_window_get_bottom_bar_cursor(view->window) + len
);
return 1;
}
static int
-edit_cursor_to_end(ledit_buffer *buffer, char *key_text, int len) {
+edit_cursor_to_end(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_bottom_bar_cursor_to_end(buffer->window);
+ ledit_window_bottom_bar_cursor_to_end(view->window);
return 1;
}
static int
-edit_cursor_to_beginning(ledit_buffer *buffer, char *key_text, int len) {
+edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_bottom_bar_cursor_to_beginning(buffer->window);
+ ledit_window_bottom_bar_cursor_to_beginning(view->window);
return 1;
}
static int
-edit_cursor_left(ledit_buffer *buffer, char *key_text, int len) {
+edit_cursor_left(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_move_bottom_bar_cursor(buffer->window, -1);
+ ledit_window_move_bottom_bar_cursor(view->window, -1);
return 1;
}
static int
-edit_cursor_right(ledit_buffer *buffer, char *key_text, int len) {
+edit_cursor_right(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_move_bottom_bar_cursor(buffer->window, 1);
+ ledit_window_move_bottom_bar_cursor(view->window, 1);
return 1;
}
static int
-edit_backspace(ledit_buffer *buffer, char *key_text, int len) {
+edit_backspace(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_delete_bottom_bar_char(buffer->window, -1);
+ ledit_window_delete_bottom_bar_char(view->window, -1);
return 1;
}
static int
-edit_delete(ledit_buffer *buffer, char *key_text, int len) {
+edit_delete(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_delete_bottom_bar_char(buffer->window, 1);
+ ledit_window_delete_bottom_bar_char(view->window, 1);
return 1;
}
static int
-edit_submit(ledit_buffer *buffer, char *key_text, int len) {
+edit_submit(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
+ ledit_window_set_bottom_bar_text_shown(view->window, 0);
/* FIXME: this is hacky */
- return handle_cmd(buffer, ledit_window_get_bottom_bar_text(buffer->window) + 1, -1);
+ return handle_cmd(view, ledit_window_get_bottom_bar_text(view->window) + 1, -1);
}
/* FIXME: support visual mode, i.e. change selection to new place? */
void
-search_next(ledit_buffer *buffer) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- enum ledit_search_state ret = ledit_search_next(buffer, &buffer->cur_line, &buffer->cur_index);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+search_next(ledit_view *view) {
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ enum ledit_search_state ret = ledit_search_next(view, &view->cur_line, &view->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
if (ret != SEARCH_NORMAL)
- ledit_window_show_message(buffer->window, ledit_search_state_to_str(ret), -1);
+ ledit_window_show_message(view->window, ledit_search_state_to_str(ret), -1);
}
void
-search_prev(ledit_buffer *buffer) {
- ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
- enum ledit_search_state ret = ledit_search_prev(buffer, &buffer->cur_line, &buffer->cur_index);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+search_prev(ledit_view *view) {
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ enum ledit_search_state ret = ledit_search_prev(view, &view->cur_line, &view->cur_index);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
if (ret != SEARCH_NORMAL)
- ledit_window_show_message(buffer->window, ledit_search_state_to_str(ret), -1);
+ ledit_window_show_message(view->window, ledit_search_state_to_str(ret), -1);
}
static int
-editsearch_submit(ledit_buffer *buffer, char *key_text, int len) {
+editsearch_submit(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
- ledit_set_search_forward(ledit_window_get_bottom_bar_text(buffer->window) + 1);
- search_next(buffer);
+ ledit_window_set_bottom_bar_text_shown(view->window, 0);
+ ledit_set_search_forward(ledit_window_get_bottom_bar_text(view->window) + 1);
+ search_next(view);
return 0;
}
static int
-editsearchb_submit(ledit_buffer *buffer, char *key_text, int len) {
+editsearchb_submit(ledit_view *view, char *key_text, size_t len) {
(void)key_text;
(void)len;
- ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
- ledit_set_search_backward(ledit_window_get_bottom_bar_text(buffer->window) + 1);
- search_next(buffer);
+ ledit_window_set_bottom_bar_text_shown(view->window, 0);
+ ledit_set_search_backward(ledit_window_get_bottom_bar_text(view->window) + 1);
+ search_next(view);
return 0;
}
static int
-edit_discard(ledit_buffer *buffer, char *key_text, int len) {
- (void)buffer;
+edit_discard(ledit_view *view, char *key_text, size_t len) {
+ (void)view;
(void)key_text;
(void)len;
- ledit_window_set_bottom_bar_text_shown(buffer->window, 0);
+ ledit_window_set_bottom_bar_text_shown(view->window, 0);
return 0;
}
struct action
-ledit_command_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index) {
+ledit_command_key_handler(ledit_view *view, XEvent *event, int lang_index) {
char buf[64];
KeySym sym;
int n;
struct key *cur_keys = keys[lang_index].keys;
int num_keys = keys[lang_index].num_keys;
unsigned int key_state = event->xkey.state;
- preprocess_key(buffer->window, event, &sym, buf, sizeof(buf), &n);
+ preprocess_key(view->window, event, &sym, buf, sizeof(buf), &n);
int grabkey = 1;
for (int i = 0; i < num_keys; i++) {
if (cur_keys[i].text) {
if (n > 0 &&
- (cur_keys[i].type == cur_type) &&
+ (cur_keys[i].type == view->cur_command_type) &&
((!strncmp(cur_keys[i].text, buf, n) &&
match_key(cur_keys[i].mods, key_state & ~ShiftMask)) ||
cur_keys[i].text[0] == '\0')) {
- grabkey = cur_keys[i].func(buffer, buf, n);
+ grabkey = cur_keys[i].func(view, buf, (size_t)n);
break;
}
- } else if ((cur_keys[i].type == cur_type) &&
+ } else if ((cur_keys[i].type == view->cur_command_type) &&
(cur_keys[i].keysym == sym) &&
(match_key(cur_keys[i].mods, key_state))) {
- grabkey = cur_keys[i].func(buffer, buf, n);
+ grabkey = cur_keys[i].func(view, buf, (size_t)n);
break;
}
}
diff --git a/keys_command.h b/keys_command.h
@@ -1,16 +1,5 @@
-enum ledit_command_type {
- CMD_EDIT,
- CMD_EDITSEARCH,
- CMD_EDITSEARCHB,
- CMD_SEARCH,
- CMD_SEARCHB,
- CMD_SUBSTITUTE
-};
-
/* these are only here so they can also be used by keys_basic */
-void search_next(ledit_buffer *buffer);
-void search_prev(ledit_buffer *buffer);
+void search_next(ledit_view *view);
+void search_prev(ledit_view *view);
-void command_key_cleanup(void);
-void ledit_command_set_type(enum ledit_command_type type);
-struct action ledit_command_key_handler(ledit_buffer *buffer, XEvent *event, int lang_index);
+struct action ledit_command_key_handler(ledit_view *view, XEvent *event, int lang_index);
diff --git a/keys_command_config.h b/keys_command_config.h
@@ -1,25 +1,25 @@
-static int substitute_yes(ledit_buffer *buffer, char *key_text, int len);
-static int substitute_yes_all(ledit_buffer *buffer, char *key_text, int len);
-static int substitute_no(ledit_buffer *buffer, char *key_text, int len);
-static int substitute_no_all(ledit_buffer *buffer, char *key_text, int len);
-static int edit_insert_text(ledit_buffer *buffer, char *key_text, int len);
-static int edit_cursor_left(ledit_buffer *buffer, char *key_text, int len);
-static int edit_cursor_right(ledit_buffer *buffer, char *key_text, int len);
-static int edit_cursor_to_end(ledit_buffer *buffer, char *key_text, int len);
-static int edit_cursor_to_beginning(ledit_buffer *buffer, char *key_text, int len);
-static int edit_backspace(ledit_buffer *buffer, char *key_text, int len);
-static int edit_delete(ledit_buffer *buffer, char *key_text, int len);
-static int edit_submit(ledit_buffer *buffer, char *key_text, int len);
-static int editsearch_submit(ledit_buffer *buffer, char *key_text, int len);
-static int editsearchb_submit(ledit_buffer *buffer, char *key_text, int len);
-static int edit_discard(ledit_buffer *buffer, char *key_text, int len);
+static int substitute_yes(ledit_view *view, char *key_text, size_t len);
+static int substitute_yes_all(ledit_view *view, char *key_text, size_t len);
+static int substitute_no(ledit_view *view, char *key_text, size_t len);
+static int substitute_no_all(ledit_view *view, char *key_text, size_t len);
+static int edit_insert_text(ledit_view *view, char *key_text, size_t len);
+static int edit_cursor_left(ledit_view *view, char *key_text, size_t len);
+static int edit_cursor_right(ledit_view *view, char *key_text, size_t len);
+static int edit_cursor_to_end(ledit_view *view, char *key_text, size_t len);
+static int edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len);
+static int edit_backspace(ledit_view *view, char *key_text, size_t len);
+static int edit_delete(ledit_view *view, char *key_text, size_t len);
+static int edit_submit(ledit_view *view, char *key_text, size_t len);
+static int editsearch_submit(ledit_view *view, char *key_text, size_t len);
+static int editsearchb_submit(ledit_view *view, char *key_text, size_t len);
+static int edit_discard(ledit_view *view, char *key_text, size_t len);
struct key {
char *text; /* for keys that correspond with text */
unsigned int mods; /* modifier mask */
KeySym keysym; /* for other keys, e.g. arrow keys */
enum ledit_command_type type; /* substitute, etc. */
- int (*func)(ledit_buffer *, char *, int); /* callback function */
+ int (*func)(ledit_view *, char *, size_t); /* callback function */
};
/* "" means catch-all, i.e. all keys with text are given to that callback */
diff --git a/ledit.c b/ledit.c
@@ -1,3 +1,4 @@
+/* FIXME: Just use int for everything? size_t just doesn't seem to be worth it */
/* FIXME: Make scrolling more smooth */
/* FIXME: Document that everything is assumed to be utf8 */
/* FIXME: Only redraw part of screen if needed */
@@ -40,41 +41,25 @@
#include "cache.h"
#include "undo.h"
#include "buffer.h"
-#include "action.h"
+#include "view.h"
#include "search.h"
#include "keys.h"
#include "keys_basic.h"
#include "keys_command.h"
+#include "macros.h"
+#include "cleanup.h"
-static void resize_window(int w, int h);
static void mainloop(void);
static void setup(int argc, char *argv[]);
-static void cleanup(void);
static void redraw(void);
-static int button_press(XEvent *event, int scroll_num);
-static int button_release(XEvent *event);
-static int drag_motion(XEvent *event);
static void change_keyboard(char *lang);
-static void key_press(XEvent *event);
+static void key_press(ledit_view *view, XEvent *event);
ledit_theme *theme = NULL;
-ledit_window *window = NULL;
ledit_buffer *buffer = NULL;
ledit_common common;
int cur_lang = 0;
-struct action cur_action = {ACTION_NONE, NULL};
-
-/* stolen from OpenBSD */
-#define ledit_timespecsub(tsp, usp, vsp) \
- do { \
- (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
- (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
- if ((vsp)->tv_nsec < 0) { \
- (vsp)->tv_sec--; \
- (vsp)->tv_nsec += 1000000000L; \
- } \
- } while (0)
static void
mainloop(void) {
@@ -83,6 +68,7 @@ mainloop(void) {
int major, minor;
if (!XkbQueryExtension(common.dpy, 0, &xkb_event_type, NULL, &major, &minor)) {
fprintf(stderr, "XKB not supported.");
+ ledit_cleanup();
exit(1);
}
/*printf("XKB (%d.%d) supported.\n", major, minor);*/
@@ -103,19 +89,11 @@ mainloop(void) {
int running = 1;
int change_kbd = 0;
- int need_redraw = 0;
redraw();
- /* store last time that a mouse event was processed in order to
- avoid sending too many mouse events to be processed */
- /* also store last draw time so framerate can be limited */
- struct timespec now, elapsed, last, last_scroll, last_motion, last_resize, sleep_time;
+ /* store last draw time so framerate can be limited */
+ struct timespec now, elapsed, last, sleep_time;
clock_gettime(CLOCK_MONOTONIC, &last);
- last_scroll = last_motion = last_resize = last;
sleep_time.tv_sec = 0;
- XEvent last_scroll_event, last_motion_event, last_resize_event;
- int last_scroll_valid = 0, last_motion_valid = 0, last_resize_valid = 0;
- int scroll_num = 0;
- int scroll_delta = 0;
while (running) {
while (XPending(common.dpy)) {
XNextEvent(common.dpy, &event);
@@ -125,91 +103,55 @@ mainloop(void) {
}
if (XFilterEvent(&event, None))
continue;
+ ledit_view *view = NULL;
+ /* FIXME: abstract view handling a bit (don't access directly here) */
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ if (buffer->views[i]->window->xwin == event.xany.window) {
+ view = buffer->views[i];
+ break;
+ }
+ }
+ if (view == NULL)
+ continue; /* shouldn't happen */
+ ledit_window *window = view->window;
switch (event.type) {
case Expose:
- /* FIXME: why did I do this? */
- redraw();
- need_redraw = 1;
+ view->redraw = 1;
break;
case ConfigureNotify:
- last_resize_event = event;
- last_resize_valid = 1;
+ ledit_window_register_resize(view->window, &event);
break;
case ButtonPress:
- /* FIXME: this is all a bit hacky */
- if (event.xbutton.button == Button4 ||
- event.xbutton.button == Button5) {
- scroll_delta = event.xbutton.button == Button4 ? -1 : 1;
- if (last_scroll_valid) {
- scroll_num += scroll_delta;
- } else {
- last_scroll_event = event;
- last_scroll_valid = 1;
- scroll_num = scroll_delta;
- }
- } else {
- need_redraw |= button_press(&event, 0);
- }
+ ledit_window_register_button_press(view->window, &event);
break;
case ButtonRelease:
- need_redraw |= button_release(&event);
+ ledit_window_button_release(view->window, &event);
break;
case MotionNotify:
- /* FIXME: is it legal to just copy event like this? */
- last_motion_event = event;
- last_motion_valid = 1;
+ ledit_window_register_motion(window, &event);
break;
case KeyPress:
- need_redraw = 1;
- key_press(&event);
+ key_press(view, &event);
break;
case ClientMessage:
- if ((Atom)event.xclient.data.l[0] == buffer->window->wm_delete_msg)
- running = 0;
+ if ((Atom)event.xclient.data.l[0] == view->window->wm_delete_msg) {
+ ledit_buffer_remove_view(buffer, view);
+ if (buffer->views_num == 0)
+ running = 0;
+ }
break;
case SelectionNotify:
case PropertyNotify:
- /* redraw because text may be pasted, then fall
- through and let window handle the event */
- need_redraw = 1;
- /* fall through */
case SelectionRequest:
- ledit_window_clipboard_event(buffer->window, &event);
+ ledit_window_clipboard_event(view->window, &event);
break;
default:
break;
}
};
- if (last_motion_valid) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- ledit_timespecsub(&now, &last_motion, &elapsed);
- if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
- need_redraw |= drag_motion(&last_motion_event);
- last_motion = now;
- last_motion_valid = 0;
- }
- }
- if (last_scroll_valid) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- ledit_timespecsub(&now, &last_scroll, &elapsed);
- if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
- need_redraw |= button_press(&last_scroll_event, scroll_num);
- last_scroll = now;
- last_scroll_valid = 0;
- }
- }
- if (last_resize_valid) {
- clock_gettime(CLOCK_MONOTONIC, &now);
- ledit_timespecsub(&now, &last_resize, &elapsed);
- if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= RESIZE_TICK) {
- resize_window(
- last_resize_event.xconfigure.width,
- last_resize_event.xconfigure.height
- );
- last_resize = now;
- last_resize_valid = 0;
- need_redraw = 1;
- }
+
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ ledit_window_handle_filtered_events(buffer->views[i]->window);
}
if (change_kbd) {
@@ -226,10 +168,7 @@ mainloop(void) {
XFree(group);
XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
}
- if (need_redraw) {
- redraw();
- need_redraw = 0;
- }
+ redraw();
clock_gettime(CLOCK_MONOTONIC, &now);
ledit_timespecsub(&now, &last, &elapsed);
@@ -258,6 +197,7 @@ setup(int argc, char *argv[]) {
);
if (!info || num_screens < 1 || info->count < 1) {
fprintf(stderr, "No visuals support Xdbe.\n");
+ ledit_cleanup();
exit(1);
}
XVisualInfo xvisinfo_templ;
@@ -276,11 +216,13 @@ setup(int argc, char *argv[]) {
stderr,
"Couldn't match a Visual with double buffering\n"
);
+ ledit_cleanup();
exit(1);
}
common.vis = xvisinfo_match->visual;
} else {
fprintf(stderr, "No Xdbe support.\n");
+ ledit_cleanup();
exit(1);
}
@@ -288,63 +230,43 @@ setup(int argc, char *argv[]) {
common.cm = DefaultColormap(common.dpy, common.screen);
theme = ledit_theme_create(&common);
- window = ledit_window_create(&common, theme);
- buffer = ledit_buffer_create(&common, theme, window);
+ buffer = ledit_buffer_create(&common);
/* FIXME: Support multiple buffers/files */
if (argc > 1) {
char *load_err;
if (ledit_buffer_load_file(buffer, argv[1], 0, &load_err)) {
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], load_err);
+ ledit_cleanup();
exit(1);
}
/* FIXME: encapsulate */
buffer->filename = ledit_strdup(argv[1]);
}
- ledit_buffer_set_mode(buffer, NORMAL);
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
- keys_basic_init(buffer);
+ ledit_buffer_add_view(buffer, theme, NORMAL, 0, 0);
+ /* FIXME: don't access view directly here */
+ ledit_view *view = buffer->views[0];
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
redraw();
}
-static void
-cleanup(void) {
+void
+ledit_cleanup(void) {
/* FIXME: check for other things to clean up */
ledit_search_cleanup();
basic_key_cleanup();
- command_key_cleanup();
- ledit_buffer_destroy(buffer);
- ledit_window_destroy(window);
- ledit_theme_destroy(&common, theme);
+ if (buffer)
+ ledit_buffer_destroy(buffer);
+ if (theme)
+ ledit_theme_destroy(&common, theme);
XCloseDisplay(common.dpy);
}
static void
redraw(void) {
- ledit_window_clear(window);
- ledit_buffer_redraw(buffer);
- ledit_window_redraw(window);
-}
-
-static int
-button_press(XEvent *event, int scroll_num) {
- return ledit_window_button_press(window, event, scroll_num);
-}
-
-static int
-button_release(XEvent *event) {
- return ledit_window_button_release(window, event);
-}
-
-static int
-drag_motion(XEvent *event) {
- return ledit_window_drag_motion(window, event);
-}
-
-static void
-resize_window(int w, int h) {
- ledit_window_resize(window, w, h);
- ledit_buffer_resize_textview(buffer);
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_redraw(buffer->views[i]);
+ }
}
static void
@@ -356,11 +278,13 @@ change_keyboard(char *lang) {
}
static void
-key_press(XEvent *event) {
- if (cur_action.type == ACTION_GRABKEY && cur_action.callback) {
- cur_action = cur_action.callback(buffer, event, cur_lang);
+key_press(ledit_view *view, XEvent *event) {
+ /* FIXME: just let view handle this since the action is part
+ of it anyways now */
+ if (view->cur_action.type == ACTION_GRABKEY && view->cur_action.callback) {
+ view->cur_action = view->cur_action.callback(view, event, cur_lang);
} else {
- cur_action = basic_key_handler(buffer, event, cur_lang);
+ view->cur_action = basic_key_handler(view, event, cur_lang);
}
}
@@ -368,7 +292,7 @@ int
main(int argc, char *argv[]) {
setup(argc, argv);
mainloop();
- cleanup();
+ ledit_cleanup();
return 0;
}
diff --git a/macros.h b/macros.h
@@ -0,0 +1,10 @@
+/* stolen from OpenBSD */
+#define ledit_timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
diff --git a/memory.c b/memory.c
@@ -2,11 +2,21 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
+
+#include "cleanup.h"
-/* FIXME: clean up on exit */
static void
fatal_err(const char *msg) {
fprintf(stderr, "%s", msg);
+ ledit_cleanup();
+ exit(1);
+}
+
+void
+err_overflow(void) {
+ fprintf(stderr, "Integer overflow.\n");
+ ledit_cleanup();
exit(1);
}
@@ -68,7 +78,7 @@ ledit_strcat(const char *str1, const char *str2) {
}
/*
- * This is from OpenBSD (adapted to exit on error):
+ * This (reallocarray) is from OpenBSD (adapted to exit on error):
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*/
@@ -83,7 +93,105 @@ ledit_reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
- fatal_err("Out of memory.\n");
+ fatal_err("Integer overflow in reallocarray.\n");
}
return realloc(optr, size * nmemb);
}
+
+void
+move_gap(
+ void *array, size_t elem_size, size_t index,
+ size_t gap, size_t cap, size_t len,
+ size_t *new_gap_ret) {
+ assert(array != NULL);
+ assert(index <= len);
+ assert(len <= cap);
+ char *carray = (char *)array; /* cast to char * for pointer arithmetic */
+ /* since the array has size cap * elem_size, it is assumed that no overflow happens */
+ if (index > gap) {
+ /* move piece between end of original gap and
+ index to beginning of original gap */
+ memmove(
+ carray + gap * elem_size,
+ carray + (gap + cap - len) * elem_size,
+ (index - gap) * elem_size
+ );
+ } else if (index < gap) {
+ /* move piece between index and original gap to
+ end of original gap */
+ memmove(
+ carray + (index + cap - len) * elem_size,
+ carray + index * elem_size,
+ (gap - index) * elem_size
+ );
+ }
+ if (new_gap_ret)
+ *new_gap_ret = index;
+}
+
+/* FIXME: replace with macro version that takes type parameter in order
+ to avoid errors with elem_size */
+/* This is almost certainly premature optimization and maybe
+ not optimization at all. */
+void *
+resize_and_move_gap(
+ void *array, size_t elem_size,
+ size_t old_gap, size_t old_cap, size_t len,
+ size_t min_size, size_t index,
+ size_t *new_gap_ret, size_t *new_cap_ret) {
+ assert(index <= len);
+ assert(len <= old_cap);
+ size_t gap_size = old_cap - len;
+ size_t new_cap = old_cap;
+ /* FIXME: read up on what the best values are here */
+ if (new_cap < min_size)
+ new_cap = old_cap * 2 > min_size ? old_cap * 2 : min_size;
+ if (new_cap < min_size)
+ err_overflow();
+ if (new_cap != old_cap)
+ array = ledit_reallocarray(array, new_cap, elem_size);
+ char *carray = (char*)array; /* cast to char to do pointer arithmetic */
+ /* we already know new_cap * elem_size does not wrap around because array
+ is of that size, so all the other multiplications here should be safe
+ (at least that's what I think, but I may be wrong) */
+ if (index > old_gap) {
+ /* move piece between end of original gap and index to
+ beginning of original gap */
+ memmove(
+ carray + old_gap * elem_size,
+ carray + (old_gap + gap_size) * elem_size,
+ (index - old_gap) * elem_size
+ );
+ /* move piece after index to end of buffer */
+ memmove(
+ carray + (new_cap - (len - index)) * elem_size,
+ carray + (index + gap_size) * elem_size,
+ (len - index) * elem_size
+ );
+ } else if (index < old_gap) {
+ /* move piece after original gap to end of buffer */
+ memmove(
+ carray + (new_cap - (len - old_gap)) * elem_size,
+ carray + (old_gap + gap_size) * elem_size,
+ (len - old_gap) * elem_size
+ );
+ /* move piece between index and original gap to end */
+ memmove(
+ carray + (new_cap - len + index) * elem_size,
+ carray + index * elem_size,
+ (old_gap - index) * elem_size
+ );
+ } else {
+ /* move piece after original gap to end of buffer */
+ memmove(
+ carray + (new_cap - (len - old_gap)) * elem_size,
+ carray + (old_gap + gap_size) * elem_size,
+ (len - old_gap) * elem_size
+ );
+ }
+ if (new_gap_ret)
+ *new_gap_ret = index;
+ if (new_cap_ret)
+ *new_cap_ret = new_cap;
+ return carray;
+}
diff --git a/memory.h b/memory.h
@@ -5,3 +5,35 @@ void *ledit_calloc(size_t nmemb, size_t size);
void *ledit_realloc(void *ptr, size_t size);
char *ledit_strcat(const char *str1, const char *str2);
void *ledit_reallocarray(void *optr, size_t nmemb, size_t size);
+
+/*
+ * Move the gap of a gap buffer with elements of size 'elem_size' to 'index'.
+ * 'index' is also written to 'new_gap_ret' if it is not NULL. This is just
+ * for convenience because it usually has to be set anyways.
+ */
+void move_gap(
+ void *array, size_t elem_size, size_t index,
+ size_t gap, size_t cap, size_t len,
+ size_t *new_gap_ret
+);
+
+/*
+ * Resize a generic gap buffer with elements of size 'elem_size' to hold at least
+ * 'min_size' elements and move the gap to element position 'index'.
+ * 'old_gap' is the old index of the gap buffer, 'old_cap' is the total length of the
+ * array (number of elements, not bytes), and 'len' is the number of valid elements.
+ * 'index' is also written to 'new_gap_ret' if it is not NULL. This is just
+ * for convenience because it usually has to be set anyways.
+ * The new total length of the array is also written to 'new_cap_ret', if it is not
+ * NULL. If it is NULL, you're doing something wrong.
+ * Returns the final array (might be same as the old one).
+ */
+void *resize_and_move_gap(
+ void *array, size_t elem_size,
+ size_t old_gap, size_t old_cap, size_t len,
+ size_t min_size, size_t index,
+ size_t *new_gap_ret, size_t *new_cap_ret
+);
+
+/* FIXME: not sure if this really belongs here */
+void err_overflow(void);
diff --git a/pango-compat.c b/pango-compat.c
@@ -6,7 +6,11 @@
/* FIXME: This is just copied from the newer version of pango. It *seems* like
the structs used are public (they're in the documentation), but it does seem
a bit dirty to do this here */
-#if !PANGO_VERSION_CHECK(1, 46, 0)
+/* This version check is disabled currently because an older version of pango had a
+ bug where pango_layout_get_direction didn't work with lines of length 0.
+ I should actually check when that was changed so I can do a version check for that. */
+//#if !PANGO_VERSION_CHECK(1, 46, 0)
+#if 1
static PangoLayoutRun *
pango_layout_line_get_run(PangoLayoutLine *line, int index) {
GSList *run_list;
@@ -44,7 +48,7 @@ pango_layout_line_get_char_direction(PangoLayoutLine *layout_line, int index) {
}
PangoDirection
-pango_layout_get_direction(PangoLayout *layout, int index) {
+ledit_pango_layout_get_direction(PangoLayout *layout, int index) {
int lineno, x;
PangoLayoutLine *line;
pango_layout_index_to_line_x(layout, index, 0, &lineno, &x);
diff --git a/pango-compat.h b/pango-compat.h
@@ -1,3 +1,4 @@
-#if !PANGO_VERSION_CHECK(1, 46, 0)
-PangoDirection pango_layout_get_direction(PangoLayout *layout, int index);
+//#if !PANGO_VERSION_CHECK(1, 46, 0)
+#if 1
+PangoDirection ledit_pango_layout_get_direction(PangoLayout *layout, int index);
#endif
diff --git a/search.c b/search.c
@@ -45,46 +45,46 @@ ledit_set_search_backward(char *pattern) {
}
static enum ledit_search_state
-search_forward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
- *line_ret = buffer->cur_line;
- *byte_ret = buffer->cur_index;
+search_forward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
+ *line_ret = view->cur_line;
+ *byte_ret = view->cur_index;
if (last_search == NULL)
return SEARCH_NO_PATTERN;
- int line = buffer->cur_line;
+ size_t line = view->cur_line;
/* start one byte later so it doesn't get stuck on a match
note: since the string ends with '\0', this is always valid */
- int byte = buffer->cur_index + 1;
+ size_t byte = view->cur_index + 1;
char *res;
- ledit_line *lline = ledit_buffer_get_line(buffer, line);
+ ledit_line *lline = ledit_buffer_get_line(view->buffer, line);
ledit_buffer_normalize_line(lline);
if ((res = strstr(lline->text + byte, last_search)) != NULL) {
*line_ret = line;
- *byte_ret = (int)(res - lline->text);
+ *byte_ret = (size_t)(res - lline->text);
return SEARCH_NORMAL;
}
- for (int i = line + 1; i < buffer->lines_num; i++) {
- lline = ledit_buffer_get_line(buffer, i);
+ for (size_t i = line + 1; i < view->lines_num; i++) {
+ lline = ledit_buffer_get_line(view->buffer, i);
ledit_buffer_normalize_line(lline);
if ((res = strstr(lline->text, last_search)) != NULL) {
*line_ret = i;
- *byte_ret = (int)(res - lline->text);
+ *byte_ret = (size_t)(res - lline->text);
return SEARCH_NORMAL;
}
}
- for (int i = 0; i < line; i++) {
- lline = ledit_buffer_get_line(buffer, i);
+ for (size_t i = 0; i < line; i++) {
+ lline = ledit_buffer_get_line(view->buffer, i);
ledit_buffer_normalize_line(lline);
if ((res = strstr(lline->text, last_search)) != NULL) {
*line_ret = i;
- *byte_ret = (int)(res - lline->text);
+ *byte_ret = (size_t)(res - lline->text);
return SEARCH_WRAPPED;
}
}
- lline = ledit_buffer_get_line(buffer, line);
+ lline = ledit_buffer_get_line(view->buffer, line);
ledit_buffer_normalize_line(lline);
if ((res = strstr(lline->text, last_search)) != NULL) {
*line_ret = line;
- *byte_ret = (int)(res - lline->text);
+ *byte_ret = (size_t)(res - lline->text);
return SEARCH_WRAPPED;
}
return SEARCH_NOT_FOUND;
@@ -93,14 +93,14 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
/* FIXME: this is insanely inefficient */
/* FIXME: just go backwards char-by-char and compare */
static enum ledit_search_state
-search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
- *line_ret = buffer->cur_line;
- *byte_ret = buffer->cur_index;
+search_backward(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
+ *line_ret = view->cur_line;
+ *byte_ret = view->cur_index;
if (last_search == NULL)
return SEARCH_NO_PATTERN;
- int line = buffer->cur_line;
- int byte = buffer->cur_index;
- ledit_line *lline = ledit_buffer_get_line(buffer, line);
+ size_t line = view->cur_line;
+ size_t byte = view->cur_index;
+ ledit_line *lline = ledit_buffer_get_line(view->buffer, line);
ledit_buffer_normalize_line(lline);
char *last = NULL, *res = lline->text;
while ((res = strstr(res, last_search)) != NULL && res < lline->text + byte) {
@@ -110,11 +110,11 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
if (last != NULL) {
*line_ret = line;
/* FIXME: check if this is safe */
- *byte_ret = (int)(last - lline->text);
+ *byte_ret = (size_t)(last - lline->text);
return SEARCH_NORMAL;
}
- for (int i = line - 1; i >= 0; i--) {
- lline = ledit_buffer_get_line(buffer, i);
+ for (size_t i = line; i > 0; i--) {
+ lline = ledit_buffer_get_line(view->buffer, i-1);
ledit_buffer_normalize_line(lline);
res = lline->text;
while ((res = strstr(res, last_search)) != NULL) {
@@ -122,13 +122,13 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
res++;
}
if (last != NULL) {
- *line_ret = i;
- *byte_ret = (int)(last - lline->text);
+ *line_ret = i-1;
+ *byte_ret = (size_t)(last - lline->text);
return SEARCH_NORMAL;
}
}
- for (int i = buffer->lines_num - 1; i > line; i--) {
- lline = ledit_buffer_get_line(buffer, i);
+ for (size_t i = view->lines_num - 1; i > line; i--) {
+ lline = ledit_buffer_get_line(view->buffer, i);
ledit_buffer_normalize_line(lline);
res = lline->text;
while ((res = strstr(res, last_search)) != NULL) {
@@ -137,11 +137,11 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
}
if (last != NULL) {
*line_ret = i;
- *byte_ret = (int)(last - lline->text);
+ *byte_ret = (size_t)(last - lline->text);
return SEARCH_WRAPPED;
}
}
- lline = ledit_buffer_get_line(buffer, line);
+ lline = ledit_buffer_get_line(view->buffer, line);
ledit_buffer_normalize_line(lline);
res = lline->text + byte;
while ((res = strstr(res, last_search)) != NULL) {
@@ -150,26 +150,26 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
}
if (last != NULL) {
*line_ret = line;
- *byte_ret = (int)(last - lline->text);
+ *byte_ret = (size_t)(last - lline->text);
return SEARCH_WRAPPED;
}
return SEARCH_NOT_FOUND;
}
enum ledit_search_state
-ledit_search_next(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
+ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
if (last_dir == FORWARD)
- return search_forward(buffer, line_ret, byte_ret);
+ return search_forward(view, line_ret, byte_ret);
else
- return search_backward(buffer, line_ret, byte_ret);
+ return search_backward(view, line_ret, byte_ret);
}
enum ledit_search_state
-ledit_search_prev(ledit_buffer *buffer, int *line_ret, int *byte_ret) {
+ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret) {
if (last_dir == FORWARD)
- return search_backward(buffer, line_ret, byte_ret);
+ return search_backward(view, line_ret, byte_ret);
else
- return search_forward(buffer, line_ret, byte_ret);
+ return search_forward(view, line_ret, byte_ret);
}
char *
diff --git a/search.h b/search.h
@@ -8,6 +8,6 @@ enum ledit_search_state {
void ledit_search_cleanup(void);
void ledit_set_search_forward(char *pattern);
void ledit_set_search_backward(char *pattern);
-enum ledit_search_state ledit_search_next(ledit_buffer *buffer, int *line_ret, int *byte_ret);
-enum ledit_search_state ledit_search_prev(ledit_buffer *buffer, int *line_ret, int *byte_ret);
+enum ledit_search_state ledit_search_next(ledit_view *view, size_t *line_ret, size_t *byte_ret);
+enum ledit_search_state ledit_search_prev(ledit_view *view, size_t *line_ret, size_t *byte_ret);
char *ledit_search_state_to_str(enum ledit_search_state state);
diff --git a/undo.c b/undo.c
@@ -1,6 +1,7 @@
#include <string.h>
#include <assert.h>
#include <stdlib.h>
+#include <stdint.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -69,6 +70,7 @@ push_undo_elem(ledit_undo_stack *undo) {
undo->cur++;
undo->len = undo->cur + 1;
if (undo->len > undo->cap) {
+ /* FIXME: wait, why is it size_t here already? */
size_t cap = undo->len * 2;
undo->stack = ledit_realloc(undo->stack, cap * sizeof(undo_elem));
for (size_t i = undo->cap; i < cap; i++) {
@@ -140,7 +142,7 @@ ledit_push_undo_delete(
ledit_undo_status
ledit_undo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
undo_insert_callback insert_cb, undo_delete_callback delete_cb,
- int *cur_line_ret, int *cur_index_ret, int *min_line_ret) {
+ size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret) {
undo_elem *e;
/* skip empty elements */
while (undo->cur >= 0 && undo->stack[undo->cur].text->len == 0) {
@@ -150,8 +152,10 @@ ledit_undo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
return UNDO_OLDEST_CHANGE;
int group = undo->stack[undo->cur].group;
int mode_group = undo->stack[undo->cur].mode_group;
- int min_line = INT_MAX;
+ size_t min_line = SIZE_MAX;
int mode_group_same = 0;
+ size_t cur_line = 0;
+ size_t cur_index = 0;
while (undo->cur >= 0 &&
(undo->stack[undo->cur].group == group || (mode_group_same =
((mode == NORMAL ||
@@ -190,9 +194,17 @@ ledit_undo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
if (e->op_range.line1 < min_line)
min_line = e->op_range.line1;
undo->cur--;
- *cur_line_ret = e->cursor_range.line1;
- *cur_index_ret = e->cursor_range.byte1;
+ cur_line = e->cursor_range.line1;
+ cur_index = e->cursor_range.byte1;
}
+ /* these can't be written directly in the while loop because that causes
+ conflicts - when text is inserted/deleted on the current line of the
+ view, the cursor is updated, but if the current line was already changed
+ here before the end of the undo run, it's possible for multiple lines
+ to have a cursor highlight set in the end (at least when working with
+ multiple views) */
+ *cur_line_ret = cur_line;
+ *cur_index_ret = cur_index;
*min_line_ret = min_line;
return UNDO_NORMAL;
}
@@ -200,7 +212,7 @@ ledit_undo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
ledit_undo_status
ledit_redo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
undo_insert_callback insert_cb, undo_delete_callback delete_cb,
- int *cur_line_ret, int *cur_index_ret, int *min_line_ret) {
+ size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret) {
undo_elem *e;
/* skip elements where no text is changed */
while (undo->cur < undo->len - 1 && undo->stack[undo->cur + 1].text->len == 0) {
@@ -211,8 +223,10 @@ ledit_redo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
undo->cur++;
int group = undo->stack[undo->cur].group;
int mode_group = undo->stack[undo->cur].mode_group;
- int min_line = INT_MAX;
+ size_t min_line = SIZE_MAX;
int mode_group_same = 0;
+ size_t cur_line = 0;
+ size_t cur_index = 0;
while (undo->cur < undo->len &&
(undo->stack[undo->cur].group == group || (mode_group_same =
((mode == NORMAL ||
@@ -244,9 +258,11 @@ ledit_redo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data,
if (e->op_range.line1 < min_line)
min_line = e->op_range.line1;
undo->cur++;
- *cur_line_ret = e->cursor_range.line2;
- *cur_index_ret = e->cursor_range.byte2;
+ cur_line = e->cursor_range.line2;
+ cur_index = e->cursor_range.byte2;
}
+ *cur_line_ret = cur_line;
+ *cur_index_ret = cur_index;
undo->cur--;
*min_line_ret = min_line;
return UNDO_NORMAL;
diff --git a/undo.h b/undo.h
@@ -5,8 +5,8 @@ typedef enum {
} ledit_undo_status;
typedef struct ledit_undo_stack ledit_undo_stack;
-typedef void (*undo_insert_callback)(void *, int, int, char *, int);
-typedef void (*undo_delete_callback)(void *, int, int, int, int);
+typedef void (*undo_insert_callback)(void *, size_t, size_t, char *, size_t);
+typedef void (*undo_delete_callback)(void *, size_t, size_t, size_t, size_t);
ledit_undo_stack *ledit_undo_stack_create(void);
void ledit_undo_stack_destroy(ledit_undo_stack *undo);
@@ -24,10 +24,10 @@ void ledit_push_undo_delete(
ledit_undo_status ledit_undo(
ledit_undo_stack *undo, enum ledit_mode mode,
void *callback_data, undo_insert_callback insert_cb, undo_delete_callback delete_cb,
- int *cur_line_ret, int *cur_index_ret, int *min_line_ret
+ size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret
);
ledit_undo_status ledit_redo(
ledit_undo_stack *undo, enum ledit_mode mode,
void *callback_data, undo_insert_callback insert_cb, undo_delete_callback delete_cb,
- int *cur_line_ret, int *cur_index_ret, int *min_line_ret
+ size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret
);
diff --git a/util.h b/util.h
@@ -1,3 +1,5 @@
+/* FIXME: rename this to draw_util.h and rename macros.h to util.h */
+
typedef struct {
XftDraw *xftdraw;
Pixmap pixmap;
diff --git a/view.c b/view.c
@@ -0,0 +1,2096 @@
+/* FIXME: shrink buffers when text length less than a fourth of the size */
+/* FIXME: also cache PangoLayouts since keeping them around isn't really of much use? */
+/* FIXME: handle all undo within buffer to keep it consistent */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <pango/pangoxft.h>
+#include <X11/extensions/Xdbe.h>
+
+#include "pango-compat.h"
+#include "memory.h"
+#include "common.h"
+#include "txtbuf.h"
+#include "undo.h"
+#include "cache.h"
+#include "theme.h"
+#include "window.h"
+#include "buffer.h"
+
+static PangoAttrList *basic_attrs = NULL;
+
+static void init_line(ledit_view *view, ledit_view_line *line);
+static void copy_selection_to_x_primary(ledit_view *view, size_t line1, size_t byte1, size_t line2, size_t byte2);
+static void set_pixmap_line_helper(void *data, size_t line, size_t index);
+static void invalidate_pixmap_line_helper(void *data, size_t line);
+static void set_layout_line_helper(void *data, size_t line, size_t index);
+static void invalidate_layout_line_helper(void *data, size_t line);
+
+/*
+ * Assign a cache index to line and set text and highlight of the pango layout.
+ */
+static void set_pango_text_and_highlight(ledit_view *view, size_t line);
+
+/*
+ * Get the pango layout for line.
+ * This first assigns a cache index (by calling set_pango_text_and_highlight).
+ */
+static PangoLayout *get_pango_layout(ledit_view *view, size_t line);
+
+/*
+ * Get an attribute list for a text highlight between the given range.
+ */
+static PangoAttrList *get_pango_attributes(ledit_view *view, size_t start_byte, size_t end_byte);
+
+/*
+ * Set the attributes for a PangoLayout belonging to the given line index.
+ * If the line is part of the view's selection, the selection is set.
+ * If that is not the case but cursor_index is set for the line, the character
+ * at that position is highlighted (this is used for the normal mode cursor).
+ * Otherwise, the default attributes (basic_attrs) are set.
+ */
+static void set_line_layout_attrs(ledit_view *view, size_t line, PangoLayout *layout);
+
+static void swap_sz(size_t *a, size_t *b);
+
+static int line_visible_callback(void *data, size_t line);
+static void set_pixmap_line_helper(void *data, size_t line, size_t index);
+static void invalidate_pixmap_line_helper(void *data, size_t line);
+static void invalidate_layout_line_helper(void *data, size_t line);
+
+/* FIXME: This is weird because mode is per-view but the undo mode group
+ is changed for the entire buffer. */
+void
+view_set_mode(ledit_view *view, enum ledit_mode mode) {
+ view->mode = mode;
+ ledit_change_mode_group(view->buffer->undo);
+ ledit_window_set_mode(view->window, mode);
+}
+
+ledit_view *
+view_create(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size_t line, size_t pos) {
+ if (basic_attrs == NULL) {
+ basic_attrs = pango_attr_list_new();
+ #if PANGO_VERSION_CHECK(1, 44, 0)
+ PangoAttribute *no_hyphens = pango_attr_insert_hyphens_new(FALSE);
+ pango_attr_list_insert(basic_attrs, no_hyphens);
+ #endif
+ }
+
+ ledit_view *view = ledit_malloc(sizeof(ledit_view));
+ view->mode = mode;
+ view->buffer = buffer;
+ view->window = ledit_window_create(buffer->common, theme, mode);
+ view->theme = theme;
+ view->cache = cache_create(buffer->common->dpy);
+ view->cur_action = (struct action){ACTION_NONE, NULL};
+ ledit_window_set_scroll_callback(view->window, &view_scroll_handler, view);
+ ledit_window_set_button_callback(view->window, &view_button_handler, view);
+ ledit_window_set_resize_callback(view->window, &view_resize_textview, view);
+
+ view->lines = ledit_reallocarray(NULL, buffer->lines_cap, sizeof(ledit_view_line));
+ view->lines_cap = buffer->lines_cap;
+ view->lines_gap = buffer->lines_num;
+ view->lines_num = buffer->lines_num;
+ for (size_t i = 0; i < view->lines_num; i++) {
+ init_line(view, &view->lines[i]);
+ }
+ view->cur_line = line;
+ view->cur_index = pos;
+ view->total_height = 0;
+ view->display_offset = 0;
+ view->sel.line1 = view->sel.byte1 = 0;
+ view->sel.line2 = view->sel.byte2 = 0;
+ view->selecting = 0;
+ view->sel_valid = 0;
+ view->redraw = 1;
+ ledit_view_line *vl = view_get_line(view, line);
+ vl->cursor_index = pos;
+ vl->cursor_index_valid = 1;
+
+ view_recalc_all_lines(view);
+
+ return view;
+}
+
+ledit_view_line *
+view_get_line(ledit_view *view, size_t index) {
+ assert(index < view->lines_num);
+ return index < view->lines_gap ?
+ &view->lines[index] :
+ &view->lines[index + view->lines_cap - view->lines_num];
+}
+
+static void
+move_line_gap(ledit_view *view, size_t index) {
+ move_gap(
+ view->lines, sizeof(ledit_view_line), index,
+ view->lines_gap, view->lines_cap, view->lines_num,
+ &view->lines_gap
+ );
+}
+
+static void
+resize_and_move_line_gap(ledit_view *view, size_t min_size, size_t index) {
+ /* FIXME: Add to common bug list: used sizeof(ledit_line) instead of sizeof(ledit_view_line) */
+ view->lines = resize_and_move_gap(
+ view->lines, sizeof(ledit_view_line),
+ view->lines_gap, view->lines_cap, view->lines_num,
+ min_size, index,
+ &view->lines_gap, &view->lines_cap
+ );
+}
+
+void
+view_notify_insert_text(ledit_view *view, size_t line, size_t index, size_t len) {
+ ledit_view_line *vl = view_get_line(view, line);
+ vl->text_dirty = 1;
+ if (line == view->cur_line && index < view->cur_index) {
+ view->cur_index += len;
+ view_set_line_cursor_attrs(view, line, view->cur_index);
+ }
+ if (view->sel_valid)
+ view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
+}
+
+void
+view_notify_delete_text(ledit_view *view, size_t line, size_t index, size_t len) {
+ ledit_view_line *vl = view_get_line(view, line);
+ vl->text_dirty = 1;
+ if (line == view->cur_line) {
+ if (index + len <= view->cur_index) {
+ view->cur_index -= len;
+ } else if (index < view->cur_index && index + len > view->cur_index) {
+ view->cur_index = index;
+ }
+ /* just so it isn't stuck at end of line after deletion */
+ if (view->mode == NORMAL) {
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
+ );
+ }
+ view_set_line_cursor_attrs(view, line, view->cur_index);
+ }
+ if (view->sel_valid)
+ view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
+}
+
+void
+view_notify_append_line(ledit_view *view, size_t line) {
+ size_t new_len = view->lines_num + 1;
+ if (new_len <= view->lines_num)
+ err_overflow();
+ resize_and_move_line_gap(view, new_len, line + 1);
+ if (line < view->cur_line)
+ view->cur_line++;
+ if (view->sel_valid)
+ view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
+ view->lines_num++;
+ view->lines_gap++;
+ ledit_view_line *vl = view_get_line(view, line + 1);
+ init_line(view, vl);
+}
+
+void
+view_notify_delete_lines(ledit_view *view, size_t index1, size_t index2) {
+ if (index2 < view->cur_line) {
+ view->cur_line -= index2 - index1 + 1;
+ } else if (index1 <= view->cur_line) {
+ /* FIXME: set cur_index properly */
+ if (index2 < view->lines_num - 1) {
+ view->cur_line = index2 + 1;
+ view->cur_index = 0;
+ } else if (index1 > 0) {
+ view->cur_line = index1 - 1;
+ view->cur_index = 0;
+ } else {
+ /* should never happen */
+ view->cur_line = 0;
+ view->cur_index = 0;
+ }
+ }
+ if (view->sel_valid)
+ view_set_selection(view, view->cur_line, view->cur_index, view->cur_line, view->cur_index);
+ cache_invalidate_from_line(
+ view->cache, index1, view,
+ &invalidate_pixmap_line_helper, &invalidate_layout_line_helper
+ );
+ move_line_gap(view, index1);
+ view->lines_num -= index2 - index1 + 1;
+ /* force first entry to offset 0 if first line was deleted */
+ if (index1 == 0) {
+ ledit_view_line *vl = view_get_line(view, 0);
+ vl->y_offset = 0;
+ }
+}
+
+void
+view_destroy(ledit_view *view) {
+ cache_destroy(view->cache);
+ ledit_window_destroy(view->window);
+ free(view->lines);
+ free(view);
+}
+
+void
+view_cleanup(void) {
+ if (basic_attrs)
+ pango_attr_list_unref(basic_attrs);
+}
+
+static PangoAttrList *
+get_pango_attributes(ledit_view *view, size_t start_byte, size_t end_byte) {
+ XRenderColor fg = view->theme->text_fg.color;
+ XRenderColor bg = view->theme->text_bg.color;
+ PangoAttribute *attr0 = pango_attr_background_new(fg.red, fg.green, fg.blue);
+ PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg.blue);
+ attr0->start_index = start_byte;
+ attr0->end_index = end_byte;
+ attr1->start_index = start_byte;
+ attr1->end_index = end_byte;
+ PangoAttrList *list = pango_attr_list_new();
+ pango_attr_list_insert(list, attr0);
+ pango_attr_list_insert(list, attr1);
+ #if PANGO_VERSION_CHECK(1, 44, 0)
+ PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
+ pango_attr_list_insert(list, attr2);
+ #endif
+ return list;
+}
+
+/* this takes layout directly to possibly avoid infinite recursion */
+static void
+set_line_layout_attrs(ledit_view *view, size_t line, PangoLayout *layout) {
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ ledit_view_line *vl = view_get_line(view, line);
+ PangoAttrList *list = NULL;
+ if (view->sel_valid) {
+ ledit_range sel = view->sel;
+ view_sort_selection(&sel.line1, &sel.byte1, &sel.line2, &sel.byte2);
+ if (sel.line1 < line && sel.line2 > line) {
+ list = get_pango_attributes(view, 0, ll->len);
+ } else if (sel.line1 == line && sel.line2 == line) {
+ size_t start = sel.byte1, end = sel.byte2;
+ if (start > end)
+ swap_sz(&start, &end);
+ list = get_pango_attributes(view, start, end);
+ } else if (sel.line1 == line && sel.line2 > line) {
+ list = get_pango_attributes(view, sel.byte1, ll->len);
+ } else if (sel.line1 < line && sel.line2 == line) {
+ list = get_pango_attributes(view, 0, sel.byte2);
+ }
+ } else if (vl->cursor_index_valid) {
+ /* FIXME: does just adding one really do the right thing? */
+ list = get_pango_attributes(view, vl->cursor_index, vl->cursor_index + 1);
+ }
+ if (list != NULL) {
+ pango_layout_set_attributes(layout, list);
+ pango_attr_list_unref(list);
+ } else {
+ pango_layout_set_attributes(layout, basic_attrs);
+ }
+ vl->highlight_dirty = 0;
+ vl->dirty = 1;
+}
+
+void
+view_set_line_cursor_attrs(ledit_view *view, size_t line, size_t index) {
+ ledit_view_line *ll = view_get_line(view, line);
+ ll->cursor_index = index;
+ ll->cursor_index_valid = 1;
+ ll->highlight_dirty = 1;
+ ll->dirty = 1;
+ view->redraw = 1;
+}
+
+void
+view_wipe_line_cursor_attrs(ledit_view *view, size_t line) {
+ ledit_view_line *vl = view_get_line(view, line);
+ vl->cursor_index = 0;
+ vl->cursor_index_valid = 0;
+ vl->highlight_dirty = 1;
+ vl->dirty = 1;
+ view->redraw = 1;
+}
+
+static int
+line_visible_callback(void *data, size_t line) {
+ return view_line_visible((ledit_view*)data, line);
+}
+
+/* FIXME: standardize variable names (line/line_index, etc.) */
+void
+view_render_line(ledit_view *view, size_t line_index) {
+ /* FIXME: check for <= 0 on size */
+ ledit_view_line *ll = view_get_line(view, line_index);
+ assert(!ll->h_dirty); /* FIXME */
+ PangoLayout *layout = get_pango_layout(view, line_index);
+ if (!ll->cache_pixmap_valid) {
+ cache_assign_pixmap_index(
+ view->cache, line_index, view,
+ &line_visible_callback, &set_pixmap_line_helper,
+ &invalidate_pixmap_line_helper
+ );
+ }
+ cache_pixmap *pix = cache_get_pixmap(view->cache, ll->cache_pixmap_index);
+ /* FIXME: fail on too large pixmap size (e.g. way too long line) */
+ /* FIXME: sensible default pixmap sizes here */
+ /* FIXME: handle this in cache */
+ if (pix->pixmap == None || pix->draw == NULL) {
+ pix->pixmap = XCreatePixmap(
+ view->buffer->common->dpy, view->window->drawable,
+ ll->w + 10, ll->h + 10, view->buffer->common->depth
+ );
+ pix->w = ll->w + 10;
+ pix->h = ll->h + 10;
+ pix->draw = XftDrawCreate(
+ view->buffer->common->dpy, pix->pixmap,
+ view->buffer->common->vis, view->buffer->common->cm
+ );
+ } else if (pix->w < ll->w || pix->h < ll->h) {
+ int new_w = ll->w > pix->w ? ll->w + 10 : pix->w + 10;
+ int new_h = ll->h > pix->h ? ll->h + 10 : pix->h + 10;
+ XFreePixmap(view->buffer->common->dpy, pix->pixmap);
+ pix->pixmap = XCreatePixmap(
+ view->buffer->common->dpy, view->window->drawable,
+ new_w, new_h, view->buffer->common->depth
+ );
+ pix->w = new_w;
+ pix->h = new_h;
+ XftDrawChange(pix->draw, pix->pixmap);
+ }
+ XftDrawRect(pix->draw, &view->theme->text_bg, 0, 0, ll->w, ll->h);
+ pango_xft_render_layout(pix->draw, &view->theme->text_fg, layout, 0, 0);
+ ll->dirty = 0;
+}
+
+static void
+init_line(ledit_view *view, ledit_view_line *line) {
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ line->view = view;
+ line->w = text_w;
+ line->h = 0;
+ line->y_offset = 0;
+ line->cache_pixmap_index = 0;
+ line->cache_layout_index = 0;
+ line->softlines = 0;
+ line->cursor_index = 0;
+ line->cursor_index_valid = 0;
+ line->cache_pixmap_valid = 0;
+ line->cache_layout_valid = 0;
+ line->dirty = 1;
+ line->text_dirty = 1;
+ line->highlight_dirty = 1;
+ line->h_dirty = 1;
+}
+
+static void
+set_pixmap_line_helper(void *data, size_t line, size_t index) {
+ ledit_view_line *vl = view_get_line((ledit_view *)data, line);
+ vl->cache_pixmap_index = index;
+ vl->cache_pixmap_valid = 1;
+}
+
+static void
+invalidate_pixmap_line_helper(void *data, size_t line) {
+ ledit_view_line *vl = view_get_line((ledit_view *)data, line);
+ vl->cache_pixmap_valid = 0;
+}
+
+static void
+set_layout_line_helper(void *data, size_t line, size_t index) {
+ ledit_view_line *vl = view_get_line((ledit_view *)data, line);
+ vl->cache_layout_index = index;
+ vl->cache_layout_valid = 1;
+}
+
+static void
+invalidate_layout_line_helper(void *data, size_t line) {
+ ledit_view_line *vl = view_get_line((ledit_view *)data, line);
+ vl->cache_layout_valid = 0;
+}
+
+/* set text of pango layout if dirty and recalculate height of line
+ * - if height hasn't changed, nothing further is done
+ * - if height has changed, offset of all following lines is changed */
+void
+view_recalc_line(ledit_view *view, size_t line) {
+ ledit_view_line *l = view_get_line(view, line);
+ if (l->text_dirty)
+ set_pango_text_and_highlight(view, line);
+
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ /* if height changed, set height of current line
+ * and adjust offsets of all lines following it */
+ if (l->h_dirty) {
+ l->h_dirty = 0;
+ long off = l->y_offset + l->h;
+ for (size_t i = line + 1; i < view->lines_num; i++) {
+ l = view_get_line(view, i);
+ l->y_offset = off;
+ off += l->h;
+ }
+ view->total_height = off;
+ if (l->y_offset < view->display_offset + text_h)
+ view->redraw = 1;
+ }
+ l = view_get_line(view, line);
+ if (l->y_offset < view->display_offset + text_h &&
+ l->y_offset + l->h >= view->display_offset) {
+ view->redraw = 1;
+ }
+ ledit_window_set_scroll_max(view->window, view->total_height);
+ view_scroll(view, view->display_offset);
+}
+
+/* set text of pango layout and recalculate height
+ * and offset for all lines starting at 'line' */
+void
+view_recalc_from_line(ledit_view *view, size_t line) {
+ ledit_view_line *l = view_get_line(view, line);
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ long off = l->y_offset;
+ if (off < view->display_offset + text_h)
+ view->redraw = 1;
+ for (size_t i = line; i < view->lines_num; i++) {
+ l = view_get_line(view, i);
+ if (l->text_dirty)
+ set_pango_text_and_highlight(view, i);
+ l->h_dirty = 0;
+ l->y_offset = off;
+ off += l->h;
+ }
+ view->total_height = off;
+ ledit_window_set_scroll_max(view->window, view->total_height);
+ view_scroll(view, view->display_offset);
+}
+
+void
+view_recalc_all_lines(ledit_view *view) {
+ /* force first line to offset 0 */
+ ledit_view_line *l = view_get_line(view, 0);
+ l->y_offset = 0;
+ view_recalc_from_line(view, 0);
+}
+
+int
+view_line_visible(ledit_view *view, size_t index) {
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ ledit_view_line *l = view_get_line(view, index);
+ return l->y_offset < view->display_offset + text_h &&
+ l->y_offset + l->h > view->display_offset;
+}
+
+/* FIXME: these functions are only here because they need the PangoLayouts to
+ determine grapheme boundaries. Maybe use a separate library for that? */
+size_t
+view_next_cursor_pos(ledit_view *view, size_t line, size_t byte, int num) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t c = line_byte_to_char(ll, byte);
+ size_t cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ if (num < 0)
+ return 0; /* FIXME: error */
+ for (size_t i = 0; i < (size_t)num; i++) {
+ cur_byte = ledit_line_next_utf8(ll, byte);
+ for (c++; c < (size_t)nattrs; c++) {
+ if (attrs[c].is_cursor_position)
+ break;
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ if (cur_byte >= ll->len)
+ break;
+ }
+ return cur_byte <= ll->len ? cur_byte : ll->len;
+}
+
+size_t
+view_prev_cursor_pos(ledit_view *view, size_t line, size_t byte, int num) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t c = line_byte_to_char(ll, byte);
+ size_t cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ if (num < 0)
+ return 0; /* FIXME: error */
+ for (int i = 0; i < num; i++) {
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
+ for (; c > 0; c--) {
+ if (attrs[c-1].is_cursor_position)
+ break;
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
+ }
+ if (cur_byte <= 0)
+ break;
+ }
+ return cur_byte > 0 ? cur_byte : 0;
+}
+
+static int
+line_next_word(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index, int wrapped_line,
+ size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ int cur_byte = wrapped_line ? byte : ledit_line_next_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ for (size_t i = wrapped_line ? char_index : char_index + 1; i < (size_t)nattrs; i++) {
+ if (attrs[i].is_word_start) {
+ *char_ret = i;
+ *real_byte_ret = cur_byte;
+ *byte_ret = cur_byte;
+ return 0;
+ }
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+static int
+line_prev_word(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index,
+ size_t *char_ret, size_t *byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t cur_byte = ledit_line_prev_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ if (char_index > (size_t)nattrs)
+ return -1;
+ /* this is a bit weird because size_t can't be negative */
+ for (size_t i = char_index; i > 0; i--) {
+ if (attrs[i-1].is_word_start) {
+ *char_ret = i-1;
+ *byte_ret = cur_byte;
+ return 0;
+ }
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+static int
+line_prev_bigword(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index,
+ size_t *char_ret, size_t *byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t cur_byte = ledit_line_prev_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ size_t next_cursorb = byte;
+ size_t next_cursorc = char_index;
+ int found_word = 0;
+ /* this is a bit weird because size_t can't be negative */
+ for (size_t i = char_index; i > 0; i--) {
+ if (!found_word && !attrs[i-1].is_white) {
+ found_word = 1;
+ } else if (found_word && attrs[i-1].is_white) {
+ *char_ret = next_cursorc;
+ *byte_ret = next_cursorb;
+ return 0;
+ }
+ if (found_word && i-1 == 0) {
+ *char_ret = 0;
+ *byte_ret = 0;
+ return 0;
+ }
+ if (attrs[i-1].is_cursor_position) {
+ next_cursorc = i-1;
+ next_cursorb = cur_byte;
+ }
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+static int
+line_next_bigword_end(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index, int wrapped_line,
+ size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ size_t last_cursorb = 0, last_cursorc = 0;
+ int last_cursor_valid;
+ if (wrapped_line) {
+ last_cursorb = byte;
+ last_cursorc = char_index;
+ last_cursor_valid = 1;
+ } else {
+ last_cursor_valid = 0;
+ }
+ int found_word = 0;
+ size_t cur_byte = byte;
+ for (size_t i = char_index; i < (size_t)nattrs; i++) {
+ if (last_cursor_valid && !found_word && !attrs[i].is_white) {
+ found_word = 1;
+ } else if (found_word && attrs[i].is_white) {
+ *char_ret = last_cursorc;
+ *real_byte_ret = cur_byte;
+ *byte_ret = last_cursorb;
+ return 0;
+ }
+ if (attrs[i].is_cursor_position) {
+ last_cursorc = i;
+ last_cursorb = cur_byte;
+ last_cursor_valid = 1;
+ }
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+static int
+line_next_word_end(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index, int wrapped_line,
+ size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t cur_byte = ledit_line_next_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ size_t last_cursorb = 0, last_cursorc = 0;
+ int last_cursor_valid;
+ if (wrapped_line) {
+ last_cursorb = byte;
+ last_cursorc = char_index;
+ last_cursor_valid = 1;
+ } else {
+ last_cursor_valid = 0;
+ }
+ for (size_t i = char_index + 1; i < (size_t)nattrs; i++) {
+ if (last_cursor_valid && attrs[i].is_word_end) {
+ *char_ret = last_cursorc;
+ *real_byte_ret = cur_byte;
+ *byte_ret = last_cursorb;
+ return 0;
+ }
+ if (attrs[i].is_cursor_position) {
+ last_cursorc = i;
+ last_cursorb = cur_byte;
+ last_cursor_valid = 1;
+ }
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+static int
+line_next_bigword(
+ ledit_view *view,
+ size_t line, size_t byte, size_t char_index, int wrapped_line,
+ size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ int found_ws = wrapped_line;
+ for (size_t i = char_index; i < (size_t)nattrs; i++) {
+ if (!found_ws && attrs[i].is_white) {
+ found_ws = 1;
+ } else if (found_ws && !attrs[i].is_white) {
+ *char_ret = i;
+ *real_byte_ret = cur_byte;
+ *byte_ret = cur_byte;
+ return 0;
+ }
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ return -1;
+}
+
+size_t
+view_line_next_non_whitespace(ledit_view *view, size_t line, size_t byte) {
+ int nattrs;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ size_t c = line_byte_to_char(ll, byte);
+ size_t cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ for (; c < (size_t)nattrs; c++) {
+ if (!attrs[c].is_white)
+ return cur_byte;
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
+ }
+ return ll->len;
+}
+
+/* FIXME: document that word and bigword are a bit weird because word uses unicode semantics */
+
+#define GEN_NEXT_WORD(name, func) \
+void \
+view_next_##name( \
+ ledit_view *view, \
+ size_t line, size_t byte, int num_repeat, \
+ size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret) { \
+ size_t cur_line = line; \
+ size_t cur_byte = byte; \
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line); \
+ size_t cur_char = line_byte_to_char(ll, byte); \
+ size_t real_byte = 0; \
+ int last_ret = -1; \
+ int wrapped_line; \
+ for (int i = 0; i < num_repeat; i++) { \
+ wrapped_line = 0; \
+ while ((last_ret = func(view, cur_line, cur_byte, cur_char, \
+ wrapped_line, &cur_char, &cur_byte, &real_byte)) == -1 && \
+ cur_line < view->lines_num - 1) { \
+ cur_line++; \
+ cur_byte = 0; \
+ wrapped_line = 1; \
+ } \
+ if (last_ret == -1 && cur_line == view->lines_num - 1) \
+ break; \
+ } \
+ if (last_ret == -1) { \
+ *line_ret = view->lines_num - 1; \
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, view->lines_num - 1); \
+ *byte_ret = view_get_legal_normal_pos(view, view->lines_num - 1, ll->len); \
+ *real_byte_ret = ll->len; \
+ } else { \
+ *line_ret = cur_line; \
+ *byte_ret = cur_byte; \
+ *real_byte_ret = real_byte; \
+ } \
+}
+
+#define GEN_PREV_WORD(name, func) \
+void \
+view_prev_##name( \
+ ledit_view *view, \
+ size_t line, size_t byte, int num_repeat, \
+ size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret) { \
+ size_t cur_line = line; \
+ size_t cur_byte = byte; \
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line); \
+ size_t cur_char = line_byte_to_char(ll, byte); \
+ int last_ret = -1; \
+ for (int i = 0; i < num_repeat; i++) { \
+ while ((last_ret = func(view, cur_line, cur_byte, cur_char, \
+ &cur_char, &cur_byte)) == -1 && cur_line > 0) { \
+ cur_line--; \
+ ll = ledit_buffer_get_line(view->buffer, cur_line); \
+ cur_byte = ll->len; \
+ } \
+ if (last_ret == -1 && cur_line == 0) \
+ break; \
+ } \
+ if (last_ret == -1) { \
+ *line_ret = 0; \
+ *byte_ret = 0; \
+ *real_byte_ret = 0; \
+ } else { \
+ *line_ret = cur_line; \
+ *byte_ret = cur_byte; \
+ *real_byte_ret = cur_byte; \
+ } \
+}
+
+GEN_NEXT_WORD(word, line_next_word)
+GEN_NEXT_WORD(word_end, line_next_word_end)
+GEN_NEXT_WORD(bigword, line_next_bigword)
+GEN_NEXT_WORD(bigword_end, line_next_bigword_end)
+GEN_PREV_WORD(word, line_prev_word)
+GEN_PREV_WORD(bigword, line_prev_bigword)
+
+void
+view_get_pos_softline_bounds(
+ ledit_view *view, size_t line, size_t pos,
+ size_t *start_byte_ret, size_t *end_byte_ret) {
+ assert(line < view->lines_num);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ assert(pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(view, line);
+ int x, sli;
+ if (pos > INT_MAX)
+ err_overflow();
+ pango_layout_index_to_line_x(layout, (int)pos, 0, &sli, &x);
+ PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, sli);
+ *start_byte_ret = (size_t)pl->start_index;
+ *end_byte_ret = (size_t)(pl->start_index + pl->length);
+}
+
+void
+view_get_softline_bounds(
+ ledit_view *view, size_t line, int softline,
+ size_t *start_byte_ret, size_t *end_byte_ret) {
+ assert(line < view->lines_num);
+ ledit_view_line *vl = view_get_line(view, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ assert(softline < vl->softlines);
+ PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, softline);
+ *start_byte_ret = (size_t)pl->start_index;
+ *end_byte_ret = (size_t)(pl->start_index + pl->length);
+}
+
+int
+view_get_softline_count(ledit_view *view, size_t line) {
+ assert(line < view->lines_num);
+ ledit_view_line *vl = view_get_line(view, line);
+ if (vl->text_dirty)
+ set_pango_text_and_highlight(view, line);
+ return vl->softlines;
+}
+
+int
+view_pos_to_softline(ledit_view *view, size_t line, size_t pos) {
+ assert(line < view->lines_num);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ assert(pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(view, line);
+ int x, sli;
+ if (pos > INT_MAX)
+ err_overflow();
+ pango_layout_index_to_line_x(layout, (int)pos, 0, &sli, &x);
+ return sli;
+}
+
+void
+view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int *x_ret, int *y_ret, int *h_ret) {
+ assert(line < view->lines_num);
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ assert(pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(view, line);
+ PangoRectangle strong, weak;
+ if (pos > INT_MAX)
+ err_overflow();
+ pango_layout_get_cursor_pos(layout, (int)pos, &strong, &weak);
+ *x_ret = strong.x / PANGO_SCALE;
+ *y_ret = strong.y / PANGO_SCALE;
+ *h_ret = strong.height / PANGO_SCALE;
+}
+
+/* prev_index_ret is used instead of just calling get_legal_normal_pos
+ because weird things happen otherwise
+ -> in certain cases, this is still weird because prev_index_ret sometimes
+ is not at the end of the line, but this is the best I could come up
+ with for now */
+size_t
+view_move_cursor_visually(ledit_view *view, size_t line, size_t pos, int movement, size_t *prev_index_ret) {
+ if (pos > INT_MAX)
+ err_overflow();
+ /* FIXME: trailing */
+ int trailing = 0;
+ ledit_line *cur_line = ledit_buffer_get_line(view->buffer, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ int tmp_index;
+ int new_index = (int)pos, last_index = (int)pos;
+ int dir = 1;
+ int num = movement;
+ if (movement < 0) {
+ dir = -1;
+ num = -movement;
+ }
+ /* FIXME: This is stupid. Anything outside the range of int won't work
+ anyways because of pango (and because everything else would break
+ anyways with such long lines), so it's stupid to do all this weird
+ casting. */
+ if (cur_line->len > INT_MAX)
+ err_overflow();
+ while (num > 0) {
+ tmp_index = new_index;
+ pango_layout_move_cursor_visually(
+ layout, TRUE,
+ new_index, trailing, dir,
+ &new_index, &trailing
+ );
+ /* for some reason, this is necessary */
+ if (new_index < 0)
+ new_index = 0;
+ else if (new_index > (int)cur_line->len)
+ new_index = (int)cur_line->len;
+ num--;
+ if (tmp_index != new_index)
+ last_index = tmp_index;
+ }
+ /* FIXME: Allow cursor to be at end of soft line */
+ /* we don't currently support a difference between the cursor being at
+ the end of a soft line and the beginning of the next line */
+ /* FIXME: spaces at end of softlines are weird in normal mode */
+ while (trailing > 0) {
+ trailing--;
+ new_index = ledit_line_next_utf8(cur_line, new_index);
+ }
+ if (new_index < 0)
+ new_index = 0;
+ if (prev_index_ret)
+ *prev_index_ret = (size_t)last_index;
+ return (size_t)new_index;
+}
+
+/* FIXME: implement */
+/*
+int
+ledit_line_nearest_cursor_pos(ledit_line *line, int byte) {
+}
+
+void
+ledit_line_word_boundaries(ledit_line *line, int byte, int *start_ret, int *end_ret) {
+}
+*/
+
+static void
+set_pango_text_and_highlight(ledit_view *view, size_t line) {
+ cache_layout *cl;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ ledit_view_line *vl = view_get_line(view, line);
+ char old_valid = vl->cache_layout_valid;
+ if (!vl->cache_layout_valid) {
+ cache_assign_layout_index(
+ view->cache, line,
+ view, &set_layout_line_helper, &invalidate_layout_line_helper
+ );
+ cl = cache_get_layout(view->cache, vl->cache_layout_index);
+ } else {
+ cl = cache_get_layout(view->cache, vl->cache_layout_index);
+ }
+ if (cl->layout == NULL) {
+ cl->layout = pango_layout_new(view->window->context);
+ pango_layout_set_font_description(cl->layout, view->window->font);
+ pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR);
+ }
+ if (vl->text_dirty || !old_valid) {
+ ledit_buffer_normalize_line(ll);
+ if (ll->len > INT_MAX)
+ err_overflow();
+ pango_layout_set_text(cl->layout, ll->text, (int)ll->len);
+ set_line_layout_attrs(view, line, cl->layout);
+ /* FIXME: is this guard necessary? */
+ vl->softlines = ll->len > 0 ? pango_layout_get_line_count(cl->layout) : 1;
+ pango_layout_set_width(cl->layout, vl->w * PANGO_SCALE);
+ int w, h;
+ pango_layout_get_pixel_size(cl->layout, &w, &h);
+ if (h != vl->h) {
+ vl->h = h;
+ vl->h_dirty = 1;
+ }
+ vl->text_dirty = 0;
+ vl->dirty = 1;
+ } else if (vl->highlight_dirty) {
+ set_line_layout_attrs(view, line, cl->layout);
+ }
+ vl->highlight_dirty = 0;
+}
+
+static PangoLayout *
+get_pango_layout(ledit_view *view, size_t line) {
+ set_pango_text_and_highlight(view, line);
+ ledit_view_line *vl = view_get_line(view, line);
+ cache_layout *cl = cache_get_layout(
+ view->cache, vl->cache_layout_index
+ );
+ return cl->layout;
+}
+
+/* FIXME: document what works with pango units and what not */
+void
+ledit_pos_to_x_softline(ledit_view *view, size_t line, size_t pos, int *x_ret, int *softline_ret) {
+ ledit_view_line *vl = view_get_line(view, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ if (pos > INT_MAX)
+ err_overflow();
+ pango_layout_index_to_line_x(layout, (int)pos, 0, softline_ret, x_ret);
+ PangoLayoutLine *pango_line = pango_layout_get_line_readonly(layout, *softline_ret);
+ /* add left margin to x position if line is aligned right */
+ if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
+ PangoRectangle rect;
+ pango_layout_line_get_extents(pango_line, NULL, &rect);
+ *x_ret += (vl->w * PANGO_SCALE - rect.width);
+ }
+ /* if in normal mode, change position to the middle of the
+ current rectangle so that moving around won't jump weirdly */
+ /* FIXME: also in visual? */
+ /* FIXME: this is too much magic for my taste */
+ if (view->mode == NORMAL) {
+ PangoRectangle rect;
+ pango_layout_index_to_pos(layout, (int)pos, &rect);
+ *x_ret += rect.width / 2;
+ }
+}
+
+/* FIXME: change this to return pos_ret directly */
+void
+ledit_x_softline_to_pos(ledit_view *view, size_t line, int x, int softline, size_t *pos_ret) {
+ int trailing = 0;
+ int x_relative = x;
+ ledit_view_line *vl = view_get_line(view, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ PangoLayoutLine *pango_line =
+ pango_layout_get_line_readonly(layout, softline);
+ /* x is absolute, so the margin at the left needs to be subtracted */
+ if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
+ PangoRectangle rect;
+ pango_layout_line_get_extents(pango_line, NULL, &rect);
+ x_relative -= (vl->w * PANGO_SCALE - rect.width);
+ }
+ int tmp_pos;
+ pango_layout_line_x_to_index(
+ pango_line, x_relative, &tmp_pos, &trailing
+ );
+ *pos_ret = (size_t)tmp_pos;
+ /* if in insert mode, snap to the nearest border between graphemes */
+ /* FIXME: add parameter for this instead of checking mode */
+ if (view->mode == INSERT) {
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ while (trailing > 0) {
+ trailing--;
+ *pos_ret = ledit_line_next_utf8(ll, *pos_ret);
+ }
+ }
+}
+
+size_t
+view_get_legal_normal_pos(ledit_view *view, size_t line, size_t pos) {
+ /* move back one grapheme if at end of line */
+ size_t ret = pos;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line);
+ if (pos == ll->len && pos > 0) {
+ int nattrs;
+ PangoLayout *layout = get_pango_layout(view, line);
+ const PangoLogAttr *attrs =
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
+ if (nattrs < 2)
+ return 0;
+ size_t cur = nattrs - 2;
+ ret = ledit_line_prev_utf8(ll, ret);
+ while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) {
+ cur--;
+ ret = ledit_line_prev_utf8(ll, ret);
+ }
+ }
+ return ret;
+}
+
+void
+view_delete_range(
+ ledit_view *view, enum delete_mode delmode,
+ size_t line_index1, size_t byte_index1,
+ size_t line_index2, size_t byte_index2,
+ size_t *new_line_ret, size_t *new_byte_ret,
+ ledit_range *final_range_ret, txtbuf *text_ret) {
+ view_delete_range_base(
+ view, delmode,
+ line_index1, byte_index1,
+ line_index2, byte_index2,
+ new_line_ret, new_byte_ret,
+ final_range_ret, text_ret
+ );
+ /* need to start recalculating one line before in case first
+ line was deleted and offset is now wrong */
+ size_t min = line_index1 < line_index2 ? line_index1 : line_index2;
+ /* FIXME: a bit ugly to do this here */
+ for (size_t i = 0; i < view->buffer->views_num; i++) {
+ ledit_buffer_recalc_all_views_from_line(
+ view->buffer, min > 0 ? min - 1 : min
+ );
+ }
+}
+
+/* Note: line_index* and byte_index* don't need to be sorted */
+/* line_index1, byte_index1 are used as the cursor position in order
+ to determine the new cursor position */
+/* FIXME: use at least somewhat sensible variable names */
+/* FIXME: I once noticed a bug where using 'dG' to delete to the end of
+ the file caused a line index way larger than buffer->lines_num to be
+ given, but I couldn't reproduce this bug */
+void
+view_delete_range_base(
+ ledit_view *view, enum delete_mode delmode,
+ size_t line_index1, size_t byte_index1,
+ size_t line_index2, size_t byte_index2,
+ size_t *new_line_ret, size_t *new_byte_ret,
+ ledit_range *final_range_ret, txtbuf *text_ret) {
+ /* FIXME: Oh boy, this is nasty */
+ /* range line x, range byte x */
+ size_t rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0;
+ size_t new_line = 0, new_byte = 0;
+ assert(line_index1 < view->lines_num);
+ assert(line_index2 < view->lines_num);
+ /* FIXME: could this be simplified by just calculating the range and then using
+ the non-line-based version? */
+ if (delmode == DELETE_HARDLINE) {
+ int x, sl_useless;
+ size_t l1 = line_index1, l2 = line_index2;
+ if (line_index1 > line_index2) {
+ l1 = line_index2;
+ l2 = line_index1;
+ }
+ size_t dell1 = l1, dell2 = l2;
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, line_index1);
+ ledit_pos_to_x_softline(view, line_index1, byte_index1, &x, &sl_useless);
+ if (l1 > 0 && l2 < view->lines_num - 1) {
+ rgl1 = l1;
+ rgb1 = 0;
+ rgl2 = l2 + 1;
+ rgb2 = 0;
+ } else if (l1 > 0) {
+ rgl1 = l1 - 1;
+ ll = ledit_buffer_get_line(view->buffer, rgl1);
+ rgb1 = ll->len;
+ rgl2 = l2;
+ ll = ledit_buffer_get_line(view->buffer, rgl2);
+ rgb2 = ll->len;
+ } else if (l2 < view->lines_num - 1) {
+ rgl1 = l1;
+ rgb1 = 0;
+ rgl2 = l2 + 1;
+ rgb2 = 0;
+ } else {
+ rgl1 = l1;
+ rgb1 = 0;
+ rgl2 = l2;
+ ll = ledit_buffer_get_line(view->buffer, rgl2);
+ rgb2 = ll->len;
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1, rgl2, rgb2
+ );
+ }
+ /* default is dell1 = l1, dell2 = l2 */
+ if (l2 < view->lines_num - 1) {
+ new_line = l1;
+ ledit_x_softline_to_pos(
+ view, l2 + 1,
+ x, 0, &new_byte
+ );
+ } else if (l1 > 0) {
+ new_line = l1 - 1;
+ ledit_x_softline_to_pos(
+ view, l1 - 1,
+ x, 0, &new_byte
+ );
+ } else {
+ dell1 = l1 + 1;
+ dell2 = l2;
+ new_line = l1;
+ new_byte = 0;
+ /* happens when all lines are deleted, so one line has to be cleared */
+ ll = ledit_buffer_get_line(view->buffer, l1);
+ ledit_buffer_delete_line_section_base(
+ view->buffer, l1, 0, ll->len
+ );
+ }
+ if (dell1 <= dell2) {
+ ledit_buffer_delete_line_entries_base(view->buffer, dell1, dell2);
+ }
+ } else if (delmode == DELETE_SOFTLINE) {
+ int x, softline1, softline2;
+ ledit_line *line1 = ledit_buffer_get_line(view->buffer, line_index1);
+ ledit_view_line *vline1 = view_get_line(view, line_index1);
+ ledit_pos_to_x_softline(view, line_index1, byte_index1, &x, &softline1);
+ if (line_index1 == line_index2) {
+ int x_useless;
+ PangoLayout *layout = get_pango_layout(view, line_index1);
+ pango_layout_index_to_line_x(layout, byte_index2, 0, &softline2, &x_useless);
+ int l1 = softline1 < softline2 ? softline1 : softline2;
+ int l2 = softline1 < softline2 ? softline2 : softline1;
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout, l1);
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout, l2);
+ /* don't delete entire line if it is the last one remaining */
+ if (l1 == 0 && l2 == vline1->softlines - 1 && view->lines_num > 1) {
+ if (line_index1 < view->lines_num - 1) {
+ /* cursor can be moved to next hard line */
+ new_line = line_index1;
+ size_t tmp_byte;
+ ledit_x_softline_to_pos(
+ view, line_index1 + 1,
+ x, 0, &tmp_byte
+ );
+ new_byte = (size_t)tmp_byte;
+ rgl1 = line_index1;
+ rgb1 = 0;
+ rgl2 = line_index1 + 1;
+ rgb2 = 0;
+ } else {
+ /* cursor has to be be moved to previous hard line
+ because last line in buffer is deleted */
+ /* note: logically, line_index1 - 1 must be >= 0 because
+ view->lines_num > 1 && line_index1 >= view->lines_num - 1 */
+ new_line = line_index1 - 1;
+ ledit_line *prevline = ledit_buffer_get_line(view->buffer, new_line);
+ ledit_view_line *vprevline = view_get_line(view, new_line);
+ if (vprevline->text_dirty)
+ set_pango_text_and_highlight(view, new_line);
+ ledit_x_softline_to_pos(view, new_line, x, vprevline->softlines - 1, &new_byte);
+ rgl1 = line_index1 - 1;
+ rgb1 = prevline->len;
+ rgl2 = line_index1;
+ rgb2 = line1->len;
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_entry_base(view->buffer, line_index1);
+ } else {
+ assert(pl2->start_index + pl2->length >= pl1->start_index);
+ rgl1 = rgl2 = line_index1;
+ rgb1 = (size_t)pl1->start_index;
+ rgb2 = (size_t)(pl2->start_index + pl2->length);
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(
+ view->buffer, line_index1, rgb1, rgb2 - rgb1
+ );
+ if (l2 == vline1->softlines - 1 && line_index1 < view->lines_num - 1) {
+ new_line = line_index1 + 1;
+ size_t tmp_byte;
+ ledit_x_softline_to_pos(
+ view, line_index1 + 1,
+ x, 0, &tmp_byte
+ );
+ new_byte = (size_t)tmp_byte;
+ } else if (l2 < vline1->softlines - 1) {
+ new_line = line_index1;
+ size_t tmp_byte;
+ ledit_x_softline_to_pos(
+ view, line_index1,
+ x, l1, &tmp_byte
+ );
+ new_byte = (size_t)tmp_byte;
+ } else if (l1 > 0) {
+ new_line = line_index1;
+ size_t tmp_byte;
+ ledit_x_softline_to_pos(
+ view, line_index1,
+ x, l1 - 1, &tmp_byte
+ );
+ new_byte = (size_t)tmp_byte;
+ } else {
+ /* the line has been emptied and is the last line remaining */
+ new_line = 0;
+ new_byte = 0;
+ }
+ }
+ } else {
+ int x_useless, sl1, sl2;
+ size_t l1, l2, b1, b2;
+ if (line_index1 < line_index2) {
+ l1 = line_index1;
+ b1 = byte_index1;
+ l2 = line_index2;
+ b2 = byte_index2;
+ } else {
+ l1 = line_index2;
+ b1 = byte_index2;
+ l2 = line_index1;
+ b2 = byte_index1;
+ }
+ ledit_line *ll1 = ledit_buffer_get_line(view->buffer, l1);
+ ledit_line *ll2 = ledit_buffer_get_line(view->buffer, l2);
+ ledit_view_line *vl1 = view_get_line(view, l1);
+ ledit_view_line *vl2 = view_get_line(view, l2);
+ PangoLayout *layout1 = get_pango_layout(view, l1);
+ PangoLayout *layout2 = get_pango_layout(view, l2);
+ pango_layout_index_to_line_x(layout1, b1, 0, &sl1, &x_useless);
+ pango_layout_index_to_line_x(layout2, b2, 0, &sl2, &x_useless);
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout1, sl1);
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout2, sl2);
+ if (sl1 == 0 && sl2 == vl2->softlines - 1) {
+ if (l1 == 0 && l2 == view->lines_num - 1) {
+ rgl1 = l1;
+ rgl2 = l2;
+ rgb1 = 0;
+ rgb2 = ll2->len;
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(view->buffer, l1, 0, ll1->len);
+ ledit_buffer_delete_line_entries_base(view->buffer, l1 + 1, l2);
+ new_line = 0;
+ new_byte = 0;
+ } else {
+ if (l2 == view->lines_num - 1) {
+ new_line = l1 - 1;
+ ledit_line *new_lline = ledit_buffer_get_line(view->buffer, new_line);
+ ledit_view_line *new_vline = view_get_line(view, new_line);
+ if (new_vline->text_dirty)
+ set_pango_text_and_highlight(view, new_line);
+ ledit_x_softline_to_pos(view, new_line, x, new_vline->softlines - 1, &new_byte);
+ rgl1 = l1 - 1;
+ rgb1 = new_lline->len;
+ rgl2 = l2;
+ rgb2 = ll2->len;
+ } else {
+ new_line = l1;
+ ledit_x_softline_to_pos(
+ view, l2 + 1, x, 0, &new_byte
+ );
+ rgl1 = l1;
+ rgb1 = 0;
+ rgl2 = l2 + 1;
+ rgb2 = 0;
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_entries_base(view->buffer, l1, l2);
+ }
+ } else if (sl1 == 0) {
+ rgl1 = l1;
+ rgb1 = 0;
+ rgl2 = l2;
+ rgb2 = (size_t)(pl2->start_index + pl2->length);
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(view->buffer, l2, 0, rgb2);
+ new_line = l1;
+ ledit_x_softline_to_pos(view, l2, x, 0, &new_byte);
+ ledit_buffer_delete_line_entries_base(view->buffer, l1, l2 - 1);
+ } else if (sl2 == vl2->softlines - 1) {
+ rgl1 = l1;
+ rgb1 = (size_t)pl1->start_index;
+ rgl2 = l2;
+ rgb2 = ll2->len;
+ if (l2 + 1 == view->lines_num) {
+ new_line = l1;
+ ledit_x_softline_to_pos(view, l1, x, sl1 - 1, &new_byte);
+ } else {
+ new_line = l1 + 1;
+ ledit_x_softline_to_pos(
+ view, l2 + 1,
+ x, 0, &new_byte
+ );
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(view->buffer, l1, rgb1, ll1->len - rgb1);
+ ledit_buffer_delete_line_entries_base(view->buffer, l1 + 1, l2);
+ } else {
+ /* FIXME: this could be made nicer by just using the range to
+ delete all in one go at the end */
+ rgl1 = l1;
+ rgb1 = (size_t)pl1->start_index;
+ rgl2 = l2;
+ rgb2 = (size_t)(pl2->start_index + pl2->length);
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(view->buffer, l1, rgb1, ll1->len - rgb1);
+ ledit_buffer_insert_text_from_line_base(
+ view->buffer,
+ l1, rgb1, l2, rgb2,
+ ll2->len - rgb2, NULL
+ );
+ ledit_buffer_delete_line_entries_base(view->buffer, l1 + 1, l2);
+ new_line = l1;
+ set_pango_text_and_highlight(view, l1);
+ /* it's technically possible that the remaining part of the
+ second line is so small that it doesn't generate a new
+ softline, so there needs to be a special case - this is
+ a bit weird because the cursor will seem to stay on the
+ same line, but it now includes the rest of the second line
+ (FIXME: this is probably not the best thing to do) */
+ ledit_x_softline_to_pos(
+ view, l1, x, sl1 + 1 < vl1->softlines ? sl1 + 1 : sl1, &new_byte
+ );
+ }
+ }
+ } else {
+ if (line_index1 == line_index2) {
+ rgl1 = rgl2 = line_index1;
+ if (byte_index1 < byte_index2) {
+ rgb1 = byte_index1;
+ rgb2 = byte_index2;
+ } else {
+ rgb1 = byte_index2;
+ rgb2 = byte_index1;
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_buffer_delete_line_section_base(view->buffer, line_index1, rgb1, rgb2 - rgb1);
+ new_line = line_index1;
+ new_byte = rgb1;
+ } else {
+ if (line_index1 < line_index2) {
+ rgl1 = line_index1;
+ rgb1 = byte_index1;
+ rgl2 = line_index2;
+ rgb2 = byte_index2;
+ } else {
+ rgl1 = line_index2;
+ rgb1 = byte_index2;
+ rgl2 = line_index1;
+ rgb2 = byte_index1;
+ }
+ if (text_ret) {
+ ledit_buffer_copy_text_to_txtbuf(
+ view->buffer, text_ret,
+ rgl1, rgb1,
+ rgl2, rgb2
+ );
+ }
+ ledit_line *line1 = ledit_buffer_get_line(view->buffer, rgl1);
+ ledit_line *line2 = ledit_buffer_get_line(view->buffer, rgl2);
+ ledit_buffer_delete_line_section_base(view->buffer, rgl1, rgb1, line1->len - rgb1);
+ ledit_buffer_insert_text_from_line_base(
+ view->buffer, rgl1, rgb1, rgl2, rgb2, line2->len - rgb2, NULL
+ );
+ new_line = rgl1;
+ new_byte = rgb1;
+ ledit_buffer_delete_line_entries_base(view->buffer, rgl1 + 1, rgl2);
+ }
+ /* FIXME: too much magic - maybe don't include this here */
+ if (view->mode == NORMAL)
+ new_byte = view_get_legal_normal_pos(view, new_line, new_byte);
+ }
+ if (final_range_ret) {
+ final_range_ret->line1 = rgl1;
+ final_range_ret->byte1 = rgb1;
+ final_range_ret->line2 = rgl2;
+ final_range_ret->byte2 = rgb2;
+ }
+ if (new_line_ret)
+ *new_line_ret = new_line;
+ if (new_byte_ret)
+ *new_byte_ret = new_byte;
+}
+
+/* FIXME: any way to make this more efficient? */
+void
+view_resize_textview(void *data) {
+ ledit_view *view = (ledit_view *)data;
+ view->total_height = 0;
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ for (size_t i = 0; i < view->lines_num; i++) {
+ ledit_view_line *line = view_get_line(view, i);
+ line->w = text_w;
+ line->text_dirty = 1; /* it's a bit weird to set this, it should rather be something like 'w_dirty' */
+ set_pango_text_and_highlight(view, i);
+ line->y_offset = view->total_height;
+ line->dirty = 1;
+ line->h_dirty = 0;
+ view->total_height += line->h;
+ }
+ ledit_window_set_scroll_max(view->window, view->total_height);
+ if (view->display_offset > 0 &&
+ view->display_offset + text_h > view->total_height) {
+ view_scroll(view, view->total_height - text_h);
+ }
+}
+
+void
+view_scroll(ledit_view *view, long new_offset) {
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ if (new_offset + text_h > view->total_height)
+ new_offset = view->total_height - text_h;
+ if (new_offset < 0)
+ new_offset = 0;
+ view->display_offset = new_offset;
+ ledit_window_set_scroll_pos(view->window, view->display_offset);
+}
+
+/* FIXME: there's gotta be a better/more efficient way to do this... */
+/* FIXME: make sure h_dirty is not set here */
+void
+view_get_nearest_legal_pos(
+ ledit_view *view,
+ size_t line, size_t byte,
+ /*int snap_to_nearest, int snap_middle, FIXME: take these parameters */
+ size_t *line_ret, size_t *byte_ret) {
+ PangoRectangle strong, weak;
+ int text_w, text_h;
+ int x, sl_useless;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ ledit_view_line *vline = view_get_line(view, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ if (byte > INT_MAX)
+ err_overflow();
+ pango_layout_get_cursor_pos(layout, (int)byte, &strong, &weak);
+ ledit_pos_to_x_softline(view, line, byte, &x, &sl_useless);
+ long cursor_y = strong.y / PANGO_SCALE + vline->y_offset;
+ PangoRectangle ink, log;
+ if (cursor_y < view->display_offset) {
+ /* search for the hard line covering the top of the screen */
+ size_t hline = line;
+ while (vline->y_offset + vline->h <= view->display_offset && hline < view->lines_num - 1) {
+ vline = view_get_line(view, ++hline);
+ }
+ /* the current hard line is now the one at the very top of the screen*/
+ layout = get_pango_layout(view, hline);
+ int num_sl = vline->softlines;
+ int cur_y_off = 0;
+ int sl_index = -1;
+ PangoLayoutLine *sl;
+ /* search for first soft line completely on-screen */
+ for (int i = 0; i < num_sl; i++) {
+ sl = pango_layout_get_line_readonly(layout, i);
+ if (cur_y_off + vline->y_offset >= view->display_offset) {
+ sl_index = i;
+ break;
+ }
+ pango_layout_line_get_pixel_extents(sl, &ink, &log);
+ cur_y_off += log.height;
+ }
+ if (sl_index >= 0) {
+ /* we found the correct soft line */
+ *line_ret = hline;
+ ledit_x_softline_to_pos(view, hline, x, sl_index, byte_ret);
+ } else if (hline < view->lines_num - 1) {
+ /* need to move to next hard line */
+ *line_ret = hline + 1;
+ ledit_x_softline_to_pos(view, hline + 1, x, 0, byte_ret);
+ } else {
+ /* no idea if this can happen, but just fail and use
+ the last soft line of the last hard line */
+ *line_ret = hline;
+ ledit_x_softline_to_pos(view, hline, x, num_sl - 1, byte_ret);
+ }
+ } else if (cursor_y + strong.height / PANGO_SCALE >
+ view->display_offset + text_h) {
+ /* search for the hard line covering the bottom of the screen */
+ size_t hline = line;
+ while (vline->y_offset > view->display_offset + text_h && hline > 0) {
+ vline = view_get_line(view, --hline);
+ }
+ /* the current hard line is now the one at the very bottom of the screen*/
+ layout = get_pango_layout(view, hline);
+ int num_sl = vline->softlines;
+ int cur_y_off = 0;
+ int sl_index = -1;
+ PangoLayoutLine *sl;
+ /* search for last soft line completely on-screen */
+ for (int i = num_sl - 1; i >= 0; i--) {
+ sl = pango_layout_get_line_readonly(layout, i);
+ if (vline->y_offset + vline->h - cur_y_off < view->display_offset + text_h) {
+ sl_index = i;
+ break;
+ }
+ pango_layout_line_get_pixel_extents(sl, &ink, &log);
+ cur_y_off += log.height;
+ }
+ if (sl_index >= 0) {
+ /* we found the correct soft line */
+ *line_ret = hline;
+ ledit_x_softline_to_pos(view, hline, x, sl_index, byte_ret);
+ } else if (hline > 0) {
+ /* need to move to previous hard line */
+ *line_ret = hline - 1;
+ vline = view_get_line(view, hline - 1);
+ num_sl = vline->softlines;
+ ledit_x_softline_to_pos(view, hline - 1, x, num_sl - 1, byte_ret);
+ } else {
+ /* no idea if this can happen, but just fail and use
+ the first soft line of the first hard line */
+ *line_ret = hline;
+ ledit_x_softline_to_pos(view, hline, x, 0, byte_ret);
+ }
+ }
+}
+
+void
+ledit_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest, size_t *line_ret, size_t *byte_ret) {
+ /* FIXME: store current line offset to speed this up */
+ /* FIXME: use y_offset in lines */
+ long h = 0;
+ long pos = view->display_offset + y;
+ for (size_t i = 0; i < view->lines_num; i++) {
+ ledit_view_line *vline = view_get_line(view, i);
+ if ((h <= pos && h + vline->h > pos) || i == view->lines_num - 1) {
+ int index, trailing;
+ PangoLayout *layout = get_pango_layout(view, i);
+ /* FIXME: what if i == view->lines_num - 1 but pos - h < 0? */
+ pango_layout_xy_to_index(
+ layout,
+ x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
+ &index, &trailing
+ );
+ *byte_ret = (size_t)index;
+ if (snap_to_nearest) {
+ ledit_line *ll = ledit_buffer_get_line(view->buffer, i);
+ while (trailing > 0) {
+ trailing--;
+ *byte_ret = ledit_line_next_utf8(ll, *byte_ret);
+ }
+ }
+ *line_ret = i;
+ break;
+ }
+ h += vline->h;
+ }
+}
+
+static void
+scroll_to_pos(ledit_view *view, size_t line, size_t byte, int top) {
+ PangoRectangle strong, weak;
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ ledit_view_line *vl = view_get_line(view, line);
+ PangoLayout *layout = get_pango_layout(view, line);
+ if (byte > INT_MAX)
+ err_overflow();
+ pango_layout_get_cursor_pos(layout, (int)byte, &strong, &weak);
+ long cursor_y = strong.y / PANGO_SCALE + vl->y_offset;
+ if (top) {
+ view_scroll(view, cursor_y);
+ } else {
+ view_scroll(view, cursor_y - text_h + strong.height / PANGO_SCALE);
+ }
+}
+
+void
+view_scroll_to_pos_top(ledit_view *view, size_t line, size_t byte) {
+ scroll_to_pos(view, line, byte, 1);
+}
+
+void
+view_scroll_to_pos_bottom(ledit_view *view, size_t line, size_t byte) {
+ scroll_to_pos(view, line, byte, 0);
+}
+
+void
+view_ensure_cursor_shown(ledit_view *view) {
+ PangoRectangle strong, weak;
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ ledit_view_line *vline = view_get_line(view, view->cur_line);
+ PangoLayout *layout = get_pango_layout(view, view->cur_line);
+ if (view->cur_index > INT_MAX)
+ err_overflow();
+ pango_layout_get_cursor_pos(
+ layout, (int)view->cur_index, &strong, &weak
+ );
+ long cursor_y = strong.y / PANGO_SCALE + vline->y_offset;
+ if (cursor_y < view->display_offset) {
+ view_scroll(view, cursor_y);
+ } else if (cursor_y + strong.height / PANGO_SCALE >
+ view->display_offset + text_h) {
+ view_scroll(view, cursor_y - text_h + strong.height / PANGO_SCALE);
+ }
+}
+
+static void
+swap_sz(size_t *a, size_t *b) {
+ size_t tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/* FIXME: this is generic, so it doesn't need to be in view.c */
+void
+view_sort_selection(size_t *line1, size_t *byte1, size_t *line2, size_t *byte2) {
+ if (*line1 > *line2) {
+ swap_sz(line1, line2);
+ swap_sz(byte1, byte2);
+ } else if (*line1 == *line2 && *byte1 > *byte2) {
+ swap_sz(byte1, byte2);
+ }
+}
+
+/* FIXME: don't reset selection when selection is clicked away */
+/* FIXME: when selecting with mouse, only call this when button is released */
+/* lines and bytes need to be sorted already! */
+static void
+copy_selection_to_x_primary(ledit_view *view, size_t line1, size_t byte1, size_t line2, size_t byte2) {
+ /* FIXME: let window handle this */
+ txtbuf *primary = ledit_window_get_primary_clipboard_buffer();
+ ledit_buffer_copy_text_to_txtbuf(view->buffer, primary, line1, byte1, line2, byte2);
+ XSetSelectionOwner(view->buffer->common->dpy, XA_PRIMARY, view->window->xwin, CurrentTime);
+ /*
+ FIXME
+ if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win)
+ selclear();
+ */
+}
+
+void
+view_wipe_selection(ledit_view *view) {
+ if (view->sel_valid) {
+ if (view->sel.line1 > view->sel.line2)
+ swap_sz(&view->sel.line1, &view->sel.line2);
+ for (size_t i = view->sel.line1; i <= view->sel.line2; i++) {
+ view_wipe_line_cursor_attrs(view, i);
+ }
+ }
+ view->sel_valid = 0;
+ view->sel.line1 = view->sel.line2 = 0;
+ view->sel.byte1 = view->sel.byte2 = 0;
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
+}
+
+void
+view_set_selection(ledit_view *view, size_t line1, size_t byte1, size_t line2, size_t byte2) {
+ if (view->sel_valid &&
+ line1 == view->sel.line1 && line2 == view->sel.line2 &&
+ byte1 == view->sel.byte1 && byte2 == view->sel.byte2) {
+ return;
+ }
+ size_t l1_new = line1, l2_new = line2;
+ size_t b1_new = byte1, b2_new = byte2;
+ view_sort_selection(&l1_new, &b1_new, &l2_new, &b2_new);
+ view_sort_selection(&view->sel.line1, &view->sel.byte1, &view->sel.line2, &view->sel.byte2);
+ /* FIXME: make this a bit nicer and optimize it */
+ if (view->sel.line1 > l2_new || view->sel.line2 < l1_new) {
+ for (size_t i = view->sel.line1; i <= view->sel.line2; i++) {
+ view_wipe_line_cursor_attrs(view, i);
+ }
+ } else {
+ for (size_t i = view->sel.line1; i < l1_new; i++) {
+ view_wipe_line_cursor_attrs(view, i);
+ }
+ for (size_t i = view->sel.line2; i > l2_new; i--) {
+ view_wipe_line_cursor_attrs(view, i);
+ }
+ }
+ for (size_t i = l1_new; i <= l2_new; i++) {
+ /* only change the ones that were not already selected */
+ if (i <= view->sel.line1 || i >= view->sel.line2) {
+ ledit_view_line *vl = view_get_line(view, i);
+ vl->highlight_dirty = 1;
+ }
+ }
+ if (l1_new != l2_new || b1_new != b2_new)
+ copy_selection_to_x_primary(view, l1_new, b1_new, l2_new, b2_new);
+ view->sel.line1 = line1;
+ view->sel.byte1 = byte1;
+ view->sel.line2 = line2;
+ view->sel.byte2 = byte2;
+ view->sel_valid = 1;
+}
+
+void
+view_scroll_handler(void *view, long pos) {
+ ((ledit_view *)view)->display_offset = pos;
+}
+
+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 snap;
+ switch (event->type) {
+ case ButtonPress:
+ snap = view->mode == NORMAL ? 0 : 1;
+ ledit_xy_to_line_byte(view, x, y, snap, &l, &b);
+ view->selecting = 1;
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ view->cur_line = l;
+ view->cur_index = b;
+ /* don't set selection yet because the mouse may not be
+ dragged, so we don't want to switch to visual (this
+ allows setting just the cursor position in normal mode
+ without always switching to visual) */
+ if (view->mode == NORMAL)
+ view_set_line_cursor_attrs(view, l, b);
+ else if (view->mode == VISUAL)
+ view_set_selection(view, l, b, l, b);
+ break;
+ case ButtonRelease:
+ view->selecting = 0;
+ break;
+ case MotionNotify:
+ if (view->selecting) {
+ y = y >= 0 ? y : 0;
+ ledit_xy_to_line_byte(view, x, y, 1, &l, &b);
+ if (view->mode == NORMAL) {
+ view_wipe_line_cursor_attrs(view, view->cur_line);
+ /* FIXME: return to old mode afterwards? */
+ /* should change_mode_group even be called here? */
+ view_set_mode(view, VISUAL);
+ }
+ if (!view->sel_valid) {
+ /* the selection has just started, so the current
+ position is already set to the beginning of the
+ selection (see case ButtonPress above) */
+ view_set_selection(
+ view,
+ view->cur_line, view->cur_index, l, b
+ );
+ } else {
+ view_set_selection(
+ view,
+ view->sel.line1, view->sel.byte1, l, b
+ );
+ }
+ view->cur_line = l;
+ view->cur_index = b;
+ }
+ break;
+ }
+}
+
+static void
+view_redraw_text(ledit_view *view) {
+ int h = 0;
+ int cur_line_y = 0;
+ int cursor_displayed = 0;
+ int text_w, text_h;
+ ledit_window_get_textview_size(view->window, &text_w, &text_h);
+ for (size_t i = 0; i < view->lines_num; i++) {
+ ledit_view_line *vline = view_get_line(view, i);
+ if (h + vline->h > view->display_offset) {
+ /* FIXME: vline->text_dirty should not happen here */
+ if (vline->text_dirty || vline->highlight_dirty)
+ set_pango_text_and_highlight(view, i);
+ if (vline->dirty || !vline->cache_pixmap_valid) {
+ view_render_line(view, i);
+ }
+ int final_y = 0;
+ int dest_y = h - view->display_offset;
+ int final_h = vline->h;
+ if (h < view->display_offset) {
+ dest_y = 0;
+ final_y = view->display_offset - h;
+ final_h -= view->display_offset - h;
+ }
+ if (dest_y + final_h > text_h) {
+ final_h -= final_y + final_h -
+ view->display_offset - text_h;
+ }
+ cache_pixmap *pix = cache_get_pixmap(
+ view->cache, vline->cache_pixmap_index
+ );
+ XCopyArea(
+ view->buffer->common->dpy, pix->pixmap,
+ view->window->drawable, view->window->gc,
+ 0, final_y, vline->w, final_h, 0, dest_y
+ );
+ if (i == view->cur_line) {
+ cur_line_y = h - view->display_offset;
+ cursor_displayed = 1;
+ }
+ if (h + vline->h >= view->display_offset + text_h)
+ break;
+ }
+ h += vline->h;
+ }
+
+ XSetForeground(view->buffer->common->dpy, view->window->gc, view->theme->text_fg.pixel);
+ PangoRectangle strong, weak;
+ ledit_line *cur_line = ledit_buffer_get_line(view->buffer, view->cur_line);
+ PangoLayout *layout = get_pango_layout(view, view->cur_line);
+ pango_layout_get_cursor_pos(
+ layout, (int)view->cur_index, &strong, &weak
+ );
+ /* FIXME: long, int, etc. */
+ int cursor_y = strong.y / PANGO_SCALE + cur_line_y;
+ if (cursor_displayed && cursor_y >= 0) {
+ if (view->mode == NORMAL) {
+ /* FIXME: figure out if there's a better way to do this */
+ /* Seriously, which of the pango folks though it would be a good idea to
+ not highlight spaces at the end of soft lines? That is an utterly
+ horrible idea. Or am I just too stupid to use it properly? */
+ /* FIXME: properly document what is happening here */
+
+ int box_x = strong.x / PANGO_SCALE;
+ int box_w = 10;
+ /* determine where the box should be drawn */
+ PangoDirection dir = PANGO_DIRECTION_LTR;
+ size_t tmp_index = view->cur_index;
+ if (view->cur_index >= cur_line->len && cur_line->len > 0)
+ tmp_index = cur_line->len - 1;
+ else if (view->cur_index >= cur_line->len)
+ tmp_index = 0;
+ dir = ledit_pango_layout_get_direction(layout, (int)tmp_index);
+
+ int x, sli;
+ pango_layout_index_to_line_x(layout, (int)view->cur_index, 0, &sli, &x);
+ PangoLayoutLine *sl = pango_layout_get_line_readonly(layout, sli);
+ if (dir != sl->resolved_dir) {
+ box_w = 3;
+ }
+ if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_RTL) {
+ box_x = box_x - box_w;
+ }
+
+ if (view->cur_index == cur_line->len ||
+ (cur_line->text[view->cur_index] == ' ' &&
+ view->cur_index == (size_t)(sl->start_index + sl->length - 1))) {
+ XFillRectangle(
+ view->buffer->common->dpy, view->window->drawable, view->window->gc,
+ box_x, cursor_y,
+ box_w, strong.height / PANGO_SCALE
+ );
+ }
+ } else if (view->mode == INSERT || view->mode == VISUAL) {
+ XDrawLine(
+ view->buffer->common->dpy, view->window->drawable, view->window->gc,
+ strong.x / PANGO_SCALE, cursor_y,
+ strong.x / PANGO_SCALE,
+ (strong.y + strong.height) / PANGO_SCALE + cur_line_y
+ );
+ }
+ }
+ /* move input method position */
+ if (!ledit_window_bottom_bar_text_shown(view->window)) {
+ xximspot(
+ view->window,
+ strong.x / PANGO_SCALE,
+ (strong.y + strong.height) / PANGO_SCALE + cur_line_y
+ );
+ }
+ view->redraw = 0;
+}
+
+void
+view_redraw(ledit_view *view) {
+ if (view->redraw || view->window->redraw) {
+ ledit_window_clear(view->window);
+ view_redraw_text(view);
+ ledit_window_redraw(view->window);
+ }
+}
+
+static void
+undo_insert_helper(void *data, size_t line, size_t byte, char *text, size_t text_len) {
+ ledit_view *view = (ledit_view *)data;
+ ledit_buffer_insert_text_with_newlines_base(view->buffer, line, byte, text, text_len, NULL, NULL);
+}
+
+static void
+undo_delete_helper(void *data, size_t line1, size_t byte1, size_t line2, size_t byte2) {
+ ledit_view *view = (ledit_view *)data;
+ view_delete_range_base(view, DELETE_CHAR, line1, byte1, line2, byte2, NULL, NULL, NULL, NULL);
+}
+
+void
+view_undo(ledit_view *view) {
+ size_t min_line;
+ size_t old_line = view->cur_line;
+ ledit_undo(
+ view->buffer->undo, view->mode, view, &undo_insert_helper,
+ &undo_delete_helper, &view->cur_line, &view->cur_index, &min_line
+ );
+ if (view->mode == NORMAL) {
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
+ );
+ }
+ view_wipe_line_cursor_attrs(view, old_line);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
+ if (min_line < view->lines_num) {
+ ledit_buffer_recalc_all_views_from_line(
+ view->buffer, min_line > 0 ? min_line - 1 : min_line
+ );
+ }
+ /* FIXME: show undo message */
+}
+
+void
+view_redo(ledit_view *view) {
+ size_t min_line;
+ size_t old_line = view->cur_line;
+ ledit_redo(
+ view->buffer->undo, view->mode, view, &undo_insert_helper,
+ &undo_delete_helper, &view->cur_line, &view->cur_index, &min_line
+ );
+ if (view->mode == NORMAL) {
+ view->cur_index = view_get_legal_normal_pos(
+ view, view->cur_line, view->cur_index
+ );
+ }
+ view_wipe_line_cursor_attrs(view, old_line);
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
+ if (min_line < view->lines_num) {
+ ledit_buffer_recalc_all_views_from_line(
+ view->buffer, min_line > 0 ? min_line - 1 : min_line
+ );
+ }
+ /* FIXME: show undo message */
+}
+
+static void
+paste_callback(void *data, char *text, size_t len) {
+ ledit_view *view = (ledit_view *)data;
+ txtbuf ins_buf = {.text = text, .len = len, .cap = len};
+ ledit_range cur_range, ins_range;
+ cur_range.line1 = ins_range.line1 = view->cur_line;
+ cur_range.byte1 = ins_range.byte1 = view->cur_index;
+ ledit_buffer_insert_text_with_newlines(
+ view->buffer, view->cur_line, view->cur_index,
+ text, len, &view->cur_line, &view->cur_index
+ );
+ cur_range.line2 = ins_range.line2 = view->cur_line;
+ cur_range.byte2 = ins_range.byte2 = view->cur_index;
+ ledit_push_undo_insert(
+ view->buffer->undo, &ins_buf, ins_range, cur_range, 1, view->mode
+ );
+}
+
+/* FIXME: guard against buffer being destroyed before paste callback is nulled */
+
+void
+view_paste_clipboard(ledit_view *view) {
+ ledit_window_set_paste_callback(view->window, &paste_callback, view);
+ clipboard_paste_clipboard(view->window);
+}
+
+void
+view_paste_primary(ledit_view *view) {
+ ledit_window_set_paste_callback(view->window, &paste_callback, view);
+ clipboard_paste_primary(view->window);
+}
diff --git a/view.h b/view.h
@@ -0,0 +1,158 @@
+#ifndef _LEDIT_VIEW_H_
+#define _LEDIT_VIEW_H_
+
+typedef struct ledit_view ledit_view;
+
+#include "buffer.h"
+
+enum action_type {
+ ACTION_NONE, /* pass next key to basic key handler */
+ ACTION_GRABKEY /* pass next key to given callback */
+};
+
+struct action {
+ enum action_type type;
+ struct action (*callback)(ledit_view *view, XEvent *event, int lang_index);
+};
+
+typedef struct {
+ ledit_view *view; /* parent view */
+ int w; /* width in pixels */
+ int h; /* height in pixels */
+ long y_offset; /* pixel offset starting at the top of the file */
+ size_t cache_pixmap_index; /* index of pixmap in cache, or -1 if not assigned */
+ size_t cache_layout_index; /* index of pango layout in cache, or -1 if not assigned */
+ int softlines; /* number of softlines - cached from PangoLayout */
+ size_t cursor_index; /* cursor index (for highlight in normal mode) */
+ char cursor_index_valid; /* whether cursor index is valid */
+ char cache_pixmap_valid; /* whether cache_pixmap_index is valid */
+ char cache_layout_valid; /* whether cache_layout_index is valid */
+ char dirty; /* whether line needs to be rendered before being drawn */
+ char text_dirty; /* whether the text in the PangoLayout needs to be
+ * updated before the layout is rendered */
+ char highlight_dirty; /* whether highlight (cursor or selection) needs to be
+ * updated still in the PangoLayout before rendering */
+ char h_dirty; /* whether height needs to be recalculated */
+} ledit_view_line;
+
+/* FIXME: It's kind of ugly to put this here instead of keys_command.h,
+ but it has to be per-view, so I don't know any other option. */
+enum ledit_command_type {
+ CMD_EDIT,
+ CMD_EDITSEARCH,
+ CMD_EDITSEARCHB,
+ CMD_SEARCH,
+ CMD_SEARCHB,
+ CMD_SUBSTITUTE
+};
+
+struct ledit_view {
+ ledit_buffer *buffer; /* parent buffer */
+ ledit_window *window; /* window showing this view */
+ ledit_theme *theme; /* current theme in use */
+ ledit_cache *cache; /* cache for pixmaps and pango layouts */
+ ledit_view_line *lines; /* array of lines, stored as gap buffer */
+ /* current command type */
+ enum ledit_command_type cur_command_type;
+ struct action cur_action; /* current action to execute on key press */
+ size_t lines_cap; /* size of lines array */
+ size_t lines_gap; /* position of gap for line gap buffer */
+ size_t lines_num; /* number of lines */
+ size_t cur_line; /* current line */
+ size_t cur_index; /* current byte index in line */
+ long total_height; /* total pixel height of all lines */
+ long display_offset; /* current pixel offset of viewport */
+ ledit_range sel; /* current selection */
+ enum ledit_mode mode; /* current mode of this view */
+ char selecting; /* whether user is currently selecting text with mouse */
+ char sel_valid; /* whether there is currently a valid selection */
+ char redraw; /* whether something has changed so the view needs to be redrawn */
+};
+
+enum delete_mode {
+ DELETE_CHAR,
+ DELETE_SOFTLINE,
+ DELETE_HARDLINE
+};
+
+void view_set_mode(ledit_view *view, enum ledit_mode mode);
+ledit_view *view_create(ledit_buffer *buffer, ledit_theme *theme, enum ledit_mode mode, size_t line, size_t pos);
+ledit_view_line *view_get_line(ledit_view *view, size_t index);
+void view_notify_insert_text(ledit_view *view, size_t line, size_t index, size_t len);
+void 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);
+void view_notify_delete_lines(ledit_view *view, size_t index1, size_t index2);
+void view_destroy(ledit_view *view);
+void view_cleanup(void);
+void view_set_line_cursor_attrs(ledit_view *view, size_t line, size_t index);
+void view_wipe_line_cursor_attrs(ledit_view *view, size_t line);
+void view_render_line(ledit_view *view, size_t line_index);
+void view_recalc_line(ledit_view *view, size_t line);
+void view_recalc_from_line(ledit_view *view, size_t line);
+void view_recalc_all_lines(ledit_view *view);
+int view_line_visible(ledit_view *view, size_t index);
+size_t view_next_cursor_pos(ledit_view *view, size_t line, size_t byte, int num);
+size_t view_prev_cursor_pos(ledit_view *view, size_t line, size_t byte, int num);
+void view_next_word(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+void view_next_word_end(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+void view_next_bigword(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+void view_next_bigword_end(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+void view_prev_word(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+void view_prev_bigword(ledit_view *view, size_t line, size_t byte, int num_repeat, size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret);
+size_t view_line_next_non_whitespace(ledit_view *view, size_t line, size_t byte);
+void view_get_pos_softline_bounds(
+ ledit_view *view, size_t line, size_t pos,
+ size_t *start_byte_ret, size_t *end_byte_ret
+);
+void view_get_softline_bounds(
+ ledit_view *view, size_t line, int softline,
+ size_t *start_byte_ret, size_t *end_byte_ret
+);
+int view_get_softline_count(ledit_view *view, size_t line);
+int view_pos_to_softline(ledit_view *view, size_t line, size_t pos);
+void view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int *x_ret, int *y_ret, int *h_ret);
+size_t view_move_cursor_visually(ledit_view *view, size_t line, size_t pos, int movement, size_t *prev_index_ret);
+void ledit_pos_to_x_softline(ledit_view *view, size_t line, size_t pos, int *x_ret, int *softline_ret);
+void ledit_x_softline_to_pos(ledit_view *view, size_t line, int x, int softline, size_t *pos_ret);
+size_t view_get_legal_normal_pos(ledit_view *view, size_t line, size_t pos);
+void view_delete_range(
+ ledit_view *view, enum delete_mode delmode,
+ size_t line_index1, size_t byte_index1,
+ size_t line_index2, size_t byte_index2,
+ size_t *new_line_ret, size_t *new_byte_ret,
+ ledit_range *final_range_ret, txtbuf *text_ret
+);
+void view_delete_range_base(
+ ledit_view *view, enum delete_mode delmode,
+ size_t line_index1, size_t byte_index1,
+ size_t line_index2, size_t byte_index2,
+ size_t *new_line_ret, size_t *new_byte_ret,
+ ledit_range *final_range_ret, txtbuf *text_ret
+);
+void view_resize_textview(void *data);
+void view_scroll(ledit_view *view, long new_offset);
+void view_get_nearest_legal_pos(
+ ledit_view *view,
+ size_t line, size_t byte,
+ /*int snap_to_nearest, int snap_middle, FIXME: take these parameters */
+ size_t *line_ret, size_t *byte_ret
+);
+/* x and y are in pixels, if snap_to_nearest is nonzero, the returned byte
+ is at the nearest grapheme boundary, if it is zero, the byte is always
+ the beginning of the grapheme under the position */
+void ledit_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest, size_t *line_ret, size_t *byte_ret);
+void view_scroll_to_pos_top(ledit_view *view, size_t line, size_t byte);
+void view_scroll_to_pos_bottom(ledit_view *view, size_t line, size_t byte);
+void view_ensure_cursor_shown(ledit_view *view);
+void view_sort_selection(size_t *line1, size_t *byte1, size_t *line2, size_t *byte2);
+void view_wipe_selection(ledit_view *view);
+void view_set_selection(ledit_view *view, size_t line1, size_t byte1, size_t line2, size_t byte2);
+void view_scroll_handler(void *view, long pos);
+void view_button_handler(void *data, XEvent *event);
+void view_redraw(ledit_view *view);
+void view_undo(ledit_view *view);
+void view_redo(ledit_view *view);
+void view_paste_clipboard(ledit_view *view);
+void view_paste_primary(ledit_view *view);
+
+#endif
diff --git a/window.c b/window.c
@@ -1,3 +1,5 @@
+/* FIXME: check if xim handling still works with multiple windows */
+#include <time.h>
#include <math.h>
#include <stdio.h>
#include <assert.h>
@@ -17,6 +19,8 @@
#include "theme.h"
#include "window.h"
#include "util.h"
+#include "macros.h"
+#include "config.h"
/* FIXME: Everything to do with the bottom bar is extremely hacky */
struct bottom_bar {
@@ -101,6 +105,7 @@ ledit_window_insert_bottom_bar_text(ledit_window *window, char *text, int len) {
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;
}
void
@@ -126,6 +131,7 @@ ledit_window_move_bottom_bar_cursor(ledit_window *window, int movement) {
if (new_index > window->bb->line_len)
new_index = window->bb->line_len;
window->bb->line_cur_pos = new_index;
+ window->redraw = 1;
}
void
@@ -136,11 +142,13 @@ ledit_window_set_bottom_bar_min_pos(ledit_window *window, int pos) {
void
ledit_window_bottom_bar_cursor_to_beginning(ledit_window *window) {
window->bb->line_cur_pos = window->bb->min_pos;
+ window->redraw = 1;
}
void
ledit_window_bottom_bar_cursor_to_end(ledit_window *window) {
window->bb->line_cur_pos = window->bb->line_len;
+ window->redraw = 1;
}
/* FIXME: respect PangoLogAttr.backspace_deletes_character */
@@ -185,12 +193,14 @@ ledit_window_delete_bottom_bar_char(ledit_window *window, int dir) {
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;
}
void
ledit_window_set_bottom_bar_cursor(ledit_window *window, int byte_pos) {
/* FIXME: check if valid? */
window->bb->line_cur_pos = byte_pos;
+ window->redraw = 1;
}
int
@@ -201,11 +211,13 @@ ledit_window_get_bottom_bar_cursor(ledit_window *window) {
void
ledit_window_set_bottom_bar_text_shown(ledit_window *window, int shown) {
window->bottom_text_shown = shown;
+ window->redraw = 1;
}
int
ledit_window_bottom_bar_text_shown(ledit_window *window) {
return window->bottom_text_shown;
+ window->redraw = 1;
}
void
@@ -213,6 +225,7 @@ ledit_window_set_bottom_bar_text(ledit_window *window, char *text, int len) {
window->bb->line_len = 0;
window->bb->line_cur_pos = 0;
ledit_window_insert_bottom_bar_text(window, text, len);
+ window->redraw = 1;
}
char *
@@ -226,6 +239,7 @@ ledit_window_show_message(ledit_window *window, char *text, int len) {
/* FIXME: rename these */
window->bottom_text_shown = 0;
window->message_shown = 1;
+ window->redraw = 1;
}
int
@@ -236,10 +250,12 @@ ledit_window_message_shown(ledit_window *window) {
void
ledit_window_hide_message(ledit_window *window) {
window->message_shown = 0;
+ window->redraw = 1;
}
void
ledit_window_set_mode(ledit_window *window, enum ledit_mode mode) {
+ window->mode = mode;
char *text;
switch (mode) {
case NORMAL:
@@ -263,12 +279,14 @@ ledit_window_set_mode(ledit_window *window, enum ledit_mode mode) {
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);
recalc_text_size(window);
+ window->redraw = 1;
}
void
ledit_window_set_mode_extra_text(ledit_window *window, char *text) {
window->mode_extra_text = ledit_strdup(text);
- ledit_window_set_mode(window, window->common->mode);
+ ledit_window_set_mode(window, window->mode);
+ window->redraw = 1;
}
/* FIXME: give these functions more sensible names */
@@ -289,21 +307,19 @@ set_scroll_pos(ledit_window *window, double pos) {
/* FIXME: check for overflow */
if (window->scroll_callback)
window->scroll_callback(window->scroll_cb_data, (long)window->scroll_offset);
+ window->redraw = 1;
}
void
ledit_window_set_scroll_max(ledit_window *window, long max) {
window->scroll_max = max;
- /* FIXME: set offset if too large */
- /* actually not, because buffer does that already */
- /* FIXME: SHOULD THIS EVEN BE USED? */
- window->common->redraw = 1;
+ window->redraw = 1;
}
void
ledit_window_set_scroll_pos(ledit_window *window, long pos) {
window->scroll_offset = pos;
- window->common->redraw = 1;
+ window->redraw = 1;
}
void
@@ -313,7 +329,7 @@ ledit_window_set_scroll_callback(ledit_window *window, void (*cb)(void *, long),
}
void
-ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *, int), void *data) {
+ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *, size_t), void *data) {
window->paste_callback = cb;
window->paste_cb_data = data;
}
@@ -324,6 +340,12 @@ ledit_window_set_button_callback(ledit_window *window, void (*cb)(void *, XEvent
window->button_cb_data = data;
}
+void
+ledit_window_set_resize_callback(ledit_window *window, void (*cb)(void *), void *data) {
+ window->resize_callback = cb;
+ window->resize_cb_data = data;
+}
+
/* FIXME: Change naming convention to fit the rest of ledit */
/* FIXME: It's a bit weird when an input box pops up during normal mode.
Can/should this be disabled? */
@@ -411,17 +433,26 @@ xximspot(ledit_window *window, int x, int y) {
}
ledit_window *
-ledit_window_create(ledit_common *common, ledit_theme *theme) {
+ledit_window_create(ledit_common *common, ledit_theme *theme, enum ledit_mode mode) {
XSetWindowAttributes attrs;
XGCValues gcv;
ledit_window *window = ledit_malloc(sizeof(ledit_window));
+ window->mode = mode;
window->scroll_dragging = 0;
window->scroll_grab_handle = 0;
window->w = 500;
window->h = 500;
window->mode_extra_text = NULL;
+ window->paste_callback = NULL;
+ window->scroll_callback = NULL;
+ window->button_callback = NULL;
+ window->resize_callback = NULL;
+ window->paste_cb_data = NULL;
+ window->scroll_cb_data = NULL;
+ window->button_cb_data = NULL;
+ window->resize_cb_data = NULL;
memset(&window->wattrs, 0, sizeof(attrs));
window->wattrs.background_pixel = BlackPixel(common->dpy, common->screen);
@@ -507,26 +538,41 @@ ledit_window_create(ledit_common *common, ledit_theme *theme) {
/* FIXME: maybe delay this (i.e. move to different function)? */
XMapWindow(common->dpy, window->xwin);
+ window->redraw = 1;
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ window->last_scroll = now;
+ window->last_motion = now;
+ window->last_resize = now;
+ window->last_scroll_valid = 0;
+ window->last_motion_valid = 0;
+ window->last_resize_valid = 0;
+ window->scroll_num = 0;
+ recalc_text_size(window);
+
return window;
}
void
ledit_window_destroy(ledit_window *window) {
- /* FIXME: cleanup everything else */
- /* FIXME: This doesn't seem to be correct - g_object_unref sometimes
- causes segfault */
+ /* FIXME: check what's still missing */
+ g_object_unref(window->bb->mode);
+ /*g_object_unref(window->bb->ruler);*/ /* FIXME: implement ruler */
+ g_object_unref(window->bb->line);
+ ledit_draw_destroy(window, window->bb->mode_draw);
+ ledit_draw_destroy(window, window->bb->line_draw);
+
pango_font_description_free(window->font);
+ /* FIXME: The pango documentation says that the context must be freed,
+ but the program segfaults when that is done. */
+ /*g_object_unref(window->context);*/
g_object_unref(window->fontmap);
- g_object_unref(window->context);
+
/* FIXME: is gc, etc. destroyed automatically when destroying window? */
if (window->spotlist)
XFree(window->spotlist);
XDestroyWindow(window->common->dpy, window->xwin);
- g_object_unref(window->bb->mode);
- /*g_object_unref(window->bb->ruler);*/ /* FIXME: implement ruler */
- g_object_unref(window->bb->line);
- ledit_draw_destroy(window, window->bb->mode_draw);
- ledit_draw_destroy(window, window->bb->line_draw);
+
if (window->mode_extra_text)
free(window->mode_extra_text);
free(window->bb->line_text);
@@ -630,6 +676,7 @@ ledit_window_redraw(ledit_window *window) {
if (!XdbeSwapBuffers(window->common->dpy, &swap_info, 1))
exit(1);
XFlush(window->common->dpy);
+ window->redraw = 0;
}
void
@@ -639,11 +686,50 @@ ledit_window_get_textview_size(ledit_window *window, int *w_ret, int *h_ret) {
}
void
+ledit_window_handle_filtered_events(ledit_window *window) {
+ struct timespec now, elapsed;
+ if (window->last_motion_valid) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &window->last_motion, &elapsed);
+ if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
+ ledit_window_drag_motion(window, &window->last_motion_event);
+ window->last_motion = now;
+ window->last_motion_valid = 0;
+ }
+ }
+ if (window->last_scroll_valid) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &window->last_scroll, &elapsed);
+ if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= MOUSE_TICK) {
+ ledit_window_button_press(window, &window->last_scroll_event, window->scroll_num);
+ window->last_scroll = now;
+ window->last_scroll_valid = 0;
+ }
+ }
+ if (window->last_resize_valid) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &window->last_resize, &elapsed);
+ if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= RESIZE_TICK) {
+ ledit_window_resize(
+ window,
+ window->last_resize_event.xconfigure.width,
+ window->last_resize_event.xconfigure.height
+ );
+ window->last_resize = now;
+ window->last_resize_valid = 0;
+ window->redraw = 1;
+ }
+ }
+}
+
+void
ledit_window_resize(ledit_window *window, int w, int h) {
window->w = w;
window->h = h;
recalc_text_size(window);
- window->common->redraw = 1;
+ if (window->resize_callback)
+ window->resize_callback(window->resize_cb_data);
+ window->redraw = 1;
}
void
@@ -748,8 +834,9 @@ clipboard_selnotify(ledit_window *window, XEvent *e) {
}
/* FIXME: XGetWindowProperty takes data as unsigned char, so it is casted here. Why? */
+ /* FIXME: buffer all pasted text and only send it in one go in the end */
if (window->paste_callback)
- window->paste_callback(window->paste_cb_data, (char *)data, (int)(nitems * format / 8));
+ window->paste_callback(window->paste_cb_data, (char *)data, (size_t)(nitems * format / 8));
XFree(data);
/* number of 32-bit chunks returned */
ofs += nitems * format / 32;
@@ -820,8 +907,38 @@ clipboard_selrequest(ledit_window *window, XEvent *e)
fprintf(stderr, "Error sending SelectionNotify event\n");
}
+void
+ledit_window_register_button_press(ledit_window *window, XEvent *event) {
+ int scroll_delta;
+ if (event->xbutton.button == Button4 ||
+ event->xbutton.button == Button5) {
+ scroll_delta = event->xbutton.button == Button4 ? -1 : 1;
+ if (window->last_scroll_valid) {
+ window->scroll_num += scroll_delta;
+ } else {
+ window->last_scroll_event = *event;
+ window->last_scroll_valid = 1;
+ window->scroll_num = scroll_delta;
+ }
+ } else {
+ ledit_window_button_press(window, event, 0);
+ }
+}
+
+void
+ledit_window_register_resize(ledit_window *window, XEvent *event) {
+ window->last_resize_event = *event;
+ window->last_resize_valid = 1;
+}
+
+void
+ledit_window_register_motion(ledit_window *window, XEvent *event) {
+ window->last_motion_event = *event;
+ window->last_motion_valid = 1;
+}
+
/* FIXME: improve set_scroll_pos; make it a bit clearer */
-int
+void
ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
int x, y;
double scroll_h, scroll_y;
@@ -837,11 +954,11 @@ ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
double new_scroll_y = y - scroll_h / 2;
set_scroll_pos(window, new_scroll_y);
}
- return 1;
+ window->redraw = 1;
} else if (y < window->text_h) {
if (window->button_callback)
window->button_callback(window->button_cb_data, event);
- return 1;
+ window->redraw = 1;
}
break;
case Button4:
@@ -854,23 +971,21 @@ ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
}
if (window->scroll_callback)
window->scroll_callback(window->scroll_cb_data, (long)window->scroll_offset);
- return 1;
+ window->redraw = 1;
}
- return 0;
}
-int
+void
ledit_window_button_release(ledit_window *window, XEvent *event) {
if (event->xbutton.button == Button1) {
window->scroll_dragging = 0;
if (window->button_callback)
window->button_callback(window->button_cb_data, event);
- return 1;
+ window->redraw = 1;
}
- return 0;
}
-int
+void
ledit_window_drag_motion(ledit_window *window, XEvent *event) {
if (window->scroll_dragging) {
double scroll_h, scroll_y;
@@ -878,16 +993,15 @@ ledit_window_drag_motion(ledit_window *window, XEvent *event) {
scroll_y += event->xbutton.y - window->scroll_grab_handle;
window->scroll_grab_handle = event->xbutton.y;
set_scroll_pos(window, scroll_y);
- return 1;
+ window->redraw = 1;
} else {
if (window->button_callback)
window->button_callback(window->button_cb_data, event);
- return 1;
+ window->redraw = 1;
}
- return 0;
}
-int
+void
ledit_window_clipboard_event(ledit_window *window, XEvent *event) {
/* FIXME: paste in bottom bar */
switch (event->type) {
@@ -903,5 +1017,4 @@ ledit_window_clipboard_event(ledit_window *window, XEvent *event) {
default:
break;
}
- return 0;
}
diff --git a/window.h b/window.h
@@ -1,6 +1,7 @@
typedef struct bottom_bar bottom_bar;
typedef struct {
+ /* FIXME: maybe move to common? */
PangoFontMap *fontmap;
PangoContext *context;
PangoFontDescription *font;
@@ -23,6 +24,21 @@ typedef struct {
Atom xtarget;
bottom_bar *bb;
int bb_msg_shown;
+ int redraw;
+ enum ledit_mode mode; /* a bit ugly to duplicate this here... */
+
+ /* filtered events */
+ struct timespec last_scroll;
+ struct timespec last_motion;
+ struct timespec last_resize;
+ XEvent last_scroll_event;
+ XEvent last_motion_event;
+ XEvent last_resize_event;
+ int last_scroll_valid;
+ int last_motion_valid;
+ int last_resize_valid;
+ int scroll_num;
+ int scroll_delta;
/* IME stuff */
XIM xim;
@@ -32,16 +48,18 @@ typedef struct {
ledit_common *common;
ledit_theme *theme;
- void (*paste_callback)(void *, char *, int);
+ void (*paste_callback)(void *, char *, size_t);
void (*scroll_callback)(void *, long);
void (*button_callback)(void *, XEvent *);
+ void (*resize_callback)(void *);
void *paste_cb_data;
void *scroll_cb_data;
void *button_cb_data;
+ void *resize_cb_data;
char *mode_extra_text;
} ledit_window;
-ledit_window *ledit_window_create(ledit_common *common, ledit_theme *theme);
+ledit_window *ledit_window_create(ledit_common *common, ledit_theme *theme, enum ledit_mode mode);
void ledit_window_destroy(ledit_window *window);
void ledit_window_cleanup(void);
@@ -68,8 +86,13 @@ void ledit_window_set_mode_extra_text(ledit_window *window, char *text);
void ledit_window_set_scroll_max(ledit_window *window, long max);
void ledit_window_set_scroll_pos(ledit_window *window, long pos);
void ledit_window_set_scroll_callback(ledit_window *window, void (*cb)(void *, long), void *data);
-void ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *, int), void *data);
+void ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, char *, size_t), void *data);
void ledit_window_set_button_callback(ledit_window *window, void (*cb)(void *, XEvent *), void *data);
+void ledit_window_set_resize_callback(ledit_window *window, void (*cb)(void *), void *data);
+void ledit_window_register_resize(ledit_window *window, XEvent *event);
+void ledit_window_register_button_press(ledit_window *window, XEvent *event);
+void ledit_window_register_motion(ledit_window *window, XEvent *event);
+void ledit_window_handle_filtered_events(ledit_window *window);
void ledit_window_clear(ledit_window *window);
void ledit_window_redraw(ledit_window *window);
@@ -81,8 +104,8 @@ void clipboard_paste_clipboard(ledit_window *window);
void clipboard_paste_primary(ledit_window *window);
txtbuf *ledit_window_get_primary_clipboard_buffer(void);
-int ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num);
-int ledit_window_button_release(ledit_window *window, XEvent *event);
-int ledit_window_drag_motion(ledit_window *window, XEvent *event);
-int ledit_window_clipboard_event(ledit_window *window, XEvent *event);
+void ledit_window_button_press(ledit_window *window, XEvent *event, int scroll_num);
+void ledit_window_button_release(ledit_window *window, XEvent *event);
+void ledit_window_drag_motion(ledit_window *window, XEvent *event);
+void ledit_window_clipboard_event(ledit_window *window, XEvent *event);
void xximspot(ledit_window *window, int x, int y);