tMinor refactoring - ledit - Text editor (WIP) | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow) | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 9df066a3d594aeeca41677744f0f29a81901f124 | |
parent 86dd70c41a66c874b49092caa1986ecedd36f254 | |
Author: lumidify <[email protected]> | |
Date: Sat, 23 Oct 2021 19:39:17 +0200 | |
Minor refactoring | |
Diffstat: | |
M IDEAS | 3 ++- | |
M LICENSE | 2 ++ | |
M Makefile | 35 +++++++++++++++++++++++++++++… | |
A README | 1 + | |
A action.h | 9 +++++++++ | |
M buffer.c | 751 +++++++++++++++++++++++------… | |
M buffer.h | 109 +++++++++++++++++------------… | |
M cache.c | 104 +++++++++++++----------------… | |
M cache.h | 17 ++++++++++++----- | |
D commands.c | 168 -----------------------------… | |
D commands.h | 1 - | |
M common.h | 39 ++++++++---------------------… | |
A keys.c | 69 ++++++++++++++++++++++++++++++ | |
A keys.h | 23 +++++++++++++++++++++++ | |
A keys_basic.c | 937 ++++++++++++++++++++++++++++++ | |
A keys_basic.h | 1 + | |
A keys_basic_config.h | 144 +++++++++++++++++++++++++++++… | |
A keys_command.c | 325 +++++++++++++++++++++++++++++… | |
A keys_command.h | 15 +++++++++++++++ | |
A keys_command_config.h | 41 +++++++++++++++++++++++++++++… | |
D lbuf.c | 51 -----------------------------… | |
D lbuf.h | 12 ------------ | |
M ledit.c | 1840 ++---------------------------… | |
M search.c | 42 ++++++++++++++++++-----------… | |
A theme.c | 35 +++++++++++++++++++++++++++++… | |
A theme.h | 16 ++++++++++++++++ | |
A theme_config.h | 9 +++++++++ | |
A txtbuf.c | 51 +++++++++++++++++++++++++++++… | |
A txtbuf.h | 11 +++++++++++ | |
M undo.c | 213 ++++++++++++++---------------… | |
M undo.h | 36 ++++++++++++++++++++---------… | |
M util.c | 17 ++++++++++------- | |
M util.h | 4 ++-- | |
A window.c | 769 ++++++++++++++++++++++++++++++ | |
A window.h | 80 +++++++++++++++++++++++++++++… | |
35 files changed, 3503 insertions(+), 2477 deletions(-) | |
--- | |
diff --git a/IDEAS b/IDEAS | |
t@@ -1,4 +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) | |
* add different (more basic) text backend | |
-* visual selection mode - allow to switch cursor between selection ends | |
* https://drewdevault.com/2021/06/27/You-cant-capture-the-nuance.html | |
diff --git a/LICENSE b/LICENSE | |
t@@ -1,3 +1,5 @@ | |
+Note: Some stuff is stolen from st (https://st.suckless.org) | |
+ | |
ISC License | |
Copyright (c) 2021 lumidify <[email protected]> | |
diff --git a/Makefile b/Makefile | |
t@@ -9,8 +9,39 @@ MANPREFIX = ${PREFIX}/man | |
BIN = ${NAME} | |
MAN1 = ${BIN:=.1} | |
-OBJ = ${BIN:=.o} cache.o buffer.o memory.o util.o search.o lbuf.o undo.o comma… | |
-HDR = cache.h buffer.h memory.h common.h util.h search.h lbuf.h undo.h command… | |
+OBJ = \ | |
+ buffer.o \ | |
+ cache.o \ | |
+ keys.o \ | |
+ keys_basic.o \ | |
+ keys_command.o \ | |
+ ledit.o \ | |
+ memory.o \ | |
+ search.o \ | |
+ theme.o \ | |
+ txtbuf.o \ | |
+ undo.o \ | |
+ util.o \ | |
+ window.o | |
+ | |
+HDR = \ | |
+ action.h \ | |
+ buffer.h \ | |
+ cache.h \ | |
+ common.h \ | |
+ keys.h \ | |
+ keys_basic.h \ | |
+ keys_basic_config.h \ | |
+ keys_command.h \ | |
+ keys_command_config.h \ | |
+ memory.h \ | |
+ search.h \ | |
+ theme.h \ | |
+ theme_config.h \ | |
+ txtbuf.h \ | |
+ undo.h \ | |
+ util.h \ | |
+ window.h | |
CFLAGS_LEDIT = -g -Wall -Wextra -D_POSIX_C_SOURCE=200809L `pkg-config --cflags… | |
LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm | |
diff --git a/README b/README | |
t@@ -0,0 +1 @@ | |
+Work in progress. Nothing to see here. | |
diff --git a/action.h b/action.h | |
t@@ -0,0 +1,9 @@ | |
+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_buffer *buffer, XEvent *event, int lan… | |
+}; | |
diff --git a/buffer.c b/buffer.c | |
t@@ -2,21 +2,25 @@ | |
/* FIXME: also cache PangoLayouts since keeping them around isn't really of mu… | |
#include <stdio.h> | |
+#include <errno.h> | |
#include <string.h> | |
#include <assert.h> | |
#include <limits.h> | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
+#include <X11/Xatom.h> | |
#include <pango/pangoxft.h> | |
#include <X11/extensions/Xdbe.h> | |
#include "memory.h" | |
#include "common.h" | |
-#include "lbuf.h" | |
-#include "buffer.h" | |
-#include "cache.h" | |
+#include "txtbuf.h" | |
#include "undo.h" | |
+#include "cache.h" | |
+#include "theme.h" | |
+#include "window.h" | |
+#include "buffer.h" | |
/* | |
* Important notes: | |
t@@ -30,25 +34,43 @@ | |
static PangoAttrList *basic_attrs = NULL; | |
+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(ledit_buffer *buffer, int line, int start, i… | |
static void delete_line_section_base(ledit_buffer *buffer, int line, int start… | |
+static void normalize_and_set_pango_text(ledit_line *line); | |
+static void swap(int *a, int *b); | |
+static void sort_selection(int *line1, int *byte1, int *line2, int *byte2); | |
+static void copy_selection_to_x_primary(ledit_buffer *buffer, int line1, int b… | |
+ | |
+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); | |
+} | |
/* FIXME: destroy basic_attrs somewhere */ | |
ledit_buffer * | |
-ledit_create_buffer(ledit_common_state *state) { | |
+ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *wi… | |
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(FAL… | |
pango_attr_list_insert(basic_attrs, no_hyphens); | |
+ #endif | |
} | |
ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer)); | |
- buffer->state = state; | |
+ buffer->common = common; | |
+ buffer->window = window; | |
buffer->lines = NULL; | |
buffer->filename = 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 */ | |
t@@ -59,8 +81,13 @@ ledit_create_buffer(ledit_common_state *state) { | |
buffer->display_offset = 0; | |
buffer->sel.line1 = buffer->sel.byte1 = -1; | |
buffer->sel.line2 = buffer->sel.byte2 = -1; | |
- ledit_append_line_base(buffer, -1, -1); | |
- ledit_recalc_all_lines(buffer); | |
+ ledit_buffer_append_line_base(buffer, -1, -1); | |
+ ledit_buffer_recalc_all_lines(buffer); | |
+ buffer->cache = ledit_cache_create(common); | |
+ buffer->undo = ledit_undo_stack_create(); | |
+ buffer->theme = theme; | |
+ ledit_window_set_scroll_callback(window, &ledit_buffer_scroll_handler,… | |
+ ledit_window_set_button_callback(window, &ledit_buffer_button_handler,… | |
return buffer; | |
} | |
t@@ -68,7 +95,7 @@ ledit_create_buffer(ledit_common_state *state) { | |
/* FIXME: don't generate extra blank line at end! */ | |
/* WARNING: errstr must be copied as soon as possible! */ | |
int | |
-ledit_load_file_into_buffer(ledit_buffer *buffer, char *filename, int line, ch… | |
+ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, char **… | |
long len; | |
int off = 0; | |
ledit_line *ll; | |
t@@ -82,7 +109,7 @@ ledit_load_file_into_buffer(ledit_buffer *buffer, char *fil… | |
if (len < 0) goto errorclose; | |
if (fseek(file, 0, SEEK_SET)) goto errorclose; | |
- ll = ledit_get_line(buffer, line); | |
+ ll = ledit_buffer_get_line(buffer, line); | |
file_contents = ledit_malloc(len + 2); | |
/* mimic nvi (or at least the openbsd version) - if the line | |
is empty, insert directly, otherwise insert after the line */ | |
t@@ -101,7 +128,7 @@ ledit_load_file_into_buffer(ledit_buffer *buffer, char *fi… | |
} | |
if (fclose(file)) goto error; | |
- ledit_insert_text_with_newlines( | |
+ ledit_buffer_insert_text_with_newlines( | |
buffer, line, ll->len, file_contents, len + off, NULL, NULL | |
); | |
free(file_contents); | |
t@@ -119,15 +146,15 @@ errorclose: | |
/* FIXME: allow to write only certain lines */ | |
int | |
-ledit_write_buffer_to_file(ledit_buffer *buffer, char *filename, char **errstr… | |
+ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr… | |
FILE *file; | |
ledit_line *ll; | |
file = fopen(filename, "w"); | |
if (!file) goto error; | |
clearerr(file); | |
for (int i = 0; i < buffer->lines_num; i++) { | |
- ll = ledit_get_line(buffer, i); | |
- ledit_normalize_line(ll); | |
+ ll = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(ll); | |
if (fprintf(file, "%s\n", ll->text) < 0) goto errorclose; | |
} | |
if (fclose(file)) goto error; | |
t@@ -144,20 +171,22 @@ errorclose: | |
} | |
void | |
-ledit_destroy_buffer(ledit_buffer *buffer) { | |
+ledit_buffer_destroy(ledit_buffer *buffer) { | |
ledit_line *l; | |
for (int i = 0; i < buffer->lines_num; i++) { | |
- l = ledit_get_line(buffer, i); | |
+ l = ledit_buffer_get_line(buffer, i); | |
g_object_unref(l->layout); | |
free(l->text); | |
} | |
+ ledit_cache_destroy(buffer->cache); | |
+ ledit_undo_stack_destroy(buffer->undo); | |
free(buffer->lines); | |
if (buffer->filename) free(buffer->filename); | |
free(buffer); | |
} | |
void | |
-ledit_normalize_line(ledit_line *line) { | |
+ledit_buffer_normalize_line(ledit_line *line) { | |
if (line->gap < line->len) { | |
memmove( | |
line->text + line->gap, | |
t@@ -177,12 +206,12 @@ err_text_dirty(ledit_buffer *buffer, int line) { | |
"WARNING: Line had text_dirty or h_dirty attribute " | |
"set when rendering. Fix your code!\n" | |
); | |
- ledit_recalc_from_line(buffer, line); | |
+ ledit_buffer_recalc_from_line(buffer, line); | |
} | |
void | |
-ledit_set_line_selection(ledit_buffer *buffer, int line, int start_byte, int e… | |
- ledit_line *l = ledit_get_line(buffer, line); | |
+ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start_byte… | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line); | |
if (l->text_dirty) | |
err_text_dirty(buffer, line); | |
/* FIXME: configure color */ | |
t@@ -192,32 +221,36 @@ ledit_set_line_selection(ledit_buffer *buffer, int line,… | |
attr0->end_index = end_byte; | |
attr1->start_index = start_byte; | |
attr1->end_index = end_byte; | |
- PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE); | |
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); | |
l->dirty = 1; | |
} | |
void | |
-ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) { | |
- ledit_line *l = ledit_get_line(buffer, line); | |
+ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) { | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line); | |
if (l->text_dirty) | |
err_text_dirty(buffer, line); | |
- if (buffer->state->mode == NORMAL) { | |
+ if (buffer->common->mode == NORMAL) { | |
PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0); | |
PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535… | |
attr0->start_index = index; | |
attr0->end_index = index + 1; | |
attr1->start_index = index; | |
attr1->end_index = index + 1; | |
- PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE); | |
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); | |
} else { | |
pango_layout_set_attributes(l->layout, basic_attrs); | |
t@@ -226,43 +259,46 @@ ledit_set_line_cursor_attrs(ledit_buffer *buffer, int li… | |
} | |
void | |
-ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) { | |
- ledit_line *l = ledit_get_line(buffer, line); | |
+ledit_buffer_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) { | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line); | |
pango_layout_set_attributes(l->layout, basic_attrs); | |
l->dirty = 1; | |
} | |
-/* FIXME: To simplify this a bit, maybe just copy text to lbuf first and | |
+/* 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_insert_text_from_line( | |
+ledit_buffer_insert_text_from_line( | |
ledit_buffer *buffer, | |
int dst_line, int dst_index, | |
int src_line, int src_index, int src_len, | |
- lbuf *text_ret) { | |
- ledit_insert_text_from_line_base( | |
+ txtbuf *text_ret) { | |
+ ledit_buffer_insert_text_from_line_base( | |
buffer, dst_line, dst_index, src_line, src_index, src_len, text_ret | |
); | |
- ledit_recalc_line(buffer, dst_line); | |
+ ledit_buffer_recalc_line(buffer, dst_line); | |
} | |
+/* FIXME: check if there can be bugs when a newline is inserted in some way ot… | |
+ other than pasting or pressing enter */ | |
+ | |
void | |
-ledit_insert_text_from_line_base( | |
+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, | |
- lbuf *text_ret) { | |
+ txtbuf *text_ret) { | |
assert(dst_line != src_line); | |
- ledit_line *ll = ledit_get_line(buffer, 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) { | |
- lbuf_grow(text_ret, src_len); | |
+ txtbuf_grow(text_ret, src_len); | |
text_ret->len = src_len; | |
} | |
if (src_index >= ll->gap) { | |
/* all text to insert is after gap */ | |
- ledit_insert_text_base( | |
+ ledit_buffer_insert_text_base( | |
buffer, dst_line, dst_index, | |
ll->text + src_index + ll->cap - ll->len, src_len | |
); | |
t@@ -275,7 +311,7 @@ ledit_insert_text_from_line_base( | |
} | |
} else if (ll->gap - src_index >= src_len) { | |
/* all text to insert is before gap */ | |
- ledit_insert_text_base( | |
+ ledit_buffer_insert_text_base( | |
buffer, dst_line, dst_index, | |
ll->text + src_index, src_len | |
); | |
t@@ -288,12 +324,12 @@ ledit_insert_text_from_line_base( | |
} | |
} else { | |
/* insert part of text before gap */ | |
- ledit_insert_text_base( | |
+ ledit_buffer_insert_text_base( | |
buffer, dst_line, dst_index, | |
ll->text + src_index, ll->gap - src_index | |
); | |
/* insert part of text after gap */ | |
- ledit_insert_text_base( | |
+ ledit_buffer_insert_text_base( | |
buffer, dst_line, dst_index + ll->gap - src_index, | |
ll->text + ll->gap + ll->cap - ll->len, | |
src_len - ll->gap + src_index | |
t@@ -386,13 +422,13 @@ resize_and_move_text_gap(ledit_line *line, int min_size,… | |
} | |
void | |
-ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text,… | |
- ledit_insert_text_base(buffer, line_index, index, text, len); | |
- ledit_recalc_line(buffer, line_index); | |
+ledit_buffer_insert_text(ledit_buffer *buffer, int line_index, int index, char… | |
+ ledit_buffer_insert_text_base(buffer, line_index, index, text, len); | |
+ ledit_buffer_recalc_line(buffer, line_index); | |
} | |
void | |
-ledit_insert_text_base(ledit_buffer *buffer, int line_index, int index, char *… | |
+ledit_buffer_insert_text_base(ledit_buffer *buffer, int line_index, int index,… | |
ledit_line *line = &buffer->lines[line_index]; | |
if (len == -1) | |
len = strlen(text); | |
t@@ -415,7 +451,7 @@ ledit_insert_text_base(ledit_buffer *buffer, int line_inde… | |
avoid another copy before rendering */ | |
/* | |
void | |
-ledit_insert_text_final(ledit_buffer *buffer, int line_index, int index, char … | |
+ledit_buffer_insert_text_final(ledit_buffer *buffer, int line_index, int index… | |
} | |
*/ | |
t@@ -438,27 +474,27 @@ strchr_len(char *text, char c, long len) { | |
Maybe len isn't needed anyways? It might be possible to enforce that text | |
just always has to be null-terminated. */ | |
void | |
-ledit_insert_text_with_newlines( | |
+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; | |
- ledit_insert_text_with_newlines_base( | |
+ ledit_buffer_insert_text_with_newlines_base( | |
buffer, line_index, index, text, len, | |
&end, end_char_ret | |
); | |
if (end_line_ret) | |
*end_line_ret = end; | |
if (line_index == end) | |
- ledit_recalc_line(buffer, line_index); | |
+ ledit_buffer_recalc_line(buffer, line_index); | |
else | |
- ledit_recalc_from_line(buffer, line_index); | |
+ ledit_buffer_recalc_from_line(buffer, line_index); | |
} | |
/* FIXME: Check for integer overflow when casting to int */ | |
void | |
-ledit_insert_text_with_newlines_base( | |
+ledit_buffer_insert_text_with_newlines_base( | |
ledit_buffer *buffer, | |
int line_index, int index, | |
char *text, long len, | |
t@@ -469,69 +505,86 @@ ledit_insert_text_with_newlines_base( | |
char *cur, *last = text; | |
int cur_line = line_index; | |
int cur_index = index; | |
+ /* FIXME: strchr_len isn't really needed when the lines are normalized… | |
while ((cur = strchr_len(last, '\n', rem_len)) != NULL) { | |
/* FIXME: inefficient because there's no gap buffer yet */ | |
- ledit_append_line_base(buffer, cur_line, cur_index); | |
- ledit_insert_text_base(buffer, cur_line, cur_index, last, cur … | |
+ ledit_buffer_append_line_base(buffer, cur_line, cur_index); | |
+ ledit_buffer_insert_text_base(buffer, cur_line, cur_index, las… | |
cur_index = 0; | |
cur_line++; | |
rem_len -= cur - last + 1; | |
last = cur + 1; | |
} | |
/* FIXME: check how legal this casting between pointers and ints is */ | |
- ledit_insert_text_base(buffer, cur_line, cur_index, last, text + len -… | |
+ ledit_buffer_insert_text_base(buffer, cur_line, cur_index, last, text … | |
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_render_line(ledit_buffer *buffer, int line_index) { | |
+ledit_buffer_render_line(ledit_buffer *buffer, int line_index) { | |
/* FIXME: check for <= 0 on size */ | |
- ledit_line *line = &buffer->lines[line_index]; | |
+ ledit_line *line = ledit_buffer_get_line(buffer, line_index); | |
/* this shouldn't happen if the functions here are used correctly */ | |
if (line->text_dirty || line->h_dirty) | |
err_text_dirty(buffer, line_index); | |
- if (line->cache_index == -1) | |
- ledit_assign_free_cache_index(buffer, line_index); | |
- ledit_cache_pixmap *pix = ledit_get_cache_pixmap(line->cache_index); | |
+ if (line->cache_index == -1) { | |
+ int new_index = ledit_get_unneeded_cache_index(buffer->cache, … | |
+ ledit_cache_pixmap *tmp_pix = ledit_get_cache_pixmap(buffer->c… | |
+ if (tmp_pix->line >= 0) { | |
+ ledit_line *old_line = ledit_buffer_get_line(buffer, t… | |
+ old_line->cache_index = -1; | |
+ } | |
+ tmp_pix->line = line_index; | |
+ line->cache_index = new_index; | |
+ } | |
+ ledit_cache_pixmap *pix = ledit_get_cache_pixmap(buffer->cache, line->… | |
/* FIXME: sensible default pixmap sizes here */ | |
if (pix->pixmap == None || pix->draw == NULL) { | |
pix->pixmap = XCreatePixmap( | |
- buffer->state->dpy, buffer->state->drawable, | |
- line->w + 10, line->h + 10, buffer->state->depth | |
+ buffer->common->dpy, buffer->window->drawable, | |
+ line->w + 10, line->h + 10, buffer->common->depth | |
); | |
pix->w = line->w + 10; | |
pix->h = line->h + 10; | |
pix->draw = XftDrawCreate( | |
- buffer->state->dpy, pix->pixmap, | |
- buffer->state->vis, buffer->state->cm | |
+ buffer->common->dpy, pix->pixmap, | |
+ buffer->common->vis, buffer->common->cm | |
); | |
} else if (pix->w < line->w || pix->h < line->h) { | |
int new_w = line->w > pix->w ? line->w + 10 : pix->w + 10; | |
int new_h = line->h > pix->h ? line->h + 10 : pix->h + 10; | |
- XFreePixmap(buffer->state->dpy, pix->pixmap); | |
+ XFreePixmap(buffer->common->dpy, pix->pixmap); | |
pix->pixmap = XCreatePixmap( | |
- buffer->state->dpy, buffer->state->drawable, | |
- new_w, new_h, buffer->state->depth | |
+ 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->state->bg, 0, 0, line->w, line->h); | |
- pango_xft_render_layout(pix->draw, &buffer->state->fg, line->layout, 0… | |
+ XftDrawRect(pix->draw, &buffer->theme->text_bg, 0, 0, line->w, line->h… | |
+ pango_xft_render_layout(pix->draw, &buffer->theme->text_fg, line->layo… | |
line->dirty = 0; | |
} | |
static void | |
init_line(ledit_buffer *buffer, ledit_line *line) { | |
+ int text_w, text_h; | |
line->parent_buffer = buffer; | |
- line->layout = pango_layout_new(buffer->state->context); | |
- pango_layout_set_width(line->layout, (buffer->state->text_w) * PANGO_S… | |
- pango_layout_set_font_description(line->layout, buffer->state->font); | |
+ line->layout = pango_layout_new(buffer->window->context); | |
+ ledit_window_get_textview_size(buffer->window, &text_w, &text_h); | |
+ pango_layout_set_width(line->layout, text_w * PANGO_SCALE); | |
+ pango_layout_set_font_description(line->layout, buffer->window->font); | |
pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); | |
pango_layout_set_attributes(line->layout, basic_attrs); | |
line->gap = 0; | |
t@@ -547,19 +600,19 @@ init_line(ledit_buffer *buffer, ledit_line *line) { | |
line->w = line->h = 0; | |
/* FIXME: does this set line height reasonably when no text yet? */ | |
pango_layout_get_pixel_size(line->layout, &line->w, &line->h); | |
- line->w = buffer->state->text_w; | |
+ line->w = text_w; | |
line->y_offset = 0; | |
} | |
void | |
-ledit_append_line(ledit_buffer *buffer, int line_index, int text_index) { | |
- ledit_append_line_base(buffer, line_index, text_index); | |
- ledit_recalc_from_line(buffer, line_index); | |
+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_recalc_from_line(buffer, line_index); | |
} | |
/* FIXME: error checking (index out of bounds, etc.) */ | |
void | |
-ledit_append_line_base(ledit_buffer *buffer, int line_index, int text_index) { | |
+ledit_buffer_append_line_base(ledit_buffer *buffer, int line_index, int text_i… | |
if (buffer->lines_num >= buffer->lines_cap) { | |
buffer->lines_cap *= 2; | |
if (buffer->lines_cap == 0) | |
t@@ -573,12 +626,12 @@ ledit_append_line_base(ledit_buffer *buffer, int line_in… | |
buffer->lines + line_index + 1, | |
(buffer->lines_num - (line_index + 1)) * sizeof(ledit_line) | |
); | |
- ledit_line *new_l = ledit_get_line(buffer, line_index + 1); | |
+ ledit_line *new_l = ledit_buffer_get_line(buffer, line_index + 1); | |
init_line(buffer, new_l); | |
buffer->lines_num++; | |
if (text_index != -1) { | |
- ledit_line *l = ledit_get_line(buffer, line_index); | |
- ledit_insert_text_from_line_base( | |
+ 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 | |
); | |
t@@ -591,19 +644,19 @@ ledit_append_line_base(ledit_buffer *buffer, int line_in… | |
/* this is very weird and ugly with the recalc */ | |
void | |
-ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2) { | |
- ledit_delete_line_entries_base(buffer, index1, index2); | |
- ledit_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0); | |
+ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int index2)… | |
+ ledit_buffer_delete_line_entries_base(buffer, index1, index2); | |
+ ledit_buffer_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0); | |
} | |
-/* IMPORTANT: ledit_recalc_from_line needs to be called sometime after this! */ | |
+/* IMPORTANT: ledit_buffer_recalc_from_line needs to be called sometime after … | |
void | |
-ledit_delete_line_entries_base(ledit_buffer *buffer, int index1, int index2) { | |
+ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int in… | |
ledit_line *l; | |
/* FIXME: make sure this is always true */ | |
assert(index2 - index1 != buffer->lines_num); | |
for (int i = index1; i <= index2; i++) { | |
- l = ledit_get_line(buffer, i); | |
+ l = ledit_buffer_get_line(buffer, i); | |
g_object_unref(l->layout); | |
free(l->text); | |
} | |
t@@ -617,29 +670,29 @@ ledit_delete_line_entries_base(ledit_buffer *buffer, int… | |
buffer->lines_num -= index2 - index1 + 1; | |
/* force a recalc of the lines */ | |
if (index1 == 0) { | |
- l = ledit_get_line(buffer, 0); | |
+ l = ledit_buffer_get_line(buffer, 0); | |
l->y_offset = 0; | |
l->h_dirty = 1; | |
} else { | |
- l = ledit_get_line(buffer, index1 - 1); | |
+ l = ledit_buffer_get_line(buffer, index1 - 1); | |
l->h_dirty = 1; | |
} | |
} | |
void | |
-ledit_delete_line_entry(ledit_buffer *buffer, int index) { | |
- ledit_delete_line_entries(buffer, index, index); | |
+ledit_buffer_delete_line_entry(ledit_buffer *buffer, int index) { | |
+ ledit_buffer_delete_line_entries(buffer, index, index); | |
} | |
void | |
-ledit_delete_line_entry_base(ledit_buffer *buffer, int index) { | |
- ledit_delete_line_entries_base(buffer, index, index); | |
+ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int 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_get_line(ledit_buffer *buffer, int index) { | |
+ledit_buffer_get_line(ledit_buffer *buffer, int index) { | |
return &buffer->lines[index]; | |
} | |
t@@ -647,11 +700,11 @@ ledit_get_line(ledit_buffer *buffer, int index) { | |
* - if height hasn't changed, nothing further is done | |
* - if height has changed, offset of all following lines is changed */ | |
void | |
-ledit_recalc_line(ledit_buffer *buffer, int line) { | |
+ledit_buffer_recalc_line(ledit_buffer *buffer, int line) { | |
int w, h; | |
- ledit_line *l = ledit_get_line(buffer, line); | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line); | |
if (l->text_dirty) { | |
- ledit_normalize_line(l); | |
+ ledit_buffer_normalize_line(l); | |
pango_layout_set_text(l->layout, l->text, l->len); | |
l->text_dirty = 0; | |
} | |
t@@ -663,7 +716,7 @@ ledit_recalc_line(ledit_buffer *buffer, int line) { | |
long off = l->y_offset + h; | |
l->h = h; | |
for (int i = line + 1; i < buffer->lines_num; i++) { | |
- l = ledit_get_line(buffer, i); | |
+ l = ledit_buffer_get_line(buffer, i); | |
l->y_offset = off; | |
off += l->h; | |
} | |
t@@ -674,14 +727,14 @@ ledit_recalc_line(ledit_buffer *buffer, int line) { | |
/* set text of pango layout and recalculate height | |
* and offset for all lines starting at 'line' */ | |
void | |
-ledit_recalc_from_line(ledit_buffer *buffer, int line) { | |
+ledit_buffer_recalc_from_line(ledit_buffer *buffer, int line) { | |
int w, h; | |
- ledit_line *l = ledit_get_line(buffer, 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_get_line(buffer, i); | |
+ l = ledit_buffer_get_line(buffer, i); | |
if (l->text_dirty) { | |
- ledit_normalize_line(l); | |
+ ledit_buffer_normalize_line(l); | |
pango_layout_set_text(l->layout, l->text, l->len); | |
l->text_dirty = 0; | |
pango_layout_get_pixel_size(l->layout, &w, &h); | |
t@@ -697,19 +750,21 @@ ledit_recalc_from_line(ledit_buffer *buffer, int line) { | |
buffer->total_height = off; | |
} | |
-/* short for 'ledit_recalc_from_line' starting at line 0 */ | |
+/* short for 'ledit_buffer_recalc_from_line' starting at line 0 */ | |
void | |
-ledit_recalc_all_lines(ledit_buffer *buffer) { | |
+ledit_buffer_recalc_all_lines(ledit_buffer *buffer) { | |
/* force first line to offset 0, just in case */ | |
- ledit_line *l = ledit_get_line(buffer, 0); | |
+ ledit_line *l = ledit_buffer_get_line(buffer, 0); | |
l->y_offset = 0; | |
- ledit_recalc_from_line(buffer, 0); | |
+ ledit_buffer_recalc_from_line(buffer, 0); | |
} | |
int | |
-ledit_line_visible(ledit_buffer *buffer, int index) { | |
- ledit_line *l = ledit_get_line(buffer, index); | |
- return l->y_offset < buffer->display_offset + buffer->state->text_h && | |
+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; | |
} | |
t@@ -718,17 +773,17 @@ ledit_line_visible(ledit_buffer *buffer, int index) { | |
* - 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_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2… | |
+ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, in… | |
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2)); | |
size_t len = 0; | |
- ledit_line *ll = ledit_get_line(buffer, line1); | |
+ ledit_line *ll = ledit_buffer_get_line(buffer, line1); | |
if (line1 == line2) { | |
len = byte2 - byte1; | |
} else { | |
/* + 1 for newline */ | |
len = ll->len - byte1 + byte2 + 1; | |
for (int i = line1 + 1; i < line2; i++) { | |
- ll = ledit_get_line(buffer, i); | |
+ ll = ledit_buffer_get_line(buffer, i); | |
len += ll->len + 1; | |
} | |
} | |
t@@ -746,11 +801,11 @@ ledit_textlen(ledit_buffer *buffer, int line1, int byte1… | |
* - dst must be large enough to contain the text and NUL | |
* - the range must be sorted already */ | |
void | |
-ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int lin… | |
+ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, … | |
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2)); | |
- ledit_line *ll1 = ledit_get_line(buffer, line1); | |
- ledit_line *ll2 = ledit_get_line(buffer, line2); | |
- ledit_normalize_line(ll1); | |
+ ledit_line *ll1 = ledit_buffer_get_line(buffer, line1); | |
+ ledit_line *ll2 = ledit_buffer_get_line(buffer, line2); | |
+ ledit_buffer_normalize_line(ll1); | |
if (line1 == line2) { | |
memcpy(dst, ll1->text + byte1, byte2 - byte1); | |
dst[byte2 - byte1] = '\0'; | |
t@@ -761,14 +816,14 @@ ledit_copy_text(ledit_buffer *buffer, char *dst, int lin… | |
dst[cur_pos] = '\n'; | |
cur_pos++; | |
for (int i = line1 + 1; i < line2; i++) { | |
- ledit_line *ll = ledit_get_line(buffer, i); | |
- ledit_normalize_line(ll); | |
+ ledit_line *ll = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(ll); | |
memcpy(dst + cur_pos, ll->text, ll->len); | |
cur_pos += ll->len; | |
dst[cur_pos] = '\n'; | |
cur_pos++; | |
} | |
- ledit_normalize_line(ll2); | |
+ ledit_buffer_normalize_line(ll2); | |
memcpy(dst + cur_pos, ll2->text, byte2); | |
cur_pos += byte2; | |
dst[cur_pos] = '\0'; | |
t@@ -781,15 +836,15 @@ ledit_copy_text(ledit_buffer *buffer, char *dst, int lin… | |
* - the range must be sorted already | |
* - returns the length of the text, not including the NUL */ | |
void | |
-ledit_copy_text_to_lbuf( | |
+ledit_buffer_copy_text_to_txtbuf( | |
ledit_buffer *buffer, | |
- lbuf *buf, | |
+ txtbuf *buf, | |
int line1, int byte1, | |
int line2, int byte2) { | |
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2)); | |
- size_t len = ledit_textlen(buffer, line1, byte1, line2, byte2); | |
- lbuf_grow(buf, len + 1); | |
- ledit_copy_text(buffer, buf->text, line1, byte1, line2, byte2); | |
+ size_t len = ledit_buffer_textlen(buffer, line1, byte1, line2, byte2); | |
+ txtbuf_grow(buf, len + 1); | |
+ ledit_buffer_copy_text(buffer, buf->text, line1, byte1, line2, byte2); | |
buf->len = len; | |
} | |
t@@ -797,16 +852,17 @@ ledit_copy_text_to_lbuf( | |
#define LINE_CHAR(line, i) ((i) < (line)->gap ? (line)->text[i] : (line)->text… | |
int | |
-ledit_prev_utf8(ledit_line *line, int index) { | |
+ledit_line_prev_utf8(ledit_line *line, int index) { | |
int 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)) | |
i--; | |
return i; | |
} | |
int | |
-ledit_next_utf8(ledit_line *line, int index) { | |
+ledit_line_next_utf8(ledit_line *line, int index) { | |
int i = index + 1; | |
while (i < line->len && ((LINE_CHAR(line, i) & 0xC0) == 0x80)) | |
i++; | |
t@@ -818,13 +874,13 @@ ledit_next_utf8(ledit_line *line, int index) { | |
static void | |
delete_line_section(ledit_buffer *buffer, int line, int start, int length) { | |
delete_line_section_base(buffer, line, start, length); | |
- ledit_recalc_line(buffer, line); | |
+ ledit_buffer_recalc_line(buffer, line); | |
} | |
*/ | |
static void | |
delete_line_section_base(ledit_buffer *buffer, int line, int start, int length… | |
- ledit_line *l = ledit_get_line(buffer, line); | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line); | |
if (start <= l->gap && start + length >= l->gap) { | |
l->gap = start; | |
} else if (start < l->gap && start + length < l->gap) { | |
t@@ -848,22 +904,22 @@ delete_line_section_base(ledit_buffer *buffer, int line,… | |
} | |
int | |
-ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index… | |
- int new_index = ledit_delete_unicode_char_base(buffer, line_index, byt… | |
- ledit_recalc_line(buffer, line_index); | |
+ledit_buffer_delete_unicode_char(ledit_buffer *buffer, int line_index, int byt… | |
+ int new_index = ledit_buffer_delete_unicode_char_base(buffer, line_ind… | |
+ ledit_buffer_recalc_line(buffer, line_index); | |
return new_index; | |
} | |
int | |
-ledit_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int byte_… | |
- ledit_line *l = ledit_get_line(buffer, line_index); | |
+ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, int line_index, in… | |
+ ledit_line *l = ledit_buffer_get_line(buffer, line_index); | |
int new_index = byte_index; | |
if (dir < 0) { | |
- int i = ledit_prev_utf8(l, byte_index); | |
+ int i = ledit_line_prev_utf8(l, byte_index); | |
delete_line_section_base(buffer, line_index, i, byte_index - i… | |
new_index = i; | |
} else { | |
- int i = ledit_next_utf8(l, byte_index); | |
+ int i = ledit_line_next_utf8(l, byte_index); | |
delete_line_section_base(buffer, line_index, byte_index, i - b… | |
} | |
return new_index; | |
t@@ -872,7 +928,7 @@ ledit_delete_unicode_char_base(ledit_buffer *buffer, int l… | |
static void | |
normalize_and_set_pango_text(ledit_line *line) { | |
if (line->text_dirty) { | |
- ledit_normalize_line(line); | |
+ ledit_buffer_normalize_line(line); | |
pango_layout_set_text(line->layout, line->text, line->len); | |
line->text_dirty = 0; | |
line->h_dirty = 1; | |
t@@ -894,7 +950,7 @@ ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_… | |
/* 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? */ | |
- if (line->parent_buffer->state->mode == NORMAL) { | |
+ if (line->parent_buffer->common->mode == NORMAL) { | |
PangoRectangle rect; | |
pango_layout_index_to_pos(line->layout, pos, &rect); | |
*x_ret += rect.width / 2; | |
t@@ -919,43 +975,43 @@ ledit_x_softline_to_pos(ledit_line *line, int x, int sof… | |
pango_line, x_relative, pos_ret, &trailing | |
); | |
/* if in insert mode, snap to the nearest border between graphemes */ | |
- if (line->parent_buffer->state->mode == INSERT) { | |
+ if (line->parent_buffer->common->mode == INSERT) { | |
while (trailing > 0) { | |
trailing--; | |
- *pos_ret = ledit_next_utf8(line, *pos_ret); | |
+ *pos_ret = ledit_line_next_utf8(line, *pos_ret); | |
} | |
} | |
} | |
/* FIXME: make sure PangoLayout has newest text already when these functions a… | |
int | |
-ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) { | |
+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 *final_line = ledit_get_line(buffer, line); | |
+ ledit_line *final_line = ledit_buffer_get_line(buffer, line); | |
normalize_and_set_pango_text(final_line); | |
if (pos == final_line->len && pos > 0) { | |
int nattrs; | |
const PangoLogAttr *attrs = | |
pango_layout_get_log_attrs_readonly(final_line->layout, &n… | |
int cur = nattrs - 2; | |
- ret = ledit_prev_utf8(final_line, ret); | |
+ ret = ledit_line_prev_utf8(final_line, ret); | |
while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) { | |
cur--; | |
- ret = ledit_prev_utf8(final_line, ret); | |
+ ret = ledit_line_prev_utf8(final_line, ret); | |
} | |
} | |
return ret; | |
} | |
void | |
-ledit_delete_range( | |
+ledit_buffer_delete_range( | |
ledit_buffer *buffer, int line_based, | |
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, lbuf *text_ret) { | |
- ledit_delete_range_base( | |
+ ledit_range *final_range_ret, txtbuf *text_ret) { | |
+ ledit_buffer_delete_range_base( | |
buffer, line_based, | |
line_index1, byte_index1, | |
line_index2, byte_index2, | |
t@@ -965,24 +1021,24 @@ ledit_delete_range( | |
/* 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_recalc_from_line(buffer, min > 0 ? min - 1 : min); | |
+ ledit_buffer_recalc_from_line(buffer, min > 0 ? min - 1 : min); | |
} | |
/* FIXME: use at least somewhat sensible variable names */ | |
void | |
-ledit_delete_range_base( | |
+ledit_buffer_delete_range_base( | |
ledit_buffer *buffer, int line_based, | |
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, lbuf *text_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; | |
if (line_based) { | |
int x, softline1, softline2; | |
- ledit_line *line1 = ledit_get_line(buffer, line_index1); | |
+ ledit_line *line1 = ledit_buffer_get_line(buffer, line_index1); | |
normalize_and_set_pango_text(line1); | |
ledit_pos_to_x_softline(line1, byte_index1, &x, &softline1); | |
if (line_index1 == line_index2) { | |
t@@ -999,7 +1055,7 @@ ledit_delete_range_base( | |
/* cursor can be moved to next hard li… | |
new_line = line_index1; | |
ledit_x_softline_to_pos( | |
- ledit_get_line(buffer, line_index1… | |
+ ledit_buffer_get_line(buffer, line… | |
x, 0, &new_byte | |
); | |
rgl1 = line_index1; | |
t@@ -1012,7 +1068,7 @@ ledit_delete_range_base( | |
/* note: logically, line_index1 - 1 mu… | |
buffer->lines_num > 1 && line_index… | |
new_line = line_index1 - 1; | |
- ledit_line *prevline = ledit_get_line(… | |
+ ledit_line *prevline = ledit_buffer_ge… | |
softlines = pango_layout_get_line_coun… | |
ledit_x_softline_to_pos(prevline, x, s… | |
rgl1 = line_index1 - 1; | |
t@@ -1021,20 +1077,20 @@ ledit_delete_range_base( | |
rgb2 = line1->len; | |
} | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
- ledit_delete_line_entry_base(buffer, line_inde… | |
+ ledit_buffer_delete_line_entry_base(buffer, li… | |
} else { | |
assert(pl2->start_index + pl2->length - pl1->s… | |
rgl1 = rgl2 = line_index1; | |
rgb1 = pl1->start_index; | |
rgb2 = pl2->start_index + pl2->length; | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
t@@ -1046,19 +1102,19 @@ ledit_delete_range_base( | |
if (l2 == softlines - 1 && line_index1 < buffe… | |
new_line = line_index1 + 1; | |
ledit_x_softline_to_pos( | |
- ledit_get_line(buffer, line_index1… | |
+ ledit_buffer_get_line(buffer, line… | |
x, 0, &new_byte | |
); | |
} else if (l2 < softlines - 1) { | |
new_line = line_index1; | |
ledit_x_softline_to_pos( | |
- ledit_get_line(buffer, line_index1… | |
+ ledit_buffer_get_line(buffer, line… | |
x, l1, &new_byte | |
); | |
} else if (l1 > 0) { | |
new_line = line_index1; | |
ledit_x_softline_to_pos( | |
- ledit_get_line(buffer, line_index1… | |
+ ledit_buffer_get_line(buffer, line… | |
x, l1 - 1, &new_byte | |
); | |
} else { | |
t@@ -1081,8 +1137,8 @@ ledit_delete_range_base( | |
l2 = line_index1; | |
b2 = byte_index1; | |
} | |
- ledit_line *ll1 = ledit_get_line(buffer, l1); | |
- ledit_line *ll2 = ledit_get_line(buffer, l2); | |
+ ledit_line *ll1 = ledit_buffer_get_line(buffer, l1); | |
+ ledit_line *ll2 = ledit_buffer_get_line(buffer, l2); | |
normalize_and_set_pango_text(ll1); | |
normalize_and_set_pango_text(ll2); | |
pango_layout_index_to_line_x(ll1->layout, b1, 0, &sl1,… | |
t@@ -1097,20 +1153,20 @@ ledit_delete_range_base( | |
rgb1 = 0; | |
rgb2 = ll2->len; | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtb… | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
delete_line_section_base(buffer, l1, 0… | |
- ledit_delete_line_entries_base(buffer,… | |
+ ledit_buffer_delete_line_entries_base(… | |
new_line = 0; | |
new_byte = 0; | |
} else { | |
if (l2 == buffer->lines_num - 1) { | |
new_line = l1 - 1; | |
- ledit_line *new_lline = ledit_… | |
+ ledit_line *new_lline = ledit_… | |
int new_softlines = pango_layo… | |
ledit_x_softline_to_pos(new_ll… | |
rgl1 = l1 - 1; | |
t@@ -1119,7 +1175,7 @@ ledit_delete_range_base( | |
rgb2 = ll2->len; | |
} else { | |
new_line = l1; | |
- ledit_line *nextline = ledit_g… | |
+ ledit_line *nextline = ledit_b… | |
ledit_x_softline_to_pos( | |
nextline, x, 0, &new_byte | |
); | |
t@@ -1129,13 +1185,13 @@ ledit_delete_range_base( | |
rgb2 = 0; | |
} | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtb… | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
- ledit_delete_line_entries_base(buffer,… | |
+ ledit_buffer_delete_line_entries_base(… | |
} | |
} else if (sl1 == 0) { | |
rgl1 = l1; | |
t@@ -1143,7 +1199,7 @@ ledit_delete_range_base( | |
rgl2 = l2; | |
rgb2 = pl2->start_index + pl2->length; | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
t@@ -1152,7 +1208,7 @@ ledit_delete_range_base( | |
delete_line_section_base(buffer, l2, 0, pl2->s… | |
new_line = l1; | |
ledit_x_softline_to_pos(ll2, x, 0, &new_byte); | |
- ledit_delete_line_entries_base(buffer, l1, l2 … | |
+ ledit_buffer_delete_line_entries_base(buffer, … | |
} else if (sl2 == softlines - 1) { | |
rgl1 = l1; | |
rgb1 = pl1->start_index; | |
t@@ -1164,19 +1220,19 @@ ledit_delete_range_base( | |
} else { | |
new_line = l1 + 1; | |
ledit_x_softline_to_pos( | |
- ledit_get_line(buffer, l2 + 1), | |
+ ledit_buffer_get_line(buffer, l2 +… | |
x, 0, &new_byte | |
); | |
} | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
delete_line_section_base(buffer, l1, pl1->star… | |
- ledit_delete_line_entries_base(buffer, l1 + 1,… | |
+ ledit_buffer_delete_line_entries_base(buffer, … | |
} else { | |
/* FIXME: this could be made nicer by just usi… | |
delete all in one go at the end */ | |
t@@ -1185,20 +1241,20 @@ ledit_delete_range_base( | |
rgl2 = l2; | |
rgb2 = pl2->start_index + pl2->length; | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
delete_line_section_base(buffer, l1, pl1->star… | |
- ledit_insert_text_from_line_base( | |
+ 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… | |
); | |
- ledit_delete_line_entries_base(buffer, l1 + 1,… | |
+ ledit_buffer_delete_line_entries_base(buffer, … | |
new_line = l1; | |
int new_softlines = pango_layout_get_line_coun… | |
/* it's technically possible that the remainin… | |
t@@ -1223,7 +1279,7 @@ ledit_delete_range_base( | |
rgb2 = byte_index1; | |
} | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
t@@ -1245,24 +1301,24 @@ ledit_delete_range_base( | |
rgb2 = byte_index1; | |
} | |
if (text_ret) { | |
- ledit_copy_text_to_lbuf( | |
+ ledit_buffer_copy_text_to_txtbuf( | |
buffer, text_ret, | |
rgl1, rgb1, | |
rgl2, rgb2 | |
); | |
} | |
- ledit_line *line1 = ledit_get_line(buffer, rgl1); | |
- ledit_line *line2 = ledit_get_line(buffer, rgl2); | |
+ 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->le… | |
- ledit_insert_text_from_line_base( | |
+ ledit_buffer_insert_text_from_line_base( | |
buffer, rgl1, rgb1, rgl2, rgb2, line2->len - rgb2,… | |
); | |
new_line = rgl1; | |
new_byte = rgb1; | |
- ledit_delete_line_entries_base(buffer, rgl1 + 1, rgl2); | |
+ ledit_buffer_delete_line_entries_base(buffer, rgl1 + 1… | |
} | |
- if (buffer->state->mode == NORMAL) | |
- new_byte = ledit_get_legal_normal_pos(buffer, new_line… | |
+ if (buffer->common->mode == NORMAL) | |
+ new_byte = ledit_buffer_get_legal_normal_pos(buffer, n… | |
} | |
if (final_range_ret) { | |
final_range_ret->line1 = rgl1; | |
t@@ -1275,3 +1331,340 @@ ledit_delete_range_base( | |
if (new_byte_ret) | |
*new_byte_ret = new_byte; | |
} | |
+ | |
+/* FIXME */ | |
+void | |
+ledit_buffer_resize_textview(ledit_buffer *buffer) { | |
+ buffer->total_height = 0; | |
+ int tmp_w, tmp_h; | |
+ 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); | |
+ pango_layout_set_width(line->layout, text_w * PANGO_SCALE); | |
+ pango_layout_get_pixel_size(line->layout, &tmp_w, &tmp_h); | |
+ line->h = tmp_h; | |
+ line->w = text_w; | |
+ line->y_offset = buffer->total_height; | |
+ line->dirty = 1; | |
+ buffer->total_height += tmp_h; | |
+ } | |
+ ledit_window_set_scroll_max(buffer->window, buffer->total_height); | |
+ if (buffer->display_offset > 0 && | |
+ buffer->display_offset + text_h >= buffer->total_height) { | |
+ buffer->display_offset = buffer->total_height - text_h; | |
+ if (buffer->display_offset < 0) | |
+ buffer->display_offset = 0; | |
+ ledit_window_set_scroll_pos(buffer->window, buffer->display_of… | |
+ } | |
+} | |
+ | |
+void | |
+ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int *line_ret, int *… | |
+ /* 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 … | |
+ int index, trailing; | |
+ pango_layout_xy_to_index( | |
+ line->layout, | |
+ x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE, | |
+ &index, &trailing | |
+ ); | |
+ while (trailing > 0) { | |
+ trailing--; | |
+ index = ledit_line_next_utf8(line, index); | |
+ } | |
+ *line_ret = i; | |
+ *byte_ret = index; | |
+ break; | |
+ } | |
+ h += line->h; | |
+ } | |
+} | |
+ | |
+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); | |
+ pango_layout_get_cursor_pos( | |
+ line->layout, buffer->cur_index, &strong, &weak | |
+ ); | |
+ long cursor_y = strong.y / PANGO_SCALE + line->y_offset; | |
+ if (cursor_y < buffer->display_offset) { | |
+ buffer->display_offset = cursor_y; | |
+ } else if (cursor_y + strong.height / PANGO_SCALE > | |
+ buffer->display_offset + text_h) { | |
+ buffer->display_offset = | |
+ cursor_y - text_h + strong.height / PANGO_SCALE; | |
+ } | |
+ ledit_window_set_scroll_pos(buffer->window, buffer->display_offset); | |
+} | |
+ | |
+static void | |
+swap(int *a, int *b) { | |
+ int tmp = *a; | |
+ *a = *b; | |
+ *b = tmp; | |
+} | |
+ | |
+static void | |
+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 li… | |
+ /* FIXME: let window handle this */ | |
+ txtbuf *primary = ledit_window_get_primary_clipboard_buffer(); | |
+ ledit_buffer_copy_text_to_txtbuf(buffer, primary, line1, byte1, line2,… | |
+ XSetSelectionOwner(buffer->common->dpy, XA_PRIMARY, buffer->window->xw… | |
+ /* | |
+ FIXME | |
+ if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win) | |
+ selclear(); | |
+ */ | |
+} | |
+ | |
+void | |
+ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int lin… | |
+ if (line1 == buffer->sel.line1 && line2 == buffer->sel.line2 && | |
+ byte1 == buffer->sel.byte1 && byte2 == buffer->sel.byte2) { | |
+ return; | |
+ } | |
+ if (buffer->sel.line1 >= 0) { | |
+ int l1_new = line1, l2_new = line2; | |
+ int b1_new = byte1, b2_new = byte2; | |
+ sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer… | |
+ sort_selection(&l1_new, &b1_new, &l2_new, &b2_new); | |
+ if (buffer->sel.line1 > l2_new || buffer->sel.line2 < l1_new) { | |
+ for (int i = buffer->sel.line1; i <= buffer->sel.line2… | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, i); | |
+ } | |
+ } else { | |
+ for (int i = buffer->sel.line1; i < l1_new; i++) { | |
+ 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 == l2_new) { | |
+ ledit_buffer_set_line_selection(buffer, l1_new, b1_new… | |
+ } else { | |
+ ledit_line *ll1 = ledit_buffer_get_line(buffer, l1_new… | |
+ ledit_buffer_set_line_selection(buffer, l1_new, b1_new… | |
+ ledit_buffer_set_line_selection(buffer, l2_new, 0, b2_… | |
+ /* FIXME: optimize this */ | |
+ for (int i = l1_new + 1; i < l2_new; i++) { | |
+ if (i <= buffer->sel.line1 || i >= buffer->sel… | |
+ ledit_line *llx = ledit_buffer_get_lin… | |
+ ledit_buffer_set_line_selection(buffer… | |
+ } | |
+ } | |
+ } | |
+ copy_selection_to_x_primary(buffer, l1_new, b1_new, l2_new, b2… | |
+ } | |
+ 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; | |
+ switch (event->type) { | |
+ case ButtonPress: | |
+ ledit_xy_to_line_byte(buffer, x, y, &l, &b); | |
+ ledit_buffer_set_selection(buffer, l, b, l, b); | |
+ if (buffer->common->mode == NORMAL) { | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cu… | |
+ /* FIXME: only set mode after dragging/when something … | |
+ /* -> return to old mode afterwards? */ | |
+ /* should change_mode_group even be called here? */ | |
+ ledit_buffer_set_mode(buffer, VISUAL); | |
+ } | |
+ buffer->cur_line = l; | |
+ buffer->cur_index = b; | |
+ buffer->selecting = 1; | |
+ 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, &l, &b); | |
+ ledit_buffer_set_selection(buffer, buffer->sel.line1, … | |
+ 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) { | |
+ if (line->dirty || line->cache_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; | |
+ } | |
+ ledit_cache_pixmap *pix = ledit_get_cache_pixmap( | |
+ buffer->cache, line->cache_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-… | |
+ PangoRectangle strong, weak; | |
+ ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line); | |
+ pango_layout_get_cursor_pos( | |
+ cur_line->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 && buffer->cur_index == cur… | |
+ XFillRectangle( | |
+ buffer->common->dpy, buffer->window->drawable, buf… | |
+ strong.x / PANGO_SCALE, cursor_y, | |
+ 10, strong.height / PANGO_SCALE | |
+ ); | |
+ } else if (buffer->common->mode == INSERT || buffer->common->m… | |
+ XDrawLine( | |
+ buffer->common->dpy, buffer->window->drawable, buf… | |
+ strong.x / PANGO_SCALE, cursor_y, | |
+ strong.x / PANGO_SCALE, | |
+ (strong.y + strong.height) / PANGO_SCALE + cur_lin… | |
+ ); | |
+ } | |
+ } | |
+ /* 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… | |
+} | |
+ | |
+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, … | |
+} | |
+ | |
+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_l… | |
+ ); | |
+ 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 … | |
+ /* 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_l… | |
+ ); | |
+ 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 … | |
+ /* FIXME: show undo message */ | |
+} | |
+ | |
+static void | |
+paste_callback(void *data, char *text, int len) { | |
+ ledit_buffer *buffer = (ledit_buffer *)data; | |
+ ledit_buffer_insert_text_with_newlines( | |
+ buffer, buffer->cur_line, buffer->cur_index, | |
+ text, len, &buffer->cur_line, &buffer->cur_index | |
+ ); | |
+} | |
+ | |
+/* 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, buffe… | |
+ clipboard_paste_clipboard(buffer->window); | |
+} | |
+ | |
+void | |
+ledit_buffer_paste_primary(ledit_buffer *buffer) { | |
+ ledit_window_set_paste_callback(buffer->window, &paste_callback, buffe… | |
+ clipboard_paste_primary(buffer->window); | |
+} | |
diff --git a/buffer.h b/buffer.h | |
t@@ -1,10 +1,3 @@ | |
-typedef struct { | |
- int line1; | |
- int byte1; | |
- int line2; | |
- int byte2; | |
-} ledit_range; | |
- | |
typedef struct ledit_buffer ledit_buffer; | |
/* FIXME: size_t for len, etc. */ | |
t@@ -27,8 +20,9 @@ typedef struct { | |
/* TODO: advisory lock on file? also check if modification date changed before… | |
struct ledit_buffer { | |
- ledit_common_state *state; /* general state, e.g. display, window, etc… | |
+ 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 */ | |
t@@ -41,37 +35,40 @@ struct ledit_buffer { | |
long total_height; /* total pixel height of all lines */ | |
double display_offset; /* current pixel offset of viewport - this | |
* is a double to make scrolling smoother */ | |
+ 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 *ledit_create_buffer(ledit_common_state *state); | |
-int ledit_load_file_into_buffer(ledit_buffer *buffer, char *filename, int line… | |
-int ledit_write_buffer_to_file(ledit_buffer *buffer, char *filename, char **er… | |
-void ledit_destroy_buffer(ledit_buffer *buffer); | |
-void ledit_normalize_line(ledit_line *line); | |
-void ledit_set_line_selection(ledit_buffer *buffer, int line, int start_byte, … | |
-void ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index); | |
-void ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line); | |
-void ledit_render_line(ledit_buffer *buffer, int line_index); | |
-ledit_line *ledit_get_line(ledit_buffer *buffer, int index); | |
-int ledit_line_visible(ledit_buffer *buffer, int index); | |
-int ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos); | |
+ledit_buffer *ledit_buffer_create(ledit_common *common, ledit_theme *theme, le… | |
+int ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, cha… | |
+int ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **er… | |
+void ledit_buffer_destroy(ledit_buffer *buffer); | |
+void ledit_buffer_normalize_line(ledit_line *line); | |
+void ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start… | |
+void ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int in… | |
+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_line *line, int pos, int *x_ret, int *softl… | |
void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_r… | |
-int ledit_next_utf8(ledit_line *line, int index); | |
-int ledit_prev_utf8(ledit_line *line, int index); | |
-size_t ledit_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, in… | |
-void ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, in… | |
-void ledit_copy_text_to_lbuf( | |
+int ledit_line_next_utf8(ledit_line *line, int index); | |
+int ledit_line_prev_utf8(ledit_line *line, int index); | |
+size_t ledit_buffer_textlen(ledit_buffer *buffer, int line1, int byte1, int li… | |
+void ledit_buffer_copy_text(ledit_buffer *buffer, char *dst, int line1, int by… | |
+void ledit_buffer_copy_text_to_txtbuf( | |
ledit_buffer *buffer, | |
- lbuf *buf, /* oh, isn't that a very non-confusing name? */ | |
+ txtbuf *buf, /* oh, isn't that a very non-confusing name? */ | |
int line1, int byte1, | |
int line2, int byte2 | |
); | |
-void ledit_recalc_line(ledit_buffer *buffer, int line); | |
-void ledit_recalc_from_line(ledit_buffer *buffer, int line); | |
-void ledit_recalc_all_lines(ledit_buffer *buffer); | |
+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 | |
t@@ -80,52 +77,66 @@ void ledit_recalc_all_lines(ledit_buffer *buffer); | |
* - The non-base versions call the appropriate recalc function in order to | |
* keep everything in a consistent state. */ | |
-void ledit_insert_text_base(ledit_buffer *buffer, int line_index, int index, c… | |
-void ledit_insert_text_with_newlines_base( | |
+void ledit_buffer_insert_text_base(ledit_buffer *buffer, int line_index, int i… | |
+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 | |
); | |
-void ledit_append_line_base(ledit_buffer *buffer, int line_index, int text_ind… | |
-void ledit_delete_line_entries_base(ledit_buffer *buffer, int index1, int inde… | |
-void ledit_delete_line_entry_base(ledit_buffer *buffer, int index); | |
-int ledit_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int b… | |
-void ledit_delete_range_base( | |
+void ledit_buffer_append_line_base(ledit_buffer *buffer, int line_index, int t… | |
+void ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, i… | |
+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… | |
+void ledit_buffer_delete_range_base( | |
ledit_buffer *buffer, int line_based, | |
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, lbuf *text_ret | |
+ ledit_range *final_range_ret, txtbuf *text_ret | |
); | |
-void ledit_insert_text_from_line_base( | |
+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, | |
- lbuf *text_ret | |
+ txtbuf *text_ret | |
); | |
-void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *… | |
-void ledit_insert_text_with_newlines( | |
+void ledit_buffer_insert_text(ledit_buffer *buffer, int line_index, int index,… | |
+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 | |
); | |
-void ledit_append_line(ledit_buffer *buffer, int line_index, int text_index); | |
-void ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2); | |
-void ledit_delete_line_entry(ledit_buffer *buffer, int index); | |
-int ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_i… | |
-void ledit_delete_range( | |
+void ledit_buffer_append_line(ledit_buffer *buffer, int line_index, int text_i… | |
+void ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int in… | |
+void ledit_buffer_delete_line_entry(ledit_buffer *buffer, int index); | |
+int ledit_buffer_delete_unicode_char(ledit_buffer *buffer, int line_index, int… | |
+void ledit_buffer_delete_range( | |
ledit_buffer *buffer, int line_based, | |
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, lbuf *text_ret | |
+ ledit_range *final_range_ret, txtbuf *text_ret | |
); | |
-void ledit_insert_text_from_line( | |
+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, | |
- lbuf *text_ret | |
+ txtbuf *text_ret | |
); | |
+ | |
+void ledit_buffer_resize_width(ledit_buffer *buffer, int width); | |
+void ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int *line_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, in… | |
+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); | |
diff --git a/cache.c b/cache.c | |
t@@ -6,94 +6,76 @@ | |
#include "common.h" | |
#include "memory.h" | |
-#include "lbuf.h" | |
-#include "buffer.h" | |
#include "cache.h" | |
- | |
-static struct { | |
- ledit_common_state *state; | |
- ledit_cache_pixmap *entries; | |
- int entries_num; | |
- int cur_replace_index; | |
-} cache = {NULL, NULL, 0, -1}; | |
- | |
-void | |
-ledit_init_cache(ledit_common_state *state) { | |
- cache.state = state; | |
+ledit_cache * | |
+ledit_cache_create(ledit_common *common) { | |
+ (void)common; /* FIXME: remove argument */ | |
/* FIXME: prevent overflow */ | |
- cache.entries = ledit_malloc(20 * sizeof(ledit_cache_pixmap)); | |
+ ledit_cache *cache = ledit_malloc(sizeof(ledit_cache)); | |
+ cache->entries = ledit_malloc(20 * sizeof(ledit_cache_pixmap)); | |
for (int i = 0; i < 20; i++) { | |
- cache.entries[i].pixmap = None; | |
- cache.entries[i].draw = NULL; | |
- cache.entries[i].line = -1; | |
+ cache->entries[i].pixmap = None; | |
+ cache->entries[i].draw = NULL; | |
+ cache->entries[i].line = -1; | |
} | |
- cache.entries_num = 20; | |
- cache.cur_replace_index = -1; | |
+ cache->entries_num = 20; | |
+ cache->cur_replace_index = -1; | |
+ return cache; | |
} | |
void | |
-ledit_flush_cache(void) { | |
- for (int i = 0; i < cache.entries_num; i++) { | |
- cache.entries[i].line = -1; | |
+ledit_cache_flush(ledit_cache *cache) { | |
+ for (int i = 0; i < cache->entries_num; i++) { | |
+ cache->entries[i].line = -1; | |
} | |
} | |
void | |
-ledit_destroy_cache(void) { | |
- for (int i = 0; i < cache.entries_num; i++) { | |
- if (cache.entries[i].pixmap != None) | |
- XFreePixmap(cache.state->dpy, cache.entries[i].pixmap); | |
- if (cache.entries[i].draw != NULL) | |
- XftDrawDestroy(cache.entries[i].draw); | |
+ledit_cache_destroy(ledit_cache *cache) { | |
+ for (int i = 0; i < cache->entries_num; i++) { | |
+ if (cache->entries[i].pixmap != None) | |
+ XFreePixmap(cache->dpy, cache->entries[i].pixmap); | |
+ if (cache->entries[i].draw != NULL) | |
+ XftDrawDestroy(cache->entries[i].draw); | |
} | |
- free(cache.entries); | |
- cache.state = NULL; | |
- cache.entries = NULL; | |
- cache.entries_num = 0; | |
- cache.cur_replace_index = -1; | |
+ free(cache->entries); | |
+ free(cache); | |
} | |
-void | |
-ledit_assign_free_cache_index(ledit_buffer *buffer, int new_line_index) { | |
+/* returns a cache index that is currently not needed (if needed, the cache si… | |
+ whether it is needed or not is checked with line_needed, to which | |
+ callback_data is always passed as the first argument */ | |
+int | |
+ledit_get_unneeded_cache_index(ledit_cache *cache, void *callback_data, int (*… | |
int entry_index; | |
int line_index; | |
- ledit_line *line; | |
/* start at 1 because the cache->cur_replace_index is actually the las… | |
- for (int i = 1; i <= cache.entries_num; i++) { | |
- entry_index = (i + cache.cur_replace_index) % cache.entries_nu… | |
- line_index = cache.entries[entry_index].line; | |
+ for (int i = 1; i <= cache->entries_num; i++) { | |
+ entry_index = (i + cache->cur_replace_index) % cache->entries_… | |
+ line_index = cache->entries[entry_index].line; | |
/* replace line when entry isn't assigned or currently assigne… | |
if (line_index == -1 || | |
(line_index >= 0 && | |
- !ledit_line_visible(buffer, line_index))) { | |
- if (line_index >= 0) { | |
- line = ledit_get_line(buffer, line_index); | |
- line->cache_index = -1; | |
- } | |
- cache.entries[entry_index].line = new_line_index; | |
- cache.cur_replace_index = entry_index; | |
- line = ledit_get_line(buffer, new_line_index); | |
- line->cache_index = entry_index; | |
- return; | |
+ !line_needed(callback_data, line_index))) { | |
+ cache->cur_replace_index = entry_index; | |
+ return entry_index; | |
} | |
} | |
/* no free entry found, increase cache size */ | |
- cache.entries = ledit_realloc(cache.entries, cache.entries_num * 2 * s… | |
- entry_index = cache.entries_num; | |
- for (int i = cache.entries_num; i < cache.entries_num * 2; i++) { | |
- cache.entries[i].line = -1; | |
- cache.entries[i].pixmap = None; | |
- cache.entries[i].draw = NULL; | |
+ cache->entries = ledit_realloc(cache->entries, cache->entries_num * 2 … | |
+ entry_index = cache->entries_num; | |
+ for (int i = cache->entries_num; i < cache->entries_num * 2; i++) { | |
+ cache->entries[i].line = -1; | |
+ cache->entries[i].pixmap = None; | |
+ cache->entries[i].draw = NULL; | |
} | |
- cache.entries_num *= 2; | |
- cache.entries[entry_index].line = new_line_index; | |
- line = ledit_get_line(buffer, new_line_index); | |
- line->cache_index = entry_index; | |
+ cache->entries_num *= 2; | |
+ return entry_index; | |
} | |
ledit_cache_pixmap * | |
-ledit_get_cache_pixmap(int index) { | |
- return &cache.entries[index]; | |
+ledit_get_cache_pixmap(ledit_cache *cache, int index) { | |
+ return &cache->entries[index]; | |
} | |
diff --git a/cache.h b/cache.h | |
t@@ -5,8 +5,15 @@ typedef struct { | |
int line; | |
} ledit_cache_pixmap; | |
-void ledit_init_cache(ledit_common_state *state); | |
-void ledit_flush_cache(void); | |
-void ledit_destroy_cache(void); | |
-ledit_cache_pixmap *ledit_get_cache_pixmap(int index); | |
-void ledit_assign_free_cache_index(ledit_buffer *buffer, int line); | |
+typedef struct { | |
+ Display *dpy; | |
+ ledit_cache_pixmap *entries; | |
+ int entries_num; | |
+ int cur_replace_index; | |
+} ledit_cache; | |
+ | |
+ledit_cache *ledit_cache_create(ledit_common *common); | |
+void ledit_cache_flush(ledit_cache *cache); | |
+void ledit_cache_destroy(ledit_cache *cache); | |
+ledit_cache_pixmap *ledit_get_cache_pixmap(ledit_cache *cache, int index); | |
+int ledit_get_unneeded_cache_index(ledit_cache *cache, void *callback_data, in… | |
diff --git a/commands.c b/commands.c | |
t@@ -1,168 +0,0 @@ | |
-/* FIXME: Parse commands properly and allow combinations of commands */ | |
-#include <ctype.h> | |
-#include <stdlib.h> | |
-#include <X11/Xlib.h> | |
-#include <X11/Xutil.h> | |
-#include <pango/pangoxft.h> | |
-#include <X11/extensions/Xdbe.h> | |
- | |
-#include "memory.h" | |
-#include "common.h" | |
-#include "lbuf.h" | |
-#include "buffer.h" | |
- | |
-static int | |
-handle_write(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
- (void)buffer; | |
- (void)cmd; | |
- (void)l1; | |
- (void)l2; | |
- /* FIXME: Implement properly; handle error */ | |
- char *errstr; | |
- if (buffer->filename) | |
- ledit_write_buffer_to_file(buffer, buffer->filename, &errstr); | |
- return 0; | |
-} | |
- | |
-static int | |
-handle_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
- (void)buffer; | |
- (void)cmd; | |
- (void)l1; | |
- (void)l2; | |
- /* FIXME: Implement */ | |
- exit(1); | |
- return 0; | |
-} | |
- | |
-static int | |
-handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
- (void)buffer; | |
- (void)cmd; | |
- (void)l1; | |
- (void)l2; | |
- printf("write quit\n"); | |
- return 0; | |
-} | |
- | |
-static int | |
-handle_substitute(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
- (void)buffer; | |
- (void)cmd; | |
- (void)l1; | |
- (void)l2; | |
- printf("substitute\n"); | |
- return 0; | |
-} | |
- | |
-enum cmd_type { | |
- CMD_NORMAL, | |
- CMD_OPTIONAL_RANGE | |
-}; | |
- | |
-static const struct { | |
- char *cmd; | |
- enum cmd_type type; | |
- int (*handler)(ledit_buffer *buffer, char *cmd, int l1, int l2); | |
-} cmds[] = { | |
- {"wq", CMD_OPTIONAL_RANGE, &handle_write_quit}, | |
- {"w", CMD_OPTIONAL_RANGE, &handle_write}, | |
- {"q", CMD_NORMAL, &handle_quit}, | |
- {"s", CMD_OPTIONAL_RANGE, &handle_substitute} | |
-}; | |
- | |
-#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
- | |
-/* | |
-. current line - FIXME: implement | |
-$ last line | |
-% all lines | |
-*/ | |
- | |
-/* FIXME: ACTUALLY USE LEN!!! */ | |
-static int | |
-parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *lin… | |
- (void)len; | |
- enum { | |
- START_LINENO = 1, | |
- START_RANGE = 2, | |
- IN_RANGE = 4, | |
- IN_LINENO = 8 | |
- } s = START_LINENO | START_RANGE; | |
- int l1 = -1, l2 = -1; | |
- char *c = cmd; | |
- for (; *c != '\0'; c++) { | |
- if (isdigit(*c)) { | |
- /* FIXME: integer overflow */ | |
- if (s & IN_LINENO) { | |
- if (l2 != -1) | |
- l2 = l2 * 10 + (*c - '0'); | |
- else | |
- l1 = l1 * 10 + (*c - '0'); | |
- } else if ((s & START_LINENO) && (s & START_RANGE)) { | |
- l1 = *c - '0'; | |
- s &= ~START_RANGE; | |
- s &= ~START_LINENO; | |
- s |= IN_RANGE | IN_LINENO; | |
- } else if ((s & START_LINENO)) { | |
- l2 = *c - '0'; | |
- s &= ~START_LINENO; | |
- s |= IN_LINENO; | |
- } | |
- } else if (*c == ',' && !(s & START_RANGE)) { | |
- if (l1 != -1 && l2 != -1) { | |
- return 1; | |
- } else { | |
- s |= START_LINENO; | |
- s &= ~IN_LINENO; | |
- } | |
- } else if (*c == '%') { | |
- if (s & START_RANGE) { | |
- l1 = 1; | |
- l2 = buffer->lines_num; | |
- break; | |
- } else { | |
- return 1; | |
- } | |
- } else if (*c == '$') { | |
- if (s & START_LINENO) { | |
- if (l1 == -1) | |
- l1 = buffer->lines_num; | |
- else | |
- l2 = buffer->lines_num; | |
- s &= ~START_LINENO; | |
- s &= ~IN_LINENO; | |
- } else { | |
- return 1; | |
- } | |
- } else { | |
- break; | |
- } | |
- } | |
- if ((l1 == -1 || l2 == -1) && !(s & START_RANGE)) | |
- return 1; | |
- *cmd_ret = c; | |
- *line1_ret = l1; | |
- *line2_ret = l2; | |
- return 0; | |
-} | |
- | |
-int | |
-ledit_handle_cmd(ledit_buffer *buffer, char *cmd, int len) { | |
- if (len < 0) | |
- len = strlen(cmd); | |
- if (len < 1) | |
- return 1; | |
- char *c; | |
- int l1, l2; | |
- if (parse_range(buffer, cmd, len, &c, &l1, &l2)) | |
- return 1; | |
- int range_given = l1 != -1 && l2 != -1; | |
- 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 1; | |
-} | |
diff --git a/commands.h b/commands.h | |
t@@ -1 +0,0 @@ | |
-int ledit_handle_cmd(ledit_buffer *buffer, char *cmd, int len); | |
diff --git a/common.h b/common.h | |
t@@ -1,41 +1,22 @@ | |
-/* FIXME: it's ugly to put this here */ | |
-typedef struct ledit_undo_stack ledit_undo_stack; | |
+typedef struct { | |
+ int line1; | |
+ int byte1; | |
+ int line2; | |
+ int byte2; | |
+} ledit_range; | |
+ | |
enum ledit_mode { | |
NORMAL = 1, | |
INSERT = 2, | |
- VISUAL = 4, | |
- COMMANDEDIT = 8, | |
- SEARCHEDIT = 16 | |
+ VISUAL = 4 | |
}; | |
typedef struct { | |
Display *dpy; | |
- PangoFontMap *fontmap; | |
- PangoContext *context; | |
- PangoFontDescription *font; | |
Visual *vis; | |
- GC gc; | |
- Window win; | |
- XdbeBackBuffer back_buf; | |
- Drawable drawable; | |
Colormap cm; | |
int screen; | |
int depth; | |
- int w; | |
- int h; | |
- int text_w; | |
- int text_h; | |
- int scroll_dragging; | |
- int scroll_grab_handle; | |
- int selecting; | |
- int bottom_text_shown; | |
- int message_shown; | |
+ int redraw; | |
enum ledit_mode mode; | |
- XIM xim; | |
- XIC xic; | |
- XftColor fg; | |
- XftColor bg; | |
- XftColor scroll_bg; | |
- XSetWindowAttributes wattrs; | |
- Atom wm_delete_msg; | |
-} ledit_common_state; | |
+} ledit_common; | |
diff --git a/keys.c b/keys.c | |
t@@ -0,0 +1,69 @@ | |
+#include <limits.h> | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <X11/keysym.h> | |
+#include <X11/XF86keysym.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+ | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "txtbuf.h" | |
+#include "theme.h" | |
+#include "window.h" | |
+#include "keys.h" | |
+ | |
+static char *key_langs[] = { | |
+ "English (US)", | |
+ "German", | |
+ "Urdu (Pakistan)", | |
+ "Hindi (Bolnagri)" | |
+}; | |
+ | |
+int | |
+get_language_index(char *lang) { | |
+ for (size_t i = 0; i < LENGTH(key_langs); i++) { | |
+ if (!strcmp(key_langs[i], lang)) { | |
+ return i; | |
+ } | |
+ } | |
+ return -1; | |
+} | |
+ | |
+/* FIXME: Does this break anything? */ | |
+static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Ma… | |
+#define XK_ANY_MOD UINT_MAX | |
+ | |
+int | |
+match_key(unsigned int mask, unsigned int state) | |
+{ | |
+ return mask == XK_ANY_MOD || mask == (state & importantmod); | |
+} | |
+ | |
+void | |
+preprocess_key( | |
+ ledit_window *window, XEvent *event, KeySym *sym_ret, | |
+ char *buf_ret, int buf_size, int *buf_len_ret) { | |
+ /* | |
+ * FIXME: I don't understand how key handling works with different key… | |
+ * If, for instance, you run "setxkbmap pk" and then type "Ctrl+c", the | |
+ * keysym will be "0x1000686, Arabic_tcheh" and the appropriate string… | |
+ * be returned by XmbLookupString (tested also using xev). | |
+ * If, however, you run "setxkbmap -layout "us,pk" -option grp:shifts_… | |
+ * and type "Ctrl+c" after switching to the pk layout, the keysym will… | |
+ * be "0x63, c" and XmbLookupString will return the control code sent … | |
+ * Ctrl+c (ascii 03). This means the handling is different depending o… | |
+ * the keymap is switched to, and I have no clue what the proper way to | |
+ * handle this would be, since the shortcuts are explicitly supposed t… | |
+ * properly in all language maps. My current solution is to just ignor… | |
+ * control key in the state so the text found by Xutf8LookupString is … | |
+ * and the modifier mask can be checked separately. Please tell me if … | |
+ * know the proper way to do this. | |
+ */ | |
+ event->xkey.state &= ~ControlMask; | |
+ /* FIXME: X_HAVE_UTF8_STRING See XmbLookupString(3) */ | |
+ *buf_len_ret = Xutf8LookupString( | |
+ window->xic, &event->xkey, buf_ret, buf_size, sym_ret, NULL | |
+ ); | |
+} | |
diff --git a/keys.h b/keys.h | |
t@@ -0,0 +1,23 @@ | |
+#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
+ | |
+/* IMPORTANT: Also edit key_langs in keys.c when changing languages! */ | |
+ | |
+#define GEN_KEY_ARRAY(key_struct, en, de, ur, hi) \ | |
+static struct { \ | |
+ key_struct *keys; \ | |
+ int num_keys; \ | |
+} keys[] = { \ | |
+ {en, LENGTH(en)}, \ | |
+ {de, LENGTH(de)}, \ | |
+ {ur, LENGTH(ur)}, \ | |
+ {hi, LENGTH(hi)} \ | |
+} | |
+ | |
+#define LANG_KEYS(index) &keys[index] | |
+ | |
+int get_language_index(char *lang); | |
+int match_key(unsigned int mask, unsigned int state); | |
+void preprocess_key( | |
+ ledit_window *window, XEvent *event, KeySym *sym_ret, | |
+ char *buf_ret, int buf_size, int *buf_len_ret | |
+); | |
diff --git a/keys_basic.c b/keys_basic.c | |
t@@ -0,0 +1,937 @@ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+#include <X11/keysym.h> | |
+#include <X11/XF86keysym.h> | |
+#include <X11/cursorfont.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" | |
+#include "search.h" | |
+ | |
+#include "keys.h" | |
+#include "action.h" | |
+#include "keys_basic.h" | |
+#include "keys_command.h" | |
+#include "keys_basic_config.h" | |
+ | |
+/* this is supposed to be global for all buffers */ | |
+txtbuf *paste_buffer = NULL; | |
+ | |
+struct key_stack_elem { | |
+ enum key_type key; | |
+ enum key_type followup; /* allowed keys to complete the keybinding */ | |
+ /* callback function that motion commands call to complete a command - | |
+ * line and char_pos already include the repetition stored in this sta… | |
+ * element; type is the type of motion command (used to determine if | |
+ * the command should operate on lines or chars) */ | |
+ void (*motion_cb)(ledit_buffer *buffer, int line, int char_pos, enum k… | |
+ int count; /* number of repetitions */ | |
+ int data1; /* misc. data 1 */ | |
+ int data2; /* misc. data 2 */ | |
+}; | |
+ | |
+static struct { | |
+ size_t len, alloc; | |
+ struct key_stack_elem *stack; | |
+} key_stack = {0, 0, NULL}; | |
+ | |
+static struct key_stack_elem *push_key_stack(void); | |
+static struct key_stack_elem *peek_key_stack(void); | |
+static struct key_stack_elem *pop_key_stack(void); | |
+void clear_key_stack(void); | |
+ | |
+static void move_cursor_left_right(ledit_buffer *buffer, int dir); | |
+static void move_cursor_up_down(ledit_buffer *buffer, int dir); | |
+static void push_num(int num); | |
+static void key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_ty… | |
+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 | |
+); | |
+ | |
+/* FIXME: move to common */ | |
+static void | |
+swap(int *a, int *b) { | |
+ int tmp = *a; | |
+ *a = *b; | |
+ *b = tmp; | |
+} | |
+ | |
+static struct key_stack_elem * | |
+push_key_stack(void) { | |
+ struct key_stack_elem *e; | |
+ if (key_stack.len >= key_stack.alloc) { | |
+ size_t new_alloc = key_stack.alloc > 0 ? key_stack.alloc * 2 :… | |
+ key_stack.stack = ledit_realloc( | |
+ key_stack.stack, new_alloc * sizeof(struct key_stack_elem) | |
+ ); | |
+ key_stack.alloc = new_alloc; | |
+ } | |
+ e = &key_stack.stack[key_stack.len]; | |
+ e->key = KEY_NONE; | |
+ e->followup = KEY_NONE; | |
+ e->motion_cb = NULL; | |
+ e->count = 0; | |
+ e->data1 = 0; | |
+ e->data2 = 0; | |
+ key_stack.len++; | |
+ return &key_stack.stack[key_stack.len - 1]; | |
+} | |
+ | |
+/* Note: for peek and pop, the returned element is only valid | |
+ * until the next element is pushed */ | |
+/* Note on the note: that's not entirely true for peek */ | |
+static struct key_stack_elem * | |
+peek_key_stack(void) { | |
+ if (key_stack.len > 0) | |
+ return &key_stack.stack[key_stack.len - 1]; | |
+ return NULL; | |
+} | |
+ | |
+static struct key_stack_elem * | |
+pop_key_stack(void) { | |
+ if (key_stack.len > 0) { | |
+ key_stack.len--; | |
+ return &key_stack.stack[key_stack.len]; | |
+ } | |
+ return NULL; | |
+} | |
+ | |
+void | |
+clear_key_stack(void) { | |
+ key_stack.len = 0; | |
+} | |
+ | |
+/* get the new line and softline when moving 'movement' softlines up or | |
+ 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_line *line = ledit_buffer_get_line(buffer, cur_line); | |
+ int x, softline; | |
+ pango_layout_index_to_line_x(line->layout, cur_index, 0, &softline, &x… | |
+ if (movement > 0) { | |
+ int softlines = pango_layout_get_line_count(line->layout); | |
+ 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) { | |
+ line = ledit_buffer_get_line(buffer, endline); | |
+ softlines = pango_layout_get_line_count(line->… | |
+ movement -= softlines; | |
+ endline++; | |
+ } | |
+ endline--; | |
+ if (movement <= 0) { | |
+ *new_softline_ret = movement + softlines - 1; | |
+ } else { | |
+ *new_softline_ret = softlines - 1; | |
+ } | |
+ *new_line_ret = endline; | |
+ } | |
+ } else if (movement < 0) { | |
+ int softlines = 0; | |
+ if (softline + movement >= 0) { | |
+ *new_line_ret = cur_line; | |
+ *new_softline_ret = softline + movement; | |
+ } else { | |
+ movement += softline; | |
+ int endline = cur_line - 1; | |
+ while (movement < 0 && endline >= 0) { | |
+ line = ledit_buffer_get_line(buffer, endline); | |
+ softlines = pango_layout_get_line_count(line->… | |
+ movement += softlines; | |
+ endline--; | |
+ } | |
+ endline++; | |
+ if (movement >= 0) { | |
+ *new_softline_ret = movement; | |
+ } else { | |
+ *new_softline_ret = 0; | |
+ } | |
+ *new_line_ret = endline; | |
+ } | |
+ } else { | |
+ *new_line_ret = cur_line; | |
+ *new_softline_ret = softline; | |
+ } | |
+} | |
+ | |
+/* FIXME: don't overwrite buffer->cur_line, etc. here? */ | |
+static void | |
+delete_range( | |
+ ledit_buffer *buffer, | |
+ int line_based, int selected, | |
+ int line_index1, int byte_index1, | |
+ int line_index2, int byte_index2) { | |
+ (void)selected; /* FIXME */ | |
+ if (!paste_buffer) | |
+ paste_buffer = txtbuf_new(); | |
+ ledit_range cur_range, del_range; | |
+ cur_range.line1 = buffer->cur_line; | |
+ cur_range.byte1 = buffer->cur_index; | |
+ ledit_buffer_delete_range( | |
+ buffer, line_based, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2, | |
+ &buffer->cur_line, &buffer->cur_index, | |
+ &del_range, paste_buffer | |
+ ); | |
+ cur_range.line2 = buffer->cur_line; | |
+ cur_range.byte2 = buffer->cur_index; | |
+ ledit_push_undo_delete( | |
+ buffer->undo, paste_buffer, del_range, cur_range, 1, buffer->commo… | |
+ ); | |
+} | |
+ | |
+static void | |
+insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, in… | |
+ if (len < 0) | |
+ len = strlen(text); | |
+ /* FIXME: this is kind of hacky... */ | |
+ txtbuf ins_buf = {.text = text, .len = len, .cap = len}; | |
+ ledit_range cur_range, del_range; | |
+ cur_range.line1 = buffer->cur_line; | |
+ cur_range.byte1 = buffer->cur_index; | |
+ del_range.line1 = line; | |
+ del_range.byte1 = index; | |
+ ledit_buffer_insert_text_with_newlines( | |
+ buffer, line, index, text, len, | |
+ &buffer->cur_line, &buffer->cur_index | |
+ ); | |
+ cur_range.line2 = buffer->cur_line; | |
+ cur_range.byte2 = buffer->cur_index; | |
+ del_range.line2 = buffer->cur_line; | |
+ del_range.byte2 = buffer->cur_index; | |
+ ledit_push_undo_insert( | |
+ buffer->undo, &ins_buf, del_range, cur_range, start_group, buffer-… | |
+ ); | |
+} | |
+ | |
+static int | |
+delete_selection(ledit_buffer *buffer) { | |
+ if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buf… | |
+ delete_range( | |
+ buffer, 0, 0, | |
+ buffer->sel.line1, buffer->sel.byte1, | |
+ buffer->sel.line2, buffer->sel.byte2 | |
+ ); | |
+ /* 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); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static struct action | |
+key_d(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ int num = 0; | |
+ if (delete_selection(buffer)) { | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ buffer->cur_index = ledit_buffer_get_legal_normal_pos(buffer, … | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, b… | |
+ clear_key_stack(); | |
+ } else { | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ if (e != NULL) { | |
+ if (e->key & KEY_NUMBER) { | |
+ num = e->count; | |
+ e = pop_key_stack(); | |
+ } | |
+ /* FIXME: checking equality of the function pointer ma… | |
+ if (e != NULL && e->motion_cb == &key_d_cb) { | |
+ int prevnum = e->count > 0 ? e->count : 1; | |
+ num = num > 0 ? num : 1; | |
+ int lines = num * prevnum; | |
+ int new_line, new_softline; | |
+ get_new_line_softline( | |
+ buffer, buffer->cur_line, buffer->cur_inde… | |
+ lines - 1, &new_line, &new_softline | |
+ ); | |
+ ledit_line *ll = ledit_buffer_get_line(buffer,… | |
+ PangoLayoutLine *pl = pango_layout_get_line_re… | |
+ e->motion_cb(buffer, new_line, pl->start_index… | |
+ clear_key_stack(); | |
+ } else if (e != NULL) { | |
+ clear_key_stack(); | |
+ } | |
+ } | |
+ if (e == NULL) { | |
+ e = push_key_stack(); | |
+ e->key = KEY_MOTION; /* ? */ | |
+ e->count = num; | |
+ e->motion_cb = &key_d_cb; | |
+ } | |
+ } | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+/* FIXME: should this get number of lines to remove or actual end line? */ | |
+static void | |
+key_d_cb(ledit_buffer *buffer, int line, int 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, | |
+ line, char_pos | |
+ ); | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
+} | |
+ | |
+static struct action | |
+key_x(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ int num = 1; | |
+ clear_key_stack(); | |
+ if (e && !(e->key & KEY_NUMBER)) | |
+ return (struct action){ACTION_NONE, NULL}; | |
+ if (e) | |
+ num = e->count; | |
+ if (num <= 0) | |
+ num = 1; | |
+ /* FIXME: actually do something */ | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static void | |
+push_num(int num) { | |
+ struct key_stack_elem *e = peek_key_stack(); | |
+ if (!e || !(e->key & KEY_NUMBER)) { | |
+ e = push_key_stack(); | |
+ e->key = KEY_NUMBER; | |
+ e->followup = KEY_NUMBER|KEY_NUMBERALLOWED; | |
+ } | |
+ /* FIXME: error (overflow) checking */ | |
+ e->count *= 10; | |
+ e->count += num; | |
+} | |
+ | |
+static struct action | |
+push_0(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(0); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_1(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(1); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_2(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(2); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_3(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(3); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_4(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(4); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_5(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(5); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_6(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(6); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_7(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(7); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_8(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(8); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+push_9(ledit_buffer *buffer, char *text, int len) { | |
+ (void)buffer; | |
+ (void)text; | |
+ (void)len; | |
+ push_num(9); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+backspace(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ if (delete_selection(buffer)) { | |
+ /* NOP */ | |
+ } else if (buffer->cur_index == 0) { | |
+ if (buffer->cur_line != 0) { | |
+ ledit_line *l1 = ledit_buffer_get_line(buffer, buffer-… | |
+ delete_range(buffer, 0, 0, buffer->cur_line - 1, l1->l… | |
+ } | |
+ } 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… | |
+ } | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+delete_key(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line); | |
+ if (delete_selection(buffer)) { | |
+ /* 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-… | |
+ } | |
+ } else { | |
+ int i = ledit_line_next_utf8(cur_line, buffer->cur_index); | |
+ delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index… | |
+ } | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static void | |
+move_cursor_left_right(ledit_buffer *buffer, int dir) { | |
+ int num = 1; | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ if (e != NULL) { | |
+ if (e->key & KEY_NUMBER) { | |
+ num = e->count > 0 ? e->count : 0; | |
+ e = pop_key_stack(); | |
+ } | |
+ if (e != NULL) | |
+ num *= (e->count > 0 ? e->count : 1); | |
+ } | |
+ | |
+ /* FIXME: trailing */ | |
+ int trailing = 0, tmp_index; | |
+ ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line); | |
+ int new_index = buffer->cur_index, last_index = buffer->cur_index; | |
+ while (num > 0) { | |
+ tmp_index = new_index; | |
+ pango_layout_move_cursor_visually( | |
+ cur_line->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; | |
+ /* when in normal mode, the cursor cannot be at the very end | |
+ of the line because it's always covering a character */ | |
+ if (new_index >= cur_line->len) { | |
+ if (buffer->common->mode == NORMAL && (e == NULL || e->motion_… | |
+ new_index = last_index; | |
+ } else { | |
+ /* FIXME: I guess this is unnecessary */ | |
+ new_index = cur_line->len; | |
+ } | |
+ } | |
+ if (e != NULL && e->motion_cb != NULL) { | |
+ e->motion_cb(buffer, buffer->cur_line, new_index, KEY_MOTION_C… | |
+ } else { | |
+ buffer->cur_index = new_index; | |
+ if (buffer->common->mode == VISUAL) { | |
+ ledit_buffer_set_selection(buffer, buffer->sel.line1, … | |
+ } 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, n… | |
+ } else if (buffer->common->mode == NORMAL) { | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur… | |
+ } | |
+ } | |
+ clear_key_stack(); | |
+} | |
+ | |
+static struct action | |
+cursor_left(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ move_cursor_left_right(buffer, -1); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+cursor_right(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ move_cursor_left_right(buffer, 1); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+return_key(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ int start_group = 1; | |
+ if (delete_selection(buffer)) | |
+ start_group = 0; | |
+ insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, sta… | |
+ /* 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->c… | |
+ buffer->cur_index = 0; */ | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+/* FIXME: Check earliest version of other used functions to get minimum pango … | |
+ for ledit as a whole */ | |
+/* 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) | |
+static PangoLayoutRun * | |
+pango_layout_line_get_run(PangoLayoutLine *line, int index) { | |
+ GSList *run_list; | |
+ | |
+ run_list = line->runs; | |
+ while (run_list) { | |
+ PangoLayoutRun *run = run_list->data; | |
+ | |
+ if (run->item->offset <= index && run->item->offset + run->ite… | |
+ return run; | |
+ | |
+ run_list = run_list->next; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+static int | |
+pango_layout_line_get_char_level(PangoLayoutLine *line, int index) { | |
+ PangoLayoutRun *run; | |
+ | |
+ run = pango_layout_line_get_run(line, index); | |
+ | |
+ if (run) | |
+ return run->item->analysis.level; | |
+ | |
+ return 0; | |
+} | |
+ | |
+static PangoDirection | |
+pango_layout_line_get_char_direction(PangoLayoutLine *layout_line, int index) { | |
+ return pango_layout_line_get_char_level(layout_line, index) % 2 | |
+ ? PANGO_DIRECTION_RTL | |
+ : PANGO_DIRECTION_LTR; | |
+} | |
+ | |
+static PangoDirection | |
+pango_layout_get_direction(PangoLayout *layout, int index) { | |
+ int lineno, x; | |
+ PangoLayoutLine *line; | |
+ pango_layout_index_to_line_x(layout, index, 0, &lineno, &x); | |
+ line = pango_layout_get_line_readonly(layout, lineno); | |
+ | |
+ if (line) | |
+ return pango_layout_line_get_char_direction(line, index); | |
+ | |
+ return PANGO_DIRECTION_LTR; | |
+} | |
+#endif | |
+ | |
+static struct action | |
+escape_key(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ clear_key_stack(); /* just in case... */ | |
+ if (buffer->common->mode == INSERT && | |
+ (buffer->sel.line1 != buffer->sel.line2 || | |
+ buffer->sel.byte1 != buffer->sel.byte2)) { | |
+ ledit_buffer_set_mode(buffer, VISUAL); | |
+ } else { | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ clear_key_stack(); | |
+ PangoDirection dir = PANGO_DIRECTION_RTL; | |
+ int tmp_index = buffer->cur_index; | |
+ ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->c… | |
+ if (buffer->cur_index >= cur_line->len) | |
+ tmp_index--; | |
+ if (tmp_index >= 0) | |
+ dir = pango_layout_get_direction(cur_line->layout, tmp… | |
+ if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_… | |
+ cursor_right(buffer, NULL, 0); | |
+ } else { | |
+ cursor_left(buffer, NULL, 0); | |
+ } | |
+ if (buffer->sel.line1 != buffer->sel.line2) { | |
+ int min = buffer->sel.line1 < buffer->sel.line2 ? buff… | |
+ int max = buffer->sel.line1 > buffer->sel.line2 ? buff… | |
+ for (int i = min; i <= max; i++) { | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, i); | |
+ } | |
+ } | |
+ /* FIXME: optimize this to avoid first wiping and then setting… | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, b… | |
+ } | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+enter_insert(ledit_buffer *buffer, char *text, int 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); | |
+ clear_key_stack(); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+/* FIXME: Check if previous key allows motion command - or is this checked aut… | |
+static void | |
+move_cursor_up_down(ledit_buffer *buffer, int dir) { | |
+ int new_line, new_softline; | |
+ | |
+ int num = 1; | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ if (e != NULL) { | |
+ if (e->key & KEY_NUMBER) { | |
+ num = e->count > 0 ? e->count : 0; | |
+ e = pop_key_stack(); | |
+ } | |
+ if (e != NULL) | |
+ num *= (e->count > 0 ? e->count : 1); | |
+ } | |
+ num *= dir; | |
+ | |
+ get_new_line_softline( | |
+ buffer, buffer->cur_line, buffer->cur_index, | |
+ num, &new_line, &new_softline | |
+ ); | |
+ | |
+ ledit_line *cur_lline = ledit_buffer_get_line(buffer, buffer->cur_line… | |
+ ledit_line *new_lline = ledit_buffer_get_line(buffer, new_line); | |
+ if (e != NULL && e->motion_cb != NULL) { | |
+ PangoLayoutLine *pl = pango_layout_get_line_readonly(new_lline… | |
+ e->motion_cb(buffer, new_line, pl->start_index, KEY_MOTION_LIN… | |
+ } else { | |
+ int lineno, x; | |
+ ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lin… | |
+ ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->c… | |
+ if (buffer->cur_line != new_line) | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cu… | |
+ buffer->cur_line = new_line; | |
+ | |
+ if (buffer->common->mode == VISUAL) { | |
+ ledit_buffer_set_selection(buffer, buffer->sel.line1, … | |
+ } 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, b… | |
+ } else if (buffer->common->mode == NORMAL) { | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur… | |
+ } | |
+ } | |
+ clear_key_stack(); | |
+} | |
+ | |
+static struct action | |
+cursor_down(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ move_cursor_up_down(buffer, 1); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+cursor_up(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ move_cursor_up_down(buffer, -1); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+cursor_to_beginning(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ struct key_stack_elem *e = pop_key_stack(); | |
+ /* FIXME: error when no callback? */ | |
+ if (e != NULL && e->motion_cb != NULL) { | |
+ e->motion_cb(buffer, buffer->cur_line, 0, KEY_MOTION_CHAR); | |
+ } else { | |
+ buffer->cur_index = 0; | |
+ if (buffer->common->mode == VISUAL) { | |
+ ledit_buffer_set_selection( | |
+ buffer, | |
+ buffer->sel.line1, buffer->sel.byte1, | |
+ buffer->cur_line, buffer->cur_index | |
+ ); | |
+ } else { | |
+ ledit_buffer_set_line_cursor_attrs( | |
+ buffer, buffer->cur_line, buffer->cur_index | |
+ ); | |
+ } | |
+ } | |
+ clear_key_stack(); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+enter_visual(ledit_buffer *buffer, char *text, int 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); | |
+ 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) { | |
+ (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; | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+#define XK_ANY_MOD UINT_MAX | |
+#define XK_NO_MOD 0 | |
+ | |
+static struct action | |
+enter_commandedit(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ ledit_window_set_bottom_bar_text(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); | |
+ return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; | |
+} | |
+ | |
+static struct action | |
+enter_searchedit_forward(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ ledit_window_set_bottom_bar_text(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); | |
+ return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; | |
+} | |
+ | |
+static struct action | |
+enter_searchedit_backward(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ ledit_window_set_bottom_bar_text(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); | |
+ return (struct action){ACTION_GRABKEY, &ledit_command_key_handler}; | |
+} | |
+ | |
+/* FIXME: support visual mode, i.e. change selection to new place? */ | |
+static struct action | |
+key_search_next(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ search_next(buffer); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+key_search_prev(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ search_prev(buffer); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+show_line(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ int textlen = snprintf(NULL, 0, "Line %d of %d", buffer->cur_line + 1,… | |
+ char *str = ledit_malloc(textlen + 1); | |
+ snprintf(str, textlen + 1, "Line %d of %d", buffer->cur_line + 1, buff… | |
+ ledit_window_show_message(buffer->window, str, textlen); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+/* FIXME: return status! */ | |
+static struct action | |
+undo(ledit_buffer *buffer, char *text, int 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->c… | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+redo(ledit_buffer *buffer, char *text, int 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->c… | |
+ 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); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+clipcopy(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ /* FIXME: abstract this through buffer */ | |
+ clipboard_primary_to_clipboard(buffer->window); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+static struct action | |
+clippaste(ledit_buffer *buffer, char *text, int len) { | |
+ (void)text; | |
+ (void)len; | |
+ ledit_buffer_paste_clipboard(buffer); | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
+ | |
+struct action | |
+basic_key_handler(ledit_buffer *buffer, 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); | |
+ | |
+ int found = 0; | |
+ struct key_stack_elem *e = peek_key_stack(); | |
+ /* FIXME: only hide when actually necessary */ | |
+ ledit_window_hide_message(buffer->window); | |
+ struct action act; | |
+ for (int i = 0; i < num_keys; i++) { | |
+ if (cur_keys[i].text) { | |
+ if (n > 0 && | |
+ (cur_keys[i].modes & buffer->common->mode) && | |
+ (!e || (e->key & cur_keys[i].prev_keys)) && | |
+ ((!strncmp(cur_keys[i].text, buf, n) && | |
+ match_key(cur_keys[i].mods, key_state & ~ShiftM… | |
+ cur_keys[i].text[0] == '\0')) { | |
+ /* FIXME: seems a bit hacky to remove shift, b… | |
+ is needed to make keys that use shift match… | |
+ act = cur_keys[i].func(buffer, buf, n); | |
+ found = 1; | |
+ break; | |
+ } | |
+ } else if ((cur_keys[i].modes & buffer->common->mode) && | |
+ cur_keys[i].keysym == sym && | |
+ match_key(cur_keys[i].mods, key_state)) { | |
+ act = cur_keys[i].func(buffer, buf, n); | |
+ found = 1; | |
+ break; | |
+ } | |
+ } | |
+ /* FIXME: only do this when necessary */ | |
+ if (found) { | |
+ ledit_buffer_ensure_cursor_shown(buffer); | |
+ return act; | |
+ } else { | |
+ /* FIXME: maybe show error */ | |
+ return (struct action){ACTION_NONE, NULL}; | |
+ } | |
+} | |
diff --git a/keys_basic.h b/keys_basic.h | |
t@@ -0,0 +1 @@ | |
+struct action basic_key_handler(ledit_buffer *buffer, XEvent *event, int lang_… | |
diff --git a/keys_basic_config.h b/keys_basic_config.h | |
t@@ -0,0 +1,144 @@ | |
+enum key_type { | |
+ KEY_NONE = 0, | |
+ KEY_MISC = 1, | |
+ KEY_CHAR = 2, | |
+ KEY_MOTION_CHAR = 4, | |
+ KEY_MOTION_LINE = 8, | |
+ KEY_MOTION = 4|8, | |
+ KEY_NUMBER = 16, | |
+ KEY_NUMBERALLOWED = 32, | |
+ KEY_ACTIONCALLBACK = 64, | |
+ KEY_ANY = 0xFF | |
+}; | |
+ | |
+struct key { | |
+ char *text; /* for keys that c… | |
+ unsigned int mods; /* modifier mask */ | |
+ KeySym keysym; /* for other keys,… | |
+ enum ledit_mode modes; /* modes in which … | |
+ enum key_type prev_keys; /* allowed previou… | |
+ enum key_type key_types; /* key types - use… | |
+ struct action (*func)(ledit_buffer *, char *, int); /* callback functi… | |
+}; | |
+ | |
+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… | |
+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 key_x(ledit_buffer *buffer, char *text, int len); | |
+static struct action key_d(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, in… | |
+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 l… | |
+static struct action enter_searchedit_backward(ledit_buffer *buffer, char *tex… | |
+static struct action enter_searchedit_forward(ledit_buffer *buffer, char *text… | |
+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,… | |
+ | |
+/* FIXME: maybe sort these and use binary search | |
+ -> but that would mess with the catch-all keys */ | |
+static struct key keys_en[] = { | |
+ {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
+ {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_lef… | |
+ {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_ri… | |
+ {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
+ {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_dow… | |
+ {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
+ {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
+ {NULL, 0, XK_Escape, VISUAL|INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
+ {"i", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
+ {"h", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
+ {"l", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
+ {"j", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
+ {"k", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
+ {"0", 0, 0, NORMAL|VISUAL, ~KEY_NUMBER, KEY_ANY, &cursor_to_beginning… | |
+ {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_NUMBER, &push_0}, | |
+ {"1", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_1}, | |
+ {"2", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_2}, | |
+ {"3", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_3}, | |
+ {"4", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_4}, | |
+ {"5", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_5}, | |
+ {"6", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_6}, | |
+ {"7", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_7}, | |
+ {"8", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_8}, | |
+ {"9", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_9}, | |
+ {"x", 0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &key_x}, | |
+ {"d", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &ke… | |
+ {"v", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual}, | |
+ {"o", 0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end}, | |
+ {"c", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy}, | |
+ {"v", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste}, | |
+ {"g", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &show_line}, | |
+ {":", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_commandedit}, | |
+ {"?", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_backwa… | |
+ {"/", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_forwar… | |
+ {"n", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &key_search_next}, | |
+ {"N", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &key_search_prev}, | |
+ {"u", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &undo}, | |
+ {"U", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &redo}, | |
+ {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &undo}, | |
+ {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &redo}, | |
+ {"", 0, 0, INSERT, KEY_ANY, KEY_ANY, &insert_mode_insert_text} | |
+}; | |
+ | |
+static struct key keys_de[] = { | |
+}; | |
+ | |
+static struct key keys_ur[] = { | |
+ {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
+ {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, | |
+ {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, | |
+ {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
+ {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, | |
+ {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
+ {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
+ {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
+ {"ی", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
+ {"Ø", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
+ {"Ù„", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
+ {"ج", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
+ {"Ú©", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
+ {"0", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning}, | |
+ {"Ú†", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy} | |
+}; | |
+ | |
+static struct key keys_hi[] = { | |
+ {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
+ {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, | |
+ {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, | |
+ {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
+ {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, | |
+ {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
+ {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
+ {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
+ {"ि", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
+ {"ह", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
+ {"ल", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
+ {"ज", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
+ {"क", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
+ {"0", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning} | |
+}; | |
+ | |
+GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_ur, keys_hi); | |
diff --git a/keys_command.c b/keys_command.c | |
t@@ -0,0 +1,325 @@ | |
+/* FIXME: Parse commands properly and allow combinations of commands */ | |
+#include <stdio.h> | |
+#include <ctype.h> | |
+#include <stdlib.h> | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/extensions/Xdbe.h> | |
+#include <X11/keysym.h> | |
+#include <X11/XF86keysym.h> | |
+#include <X11/cursorfont.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" | |
+#include "search.h" | |
+ | |
+#include "keys.h" | |
+#include "action.h" | |
+#include "keys_command.h" | |
+#include "keys_command_config.h" | |
+ | |
+/* 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; | |
+ | |
+void | |
+ledit_command_set_type(enum ledit_command_type type) { | |
+ cur_type = type; | |
+} | |
+ | |
+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_re… | |
+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; | |
+ (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); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+handle_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
+ (void)buffer; | |
+ (void)cmd; | |
+ (void)l1; | |
+ (void)l2; | |
+ /* FIXME: Implement */ | |
+ exit(1); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+handle_write_quit(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
+ (void)buffer; | |
+ (void)cmd; | |
+ (void)l1; | |
+ (void)l2; | |
+ printf("write quit\n"); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+handle_substitute(ledit_buffer *buffer, char *cmd, int l1, int l2) { | |
+ (void)buffer; | |
+ (void)cmd; | |
+ (void)l1; | |
+ (void)l2; | |
+ printf("substitute\n"); | |
+ return 0; | |
+} | |
+ | |
+enum cmd_type { | |
+ CMD_NORMAL, | |
+ CMD_OPTIONAL_RANGE | |
+}; | |
+ | |
+static const struct { | |
+ char *cmd; | |
+ enum cmd_type type; | |
+ int (*handler)(ledit_buffer *buffer, char *cmd, int l1, int l2); | |
+} cmds[] = { | |
+ {"wq", CMD_OPTIONAL_RANGE, &handle_write_quit}, | |
+ {"w", CMD_OPTIONAL_RANGE, &handle_write}, | |
+ {"q", CMD_NORMAL, &handle_quit}, | |
+ {"s", CMD_OPTIONAL_RANGE, &handle_substitute} | |
+}; | |
+ | |
+/* | |
+. current line - FIXME: implement | |
+$ last line | |
+% all lines | |
+*/ | |
+ | |
+/* FIXME: ACTUALLY USE LEN!!! */ | |
+static int | |
+parse_range(ledit_buffer *buffer, char *cmd, int len, char **cmd_ret, int *lin… | |
+ (void)len; | |
+ enum { | |
+ START_LINENO = 1, | |
+ START_RANGE = 2, | |
+ IN_RANGE = 4, | |
+ IN_LINENO = 8 | |
+ } s = START_LINENO | START_RANGE; | |
+ int l1 = -1, l2 = -1; | |
+ char *c = cmd; | |
+ for (; *c != '\0'; c++) { | |
+ if (isdigit(*c)) { | |
+ /* FIXME: integer overflow */ | |
+ if (s & IN_LINENO) { | |
+ if (l2 != -1) | |
+ l2 = l2 * 10 + (*c - '0'); | |
+ else | |
+ l1 = l1 * 10 + (*c - '0'); | |
+ } else if ((s & START_LINENO) && (s & START_RANGE)) { | |
+ l1 = *c - '0'; | |
+ s &= ~START_RANGE; | |
+ s &= ~START_LINENO; | |
+ s |= IN_RANGE | IN_LINENO; | |
+ } else if ((s & START_LINENO)) { | |
+ l2 = *c - '0'; | |
+ s &= ~START_LINENO; | |
+ s |= IN_LINENO; | |
+ } | |
+ } else if (*c == ',' && !(s & START_RANGE)) { | |
+ if (l1 != -1 && l2 != -1) { | |
+ return 1; | |
+ } else { | |
+ s |= START_LINENO; | |
+ s &= ~IN_LINENO; | |
+ } | |
+ } else if (*c == '%') { | |
+ if (s & START_RANGE) { | |
+ l1 = 1; | |
+ l2 = buffer->lines_num; | |
+ break; | |
+ } else { | |
+ return 1; | |
+ } | |
+ } else if (*c == '$') { | |
+ if (s & START_LINENO) { | |
+ if (l1 == -1) | |
+ l1 = buffer->lines_num; | |
+ else | |
+ l2 = buffer->lines_num; | |
+ s &= ~START_LINENO; | |
+ s &= ~IN_LINENO; | |
+ } else { | |
+ return 1; | |
+ } | |
+ } else { | |
+ break; | |
+ } | |
+ } | |
+ if ((l1 == -1 || l2 == -1) && !(s & START_RANGE)) | |
+ return 1; | |
+ *cmd_ret = c; | |
+ *line1_ret = l1; | |
+ *line2_ret = l2; | |
+ return 0; | |
+} | |
+ | |
+static int | |
+handle_cmd(ledit_buffer *buffer, char *cmd, int len) { | |
+ if (len < 0) | |
+ len = strlen(cmd); | |
+ if (len < 1) | |
+ return 0; | |
+ char *c; | |
+ int l1, l2; | |
+ /* FIXME: show error msg here */ | |
+ if (parse_range(buffer, cmd, len, &c, &l1, &l2)) | |
+ return 0; | |
+ int range_given = l1 != -1 && l2 != -1; | |
+ 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 0; | |
+} | |
+ | |
+static int | |
+substitute_yes(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)buffer; | |
+ (void)key_text; | |
+ (void)len; | |
+ return 1; | |
+} | |
+ | |
+static int | |
+substitute_yes_all(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)buffer; | |
+ (void)key_text; | |
+ (void)len; | |
+ return 0; | |
+} | |
+ | |
+static int | |
+substitute_no(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)buffer; | |
+ (void)key_text; | |
+ (void)len; | |
+ return 1; | |
+} | |
+ | |
+static int | |
+substitute_no_all(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)buffer; | |
+ (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); | |
+ ledit_window_set_bottom_bar_cursor( | |
+ buffer->window, ledit_window_get_bottom_bar_cursor(buffer->window)… | |
+ ); | |
+ return 1; | |
+} | |
+ | |
+static int | |
+edit_submit(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)key_text; | |
+ (void)len; | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ ledit_window_set_bottom_bar_text_shown(buffer->window, 0); | |
+ /* FIXME: this is hacky */ | |
+ return handle_cmd(buffer, ledit_window_get_bottom_bar_text(buffer->win… | |
+} | |
+ | |
+/* FIXME: support visual mode, i.e. change selection to new place? */ | |
+void | |
+search_next(ledit_buffer *buffer) { | |
+ /* FIXME: avoid this when line doesn't change */ | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ enum ledit_search_state ret = ledit_search_next(buffer, &buffer->cur_l… | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
+ if (ret != SEARCH_NORMAL) | |
+ ledit_window_show_message(buffer->window, ledit_search_state_t… | |
+} | |
+ | |
+void | |
+search_prev(ledit_buffer *buffer) { | |
+ /* FIXME: avoid this when line doesn't change */ | |
+ ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
+ enum ledit_search_state ret = ledit_search_prev(buffer, &buffer->cur_l… | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
+ if (ret != SEARCH_NORMAL) | |
+ ledit_window_show_message(buffer->window, ledit_search_state_t… | |
+} | |
+ | |
+static int | |
+editsearch_submit(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)key_text; | |
+ (void)len; | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ ledit_window_set_bottom_bar_text_shown(buffer->window, 0); | |
+ ledit_set_search_forward(ledit_window_get_bottom_bar_text(buffer->wind… | |
+ search_next(buffer); | |
+ return 0; | |
+} | |
+ | |
+static int | |
+editsearchb_submit(ledit_buffer *buffer, char *key_text, int len) { | |
+ (void)key_text; | |
+ (void)len; | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ ledit_window_set_bottom_bar_text_shown(buffer->window, 0); | |
+ ledit_set_search_backward(ledit_window_get_bottom_bar_text(buffer->win… | |
+ search_next(buffer); | |
+ return 0; | |
+} | |
+ | |
+struct action | |
+ledit_command_key_handler(ledit_buffer *buffer, 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); | |
+ int grabkey = 0; | |
+ for (int i = 0; i < num_keys; i++) { | |
+ if (cur_keys[i].text) { | |
+ if (n > 0 && | |
+ (cur_keys[i].type == cur_type) && | |
+ ((!strncmp(cur_keys[i].text, buf, n) && | |
+ match_key(cur_keys[i].mods, key_state & ~ShiftMa… | |
+ cur_keys[i].text[0] == '\0')) { | |
+ grabkey = cur_keys[i].func(buffer, buf, n); | |
+ break; | |
+ } | |
+ } else if ((cur_keys[i].type == cur_type) && | |
+ (cur_keys[i].keysym == sym) && | |
+ (match_key(cur_keys[i].mods, key_state))) { | |
+ grabkey = cur_keys[i].func(buffer, buf, n); | |
+ break; | |
+ } | |
+ } | |
+ if (grabkey) | |
+ return (struct action){ACTION_GRABKEY, &ledit_command_key_hand… | |
+ else | |
+ return (struct action){ACTION_NONE, NULL}; | |
+} | |
diff --git a/keys_command.h b/keys_command.h | |
t@@ -0,0 +1,15 @@ | |
+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 ledit_command_set_type(enum ledit_command_type type); | |
+struct action ledit_command_key_handler(ledit_buffer *buffer, XEvent *event, i… | |
diff --git a/keys_command_config.h b/keys_command_config.h | |
t@@ -0,0 +1,41 @@ | |
+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_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); | |
+ | |
+struct key { | |
+ char *text; /* for keys that correspond… | |
+ unsigned int mods; /* modifier mask */ | |
+ KeySym keysym; /* for other keys, e.g. arr… | |
+ enum ledit_command_type type; /* substitute, etc. */ | |
+ int (*func)(ledit_buffer *, char *, int); /* callback function */ | |
+}; | |
+ | |
+/* "" means catch-all, i.e. all keys with text are given to that callback */ | |
+static struct key keys_en[] = { | |
+ {"y", 0, 0, CMD_SUBSTITUTE, &substitute_yes}, | |
+ {"Y", 0, 0, CMD_SUBSTITUTE, &substitute_yes_all}, | |
+ {"n", 0, 0, CMD_SUBSTITUTE, &substitute_no}, | |
+ {"N", 0, 0, CMD_SUBSTITUTE, &substitute_no_all}, | |
+ {NULL, 0, XK_Return, CMD_EDIT, &edit_submit}, | |
+ {NULL, 0, XK_Return, CMD_EDITSEARCH, &editsearch_submit}, | |
+ {NULL, 0, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit}, | |
+ {"", 0, 0, CMD_EDIT, &edit_insert_text}, | |
+ {"", 0, 0, CMD_EDITSEARCH, &edit_insert_text}, | |
+ {"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text} | |
+}; | |
+ | |
+static struct key keys_de[] = { | |
+}; | |
+ | |
+static struct key keys_hi[] = { | |
+}; | |
+ | |
+static struct key keys_ur[] = { | |
+}; | |
+ | |
+GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_hi, keys_ur); | |
diff --git a/lbuf.c b/lbuf.c | |
t@@ -1,51 +0,0 @@ | |
-#include <stdlib.h> | |
-#include <string.h> | |
- | |
-#include "memory.h" | |
-#include "lbuf.h" | |
- | |
-lbuf * | |
-lbuf_new(void) { | |
- lbuf *buf = ledit_malloc(sizeof(lbuf)); | |
- buf->text = NULL; | |
- buf->cap = buf->len = 0; | |
- return buf; | |
-} | |
- | |
-void | |
-lbuf_grow(lbuf *buf, size_t sz) { | |
- /* always leave room for extra \0 */ | |
- if (sz + 1 > buf->cap) { | |
- /* FIXME: what are the best values here? */ | |
- buf->cap = buf->cap * 2 > sz + 1 ? buf->cap * 2 : sz + 1; | |
- buf->text = ledit_realloc(buf->text, buf->cap); | |
- } | |
-} | |
- | |
-void | |
-lbuf_shrink(lbuf *buf) { | |
- if ((buf->len + 1) * 4 < buf->cap) { | |
- buf->cap /= 2; | |
- buf->text = ledit_realloc(buf->text, buf->cap); | |
- } | |
-} | |
- | |
-void | |
-lbuf_destroy(lbuf *buf) { | |
- free(buf->text); | |
- free(buf); | |
-} | |
- | |
-void | |
-lbuf_cpy(lbuf *dst, lbuf *src) { | |
- lbuf_grow(dst, src->len); | |
- memcpy(dst->text, src->text, src->len); | |
- dst->len = src->len; | |
-} | |
- | |
-lbuf * | |
-lbuf_dup(lbuf *src) { | |
- lbuf *dst = lbuf_new(); | |
- lbuf_cpy(dst, src); | |
- return dst; | |
-} | |
diff --git a/lbuf.h b/lbuf.h | |
t@@ -1,12 +0,0 @@ | |
-/* FIXME: RENAME THIS */ | |
-typedef struct { | |
- size_t len, cap; | |
- char *text; | |
-} lbuf; | |
- | |
-lbuf *lbuf_new(void); | |
-void lbuf_grow(lbuf *buf, size_t sz); | |
-void lbuf_shrink(lbuf *buf); | |
-void lbuf_destroy(lbuf *buf); | |
-void lbuf_cpy(lbuf *dst, lbuf *src); | |
-lbuf *lbuf_dup(lbuf *src); | |
diff --git a/ledit.c b/ledit.c | |
t@@ -1,3 +1,4 @@ | |
+/* FIXME: Document that everything is assumed to be utf8 */ | |
/* FIXME: Only redraw part of screen if needed */ | |
/* FIXME: overflow in repeated commands */ | |
/* FIXME: Fix lag when scrolling - combine repeated mouse motion events */ | |
t@@ -16,103 +17,30 @@ | |
#include <limits.h> | |
#include <unistd.h> | |
#include <locale.h> | |
+ | |
#include <X11/Xlib.h> | |
#include <X11/Xatom.h> | |
#include <X11/Xutil.h> | |
#include <X11/keysym.h> | |
#include <X11/XF86keysym.h> | |
#include <X11/cursorfont.h> | |
- | |
#include <pango/pangoxft.h> | |
- | |
#include <X11/XKBlib.h> | |
#include <X11/extensions/XKBrules.h> | |
#include <X11/extensions/Xdbe.h> | |
#include "memory.h" | |
#include "common.h" | |
-#include "lbuf.h" | |
-#include "buffer.h" | |
-#include "search.h" | |
+#include "txtbuf.h" | |
+#include "theme.h" | |
+#include "window.h" | |
#include "cache.h" | |
-#include "util.h" | |
#include "undo.h" | |
-#include "commands.h" | |
- | |
-enum key_type { | |
- KEY_NONE = 0, | |
- KEY_MISC = 1, | |
- KEY_CHAR = 2, | |
- KEY_MOTION_CHAR = 4, | |
- KEY_MOTION_LINE = 8, | |
- KEY_MOTION = 4|8, | |
- KEY_NUMBER = 16, | |
- KEY_NUMBERALLOWED = 32, | |
- KEY_ANY = 0xFF | |
-}; | |
- | |
-struct { | |
- /* FIXME: encapsulate layout, width, draw a bit */ | |
- PangoLayout *mode; | |
- ledit_draw *mode_draw; | |
- int mode_w, mode_h; | |
- PangoLayout *ruler; | |
- ledit_draw *ruler_draw; | |
- int ruler_w, ruler_h; | |
- PangoLayout *line; | |
- ledit_draw *line_draw; | |
- int line_w, line_h; | |
- char *line_text; | |
- int line_alloc, line_len; | |
- int line_cur_pos; | |
-} bottom_bar; | |
- | |
-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 function… | |
- enum key_type prev_keys; /* allowed previous keys */ | |
- enum key_type key_types; /* key types - used to determine if the key i… | |
- void (*func)(void); /* callback function */ | |
-}; | |
- | |
-struct key_stack_elem { | |
- enum key_type key; | |
- enum key_type followup; /* allowed keys to complete the keybinding */ | |
- /* callback function that motion commands call to complete a command - | |
- * line and char_pos already include the repetition stored in this sta… | |
- * element; type is the type of motion command (used to determine if | |
- * the command should operate on lines or chars) */ | |
- void (*motion_cb)(int line, int char_pos, enum key_type type); | |
- int count; /* number of repetitions */ | |
- int data1; /* misc. data 1 */ | |
- int data2; /* misc. data 2 */ | |
-}; | |
- | |
-/* buffer for storing yanked text */ | |
-lbuf *paste_buffer = NULL; | |
-/* temporary buffer used for storing text | |
- in order to add it to the undo stack */ | |
-lbuf *tmp_buffer = NULL; | |
- | |
-static struct { | |
- size_t len, alloc; | |
- struct key_stack_elem *stack; | |
-} key_stack; | |
- | |
-static ledit_common_state state; | |
-static ledit_buffer *buffer; | |
- | |
-/* TODO: protect against overflow, especially on repeating commands */ | |
- | |
-static struct key_stack_elem *push_key_stack(void); | |
-static struct key_stack_elem *peek_key_stack(void); | |
-static struct key_stack_elem *pop_key_stack(void); | |
-void clear_key_stack(void); | |
+#include "buffer.h" | |
+#include "action.h" | |
+#include "keys.h" | |
+#include "keys_basic.h" | |
-static void set_scroll_pos(double pos); | |
-static void get_scroll_pos_height(double *pos, double *height); | |
static void resize_window(int w, int h); | |
static void mainloop(void); | |
static void setup(int argc, char *argv[]); | |
t@@ -121,649 +49,23 @@ static void redraw(void); | |
static int button_press(XEvent *event); | |
static int button_release(XEvent *event); | |
static int drag_motion(XEvent *event); | |
-static void ensure_cursor_shown(void); | |
-static void resize_window(int w, int h); | |
- | |
-static void backspace(void); | |
-static void delete_key(void); | |
-static void move_cursor_left_right(int dir); | |
-static void cursor_left(void); | |
-static void cursor_right(void); | |
-static void move_cursor_up_down(int dir); | |
-static void cursor_down(void); | |
-static void cursor_up(void); | |
-static void cursor_to_beginning(void); | |
-static void return_key(void); | |
-static void escape_key(void); | |
-static void enter_insert(void); | |
-static void key_x(void); | |
-static void push_num(int num); | |
-static void push_0(void); | |
-static void push_1(void); | |
-static void push_2(void); | |
-static void push_3(void); | |
-static void push_4(void); | |
-static void push_5(void); | |
-static void push_6(void); | |
-static void push_7(void); | |
-static void push_8(void); | |
-static void push_9(void); | |
-static void key_d(void); | |
-static void key_d_cb(int line, int char_pos, enum key_type type); | |
static void change_keyboard(char *lang); | |
-static void key_press(XEvent event); | |
-static void get_new_line_softline( | |
- int cur_line, int cur_index, int movement, | |
- int *new_line_ret, int *new_softline_ret | |
-); | |
- | |
-#define SCROLLBAR_WIDTH 10 | |
-#define SCROLL_STEP 10 | |
- | |
-/* FIXME: shouldn't state.bottom_text_shown also be true when message_shown? */ | |
-static void | |
-recalc_text_size(void) { | |
- int bar_h = bottom_bar.mode_h; | |
- if ((state.bottom_text_shown || state.message_shown) && bottom_bar.lin… | |
- bar_h = bottom_bar.line_h; | |
- state.text_w = state.w - SCROLLBAR_WIDTH; | |
- state.text_h = state.h - bar_h; | |
-} | |
- | |
-/* FIXME: allow lines longer than window width to be displayed properly */ | |
-static void | |
-insert_bottom_bar_text(char *text, int len) { | |
- assert(len >= -1); | |
- assert(bottom_bar.line_cur_pos <= bottom_bar.line_alloc); | |
- | |
- if (len == -1) | |
- len = strlen(text); | |
- /* \0 not included in len */ | |
- if (bottom_bar.line_len + len + 1 > bottom_bar.line_alloc || bottom_ba… | |
- /* FIXME: read up on what the best values are here */ | |
- bottom_bar.line_alloc = | |
- bottom_bar.line_alloc * 2 > bottom_bar.line_len + len + 1 ? | |
- bottom_bar.line_alloc * 2 : | |
- bottom_bar.line_len + len + 1; | |
- bottom_bar.line_text = ledit_realloc(bottom_bar.line_text, bot… | |
- } | |
- memmove( | |
- bottom_bar.line_text + bottom_bar.line_cur_pos + len, | |
- bottom_bar.line_text + bottom_bar.line_cur_pos, | |
- bottom_bar.line_len - bottom_bar.line_cur_pos | |
- ); | |
- memcpy(bottom_bar.line_text + bottom_bar.line_cur_pos, text, len); | |
- bottom_bar.line_len += len; | |
- bottom_bar.line_text[bottom_bar.line_len] = '\0'; | |
- pango_layout_set_text(bottom_bar.line, bottom_bar.line_text, bottom_ba… | |
- pango_layout_get_pixel_size(bottom_bar.line, &bottom_bar.line_w, &bott… | |
- ledit_grow_draw(&state, bottom_bar.line_draw, bottom_bar.line_w, botto… | |
- XftDrawRect(bottom_bar.line_draw->xftdraw, &state.bg, 0, 0, bottom_bar… | |
- pango_xft_render_layout(bottom_bar.line_draw->xftdraw, &state.fg, bott… | |
- recalc_text_size(); | |
-} | |
- | |
-static void | |
-set_bottom_bar_text(char *text, int len) { | |
- bottom_bar.line_len = 0; | |
- bottom_bar.line_cur_pos = 0; | |
- insert_bottom_bar_text(text, len); | |
-} | |
- | |
-static void | |
-show_message(char *text, int len) { | |
- set_bottom_bar_text(text, len); | |
- /* FIXME: rename these */ | |
- state.bottom_text_shown = 0; | |
- state.message_shown = 2; | |
-} | |
- | |
-/* clipboard handling largely stolen from st (simple terminal) */ | |
- | |
-#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+static void key_press(XEvent *event); | |
-struct { | |
- Atom xtarget; | |
- lbuf *primary; | |
- char *clipboard; | |
-} xsel; | |
- | |
-static void | |
-set_mode(enum ledit_mode mode) { | |
- state.mode = mode; | |
- switch (mode) { | |
- case NORMAL: | |
- pango_layout_set_text(bottom_bar.mode, "Normal", -1); | |
- break; | |
- case VISUAL: | |
- pango_layout_set_text(bottom_bar.mode, "Visual", -1); | |
- break; | |
- case INSERT: | |
- pango_layout_set_text(bottom_bar.mode, "Insert", -1); | |
- break; | |
- default: | |
- pango_layout_set_text(bottom_bar.mode, "ledit is buggy… | |
- break; | |
- } | |
- pango_layout_get_pixel_size(bottom_bar.mode, &bottom_bar.mode_w, &bott… | |
- ledit_grow_draw(&state, bottom_bar.mode_draw, bottom_bar.mode_w, botto… | |
- XftDrawRect(bottom_bar.mode_draw->xftdraw, &state.bg, 0, 0, bottom_bar… | |
- pango_xft_render_layout(bottom_bar.mode_draw->xftdraw, &state.fg, bott… | |
- recalc_text_size(); | |
- ledit_change_mode_group(buffer); | |
-} | |
- | |
-void | |
-clipcopy(void) | |
-{ | |
- Atom clipboard; | |
- | |
- free(xsel.clipboard); | |
- xsel.clipboard = NULL; | |
- | |
- /* FIXME: don't copy if text empty (no selection)? */ | |
- if (xsel.primary->text != NULL) { | |
- xsel.clipboard = ledit_strdup(xsel.primary->text); | |
- clipboard = XInternAtom(state.dpy, "CLIPBOARD", 0); | |
- XSetSelectionOwner(state.dpy, clipboard, state.win, CurrentTim… | |
- } | |
-} | |
- | |
-void | |
-clippaste(void) | |
-{ | |
- Atom clipboard; | |
- | |
- clipboard = XInternAtom(state.dpy, "CLIPBOARD", 0); | |
- XConvertSelection(state.dpy, clipboard, xsel.xtarget, clipboard, | |
- state.win, CurrentTime); | |
-} | |
- | |
-void | |
-selpaste(void) | |
-{ | |
- XConvertSelection(state.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, | |
- state.win, CurrentTime); | |
-} | |
- | |
-void selnotify(XEvent *e); | |
- | |
-void | |
-propnotify(XEvent *e) | |
-{ | |
- XPropertyEvent *xpev; | |
- Atom clipboard = XInternAtom(state.dpy, "CLIPBOARD", 0); | |
- | |
- xpev = &e->xproperty; | |
- if (xpev->state == PropertyNewValue && | |
- (xpev->atom == XA_PRIMARY || | |
- xpev->atom == clipboard)) { | |
- selnotify(e); | |
- } | |
-} | |
- | |
-void | |
-selnotify(XEvent *e) | |
-{ | |
- unsigned long nitems, ofs, rem; | |
- int format; | |
- unsigned char *data; | |
- Atom type, incratom, property = None; | |
- | |
- incratom = XInternAtom(state.dpy, "INCR", 0); | |
- | |
- ofs = 0; | |
- if (e->type == SelectionNotify) { | |
- property = e->xselection.property; | |
- } else if (e->type == PropertyNotify) { | |
- property = e->xproperty.atom; | |
- } | |
- | |
- if (property == None) | |
- return; | |
- | |
- do { | |
- if (XGetWindowProperty(state.dpy, state.win, property, ofs, | |
- BUFSIZ/4, False, AnyPropertyType, | |
- &type, &format, &nitems, &rem, | |
- &data)) { | |
- fprintf(stderr, "Clipboard allocation failed\n"); | |
- return; | |
- } | |
- | |
- if (e->type == PropertyNotify && nitems == 0 && rem == 0) { | |
- /* | |
- * If there is some PropertyNotify with no data, then | |
- * this is the signal of the selection owner that all | |
- * data has been transferred. We won't need to receive | |
- * PropertyNotify events anymore. | |
- */ | |
- MODBIT(state.wattrs.event_mask, 0, PropertyChangeMask); | |
- XChangeWindowAttributes(state.dpy, state.win, CWEventM… | |
- } | |
- | |
- if (type == incratom) { | |
- /* | |
- * Activate the PropertyNotify events so we receive | |
- * when the selection owner sends us the next | |
- * chunk of data. | |
- */ | |
- MODBIT(state.wattrs.event_mask, 1, PropertyChangeMask); | |
- XChangeWindowAttributes(state.dpy, state.win, CWEventM… | |
- | |
- /* | |
- * Deleting the property is the transfer start signal. | |
- */ | |
- XDeleteProperty(state.dpy, state.win, (int)property); | |
- continue; | |
- } | |
- | |
- ledit_insert_text_with_newlines( | |
- buffer, | |
- buffer->cur_line, buffer->cur_index, | |
- (char*)data, (int)(nitems * format / 8), | |
- &buffer->cur_line, &buffer->cur_index | |
- ); | |
- XFree(data); | |
- /* number of 32-bit chunks returned */ | |
- ofs += nitems * format / 32; | |
- } while (rem > 0); | |
- | |
- /* | |
- * Deleting the property again tells the selection owner to send the | |
- * next data chunk in the property. | |
- */ | |
- XDeleteProperty(state.dpy, state.win, (int)property); | |
-} | |
- | |
-void | |
-selrequest(XEvent *e) | |
-{ | |
- XSelectionRequestEvent *xsre; | |
- XSelectionEvent xev; | |
- Atom xa_targets, string, clipboard; | |
- char *seltext; | |
- | |
- xsre = (XSelectionRequestEvent *) e; | |
- xev.type = SelectionNotify; | |
- xev.requestor = xsre->requestor; | |
- xev.selection = xsre->selection; | |
- xev.target = xsre->target; | |
- xev.time = xsre->time; | |
- if (xsre->property == None) | |
- xsre->property = xsre->target; | |
- | |
- /* reject */ | |
- xev.property = None; | |
- | |
- xa_targets = XInternAtom(state.dpy, "TARGETS", 0); | |
- if (xsre->target == xa_targets) { | |
- /* respond with the supported type */ | |
- string = xsel.xtarget; | |
- XChangeProperty(xsre->display, xsre->requestor, xsre->property, | |
- XA_ATOM, 32, PropModeReplace, | |
- (unsigned char *) &string, 1); | |
- xev.property = xsre->property; | |
- } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { | |
- /* | |
- * xith XA_STRING non ascii characters may be incorrect in the | |
- * requestor. It is not our problem, use utf8. | |
- */ | |
- clipboard = XInternAtom(state.dpy, "CLIPBOARD", 0); | |
- if (xsre->selection == XA_PRIMARY) { | |
- seltext = xsel.primary->text; | |
- } else if (xsre->selection == clipboard) { | |
- seltext = xsel.clipboard; | |
- } else { | |
- fprintf(stderr, | |
- "Unhandled clipboard selection 0x%lx\n", | |
- xsre->selection); | |
- return; | |
- } | |
- if (seltext != NULL) { | |
- XChangeProperty(xsre->display, xsre->requestor, | |
- xsre->property, xsre->target, | |
- 8, PropModeReplace, | |
- (unsigned char *)seltext, strlen(selte… | |
- xev.property = xsre->property; | |
- } | |
- } | |
- | |
- /* all done, send a notification to the listener */ | |
- if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) | |
- fprintf(stderr, "Error sending SelectionNotify event\n"); | |
-} | |
- | |
-static void | |
-get_new_line_softline( | |
- int cur_line, int cur_index, int movement, | |
- int *new_line_ret, int *new_softline_ret) { | |
- ledit_line *line = ledit_get_line(buffer, cur_line); | |
- int x, softline; | |
- pango_layout_index_to_line_x(line->layout, cur_index, 0, &softline, &x… | |
- if (movement > 0) { | |
- int softlines = pango_layout_get_line_count(line->layout); | |
- 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) { | |
- line = ledit_get_line(buffer, endline); | |
- softlines = pango_layout_get_line_count(line->… | |
- movement -= softlines; | |
- endline++; | |
- } | |
- endline--; | |
- if (movement <= 0) { | |
- *new_softline_ret = movement + softlines - 1; | |
- } else { | |
- *new_softline_ret = softlines - 1; | |
- } | |
- *new_line_ret = endline; | |
- } | |
- } else if (movement < 0) { | |
- int softlines = 0; | |
- if (softline + movement >= 0) { | |
- *new_line_ret = cur_line; | |
- *new_softline_ret = softline + movement; | |
- } else { | |
- movement += softline; | |
- int endline = cur_line - 1; | |
- while (movement < 0 && endline >= 0) { | |
- line = ledit_get_line(buffer, endline); | |
- softlines = pango_layout_get_line_count(line->… | |
- movement += softlines; | |
- endline--; | |
- } | |
- endline++; | |
- if (movement >= 0) { | |
- *new_softline_ret = movement; | |
- } else { | |
- *new_softline_ret = 0; | |
- } | |
- *new_line_ret = endline; | |
- } | |
- } else { | |
- *new_line_ret = cur_line; | |
- *new_softline_ret = softline; | |
- } | |
-} | |
- | |
-/* FIXME: don't overwrite buffer->cur_line, etc. here? */ | |
-static void | |
-delete_range( | |
- int line_based, int selected, | |
- int line_index1, int byte_index1, | |
- int line_index2, int byte_index2) { | |
- (void)selected; /* FIXME */ | |
- ledit_range cur_range, del_range; | |
- cur_range.line1 = buffer->cur_line; | |
- cur_range.byte1 = buffer->cur_index; | |
- ledit_delete_range( | |
- buffer, line_based, | |
- line_index1, byte_index1, | |
- line_index2, byte_index2, | |
- &buffer->cur_line, &buffer->cur_index, | |
- &del_range, paste_buffer | |
- ); | |
- cur_range.line2 = buffer->cur_line; | |
- cur_range.byte2 = buffer->cur_index; | |
- ledit_push_undo_delete( | |
- buffer, paste_buffer, del_range, cur_range, 1 | |
- ); | |
-} | |
- | |
-static void | |
-insert_text(int line, int index, char *text, int len, int start_group) { | |
- if (len < 0) | |
- len = strlen(text); | |
- /* FIXME: this is kind of hacky... */ | |
- lbuf ins_buf = {.text = text, .len = len, .cap = len}; | |
- ledit_range cur_range, del_range; | |
- cur_range.line1 = buffer->cur_line; | |
- cur_range.byte1 = buffer->cur_index; | |
- del_range.line1 = line; | |
- del_range.byte1 = index; | |
- ledit_insert_text_with_newlines( | |
- buffer, line, index, text, len, | |
- &buffer->cur_line, &buffer->cur_index | |
- ); | |
- cur_range.line2 = buffer->cur_line; | |
- cur_range.byte2 = buffer->cur_index; | |
- del_range.line2 = buffer->cur_line; | |
- del_range.byte2 = buffer->cur_index; | |
- ledit_push_undo_insert( | |
- buffer, &ins_buf, del_range, cur_range, start_group | |
- ); | |
-} | |
- | |
-static int | |
-delete_selection(void) { | |
- if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buf… | |
- delete_range( | |
- 0, 0, | |
- buffer->sel.line1, buffer->sel.byte1, | |
- buffer->sel.line2, buffer->sel.byte2 | |
- ); | |
- /* 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_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- return 1; | |
- } | |
- return 0; | |
-} | |
- | |
-static void | |
-key_d(void) { | |
- int num = 0; | |
- if (delete_selection()) { | |
- set_mode(NORMAL); | |
- buffer->cur_index = ledit_get_legal_normal_pos(buffer, buffer-… | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
- clear_key_stack(); | |
- } else { | |
- struct key_stack_elem *e = pop_key_stack(); | |
- if (e != NULL) { | |
- if (e->key & KEY_NUMBER) { | |
- num = e->count; | |
- e = pop_key_stack(); | |
- } | |
- /* FIXME: checking equality of the function pointer ma… | |
- if (e != NULL && e->motion_cb == &key_d_cb) { | |
- int prevnum = e->count > 0 ? e->count : 1; | |
- num = num > 0 ? num : 1; | |
- int lines = num * prevnum; | |
- int new_line, new_softline; | |
- get_new_line_softline( | |
- buffer->cur_line, buffer->cur_index, lines… | |
- &new_line, &new_softline | |
- ); | |
- ledit_line *ll = ledit_get_line(buffer, new_li… | |
- PangoLayoutLine *pl = pango_layout_get_line_re… | |
- e->motion_cb(new_line, pl->start_index, KEY_MO… | |
- clear_key_stack(); | |
- } else if (e != NULL) { | |
- clear_key_stack(); | |
- } | |
- } | |
- if (e == NULL) { | |
- e = push_key_stack(); | |
- e->key = KEY_MOTION; /* ? */ | |
- e->count = num; | |
- e->motion_cb = &key_d_cb; | |
- } | |
- } | |
-} | |
- | |
-/* FIXME: should this get number of lines to remove or actual end line? */ | |
-static void | |
-key_d_cb(int line, int char_pos, enum key_type type) { | |
- int line_based = type == KEY_MOTION_LINE ? 1 : 0; | |
- delete_range( | |
- line_based, 0, | |
- buffer->cur_line, buffer->cur_index, | |
- line, char_pos | |
- ); | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
-} | |
- | |
-static void | |
-key_x(void) { | |
- struct key_stack_elem *e = pop_key_stack(); | |
- int num = 1; | |
- clear_key_stack(); | |
- if (e && !(e->key & KEY_NUMBER)) | |
- return; | |
- if (e) | |
- num = e->count; | |
- if (num <= 0) | |
- num = 1; | |
- /* FIXME: actually do something */ | |
-} | |
- | |
-static void | |
-push_num(int num) { | |
- struct key_stack_elem *e = peek_key_stack(); | |
- if (!e || !(e->key & KEY_NUMBER)) { | |
- e = push_key_stack(); | |
- e->key = KEY_NUMBER; | |
- e->followup = KEY_NUMBER|KEY_NUMBERALLOWED; | |
- } | |
- /* FIXME: error (overflow) checking */ | |
- e->count *= 10; | |
- e->count += num; | |
-} | |
- | |
-static void | |
-push_0(void) { | |
- push_num(0); | |
-} | |
- | |
-static void | |
-push_1(void) { | |
- push_num(1); | |
-} | |
- | |
-static void | |
-push_2(void) { | |
- push_num(2); | |
-} | |
- | |
-static void | |
-push_3(void) { | |
- push_num(3); | |
-} | |
- | |
-static void | |
-push_4(void) { | |
- push_num(4); | |
-} | |
- | |
-static void | |
-push_5(void) { | |
- push_num(5); | |
-} | |
- | |
-static void | |
-push_6(void) { | |
- push_num(6); | |
-} | |
- | |
-static void | |
-push_7(void) { | |
- push_num(7); | |
-} | |
- | |
-static void | |
-push_8(void) { | |
- push_num(8); | |
-} | |
- | |
-static void | |
-push_9(void) { | |
- push_num(9); | |
-} | |
- | |
-int | |
-main(int argc, char *argv[]) { | |
- setup(argc, argv); | |
- mainloop(); | |
- cleanup(); | |
- | |
- return 0; | |
-} | |
- | |
-static struct key_stack_elem * | |
-push_key_stack(void) { | |
- struct key_stack_elem *e; | |
- if (key_stack.len >= key_stack.alloc) { | |
- size_t new_alloc = key_stack.alloc > 0 ? key_stack.alloc * 2 :… | |
- key_stack.stack = ledit_realloc( | |
- key_stack.stack, new_alloc * sizeof(struct key_stack_elem) | |
- ); | |
- key_stack.alloc = new_alloc; | |
- } | |
- e = &key_stack.stack[key_stack.len]; | |
- e->key = KEY_NONE; | |
- e->followup = KEY_NONE; | |
- e->motion_cb = NULL; | |
- e->count = 0; | |
- e->data1 = 0; | |
- e->data2 = 0; | |
- key_stack.len++; | |
- return &key_stack.stack[key_stack.len - 1]; | |
-} | |
- | |
-/* Note: for peek and pop, the returned element is only valid | |
- * until the next element is pushed */ | |
-/* Note on the note: that's not entirely true for peek */ | |
-static struct key_stack_elem * | |
-peek_key_stack(void) { | |
- if (key_stack.len > 0) | |
- return &key_stack.stack[key_stack.len - 1]; | |
- return NULL; | |
-} | |
- | |
-static struct key_stack_elem * | |
-pop_key_stack(void) { | |
- if (key_stack.len > 0) { | |
- key_stack.len--; | |
- return &key_stack.stack[key_stack.len]; | |
- } | |
- return NULL; | |
-} | |
- | |
-void | |
-clear_key_stack(void) { | |
- key_stack.len = 0; | |
-} | |
- | |
-static void | |
-get_scroll_pos_height(double *pos, double *height) { | |
- *height = ((double)state.text_h / buffer->total_height) * state.text_h; | |
- *pos = (buffer->display_offset / | |
- (buffer->total_height - state.text_h)) * (state.text_h - *heigh… | |
-} | |
- | |
-static void | |
-set_scroll_pos(double pos) { | |
- buffer->display_offset = pos * (buffer->total_height / (double)state.t… | |
- if (buffer->display_offset < 0) | |
- buffer->display_offset = 0; | |
- if (buffer->display_offset + state.text_h > buffer->total_height) | |
- buffer->display_offset = buffer->total_height - state.text_h; | |
-} | |
+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}; | |
static void | |
mainloop(void) { | |
XEvent event; | |
int xkb_event_type; | |
int major, minor; | |
- if (!XkbQueryExtension(state.dpy, 0, &xkb_event_type, NULL, &major, &m… | |
+ if (!XkbQueryExtension(common.dpy, 0, &xkb_event_type, NULL, &major, &… | |
fprintf(stderr, "XKB not supported."); | |
exit(1); | |
} | |
t@@ -774,14 +76,14 @@ mainloop(void) { | |
* switching is used (e.g. 'setxkbmap -option grp:shifts_toggle'), | |
* this issue does not occur because only a state event is sent. */ | |
XkbSelectEvents( | |
- state.dpy, XkbUseCoreKbd, | |
+ common.dpy, XkbUseCoreKbd, | |
XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask | |
); | |
XkbSelectEventDetails( | |
- state.dpy, XkbUseCoreKbd, XkbStateNotify, | |
+ common.dpy, XkbUseCoreKbd, XkbStateNotify, | |
XkbAllStateComponentsMask, XkbGroupStateMask | |
); | |
- XSync(state.dpy, False); | |
+ XSync(common.dpy, False); | |
int running = 1; | |
int change_kbd = 0; | |
t@@ -790,7 +92,7 @@ mainloop(void) { | |
while (running) { | |
do { | |
- XNextEvent(state.dpy, &event); | |
+ XNextEvent(common.dpy, &event); | |
if (event.type == xkb_event_type) { | |
change_kbd = 1; | |
continue; | |
t@@ -799,6 +101,7 @@ mainloop(void) { | |
continue; | |
switch (event.type) { | |
case Expose: | |
+ /* FIXME: why did I do this? */ | |
redraw(); | |
need_redraw = 1; | |
break; | |
t@@ -820,37 +123,34 @@ mainloop(void) { | |
break; | |
case KeyPress: | |
need_redraw = 1; | |
- key_press(event); | |
+ key_press(&event); | |
break; | |
case ClientMessage: | |
- if ((Atom)event.xclient.data.l[0] == state.wm_… | |
+ if ((Atom)event.xclient.data.l[0] == buffer->w… | |
running = 0; | |
break; | |
case SelectionNotify: | |
- need_redraw = 1; | |
- selnotify(&event); | |
- break; | |
case PropertyNotify: | |
+ /* redraw because text may be pasted, then fall | |
+ through and let window handle the event */ | |
need_redraw = 1; | |
- propnotify(&event); | |
- break; | |
case SelectionRequest: | |
- selrequest(&event); | |
+ ledit_window_clipboard_event(buffer->window, &… | |
break; | |
default: | |
break; | |
} | |
- } while (XPending(state.dpy)); | |
+ } while (XPending(common.dpy)); | |
if (change_kbd) { | |
change_kbd = 0; | |
XkbStateRec s; | |
- XkbGetState(state.dpy, XkbUseCoreKbd, &s); | |
+ XkbGetState(common.dpy, XkbUseCoreKbd, &s); | |
XkbDescPtr desc = XkbGetKeyboard( | |
- state.dpy, XkbAllComponentsMask, XkbUseCoreKbd | |
+ common.dpy, XkbAllComponentsMask, XkbUseCoreKbd | |
); | |
char *group = XGetAtomName( | |
- state.dpy, desc->names->groups[s.group] | |
+ common.dpy, desc->names->groups[s.group] | |
); | |
change_keyboard(group); | |
XFree(group); | |
t@@ -867,30 +167,16 @@ static void | |
setup(int argc, char *argv[]) { | |
setlocale(LC_CTYPE, ""); | |
XSetLocaleModifiers(""); | |
- XSetWindowAttributes attrs; | |
- XGCValues gcv; | |
- | |
- state.scroll_dragging = 0; | |
- state.scroll_grab_handle = 0; | |
- state.selecting = 0; | |
- state.w = 500; | |
- state.h = 500; | |
- state.dpy = XOpenDisplay(NULL); | |
- state.screen = DefaultScreen(state.dpy); | |
+ common.dpy = XOpenDisplay(NULL); | |
+ common.screen = DefaultScreen(common.dpy); | |
/* based on http://wili.cc/blog/xdbe.html */ | |
int major, minor; | |
- if (XdbeQueryExtension(state.dpy, &major, &minor)) { | |
- /* | |
- printf( | |
- "Xdbe (%d.%d) supported, using double buffering.\n", | |
- major, minor | |
- ); | |
- */ | |
+ if (XdbeQueryExtension(common.dpy, &major, &minor)) { | |
int num_screens = 1; | |
- Drawable screens[] = { DefaultRootWindow(state.dpy) }; | |
+ Drawable screens[] = {DefaultRootWindow(common.dpy)}; | |
XdbeScreenVisualInfo *info = XdbeGetVisualInfo( | |
- state.dpy, screens, &num_screens | |
+ common.dpy, screens, &num_screens | |
); | |
if (!info || num_screens < 1 || info->count < 1) { | |
fprintf(stderr, "No visuals support Xdbe.\n"); | |
t@@ -903,7 +189,7 @@ setup(int argc, char *argv[]) { | |
xvisinfo_templ.depth = info->visinfo[0].depth; | |
int matches; | |
XVisualInfo *xvisinfo_match = XGetVisualInfo( | |
- state.dpy, | |
+ common.dpy, | |
VisualIDMask | VisualScreenMask | VisualDepthMask, | |
&xvisinfo_templ, &matches | |
); | |
t@@ -914,1075 +200,97 @@ setup(int argc, char *argv[]) { | |
); | |
exit(1); | |
} | |
- state.vis = xvisinfo_match->visual; | |
+ common.vis = xvisinfo_match->visual; | |
} else { | |
fprintf(stderr, "No Xdbe support.\n"); | |
exit(1); | |
} | |
- state.depth = DefaultDepth(state.dpy, state.screen); | |
- state.cm = DefaultColormap(state.dpy, state.screen); | |
- | |
- memset(&state.wattrs, 0, sizeof(attrs)); | |
- state.wattrs.background_pixel = BlackPixel(state.dpy, state.screen); | |
- state.wattrs.colormap = state.cm; | |
- /* this causes the window contents to be kept | |
- * when it is resized, leading to less flicker */ | |
- state.wattrs.bit_gravity = NorthWestGravity; | |
- /* FIXME: FocusChangeMask? */ | |
- state.wattrs.event_mask = KeyPressMask | | |
- ExposureMask | VisibilityChangeMask | StructureNotifyMask | | |
- ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | |
- state.win = XCreateWindow( | |
- state.dpy, DefaultRootWindow(state.dpy), 0, 0, | |
- state.w, state.h, 0, state.depth, | |
- InputOutput, state.vis, | |
- CWBackPixel | CWColormap | CWBitGravity | CWEventMask, &state.watt… | |
- ); | |
- XSetStandardProperties(state.dpy, state.win, "ledit", NULL, None, argv… | |
- | |
- state.back_buf = XdbeAllocateBackBufferName( | |
- state.dpy, state.win, XdbeBackground | |
- ); | |
- state.drawable = state.back_buf; | |
- | |
- memset(&gcv, 0, sizeof(gcv)); | |
- gcv.line_width = 1; | |
- state.gc = XCreateGC(state.dpy, state.back_buf, GCLineWidth, &gcv); | |
- | |
- state.fontmap = pango_xft_get_font_map(state.dpy, state.screen); | |
- state.context = pango_font_map_create_context(state.fontmap); | |
- | |
- state.font = pango_font_description_from_string("Monospace"); | |
- pango_font_description_set_size(state.font, 12 * PANGO_SCALE); | |
- | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#000000", &state.fg… | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#FFFFFF", &state.bg… | |
- XftColorAllocName(state.dpy, state.vis, state.cm, "#CCCCCC", &state.sc… | |
- | |
- /* | |
- XSelectInput( | |
- state.dpy, state.win, | |
- StructureNotifyMask | KeyPressMask | | |
- ButtonPressMask | ButtonReleaseMask | | |
- PointerMotionMask | ExposureMask | |
- ); | |
- */ | |
- | |
- state.wm_delete_msg = XInternAtom(state.dpy, "WM_DELETE_WINDOW", False… | |
- XSetWMProtocols(state.dpy, state.win, &state.wm_delete_msg, 1); | |
- | |
- /* blatantly stolen from st (simple terminal) */ | |
- /* FIXME: get improved input handling from newer version of st */ | |
- if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NULL) { | |
- XSetLocaleModifiers("@im=local"); | |
- if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL)) == NUL… | |
- XSetLocaleModifiers("@im="); | |
- if ((state.xim = XOpenIM(state.dpy, NULL, NULL, NULL))… | |
- fprintf( | |
- stderr, | |
- "XOpenIM failed. Could not open input devi… | |
- ); | |
- exit(1); | |
- } | |
- } | |
- } | |
- state.xic = XCreateIC( | |
- state.xim, XNInputStyle, | |
- XIMPreeditNothing | XIMStatusNothing, | |
- XNClientWindow, state.win, | |
- XNFocusWindow, state.win, NULL | |
- ); | |
- if (state.xic == NULL) { | |
- fprintf( | |
- stderr, | |
- "XCreateIC failed. Could not obtain input method.\n" | |
- ); | |
- exit(1); | |
- } | |
- XSetICFocus(state.xic); | |
- | |
- bottom_bar.mode = pango_layout_new(state.context); | |
- pango_layout_set_font_description(bottom_bar.mode, state.font); | |
- /* FIXME: only create "dummy draw" at first and create with proper siz… | |
- bottom_bar.mode_draw = ledit_create_draw(&state, 10, 10); | |
- bottom_bar.line = pango_layout_new(state.context); | |
- pango_layout_set_font_description(bottom_bar.line, state.font); | |
- bottom_bar.line_draw = ledit_create_draw(&state, 10, 10); | |
- bottom_bar.line_w = bottom_bar.line_h = 10; | |
- bottom_bar.line_text = NULL; | |
- bottom_bar.line_alloc = bottom_bar.line_len = 0; | |
- bottom_bar.line_cur_pos = 0; | |
- state.bottom_text_shown = 0; | |
- state.message_shown = 0; | |
- | |
- XMapWindow(state.dpy, state.win); | |
+ common.depth = DefaultDepth(common.dpy, common.screen); | |
+ common.cm = DefaultColormap(common.dpy, common.screen); | |
- ledit_init_cache(&state); | |
- buffer = ledit_create_buffer(&state); | |
- /* FIXME: move this to create_buffer */ | |
- ledit_init_undo_stack(buffer); | |
+ theme = ledit_theme_create(&common); | |
+ window = ledit_window_create(&common, theme); | |
+ buffer = ledit_buffer_create(&common, theme, window); | |
/* FIXME: Support multiple buffers/files */ | |
if (argc > 1) { | |
char *load_err; | |
- if (ledit_load_file_into_buffer(buffer, argv[1], 0, &load_err)… | |
+ if (ledit_buffer_load_file(buffer, argv[1], 0, &load_err)) { | |
fprintf(stderr, "Error opening file '%s': %s\n", argv[… | |
exit(1); | |
} | |
+ /* FIXME: encapsulate */ | |
buffer->filename = ledit_strdup(argv[1]); | |
} | |
- set_mode(NORMAL); | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
- | |
- key_stack.len = key_stack.alloc = 0; | |
- key_stack.stack = NULL; | |
+ ledit_buffer_set_mode(buffer, NORMAL); | |
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c… | |
- paste_buffer = lbuf_new(); | |
- tmp_buffer = lbuf_new(); | |
- | |
- xsel.primary = lbuf_new(); | |
- xsel.clipboard = NULL; | |
- xsel.xtarget = XInternAtom(state.dpy, "UTF8_STRING", 0); | |
- if (xsel.xtarget == None) | |
- xsel.xtarget = XA_STRING; | |
- | |
- recalc_text_size(); | |
redraw(); | |
} | |
static void | |
cleanup(void) { | |
/* FIXME: cleanup everything else */ | |
+ /* | |
ledit_cleanup_search(); | |
ledit_destroy_cache(); | |
ledit_destroy_undo_stack(buffer); | |
ledit_destroy_buffer(buffer); | |
- XDestroyWindow(state.dpy, state.win); | |
- XCloseDisplay(state.dpy); | |
-} | |
- | |
-static void | |
-redraw(void) { | |
- XSetForeground(state.dpy, state.gc, state.bg.pixel); | |
- XFillRectangle( | |
- state.dpy, state.back_buf, state.gc, 0, 0, state.w, state.h | |
- ); | |
- | |
- int h = 0; | |
- int cur_line_y = 0; | |
- int cursor_displayed = 0; | |
- for (int i = 0; i < buffer->lines_num; i++) { | |
- ledit_line *line = ledit_get_line(buffer, i); | |
- if (h + line->h > buffer->display_offset) { | |
- if (line->dirty || line->cache_index == -1) { | |
- ledit_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 > state.text_h) { | |
- final_h -= final_y + final_h - | |
- buffer->display_offset - state.text… | |
- } | |
- ledit_cache_pixmap *pix = ledit_get_cache_pixmap( | |
- line->cache_index | |
- ); | |
- XCopyArea( | |
- state.dpy, pix->pixmap, | |
- state.drawable, state.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 + state.text_h) | |
- break; | |
- h += line->h; | |
- } | |
- | |
- XSetForeground(state.dpy, state.gc, state.fg.pixel); | |
- PangoRectangle strong, weak; | |
- ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
- pango_layout_get_cursor_pos( | |
- cur_line->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 (state.mode == NORMAL && buffer->cur_index == cur_line->len… | |
- XFillRectangle( | |
- state.dpy, state.drawable, state.gc, | |
- strong.x / PANGO_SCALE, cursor_y, | |
- 10, strong.height / PANGO_SCALE | |
- ); | |
- } else if (state.mode == INSERT || state.mode == VISUAL) { | |
- XDrawLine( | |
- state.dpy, state.drawable, state.gc, | |
- strong.x / PANGO_SCALE, cursor_y, | |
- strong.x / PANGO_SCALE, | |
- (strong.y + strong.height) / PANGO_SCALE + cur_lin… | |
- ); | |
- } | |
- } | |
- if (buffer->total_height > state.text_h) { | |
- XSetForeground(state.dpy, state.gc, state.scroll_bg.pixel); | |
- XFillRectangle( | |
- state.dpy, state.drawable, state.gc, | |
- state.w - SCROLLBAR_WIDTH, 0, SCROLLBAR_WIDTH, state.text_h | |
- ); | |
- XSetForeground(state.dpy, state.gc, state.fg.pixel); | |
- double scroll_h, scroll_y; | |
- get_scroll_pos_height(&scroll_y, &scroll_h); | |
- XFillRectangle( | |
- state.dpy, state.drawable, state.gc, | |
- state.w - SCROLLBAR_WIDTH, (int)round(scroll_y), | |
- SCROLLBAR_WIDTH, (int)round(scroll_h) | |
- ); | |
- } | |
- XSetForeground(state.dpy, state.gc, state.bg.pixel); | |
- /* FIXME: allow different color for bar */ | |
- XFillRectangle( | |
- state.dpy, state.drawable, state.gc, | |
- 0, state.text_h, | |
- state.w, state.h - state.text_h | |
- ); | |
- if (state.bottom_text_shown || state.message_shown) { | |
- XCopyArea( | |
- state.dpy, bottom_bar.line_draw->pixmap, | |
- state.drawable, state.gc, | |
- 0, 0, bottom_bar.line_w, bottom_bar.line_h, | |
- 0, state.text_h | |
- ); | |
- } else { | |
- XCopyArea( | |
- state.dpy, bottom_bar.mode_draw->pixmap, | |
- state.drawable, state.gc, | |
- 0, 0, bottom_bar.mode_w, bottom_bar.mode_h, | |
- state.w - bottom_bar.mode_w, state.text_h | |
- ); | |
- } | |
- | |
- XdbeSwapInfo swap_info; | |
- swap_info.swap_window = state.win; | |
- swap_info.swap_action = XdbeBackground; | |
- | |
- if (!XdbeSwapBuffers(state.dpy, &swap_info, 1)) | |
- exit(1); | |
- XFlush(state.dpy); | |
-} | |
- | |
-static void | |
-xy_to_line_byte(int x, int y, 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_get_line(buffer, i); | |
- if ((h <= pos && h + line->h > pos) || i == buffer->lines_num … | |
- int index, trailing; | |
- pango_layout_xy_to_index( | |
- line->layout, | |
- x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE, | |
- &index, &trailing | |
- ); | |
- while (trailing > 0) { | |
- trailing--; | |
- index = ledit_next_utf8(line, index); | |
- } | |
- *line_ret = i; | |
- *byte_ret = index; | |
- break; | |
- } | |
- h += line->h; | |
- } | |
-} | |
- | |
-static void | |
-swap(int *a, int *b) { | |
- int tmp = *a; | |
- *a = *b; | |
- *b = tmp; | |
-} | |
- | |
-static void | |
-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(int line1, int byte1, int line2, int byte2) { | |
- ledit_copy_text_to_lbuf(buffer, xsel.primary, line1, byte1, line2, byt… | |
- XSetSelectionOwner(state.dpy, XA_PRIMARY, state.win, CurrentTime); | |
- /* | |
- FIXME | |
- if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win) | |
- selclear(); | |
*/ | |
+ ledit_window_destroy(window); | |
+ XCloseDisplay(common.dpy); | |
} | |
static void | |
-set_selection(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; | |
- } | |
- if (buffer->sel.line1 >= 0) { | |
- int l1_new = line1, l2_new = line2; | |
- int b1_new = byte1, b2_new = byte2; | |
- sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer… | |
- sort_selection(&l1_new, &b1_new, &l2_new, &b2_new); | |
- if (buffer->sel.line1 > l2_new || buffer->sel.line2 < l1_new) { | |
- for (int i = buffer->sel.line1; i <= buffer->sel.line2… | |
- ledit_wipe_line_cursor_attrs(buffer, i); | |
- } | |
- } else { | |
- for (int i = buffer->sel.line1; i < l1_new; i++) { | |
- ledit_wipe_line_cursor_attrs(buffer, i); | |
- } | |
- for (int i = buffer->sel.line2; i > l2_new; i--) { | |
- ledit_wipe_line_cursor_attrs(buffer, i); | |
- } | |
- } | |
- if (l1_new == l2_new) { | |
- ledit_set_line_selection(buffer, l1_new, b1_new, b2_ne… | |
- } else { | |
- ledit_line *ll1 = ledit_get_line(buffer, l1_new); | |
- ledit_set_line_selection(buffer, l1_new, b1_new, ll1->… | |
- ledit_set_line_selection(buffer, l2_new, 0, b2_new); | |
- /* FIXME: optimize this */ | |
- for (int i = l1_new + 1; i < l2_new; i++) { | |
- if (i <= buffer->sel.line1 || i >= buffer->sel… | |
- ledit_line *llx = ledit_get_line(buffe… | |
- ledit_set_line_selection(buffer, i, 0,… | |
- } | |
- } | |
- } | |
- copy_selection_to_x_primary(l1_new, b1_new, l2_new, b2_new); | |
- } | |
- buffer->sel.line1 = line1; | |
- buffer->sel.byte1 = byte1; | |
- buffer->sel.line2 = line2; | |
- buffer->sel.byte2 = byte2; | |
+redraw(void) { | |
+ ledit_window_clear(window); | |
+ ledit_buffer_redraw(buffer); | |
+ ledit_window_redraw(window); | |
} | |
static int | |
button_press(XEvent *event) { | |
- int x, y; | |
- double scroll_h, scroll_y; | |
- switch (event->xbutton.button) { | |
- case Button1: | |
- get_scroll_pos_height(&scroll_y, &scroll_h); | |
- x = event->xbutton.x; | |
- y = event->xbutton.y; | |
- if (x >= state.text_w) { | |
- state.scroll_dragging = 1; | |
- state.scroll_grab_handle = y; | |
- if (y < scroll_y || y > scroll_y + scroll_h) { | |
- double new_scroll_y = y - scroll_h / 2; | |
- set_scroll_pos(new_scroll_y); | |
- } | |
- return 1; | |
- } else if (y < state.text_h) { | |
- int l, b; | |
- xy_to_line_byte(x, y, &l, &b); | |
- set_selection(l, b, l, b); | |
- if (state.mode == NORMAL) { | |
- ledit_wipe_line_cursor_attrs(buffer, b… | |
- set_mode(VISUAL); | |
- } | |
- buffer->cur_line = l; | |
- buffer->cur_index = b; | |
- state.selecting = 1; | |
- return 1; | |
- } | |
- break; | |
- case Button4: | |
- buffer->display_offset -= SCROLL_STEP; | |
- if (buffer->display_offset < 0) | |
- buffer->display_offset = 0; | |
- return 1; | |
- case Button5: | |
- if (buffer->display_offset + state.text_h < | |
- buffer->total_height) { | |
- buffer->display_offset += SCROLL_STEP; | |
- if (buffer->display_offset + state.text_h > | |
- buffer->total_height) { | |
- buffer->display_offset = | |
- buffer->total_height - state.text_… | |
- } | |
- } | |
- return 1; | |
- } | |
- return 0; | |
+ return ledit_window_button_press(window, event); | |
} | |
static int | |
button_release(XEvent *event) { | |
- if (event->xbutton.button == Button1) { | |
- state.scroll_dragging = 0; | |
- state.selecting = 0; | |
- return 1; | |
- } | |
- return 0; | |
+ return ledit_window_button_release(window, event); | |
} | |
static int | |
drag_motion(XEvent *event) { | |
- if (state.scroll_dragging) { | |
- double scroll_h, scroll_y; | |
- get_scroll_pos_height(&scroll_y, &scroll_h); | |
- scroll_y += event->xbutton.y - state.scroll_grab_handle; | |
- state.scroll_grab_handle = event->xbutton.y; | |
- set_scroll_pos(scroll_y); | |
- return 1; | |
- } else if (state.selecting) { | |
- int l, b; | |
- int y = event->xbutton.y >= 0 ? event->xbutton.y : 0; | |
- xy_to_line_byte(event->xbutton.x, y, &l, &b); | |
- set_selection(buffer->sel.line1, buffer->sel.byte1, l, b); | |
- buffer->cur_line = l; | |
- buffer->cur_index = b; | |
- return 1; | |
- } | |
- return 0; | |
-} | |
- | |
-static void | |
-ensure_cursor_shown(void) { | |
- PangoRectangle strong, weak; | |
- ledit_line *line = ledit_get_line(buffer, buffer->cur_line); | |
- pango_layout_get_cursor_pos( | |
- line->layout, buffer->cur_index, &strong, &weak | |
- ); | |
- long cursor_y = strong.y / PANGO_SCALE + line->y_offset; | |
- if (cursor_y < buffer->display_offset) { | |
- buffer->display_offset = cursor_y; | |
- } else if (cursor_y + strong.height / PANGO_SCALE > | |
- buffer->display_offset + state.text_h) { | |
- buffer->display_offset = | |
- cursor_y - state.text_h + strong.height / PANGO_SCALE; | |
- } | |
+ return ledit_window_drag_motion(window, event); | |
} | |
-/* FIXME: move the recalculation part of this to buffer.c */ | |
static void | |
resize_window(int w, int h) { | |
- state.w = w; | |
- state.h = h; | |
- buffer->total_height = 0; | |
- int tmp_w, tmp_h; | |
- for (int i = 0; i < buffer->lines_num; i++) { | |
- ledit_line *line = ledit_get_line(buffer, i); | |
- /* 10 pixels for scrollbar */ | |
- pango_layout_set_width(line->layout, (w - SCROLLBAR_WIDTH) * P… | |
- pango_layout_get_pixel_size(line->layout, &tmp_w, &tmp_h); | |
- line->h = tmp_h; | |
- line->w = w - SCROLLBAR_WIDTH; | |
- line->y_offset = buffer->total_height; | |
- line->dirty = 1; | |
- buffer->total_height += tmp_h; | |
- } | |
- recalc_text_size(); | |
- if (buffer->display_offset > 0 && | |
- buffer->display_offset + state.text_h >= buffer->total_height) { | |
- buffer->display_offset = buffer->total_height - state.text_h; | |
- if (buffer->display_offset < 0) | |
- buffer->display_offset = 0; | |
- } | |
-} | |
- | |
-static void | |
-backspace(void) { | |
- if (delete_selection()) { | |
- /* NOP */ | |
- } else if (buffer->cur_index == 0) { | |
- if (buffer->cur_line != 0) { | |
- ledit_line *l1 = ledit_get_line(buffer, buffer->cur_li… | |
- delete_range(0, 0, buffer->cur_line - 1, l1->len, buff… | |
- /* | |
- int old_len = l1->len; | |
- ledit_insert_text_from_line( | |
- buffer, buffer->cur_line - 1, l1->len, | |
- buffer->cur_line, 0, l2->len, tmp_buffer | |
- ); | |
- ledit_delete_line_entry(buffer, buffer->cur_line); | |
- buffer->cur_line--; | |
- buffer->cur_index = old_len; | |
- */ | |
- } | |
- } else { | |
- ledit_line *l = ledit_get_line(buffer, buffer->cur_line); | |
- int i = ledit_prev_utf8(l, buffer->cur_index); | |
- delete_range(0, 0, buffer->cur_line, buffer->cur_index, buffer… | |
- /* | |
- buffer->cur_index = ledit_delete_unicode_char( | |
- buffer, buffer->cur_line, buffer->cur_index, -1 | |
- ); | |
- */ | |
- } | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
-} | |
- | |
-static void | |
-delete_key(void) { | |
- ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
- if (delete_selection()) { | |
- /* NOP */ | |
- } else if (buffer->cur_index == cur_line->len) { | |
- if (buffer->cur_line != buffer->lines_num - 1) { | |
- delete_range(0, 0, buffer->cur_line, cur_line->len, bu… | |
- /* | |
- int old_len = cur_line->len; | |
- ledit_insert_text_from_line( | |
- buffer, buffer->cur_line, cur_line->len, | |
- buffer->cur_line + 1, 0, -1 | |
- ); | |
- ledit_delete_line_entry(buffer, buffer->cur_line + 1); | |
- */ | |
- } | |
- } else { | |
- int i = ledit_next_utf8(cur_line, buffer->cur_index); | |
- delete_range(0, 0, buffer->cur_line, buffer->cur_index, buffer… | |
- /* | |
- buffer->cur_index = ledit_delete_unicode_char( | |
- buffer, buffer->cur_line, buffer->cur_index, 1 | |
- ); | |
- */ | |
- } | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
-} | |
- | |
-static void | |
-move_cursor_left_right(int dir) { | |
- int num = 1; | |
- struct key_stack_elem *e = pop_key_stack(); | |
- if (e != NULL) { | |
- if (e->key & KEY_NUMBER) { | |
- num = e->count > 0 ? e->count : 0; | |
- e = pop_key_stack(); | |
- } | |
- if (e != NULL) | |
- num *= (e->count > 0 ? e->count : 1); | |
- } | |
- | |
- /* FIXME: trailing */ | |
- int trailing = 0, tmp_index; | |
- ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line); | |
- int new_index = buffer->cur_index, last_index = buffer->cur_index; | |
- while (num > 0) { | |
- tmp_index = new_index; | |
- pango_layout_move_cursor_visually( | |
- cur_line->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_next_utf8(cur_line, new_index); | |
- } | |
- if (new_index < 0) | |
- new_index = 0; | |
- /* 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 (state.mode == NORMAL && (e == NULL || e->motion_cb == NULL… | |
- new_index = last_index; | |
- } else { | |
- /* FIXME: I guess this is unnecessary */ | |
- new_index = cur_line->len; | |
- } | |
- } | |
- if (e != NULL && e->motion_cb != NULL) { | |
- e->motion_cb(buffer->cur_line, new_index, KEY_MOTION_CHAR); | |
- } else { | |
- buffer->cur_index = new_index; | |
- if (state.mode == VISUAL) { | |
- set_selection(buffer->sel.line1, buffer->sel.byte1, bu… | |
- } else if (state.mode == INSERT && | |
- (buffer->sel.line1 != buffer->sel.line2 || | |
- buffer->sel.byte1 != buffer->sel.byte2)) { | |
- set_selection(buffer->cur_line, new_index, buffer->cur… | |
- } else if (state.mode == NORMAL) { | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, … | |
- } | |
- } | |
- clear_key_stack(); | |
+ ledit_window_resize(window, w, h); | |
+ ledit_buffer_resize_textview(buffer); | |
} | |
static void | |
-cursor_left(void) { | |
- move_cursor_left_right(-1); | |
-} | |
- | |
-static void | |
-cursor_right(void) { | |
- move_cursor_left_right(1); | |
-} | |
- | |
-static void | |
-return_key(void) { | |
- int start_group = 1; | |
- if (delete_selection()) | |
- start_group = 0; | |
- insert_text(buffer->cur_line, buffer->cur_index, "\n", -1, start_group… | |
- /* ledit_append_line(buffer, buffer->cur_line, buffer->cur_index); */ | |
- /* FIXME: these aren't needed, right? This only works in insert mode | |
- * anyways, so there's nothing to wipe */ | |
- /* ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- buffer->cur_line++; | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
- buffer->cur_index = 0; */ | |
-} | |
- | |
-static void | |
-escape_key(void) { | |
- clear_key_stack(); /* just in case... */ | |
- if (state.mode == INSERT && | |
- (buffer->sel.line1 != buffer->sel.line2 || | |
- buffer->sel.byte1 != buffer->sel.byte2)) { | |
- set_mode(VISUAL); | |
- } else { | |
- set_mode(NORMAL); | |
- clear_key_stack(); | |
- PangoDirection dir = PANGO_DIRECTION_RTL; | |
- int tmp_index = buffer->cur_index; | |
- ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line… | |
- if (buffer->cur_index >= cur_line->len) | |
- tmp_index--; | |
- if (tmp_index >= 0) | |
- dir = pango_layout_get_direction(cur_line->layout, tmp… | |
- if (dir == PANGO_DIRECTION_RTL || dir == PANGO_DIRECTION_WEAK_… | |
- cursor_right(); | |
- } else { | |
- cursor_left(); | |
- } | |
- if (buffer->sel.line1 != buffer->sel.line2) { | |
- int min = buffer->sel.line1 < buffer->sel.line2 ? buff… | |
- int max = buffer->sel.line1 > buffer->sel.line2 ? buff… | |
- for (int i = min; i <= max; i++) { | |
- ledit_wipe_line_cursor_attrs(buffer, i); | |
- } | |
- } | |
- /* FIXME: optimize this to avoid first wiping and then setting… | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->… | |
- } | |
-} | |
- | |
-static void | |
-enter_insert(void) { | |
- if (state.mode == NORMAL) | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- set_mode(INSERT); | |
- clear_key_stack(); | |
-} | |
- | |
-/* FIXME: Check if previous key allows motion command - or is this checked aut… | |
-static void | |
-move_cursor_up_down(int dir) { | |
- int new_line, new_softline; | |
- | |
- int num = 1; | |
- struct key_stack_elem *e = pop_key_stack(); | |
- if (e != NULL) { | |
- if (e->key & KEY_NUMBER) { | |
- num = e->count > 0 ? e->count : 0; | |
- e = pop_key_stack(); | |
- } | |
- if (e != NULL) | |
- num *= (e->count > 0 ? e->count : 1); | |
- } | |
- num *= dir; | |
- | |
- get_new_line_softline( | |
- buffer->cur_line, buffer->cur_index, num, | |
- &new_line, &new_softline | |
- ); | |
- | |
- ledit_line *cur_lline = ledit_get_line(buffer, buffer->cur_line); | |
- ledit_line *new_lline = ledit_get_line(buffer, new_line); | |
- if (e != NULL && e->motion_cb != NULL) { | |
- PangoLayoutLine *pl = pango_layout_get_line_readonly(new_lline… | |
- e->motion_cb(new_line, pl->start_index, KEY_MOTION_LINE); | |
- } else { | |
- int lineno, x; | |
- ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lin… | |
- ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->c… | |
- if (buffer->cur_line != new_line) | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- buffer->cur_line = new_line; | |
- | |
- if (state.mode == VISUAL) { | |
- set_selection(buffer->sel.line1, buffer->sel.byte1, bu… | |
- } else if (state.mode == INSERT && | |
- (buffer->sel.line1 != buffer->sel.line2 || | |
- buffer->sel.byte1 != buffer->sel.byte2)) { | |
- set_selection(buffer->cur_line, buffer->cur_index, buf… | |
- } else if (state.mode == NORMAL) { | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, … | |
- } | |
- } | |
- clear_key_stack(); | |
-} | |
- | |
-static void | |
-cursor_down(void) { | |
- move_cursor_up_down(1); | |
-} | |
- | |
-static void | |
-cursor_up(void) { | |
- move_cursor_up_down(-1); | |
+change_keyboard(char *lang) { | |
+ printf("%s\n", lang); | |
+ cur_lang = get_language_index(lang); | |
+ if (cur_lang < 0) | |
+ cur_lang = 0; | |
} | |
static void | |
-cursor_to_beginning(void) { | |
- struct key_stack_elem *e = pop_key_stack(); | |
- /* FIXME: error when no callback? */ | |
- if (e != NULL && e->motion_cb != NULL) { | |
- e->motion_cb(buffer->cur_line, 0, KEY_MOTION_CHAR); | |
+key_press(XEvent *event) { | |
+ if (cur_action.type == ACTION_GRABKEY && cur_action.callback) { | |
+ cur_action = cur_action.callback(buffer, event, cur_lang); | |
} else { | |
- buffer->cur_index = 0; | |
- if (state.mode == VISUAL) { | |
- set_selection( | |
- buffer->sel.line1, buffer->sel.byte1, | |
- buffer->cur_line, buffer->cur_index | |
- ); | |
- } else { | |
- ledit_set_line_cursor_attrs( | |
- buffer, buffer->cur_line, buffer->cur_index | |
- ); | |
- } | |
- } | |
- clear_key_stack(); | |
-} | |
- | |
-static void | |
-enter_visual(void) { | |
- set_mode(VISUAL); | |
- buffer->sel.line1 = buffer->sel.line2 = buffer->cur_line; | |
- buffer->sel.byte1 = buffer->sel.byte2 = buffer->cur_index; | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- clear_key_stack(); /* FIXME: error if not empty? */ | |
-} | |
- | |
-static void | |
-switch_selection_end(void) { | |
- 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; | |
-} | |
- | |
-#define XK_ANY_MOD UINT_MAX | |
-#define XK_NO_MOD 0 | |
- | |
-static void | |
-enter_commandedit(void) { | |
- set_bottom_bar_text(":", -1); | |
- bottom_bar.line_cur_pos = 1; | |
- state.mode = COMMANDEDIT; | |
- state.bottom_text_shown = 1; | |
-} | |
- | |
-static void | |
-enter_searchedit_forward(void) { | |
- set_bottom_bar_text("/", -1); | |
- bottom_bar.line_cur_pos = 1; | |
- state.mode = SEARCHEDIT; | |
- state.bottom_text_shown = 1; | |
-} | |
- | |
-static void | |
-enter_searchedit_backward(void) { | |
- set_bottom_bar_text("?", -1); | |
- bottom_bar.line_cur_pos = 1; | |
- state.mode = SEARCHEDIT; | |
- state.bottom_text_shown = 1; | |
-} | |
- | |
-/* FIXME: support visual mode, i.e. change selection to new place? */ | |
-static void | |
-search_next(void) { | |
- /* FIXME: avoid this when line doesn't change */ | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- enum ledit_search_state ret = ledit_search_next(buffer, &buffer->cur_l… | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
- if (ret != SEARCH_NORMAL) | |
- show_message(ledit_search_state_to_str(ret), -1); | |
-} | |
- | |
-static void | |
-search_prev(void) { | |
- /* FIXME: avoid this when line doesn't change */ | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- enum ledit_search_state ret = ledit_search_prev(buffer, &buffer->cur_l… | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
- if (ret != SEARCH_NORMAL) | |
- show_message(ledit_search_state_to_str(ret), -1); | |
-} | |
- | |
-static void | |
-end_lineedit(void) { | |
- enum ledit_mode old_mode = state.mode; | |
- /* FIXME: go to last mode (visual or normal) */ | |
- set_mode(NORMAL); | |
- state.bottom_text_shown = 0; | |
- | |
- /* FIXME: forward/backward; check for empty string; | |
- end edit mode when / or ? is deleted with backspace */ | |
- if (old_mode == SEARCHEDIT) { | |
- /* FIXME: this is all so horrible */ | |
- if (bottom_bar.line_text[0] == '/') | |
- ledit_set_search_forward(bottom_bar.line_text + 1); | |
- else | |
- ledit_set_search_backward(bottom_bar.line_text + 1); | |
- search_next(); | |
- } else if (old_mode == COMMANDEDIT) { | |
- ledit_handle_cmd(buffer, bottom_bar.line_text + 1, -1); | |
+ cur_action = basic_key_handler(buffer, event, cur_lang); | |
} | |
} | |
-static void | |
-show_line(void) { | |
- int len = snprintf(NULL, 0, "Line %d of %d", buffer->cur_line + 1, buf… | |
- char *str = ledit_malloc(len + 1); | |
- snprintf(str, len + 1, "Line %d of %d", buffer->cur_line + 1, buffer->… | |
- show_message(str, len); | |
-} | |
- | |
-/* FIXME: return status! */ | |
-static void | |
-undo(void) { | |
- set_selection(0, 0, 0, 0); | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- ledit_undo(buffer); | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
-} | |
- | |
-static void | |
-redo(void) { | |
- set_selection(0, 0, 0, 0); | |
- ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line); | |
- ledit_redo(buffer); | |
- ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_inde… | |
-} | |
- | |
-/* FIXME: maybe sort these and use binary search */ | |
-static struct key keys_en[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_lef… | |
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_ri… | |
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_dow… | |
- {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
- {NULL, 0, XK_Escape, VISUAL|INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
- {"i", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
- {"h", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
- {"l", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
- {"j", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
- {"k", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &… | |
- {"0", 0, 0, NORMAL|VISUAL, ~KEY_NUMBER, KEY_ANY, &cursor_to_beginning… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_NUMBER, &push_0}, | |
- {"1", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_1}, | |
- {"2", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_2}, | |
- {"3", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_3}, | |
- {"4", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_4}, | |
- {"5", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_5}, | |
- {"6", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_6}, | |
- {"7", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_7}, | |
- {"8", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_8}, | |
- {"9", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_9}, | |
- {"x", 0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &key_x}, | |
- {"d", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &ke… | |
- {"v", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual}, | |
- {"o", 0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end}, | |
- {"c", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy}, | |
- {"v", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste}, | |
- {"g", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &show_line}, | |
- {":", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_commandedit}, | |
- {"?", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_backwa… | |
- {"/", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_forwar… | |
- {NULL, 0, XK_Return, COMMANDEDIT|SEARCHEDIT, KEY_ANY, KEY_ANY, &end_li… | |
- {"n", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &search_next}, | |
- {"N", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &search_prev}, | |
- {"u", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &undo}, | |
- {"U", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &redo}, | |
- {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &undo}, | |
- {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &redo} | |
-}; | |
- | |
-static struct key keys_ur[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
- {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, | |
- {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, | |
- {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
- {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, | |
- {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
- {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
- {"ی", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
- {"Ø", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
- {"Ù„", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
- {"ج", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
- {"Ú©", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &cursor… | |
- {"0", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning}, | |
- {"Ú†", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy} | |
-}; | |
- | |
-static struct key keys_hi[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace}, | |
- {NULL, 0, XK_Left, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_left}, | |
- {NULL, 0, XK_Right, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_right}, | |
- {NULL, 0, XK_Up, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_up}, | |
- {NULL, 0, XK_Down, INSERT|NORMAL, KEY_ANY, KEY_ANY, &cursor_down}, | |
- {NULL, 0, XK_Return, INSERT, KEY_ANY, KEY_ANY, &return_key}, | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ANY, &delete_key}, | |
- {NULL, 0, XK_Escape, INSERT, KEY_ANY, KEY_ANY, &escape_key}, | |
- {"ि", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_insert}, | |
- {"ह", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
- {"ल", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
- {"ज", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
- {"क", 0, 0, NORMAL, KEY_ANY, KEY_MOTION | KEY_NUMBERALLOWED, &curso… | |
- {"0", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &cursor_to_beginning} | |
-}; | |
- | |
-#define LENGTH(X) (sizeof(X) / sizeof(X[0])) | |
- | |
-struct lang_keys { | |
- char *lang; | |
- struct key *keys; | |
- int num_keys; | |
-}; | |
- | |
-static struct lang_keys keys[] = { | |
- {"English (US)", keys_en, LENGTH(keys_en)}, | |
- {"German", keys_en, LENGTH(keys_en)}, | |
- {"Urdu (Pakistan)", keys_ur, LENGTH(keys_ur)}, | |
- {"Hindi (Bolnagri)", keys_hi, LENGTH(keys_hi)} | |
-}; | |
- | |
-static struct lang_keys *cur_keys = &keys[0]; | |
- | |
-static void change_keyboard(char *lang) { | |
- printf("%s\n", lang); | |
- for (size_t i = 0; i < LENGTH(keys); i++) { | |
- if (!strcmp(keys[i].lang, lang)) { | |
- cur_keys = &keys[i]; | |
- break; | |
- } | |
- } | |
-} | |
- | |
-/* FIXME: Does this break anything? */ | |
-static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Ma… | |
- | |
-static int | |
-match(unsigned int mask, unsigned int state) | |
-{ | |
- return mask == XK_ANY_MOD || mask == (state & importantmod); | |
-} | |
+int | |
+main(int argc, char *argv[]) { | |
+ setup(argc, argv); | |
+ mainloop(); | |
+ cleanup(); | |
-static void | |
-key_press(XEvent event) { | |
- char buf[32]; | |
- KeySym sym; | |
- /* | |
- * FIXME: I don't understand how key handling works with different key… | |
- * If, for instance, you run "setxkbmap pk" and then type "Ctrl+c", the | |
- * keysym will be "0x1000686, Arabic_tcheh" and the appropriate string… | |
- * be returned by XmbLookupString (tested also using xev). | |
- * If, however, you run "setxkbmap -layout "us,pk" -option grp:shifts_… | |
- * and type "Ctrl+c" after switching to the pk layout, the keysym will… | |
- * be "0x63, c" and XmbLookupString will return the control code sent … | |
- * Ctrl+c (ascii 03). This means the handling is different depending o… | |
- * the keymap is switched to, and I have no clue what the proper way to | |
- * handle this would be, since the shortcuts are explicitly supposed t… | |
- * properly in all language maps. My current solution is to just ignor… | |
- * control key in the state so the text found by Xutf8LookupString is … | |
- * and the modifier mask can be checked separately. Please tell me if … | |
- * know the proper way to do this. | |
- */ | |
- unsigned int key_state = event.xkey.state; | |
- event.xkey.state &= ~ControlMask; | |
- /* FIXME: X_HAVE_UTF8_STRING See XmbLookupString(3) */ | |
- int n = Xutf8LookupString( | |
- state.xic, &event.xkey, buf, sizeof(buf), &sym, NULL | |
- ); | |
- int found = 0; | |
- struct key_stack_elem *e = peek_key_stack(); | |
- for (int i = 0; i < cur_keys->num_keys; i++) { | |
- if (cur_keys->keys[i].text) { | |
- if (n > 0 && | |
- (cur_keys->keys[i].modes & state.mode) && | |
- (!e || (e->key & cur_keys->keys[i].prev_keys)) && | |
- !strncmp(cur_keys->keys[i].text, buf, n) && | |
- match(cur_keys->keys[i].mods, key_state & ~ShiftM… | |
- /* FIXME: seems a bit hacky to remove shift, b… | |
- is needed to make keys that use shift match… | |
- cur_keys->keys[i].func(); | |
- found = 1; | |
- break; | |
- } | |
- } else if ((cur_keys->keys[i].modes & state.mode) && | |
- cur_keys->keys[i].keysym == sym && | |
- match(cur_keys->keys[i].mods, key_state)) { | |
- cur_keys->keys[i].func(); | |
- found = 1; | |
- break; | |
- } | |
- } | |
- if (found) { | |
- /* FIXME: only do this when necessary */ | |
- ensure_cursor_shown(); | |
- /* FIXME: this is a bit hacky */ | |
- if (state.message_shown > 0) | |
- state.message_shown--; | |
- } else if (state.mode == INSERT && !found && n > 0) { | |
- delete_selection(); | |
- insert_text(buffer->cur_line, buffer->cur_index, buf, n, 1); | |
- /* | |
- ledit_insert_text_with_newlines( | |
- buffer, | |
- buffer->cur_line, buffer->cur_index, | |
- buf, n, | |
- &buffer->cur_line, &buffer->cur_index | |
- ); | |
- */ | |
- ensure_cursor_shown(); | |
- if (state.message_shown > 0) | |
- state.message_shown--; | |
- } else if (!found && (state.mode == COMMANDEDIT || state.mode == SEARC… | |
- insert_bottom_bar_text(buf, n); | |
- bottom_bar.line_cur_pos += n; | |
- } | |
+ return 0; | |
} | |
diff --git a/search.c b/search.c | |
t@@ -1,4 +1,8 @@ | |
+/* FIXME: split buffer into pure text part and graphical part so this | |
+ * doesn't depend on all the graphics stuff */ | |
+ | |
#include <string.h> | |
+ | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <pango/pangoxft.h> | |
t@@ -6,7 +10,11 @@ | |
#include "memory.h" | |
#include "common.h" | |
-#include "lbuf.h" | |
+#include "txtbuf.h" | |
+#include "undo.h" | |
+#include "cache.h" | |
+#include "theme.h" | |
+#include "window.h" | |
#include "buffer.h" | |
#include "search.h" | |
t@@ -48,16 +56,16 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *b… | |
note: since the string ends with '\0', this is always valid */ | |
int byte = buffer->cur_index + 1; | |
char *res; | |
- ledit_line *lline = ledit_get_line(buffer, line); | |
- ledit_normalize_line(lline); | |
+ ledit_line *lline = ledit_buffer_get_line(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); | |
return SEARCH_NORMAL; | |
} | |
for (int i = line + 1; i < buffer->lines_num; i++) { | |
- lline = ledit_get_line(buffer, i); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = i; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -65,16 +73,16 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *b… | |
} | |
} | |
for (int i = 0; i < line; i++) { | |
- lline = ledit_get_line(buffer, i); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = i; | |
*byte_ret = (int)(res - lline->text); | |
return SEARCH_WRAPPED; | |
} | |
} | |
- lline = ledit_get_line(buffer, line); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, line); | |
+ ledit_buffer_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = line; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -92,8 +100,8 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *b… | |
return SEARCH_NO_PATTERN; | |
int line = buffer->cur_line; | |
int byte = buffer->cur_index; | |
- ledit_line *lline = ledit_get_line(buffer, line); | |
- ledit_normalize_line(lline); | |
+ ledit_line *lline = ledit_buffer_get_line(buffer, line); | |
+ ledit_buffer_normalize_line(lline); | |
char *last = NULL, *res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL && res < lline->text +… | |
last = res; | |
t@@ -106,8 +114,8 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
return SEARCH_NORMAL; | |
} | |
for (int i = line - 1; i >= 0; i--) { | |
- lline = ledit_get_line(buffer, i); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(lline); | |
res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; | |
t@@ -120,8 +128,8 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
} | |
} | |
for (int i = buffer->lines_num - 1; i > line; i--) { | |
- lline = ledit_get_line(buffer, i); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, i); | |
+ ledit_buffer_normalize_line(lline); | |
res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; | |
t@@ -133,8 +141,8 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
return SEARCH_WRAPPED; | |
} | |
} | |
- lline = ledit_get_line(buffer, line); | |
- ledit_normalize_line(lline); | |
+ lline = ledit_buffer_get_line(buffer, line); | |
+ ledit_buffer_normalize_line(lline); | |
res = lline->text + byte; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; | |
diff --git a/theme.c b/theme.c | |
t@@ -0,0 +1,35 @@ | |
+#include <stddef.h> | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xft/Xft.h> | |
+ | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "theme.h" | |
+#include "theme_config.h" | |
+ | |
+ledit_theme * | |
+ledit_theme_create(ledit_common *common) { | |
+ ledit_theme *theme = ledit_malloc(sizeof(ledit_theme)); | |
+ theme->scrollbar_width = SCROLLBAR_WIDTH; | |
+ theme->scrollbar_step = SCROLLBAR_STEP; | |
+ theme->text_size = TEXT_SIZE; | |
+ theme->text_fg_hex = TEXT_FG; | |
+ theme->text_bg_hex = TEXT_BG; | |
+ theme->scrollbar_fg_hex = SCROLLBAR_FG; | |
+ theme->scrollbar_bg_hex = SCROLLBAR_BG; | |
+ XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_FG, &them… | |
+ XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_BG, &them… | |
+ XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_FG, … | |
+ XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_BG, … | |
+ return theme; | |
+} | |
+ | |
+void | |
+ledit_theme_destroy(ledit_common *common, ledit_theme *theme) { | |
+ XftColorFree(common->dpy, common->vis, common->cm, &theme->text_fg); | |
+ XftColorFree(common->dpy, common->vis, common->cm, &theme->text_bg); | |
+ XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_f… | |
+ XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_b… | |
+ free(theme); | |
+} | |
diff --git a/theme.h b/theme.h | |
t@@ -0,0 +1,16 @@ | |
+typedef struct { | |
+ int scrollbar_width; | |
+ int scrollbar_step; | |
+ int text_size; | |
+ XftColor text_fg; | |
+ XftColor text_bg; | |
+ XftColor scrollbar_fg; | |
+ XftColor scrollbar_bg; | |
+ const char *text_fg_hex; | |
+ const char *text_bg_hex; | |
+ const char *scrollbar_fg_hex; | |
+ const char *scrollbar_bg_hex; | |
+} ledit_theme; | |
+ | |
+ledit_theme *ledit_theme_create(ledit_common *common); | |
+void ledit_theme_destroy(ledit_common *common, ledit_theme *theme); | |
diff --git a/theme_config.h b/theme_config.h | |
t@@ -0,0 +1,9 @@ | |
+static const int TEXT_SIZE = 12; | |
+static const char *TEXT_FG = "#000000"; | |
+static const char *TEXT_BG = "#FFFFFF"; | |
+ | |
+/* FIXME: give in units other than pixels */ | |
+static const int SCROLLBAR_WIDTH = 10; | |
+static const int SCROLLBAR_STEP = 10; | |
+static const char *SCROLLBAR_BG = "#CCCCCC"; | |
+static const char *SCROLLBAR_FG = "#000000"; | |
diff --git a/txtbuf.c b/txtbuf.c | |
t@@ -0,0 +1,51 @@ | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+#include "memory.h" | |
+#include "txtbuf.h" | |
+ | |
+txtbuf * | |
+txtbuf_new(void) { | |
+ txtbuf *buf = ledit_malloc(sizeof(txtbuf)); | |
+ buf->text = NULL; | |
+ buf->cap = buf->len = 0; | |
+ return buf; | |
+} | |
+ | |
+void | |
+txtbuf_grow(txtbuf *buf, size_t sz) { | |
+ /* always leave room for extra \0 */ | |
+ if (sz + 1 > buf->cap) { | |
+ /* FIXME: what are the best values here? */ | |
+ buf->cap = buf->cap * 2 > sz + 1 ? buf->cap * 2 : sz + 1; | |
+ buf->text = ledit_realloc(buf->text, buf->cap); | |
+ } | |
+} | |
+ | |
+void | |
+txtbuf_shrink(txtbuf *buf) { | |
+ if ((buf->len + 1) * 4 < buf->cap) { | |
+ buf->cap /= 2; | |
+ buf->text = ledit_realloc(buf->text, buf->cap); | |
+ } | |
+} | |
+ | |
+void | |
+txtbuf_destroy(txtbuf *buf) { | |
+ free(buf->text); | |
+ free(buf); | |
+} | |
+ | |
+void | |
+txtbuf_copy(txtbuf *dst, txtbuf *src) { | |
+ txtbuf_grow(dst, src->len); | |
+ memcpy(dst->text, src->text, src->len); | |
+ dst->len = src->len; | |
+} | |
+ | |
+txtbuf * | |
+txtbuf_dup(txtbuf *src) { | |
+ txtbuf *dst = txtbuf_new(); | |
+ txtbuf_copy(dst, src); | |
+ return dst; | |
+} | |
diff --git a/txtbuf.h b/txtbuf.h | |
t@@ -0,0 +1,11 @@ | |
+typedef struct { | |
+ size_t len, cap; | |
+ char *text; | |
+} txtbuf; | |
+ | |
+txtbuf *txtbuf_new(void); | |
+void txtbuf_grow(txtbuf *buf, size_t sz); | |
+void txtbuf_shrink(txtbuf *buf); | |
+void txtbuf_destroy(txtbuf *buf); | |
+void txtbuf_copy(txtbuf *dst, txtbuf *src); | |
+txtbuf *txtbuf_dup(txtbuf *src); | |
diff --git a/undo.c b/undo.c | |
t@@ -5,14 +5,10 @@ | |
#include <X11/Xlib.h> | |
#include <X11/Xutil.h> | |
#include <pango/pangoxft.h> | |
-/* FIXME: move some parts of common to ledit.c so | |
- this include isn't needed */ | |
-#include <X11/extensions/Xdbe.h> | |
#include "memory.h" | |
#include "common.h" | |
-#include "lbuf.h" | |
-#include "buffer.h" | |
+#include "txtbuf.h" | |
#include "cache.h" | |
#include "undo.h" | |
t@@ -22,7 +18,7 @@ enum operation { | |
}; | |
typedef struct { | |
- lbuf *text; | |
+ txtbuf *text; | |
enum operation type; | |
enum ledit_mode mode; | |
ledit_range op_range; | |
t@@ -38,119 +34,117 @@ struct ledit_undo_stack { | |
int change_mode_group; | |
}; | |
-/* FIXME: maybe make these work directly on the stack instead of buffer */ | |
-void | |
-ledit_init_undo_stack(ledit_buffer *buffer) { | |
- buffer->undo = ledit_malloc(sizeof(ledit_undo_stack)); | |
- buffer->undo->len = buffer->undo->cap = 0; | |
- buffer->undo->cur = -1; | |
- buffer->undo->stack = NULL; | |
- buffer->undo->change_mode_group = 0; | |
+ledit_undo_stack * | |
+ledit_undo_stack_create(void) { | |
+ ledit_undo_stack *undo = ledit_malloc(sizeof(ledit_undo_stack)); | |
+ undo->len = undo->cap = 0; | |
+ undo->cur = -1; | |
+ undo->stack = NULL; | |
+ undo->change_mode_group = 0; | |
+ return undo; | |
} | |
void | |
-ledit_destroy_undo_stack(ledit_buffer *buffer) { | |
- free(buffer->undo->stack); | |
- free(buffer->undo); | |
+ledit_undo_stack_destroy(ledit_undo_stack *undo) { | |
+ free(undo->stack); | |
+ free(undo); | |
} | |
/* FIXME: resize text buffers when they aren't needed anymore */ | |
static undo_elem * | |
-push_undo_elem(ledit_buffer *buffer) { | |
- ledit_undo_stack *s = buffer->undo; | |
- assert(s->cur >= -1); | |
- s->cur++; | |
- s->len = s->cur + 1; | |
- if (s->len > s->cap) { | |
- size_t cap = s->len * 2; | |
- s->stack = ledit_realloc(s->stack, cap * sizeof(undo_elem)); | |
- for (size_t i = s->cap; i < cap; i++) { | |
- s->stack[i].text = NULL; | |
+push_undo_elem(ledit_undo_stack *undo) { | |
+ assert(undo->cur >= -1); | |
+ undo->cur++; | |
+ undo->len = undo->cur + 1; | |
+ if (undo->len > undo->cap) { | |
+ size_t cap = undo->len * 2; | |
+ undo->stack = ledit_realloc(undo->stack, cap * sizeof(undo_ele… | |
+ for (size_t i = undo->cap; i < cap; i++) { | |
+ undo->stack[i].text = NULL; | |
} | |
- s->cap = cap; | |
+ undo->cap = cap; | |
} | |
- return &s->stack[s->cur]; | |
+ return &undo->stack[undo->cur]; | |
} | |
static undo_elem * | |
-peek_undo_elem(ledit_buffer *buffer) { | |
- ledit_undo_stack *s = buffer->undo; | |
- if (s->cur < 0) | |
+peek_undo_elem(ledit_undo_stack *undo) { | |
+ if (undo->cur < 0) | |
return NULL; | |
- return &s->stack[s->cur]; | |
+ return &undo->stack[undo->cur]; | |
} | |
void | |
-ledit_change_mode_group(ledit_buffer *buffer) { | |
- buffer->undo->change_mode_group = 1; | |
+ledit_change_mode_group(ledit_undo_stack *undo) { | |
+ undo->change_mode_group = 1; | |
} | |
-/* FIXME: The current cursor position could be taken directly from the | |
- buffer, but maybe it's better this way to make it a bit more explicit? */ | |
static void | |
push_undo( | |
- ledit_buffer *buffer, lbuf *text, | |
+ ledit_undo_stack *undo, txtbuf *text, | |
ledit_range insert_range, | |
ledit_range cursor_range, | |
- int start_group, enum operation type) { | |
- undo_elem *old = peek_undo_elem(buffer); | |
+ int start_group, enum operation type, | |
+ enum ledit_mode mode) { | |
+ undo_elem *old = peek_undo_elem(undo); | |
int last_group = old == NULL ? 0 : old->group; | |
int last_mode_group = old == NULL ? 0 : old->mode_group; | |
- undo_elem *e = push_undo_elem(buffer); | |
+ undo_elem *e = push_undo_elem(undo); | |
e->group = start_group ? !last_group : last_group; | |
- e->mode_group = buffer->undo->change_mode_group ? !last_mode_group : l… | |
- buffer->undo->change_mode_group = 0; | |
+ e->mode_group = undo->change_mode_group ? !last_mode_group : last_mode… | |
+ undo->change_mode_group = 0; | |
e->op_range = insert_range; | |
e->cursor_range = cursor_range; | |
- e->mode = buffer->state->mode; | |
+ e->mode = mode; | |
e->type = type; | |
if (e->text != NULL) | |
- lbuf_cpy(e->text, text); | |
+ txtbuf_copy(e->text, text); | |
else | |
- e->text = lbuf_dup(text); | |
+ e->text = txtbuf_dup(text); | |
} | |
void | |
ledit_push_undo_insert( | |
- ledit_buffer *buffer, lbuf *text, | |
+ ledit_undo_stack *undo, txtbuf *text, | |
ledit_range insert_range, | |
ledit_range cursor_range, | |
- int start_group) { | |
+ int start_group, enum ledit_mode mode) { | |
push_undo( | |
- buffer, text, insert_range, | |
- cursor_range, start_group, UNDO_INSERT | |
+ undo, text, insert_range, cursor_range, | |
+ start_group, UNDO_INSERT, mode | |
); | |
} | |
void | |
ledit_push_undo_delete( | |
- ledit_buffer *buffer, lbuf *text, | |
+ ledit_undo_stack *undo, txtbuf *text, | |
ledit_range insert_range, | |
ledit_range cursor_range, | |
- int start_group) { | |
+ int start_group, enum ledit_mode mode) { | |
push_undo( | |
- buffer, text, insert_range, | |
- cursor_range, start_group, UNDO_DELETE | |
+ undo, text, insert_range, cursor_range, | |
+ start_group, UNDO_DELETE, mode | |
); | |
} | |
ledit_undo_status | |
-ledit_undo(ledit_buffer *buffer) { | |
+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) { | |
undo_elem *e; | |
- ledit_undo_stack *s = buffer->undo; | |
- if (s->cur < 0) | |
+ if (undo->cur < 0) | |
return UNDO_OLDEST_CHANGE; | |
- int group = s->stack[s->cur].group; | |
- int mode_group = s->stack[s->cur].mode_group; | |
- int min_line = buffer->lines_num - 1; | |
+ int group = undo->stack[undo->cur].group; | |
+ int mode_group = undo->stack[undo->cur].mode_group; | |
+ int min_line = INT_MAX; | |
int mode_group_same = 0; | |
- while (s->cur >= 0 && | |
- (s->stack[s->cur].group == group || (mode_group_same = | |
- ((buffer->state->mode == NORMAL || | |
- buffer->state->mode == VISUAL) && | |
- s->stack[s->cur].mode == INSERT && | |
- s->stack[s->cur].mode_group == mode_group)))) { | |
- e = &s->stack[s->cur]; | |
+ while (undo->cur >= 0 && | |
+ (undo->stack[undo->cur].group == group || (mode_group_same = | |
+ ((mode == NORMAL || | |
+ mode == VISUAL) && | |
+ undo->stack[undo->cur].mode == INSERT && | |
+ undo->stack[undo->cur].mode_group == mode_group)))) { | |
+ e = &undo->stack[undo->cur]; | |
/* if the mode group is the same, we need to update the group, | |
otherwise it can happen that some iterations are performed | |
because the mode group (but not the normal group) is the | |
t@@ -161,17 +155,17 @@ ledit_undo(ledit_buffer *buffer) { | |
switch (e->type) { | |
case UNDO_INSERT: | |
/* FIXME: should the paste buffer also be modified? */ | |
- ledit_delete_range_base( | |
- buffer, 0, | |
+ delete_cb( | |
+ callback_data, | |
e->op_range.line1, e->op_range.byte1, | |
- e->op_range.line2, e->op_range.byte2, | |
- NULL, NULL, NULL, NULL | |
+ e->op_range.line2, e->op_range.byte2 | |
); | |
break; | |
case UNDO_DELETE: | |
- ledit_insert_text_with_newlines_base( | |
- buffer, e->op_range.line1, e->op_range.byte1, | |
- e->text->text, e->text->len, NULL, NULL | |
+ insert_cb( | |
+ callback_data, | |
+ e->op_range.line1, e->op_range.byte1, | |
+ e->text->text, e->text->len | |
); | |
break; | |
default: | |
t@@ -181,52 +175,48 @@ ledit_undo(ledit_buffer *buffer) { | |
/* FIXME: make sure this is always sorted already */ | |
if (e->op_range.line1 < min_line) | |
min_line = e->op_range.line1; | |
- s->cur--; | |
- buffer->cur_line = e->cursor_range.line1; | |
- buffer->cur_index = e->cursor_range.byte1; | |
- } | |
- if (buffer->state->mode == NORMAL) { | |
- buffer->cur_index = ledit_get_legal_normal_pos( | |
- buffer, buffer->cur_line, buffer->cur_index | |
- ); | |
+ undo->cur--; | |
+ *cur_line_ret = e->cursor_range.line1; | |
+ *cur_index_ret = e->cursor_range.byte1; | |
} | |
- ledit_recalc_from_line(buffer, min_line > 0 ? min_line - 1 : min_line); | |
+ *min_line_ret = min_line; | |
return UNDO_NORMAL; | |
} | |
ledit_undo_status | |
-ledit_redo(ledit_buffer *buffer) { | |
+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) { | |
undo_elem *e; | |
- ledit_undo_stack *s = buffer->undo; | |
- if (s->cur >= s->len - 1) | |
+ if (undo->cur >= undo->len - 1) | |
return UNDO_NEWEST_CHANGE; | |
- s->cur++; | |
- int group = s->stack[s->cur].group; | |
- int mode_group = s->stack[s->cur].mode_group; | |
- int min_line = buffer->lines_num - 1; | |
+ undo->cur++; | |
+ int group = undo->stack[undo->cur].group; | |
+ int mode_group = undo->stack[undo->cur].mode_group; | |
+ int min_line = INT_MAX; | |
int mode_group_same = 0; | |
- while (s->cur < s->len && | |
- (s->stack[s->cur].group == group || (mode_group_same = | |
- ((buffer->state->mode == NORMAL || | |
- buffer->state->mode == VISUAL) && | |
- s->stack[s->cur].mode == INSERT && | |
- s->stack[s->cur].mode_group == mode_group)))) { | |
- e = &s->stack[s->cur]; | |
+ while (undo->cur < undo->len && | |
+ (undo->stack[undo->cur].group == group || (mode_group_same = | |
+ ((mode == NORMAL || | |
+ mode == VISUAL) && | |
+ undo->stack[undo->cur].mode == INSERT && | |
+ undo->stack[undo->cur].mode_group == mode_group)))) { | |
+ e = &undo->stack[undo->cur]; | |
if (mode_group_same) | |
group = e->group; | |
switch (e->type) { | |
case UNDO_INSERT: | |
- ledit_insert_text_with_newlines_base( | |
- buffer, e->op_range.line1, e->op_range.byte1, | |
- e->text->text, e->text->len, NULL, NULL | |
+ insert_cb( | |
+ callback_data, | |
+ e->op_range.line1, e->op_range.byte1, | |
+ e->text->text, e->text->len | |
); | |
break; | |
case UNDO_DELETE: | |
- ledit_delete_range_base( | |
- buffer, 0, | |
+ delete_cb( | |
+ callback_data, | |
e->op_range.line1, e->op_range.byte1, | |
- e->op_range.line2, e->op_range.byte2, | |
- NULL, NULL, NULL, NULL | |
+ e->op_range.line2, e->op_range.byte2 | |
); | |
break; | |
default: | |
t@@ -235,16 +225,11 @@ ledit_redo(ledit_buffer *buffer) { | |
} | |
if (e->op_range.line1 < min_line) | |
min_line = e->op_range.line1; | |
- s->cur++; | |
- buffer->cur_line = e->cursor_range.line2; | |
- buffer->cur_index = e->cursor_range.byte2; | |
- } | |
- s->cur--; | |
- if (buffer->state->mode == NORMAL) { | |
- buffer->cur_index = ledit_get_legal_normal_pos( | |
- buffer, buffer->cur_line, buffer->cur_index | |
- ); | |
+ undo->cur++; | |
+ *cur_line_ret = e->cursor_range.line2; | |
+ *cur_index_ret = e->cursor_range.byte2; | |
} | |
- ledit_recalc_from_line(buffer, min_line > 0 ? min_line - 1 : min_line); | |
+ undo->cur--; | |
+ *min_line_ret = min_line; | |
return UNDO_NORMAL; | |
} | |
diff --git a/undo.h b/undo.h | |
t@@ -4,20 +4,30 @@ typedef enum { | |
UNDO_NEWEST_CHANGE | |
} ledit_undo_status; | |
-void ledit_init_undo_stack(ledit_buffer *buffer); | |
-void ledit_destroy_undo_stack(ledit_buffer *buffer); | |
-ledit_undo_status ledit_undo(ledit_buffer *buffer); | |
-ledit_undo_status ledit_redo(ledit_buffer *buffer); | |
+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); | |
+ | |
+ledit_undo_stack *ledit_undo_stack_create(void); | |
+void ledit_undo_stack_destroy(ledit_undo_stack *undo); | |
+void ledit_change_mode_group(ledit_undo_stack *undo); | |
void ledit_push_undo_insert( | |
- ledit_buffer *buffer, lbuf *text, | |
- ledit_range insert_range, | |
- ledit_range cursor_range, | |
- int start_group | |
+ ledit_undo_stack *undo, txtbuf *text, | |
+ ledit_range insert_range, ledit_range cursor_range, | |
+ int start_group, enum ledit_mode mode | |
); | |
void ledit_push_undo_delete( | |
- ledit_buffer *buffer, lbuf *text, | |
- ledit_range insert_range, | |
- ledit_range cursor_range, | |
- int start_group | |
+ ledit_undo_stack *undo, txtbuf *text, | |
+ ledit_range insert_range, ledit_range cursor_range, | |
+ int start_group, enum ledit_mode mode | |
+); | |
+ledit_undo_status ledit_undo( | |
+ ledit_undo_stack *undo, enum ledit_mode mode, | |
+ void *callback_data, undo_insert_callback insert_cb, undo_delete_callback … | |
+ int *cur_line_ret, int *cur_index_ret, int *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 … | |
+ int *cur_line_ret, int *cur_index_ret, int *min_line_ret | |
); | |
-void ledit_change_mode_group(ledit_buffer *buffer); | |
diff --git a/util.c b/util.c | |
t@@ -5,33 +5,36 @@ | |
#include "memory.h" | |
#include "common.h" | |
+#include "txtbuf.h" | |
+#include "theme.h" | |
+#include "window.h" | |
#include "util.h" | |
ledit_draw * | |
-ledit_create_draw(ledit_common_state *state, int w, int h) { | |
+ledit_draw_create(ledit_window *window, int w, int h) { | |
ledit_draw *draw = ledit_malloc(sizeof(ledit_draw)); | |
draw->w = w; | |
draw->h = h; | |
draw->pixmap = XCreatePixmap( | |
- state->dpy, state->drawable, w, h, state->depth | |
+ window->common->dpy, window->drawable, w, h, window->common->depth | |
); | |
draw->xftdraw = XftDrawCreate( | |
- state->dpy, draw->pixmap, state->vis, state->cm | |
+ window->common->dpy, draw->pixmap, window->common->vis, window->co… | |
); | |
return draw; | |
} | |
void | |
-ledit_grow_draw(ledit_common_state *state, ledit_draw *draw, int w, int h) { | |
+ledit_draw_grow(ledit_window *window, ledit_draw *draw, int w, int h) { | |
/* FIXME: sensible default pixmap sizes here */ | |
/* FIXME: maybe shrink the pixmaps at some point */ | |
if (draw->w < w || draw->h < h) { | |
draw->w = w > draw->w ? w + 10 : draw->w; | |
draw->h = h > draw->h ? h + 10 : draw->h; | |
- XFreePixmap(state->dpy, draw->pixmap); | |
+ XFreePixmap(window->common->dpy, draw->pixmap); | |
draw->pixmap = XCreatePixmap( | |
- state->dpy, state->drawable, | |
- draw->w, draw->h, state->depth | |
+ window->common->dpy, window->drawable, | |
+ draw->w, draw->h, window->common->depth | |
); | |
XftDrawChange(draw->xftdraw, draw->pixmap); | |
} | |
diff --git a/util.h b/util.h | |
t@@ -4,5 +4,5 @@ typedef struct { | |
int w, h; | |
} ledit_draw; | |
-ledit_draw *ledit_create_draw(ledit_common_state *state, int w, int h); | |
-void ledit_grow_draw(ledit_common_state *state, ledit_draw *draw, int w, int h… | |
+ledit_draw *ledit_draw_create(ledit_window *window, int w, int h); | |
+void ledit_draw_grow(ledit_window *window, ledit_draw *draw, int w, int h); | |
diff --git a/window.c b/window.c | |
t@@ -0,0 +1,769 @@ | |
+#include <math.h> | |
+#include <stdio.h> | |
+#include <assert.h> | |
+#include <stdlib.h> | |
+ | |
+#include <X11/Xlib.h> | |
+#include <X11/Xatom.h> | |
+#include <X11/Xutil.h> | |
+#include <pango/pangoxft.h> | |
+#include <X11/XKBlib.h> | |
+#include <X11/extensions/XKBrules.h> | |
+#include <X11/extensions/Xdbe.h> | |
+ | |
+#include "memory.h" | |
+#include "common.h" | |
+#include "txtbuf.h" | |
+#include "theme.h" | |
+#include "window.h" | |
+#include "util.h" | |
+ | |
+struct bottom_bar { | |
+ /* FIXME: encapsulate layout, width, draw a bit */ | |
+ PangoLayout *mode; | |
+ ledit_draw *mode_draw; | |
+ int mode_w, mode_h; | |
+ PangoLayout *ruler; | |
+ ledit_draw *ruler_draw; | |
+ int ruler_w, ruler_h; | |
+ PangoLayout *line; | |
+ ledit_draw *line_draw; | |
+ int line_w, line_h; | |
+ char *line_text; | |
+ int line_alloc, line_len; | |
+ int line_cur_pos; | |
+}; | |
+ | |
+/* clipboard handling largely stolen from st (simple terminal) */ | |
+ | |
+#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+ | |
+struct { | |
+ txtbuf *primary; | |
+ char *clipboard; | |
+} xsel = {NULL, NULL}; | |
+ | |
+static void recalc_text_size(ledit_window *window); | |
+static void get_scroll_pos_height(ledit_window *windown, double *pos, double *… | |
+static void set_scroll_pos(ledit_window *window, double pos); | |
+static void clipboard_propnotify(ledit_window *window, XEvent *e); | |
+static void clipboard_selnotify(ledit_window *window, XEvent *e); | |
+static void clipboard_selrequest(ledit_window *window, XEvent *e); | |
+ | |
+txtbuf * | |
+ledit_window_get_primary_clipboard_buffer(void) { | |
+ /* FIXME: check if NULL */ | |
+ return xsel.primary; | |
+} | |
+ | |
+/* FIXME static void ensure_cursor_shown(void); */ | |
+ | |
+/* FIXME: shouldn't window->bottom_text_shown also be true when message_shown?… | |
+static void | |
+recalc_text_size(ledit_window *window) { | |
+ int bar_h = window->bb->mode_h; | |
+ if ((window->bottom_text_shown || window->message_shown) && window->bb… | |
+ bar_h = window->bb->line_h; | |
+ window->text_w = window->w - window->theme->scrollbar_width; | |
+ window->text_h = window->h - bar_h; | |
+} | |
+ | |
+/* FIXME: allow lines longer than window width to be displayed properly */ | |
+void | |
+ledit_window_insert_bottom_bar_text(ledit_window *window, char *text, int len)… | |
+ assert(len >= -1); | |
+ assert(window->bb->line_cur_pos <= window->bb->line_alloc); | |
+ | |
+ if (len == -1) | |
+ len = strlen(text); | |
+ /* \0 not included in len */ | |
+ if (window->bb->line_len + len + 1 > window->bb->line_alloc || window-… | |
+ /* FIXME: read up on what the best values are here */ | |
+ window->bb->line_alloc = | |
+ window->bb->line_alloc * 2 > window->bb->line_len + len + … | |
+ window->bb->line_alloc * 2 : | |
+ window->bb->line_len + len + 1; | |
+ window->bb->line_text = ledit_realloc(window->bb->line_text, w… | |
+ } | |
+ memmove( | |
+ window->bb->line_text + window->bb->line_cur_pos + len, | |
+ window->bb->line_text + window->bb->line_cur_pos, | |
+ window->bb->line_len - window->bb->line_cur_pos | |
+ ); | |
+ memcpy(window->bb->line_text + window->bb->line_cur_pos, text, len); | |
+ window->bb->line_len += len; | |
+ window->bb->line_text[window->bb->line_len] = '\0'; | |
+ pango_layout_set_text(window->bb->line, window->bb->line_text, window-… | |
+ pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &wi… | |
+ ledit_draw_grow(window, window->bb->line_draw, window->bb->line_w, win… | |
+ XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->text_bg, 0… | |
+ pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme… | |
+ recalc_text_size(window); | |
+} | |
+ | |
+void | |
+ledit_window_set_bottom_bar_cursor(ledit_window *window, int byte_pos) { | |
+ /* FIXME: check if valid? */ | |
+ window->bb->line_cur_pos = byte_pos; | |
+} | |
+ | |
+int | |
+ledit_window_get_bottom_bar_cursor(ledit_window *window) { | |
+ return window->bb->line_cur_pos; | |
+} | |
+ | |
+void | |
+ledit_window_set_bottom_bar_text_shown(ledit_window *window, int shown) { | |
+ window->bottom_text_shown = shown; | |
+} | |
+ | |
+int | |
+ledit_window_bottom_bar_text_shown(ledit_window *window) { | |
+ return window->bottom_text_shown; | |
+} | |
+ | |
+void | |
+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); | |
+} | |
+ | |
+char * | |
+ledit_window_get_bottom_bar_text(ledit_window *window) { | |
+ return window->bb->line_text; | |
+} | |
+ | |
+void | |
+ledit_window_show_message(ledit_window *window, char *text, int len) { | |
+ ledit_window_set_bottom_bar_text(window, text, len); | |
+ /* FIXME: rename these */ | |
+ window->bottom_text_shown = 0; | |
+ window->message_shown = 1; | |
+} | |
+ | |
+int | |
+ledit_window_message_shown(ledit_window *window) { | |
+ return window->message_shown; | |
+} | |
+ | |
+void | |
+ledit_window_hide_message(ledit_window *window) { | |
+ window->message_shown = 0; | |
+} | |
+ | |
+void | |
+ledit_window_set_mode(ledit_window *window, enum ledit_mode mode) { | |
+ switch (mode) { | |
+ case NORMAL: | |
+ pango_layout_set_text(window->bb->mode, "Normal", -1); | |
+ break; | |
+ case VISUAL: | |
+ pango_layout_set_text(window->bb->mode, "Visual", -1); | |
+ break; | |
+ case INSERT: | |
+ pango_layout_set_text(window->bb->mode, "Insert", -1); | |
+ break; | |
+ default: | |
+ pango_layout_set_text(window->bb->mode, "ledit is bugg… | |
+ break; | |
+ } | |
+ pango_layout_get_pixel_size(window->bb->mode, &window->bb->mode_w, &wi… | |
+ ledit_draw_grow(window, window->bb->mode_draw, window->bb->mode_w, win… | |
+ XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->text_bg, 0… | |
+ pango_xft_render_layout(window->bb->mode_draw->xftdraw, &window->theme… | |
+ recalc_text_size(window); | |
+} | |
+ | |
+/* FIXME: give these functions more sensible names */ | |
+static void | |
+get_scroll_pos_height(ledit_window *window, double *pos, double *height) { | |
+ *height = ((double)window->text_h / window->scroll_max) * window->text… | |
+ *pos = (window->scroll_offset / | |
+ (window->scroll_max - window->text_h)) * (window->text_h - *hei… | |
+} | |
+ | |
+static void | |
+set_scroll_pos(ledit_window *window, double pos) { | |
+ window->scroll_offset = pos * (window->scroll_max / (double)window->te… | |
+ if (window->scroll_offset < 0) | |
+ window->scroll_offset = 0; | |
+ if (window->scroll_offset + window->text_h > window->scroll_max) | |
+ window->scroll_offset = window->scroll_max - window->text_h; | |
+ /* FIXME: check for overflow */ | |
+ if (window->scroll_callback) | |
+ window->scroll_callback(window->scroll_cb_data, (long)window->… | |
+} | |
+ | |
+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; | |
+} | |
+ | |
+void | |
+ledit_window_set_scroll_pos(ledit_window *window, long pos) { | |
+ window->scroll_offset = pos; | |
+ window->common->redraw = 1; | |
+} | |
+ | |
+void | |
+ledit_window_set_scroll_callback(ledit_window *window, void (*cb)(void *, long… | |
+ window->scroll_callback = cb; | |
+ window->scroll_cb_data = data; | |
+} | |
+ | |
+void | |
+ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, char … | |
+ window->paste_callback = cb; | |
+ window->paste_cb_data = data; | |
+} | |
+ | |
+void | |
+ledit_window_set_button_callback(ledit_window *window, void (*cb)(void *, XEve… | |
+ window->button_callback = cb; | |
+ window->button_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? */ | |
+int ximopen(ledit_window *window, Display *dpy); | |
+void ximinstantiate(Display *dpy, XPointer client, XPointer call); | |
+void ximdestroy(XIM xim, XPointer client, XPointer call); | |
+int xicdestroy(XIC xim, XPointer client, XPointer call); | |
+ | |
+/* blatantly stolen from st */ | |
+int | |
+ximopen(ledit_window *window, Display *dpy) | |
+{ | |
+ XIMCallback imdestroy = { .client_data = (XPointer)window, .callback =… | |
+ XICCallback icdestroy = { .client_data = (XPointer)window, .callback =… | |
+ | |
+ window->xim = XOpenIM(dpy, NULL, NULL, NULL); | |
+ if (window->xim == NULL) | |
+ return 0; | |
+ | |
+ if (XSetIMValues(window->xim, XNDestroyCallback, &imdestroy, NULL)) { | |
+ fprintf( | |
+ stderr, "XSetIMValues: Could not set XNDestroyCallback.\n" | |
+ ); | |
+ } | |
+ | |
+ window->spotlist = XVaCreateNestedList(0, XNSpotLocation, &window->spo… | |
+ | |
+ if (window->xic == NULL) { | |
+ window->xic = XCreateIC( | |
+ window->xim, XNInputStyle, | |
+ XIMPreeditNothing | XIMStatusNothing, | |
+ XNClientWindow, window->xwin, | |
+ XNDestroyCallback, &icdestroy, NULL | |
+ ); | |
+ } | |
+ if (window->xic == NULL) | |
+ fprintf(stderr, "XCreateIC: Could not create input context.\n"… | |
+ | |
+ return 1; | |
+} | |
+ | |
+void | |
+ximinstantiate(Display *dpy, XPointer client, XPointer call) | |
+{ | |
+ (void)call; | |
+ ledit_window *window = (ledit_window *)client; | |
+ if (ximopen(window, dpy)) { | |
+ XUnregisterIMInstantiateCallback( | |
+ dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)window | |
+ ); | |
+ } | |
+} | |
+ | |
+void | |
+ximdestroy(XIM xim, XPointer client, XPointer call) | |
+{ | |
+ (void)xim; | |
+ (void)call; | |
+ ledit_window *window = (ledit_window *)client; | |
+ window->xim = NULL; | |
+ XRegisterIMInstantiateCallback( | |
+ window->common->dpy, NULL, NULL, NULL, ximinstantiate, (XPointer)w… | |
+ ); | |
+ XFree(window->spotlist); | |
+} | |
+ | |
+int | |
+xicdestroy(XIC xim, XPointer client, XPointer call) | |
+{ | |
+ (void)xim; | |
+ (void)call; | |
+ ledit_window *window = (ledit_window *)client; | |
+ window->xic = NULL; | |
+ return 1; | |
+} | |
+ | |
+void | |
+xximspot(ledit_window *window, int x, int y) { | |
+ if (window->xic == NULL) | |
+ return; | |
+ /* FIXME! */ | |
+ window->spot.x = x; | |
+ window->spot.y = y; | |
+ XSetICValues(window->xic, XNPreeditAttributes, window->spotlist, NULL); | |
+} | |
+ | |
+ledit_window * | |
+ledit_window_create(ledit_common *common, ledit_theme *theme) { | |
+ XSetWindowAttributes attrs; | |
+ XGCValues gcv; | |
+ | |
+ ledit_window *window = ledit_malloc(sizeof(ledit_window)); | |
+ | |
+ window->scroll_dragging = 0; | |
+ window->scroll_grab_handle = 0; | |
+ window->w = 500; | |
+ window->h = 500; | |
+ | |
+ memset(&window->wattrs, 0, sizeof(attrs)); | |
+ window->wattrs.background_pixel = BlackPixel(common->dpy, common->scre… | |
+ window->wattrs.colormap = common->cm; | |
+ /* this causes the window contents to be kept | |
+ * when it is resized, leading to less flicker */ | |
+ window->wattrs.bit_gravity = NorthWestGravity; | |
+ /* FIXME: FocusChangeMask? */ | |
+ window->wattrs.event_mask = KeyPressMask | | |
+ ExposureMask | VisibilityChangeMask | StructureNotifyMask | | |
+ ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; | |
+ window->xwin = XCreateWindow( | |
+ common->dpy, DefaultRootWindow(common->dpy), 0, 0, | |
+ window->w, window->h, 0, common->depth, | |
+ InputOutput, common->vis, | |
+ CWBackPixel | CWColormap | CWBitGravity | CWEventMask, &window->wa… | |
+ ); | |
+ XSetStandardProperties(common->dpy, window->xwin, "ledit", NULL, None,… | |
+ | |
+ window->back_buf = XdbeAllocateBackBufferName( | |
+ common->dpy, window->xwin, XdbeBackground | |
+ ); | |
+ window->drawable = window->back_buf; | |
+ | |
+ memset(&gcv, 0, sizeof(gcv)); | |
+ gcv.line_width = 1; | |
+ window->gc = XCreateGC(common->dpy, window->back_buf, GCLineWidth, &gc… | |
+ | |
+ /* FIXME: move to common */ | |
+ window->fontmap = pango_xft_get_font_map(common->dpy, common->screen); | |
+ window->context = pango_font_map_create_context(window->fontmap); | |
+ | |
+ /* FIXME: theme font name */ | |
+ window->font = pango_font_description_from_string("Monospace"); | |
+ pango_font_description_set_size(window->font, theme->text_size * PANGO… | |
+ | |
+ window->wm_delete_msg = XInternAtom(common->dpy, "WM_DELETE_WINDOW", F… | |
+ XSetWMProtocols(common->dpy, window->xwin, &window->wm_delete_msg, 1); | |
+ | |
+ window->common = common; | |
+ window->theme = theme; | |
+ | |
+ /* FIXME: theme font */ | |
+ window->bb = ledit_malloc(sizeof(bottom_bar)); | |
+ window->bb->mode = pango_layout_new(window->context); | |
+ pango_layout_set_font_description(window->bb->mode, window->font); | |
+ /* FIXME: only create "dummy draw" at first and create with proper siz… | |
+ window->bb->mode_draw = ledit_draw_create(window, 10, 10); | |
+ window->bb->line = pango_layout_new(window->context); | |
+ pango_layout_set_font_description(window->bb->line, window->font); | |
+ window->bb->line_draw = ledit_draw_create(window, 10, 10); | |
+ window->bb->line_w = window->bb->line_h = 10; | |
+ window->bb->line_text = NULL; | |
+ window->bb->line_alloc = window->bb->line_len = 0; | |
+ window->bb->line_cur_pos = 0; | |
+ window->bottom_text_shown = 0; | |
+ window->message_shown = 0; | |
+ | |
+ window->xim = NULL; | |
+ window->xic = NULL; | |
+ if (!ximopen(window, common->dpy)) { | |
+ XRegisterIMInstantiateCallback( | |
+ common->dpy, NULL, NULL, NULL, | |
+ ximinstantiate, (XPointer)window | |
+ ); | |
+ } | |
+ | |
+ XMapWindow(common->dpy, window->xwin); | |
+ | |
+ /* FIXME: why is this part of the window? */ | |
+ window->xtarget = XInternAtom(common->dpy, "UTF8_STRING", 0); | |
+ window->paste_callback = NULL; | |
+ if (window->xtarget == None) | |
+ window->xtarget = XA_STRING; | |
+ | |
+ /* | |
+ ledit_clear_window(window); | |
+ ledit_redraw_window(window); | |
+ */ | |
+ if (xsel.primary == NULL) | |
+ xsel.primary = txtbuf_new(); | |
+ /* FIXME: maybe delay this (i.e. move to different function)? */ | |
+ XMapWindow(common->dpy, window->xwin); | |
+ | |
+ return window; | |
+} | |
+ | |
+void | |
+ledit_window_destroy(ledit_window *window) { | |
+ /* FIXME: cleanup everything else */ | |
+ XDestroyWindow(window->common->dpy, window->xwin); | |
+} | |
+ | |
+void | |
+ledit_window_clear(ledit_window *window) { | |
+ XSetForeground(window->common->dpy, window->gc, window->theme->text_bg… | |
+ XFillRectangle( | |
+ window->common->dpy, window->back_buf, window->gc, 0, 0, window->w… | |
+ ); | |
+} | |
+ | |
+void | |
+ledit_window_redraw(ledit_window *window) { | |
+ ledit_theme *t = window->theme; | |
+ if (window->scroll_max > window->text_h) { | |
+ XSetForeground(window->common->dpy, window->gc, t->scrollbar_b… | |
+ XFillRectangle( | |
+ window->common->dpy, window->drawable, window->gc, | |
+ window->w - t->scrollbar_width, 0, t->scrollbar_width, win… | |
+ ); | |
+ XSetForeground(window->common->dpy, window->gc, t->text_fg.pix… | |
+ double scroll_h, scroll_y; | |
+ get_scroll_pos_height(window, &scroll_y, &scroll_h); | |
+ XFillRectangle( | |
+ window->common->dpy, window->drawable, window->gc, | |
+ window->w - t->scrollbar_width, (int)round(scroll_y), | |
+ t->scrollbar_width, (int)round(scroll_h) | |
+ ); | |
+ } | |
+ XSetForeground(window->common->dpy, window->gc, t->text_bg.pixel); | |
+ /* FIXME: allow different color for bar */ | |
+ XFillRectangle( | |
+ window->common->dpy, window->drawable, window->gc, | |
+ 0, window->text_h, | |
+ window->w, window->h - window->text_h | |
+ ); | |
+ if (window->bottom_text_shown) { | |
+ /* move input method position to cursor */ | |
+ PangoRectangle strong, weak; | |
+ pango_layout_get_cursor_pos( | |
+ window->bb->line, window->bb->line_cur_pos, &strong, &weak | |
+ ); | |
+ /* FIXME: Is this the best position? The bottom of the lins is | |
+ just the bottom of the window, so an input method box will | |
+ have to be moved out of the way anyways (fcitx just moves it | |
+ up a bit so it sort of works) */ | |
+ xximspot(window, strong.x / PANGO_SCALE, window->h); | |
+ } | |
+ if (window->bottom_text_shown || window->message_shown) { | |
+ XCopyArea( | |
+ window->common->dpy, window->bb->line_draw->pixmap, | |
+ window->drawable, window->gc, | |
+ 0, 0, window->bb->line_w, window->bb->line_h, | |
+ 0, window->text_h | |
+ ); | |
+ } else { | |
+ XCopyArea( | |
+ window->common->dpy, window->bb->mode_draw->pixmap, | |
+ window->drawable, window->gc, | |
+ 0, 0, window->bb->mode_w, window->bb->mode_h, | |
+ window->w - window->bb->mode_w, window->text_h | |
+ ); | |
+ } | |
+ | |
+ XdbeSwapInfo swap_info; | |
+ swap_info.swap_window = window->xwin; | |
+ swap_info.swap_action = XdbeBackground; | |
+ | |
+ if (!XdbeSwapBuffers(window->common->dpy, &swap_info, 1)) | |
+ exit(1); | |
+ XFlush(window->common->dpy); | |
+} | |
+ | |
+void | |
+ledit_window_get_textview_size(ledit_window *window, int *w_ret, int *h_ret) { | |
+ *w_ret = window->text_w; | |
+ *h_ret = window->text_h; | |
+} | |
+ | |
+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; | |
+} | |
+ | |
+void | |
+clipboard_primary_to_clipboard(ledit_window *window) { | |
+ Atom clipboard; | |
+ | |
+ free(xsel.clipboard); | |
+ xsel.clipboard = NULL; | |
+ | |
+ /* FIXME: don't copy if text empty (no selection)? */ | |
+ if (xsel.primary->text != NULL) { | |
+ xsel.clipboard = ledit_strdup(xsel.primary->text); | |
+ clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0); | |
+ XSetSelectionOwner(window->common->dpy, clipboard, window->xwi… | |
+ } | |
+} | |
+ | |
+void | |
+clipboard_paste_clipboard(ledit_window *window) { | |
+ Atom clipboard; | |
+ | |
+ clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0); | |
+ XConvertSelection(window->common->dpy, clipboard, window->xtarget, cli… | |
+} | |
+ | |
+void | |
+clipboard_paste_primary(ledit_window *window) { | |
+ XConvertSelection(window->common->dpy, XA_PRIMARY, window->xtarget, XA… | |
+} | |
+ | |
+static void | |
+clipboard_propnotify(ledit_window *window, XEvent *e) { | |
+ XPropertyEvent *xpev; | |
+ Atom clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0); | |
+ | |
+ xpev = &e->xproperty; | |
+ if (xpev->state == PropertyNewValue && | |
+ (xpev->atom == XA_PRIMARY || | |
+ xpev->atom == clipboard)) { | |
+ clipboard_selnotify(window, e); | |
+ } | |
+} | |
+ | |
+static void | |
+clipboard_selnotify(ledit_window *window, XEvent *e) { | |
+ unsigned long nitems, ofs, rem; | |
+ int format; | |
+ unsigned char *data; | |
+ Atom type, incratom, property = None; | |
+ | |
+ incratom = XInternAtom(window->common->dpy, "INCR", 0); | |
+ | |
+ ofs = 0; | |
+ if (e->type == SelectionNotify) { | |
+ property = e->xselection.property; | |
+ } else if (e->type == PropertyNotify) { | |
+ property = e->xproperty.atom; | |
+ } | |
+ | |
+ if (property == None) | |
+ return; | |
+ | |
+ do { | |
+ /* FIXME: proper error logging */ | |
+ if (XGetWindowProperty(window->common->dpy, window->xwin, prop… | |
+ BUFSIZ/4, False, AnyPropertyType, | |
+ &type, &format, &nitems, &rem, | |
+ &data)) { | |
+ fprintf(stderr, "Clipboard allocation failed\n"); | |
+ return; | |
+ } | |
+ | |
+ if (e->type == PropertyNotify && nitems == 0 && rem == 0) { | |
+ /* | |
+ * If there is some PropertyNotify with no data, then | |
+ * this is the signal of the selection owner that all | |
+ * data has been transferred. We won't need to receive | |
+ * PropertyNotify events anymore. | |
+ */ | |
+ MODBIT(window->wattrs.event_mask, 0, PropertyChangeMas… | |
+ XChangeWindowAttributes(window->common->dpy, window->x… | |
+ /* remove callback - this has to be set again the next… | |
+ something is pasted */ | |
+ window->paste_callback = NULL; | |
+ window->paste_cb_data = NULL; | |
+ } | |
+ | |
+ if (type == incratom) { | |
+ /* | |
+ * Activate the PropertyNotify events so we receive | |
+ * when the selection owner sends us the next | |
+ * chunk of data. | |
+ */ | |
+ MODBIT(window->wattrs.event_mask, 1, PropertyChangeMas… | |
+ XChangeWindowAttributes(window->common->dpy, window->x… | |
+ | |
+ /* | |
+ * Deleting the property is the transfer start signal. | |
+ */ | |
+ XDeleteProperty(window->common->dpy, window->xwin, (in… | |
+ continue; | |
+ } | |
+ | |
+ /* FIXME: XGetWindowProperty takes data as unsigned char, so i… | |
+ if (window->paste_callback) | |
+ window->paste_callback(window->paste_cb_data, (char *)… | |
+ XFree(data); | |
+ /* number of 32-bit chunks returned */ | |
+ ofs += nitems * format / 32; | |
+ } while (rem > 0); | |
+ | |
+ /* | |
+ * Deleting the property again tells the selection owner to send the | |
+ * next data chunk in the property. | |
+ */ | |
+ XDeleteProperty(window->common->dpy, window->xwin, (int)property); | |
+} | |
+ | |
+static void | |
+clipboard_selrequest(ledit_window *window, XEvent *e) | |
+{ | |
+ XSelectionRequestEvent *xsre; | |
+ XSelectionEvent xev; | |
+ Atom xa_targets, string, clipboard; | |
+ char *seltext; | |
+ | |
+ xsre = (XSelectionRequestEvent *) e; | |
+ xev.type = SelectionNotify; | |
+ xev.requestor = xsre->requestor; | |
+ xev.selection = xsre->selection; | |
+ xev.target = xsre->target; | |
+ xev.time = xsre->time; | |
+ if (xsre->property == None) | |
+ xsre->property = xsre->target; | |
+ | |
+ /* reject */ | |
+ xev.property = None; | |
+ | |
+ xa_targets = XInternAtom(window->common->dpy, "TARGETS", 0); | |
+ if (xsre->target == xa_targets) { | |
+ /* respond with the supported type */ | |
+ string = window->xtarget; | |
+ XChangeProperty(xsre->display, xsre->requestor, xsre->property, | |
+ XA_ATOM, 32, PropModeReplace, | |
+ (unsigned char *) &string, 1); | |
+ xev.property = xsre->property; | |
+ } else if (xsre->target == window->xtarget || xsre->target == XA_STRIN… | |
+ /* | |
+ * xith XA_STRING non ascii characters may be incorrect in the | |
+ * requestor. It is not our problem, use utf8. | |
+ */ | |
+ clipboard = XInternAtom(window->common->dpy, "CLIPBOARD", 0); | |
+ if (xsre->selection == XA_PRIMARY) { | |
+ seltext = xsel.primary->text; | |
+ } else if (xsre->selection == clipboard) { | |
+ seltext = xsel.clipboard; | |
+ } else { | |
+ fprintf(stderr, | |
+ "Unhandled clipboard selection 0x%lx\n", | |
+ xsre->selection); | |
+ return; | |
+ } | |
+ if (seltext != NULL) { | |
+ XChangeProperty(xsre->display, xsre->requestor, | |
+ xsre->property, xsre->target, | |
+ 8, PropModeReplace, | |
+ (unsigned char *)seltext, strlen(selte… | |
+ xev.property = xsre->property; | |
+ } | |
+ } | |
+ | |
+ /* all done, send a notification to the listener */ | |
+ if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) | |
+ fprintf(stderr, "Error sending SelectionNotify event\n"); | |
+} | |
+ | |
+/* FIXME: improve set_scroll_pos; make it a bit clearer */ | |
+int | |
+ledit_window_button_press(ledit_window *window, XEvent *event) { | |
+ int x, y; | |
+ double scroll_h, scroll_y; | |
+ switch (event->xbutton.button) { | |
+ case Button1: | |
+ get_scroll_pos_height(window, &scroll_y, &scroll_h); | |
+ x = event->xbutton.x; | |
+ y = event->xbutton.y; | |
+ if (x >= window->text_w) { | |
+ window->scroll_dragging = 1; | |
+ window->scroll_grab_handle = y; | |
+ if (y < scroll_y || y > scroll_y + scroll_h) { | |
+ double new_scroll_y = y - scroll_h / 2; | |
+ set_scroll_pos(window, new_scroll_y); | |
+ } | |
+ return 1; | |
+ } else if (y < window->text_h) { | |
+ if (window->button_callback) | |
+ window->button_callback(window->button… | |
+ return 1; | |
+ } | |
+ break; | |
+ case Button4: | |
+ window->scroll_offset -= window->theme->scrollbar_step; | |
+ if (window->scroll_offset < 0) | |
+ window->scroll_offset = 0; | |
+ if (window->scroll_callback) | |
+ window->scroll_callback(window->scroll_cb_data… | |
+ return 1; | |
+ case Button5: | |
+ if (window->scroll_offset + window->text_h < window->s… | |
+ window->scroll_offset += window->theme->scroll… | |
+ if (window->scroll_offset + window->text_h > w… | |
+ window->scroll_offset = window->scroll… | |
+ } | |
+ } | |
+ if (window->scroll_callback) | |
+ window->scroll_callback(window->scroll_cb_data… | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+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; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+ledit_window_drag_motion(ledit_window *window, XEvent *event) { | |
+ if (window->scroll_dragging) { | |
+ double scroll_h, scroll_y; | |
+ get_scroll_pos_height(window, &scroll_y, &scroll_h); | |
+ scroll_y += event->xbutton.y - window->scroll_grab_handle; | |
+ window->scroll_grab_handle = event->xbutton.y; | |
+ set_scroll_pos(window, scroll_y); | |
+ return 1; | |
+ } else { | |
+ if (window->button_callback) | |
+ window->button_callback(window->button_cb_data, event); | |
+ return 1; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+int | |
+ledit_window_clipboard_event(ledit_window *window, XEvent *event) { | |
+ /* FIXME: paste in bottom bar */ | |
+ switch (event->type) { | |
+ case SelectionNotify: | |
+ clipboard_selnotify(window, event); | |
+ break; | |
+ case PropertyNotify: | |
+ clipboard_propnotify(window, event); | |
+ break; | |
+ case SelectionRequest: | |
+ clipboard_selrequest(window, event); | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ return 0; | |
+} | |
diff --git a/window.h b/window.h | |
t@@ -0,0 +1,80 @@ | |
+typedef struct bottom_bar bottom_bar; | |
+ | |
+typedef struct { | |
+ PangoFontMap *fontmap; | |
+ PangoContext *context; | |
+ PangoFontDescription *font; | |
+ GC gc; | |
+ Window xwin; | |
+ XdbeBackBuffer back_buf; | |
+ Drawable drawable; | |
+ int w; | |
+ int h; | |
+ int text_w; | |
+ int text_h; | |
+ int scroll_dragging; | |
+ int scroll_grab_handle; | |
+ long scroll_max; | |
+ double scroll_offset; /* double for smoother scrolling */ | |
+ int bottom_text_shown; | |
+ int message_shown; | |
+ XSetWindowAttributes wattrs; | |
+ Atom wm_delete_msg; | |
+ Atom xtarget; | |
+ bottom_bar *bb; | |
+ int bb_msg_shown; | |
+ | |
+ /* IME stuff */ | |
+ XIM xim; | |
+ XIC xic; | |
+ XPoint spot; | |
+ XVaNestedList spotlist; | |
+ | |
+ ledit_common *common; | |
+ ledit_theme *theme; | |
+ void (*paste_callback)(void *, char *, int); | |
+ void (*scroll_callback)(void *, long); | |
+ void (*button_callback)(void *, XEvent *); | |
+ void *paste_cb_data; | |
+ void *scroll_cb_data; | |
+ void *button_cb_data; | |
+} ledit_window; | |
+ | |
+ledit_window *ledit_window_create(ledit_common *common, ledit_theme *theme); | |
+void ledit_window_destroy(ledit_window *window); | |
+ | |
+/* FIXME: this is a bit confusing because there's a difference between editable | |
+ text shown and non-editable message shown */ | |
+void ledit_window_set_bottom_bar_text_shown(ledit_window *window, int shown); | |
+int ledit_window_bottom_bar_text_shown(ledit_window *window); | |
+void ledit_window_set_bottom_bar_cursor(ledit_window *window, int byte_pos); | |
+int ledit_window_get_bottom_bar_cursor(ledit_window *window); | |
+void ledit_window_insert_bottom_bar_text(ledit_window *window, char *text, int… | |
+void ledit_window_set_bottom_bar_text(ledit_window *window, char *text, int le… | |
+char *ledit_window_get_bottom_bar_text(ledit_window *window); | |
+void ledit_window_show_message(ledit_window *window, char *text, int len); | |
+void ledit_window_hide_message(ledit_window *window); | |
+int ledit_window_message_shown(ledit_window *window); | |
+void ledit_window_set_mode(ledit_window *window, enum ledit_mode mode); | |
+ | |
+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 *,… | |
+void ledit_window_set_paste_callback(ledit_window *window, void (*cb)(void *, … | |
+void ledit_window_set_button_callback(ledit_window *window, void (*cb)(void *,… | |
+ | |
+void ledit_window_clear(ledit_window *window); | |
+void ledit_window_redraw(ledit_window *window); | |
+void ledit_window_resize(ledit_window *window, int w, int h); | |
+void ledit_window_get_textview_size(ledit_window *window, int *w_ret, int *h_r… | |
+ | |
+void clipboard_primary_to_clipboard(ledit_window *window); | |
+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 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 xximspot(ledit_window *window, int x, int y); |