/src/tmux/window-buffer.c
Line | Count | Source |
1 | | /* $OpenBSD: window-buffer.c,v 1.51 2026/07/15 12:45:39 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <sys/types.h> |
20 | | |
21 | | #include <stdio.h> |
22 | | #include <ctype.h> |
23 | | #include <stdlib.h> |
24 | | #include <string.h> |
25 | | #include <time.h> |
26 | | #include <unistd.h> |
27 | | |
28 | | #include "tmux.h" |
29 | | |
30 | | static struct screen *window_buffer_init(struct window_mode_entry *, |
31 | | struct cmdq_item *, struct cmd_find_state *, |
32 | | struct args *); |
33 | | static void window_buffer_free(struct window_mode_entry *); |
34 | | static void window_buffer_resize(struct window_mode_entry *, u_int, |
35 | | u_int); |
36 | | static void window_buffer_update(struct window_mode_entry *); |
37 | | static void window_buffer_key(struct window_mode_entry *, |
38 | | struct client *, struct session *, |
39 | | struct winlink *, key_code, struct mouse_event *); |
40 | | |
41 | 0 | #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -p -b '%%'" |
42 | | |
43 | | #define WINDOW_BUFFER_DEFAULT_FORMAT \ |
44 | 0 | "#{t/p:buffer_created}: #{buffer_sample}" |
45 | | |
46 | | #define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \ |
47 | 0 | "#{?#{e|<:#{line},10}," \ |
48 | 0 | "#{line}" \ |
49 | 0 | ",#{e|<:#{line},36}," \ |
50 | 0 | "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ |
51 | 0 | "}" |
52 | | |
53 | | static const struct menu_item window_buffer_menu_items[] = { |
54 | | { "Paste", 'p', NULL }, |
55 | | { "Paste Tagged", 'P', NULL }, |
56 | | { "", KEYC_NONE, NULL }, |
57 | | { "Tag", 't', NULL }, |
58 | | { "Tag All", '\024', NULL }, |
59 | | { "Tag None", 'T', NULL }, |
60 | | { "", KEYC_NONE, NULL }, |
61 | | { "Delete", 'd', NULL }, |
62 | | { "Delete Tagged", 'D', NULL }, |
63 | | { "", KEYC_NONE, NULL }, |
64 | | { "Cancel", 'q', NULL }, |
65 | | |
66 | | { NULL, KEYC_NONE, NULL } |
67 | | }; |
68 | | |
69 | | const struct window_mode window_buffer_mode = { |
70 | | .name = "buffer-mode", |
71 | | .default_format = WINDOW_BUFFER_DEFAULT_FORMAT, |
72 | | |
73 | | .init = window_buffer_init, |
74 | | .free = window_buffer_free, |
75 | | .resize = window_buffer_resize, |
76 | | .update = window_buffer_update, |
77 | | .key = window_buffer_key, |
78 | | }; |
79 | | |
80 | | struct window_buffer_itemdata { |
81 | | const char *name; |
82 | | u_int order; |
83 | | size_t size; |
84 | | }; |
85 | | |
86 | | struct window_buffer_modedata { |
87 | | struct window_pane *wp; |
88 | | struct cmd_find_state fs; |
89 | | |
90 | | struct mode_tree_data *data; |
91 | | struct spawn_editor_state *editor; |
92 | | struct window_buffer_editdata *edit; |
93 | | char *command; |
94 | | char *format; |
95 | | char *key_format; |
96 | | |
97 | | struct window_buffer_itemdata **item_list; |
98 | | u_int item_size; |
99 | | }; |
100 | | |
101 | | struct window_buffer_editdata { |
102 | | u_int wp_id; |
103 | | char *name; |
104 | | struct paste_buffer *pb; |
105 | | struct spawn_editor_state *editor; |
106 | | }; |
107 | | |
108 | | static void window_buffer_finish_edit(struct window_buffer_editdata *); |
109 | | static void window_buffer_draw_waiting(struct window_buffer_modedata *); |
110 | | |
111 | | static enum sort_order window_buffer_order_seq[] = { |
112 | | SORT_CREATION, |
113 | | SORT_NAME, |
114 | | SORT_SIZE, |
115 | | SORT_END, |
116 | | }; |
117 | | |
118 | | static struct window_buffer_itemdata * |
119 | | window_buffer_add_item(struct window_buffer_modedata *data) |
120 | 0 | { |
121 | 0 | struct window_buffer_itemdata *item; |
122 | |
|
123 | 0 | data->item_list = xreallocarray(data->item_list, data->item_size + 1, |
124 | 0 | sizeof *data->item_list); |
125 | 0 | item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); |
126 | 0 | return (item); |
127 | 0 | } |
128 | | |
129 | | static void |
130 | | window_buffer_free_item(struct window_buffer_itemdata *item) |
131 | 0 | { |
132 | 0 | free((void *)item->name); |
133 | 0 | free(item); |
134 | 0 | } |
135 | | |
136 | | static void |
137 | | window_buffer_build(void *modedata, struct sort_criteria *sort_crit, |
138 | | __unused uint64_t *tag, const char *filter) |
139 | 0 | { |
140 | 0 | struct window_buffer_modedata *data = modedata; |
141 | 0 | struct window_buffer_itemdata *item; |
142 | 0 | u_int i, n; |
143 | 0 | struct paste_buffer *pb, **l; |
144 | 0 | char *text, *cp; |
145 | 0 | struct format_tree *ft; |
146 | 0 | struct session *s = NULL; |
147 | 0 | struct winlink *wl = NULL; |
148 | 0 | struct window_pane *wp = NULL; |
149 | |
|
150 | 0 | for (i = 0; i < data->item_size; i++) |
151 | 0 | window_buffer_free_item(data->item_list[i]); |
152 | 0 | free(data->item_list); |
153 | 0 | data->item_list = NULL; |
154 | 0 | data->item_size = 0; |
155 | |
|
156 | 0 | l = sort_get_buffers(&n, sort_crit); |
157 | 0 | for (i = 0; i < n; i++) { |
158 | 0 | item = window_buffer_add_item(data); |
159 | 0 | item->name = xstrdup(paste_buffer_name(l[i])); |
160 | 0 | paste_buffer_data(l[i], &item->size); |
161 | 0 | item->order = paste_buffer_order(l[i]); |
162 | 0 | } |
163 | |
|
164 | 0 | if (cmd_find_valid_state(&data->fs)) { |
165 | 0 | s = data->fs.s; |
166 | 0 | wl = data->fs.wl; |
167 | 0 | wp = data->fs.wp; |
168 | 0 | } |
169 | |
|
170 | 0 | for (i = 0; i < data->item_size; i++) { |
171 | 0 | item = data->item_list[i]; |
172 | |
|
173 | 0 | pb = paste_get_name(item->name); |
174 | 0 | if (pb == NULL) |
175 | 0 | continue; |
176 | 0 | ft = format_create(NULL, NULL, FORMAT_NONE, 0); |
177 | 0 | format_defaults(ft, NULL, s, wl, wp); |
178 | 0 | format_defaults_paste_buffer(ft, pb); |
179 | |
|
180 | 0 | if (filter != NULL) { |
181 | 0 | cp = format_expand(ft, filter); |
182 | 0 | if (!format_true(cp)) { |
183 | 0 | free(cp); |
184 | 0 | format_free(ft); |
185 | 0 | continue; |
186 | 0 | } |
187 | 0 | free(cp); |
188 | 0 | } |
189 | | |
190 | 0 | text = format_expand(ft, data->format); |
191 | 0 | mode_tree_add(data->data, NULL, item, item->order, item->name, |
192 | 0 | text, -1); |
193 | 0 | free(text); |
194 | |
|
195 | 0 | format_free(ft); |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | | static void |
200 | | window_buffer_draw(__unused void *modedata, void *itemdata, |
201 | | struct screen_write_ctx *ctx, u_int sx, u_int sy) |
202 | 0 | { |
203 | 0 | struct window_buffer_itemdata *item = itemdata; |
204 | 0 | struct paste_buffer *pb; |
205 | 0 | const char *pdata, *start, *end; |
206 | 0 | char *buf = NULL; |
207 | 0 | size_t psize; |
208 | 0 | u_int i, cx = ctx->s->cx, cy = ctx->s->cy; |
209 | |
|
210 | 0 | pb = paste_get_name(item->name); |
211 | 0 | if (pb == NULL) |
212 | 0 | return; |
213 | | |
214 | 0 | pdata = end = paste_buffer_data(pb, &psize); |
215 | 0 | for (i = 0; i < sy; i++) { |
216 | 0 | start = end; |
217 | 0 | while (end != pdata + psize && *end != '\n') |
218 | 0 | end++; |
219 | 0 | buf = xreallocarray(buf, 4, end - start + 1); |
220 | 0 | utf8_strvis(buf, start, end - start, |
221 | 0 | VIS_OCTAL|VIS_CSTYLE|VIS_TAB); |
222 | 0 | if (*buf != '\0') { |
223 | 0 | screen_write_cursormove(ctx, cx, cy + i, 0); |
224 | 0 | screen_write_nputs(ctx, sx, &grid_default_cell, "%s", |
225 | 0 | buf); |
226 | 0 | } |
227 | |
|
228 | 0 | if (end == pdata + psize) |
229 | 0 | break; |
230 | 0 | end++; |
231 | 0 | } |
232 | 0 | free(buf); |
233 | 0 | } |
234 | | |
235 | | static int |
236 | | window_buffer_find(const void *data, size_t datalen, const void *find, |
237 | | size_t findlen, int icase) |
238 | 0 | { |
239 | 0 | const u_char *udata = data, *ufind = find; |
240 | 0 | size_t i, j; |
241 | |
|
242 | 0 | if (findlen == 0 || datalen < findlen) |
243 | 0 | return (0); |
244 | 0 | for (i = 0; i + findlen <= datalen; i++) { |
245 | 0 | for (j = 0; j < findlen; j++) { |
246 | 0 | if (!icase && udata[i + j] != ufind[j]) |
247 | 0 | break; |
248 | 0 | if (icase && tolower(udata[i + j]) != tolower(ufind[j])) |
249 | 0 | break; |
250 | 0 | } |
251 | 0 | if (j == findlen) |
252 | 0 | return (1); |
253 | 0 | } |
254 | 0 | return (0); |
255 | 0 | } |
256 | | |
257 | | static int |
258 | | window_buffer_search(__unused void *modedata, void *itemdata, const char *ss, |
259 | | int icase) |
260 | 0 | { |
261 | 0 | struct window_buffer_itemdata *item = itemdata; |
262 | 0 | struct paste_buffer *pb; |
263 | 0 | const char *bufdata; |
264 | 0 | size_t bufsize; |
265 | |
|
266 | 0 | if ((pb = paste_get_name(item->name)) == NULL) |
267 | 0 | return (0); |
268 | 0 | if (icase) { |
269 | 0 | if (strcasestr(item->name, ss) != NULL) |
270 | 0 | return (1); |
271 | 0 | bufdata = paste_buffer_data(pb, &bufsize); |
272 | 0 | return (window_buffer_find(bufdata, bufsize, ss, strlen(ss), |
273 | 0 | icase)); |
274 | 0 | } else { |
275 | 0 | if (strstr(item->name, ss) != NULL) |
276 | 0 | return (1); |
277 | 0 | bufdata = paste_buffer_data(pb, &bufsize); |
278 | 0 | return (window_buffer_find(bufdata, bufsize, ss, strlen(ss), |
279 | 0 | icase)); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | static void |
284 | | window_buffer_menu(void *modedata, struct client *c, key_code key) |
285 | 0 | { |
286 | 0 | struct window_buffer_modedata *data = modedata; |
287 | 0 | struct window_pane *wp = data->wp; |
288 | 0 | struct window_mode_entry *wme; |
289 | |
|
290 | 0 | wme = TAILQ_FIRST(&wp->modes); |
291 | 0 | if (wme == NULL || wme->data != modedata) |
292 | 0 | return; |
293 | 0 | window_buffer_key(wme, c, NULL, NULL, key, NULL); |
294 | 0 | } |
295 | | |
296 | | static key_code |
297 | | window_buffer_get_key(void *modedata, void *itemdata, u_int line) |
298 | 0 | { |
299 | 0 | struct window_buffer_modedata *data = modedata; |
300 | 0 | struct window_buffer_itemdata *item = itemdata; |
301 | 0 | struct format_tree *ft; |
302 | 0 | struct session *s = NULL; |
303 | 0 | struct winlink *wl = NULL; |
304 | 0 | struct window_pane *wp = NULL; |
305 | 0 | struct paste_buffer *pb; |
306 | 0 | char *expanded; |
307 | 0 | key_code key; |
308 | |
|
309 | 0 | if (cmd_find_valid_state(&data->fs)) { |
310 | 0 | s = data->fs.s; |
311 | 0 | wl = data->fs.wl; |
312 | 0 | wp = data->fs.wp; |
313 | 0 | } |
314 | 0 | pb = paste_get_name(item->name); |
315 | 0 | if (pb == NULL) |
316 | 0 | return (KEYC_NONE); |
317 | | |
318 | 0 | ft = format_create(NULL, NULL, FORMAT_NONE, 0); |
319 | 0 | format_defaults(ft, NULL, NULL, 0, NULL); |
320 | 0 | format_defaults(ft, NULL, s, wl, wp); |
321 | 0 | format_defaults_paste_buffer(ft, pb); |
322 | 0 | format_add(ft, "line", "%u", line); |
323 | |
|
324 | 0 | expanded = format_expand(ft, data->key_format); |
325 | 0 | key = key_string_lookup_string(expanded); |
326 | 0 | free(expanded); |
327 | 0 | format_free(ft); |
328 | 0 | return (key); |
329 | 0 | } |
330 | | |
331 | | static void |
332 | | window_buffer_sort(struct sort_criteria *sort_crit) |
333 | 0 | { |
334 | 0 | sort_crit->order_seq = window_buffer_order_seq; |
335 | 0 | if (sort_crit->order == SORT_END) |
336 | 0 | sort_crit->order = sort_crit->order_seq[0]; |
337 | 0 | } |
338 | | |
339 | | static const char* window_buffer_help_lines[] = { |
340 | | "#[fg=themelightgrey]" |
341 | | " Enter #[#{E:tree-mode-border-style},acs]x#[default] Paste selected %1", |
342 | | "#[fg=themelightgrey]" |
343 | | " p #[#{E:tree-mode-border-style},acs]x#[default] Paste selected %1", |
344 | | "#[fg=themelightgrey]" |
345 | | " P #[#{E:tree-mode-border-style},acs]x#[default] Paste tagged %1s", |
346 | | "#[fg=themelightgrey]" |
347 | | " d #[#{E:tree-mode-border-style},acs]x#[default] Delete selected %1", |
348 | | "#[fg=themelightgrey]" |
349 | | " D #[#{E:tree-mode-border-style},acs]x#[default] Delete tagged %1s", |
350 | | "#[fg=themelightgrey]" |
351 | | " e #[#{E:tree-mode-border-style},acs]x#[default] Open %1 in editor", |
352 | | "#[fg=themelightgrey]" |
353 | | " f #[#{E:tree-mode-border-style},acs]x#[default] Enter a filter", |
354 | | NULL |
355 | | }; |
356 | | |
357 | | static const char** |
358 | | window_buffer_help(u_int *width, const char **item) |
359 | 0 | { |
360 | 0 | *width = 0; |
361 | 0 | *item = "buffer"; |
362 | 0 | return (window_buffer_help_lines); |
363 | 0 | } |
364 | | |
365 | | static struct screen * |
366 | | window_buffer_init(struct window_mode_entry *wme, |
367 | | __unused struct cmdq_item *item, struct cmd_find_state *fs, |
368 | | struct args *args) |
369 | 0 | { |
370 | 0 | struct window_pane *wp = wme->wp; |
371 | 0 | struct window_buffer_modedata *data; |
372 | 0 | struct screen *s; |
373 | |
|
374 | 0 | wme->data = data = xcalloc(1, sizeof *data); |
375 | 0 | data->wp = wp; |
376 | 0 | cmd_find_copy_state(&data->fs, fs); |
377 | |
|
378 | 0 | if (args == NULL || !args_has(args, 'F')) |
379 | 0 | data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); |
380 | 0 | else |
381 | 0 | data->format = xstrdup(args_get(args, 'F')); |
382 | 0 | if (args == NULL || !args_has(args, 'K')) |
383 | 0 | data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT); |
384 | 0 | else |
385 | 0 | data->key_format = xstrdup(args_get(args, 'K')); |
386 | 0 | if (args == NULL || args_count(args) == 0) |
387 | 0 | data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); |
388 | 0 | else |
389 | 0 | data->command = xstrdup(args_string(args, 0)); |
390 | |
|
391 | 0 | data->data = mode_tree_start(wp, args, window_buffer_build, |
392 | 0 | window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, |
393 | 0 | window_buffer_get_key, NULL, window_buffer_sort, window_buffer_help, |
394 | 0 | data, window_buffer_menu_items, &s); |
395 | 0 | mode_tree_zoom(data->data, args); |
396 | |
|
397 | 0 | mode_tree_build(data->data); |
398 | 0 | mode_tree_draw(data->data); |
399 | |
|
400 | 0 | return (s); |
401 | 0 | } |
402 | | |
403 | | static void |
404 | | window_buffer_free(struct window_mode_entry *wme) |
405 | 0 | { |
406 | 0 | struct window_buffer_modedata *data = wme->data; |
407 | 0 | u_int i; |
408 | |
|
409 | 0 | if (data == NULL) |
410 | 0 | return; |
411 | | |
412 | 0 | if (data->editor != NULL) { |
413 | 0 | spawn_cancel_editor(data->editor); |
414 | 0 | window_buffer_finish_edit(data->edit); |
415 | 0 | } |
416 | |
|
417 | 0 | mode_tree_free(data->data); |
418 | |
|
419 | 0 | for (i = 0; i < data->item_size; i++) |
420 | 0 | window_buffer_free_item(data->item_list[i]); |
421 | 0 | free(data->item_list); |
422 | |
|
423 | 0 | free(data->format); |
424 | 0 | free(data->key_format); |
425 | 0 | free(data->command); |
426 | |
|
427 | 0 | free(data); |
428 | 0 | } |
429 | | |
430 | | static void |
431 | | window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) |
432 | 0 | { |
433 | 0 | struct window_buffer_modedata *data = wme->data; |
434 | |
|
435 | 0 | mode_tree_resize(data->data, sx, sy); |
436 | 0 | } |
437 | | |
438 | | static void |
439 | | window_buffer_update(struct window_mode_entry *wme) |
440 | 0 | { |
441 | 0 | struct window_buffer_modedata *data = wme->data; |
442 | |
|
443 | 0 | mode_tree_build(data->data); |
444 | 0 | mode_tree_draw(data->data); |
445 | 0 | window_buffer_draw_waiting(data); |
446 | 0 | data->wp->flags |= PANE_REDRAW; |
447 | 0 | } |
448 | | |
449 | | static void |
450 | | window_buffer_do_delete(void *modedata, void *itemdata, |
451 | | __unused struct client *c, __unused key_code key) |
452 | 0 | { |
453 | 0 | struct window_buffer_modedata *data = modedata; |
454 | 0 | struct window_buffer_itemdata *item = itemdata; |
455 | 0 | struct paste_buffer *pb; |
456 | |
|
457 | 0 | if (item == mode_tree_get_current(data->data) && |
458 | 0 | !mode_tree_down(data->data, 0)) { |
459 | | /* |
460 | | *If we were unable to select the item further down we are at |
461 | | * the end of the list. Move one element up instead, to make |
462 | | * sure that we preserve a valid selection or we risk having |
463 | | * the tree build logic reset it to the first item. |
464 | | */ |
465 | 0 | mode_tree_up(data->data, 0); |
466 | 0 | } |
467 | |
|
468 | 0 | if ((pb = paste_get_name(item->name)) != NULL) |
469 | 0 | paste_free(pb); |
470 | 0 | } |
471 | | |
472 | | static void |
473 | | window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, |
474 | | __unused key_code key) |
475 | 0 | { |
476 | 0 | struct window_buffer_modedata *data = modedata; |
477 | 0 | struct window_buffer_itemdata *item = itemdata; |
478 | |
|
479 | 0 | if (paste_get_name(item->name) != NULL) |
480 | 0 | mode_tree_run_command(c, NULL, data->command, item->name); |
481 | 0 | } |
482 | | |
483 | | static void |
484 | | window_buffer_finish_edit(struct window_buffer_editdata *ed) |
485 | 0 | { |
486 | 0 | free(ed->name); |
487 | 0 | free(ed); |
488 | 0 | } |
489 | | |
490 | | static void |
491 | | window_buffer_draw_waiting(struct window_buffer_modedata *data) |
492 | 0 | { |
493 | 0 | struct screen_write_ctx ctx; |
494 | 0 | struct screen *s = data->wp->screen; |
495 | 0 | struct grid_cell gc; |
496 | 0 | char text[128]; |
497 | 0 | u_int sx, sy, box_w, box_h, x, y, text_x; |
498 | 0 | size_t textlen; |
499 | 0 | pid_t pid; |
500 | |
|
501 | 0 | if (data->editor == NULL) |
502 | 0 | return; |
503 | 0 | sx = screen_size_x(s); |
504 | 0 | sy = screen_size_y(s); |
505 | 0 | if (sx == 0 || sy == 0) |
506 | 0 | return; |
507 | | |
508 | 0 | pid = spawn_get_editor_pid(data->editor); |
509 | 0 | if (pid == -1) |
510 | 0 | xsnprintf(text, sizeof text, "WAITING FOR EDITOR"); |
511 | 0 | else |
512 | 0 | xsnprintf(text, sizeof text, "WAITING FOR EDITOR (PID %ld)", |
513 | 0 | (long)pid); |
514 | |
|
515 | 0 | textlen = strlen(text); |
516 | 0 | box_w = textlen + 4; |
517 | 0 | box_h = 3; |
518 | 0 | if (sx < box_w || sy < box_h) |
519 | 0 | return; |
520 | 0 | x = (sx - box_w) / 2; |
521 | 0 | y = (sy - box_h) / 2; |
522 | 0 | text_x = x + (box_w - textlen) / 2; |
523 | |
|
524 | 0 | memcpy(&gc, &grid_default_cell, sizeof gc); |
525 | 0 | screen_write_start(&ctx, s); |
526 | 0 | screen_write_cursormove(&ctx, x, y, 0); |
527 | 0 | screen_write_box(&ctx, box_w, box_h, BOX_LINES_DEFAULT, &gc, NULL); |
528 | 0 | screen_write_cursormove(&ctx, x + 1, y + 1, 0); |
529 | 0 | screen_write_clearcharacter(&ctx, box_w - 2, gc.bg); |
530 | 0 | screen_write_cursormove(&ctx, text_x, y + 1, 0); |
531 | 0 | screen_write_nputs(&ctx, box_w - 2, &gc, "%s", text); |
532 | 0 | screen_write_stop(&ctx); |
533 | 0 | } |
534 | | |
535 | | static void |
536 | | window_buffer_edit_close_cb(char *buf, size_t len, void *arg) |
537 | 0 | { |
538 | 0 | struct window_buffer_editdata *ed = arg; |
539 | 0 | size_t oldlen; |
540 | 0 | const char *oldbuf; |
541 | 0 | struct paste_buffer *pb; |
542 | 0 | struct window_pane *wp; |
543 | 0 | struct window_buffer_modedata *data; |
544 | 0 | struct window_mode_entry *wme; |
545 | |
|
546 | 0 | wp = window_pane_find_by_id(ed->wp_id); |
547 | 0 | if (wp != NULL) { |
548 | 0 | wme = TAILQ_FIRST(&wp->modes); |
549 | 0 | if (wme != NULL && wme->mode == &window_buffer_mode) { |
550 | 0 | data = wme->data; |
551 | 0 | if (data->editor == ed->editor) { |
552 | 0 | data->editor = NULL; |
553 | 0 | data->edit = NULL; |
554 | 0 | } |
555 | 0 | } |
556 | 0 | } |
557 | |
|
558 | 0 | if (buf == NULL || len == 0) { |
559 | 0 | window_buffer_finish_edit(ed); |
560 | 0 | return; |
561 | 0 | } |
562 | | |
563 | 0 | pb = paste_get_name(ed->name); |
564 | 0 | if (pb == NULL || pb != ed->pb) { |
565 | 0 | window_buffer_finish_edit(ed); |
566 | 0 | return; |
567 | 0 | } |
568 | | |
569 | 0 | oldbuf = paste_buffer_data(pb, &oldlen); |
570 | 0 | if (oldlen != '\0' && |
571 | 0 | oldbuf[oldlen - 1] != '\n' && |
572 | 0 | buf[len - 1] == '\n') |
573 | 0 | len--; |
574 | 0 | if (len != 0) |
575 | 0 | paste_replace(pb, buf, len); |
576 | |
|
577 | 0 | wp = window_pane_find_by_id(ed->wp_id); |
578 | 0 | if (wp != NULL) { |
579 | 0 | wme = TAILQ_FIRST(&wp->modes); |
580 | 0 | if (wme != NULL && wme->mode == &window_buffer_mode) { |
581 | 0 | data = wme->data; |
582 | 0 | mode_tree_build(data->data); |
583 | 0 | mode_tree_draw(data->data); |
584 | 0 | window_buffer_draw_waiting(data); |
585 | 0 | } |
586 | 0 | wp->flags |= PANE_REDRAW; |
587 | 0 | } |
588 | 0 | window_buffer_finish_edit(ed); |
589 | 0 | } |
590 | | |
591 | | static void |
592 | | window_buffer_start_edit(struct window_buffer_modedata *data, |
593 | | struct window_buffer_itemdata *item, struct client *c) |
594 | 0 | { |
595 | 0 | struct paste_buffer *pb; |
596 | 0 | const char *buf; |
597 | 0 | size_t len; |
598 | 0 | struct window_buffer_editdata *ed; |
599 | |
|
600 | 0 | if (data->editor != NULL) |
601 | 0 | return; |
602 | 0 | if ((pb = paste_get_name(item->name)) == NULL) |
603 | 0 | return; |
604 | 0 | buf = paste_buffer_data(pb, &len); |
605 | |
|
606 | 0 | ed = xcalloc(1, sizeof *ed); |
607 | 0 | ed->wp_id = data->wp->id; |
608 | 0 | ed->name = xstrdup(paste_buffer_name(pb)); |
609 | 0 | ed->pb = pb; |
610 | |
|
611 | 0 | ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed); |
612 | 0 | if (ed->editor == NULL) |
613 | 0 | window_buffer_finish_edit(ed); |
614 | 0 | else { |
615 | 0 | data->editor = ed->editor; |
616 | 0 | data->edit = ed; |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | | static void |
621 | | window_buffer_key(struct window_mode_entry *wme, struct client *c, |
622 | | __unused struct session *s, __unused struct winlink *wl, key_code key, |
623 | | struct mouse_event *m) |
624 | 0 | { |
625 | 0 | struct window_pane *wp = wme->wp; |
626 | 0 | struct window_buffer_modedata *data = wme->data; |
627 | 0 | struct mode_tree_data *mtd = data->data; |
628 | 0 | struct window_buffer_itemdata *item; |
629 | 0 | int finished; |
630 | |
|
631 | 0 | if (paste_is_empty()) { |
632 | 0 | finished = 1; |
633 | 0 | goto out; |
634 | 0 | } |
635 | 0 | if (data->editor != NULL) { |
636 | 0 | if (key == 'q' || key == '\033' || key == '\003') |
637 | 0 | finished = 1; |
638 | 0 | else |
639 | 0 | finished = 0; |
640 | 0 | goto out; |
641 | 0 | } |
642 | | |
643 | 0 | finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); |
644 | 0 | switch (key) { |
645 | 0 | case 'e': |
646 | 0 | item = mode_tree_get_current(mtd); |
647 | 0 | window_buffer_start_edit(data, item, c); |
648 | 0 | break; |
649 | 0 | case 'd': |
650 | 0 | item = mode_tree_get_current(mtd); |
651 | 0 | window_buffer_do_delete(data, item, c, key); |
652 | 0 | mode_tree_build(mtd); |
653 | 0 | break; |
654 | 0 | case 'D': |
655 | 0 | mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0); |
656 | 0 | mode_tree_build(mtd); |
657 | 0 | break; |
658 | 0 | case 'P': |
659 | 0 | mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0); |
660 | 0 | finished = 1; |
661 | 0 | break; |
662 | 0 | case 'p': |
663 | 0 | case '\r': |
664 | 0 | item = mode_tree_get_current(mtd); |
665 | 0 | window_buffer_do_paste(data, item, c, key); |
666 | 0 | finished = 1; |
667 | 0 | break; |
668 | 0 | } |
669 | | |
670 | 0 | out: |
671 | 0 | if (finished || paste_is_empty()) |
672 | 0 | window_pane_reset_mode(wp); |
673 | 0 | else { |
674 | 0 | mode_tree_draw(mtd); |
675 | 0 | window_buffer_draw_waiting(data); |
676 | 0 | wp->flags |= PANE_REDRAW; |
677 | 0 | } |
678 | 0 | } |