xref: /netbsd-src/external/bsd/tmux/dist/window-buffer.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
1c9ad075bSchristos /* $OpenBSD$ */
2c9ad075bSchristos 
3c9ad075bSchristos /*
4c9ad075bSchristos  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5c9ad075bSchristos  *
6c9ad075bSchristos  * Permission to use, copy, modify, and distribute this software for any
7c9ad075bSchristos  * purpose with or without fee is hereby granted, provided that the above
8c9ad075bSchristos  * copyright notice and this permission notice appear in all copies.
9c9ad075bSchristos  *
10c9ad075bSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c9ad075bSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c9ad075bSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c9ad075bSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c9ad075bSchristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15c9ad075bSchristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16c9ad075bSchristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c9ad075bSchristos  */
18c9ad075bSchristos 
19c9ad075bSchristos #include <sys/types.h>
20c9ad075bSchristos 
21e271dbb8Schristos #include <stdio.h>
22c9ad075bSchristos #include <stdlib.h>
23c9ad075bSchristos #include <string.h>
24c9ad075bSchristos #include <time.h>
25e271dbb8Schristos #include <unistd.h>
26c9ad075bSchristos 
27c9ad075bSchristos #include "tmux.h"
28c9ad075bSchristos 
290a274e86Schristos static struct screen	*window_buffer_init(struct window_mode_entry *,
30c9ad075bSchristos 			     struct cmd_find_state *, struct args *);
310a274e86Schristos static void		 window_buffer_free(struct window_mode_entry *);
320a274e86Schristos static void		 window_buffer_resize(struct window_mode_entry *, u_int,
33c9ad075bSchristos 			     u_int);
34e271dbb8Schristos static void		 window_buffer_update(struct window_mode_entry *);
350a274e86Schristos static void		 window_buffer_key(struct window_mode_entry *,
360a274e86Schristos 			     struct client *, struct session *,
370a274e86Schristos 			     struct winlink *, key_code, struct mouse_event *);
38c9ad075bSchristos 
39*890b6d91Swiz #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -p -b '%%'"
40c9ad075bSchristos 
41c9ad075bSchristos #define WINDOW_BUFFER_DEFAULT_FORMAT \
42e271dbb8Schristos 	"#{t/p:buffer_created}: #{buffer_sample}"
43e271dbb8Schristos 
44e271dbb8Schristos #define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
45e271dbb8Schristos 	"#{?#{e|<:#{line},10}," \
46e271dbb8Schristos 		"#{line}" \
47e271dbb8Schristos 	"," \
48e271dbb8Schristos 		"#{?#{e|<:#{line},36},"	\
49e271dbb8Schristos 	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
50e271dbb8Schristos 		"," \
51e271dbb8Schristos 	        	"" \
52e271dbb8Schristos 		"}" \
53e271dbb8Schristos 	"}"
54c9ad075bSchristos 
5530744affSchristos static const struct menu_item window_buffer_menu_items[] = {
5630744affSchristos 	{ "Paste", 'p', NULL },
5730744affSchristos 	{ "Paste Tagged", 'P', NULL },
5830744affSchristos 	{ "", KEYC_NONE, NULL },
5930744affSchristos 	{ "Tag", 't', NULL },
6030744affSchristos 	{ "Tag All", '\024', NULL },
6130744affSchristos 	{ "Tag None", 'T', NULL },
6230744affSchristos 	{ "", KEYC_NONE, NULL },
6330744affSchristos 	{ "Delete", 'd', NULL },
6430744affSchristos 	{ "Delete Tagged", 'D', NULL },
6530744affSchristos 	{ "", KEYC_NONE, NULL },
6630744affSchristos 	{ "Cancel", 'q', NULL },
6730744affSchristos 
6830744affSchristos 	{ NULL, KEYC_NONE, NULL }
6930744affSchristos };
7030744affSchristos 
71c9ad075bSchristos const struct window_mode window_buffer_mode = {
72c9ad075bSchristos 	.name = "buffer-mode",
730a274e86Schristos 	.default_format = WINDOW_BUFFER_DEFAULT_FORMAT,
74c9ad075bSchristos 
75c9ad075bSchristos 	.init = window_buffer_init,
76c9ad075bSchristos 	.free = window_buffer_free,
77c9ad075bSchristos 	.resize = window_buffer_resize,
78e271dbb8Schristos 	.update = window_buffer_update,
79c9ad075bSchristos 	.key = window_buffer_key,
80c9ad075bSchristos };
81c9ad075bSchristos 
82c9ad075bSchristos enum window_buffer_sort_type {
83c9ad075bSchristos 	WINDOW_BUFFER_BY_TIME,
84c9ad075bSchristos 	WINDOW_BUFFER_BY_NAME,
85c9ad075bSchristos 	WINDOW_BUFFER_BY_SIZE,
86c9ad075bSchristos };
87c9ad075bSchristos static const char *window_buffer_sort_list[] = {
88c9ad075bSchristos 	"time",
89c9ad075bSchristos 	"name",
90c9ad075bSchristos 	"size"
91c9ad075bSchristos };
9268e6ba84Schristos static struct mode_tree_sort_criteria *window_buffer_sort;
93c9ad075bSchristos 
94c9ad075bSchristos struct window_buffer_itemdata {
95c9ad075bSchristos 	const char	*name;
96c9ad075bSchristos 	u_int		 order;
97c9ad075bSchristos 	size_t		 size;
98c9ad075bSchristos };
99c9ad075bSchristos 
100c9ad075bSchristos struct window_buffer_modedata {
10130744affSchristos 	struct window_pane		 *wp;
10230744affSchristos 	struct cmd_find_state		  fs;
10330744affSchristos 
104c9ad075bSchristos 	struct mode_tree_data		 *data;
105c9ad075bSchristos 	char				 *command;
106c9ad075bSchristos 	char				 *format;
107e271dbb8Schristos 	char				 *key_format;
108c9ad075bSchristos 
109c9ad075bSchristos 	struct window_buffer_itemdata	**item_list;
110c9ad075bSchristos 	u_int				  item_size;
111c9ad075bSchristos };
112c9ad075bSchristos 
113e271dbb8Schristos struct window_buffer_editdata {
114e271dbb8Schristos 	u_int			 wp_id;
115e271dbb8Schristos 	char			*name;
116e271dbb8Schristos 	struct paste_buffer	*pb;
117e271dbb8Schristos };
118e271dbb8Schristos 
119c9ad075bSchristos static struct window_buffer_itemdata *
120c9ad075bSchristos window_buffer_add_item(struct window_buffer_modedata *data)
121c9ad075bSchristos {
122c9ad075bSchristos 	struct window_buffer_itemdata	*item;
123c9ad075bSchristos 
124c9ad075bSchristos 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
125c9ad075bSchristos 	    sizeof *data->item_list);
126c9ad075bSchristos 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
127c9ad075bSchristos 	return (item);
128c9ad075bSchristos }
129c9ad075bSchristos 
130c9ad075bSchristos static void
131c9ad075bSchristos window_buffer_free_item(struct window_buffer_itemdata *item)
132c9ad075bSchristos {
133fe99a117Schristos 	free(__UNCONST(item->name));
134c9ad075bSchristos 	free(item);
135c9ad075bSchristos }
136c9ad075bSchristos 
137c9ad075bSchristos static int
13868e6ba84Schristos window_buffer_cmp(const void *a0, const void *b0)
139c9ad075bSchristos {
140c9ad075bSchristos 	const struct window_buffer_itemdata *const	*a = a0;
141c9ad075bSchristos 	const struct window_buffer_itemdata *const	*b = b0;
14268e6ba84Schristos 	int						 result = 0;
143c9ad075bSchristos 
14468e6ba84Schristos 	if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME)
14568e6ba84Schristos 		result = (*b)->order - (*a)->order;
14668e6ba84Schristos 	else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE)
14768e6ba84Schristos 		result = (*b)->size - (*a)->size;
148c9ad075bSchristos 
14968e6ba84Schristos 	/* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
15068e6ba84Schristos 	if (result == 0)
15168e6ba84Schristos 		result = strcmp((*a)->name, (*b)->name);
152c9ad075bSchristos 
15368e6ba84Schristos 	if (window_buffer_sort->reversed)
15468e6ba84Schristos 		result = -result;
15568e6ba84Schristos 	return (result);
156c9ad075bSchristos }
157c9ad075bSchristos 
158c9ad075bSchristos static void
15968e6ba84Schristos window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
16068e6ba84Schristos     __unused uint64_t *tag, const char *filter)
161c9ad075bSchristos {
162c9ad075bSchristos 	struct window_buffer_modedata	*data = modedata;
163c9ad075bSchristos 	struct window_buffer_itemdata	*item;
164c9ad075bSchristos 	u_int				 i;
165c9ad075bSchristos 	struct paste_buffer		*pb;
166c9ad075bSchristos 	char				*text, *cp;
167c9ad075bSchristos 	struct format_tree		*ft;
16830744affSchristos 	struct session			*s = NULL;
16930744affSchristos 	struct winlink			*wl = NULL;
17030744affSchristos 	struct window_pane		*wp = NULL;
171c9ad075bSchristos 
172c9ad075bSchristos 	for (i = 0; i < data->item_size; i++)
173c9ad075bSchristos 		window_buffer_free_item(data->item_list[i]);
174c9ad075bSchristos 	free(data->item_list);
175c9ad075bSchristos 	data->item_list = NULL;
176c9ad075bSchristos 	data->item_size = 0;
177c9ad075bSchristos 
178c9ad075bSchristos 	pb = NULL;
179c9ad075bSchristos 	while ((pb = paste_walk(pb)) != NULL) {
180c9ad075bSchristos 		item = window_buffer_add_item(data);
181c9ad075bSchristos 		item->name = xstrdup(paste_buffer_name(pb));
182c9ad075bSchristos 		paste_buffer_data(pb, &item->size);
183c9ad075bSchristos 		item->order = paste_buffer_order(pb);
184c9ad075bSchristos 	}
185c9ad075bSchristos 
18668e6ba84Schristos 	window_buffer_sort = sort_crit;
187c9ad075bSchristos 	qsort(data->item_list, data->item_size, sizeof *data->item_list,
18868e6ba84Schristos 	    window_buffer_cmp);
189c9ad075bSchristos 
19030744affSchristos 	if (cmd_find_valid_state(&data->fs)) {
19130744affSchristos 		s = data->fs.s;
19230744affSchristos 		wl = data->fs.wl;
19330744affSchristos 		wp = data->fs.wp;
19430744affSchristos 	}
19530744affSchristos 
196c9ad075bSchristos 	for (i = 0; i < data->item_size; i++) {
197c9ad075bSchristos 		item = data->item_list[i];
198c9ad075bSchristos 
199c9ad075bSchristos 		pb = paste_get_name(item->name);
200c9ad075bSchristos 		if (pb == NULL)
201c9ad075bSchristos 			continue;
202c9ad075bSchristos 		ft = format_create(NULL, NULL, FORMAT_NONE, 0);
20330744affSchristos 		format_defaults(ft, NULL, s, wl, wp);
204c9ad075bSchristos 		format_defaults_paste_buffer(ft, pb);
205c9ad075bSchristos 
206c9ad075bSchristos 		if (filter != NULL) {
207c9ad075bSchristos 			cp = format_expand(ft, filter);
208c9ad075bSchristos 			if (!format_true(cp)) {
209c9ad075bSchristos 				free(cp);
210c9ad075bSchristos 				format_free(ft);
211c9ad075bSchristos 				continue;
212c9ad075bSchristos 			}
213c9ad075bSchristos 			free(cp);
214c9ad075bSchristos 		}
215c9ad075bSchristos 
216c9ad075bSchristos 		text = format_expand(ft, data->format);
217c9ad075bSchristos 		mode_tree_add(data->data, NULL, item, item->order, item->name,
218c9ad075bSchristos 		    text, -1);
219c9ad075bSchristos 		free(text);
220c9ad075bSchristos 
221c9ad075bSchristos 		format_free(ft);
222c9ad075bSchristos 	}
223c9ad075bSchristos 
224c9ad075bSchristos }
225c9ad075bSchristos 
226c7e17de0Schristos static void
227c7e17de0Schristos window_buffer_draw(__unused void *modedata, void *itemdata,
228c7e17de0Schristos     struct screen_write_ctx *ctx, u_int sx, u_int sy)
229c9ad075bSchristos {
230c9ad075bSchristos 	struct window_buffer_itemdata	*item = itemdata;
231c9ad075bSchristos 	struct paste_buffer		*pb;
23268e6ba84Schristos 	const char			*pdata, *start, *end;
23368e6ba84Schristos 	char				*buf = NULL;
23468e6ba84Schristos 	size_t				 psize;
235c7e17de0Schristos 	u_int				 i, cx = ctx->s->cx, cy = ctx->s->cy;
236c9ad075bSchristos 
237c9ad075bSchristos 	pb = paste_get_name(item->name);
238c9ad075bSchristos 	if (pb == NULL)
239c7e17de0Schristos 		return;
240c9ad075bSchristos 
241c9ad075bSchristos 	pdata = end = paste_buffer_data(pb, &psize);
242c9ad075bSchristos 	for (i = 0; i < sy; i++) {
24368e6ba84Schristos 		start = end;
24468e6ba84Schristos 		while (end != pdata + psize && *end != '\n')
245c9ad075bSchristos 			end++;
24668e6ba84Schristos 		buf = xreallocarray(buf, 4, end - start + 1);
247e271dbb8Schristos 		utf8_strvis(buf, start, end - start,
248e271dbb8Schristos 		    VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
24968e6ba84Schristos 		if (*buf != '\0') {
2500a274e86Schristos 			screen_write_cursormove(ctx, cx, cy + i, 0);
25168e6ba84Schristos 			screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
25268e6ba84Schristos 			    buf);
253c9ad075bSchristos 		}
254c9ad075bSchristos 
255c9ad075bSchristos 		if (end == pdata + psize)
256c9ad075bSchristos 			break;
257c9ad075bSchristos 		end++;
258c9ad075bSchristos 	}
25968e6ba84Schristos 	free(buf);
260c9ad075bSchristos }
261c9ad075bSchristos 
262c9ad075bSchristos static int
263c9ad075bSchristos window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
264c9ad075bSchristos {
265c9ad075bSchristos 	struct window_buffer_itemdata	*item = itemdata;
266c9ad075bSchristos 	struct paste_buffer		*pb;
267c9ad075bSchristos 	const char			*bufdata;
268c9ad075bSchristos 	size_t				 bufsize;
269c9ad075bSchristos 
270c9ad075bSchristos 	if ((pb = paste_get_name(item->name)) == NULL)
271c9ad075bSchristos 		return (0);
272c9ad075bSchristos 	if (strstr(item->name, ss) != NULL)
273c9ad075bSchristos 		return (1);
274c9ad075bSchristos 	bufdata = paste_buffer_data(pb, &bufsize);
275c9ad075bSchristos 	return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL);
276c9ad075bSchristos }
277c9ad075bSchristos 
27830744affSchristos static void
27930744affSchristos window_buffer_menu(void *modedata, struct client *c, key_code key)
28030744affSchristos {
28130744affSchristos 	struct window_buffer_modedata	*data = modedata;
28230744affSchristos 	struct window_pane		*wp = data->wp;
28330744affSchristos 	struct window_mode_entry	*wme;
28430744affSchristos 
28530744affSchristos 	wme = TAILQ_FIRST(&wp->modes);
28630744affSchristos 	if (wme == NULL || wme->data != modedata)
28730744affSchristos 		return;
28830744affSchristos 	window_buffer_key(wme, c, NULL, NULL, key, NULL);
28930744affSchristos }
29030744affSchristos 
291e271dbb8Schristos static key_code
292e271dbb8Schristos window_buffer_get_key(void *modedata, void *itemdata, u_int line)
293e271dbb8Schristos {
294e271dbb8Schristos 	struct window_buffer_modedata	*data = modedata;
295e271dbb8Schristos 	struct window_buffer_itemdata	*item = itemdata;
296e271dbb8Schristos 	struct format_tree		*ft;
29759b94b2cSchristos 	struct session			*s = NULL;
29859b94b2cSchristos 	struct winlink			*wl = NULL;
299e271dbb8Schristos 	struct window_pane		*wp = NULL;
300e271dbb8Schristos 	struct paste_buffer		*pb;
301e271dbb8Schristos 	char				*expanded;
302e271dbb8Schristos 	key_code			 key;
303e271dbb8Schristos 
304e271dbb8Schristos 	if (cmd_find_valid_state(&data->fs)) {
305e271dbb8Schristos 		s = data->fs.s;
306e271dbb8Schristos 		wl = data->fs.wl;
307e271dbb8Schristos 		wp = data->fs.wp;
308e271dbb8Schristos 	}
309e271dbb8Schristos 	pb = paste_get_name(item->name);
310e271dbb8Schristos 	if (pb == NULL)
31146548964Swiz 		return (KEYC_NONE);
312e271dbb8Schristos 
313e271dbb8Schristos 	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
314e271dbb8Schristos 	format_defaults(ft, NULL, NULL, 0, NULL);
315e271dbb8Schristos 	if (wp != NULL)
316e271dbb8Schristos 		format_defaults(ft, NULL, s, wl, wp);
317e271dbb8Schristos 	format_defaults_paste_buffer(ft, pb);
318e271dbb8Schristos 	format_add(ft, "line", "%u", line);
319e271dbb8Schristos 
320e271dbb8Schristos 	expanded = format_expand(ft, data->key_format);
321e271dbb8Schristos 	key = key_string_lookup_string(expanded);
322e271dbb8Schristos 	free(expanded);
323e271dbb8Schristos 	format_free(ft);
32446548964Swiz 	return (key);
325e271dbb8Schristos }
326e271dbb8Schristos 
327c9ad075bSchristos static struct screen *
32830744affSchristos window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
32930744affSchristos     struct args *args)
330c9ad075bSchristos {
3310a274e86Schristos 	struct window_pane		*wp = wme->wp;
332c9ad075bSchristos 	struct window_buffer_modedata	*data;
333c9ad075bSchristos 	struct screen			*s;
334c9ad075bSchristos 
3350a274e86Schristos 	wme->data = data = xcalloc(1, sizeof *data);
33630744affSchristos 	data->wp = wp;
33730744affSchristos 	cmd_find_copy_state(&data->fs, fs);
338c9ad075bSchristos 
339c9ad075bSchristos 	if (args == NULL || !args_has(args, 'F'))
340c9ad075bSchristos 		data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
341c9ad075bSchristos 	else
342c9ad075bSchristos 		data->format = xstrdup(args_get(args, 'F'));
343e271dbb8Schristos 	if (args == NULL || !args_has(args, 'K'))
344e271dbb8Schristos 		data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT);
345e271dbb8Schristos 	else
346e271dbb8Schristos 		data->key_format = xstrdup(args_get(args, 'K'));
34746548964Swiz 	if (args == NULL || args_count(args) == 0)
348c9ad075bSchristos 		data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
349c9ad075bSchristos 	else
35046548964Swiz 		data->command = xstrdup(args_string(args, 0));
351c9ad075bSchristos 
352c9ad075bSchristos 	data->data = mode_tree_start(wp, args, window_buffer_build,
353e271dbb8Schristos 	    window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
354e271dbb8Schristos 	    window_buffer_get_key, data, window_buffer_menu_items,
355e271dbb8Schristos 	    window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
356c7e17de0Schristos 	mode_tree_zoom(data->data, args);
357c9ad075bSchristos 
358c9ad075bSchristos 	mode_tree_build(data->data);
359c9ad075bSchristos 	mode_tree_draw(data->data);
360c9ad075bSchristos 
361c9ad075bSchristos 	return (s);
362c9ad075bSchristos }
363c9ad075bSchristos 
364c9ad075bSchristos static void
3650a274e86Schristos window_buffer_free(struct window_mode_entry *wme)
366c9ad075bSchristos {
3670a274e86Schristos 	struct window_buffer_modedata	*data = wme->data;
368c9ad075bSchristos 	u_int				 i;
369c9ad075bSchristos 
370c9ad075bSchristos 	if (data == NULL)
371c9ad075bSchristos 		return;
372c9ad075bSchristos 
373c9ad075bSchristos 	mode_tree_free(data->data);
374c9ad075bSchristos 
375c9ad075bSchristos 	for (i = 0; i < data->item_size; i++)
376c9ad075bSchristos 		window_buffer_free_item(data->item_list[i]);
377c9ad075bSchristos 	free(data->item_list);
378c9ad075bSchristos 
379c9ad075bSchristos 	free(data->format);
380e271dbb8Schristos 	free(data->key_format);
381c9ad075bSchristos 	free(data->command);
382c9ad075bSchristos 
383c9ad075bSchristos 	free(data);
384c9ad075bSchristos }
385c9ad075bSchristos 
386c9ad075bSchristos static void
3870a274e86Schristos window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
388c9ad075bSchristos {
3890a274e86Schristos 	struct window_buffer_modedata	*data = wme->data;
390c9ad075bSchristos 
391c9ad075bSchristos 	mode_tree_resize(data->data, sx, sy);
392c9ad075bSchristos }
393c9ad075bSchristos 
394c9ad075bSchristos static void
395e271dbb8Schristos window_buffer_update(struct window_mode_entry *wme)
396e271dbb8Schristos {
397e271dbb8Schristos 	struct window_buffer_modedata	*data = wme->data;
398e271dbb8Schristos 
399e271dbb8Schristos 	mode_tree_build(data->data);
400e271dbb8Schristos 	mode_tree_draw(data->data);
401e271dbb8Schristos 	data->wp->flags |= PANE_REDRAW;
402e271dbb8Schristos }
403e271dbb8Schristos 
404e271dbb8Schristos static void
405c7e17de0Schristos window_buffer_do_delete(void *modedata, void *itemdata,
406c7e17de0Schristos     __unused struct client *c, __unused key_code key)
407c9ad075bSchristos {
408c9ad075bSchristos 	struct window_buffer_modedata	*data = modedata;
409c9ad075bSchristos 	struct window_buffer_itemdata	*item = itemdata;
410c9ad075bSchristos 	struct paste_buffer		*pb;
411c9ad075bSchristos 
412*890b6d91Swiz 	if (item == mode_tree_get_current(data->data) &&
413*890b6d91Swiz 	    !mode_tree_down(data->data, 0)) {
414*890b6d91Swiz 		/*
415*890b6d91Swiz 		 *If we were unable to select the item further down we are at
416*890b6d91Swiz 		 * the end of the list. Move one element up instead, to make
417*890b6d91Swiz 		 * sure that we preserve a valid selection or we risk having
418*890b6d91Swiz 		 * the tree build logic reset it to the first item.
419*890b6d91Swiz 		 */
420*890b6d91Swiz 		mode_tree_up(data->data, 0);
421*890b6d91Swiz 	}
422*890b6d91Swiz 
423c9ad075bSchristos 	if ((pb = paste_get_name(item->name)) != NULL)
424c9ad075bSchristos 		paste_free(pb);
425c9ad075bSchristos }
426c9ad075bSchristos 
427c9ad075bSchristos static void
428c7e17de0Schristos window_buffer_do_paste(void *modedata, void *itemdata, struct client *c,
429c7e17de0Schristos     __unused key_code key)
430c7e17de0Schristos {
431c7e17de0Schristos 	struct window_buffer_modedata	*data = modedata;
432c7e17de0Schristos 	struct window_buffer_itemdata	*item = itemdata;
433c7e17de0Schristos 
434e271dbb8Schristos 	if (paste_get_name(item->name) != NULL)
435c7e17de0Schristos 		mode_tree_run_command(c, NULL, data->command, item->name);
436c7e17de0Schristos }
437c7e17de0Schristos 
438c7e17de0Schristos static void
439e271dbb8Schristos window_buffer_finish_edit(struct window_buffer_editdata *ed)
440e271dbb8Schristos {
441e271dbb8Schristos 	free(ed->name);
442e271dbb8Schristos 	free(ed);
443e271dbb8Schristos }
444e271dbb8Schristos 
445e271dbb8Schristos static void
446e271dbb8Schristos window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
447e271dbb8Schristos {
448e271dbb8Schristos 	struct window_buffer_editdata	*ed = arg;
449e271dbb8Schristos 	size_t				 oldlen;
450e271dbb8Schristos 	const char			*oldbuf;
451e271dbb8Schristos 	struct paste_buffer		*pb;
452e271dbb8Schristos 	struct window_pane		*wp;
453e271dbb8Schristos 	struct window_buffer_modedata	*data;
454e271dbb8Schristos 	struct window_mode_entry	*wme;
455e271dbb8Schristos 
456e271dbb8Schristos 	if (buf == NULL || len == 0) {
457e271dbb8Schristos 		window_buffer_finish_edit(ed);
458e271dbb8Schristos 		return;
459e271dbb8Schristos 	}
460e271dbb8Schristos 
461e271dbb8Schristos 	pb = paste_get_name(ed->name);
462e271dbb8Schristos 	if (pb == NULL || pb != ed->pb) {
463e271dbb8Schristos 		window_buffer_finish_edit(ed);
464e271dbb8Schristos 		return;
465e271dbb8Schristos 	}
466e271dbb8Schristos 
467e271dbb8Schristos 	oldbuf = paste_buffer_data(pb, &oldlen);
468e271dbb8Schristos 	if (oldlen != '\0' &&
469e271dbb8Schristos 	    oldbuf[oldlen - 1] != '\n' &&
470e271dbb8Schristos 	    buf[len - 1] == '\n')
471e271dbb8Schristos 		len--;
472e271dbb8Schristos 	if (len != 0)
473e271dbb8Schristos 		paste_replace(pb, buf, len);
474e271dbb8Schristos 
475e271dbb8Schristos 	wp = window_pane_find_by_id(ed->wp_id);
476e271dbb8Schristos 	if (wp != NULL) {
477e271dbb8Schristos 		wme = TAILQ_FIRST(&wp->modes);
478e271dbb8Schristos 		if (wme->mode == &window_buffer_mode) {
479e271dbb8Schristos 			data = wme->data;
480e271dbb8Schristos 			mode_tree_build(data->data);
481e271dbb8Schristos 			mode_tree_draw(data->data);
482e271dbb8Schristos 		}
483e271dbb8Schristos 		wp->flags |= PANE_REDRAW;
484e271dbb8Schristos 	}
485e271dbb8Schristos 	window_buffer_finish_edit(ed);
486e271dbb8Schristos }
487e271dbb8Schristos 
488e271dbb8Schristos static void
489e271dbb8Schristos window_buffer_start_edit(struct window_buffer_modedata *data,
490e271dbb8Schristos     struct window_buffer_itemdata *item, struct client *c)
491e271dbb8Schristos {
492e271dbb8Schristos 	struct paste_buffer		*pb;
493e271dbb8Schristos 	const char			*buf;
494e271dbb8Schristos 	size_t				 len;
495e271dbb8Schristos 	struct window_buffer_editdata	*ed;
496e271dbb8Schristos 
497e271dbb8Schristos 	if ((pb = paste_get_name(item->name)) == NULL)
498e271dbb8Schristos 		return;
499e271dbb8Schristos 	buf = paste_buffer_data(pb, &len);
500e271dbb8Schristos 
501e271dbb8Schristos 	ed = xcalloc(1, sizeof *ed);
502e271dbb8Schristos 	ed->wp_id = data->wp->id;
503e271dbb8Schristos 	ed->name = xstrdup(paste_buffer_name(pb));
504e271dbb8Schristos 	ed->pb = pb;
505e271dbb8Schristos 
506e271dbb8Schristos 	if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0)
507e271dbb8Schristos 		window_buffer_finish_edit(ed);
508e271dbb8Schristos }
509e271dbb8Schristos 
510e271dbb8Schristos static void
5110a274e86Schristos window_buffer_key(struct window_mode_entry *wme, struct client *c,
5120a274e86Schristos     __unused struct session *s, __unused struct winlink *wl, key_code key,
5130a274e86Schristos     struct mouse_event *m)
514c9ad075bSchristos {
5150a274e86Schristos 	struct window_pane		*wp = wme->wp;
5160a274e86Schristos 	struct window_buffer_modedata	*data = wme->data;
517c7e17de0Schristos 	struct mode_tree_data		*mtd = data->data;
518c9ad075bSchristos 	struct window_buffer_itemdata	*item;
519c9ad075bSchristos 	int				 finished;
520c9ad075bSchristos 
521*890b6d91Swiz 	if (paste_is_empty()) {
522f844e94eSwiz 		finished = 1;
523f844e94eSwiz 		goto out;
524f844e94eSwiz 	}
525f844e94eSwiz 
526c7e17de0Schristos 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
527c9ad075bSchristos 	switch (key) {
528e271dbb8Schristos 	case 'e':
529e271dbb8Schristos 		item = mode_tree_get_current(mtd);
530e271dbb8Schristos 		window_buffer_start_edit(data, item, c);
531e271dbb8Schristos 		break;
532c9ad075bSchristos 	case 'd':
533c7e17de0Schristos 		item = mode_tree_get_current(mtd);
534c7e17de0Schristos 		window_buffer_do_delete(data, item, c, key);
535c7e17de0Schristos 		mode_tree_build(mtd);
536c9ad075bSchristos 		break;
537c9ad075bSchristos 	case 'D':
538c7e17de0Schristos 		mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0);
539c7e17de0Schristos 		mode_tree_build(mtd);
540c9ad075bSchristos 		break;
541c7e17de0Schristos 	case 'P':
542c7e17de0Schristos 		mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0);
543c7e17de0Schristos 		finished = 1;
544c7e17de0Schristos 		break;
545c7e17de0Schristos 	case 'p':
546c9ad075bSchristos 	case '\r':
547c7e17de0Schristos 		item = mode_tree_get_current(mtd);
548c7e17de0Schristos 		window_buffer_do_paste(data, item, c, key);
549c7e17de0Schristos 		finished = 1;
550c7e17de0Schristos 		break;
551c9ad075bSchristos 	}
552f844e94eSwiz 
553f844e94eSwiz out:
554*890b6d91Swiz 	if (finished || paste_is_empty())
555c9ad075bSchristos 		window_pane_reset_mode(wp);
556c9ad075bSchristos 	else {
557c7e17de0Schristos 		mode_tree_draw(mtd);
558c9ad075bSchristos 		wp->flags |= PANE_REDRAW;
559c9ad075bSchristos 	}
560c9ad075bSchristos }
561