Introduction
Introduction Statistics Contact Development Disclaimer Help
view.c - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/…
Log
Files
Refs
README
LICENSE
---
view.c (71581B)
---
1 #include <stdio.h>
2 #include <errno.h>
3 #include <string.h>
4 #include <limits.h>
5 #include <stdlib.h>
6
7 #include <X11/Xlib.h>
8 #include <X11/Xutil.h>
9 #include <X11/Xatom.h>
10 #include <pango/pangoxft.h>
11 #include <pango/pango-utils.h> /* for PANGO_VERSION_CHECK */
12 #include <X11/extensions/Xdbe.h>
13
14 #include "util.h"
15 #include "pango-compat.h"
16 #include "memory.h"
17 #include "common.h"
18 #include "clipboard.h"
19 #include "txtbuf.h"
20 #include "undo.h"
21 #include "cache.h"
22 #include "window.h"
23 #include "buffer.h"
24 #include "assert.h"
25 #include "configparser.h"
26
27 /* FIXME: handle selections better - it can happen that the cursor is mo…
28 the selection, leading to weird "jumping selection" - this should be …
29
30 /* Basic attributes set for all text. */
31 static PangoAttrList *basic_attrs = NULL;
32
33 /* Initialize line with default values. */
34 static void init_line(ledit_view *view, ledit_view_line *line);
35
36 /* Copy given selection to x primary selection - must be sorted already.…
37 static void copy_selection_to_x_primary(ledit_view *view, size_t line1, …
38
39 /* Callbacks for cache handling */
40 static void set_pixmap_line_helper(void *data, size_t line, size_t index…
41 static void invalidate_pixmap_line_helper(void *data, size_t line);
42 static void set_layout_line_helper(void *data, size_t line, size_t index…
43 static void invalidate_layout_line_helper(void *data, size_t line);
44
45 /* line_visible_callback just converts void *data to ledit_view *view */
46 static int view_line_visible(ledit_view *view, size_t index);
47 static int line_visible_callback(void *data, size_t line);
48
49 /* Redraw just the text. */
50 static void view_redraw_text(ledit_view *view);
51
52 /* Callbacks */
53 static void view_button_handler(void *data, XEvent *event);
54 static void view_scroll_handler(void *view, long pos);
55
56 /* Render a line onto a pixmap that is assigned from the cache. */
57 static void render_line(ledit_view *view, size_t line_index);
58
59 /* Assign a cache index to line and set text and highlight of the pango …
60 static void set_pango_text_and_highlight(ledit_view *view, size_t line);
61
62 /*
63 * Get the pango layout for line.
64 * This first assigns a cache index (by calling set_pango_text_and_highl…
65 */
66 static PangoLayout *get_pango_layout(ledit_view *view, size_t line);
67
68 /* Get an attribute list for a text highlight between the given range. */
69 static PangoAttrList *get_pango_attributes(
70 size_t start_byte, size_t end_byte,
71 XRenderColor fg, XRenderColor bg
72 );
73
74 /*
75 * Set the attributes for a PangoLayout belonging to the given line inde…
76 * If the line is part of the view's selection, the selection is set.
77 * If that is not the case but cursor_index is set for the line, the cha…
78 * at that position is highlighted (this is used for the normal mode cur…
79 * Otherwise, the default attributes (basic_attrs) are set.
80 */
81 static void set_line_layout_attrs(ledit_view *view, size_t line, PangoLa…
82
83 /* Move the gap of the line gap buffer to index 'index'. */
84 static void move_line_gap(ledit_view *view, size_t index);
85
86 /*
87 * Resize the line gap buffer so it can hold at least 'min_size' lines a…
88 * move the gap to line at position 'index'.
89 */
90 static void resize_and_move_line_gap(ledit_view *view, size_t min_size, …
91
92 /* FIXME: This is weird because mode is per-view but the undo mode group
93 is changed for the entire buffer. */
94 void
95 view_set_mode(ledit_view *view, ledit_mode mode) {
96 view->mode = mode;
97 undo_change_mode_group(view->buffer->undo);
98 }
99
100 ledit_view *
101 view_create(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t p…
102 if (basic_attrs == NULL) {
103 basic_attrs = pango_attr_list_new();
104 #if PANGO_VERSION_CHECK(1, 44, 0)
105 PangoAttribute *no_hyphens = pango_attr_insert_hyphens_n…
106 pango_attr_list_insert(basic_attrs, no_hyphens);
107 #endif
108 }
109
110 ledit_view *view = ledit_malloc(sizeof(ledit_view));
111 view->mode = mode;
112 view->buffer = buffer;
113 view->window = window_create(buffer->common, buffer->clipboard);
114 view->cache = cache_create(buffer->common->dpy);
115 view->lock_text = NULL;
116 view->cur_action = (struct action){ACTION_NONE, NULL};
117 window_set_scroll_callback(view->window, &view_scroll_handler, v…
118 window_set_button_callback(view->window, &view_button_handler, v…
119 window_set_resize_callback(view->window, &view_resize_textview, …
120
121 view->lines = ledit_reallocarray(NULL, buffer->lines_cap, sizeof…
122 view->lines_cap = buffer->lines_cap;
123 view->lines_gap = buffer->lines_num;
124 view->lines_num = buffer->lines_num;
125 for (size_t i = 0; i < view->lines_num; i++) {
126 init_line(view, &view->lines[i]);
127 }
128 view->cur_line = line;
129 view->cur_index = pos;
130 if (line >= buffer->lines_num)
131 view->cur_line = buffer->lines_num - 1;
132 ledit_line *ll = buffer_get_line(buffer, view->cur_line);
133 if (pos > ll->len)
134 pos = 0; /* should never happen anyways */
135 view->total_height = 0;
136 view->display_offset = 0;
137 view->sel.line1 = view->sel.byte1 = 0;
138 view->sel.line2 = view->sel.byte2 = 0;
139 view->destroy = 0;
140 view->button2_pressed = 0;
141 view->selecting = 0;
142 view->sel_valid = 0;
143 view->redraw = 1;
144 ledit_view_line *vl = view_get_line(view, line);
145 vl->cursor_index = pos;
146 vl->cursor_index_valid = 1;
147
148 view_recalc_all_lines(view);
149
150 return view;
151 }
152
153 void
154 view_lock(ledit_view *view, char *lock_text) {
155 free(view->lock_text);
156 view->lock_text = ledit_strdup(lock_text);
157 }
158
159 void
160 view_unlock(ledit_view *view) {
161 free(view->lock_text);
162 view->lock_text = NULL;
163 }
164
165 ledit_view_line *
166 view_get_line_impl(ledit_view *view, size_t index, const char *file, int…
167 ledit_assert_manual(index < view->lines_num, file, line, func);
168 return index < view->lines_gap ?
169 &view->lines[index] :
170 &view->lines[index + view->lines_cap - view->lines_num];
171 }
172
173 static void
174 move_line_gap(ledit_view *view, size_t index) {
175 move_gap(
176 view->lines, sizeof(ledit_view_line), index,
177 view->lines_gap, view->lines_cap, view->lines_num,
178 &view->lines_gap
179 );
180 }
181
182 static void
183 resize_and_move_line_gap(ledit_view *view, size_t min_size, size_t index…
184 view->lines = resize_and_move_gap(
185 view->lines, sizeof(ledit_view_line),
186 view->lines_gap, view->lines_cap, view->lines_num,
187 min_size, index,
188 &view->lines_gap, &view->lines_cap
189 );
190 }
191
192 /* Checking vl->cursor_index_valid in these notify functions is needed
193 to avoid re-setting the cursor index for lines that were wiped but
194 where the line/index of the view hasn't been updated yet (e.g. when
195 the current line is wiped, then view_delete_range is called to delete
196 a part, which may cause these notification functions to be called) */
197 void
198 view_notify_insert_text(ledit_view *view, size_t line, size_t index, siz…
199 int sel_valid = view->sel_valid;
200 view_wipe_selection(view);
201 ledit_view_line *vl = view_get_line(view, line);
202 vl->text_dirty = 1;
203 if (line == view->cur_line && index < view->cur_index) {
204 view->cur_index += len;
205 ledit_view_line *vl = view_get_line(view, line);
206 if (vl->cursor_index_valid)
207 view_set_line_cursor_attrs(view, line, view->cur…
208 }
209 if (sel_valid)
210 view_set_selection(view, view->cur_line, view->cur_index…
211 }
212
213 void
214 view_notify_delete_text(ledit_view *view, size_t line, size_t index, siz…
215 int sel_valid = view->sel_valid;
216 view_wipe_selection(view);
217 ledit_view_line *vl = view_get_line(view, line);
218 vl->text_dirty = 1;
219 if (line == view->cur_line) {
220 if (index + len <= view->cur_index) {
221 view->cur_index -= len;
222 } else if (index < view->cur_index && index + len > view…
223 view->cur_index = index;
224 }
225 /* just so it isn't stuck at end of line after deletion …
226 if (view->mode == NORMAL) {
227 view->cur_index = view_get_legal_normal_pos(
228 view, view->cur_line, view->cur_index
229 );
230 }
231 ledit_view_line *vl = view_get_line(view, line);
232 if (vl->cursor_index_valid)
233 view_set_line_cursor_attrs(view, line, view->cur…
234 }
235 if (sel_valid)
236 view_set_selection(view, view->cur_line, view->cur_index…
237 }
238
239 void
240 view_notify_append_line(ledit_view *view, size_t line) {
241 int sel_valid = view->sel_valid;
242 view_wipe_selection(view);
243 cache_invalidate_from_line(
244 view->cache, line + 1, view,
245 &invalidate_pixmap_line_helper, &invalidate_layout_line_help…
246 );
247 resize_and_move_line_gap(view, add_sz(view->lines_num, 1), line …
248 if (line < view->cur_line)
249 view->cur_line++;
250 view->lines_num++;
251 view->lines_gap++;
252 ledit_view_line *vl = view_get_line(view, line + 1);
253 init_line(view, vl);
254 if (sel_valid)
255 view_set_selection(view, view->cur_line, view->cur_index…
256 }
257
258 void
259 view_notify_delete_lines(ledit_view *view, size_t index1, size_t index2)…
260 /* FIXME: this is needed to avoid some bugs, but maybe check if …
261 int sel_valid = view->sel_valid;
262 view_wipe_selection(view);
263 if (index2 < view->cur_line) {
264 view->cur_line -= index2 - index1 + 1;
265 } else if (index1 <= view->cur_line) {
266 /* FIXME: set cur_index properly */
267 if (index2 < view->lines_num - 1) {
268 view->cur_line = index1;
269 view->cur_index = 0;
270 } else if (index1 > 0) {
271 view->cur_line = index1 - 1;
272 view->cur_index = 0;
273 } else {
274 /* should never happen */
275 view->cur_line = 0;
276 view->cur_index = 0;
277 }
278 ledit_view_line *vl = view_get_line(view, view->cur_line…
279 if (vl->cursor_index_valid)
280 view_set_line_cursor_attrs(view, view->cur_line,…
281 }
282 cache_invalidate_from_line(
283 view->cache, index1, view,
284 &invalidate_pixmap_line_helper, &invalidate_layout_line_help…
285 );
286 move_line_gap(view, index1);
287 view->lines_num -= index2 - index1 + 1;
288 /* possibly decrease size of array - this needs to be after
289 actually deleting the lines so the length is already less */
290 size_t min_size = ideal_array_size(view->lines_cap, view->lines_…
291 if (min_size != view->lines_cap)
292 resize_and_move_line_gap(view, view->lines_num, view->li…
293 /* force first entry to offset 0 if first line was deleted */
294 if (index1 == 0) {
295 ledit_view_line *vl = view_get_line(view, 0);
296 vl->y_offset = 0;
297 }
298 if (sel_valid)
299 view_set_selection(view, view->cur_line, view->cur_index…
300 }
301
302 void
303 view_destroy(ledit_view *view) {
304 cache_destroy(view->cache);
305 window_destroy(view->window);
306 free(view->lock_text);
307 free(view->lines);
308 free(view);
309 }
310
311 void
312 view_cleanup(void) {
313 if (basic_attrs)
314 pango_attr_list_unref(basic_attrs);
315 basic_attrs = NULL;
316 }
317
318 static PangoAttrList *
319 get_pango_attributes(size_t start_byte, size_t end_byte, XRenderColor fg…
320 PangoAttribute *attr0 = pango_attr_foreground_new(fg.red, fg.gre…
321 PangoAttribute *attr1 = pango_attr_background_new(bg.red, bg.gre…
322 attr0->start_index = start_byte;
323 attr0->end_index = end_byte;
324 attr1->start_index = start_byte;
325 attr1->end_index = end_byte;
326 PangoAttrList *list = pango_attr_list_new();
327 pango_attr_list_insert(list, attr0);
328 pango_attr_list_insert(list, attr1);
329 #if PANGO_VERSION_CHECK(1, 44, 0)
330 PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
331 pango_attr_list_insert(list, attr2);
332 #endif
333 return list;
334 }
335
336 /* this takes layout directly to possibly avoid infinite recursion */
337 static void
338 set_line_layout_attrs(ledit_view *view, size_t line, PangoLayout *layout…
339 ledit_theme *theme = config_get_theme();
340 ledit_line *ll = buffer_get_line(view->buffer, line);
341 ledit_view_line *vl = view_get_line(view, line);
342 PangoAttrList *list = NULL;
343 if (view->sel_valid) {
344 XRenderColor fg = theme->selection_fg.color;
345 XRenderColor bg = theme->selection_bg.color;
346 ledit_range sel = view->sel;
347 sort_range(&sel.line1, &sel.byte1, &sel.line2, &sel.byte…
348 if (sel.line1 < line && sel.line2 > line) {
349 list = get_pango_attributes(0, ll->len, fg, bg);
350 } else if (sel.line1 == line && sel.line2 == line) {
351 size_t start = sel.byte1, end = sel.byte2;
352 if (start > end)
353 swap_sz(&start, &end);
354 list = get_pango_attributes(start, end, fg, bg);
355 } else if (sel.line1 == line && sel.line2 > line) {
356 list = get_pango_attributes(sel.byte1, ll->len, …
357 } else if (sel.line1 < line && sel.line2 == line) {
358 list = get_pango_attributes(0, sel.byte2, fg, bg…
359 }
360 } else if (vl->cursor_index_valid) {
361 XRenderColor fg = theme->cursor_fg.color;
362 XRenderColor bg = theme->cursor_bg.color;
363 /* FIXME: does just adding one really do the right thing…
364 list = get_pango_attributes(vl->cursor_index, vl->cursor…
365 }
366 if (list != NULL) {
367 pango_layout_set_attributes(layout, list);
368 pango_attr_list_unref(list);
369 } else {
370 pango_layout_set_attributes(layout, basic_attrs);
371 }
372 vl->highlight_dirty = 0;
373 vl->dirty = 1;
374 }
375
376 void
377 view_set_line_cursor_attrs(ledit_view *view, size_t line, size_t index) {
378 ledit_view_line *ll = view_get_line(view, line);
379 ll->cursor_index = index;
380 ll->cursor_index_valid = 1;
381 ll->highlight_dirty = 1;
382 ll->dirty = 1;
383 view->redraw = 1;
384 }
385
386 void
387 view_wipe_line_cursor_attrs(ledit_view *view, size_t line) {
388 ledit_view_line *vl = view_get_line(view, line);
389 vl->cursor_index = 0;
390 vl->cursor_index_valid = 0;
391 vl->highlight_dirty = 1;
392 vl->dirty = 1;
393 view->redraw = 1;
394 }
395
396 static int
397 line_visible_callback(void *data, size_t line) {
398 return view_line_visible((ledit_view*)data, line);
399 }
400
401 /* FIXME: standardize variable names (line/line_index, etc.) */
402 void
403 render_line(ledit_view *view, size_t line_index) {
404 ledit_theme *theme = config_get_theme();
405 /* FIXME: check for <= 0 on size */
406 ledit_view_line *ll = view_get_line(view, line_index);
407 ledit_assert(!ll->h_dirty); /* FIXME */
408 PangoLayout *layout = get_pango_layout(view, line_index);
409 if (!ll->cache_pixmap_valid) {
410 cache_assign_pixmap_index(
411 view->cache, line_index, view,
412 &line_visible_callback, &set_pixmap_line_helper,
413 &invalidate_pixmap_line_helper
414 );
415 }
416 cache_pixmap *pix = cache_get_pixmap(view->cache, ll->cache_pixm…
417 /* FIXME: fail on too large pixmap size (e.g. way too long line)…
418 /* FIXME: sensible default pixmap sizes here */
419 /* FIXME: handle this in cache */
420 if (pix->pixmap == None || pix->draw == NULL) {
421 pix->pixmap = XCreatePixmap(
422 view->buffer->common->dpy, view->window->drawable,
423 ll->w + 10, ll->h + 10, view->buffer->common->depth
424 );
425 pix->w = ll->w + 10;
426 pix->h = ll->h + 10;
427 pix->draw = XftDrawCreate(
428 view->buffer->common->dpy, pix->pixmap,
429 view->buffer->common->vis, view->buffer->common->cm
430 );
431 } else if (pix->w < ll->w || pix->h < ll->h) {
432 int new_w = ll->w > pix->w ? ll->w + 10 : pix->w + 10;
433 int new_h = ll->h > pix->h ? ll->h + 10 : pix->h + 10;
434 XFreePixmap(view->buffer->common->dpy, pix->pixmap);
435 pix->pixmap = XCreatePixmap(
436 view->buffer->common->dpy, view->window->drawable,
437 new_w, new_h, view->buffer->common->depth
438 );
439 pix->w = new_w;
440 pix->h = new_h;
441 XftDrawChange(pix->draw, pix->pixmap);
442 }
443 XftDrawRect(pix->draw, &theme->text_bg, 0, 0, ll->w, ll->h);
444 pango_xft_render_layout(pix->draw, &theme->text_fg, layout, 0, 0…
445 ll->dirty = 0;
446 }
447
448 static void
449 init_line(ledit_view *view, ledit_view_line *line) {
450 int text_w, text_h;
451 window_get_textview_size(view->window, &text_w, &text_h);
452 line->view = view;
453 line->w = text_w;
454 line->h = 0;
455 line->y_offset = 0;
456 line->cache_pixmap_index = 0;
457 line->cache_layout_index = 0;
458 line->softlines = 0;
459 line->cursor_index = 0;
460 line->cursor_index_valid = 0;
461 line->cache_pixmap_valid = 0;
462 line->cache_layout_valid = 0;
463 line->dirty = 1;
464 line->text_dirty = 1;
465 line->highlight_dirty = 1;
466 line->h_dirty = 1;
467 }
468
469 static void
470 set_pixmap_line_helper(void *data, size_t line, size_t index) {
471 ledit_view_line *vl = view_get_line((ledit_view *)data, line);
472 vl->cache_pixmap_index = index;
473 vl->cache_pixmap_valid = 1;
474 }
475
476 static void
477 invalidate_pixmap_line_helper(void *data, size_t line) {
478 ledit_view_line *vl = view_get_line((ledit_view *)data, line);
479 vl->cache_pixmap_valid = 0;
480 }
481
482 static void
483 set_layout_line_helper(void *data, size_t line, size_t index) {
484 ledit_view_line *vl = view_get_line((ledit_view *)data, line);
485 vl->cache_layout_index = index;
486 vl->cache_layout_valid = 1;
487 }
488
489 static void
490 invalidate_layout_line_helper(void *data, size_t line) {
491 ledit_view_line *vl = view_get_line((ledit_view *)data, line);
492 vl->cache_layout_valid = 0;
493 }
494
495 void
496 view_recalc_line(ledit_view *view, size_t line) {
497 ledit_theme *theme = config_get_theme();
498 ledit_view_line *l = view_get_line(view, line);
499 if (l->text_dirty)
500 set_pango_text_and_highlight(view, line);
501
502 int text_w, text_h;
503 window_get_textview_size(view->window, &text_w, &text_h);
504 /* if height changed, set height of current line
505 * and adjust offsets of all lines following it */
506 if (l->h_dirty) {
507 l->h_dirty = 0;
508 /* FIXME: maybe also check overflow for offset? */
509 long off = l->y_offset + l->h + theme->extra_line_spacin…
510 for (size_t i = line + 1; i < view->lines_num; i++) {
511 l = view_get_line(view, i);
512 l->y_offset = off;
513 off += l->h + theme->extra_line_spacing;
514 }
515 view->total_height = off - theme->extra_line_spacing;
516 if (l->y_offset < view->display_offset + text_h)
517 view->redraw = 1;
518 }
519 l = view_get_line(view, line);
520 if (l->y_offset < view->display_offset + text_h &&
521 l->y_offset + l->h >= view->display_offset) {
522 view->redraw = 1;
523 }
524 window_set_scroll_max(view->window, view->total_height);
525 view_scroll(view, view->display_offset);
526 }
527
528 void
529 view_recalc_from_line(ledit_view *view, size_t line) {
530 ledit_theme *theme = config_get_theme();
531 ledit_view_line *l = view_get_line(view, line);
532 /* force first line to offset 0 */
533 if (line == 0)
534 l->y_offset = 0;
535 int text_w, text_h;
536 window_get_textview_size(view->window, &text_w, &text_h);
537 long off = l->y_offset;
538 if (off < view->display_offset + text_h)
539 view->redraw = 1;
540 for (size_t i = line; i < view->lines_num; i++) {
541 l = view_get_line(view, i);
542 if (l->text_dirty)
543 set_pango_text_and_highlight(view, i);
544 l->h_dirty = 0;
545 l->y_offset = off;
546 off += l->h + theme->extra_line_spacing;
547 }
548 view->total_height = off - theme->extra_line_spacing;
549 window_set_scroll_max(view->window, view->total_height);
550 view_scroll(view, view->display_offset);
551 }
552
553 void
554 view_recalc_all_lines(ledit_view *view) {
555 view_recalc_from_line(view, 0);
556 }
557
558 static int
559 view_line_visible(ledit_view *view, size_t index) {
560 int text_w, text_h;
561 window_get_textview_size(view->window, &text_w, &text_h);
562 ledit_view_line *l = view_get_line(view, index);
563 return l->y_offset < view->display_offset + text_h &&
564 l->y_offset + l->h > view->display_offset;
565 }
566
567 /* FIXME: these functions are only here because they need the PangoLayou…
568 determine grapheme boundaries. Maybe use a separate library for that?…
569 void
570 view_next_cursor_pos(
571 ledit_view *view,
572 size_t line, size_t byte,
573 int num, int multiline,
574 size_t *line_ret, size_t *byte_ret) {
575 int nattrs;
576 ledit_line *ll = buffer_get_line(view->buffer, line);
577 size_t c = line_byte_to_char(ll, byte);
578 size_t cur_byte = byte;
579 PangoLayout *layout = get_pango_layout(view, line);
580 const PangoLogAttr *attrs =
581 pango_layout_get_log_attrs_readonly(layout, &nattrs);
582 for (int i = 0; i < num; i++) {
583 if (cur_byte >= ll->len) {
584 if (multiline && line < view->lines_num - 1) {
585 line++;
586 ll = buffer_get_line(view->buffer, line);
587 layout = get_pango_layout(view, line);
588 attrs = pango_layout_get_log_attrs_reado…
589 c = 0;
590 cur_byte = 0;
591 i++;
592 continue;
593 } else {
594 break;
595 }
596 }
597 cur_byte = line_next_utf8(ll, cur_byte);
598 for (c++; c < (size_t)nattrs; c++) {
599 if (attrs[c].is_cursor_position)
600 break;
601 cur_byte = line_next_utf8(ll, cur_byte);
602 }
603 }
604 if (line_ret)
605 *line_ret = line;
606 if (byte_ret)
607 *byte_ret = cur_byte <= ll->len ? cur_byte : ll->len;
608 }
609
610 void
611 view_prev_cursor_pos(
612 ledit_view *view,
613 size_t line, size_t byte,
614 int num, int multiline,
615 size_t *line_ret, size_t *byte_ret) {
616 int nattrs;
617 ledit_line *ll = buffer_get_line(view->buffer, line);
618 size_t c = line_byte_to_char(ll, byte);
619 size_t cur_byte = byte;
620 PangoLayout *layout = get_pango_layout(view, line);
621 const PangoLogAttr *attrs =
622 pango_layout_get_log_attrs_readonly(layout, &nattrs);
623 for (int i = 0; i < num; i++) {
624 if (cur_byte == 0) {
625 if (multiline && line > 0) {
626 line--;
627 ll = buffer_get_line(view->buffer, line);
628 layout = get_pango_layout(view, line);
629 attrs = pango_layout_get_log_attrs_reado…
630 c = (size_t)nattrs;
631 cur_byte = ll->len;
632 i++;
633 continue;
634 } else {
635 break;
636 }
637 }
638 cur_byte = line_prev_utf8(ll, cur_byte);
639 for (; c-- > 0;) {
640 if (attrs[c].is_cursor_position)
641 break;
642 cur_byte = line_prev_utf8(ll, cur_byte);
643 }
644 }
645 if (line_ret)
646 *line_ret = line;
647 if (byte_ret)
648 *byte_ret = cur_byte;
649 }
650
651 static int
652 line_next_word(
653 ledit_view *view,
654 size_t line, size_t byte, size_t char_index, int wrapped_line,
655 size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
656 int nattrs;
657 ledit_line *ll = buffer_get_line(view->buffer, line);
658 int cur_byte = wrapped_line ? byte : line_next_utf8(ll, byte);
659 PangoLayout *layout = get_pango_layout(view, line);
660 const PangoLogAttr *attrs =
661 pango_layout_get_log_attrs_readonly(layout, &nattrs);
662 for (size_t i = wrapped_line ? char_index : char_index + 1; i < …
663 if (attrs[i].is_word_start) {
664 *char_ret = i;
665 *real_byte_ret = cur_byte;
666 *byte_ret = cur_byte;
667 return 0;
668 }
669 cur_byte = line_next_utf8(ll, cur_byte);
670 }
671 return -1;
672 }
673
674 static int
675 line_prev_word(
676 ledit_view *view,
677 size_t line, size_t byte, size_t char_index,
678 size_t *char_ret, size_t *byte_ret) {
679 int nattrs;
680 ledit_line *ll = buffer_get_line(view->buffer, line);
681 size_t cur_byte = line_prev_utf8(ll, byte);
682 PangoLayout *layout = get_pango_layout(view, line);
683 const PangoLogAttr *attrs =
684 pango_layout_get_log_attrs_readonly(layout, &nattrs);
685 if (char_index > (size_t)nattrs - 1)
686 char_index = (size_t)nattrs - 1;
687 /* this is a bit weird because size_t can't be negative */
688 for (size_t i = char_index; i > 0; i--) {
689 if (attrs[i-1].is_word_start) {
690 *char_ret = i-1;
691 *byte_ret = cur_byte;
692 return 0;
693 }
694 cur_byte = line_prev_utf8(ll, cur_byte);
695 }
696 return -1;
697 }
698
699 static int
700 line_prev_bigword(
701 ledit_view *view,
702 size_t line, size_t byte, size_t char_index,
703 size_t *char_ret, size_t *byte_ret) {
704 int nattrs;
705 ledit_line *ll = buffer_get_line(view->buffer, line);
706 size_t cur_byte = line_prev_utf8(ll, byte);
707 PangoLayout *layout = get_pango_layout(view, line);
708 const PangoLogAttr *attrs =
709 pango_layout_get_log_attrs_readonly(layout, &nattrs);
710 size_t next_cursorb = byte;
711 size_t next_cursorc = char_index;
712 int found_word = 0;
713 if (char_index > (size_t)nattrs - 1)
714 char_index = (size_t)nattrs - 1;
715 /* FIXME: use for (size_t i = ...; i-- > 0;) everywhere */
716 /* this is a bit weird because size_t can't be negative */
717 for (size_t i = char_index; i > 0; i--) {
718 if (!found_word && !attrs[i-1].is_white) {
719 found_word = 1;
720 } else if (found_word && attrs[i-1].is_white) {
721 *char_ret = next_cursorc;
722 *byte_ret = next_cursorb;
723 return 0;
724 }
725 if (found_word && i-1 == 0) {
726 *char_ret = 0;
727 *byte_ret = 0;
728 return 0;
729 }
730 if (attrs[i-1].is_cursor_position) {
731 next_cursorc = i-1;
732 next_cursorb = cur_byte;
733 }
734 cur_byte = line_prev_utf8(ll, cur_byte);
735 }
736 return -1;
737 }
738
739 static int
740 line_next_bigword_end(
741 ledit_view *view,
742 size_t line, size_t byte, size_t char_index, int wrapped_line,
743 size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
744 int nattrs;
745 ledit_line *ll = buffer_get_line(view->buffer, line);
746 PangoLayout *layout = get_pango_layout(view, line);
747 const PangoLogAttr *attrs =
748 pango_layout_get_log_attrs_readonly(layout, &nattrs);
749 size_t last_cursorb = 0, last_cursorc = 0;
750 int last_cursor_valid;
751 if (wrapped_line) {
752 last_cursorb = byte;
753 last_cursorc = char_index;
754 last_cursor_valid = 1;
755 } else {
756 last_cursor_valid = 0;
757 }
758 int found_word = 0;
759 size_t cur_byte = byte;
760 for (size_t i = char_index; i < (size_t)nattrs; i++) {
761 if (last_cursor_valid && !found_word && !attrs[i].is_whi…
762 found_word = 1;
763 } else if (found_word && attrs[i].is_white) {
764 *char_ret = last_cursorc;
765 *real_byte_ret = cur_byte;
766 *byte_ret = last_cursorb;
767 return 0;
768 }
769 if (attrs[i].is_cursor_position) {
770 last_cursorc = i;
771 last_cursorb = cur_byte;
772 last_cursor_valid = 1;
773 }
774 cur_byte = line_next_utf8(ll, cur_byte);
775 }
776 return -1;
777 }
778
779 static int
780 line_next_word_end(
781 ledit_view *view,
782 size_t line, size_t byte, size_t char_index, int wrapped_line,
783 size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
784 int nattrs;
785 ledit_line *ll = buffer_get_line(view->buffer, line);
786 size_t cur_byte = line_next_utf8(ll, byte);
787 PangoLayout *layout = get_pango_layout(view, line);
788 const PangoLogAttr *attrs =
789 pango_layout_get_log_attrs_readonly(layout, &nattrs);
790 size_t last_cursorb = 0, last_cursorc = 0;
791 int last_cursor_valid;
792 if (wrapped_line) {
793 last_cursorb = byte;
794 last_cursorc = char_index;
795 last_cursor_valid = 1;
796 } else {
797 last_cursor_valid = 0;
798 }
799 for (size_t i = char_index + 1; i < (size_t)nattrs; i++) {
800 if (last_cursor_valid && attrs[i].is_word_end) {
801 *char_ret = last_cursorc;
802 *real_byte_ret = cur_byte;
803 *byte_ret = last_cursorb;
804 return 0;
805 }
806 if (attrs[i].is_cursor_position) {
807 last_cursorc = i;
808 last_cursorb = cur_byte;
809 last_cursor_valid = 1;
810 }
811 cur_byte = line_next_utf8(ll, cur_byte);
812 }
813 return -1;
814 }
815
816 static int
817 line_next_bigword(
818 ledit_view *view,
819 size_t line, size_t byte, size_t char_index, int wrapped_line,
820 size_t *char_ret, size_t *byte_ret, size_t *real_byte_ret) {
821 int nattrs;
822 ledit_line *ll = buffer_get_line(view->buffer, line);
823 size_t cur_byte = byte;
824 PangoLayout *layout = get_pango_layout(view, line);
825 const PangoLogAttr *attrs =
826 pango_layout_get_log_attrs_readonly(layout, &nattrs);
827 int found_ws = wrapped_line;
828 for (size_t i = char_index; i < (size_t)nattrs; i++) {
829 if (!found_ws && attrs[i].is_white) {
830 found_ws = 1;
831 } else if (found_ws && !attrs[i].is_white) {
832 *char_ret = i;
833 *real_byte_ret = cur_byte;
834 *byte_ret = cur_byte;
835 return 0;
836 }
837 cur_byte = line_next_utf8(ll, cur_byte);
838 }
839 return -1;
840 }
841
842 size_t
843 view_line_next_non_whitespace(ledit_view *view, size_t line, size_t byte…
844 int nattrs;
845 ledit_line *ll = buffer_get_line(view->buffer, line);
846 size_t c = line_byte_to_char(ll, byte);
847 size_t cur_byte = byte;
848 PangoLayout *layout = get_pango_layout(view, line);
849 const PangoLogAttr *attrs =
850 pango_layout_get_log_attrs_readonly(layout, &nattrs);
851 for (; c < (size_t)nattrs; c++) {
852 if (!attrs[c].is_white)
853 return cur_byte;
854 cur_byte = line_next_utf8(ll, cur_byte);
855 }
856 return ll->len;
857 }
858
859 /* FIXME: document that word and bigword are a bit weird because word us…
860
861 #define GEN_NEXT_WORD(name, func) …
862 void …
863 view_next_##name( …
864 ledit_view *view, …
865 size_t line, size_t byte, int num_repeat, …
866 size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret) { …
867 size_t cur_line = line; …
868 size_t cur_byte = byte; …
869 ledit_line *ll = buffer_get_line(view->buffer, line); …
870 size_t cur_char = line_byte_to_char(ll, byte); …
871 size_t real_byte = 0; …
872 int last_ret = -1; …
873 int wrapped_line; …
874 for (int i = 0; i < num_repeat; i++) { …
875 wrapped_line = 0; …
876 while ((last_ret = func(view, cur_line, cur_byte, cur_ch…
877 wrapped_line, &cur_char, &cur_byte, &real_byte))…
878 cur_line < view->lines_num - 1) { …
879 cur_line++; …
880 cur_byte = 0; …
881 cur_char = 0; …
882 wrapped_line = 1; …
883 } …
884 if (last_ret == -1 && cur_line == view->lines_num - 1) …
885 break; …
886 } …
887 if (last_ret == -1) { …
888 *line_ret = view->lines_num - 1; …
889 ledit_line *ll = buffer_get_line(view->buffer, view->lin…
890 *byte_ret = view_get_legal_normal_pos(view, view->lines_…
891 *real_byte_ret = ll->len; …
892 } else { …
893 *line_ret = cur_line; …
894 *byte_ret = cur_byte; …
895 *real_byte_ret = real_byte; …
896 } …
897 }
898
899 #define GEN_PREV_WORD(name, func) …
900 void …
901 view_prev_##name( …
902 ledit_view *view, …
903 size_t line, size_t byte, int num_repeat, …
904 size_t *line_ret, size_t *byte_ret, size_t *real_byte_ret) { …
905 size_t cur_line = line; …
906 size_t cur_byte = byte; …
907 ledit_line *ll = buffer_get_line(view->buffer, line); …
908 size_t cur_char = line_byte_to_char(ll, byte); …
909 int last_ret = -1; …
910 for (int i = 0; i < num_repeat; i++) { …
911 while ((last_ret = func(view, cur_line, cur_byte, cur_ch…
912 &cur_char, &cur_byte)) == -1 && cur_line > 0) { …
913 cur_line--; …
914 ll = buffer_get_line(view->buffer, cur_line); …
915 cur_byte = ll->len; …
916 cur_char = ll->len; …
917 } …
918 if (last_ret == -1 && cur_line == 0) …
919 break; …
920 } …
921 if (last_ret == -1) { …
922 *line_ret = 0; …
923 *byte_ret = 0; …
924 *real_byte_ret = 0; …
925 } else { …
926 *line_ret = cur_line; …
927 *byte_ret = cur_byte; …
928 *real_byte_ret = cur_byte; …
929 } …
930 }
931
932 GEN_NEXT_WORD(word, line_next_word)
933 GEN_NEXT_WORD(word_end, line_next_word_end)
934 GEN_NEXT_WORD(bigword, line_next_bigword)
935 GEN_NEXT_WORD(bigword_end, line_next_bigword_end)
936 GEN_PREV_WORD(word, line_prev_word)
937 GEN_PREV_WORD(bigword, line_prev_bigword)
938
939 void
940 view_get_pos_softline_bounds(
941 ledit_view *view, size_t line, size_t pos,
942 size_t *start_byte_ret, size_t *end_byte_ret) {
943 ledit_assert(line < view->lines_num);
944 ledit_line *ll = buffer_get_line(view->buffer, line);
945 ledit_assert(pos <= ll->len);
946 PangoLayout *layout = get_pango_layout(view, line);
947 int x, sli;
948 if (pos > INT_MAX)
949 err_overflow();
950 pango_layout_index_to_line_x(layout, (int)pos, 0, &sli, &x);
951 PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, sli…
952 *start_byte_ret = (size_t)pl->start_index;
953 *end_byte_ret = (size_t)(pl->start_index + pl->length);
954 }
955
956 void
957 view_get_softline_bounds(
958 ledit_view *view, size_t line, int softline,
959 size_t *start_byte_ret, size_t *end_byte_ret) {
960 ledit_assert(line < view->lines_num);
961 ledit_view_line *vl = view_get_line(view, line);
962 PangoLayout *layout = get_pango_layout(view, line);
963 ledit_assert(softline < vl->softlines);
964 PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, sof…
965 *start_byte_ret = (size_t)pl->start_index;
966 *end_byte_ret = (size_t)(pl->start_index + pl->length);
967 }
968
969 int
970 view_get_softline_count(ledit_view *view, size_t line) {
971 ledit_assert(line < view->lines_num);
972 ledit_view_line *vl = view_get_line(view, line);
973 if (vl->text_dirty)
974 set_pango_text_and_highlight(view, line);
975 return vl->softlines;
976 }
977
978 int
979 view_pos_to_softline(ledit_view *view, size_t line, size_t pos) {
980 ledit_assert(line < view->lines_num);
981 ledit_line *ll = buffer_get_line(view->buffer, line);
982 ledit_assert(pos <= ll->len);
983 PangoLayout *layout = get_pango_layout(view, line);
984 int x, sli;
985 if (pos > INT_MAX)
986 err_overflow();
987 pango_layout_index_to_line_x(layout, (int)pos, 0, &sli, &x);
988 return sli;
989 }
990
991 void
992 view_get_cursor_pixel_pos(ledit_view *view, size_t line, size_t pos, int…
993 ledit_assert(line < view->lines_num);
994 ledit_line *ll = buffer_get_line(view->buffer, line);
995 ledit_assert(pos <= ll->len);
996 PangoLayout *layout = get_pango_layout(view, line);
997 PangoRectangle strong, weak;
998 if (pos > INT_MAX)
999 err_overflow();
1000 pango_layout_get_cursor_pos(layout, (int)pos, &strong, &weak);
1001 *x_ret = strong.x / PANGO_SCALE;
1002 *y_ret = strong.y / PANGO_SCALE;
1003 *h_ret = strong.height / PANGO_SCALE;
1004 }
1005
1006 /* prev_index_ret is used instead of just calling get_legal_normal_pos
1007 because weird things happen otherwise
1008 -> in certain cases, this is still weird because prev_index_ret somet…
1009 is not at the end of the line, but this is the best I could come up
1010 with for now */
1011 size_t
1012 view_move_cursor_visually(ledit_view *view, size_t line, size_t pos, int…
1013 if (pos > INT_MAX)
1014 err_overflow();
1015 /* FIXME: trailing */
1016 int trailing = 0;
1017 ledit_line *cur_line = buffer_get_line(view->buffer, line);
1018 PangoLayout *layout = get_pango_layout(view, line);
1019 int tmp_index;
1020 int new_index = (int)pos, last_index = (int)pos;
1021 int dir = 1;
1022 int num = movement;
1023 if (movement < 0) {
1024 dir = -1;
1025 num = -movement;
1026 }
1027 /* FIXME: This is stupid. Anything outside the range of int won'…
1028 anyways because of pango (and because everything else would b…
1029 anyways with such long lines), so it's stupid to do all this …
1030 casting. */
1031 if (cur_line->len > INT_MAX)
1032 err_overflow();
1033 while (num > 0) {
1034 tmp_index = new_index;
1035 pango_layout_move_cursor_visually(
1036 layout, TRUE,
1037 new_index, trailing, dir,
1038 &new_index, &trailing
1039 );
1040 if (new_index < 0)
1041 new_index = 0;
1042 else if (new_index > (int)cur_line->len)
1043 new_index = (int)cur_line->len;
1044 num--;
1045 if (tmp_index != new_index)
1046 last_index = tmp_index;
1047 }
1048 /* FIXME: Allow cursor to be at end of soft line */
1049 /* we don't currently support a difference between the cursor be…
1050 the end of a soft line and the beginning of the next line */
1051 /* FIXME: spaces at end of softlines are weird in normal mode */
1052 while (trailing > 0) {
1053 trailing--;
1054 new_index = line_next_utf8(cur_line, new_index);
1055 }
1056 if (new_index < 0)
1057 new_index = 0;
1058 if (prev_index_ret)
1059 *prev_index_ret = (size_t)last_index;
1060 return (size_t)new_index;
1061 }
1062
1063 /* FIXME: implement */
1064 /*
1065 int
1066 ledit_line_nearest_cursor_pos(ledit_line *line, int byte) {
1067 }
1068
1069 void
1070 ledit_line_word_boundaries(ledit_line *line, int byte, int *start_ret, i…
1071 }
1072 */
1073
1074 static void
1075 set_pango_text_and_highlight(ledit_view *view, size_t line) {
1076 cache_layout *cl;
1077 ledit_theme *theme = config_get_theme();
1078 ledit_line *ll = buffer_get_line(view->buffer, line);
1079 ledit_view_line *vl = view_get_line(view, line);
1080 char old_valid = vl->cache_layout_valid;
1081 if (!vl->cache_layout_valid) {
1082 cache_assign_layout_index(
1083 view->cache, line,
1084 view, &set_layout_line_helper, &invalidate_layout_li…
1085 );
1086 cl = cache_get_layout(view->cache, vl->cache_layout_inde…
1087 } else {
1088 cl = cache_get_layout(view->cache, vl->cache_layout_inde…
1089 }
1090 if (cl->layout == NULL) {
1091 cl->layout = pango_layout_new(view->window->context);
1092 pango_layout_set_font_description(cl->layout, view->wind…
1093 pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR);
1094 pango_layout_set_spacing(cl->layout, theme->extra_line_s…
1095 }
1096 if (vl->text_dirty || !old_valid) {
1097 buffer_normalize_line(ll);
1098 if (ll->len > INT_MAX)
1099 err_overflow();
1100 pango_layout_set_text(cl->layout, ll->text, (int)ll->len…
1101 set_line_layout_attrs(view, line, cl->layout);
1102 pango_layout_set_width(cl->layout, vl->w * PANGO_SCALE);
1103 vl->softlines = pango_layout_get_line_count(cl->layout);
1104 int w, h;
1105 pango_layout_get_pixel_size(cl->layout, &w, &h);
1106 if (h != vl->h) {
1107 vl->h = h;
1108 vl->h_dirty = 1;
1109 }
1110 vl->text_dirty = 0;
1111 vl->dirty = 1;
1112 } else if (vl->highlight_dirty) {
1113 set_line_layout_attrs(view, line, cl->layout);
1114 }
1115 vl->highlight_dirty = 0;
1116 }
1117
1118 static PangoLayout *
1119 get_pango_layout(ledit_view *view, size_t line) {
1120 set_pango_text_and_highlight(view, line);
1121 ledit_view_line *vl = view_get_line(view, line);
1122 cache_layout *cl = cache_get_layout(
1123 view->cache, vl->cache_layout_index
1124 );
1125 return cl->layout;
1126 }
1127
1128 void
1129 view_pos_to_x_softline(ledit_view *view, size_t line, size_t pos, int *x…
1130 ledit_view_line *vl = view_get_line(view, line);
1131 PangoLayout *layout = get_pango_layout(view, line);
1132 if (pos > INT_MAX)
1133 err_overflow();
1134 pango_layout_index_to_line_x(layout, (int)pos, 0, softline_ret, …
1135 PangoLayoutLine *pango_line = pango_layout_get_line_readonly(lay…
1136 /* add left margin to x position if line is aligned right */
1137 if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
1138 PangoRectangle rect;
1139 pango_layout_line_get_extents(pango_line, NULL, &rect);
1140 *x_ret += (vl->w * PANGO_SCALE - rect.width);
1141 }
1142 /* if in normal mode, change position to the middle of the
1143 current rectangle so that moving around won't jump weirdly */
1144 /* FIXME: also in visual? */
1145 /* FIXME: this is too much magic for my taste */
1146 if (view->mode == NORMAL) {
1147 PangoRectangle rect;
1148 pango_layout_index_to_pos(layout, (int)pos, &rect);
1149 *x_ret += rect.width / 2;
1150 }
1151 }
1152
1153 size_t
1154 view_x_softline_to_pos(ledit_view *view, size_t line, int x, int softlin…
1155 int trailing = 0;
1156 int x_relative = x;
1157 ledit_view_line *vl = view_get_line(view, line);
1158 PangoLayout *layout = get_pango_layout(view, line);
1159 PangoLayoutLine *pango_line =
1160 pango_layout_get_line_readonly(layout, softline);
1161 /* x is absolute, so the margin at the left needs to be subtract…
1162 if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
1163 PangoRectangle rect;
1164 pango_layout_line_get_extents(pango_line, NULL, &rect);
1165 x_relative -= (vl->w * PANGO_SCALE - rect.width);
1166 }
1167 int tmp_pos;
1168 pango_layout_line_x_to_index(
1169 pango_line, x_relative, &tmp_pos, &trailing
1170 );
1171 size_t pos = (size_t)tmp_pos;
1172 /* if in insert or visual mode, snap to the nearest border betwe…
1173 /* FIXME: add parameter for this instead of checking mode */
1174 if (view->mode == INSERT || view->mode == VISUAL) {
1175 ledit_line *ll = buffer_get_line(view->buffer, line);
1176 while (trailing > 0) {
1177 trailing--;
1178 pos = line_next_utf8(ll, pos);
1179 }
1180 }
1181 return pos;
1182 }
1183
1184 size_t
1185 view_get_legal_normal_pos(ledit_view *view, size_t line, size_t pos) {
1186 /* move back one grapheme if at end of line */
1187 size_t ret = pos;
1188 ledit_line *ll = buffer_get_line(view->buffer, line);
1189 if (pos == ll->len && pos > 0) {
1190 int nattrs;
1191 PangoLayout *layout = get_pango_layout(view, line);
1192 const PangoLogAttr *attrs =
1193 pango_layout_get_log_attrs_readonly(layout, &nattrs);
1194 if (nattrs < 2)
1195 return 0;
1196 size_t cur = nattrs - 2;
1197 ret = line_prev_utf8(ll, ret);
1198 while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_posit…
1199 cur--;
1200 ret = line_prev_utf8(ll, ret);
1201 }
1202 }
1203 return ret;
1204 }
1205
1206 void
1207 view_delete_range(
1208 ledit_view *view,
1209 enum delete_mode delmode, int start_undo_group,
1210 size_t line_index1, size_t byte_index1,
1211 size_t line_index2, size_t byte_index2,
1212 size_t *new_line_ret, size_t *new_byte_ret,
1213 txtbuf *text_ret) {
1214 view_delete_range_base(
1215 view,
1216 delmode, start_undo_group,
1217 line_index1, byte_index1,
1218 line_index2, byte_index2,
1219 new_line_ret, new_byte_ret,
1220 text_ret
1221 );
1222 /* need to start recalculating one line before in case first
1223 line was deleted and offset is now wrong */
1224 size_t min = line_index1 < line_index2 ? line_index1 : line_inde…
1225 buffer_recalc_all_views_from_line(
1226 view->buffer, min > 0 ? min - 1 : min
1227 );
1228 }
1229
1230 /* Note: line_index* and byte_index* don't need to be sorted */
1231 /* line_index1, byte_index1 are used as the cursor position in order
1232 to determine the new cursor position */
1233 /* FIXME: use at least somewhat sensible variable names */
1234 void
1235 view_delete_range_base(
1236 ledit_view *view,
1237 enum delete_mode delmode, int start_undo_group,
1238 size_t line_index1, size_t byte_index1,
1239 size_t line_index2, size_t byte_index2,
1240 size_t *new_line_ret, size_t *new_byte_ret,
1241 txtbuf *text_ret) {
1242 /* FIXME: Oh boy, this is nasty */
1243 /* range line x, range byte x */
1244 size_t rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0;
1245 /* line_index1 and byte_index1 are used as cursor start position
1246 -> FIXME: why not just use view->cur_line, view->cur_index he…
1247 size_t cur_line = line_index1;
1248 size_t cur_byte = byte_index1;
1249 sort_range(&line_index1, &byte_index1, &line_index2, &byte_index…
1250 size_t new_line = 0, new_byte = 0;
1251 ledit_assert(line_index1 < view->lines_num);
1252 ledit_assert(line_index2 < view->lines_num);
1253 ledit_range cur_range = {view->cur_line, view->cur_index, 0, 0};
1254 /* FIXME: could this be simplified by just calculating the range…
1255 the non-line-based version? */
1256 if (delmode == DELETE_HARDLINE) {
1257 int x, sl_useless;
1258 size_t l1 = line_index1, l2 = line_index2;
1259 ledit_line *ll;
1260 view_pos_to_x_softline(view, cur_line, cur_byte, &x, &sl…
1261 if (l1 > 0 && l2 < view->lines_num - 1) {
1262 rgl1 = l1;
1263 rgb1 = 0;
1264 rgl2 = l2 + 1;
1265 rgb2 = 0;
1266 } else if (l1 > 0) {
1267 rgl1 = l1 - 1;
1268 ll = buffer_get_line(view->buffer, rgl1);
1269 rgb1 = ll->len;
1270 rgl2 = l2;
1271 ll = buffer_get_line(view->buffer, rgl2);
1272 rgb2 = ll->len;
1273 } else if (l2 < view->lines_num - 1) {
1274 rgl1 = l1;
1275 rgb1 = 0;
1276 rgl2 = l2 + 1;
1277 rgb2 = 0;
1278 } else {
1279 rgl1 = l1;
1280 rgb1 = 0;
1281 rgl2 = l2;
1282 ll = buffer_get_line(view->buffer, rgl2);
1283 rgb2 = ll->len;
1284 }
1285 if (l2 < view->lines_num - 1) {
1286 new_line = l1;
1287 new_byte = view_x_softline_to_pos(
1288 view, l2 + 1, x, 0
1289 );
1290 } else if (l1 > 0) {
1291 new_line = l1 - 1;
1292 new_byte = view_x_softline_to_pos(
1293 view, l1 - 1, x, 0
1294 );
1295 } else {
1296 new_line = 0;
1297 new_byte = 0;
1298 }
1299 buffer_delete_with_undo_base(
1300 view->buffer, cur_range,
1301 start_undo_group, view->mode,
1302 rgl1, rgb1, rgl2, rgb2, text_ret
1303 );
1304 } else if (delmode == DELETE_SOFTLINE) {
1305 int x, sl_useless;
1306 view_pos_to_x_softline(view, cur_line, cur_byte, &x, &sl…
1307 if (line_index1 == line_index2) {
1308 int x_useless, l1, l2;
1309 ledit_line *line1 = buffer_get_line(view->buffer…
1310 ledit_view_line *vline1 = view_get_line(view, li…
1311 view_pos_to_x_softline(view, line_index1, byte_i…
1312 view_pos_to_x_softline(view, line_index2, byte_i…
1313 PangoLayout *layout = get_pango_layout(view, lin…
1314 PangoLayoutLine *pl1 = pango_layout_get_line_rea…
1315 PangoLayoutLine *pl2 = pango_layout_get_line_rea…
1316 /* don't delete entire line if it is the last on…
1317 if (l1 == 0 && l2 == vline1->softlines - 1 && vi…
1318 if (line_index1 < view->lines_num - 1) {
1319 /* cursor can be moved to next h…
1320 new_line = line_index1;
1321 new_byte = view_x_softline_to_po…
1322 view, line_index1 + 1, x, 0
1323 );
1324 rgl1 = line_index1;
1325 rgb1 = 0;
1326 rgl2 = line_index1 + 1;
1327 rgb2 = 0;
1328 } else {
1329 /* cursor has to be be moved to …
1330 because last line in buffer i…
1331 /* note: logically, line_index1 …
1332 view->lines_num > 1 && line_i…
1333 new_line = line_index1 - 1;
1334 ledit_line *prevline = buffer_ge…
1335 ledit_view_line *vprevline = vie…
1336 if (vprevline->text_dirty)
1337 set_pango_text_and_highl…
1338 new_byte = view_x_softline_to_po…
1339 rgl1 = line_index1 - 1;
1340 rgb1 = prevline->len;
1341 rgl2 = line_index1;
1342 rgb2 = line1->len;
1343 }
1344 buffer_delete_with_undo_base(
1345 view->buffer, cur_range,
1346 start_undo_group, view->mode,
1347 rgl1, rgb1, rgl2, rgb2, text_ret
1348 );
1349 } else {
1350 ledit_assert(pl2->start_index + pl2->len…
1351 rgl1 = rgl2 = line_index1;
1352 rgb1 = (size_t)pl1->start_index;
1353 rgb2 = (size_t)(pl2->start_index + pl2->…
1354 /* the deletion has to happen before cal…
1355 position because deleting softlines c…
1356 wrapping as well */
1357 buffer_delete_with_undo_base(
1358 view->buffer, cur_range,
1359 start_undo_group, view->mode,
1360 rgl1, rgb1, rgl2, rgb2, text_ret
1361 );
1362 set_pango_text_and_highlight(view, line_…
1363 vline1 = view_get_line(view, line_index1…
1364 if (l1 == vline1->softlines && line_inde…
1365 new_line = line_index1 + 1;
1366 new_byte = view_x_softline_to_po…
1367 view, line_index1 + 1, x, 0
1368 );
1369 } else if (l1 <= vline1->softlines - 1) {
1370 new_line = line_index1;
1371 new_byte = view_x_softline_to_po…
1372 view, line_index1, x, l1
1373 );
1374 } else if (l1 > 0) {
1375 new_line = line_index1;
1376 new_byte = view_x_softline_to_po…
1377 view, line_index1, x, l1 - 1
1378 );
1379 } else {
1380 /* the line has been emptied and…
1381 new_line = 0;
1382 new_byte = 0;
1383 }
1384 }
1385 } else {
1386 int x_useless, sl1, sl2;
1387 size_t l1 = line_index1, b1 = byte_index1;
1388 size_t l2 = line_index2, b2 = byte_index2;
1389 ledit_line *ll2 = buffer_get_line(view->buffer, …
1390 ledit_view_line *vl2 = view_get_line(view, l2);
1391 PangoLayout *layout1 = get_pango_layout(view, l1…
1392 PangoLayout *layout2 = get_pango_layout(view, l2…
1393 pango_layout_index_to_line_x(layout1, b1, 0, &sl…
1394 pango_layout_index_to_line_x(layout2, b2, 0, &sl…
1395 PangoLayoutLine *pl1 = pango_layout_get_line_rea…
1396 PangoLayoutLine *pl2 = pango_layout_get_line_rea…
1397 if (sl1 == 0 && sl2 == vl2->softlines - 1) {
1398 if (l1 == 0 && l2 == view->lines_num - 1…
1399 rgl1 = l1;
1400 rgl2 = l2;
1401 rgb1 = 0;
1402 rgb2 = ll2->len;
1403 new_line = 0;
1404 new_byte = 0;
1405 } else {
1406 if (l2 == view->lines_num - 1) {
1407 new_line = l1 - 1;
1408 ledit_line *new_lline = …
1409 ledit_view_line *new_vli…
1410 if (new_vline->text_dirt…
1411 set_pango_text_a…
1412 new_byte = view_x_softli…
1413 rgl1 = l1 - 1;
1414 rgb1 = new_lline->len;
1415 rgl2 = l2;
1416 rgb2 = ll2->len;
1417 } else {
1418 new_line = l1;
1419 new_byte = view_x_softli…
1420 view, l2 + 1, x, 0
1421 );
1422 rgl1 = l1;
1423 rgb1 = 0;
1424 rgl2 = l2 + 1;
1425 rgb2 = 0;
1426 }
1427 }
1428 buffer_delete_with_undo_base(
1429 view->buffer, cur_range,
1430 start_undo_group, view->mode,
1431 rgl1, rgb1, rgl2, rgb2, text_ret
1432 );
1433 } else if (sl1 == 0) {
1434 rgl1 = l1;
1435 rgb1 = 0;
1436 rgl2 = l2;
1437 rgb2 = (size_t)(pl2->start_index + pl2->…
1438 buffer_delete_with_undo_base(
1439 view->buffer, cur_range,
1440 start_undo_group, view->mode,
1441 rgl1, rgb1, rgl2, rgb2, text_ret
1442 );
1443 new_line = l1;
1444 new_byte = view_x_softline_to_pos(view, …
1445 } else if (sl2 == vl2->softlines - 1) {
1446 rgl1 = l1;
1447 rgb1 = (size_t)pl1->start_index;
1448 rgl2 = l2;
1449 rgb2 = ll2->len;
1450 if (l2 + 1 == view->lines_num) {
1451 new_line = l1;
1452 new_byte = view_x_softline_to_po…
1453 } else {
1454 new_line = l1 + 1;
1455 new_byte = view_x_softline_to_po…
1456 view, l2 + 1, x, 0
1457 );
1458 }
1459 buffer_delete_with_undo_base(
1460 view->buffer, cur_range,
1461 start_undo_group, view->mode,
1462 rgl1, rgb1, rgl2, rgb2, text_ret
1463 );
1464 } else {
1465 rgl1 = l1;
1466 rgb1 = (size_t)pl1->start_index;
1467 rgl2 = l2;
1468 rgb2 = (size_t)(pl2->start_index + pl2->…
1469 buffer_delete_with_undo_base(
1470 view->buffer, cur_range,
1471 start_undo_group, view->mode,
1472 rgl1, rgb1, rgl2, rgb2, text_ret
1473 );
1474 new_line = l1;
1475 /* important so vl1->softlines is update…
1476 set_pango_text_and_highlight(view, l1);
1477 ledit_view_line *vl1 = view_get_line(vie…
1478 /* it's technically possible that the re…
1479 second line is so small that it doesn…
1480 softline, so there needs to be a spec…
1481 a bit weird because the cursor will s…
1482 same line, but it now includes the re…
1483 (FIXME: this is probably not the best…
1484 new_byte = view_x_softline_to_pos(
1485 view, l1, x, sl1 + 1 < vl1->softline…
1486 );
1487 }
1488 }
1489 } else {
1490 rgl1 = line_index1;
1491 rgb1 = byte_index1;
1492 rgl2 = line_index2;
1493 rgb2 = byte_index2;
1494 new_line = rgl1;
1495 new_byte = rgb1;
1496 buffer_delete_with_undo_base(
1497 view->buffer, cur_range,
1498 start_undo_group, view->mode,
1499 rgl1, rgb1, rgl2, rgb2, text_ret
1500 );
1501 }
1502 /* note: line1/byte1 need to be set at the top since deleting te…
1503 might change the current line/byte of the view through the no…
1504 functions */
1505 cur_range.line2 = new_line;
1506 cur_range.byte2 = new_byte;
1507 undo_change_last_cur_range(view->buffer->undo, cur_range);
1508 /* FIXME: too much magic - maybe don't include this here */
1509 if (view->mode == NORMAL)
1510 new_byte = view_get_legal_normal_pos(view, new_line, new…
1511 if (new_line_ret)
1512 *new_line_ret = new_line;
1513 if (new_byte_ret)
1514 *new_byte_ret = new_byte;
1515 }
1516
1517 /* FIXME: any way to make this more efficient? */
1518 void
1519 view_resize_textview(void *data) {
1520 ledit_theme *theme = config_get_theme();
1521 ledit_view *view = (ledit_view *)data;
1522 view->total_height = 0;
1523 int text_w, text_h;
1524 window_get_textview_size(view->window, &text_w, &text_h);
1525 for (size_t i = 0; i < view->lines_num; i++) {
1526 ledit_view_line *line = view_get_line(view, i);
1527 line->w = text_w;
1528 line->text_dirty = 1; /* it's a bit weird to set this, i…
1529 set_pango_text_and_highlight(view, i);
1530 line->y_offset = view->total_height;
1531 line->dirty = 1;
1532 line->h_dirty = 0;
1533 view->total_height += line->h + theme->extra_line_spacin…
1534 }
1535 view->total_height -= theme->extra_line_spacing;
1536 window_set_scroll_max(view->window, view->total_height);
1537 if (view->display_offset > 0 &&
1538 view->display_offset + text_h > view->total_height) {
1539 view_scroll(view, view->total_height - text_h);
1540 }
1541 }
1542
1543 void
1544 view_scroll(ledit_view *view, long new_offset) {
1545 int text_w, text_h;
1546 window_get_textview_size(view->window, &text_w, &text_h);
1547 if (new_offset + text_h > view->total_height)
1548 new_offset = view->total_height - text_h;
1549 if (new_offset < 0)
1550 new_offset = 0;
1551 view->display_offset = new_offset;
1552 window_set_scroll_pos(view->window, view->display_offset);
1553 }
1554
1555 /* FIXME: there's gotta be a better/more efficient way to do this... */
1556 /* FIXME: make sure h_dirty is not set here */
1557 /* FIXME: this might cause weird effects when used together with extra-l…
1558 void
1559 view_get_nearest_legal_pos(
1560 ledit_view *view,
1561 size_t line, size_t byte,
1562 /*int snap_to_nearest, int snap_middle, FIXME: take these parameters…
1563 size_t *line_ret, size_t *byte_ret) {
1564 PangoRectangle strong, weak;
1565 int text_w, text_h;
1566 int x, sl_useless;
1567 window_get_textview_size(view->window, &text_w, &text_h);
1568 ledit_view_line *vline = view_get_line(view, line);
1569 PangoLayout *layout = get_pango_layout(view, line);
1570 if (byte > INT_MAX)
1571 err_overflow();
1572 pango_layout_get_cursor_pos(layout, (int)byte, &strong, &weak);
1573 view_pos_to_x_softline(view, line, byte, &x, &sl_useless);
1574 long cursor_y = strong.y / PANGO_SCALE + vline->y_offset;
1575 PangoRectangle ink, log;
1576 if (cursor_y < view->display_offset) {
1577 /* search for the hard line covering the top of the scre…
1578 size_t hline = line;
1579 while (vline->y_offset + vline->h <= view->display_offse…
1580 ++hline;
1581 vline = view_get_line(view, hline);
1582 }
1583 /* the current hard line is now the one at the very top …
1584 layout = get_pango_layout(view, hline);
1585 int num_sl = vline->softlines;
1586 int cur_y_off = 0;
1587 int sl_index = -1;
1588 PangoLayoutLine *sl;
1589 /* search for first soft line completely on-screen */
1590 for (int i = 0; i < num_sl; i++) {
1591 sl = pango_layout_get_line_readonly(layout, i);
1592 if (cur_y_off + vline->y_offset >= view->display…
1593 sl_index = i;
1594 break;
1595 }
1596 pango_layout_line_get_pixel_extents(sl, &ink, &l…
1597 cur_y_off += log.height;
1598 }
1599 if (sl_index >= 0) {
1600 /* we found the correct soft line */
1601 *line_ret = hline;
1602 *byte_ret = view_x_softline_to_pos(view, hline, …
1603 } else if (hline < view->lines_num - 1) {
1604 /* need to move to next hard line */
1605 *line_ret = hline + 1;
1606 *byte_ret = view_x_softline_to_pos(view, hline +…
1607 } else {
1608 /* no idea if this can happen, but just fail and…
1609 the last soft line of the last hard line */
1610 *line_ret = hline;
1611 *byte_ret = view_x_softline_to_pos(view, hline, …
1612 }
1613 } else if (cursor_y + strong.height / PANGO_SCALE >
1614 view->display_offset + text_h) {
1615 /* search for the hard line covering the bottom of the s…
1616 size_t hline = line;
1617 while (vline->y_offset > view->display_offset + text_h &…
1618 --hline;
1619 vline = view_get_line(view, hline);
1620 }
1621 /* the current hard line is now the one at the very bott…
1622 layout = get_pango_layout(view, hline);
1623 int num_sl = vline->softlines;
1624 int cur_y_off = 0;
1625 int sl_index = -1;
1626 PangoLayoutLine *sl;
1627 /* search for last soft line completely on-screen */
1628 for (int i = num_sl - 1; i >= 0; i--) {
1629 sl = pango_layout_get_line_readonly(layout, i);
1630 if (vline->y_offset + vline->h - cur_y_off < vie…
1631 sl_index = i;
1632 break;
1633 }
1634 pango_layout_line_get_pixel_extents(sl, &ink, &l…
1635 cur_y_off += log.height;
1636 }
1637 if (sl_index >= 0) {
1638 /* we found the correct soft line */
1639 *line_ret = hline;
1640 *byte_ret = view_x_softline_to_pos(view, hline, …
1641 } else if (hline > 0) {
1642 /* need to move to previous hard line */
1643 *line_ret = hline - 1;
1644 vline = view_get_line(view, hline - 1);
1645 num_sl = vline->softlines;
1646 *byte_ret = view_x_softline_to_pos(view, hline -…
1647 } else {
1648 /* no idea if this can happen, but just fail and…
1649 the first soft line of the first hard line */
1650 *line_ret = hline;
1651 *byte_ret = view_x_softline_to_pos(view, hline, …
1652 }
1653 } else {
1654 *line_ret = line;
1655 *byte_ret = byte;
1656 }
1657 }
1658
1659 void
1660 view_xy_to_line_byte(ledit_view *view, int x, int y, int snap_to_nearest…
1661 ledit_theme *theme = config_get_theme();
1662 long pos = view->display_offset + y;
1663 for (size_t i = 0; i < view->lines_num; i++) {
1664 ledit_view_line *vline = view_get_line(view, i);
1665 long y1 = vline->y_offset - (i == 0 ? 0 : theme->extra_l…
1666 long y2 = vline->y_offset + vline->h + theme->extra_line…
1667 if ((y1 <= pos && y2 > pos) || i == view->lines_num - 1)…
1668 int index, trailing;
1669 PangoLayout *layout = get_pango_layout(view, i);
1670 /* FIXME: what if i == view->lines_num - 1 but p…
1671 pango_layout_xy_to_index(
1672 layout,
1673 x * PANGO_SCALE, (int)(pos - y1) * PANGO_SCA…
1674 &index, &trailing
1675 );
1676 *byte_ret = (size_t)index;
1677 if (snap_to_nearest) {
1678 ledit_line *ll = buffer_get_line(view->b…
1679 while (trailing > 0) {
1680 trailing--;
1681 *byte_ret = line_next_utf8(ll, *…
1682 }
1683 }
1684 *line_ret = i;
1685 return;
1686 }
1687 }
1688 *line_ret = 0;
1689 *byte_ret = 0;
1690 }
1691
1692 static void
1693 scroll_to_pos(ledit_view *view, size_t line, size_t byte, int top) {
1694 PangoRectangle strong, weak;
1695 int text_w, text_h;
1696 window_get_textview_size(view->window, &text_w, &text_h);
1697 ledit_view_line *vl = view_get_line(view, line);
1698 PangoLayout *layout = get_pango_layout(view, line);
1699 if (byte > INT_MAX)
1700 err_overflow();
1701 pango_layout_get_cursor_pos(layout, (int)byte, &strong, &weak);
1702 long cursor_y = strong.y / PANGO_SCALE + vl->y_offset;
1703 if (top) {
1704 view_scroll(view, cursor_y);
1705 } else {
1706 view_scroll(view, cursor_y - text_h + strong.height / PA…
1707 }
1708 }
1709
1710 void
1711 view_scroll_to_pos_top(ledit_view *view, size_t line, size_t byte) {
1712 scroll_to_pos(view, line, byte, 1);
1713 }
1714
1715 void
1716 view_scroll_to_pos_bottom(ledit_view *view, size_t line, size_t byte) {
1717 scroll_to_pos(view, line, byte, 0);
1718 }
1719
1720 void
1721 view_ensure_cursor_shown(ledit_view *view) {
1722 PangoRectangle strong, weak;
1723 int text_w, text_h;
1724 window_get_textview_size(view->window, &text_w, &text_h);
1725 ledit_view_line *vline = view_get_line(view, view->cur_line);
1726 PangoLayout *layout = get_pango_layout(view, view->cur_line);
1727 if (view->cur_index > INT_MAX)
1728 err_overflow();
1729 pango_layout_get_cursor_pos(
1730 layout, (int)view->cur_index, &strong, &weak
1731 );
1732 long cursor_y = strong.y / PANGO_SCALE + vline->y_offset;
1733 if (cursor_y < view->display_offset) {
1734 view_scroll(view, cursor_y);
1735 } else if (cursor_y + strong.height / PANGO_SCALE >
1736 view->display_offset + text_h) {
1737 view_scroll(view, cursor_y - text_h + strong.height / PA…
1738 }
1739 }
1740
1741 /* FIXME: don't reset selection when selection is clicked away */
1742 /* FIXME: when selecting with mouse, only call this when button is relea…
1743 /* lines and bytes need to be sorted already! */
1744 static void
1745 copy_selection_to_x_primary(ledit_view *view, size_t line1, size_t byte1…
1746 txtbuf *primary = clipboard_get_primary_buffer(view->buffer->cli…
1747 buffer_copy_text_to_txtbuf(view->buffer, primary, line1, byte1, …
1748 clipboard_set_primary_selection_owner(view->buffer->clipboard);
1749 /*
1750 FIXME
1751 if (XGetSelectionOwner(state.dpy, XA_PRIMARY) != state.win)
1752 selclear();
1753 */
1754 }
1755
1756 void
1757 view_wipe_selection(ledit_view *view) {
1758 if (view->sel_valid) {
1759 if (view->sel.line1 > view->sel.line2)
1760 swap_sz(&view->sel.line1, &view->sel.line2);
1761 for (size_t i = view->sel.line1; i <= view->sel.line2; i…
1762 view_wipe_line_cursor_attrs(view, i);
1763 }
1764 }
1765 view->sel_valid = 0;
1766 view->sel.line1 = view->sel.line2 = 0;
1767 view->sel.byte1 = view->sel.byte2 = 0;
1768 }
1769
1770 void
1771 view_set_selection(ledit_view *view, size_t line1, size_t byte1, size_t …
1772 if (view->sel_valid &&
1773 line1 == view->sel.line1 && line2 == view->sel.line2 &&
1774 byte1 == view->sel.byte1 && byte2 == view->sel.byte2) {
1775 return;
1776 }
1777 size_t l1_new = line1, l2_new = line2;
1778 size_t b1_new = byte1, b2_new = byte2;
1779 sort_range(&l1_new, &b1_new, &l2_new, &b2_new);
1780 sort_range(&view->sel.line1, &view->sel.byte1, &view->sel.line2,…
1781 /* FIXME: make this a bit nicer and optimize it */
1782 /* FIXME: always reset view->sel so bugs like this can't happen …
1783 a function forgets to check sel_valid */
1784 if (view->sel_valid) {
1785 if (view->sel.line1 > l2_new || view->sel.line2 < l1_new…
1786 for (size_t i = view->sel.line1; i <= view->sel.…
1787 view_wipe_line_cursor_attrs(view, i);
1788 }
1789 } else {
1790 for (size_t i = view->sel.line1; i < l1_new; i++…
1791 view_wipe_line_cursor_attrs(view, i);
1792 }
1793 for (size_t i = view->sel.line2; i > l2_new; i--…
1794 view_wipe_line_cursor_attrs(view, i);
1795 }
1796 }
1797 }
1798 for (size_t i = l1_new; i <= l2_new; i++) {
1799 /* only change the ones that were not already selected */
1800 if (!view->sel_valid || i <= view->sel.line1 || i >= vie…
1801 ledit_view_line *vl = view_get_line(view, i);
1802 vl->highlight_dirty = 1;
1803 }
1804 }
1805 /* force current line to recalculate in case it wasn't covered a…
1806 ledit_view_line *vl = view_get_line(view, line2);
1807 vl->highlight_dirty = 1;
1808 if (l1_new != l2_new || b1_new != b2_new)
1809 copy_selection_to_x_primary(view, l1_new, b1_new, l2_new…
1810 view->sel.line1 = line1;
1811 view->sel.byte1 = byte1;
1812 view->sel.line2 = line2;
1813 view->sel.byte2 = byte2;
1814 view->sel_valid = 1;
1815 view->redraw = 1;
1816 }
1817
1818 static void
1819 view_scroll_handler(void *view, long pos) {
1820 /* FIXME: check for invalid pos? */
1821 ((ledit_view *)view)->display_offset = pos;
1822 }
1823
1824 static void
1825 view_button_handler(void *data, XEvent *event) {
1826 size_t l, b;
1827 ledit_view *view= (ledit_view *)data;
1828 int x, y;
1829 int snap;
1830 switch (event->type) {
1831 case ButtonPress:
1832 if (event->xbutton.button == Button1) {
1833 x = event->xbutton.x;
1834 y = event->xbutton.y;
1835 snap = view->mode == NORMAL ? 0 : 1;
1836 view_xy_to_line_byte(view, x, y, snap, &l, &b);
1837 view->selecting = 1;
1838 view_wipe_line_cursor_attrs(view, view->cur_line…
1839 view->cur_line = l;
1840 view->cur_index = b;
1841 /* don't set selection yet because the mouse may…
1842 dragged, so we don't want to switch to visual…
1843 allows setting just the cursor position in no…
1844 without always switching to visual) */
1845 if (view->mode == NORMAL)
1846 view_set_line_cursor_attrs(view, l, b);
1847 else if (view->mode == VISUAL)
1848 view_set_selection(view, l, b, l, b);
1849 } else if (event->xbutton.button == Button2) {
1850 view->button2_pressed = 1;
1851 }
1852 break;
1853 case ButtonRelease:
1854 /* FIXME: view->button2_pressed should be set to 0 even …
1855 the event does not occur inside the text area */
1856 if (event->xbutton.button == Button1) {
1857 view->selecting = 0;
1858 } else if (event->xbutton.button == Button2) {
1859 if (view->button2_pressed && view->mode == INSER…
1860 view_paste_primary(view);
1861 view->button2_pressed = 0;
1862 }
1863 break;
1864 case MotionNotify:
1865 x = event->xmotion.x;
1866 y = event->xmotion.y;
1867 if (view->selecting) {
1868 y = y >= 0 ? y : 0;
1869 view_xy_to_line_byte(view, x, y, 1, &l, &b);
1870 if (view->mode == NORMAL) {
1871 view_wipe_line_cursor_attrs(view, view->…
1872 /* FIXME: return to old mode afterwards?…
1873 /* should change_mode_group even be call…
1874 }
1875 view_set_mode(view, VISUAL);
1876 if (!view->sel_valid) {
1877 /* the selection has just started, so th…
1878 position is already set to the beginn…
1879 selection (see case ButtonPress above…
1880 view_set_selection(
1881 view,
1882 view->cur_line, view->cur_index, l, b
1883 );
1884 } else {
1885 view_set_selection(
1886 view,
1887 view->sel.line1, view->sel.byte1, l,…
1888 );
1889 }
1890 view->cur_line = l;
1891 view->cur_index = b;
1892 }
1893 break;
1894 }
1895 }
1896
1897 static void
1898 view_redraw_text(ledit_view *view) {
1899 ledit_theme *theme = config_get_theme();
1900 int cur_line_y = 0;
1901 int cursor_displayed = 0;
1902 int text_w, text_h;
1903 window_get_textview_size(view->window, &text_w, &text_h);
1904 /* FIXME: use binary search here */
1905 /* FIXME: draw extra highlight when extra-line-spacing set
1906 (also between soft lines because pango doesn't do that) */
1907 for (size_t i = 0; i < view->lines_num; i++) {
1908 ledit_view_line *vline = view_get_line(view, i);
1909 if (vline->y_offset + vline->h > view->display_offset) {
1910 /* FIXME: vline->text_dirty should not happen he…
1911 if (vline->text_dirty || vline->highlight_dirty)
1912 set_pango_text_and_highlight(view, i);
1913 if (vline->dirty || !vline->cache_pixmap_valid) {
1914 render_line(view, i);
1915 }
1916 int final_y = 0;
1917 int dest_y = vline->y_offset - view->display_off…
1918 int final_h = vline->h;
1919 if (vline->y_offset < view->display_offset) {
1920 dest_y = 0;
1921 final_y = view->display_offset - vline->…
1922 final_h -= view->display_offset - vline-…
1923 }
1924 if (dest_y + final_h > text_h) {
1925 final_h -= final_y + final_h -
1926 view->display_offset - text_h;
1927 }
1928 cache_pixmap *pix = cache_get_pixmap(
1929 view->cache, vline->cache_pixmap_index
1930 );
1931 XCopyArea(
1932 view->buffer->common->dpy, pix->pixmap,
1933 view->window->drawable, view->window->gc,
1934 0, final_y, vline->w, final_h, 0, dest_y
1935 );
1936 if (i == view->cur_line) {
1937 cur_line_y = vline->y_offset - view->dis…
1938 cursor_displayed = 1;
1939 }
1940 if (vline->y_offset + vline->h >= view->display_…
1941 break;
1942 }
1943 }
1944
1945 XSetForeground(view->buffer->common->dpy, view->window->gc, them…
1946 PangoRectangle strong, weak;
1947 ledit_line *cur_line = buffer_get_line(view->buffer, view->cur_l…
1948 PangoLayout *layout = get_pango_layout(view, view->cur_line);
1949 pango_layout_get_cursor_pos(
1950 layout, (int)view->cur_index, &strong, &weak
1951 );
1952 /* FIXME: long, int, etc. */
1953 int cursor_y = strong.y / PANGO_SCALE + cur_line_y;
1954 if (cursor_displayed && cursor_y >= 0) {
1955 if (view->mode == NORMAL) {
1956 /* FIXME: figure out if there's a better way to …
1957 /* Seriously, which of the pango folks though it…
1958 not highlight spaces at the end of soft lines…
1959 horrible idea. Or am I just too stupid to use…
1960 /* FIXME: properly document what is happening he…
1961
1962 int box_x = strong.x / PANGO_SCALE;
1963 int box_w = 10;
1964 /* determine where the box should be drawn */
1965 PangoDirection dir = PANGO_DIRECTION_LTR;
1966 size_t tmp_index = view->cur_index;
1967 if (view->cur_index >= cur_line->len && cur_line…
1968 tmp_index = cur_line->len - 1;
1969 else if (view->cur_index >= cur_line->len)
1970 tmp_index = 0;
1971 dir = ledit_pango_layout_get_direction(layout, (…
1972
1973 int x, sli;
1974 pango_layout_index_to_line_x(layout, (int)view->…
1975 PangoLayoutLine *sl = pango_layout_get_line_read…
1976 if (dir != sl->resolved_dir) {
1977 box_w = 3;
1978 }
1979 if (dir == PANGO_DIRECTION_RTL || dir == PANGO_D…
1980 box_x = box_x - box_w;
1981 }
1982
1983 if (view->cur_index == cur_line->len ||
1984 (cur_line->text[view->cur_index] == ' ' &&
1985 view->cur_index == (size_t)(sl->start_index…
1986 XFillRectangle(
1987 view->buffer->common->dpy, view->win…
1988 box_x, cursor_y,
1989 box_w, strong.height / PANGO_SCALE
1990 );
1991 }
1992 } else if (view->mode == INSERT || view->mode == VISUAL)…
1993 XDrawLine(
1994 view->buffer->common->dpy, view->window->dra…
1995 strong.x / PANGO_SCALE, cursor_y,
1996 strong.x / PANGO_SCALE,
1997 (strong.y + strong.height) / PANGO_SCALE + c…
1998 );
1999 }
2000 }
2001 /* move input method position */
2002 if (!ledit_window_bottom_bar_text_shown(view->window)) {
2003 xximspot(
2004 view->window,
2005 strong.x / PANGO_SCALE,
2006 (strong.y + strong.height) / PANGO_SCALE + cur_line_y
2007 );
2008 }
2009 view->redraw = 0;
2010 }
2011
2012 void
2013 view_redraw(ledit_view *view, size_t lang_index) {
2014 window_set_format_args(
2015 view->window, view->mode, view->buffer->hard_line_based,
2016 view->cur_line + 1, view->cur_index + 1, lang_index
2017 );
2018 if (view->redraw || view->window->redraw) {
2019 window_clear(view->window);
2020 view_redraw_text(view);
2021 window_redraw(view->window);
2022 }
2023 }
2024
2025 void
2026 view_undo(ledit_view *view, int num) {
2027 /* FIXME: maybe wipe selection (although I guess this
2028 currently isn't possible in visual mode anyways) */
2029 view_wipe_line_cursor_attrs(view, view->cur_line);
2030 for (int i = 0; i < num; i++) {
2031 undo_status s = buffer_undo(view->buffer, view->mode, &v…
2032 if (view->mode == NORMAL) {
2033 view->cur_index = view_get_legal_normal_pos(
2034 view, view->cur_line, view->cur_index
2035 );
2036 }
2037 if (s != UNDO_NORMAL) {
2038 window_show_message(view->window, undo_state_to_…
2039 break;
2040 }
2041 }
2042 if (view->mode == NORMAL)
2043 view_set_line_cursor_attrs(view, view->cur_line, view->c…
2044 else
2045 view_wipe_line_cursor_attrs(view, view->cur_line);
2046 }
2047
2048 void
2049 view_redo(ledit_view *view, int num) {
2050 view_wipe_line_cursor_attrs(view, view->cur_line);
2051 for (int i = 0; i < num; i++) {
2052 undo_status s = buffer_redo(view->buffer, view->mode, &v…
2053 if (view->mode == NORMAL) {
2054 view->cur_index = view_get_legal_normal_pos(
2055 view, view->cur_line, view->cur_index
2056 );
2057 }
2058 if (s != UNDO_NORMAL) {
2059 window_show_message(view->window, undo_state_to_…
2060 break;
2061 }
2062 }
2063 if (view->mode == NORMAL)
2064 view_set_line_cursor_attrs(view, view->cur_line, view->c…
2065 else
2066 view_wipe_line_cursor_attrs(view, view->cur_line);
2067 }
2068
2069 static void
2070 paste_txtbuf(ledit_view *view, txtbuf *buf) {
2071 if (view->mode == NORMAL)
2072 view_wipe_line_cursor_attrs(view, view->cur_line);
2073 ledit_range cur_range;
2074 cur_range.line1 = view->cur_line;
2075 cur_range.byte1 = view->cur_index;
2076 /* just to avoid false positives during static analysis */
2077 cur_range.line2 = cur_range.byte2 = 0;
2078 buffer_insert_with_undo(
2079 view->buffer, cur_range, 1, 1, view->mode,
2080 view->cur_line, view->cur_index, buf->text, buf->len,
2081 &view->cur_line, &view->cur_index
2082 );
2083 view_ensure_cursor_shown(view);
2084 if (view->mode == NORMAL)
2085 view_set_line_cursor_attrs(view, view->cur_line, view->c…
2086 }
2087
2088 void
2089 view_paste_clipboard(ledit_view *view) {
2090 txtbuf *buf = clipboard_get_clipboard_text(view->buffer->clipboa…
2091 if (!buf)
2092 return; /* FIXME: warning? */
2093 paste_txtbuf(view, buf);
2094 }
2095
2096 void
2097 view_paste_primary(ledit_view *view) {
2098 txtbuf *buf = clipboard_get_primary_text(view->buffer->clipboard…
2099 if (!buf)
2100 return; /* FIXME: warning? */
2101 paste_txtbuf(view, buf);
2102 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.