1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2019 Nicholas Marriott <[email protected]>
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 <stdlib.h>
22#include <string.h>
23
24#include "tmux.h"
25
26/*
27 * Display a menu on a client.
28 */
29
30static enum cmd_retval cmd_display_menu_exec(struct cmd *,
31 struct cmdq_item *);
32
33const struct cmd_entry cmd_display_menu_entry = {
34 .name = "display-menu",
35 .alias = "menu",
36
37 .args = { "c:t:T:x:y:", 1, -1 },
38 .usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
39 "[-x position] [-y position] name key command ...",
40
41 .target = { 't', CMD_FIND_PANE, 0 },
42
43 .flags = CMD_AFTERHOOK,
44 .exec = cmd_display_menu_exec
45};
46
47static enum cmd_retval
48cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
49{
50 struct args *args = self->args;
51 struct client *c;
52 struct session *s = item->target.s;
53 struct winlink *wl = item->target.wl;
54 struct window_pane *wp = item->target.wp;
55 struct cmd_find_state *fs = &item->target;
56 struct menu *menu = NULL;
57 struct style_range *sr;
58 struct menu_item menu_item;
59 const char *xp, *yp, *key;
60 char *title, *name;
61 int at, flags, i;
62 u_int px, py, ox, oy, sx, sy;
63
64 if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
65 return (CMD_RETURN_ERROR);
66 if (c->overlay_draw != NULL)
67 return (CMD_RETURN_NORMAL);
68 at = status_at_line(c);
69
70 if (args_has(args, 'T'))
71 title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp);
72 else
73 title = xstrdup("");
74
75 menu = menu_create(title);
76
77 for (i = 0; i != args->argc; /* nothing */) {
78 name = args->argv[i++];
79 if (*name == '\0') {
80 menu_add_item(menu, NULL, item, c, fs);
81 continue;
82 }
83
84 if (args->argc - i < 2) {
85 cmdq_error(item, "not enough arguments");
86 free(title);
87 menu_free(menu);
88 return (CMD_RETURN_ERROR);
89 }
90 key = args->argv[i++];
91
92 menu_item.name = name;
93 menu_item.key = key_string_lookup_string(key);
94 menu_item.command = args->argv[i++];
95
96 menu_add_item(menu, &menu_item, item, c, fs);
97 }
98 free(title);
99 if (menu == NULL) {
100 cmdq_error(item, "invalid menu arguments");
101 return (CMD_RETURN_ERROR);
102 }
103 if (menu->count == 0) {
104 menu_free(menu);
105 return (CMD_RETURN_NORMAL);
106 }
107
108 xp = args_get(args, 'x');
109 if (xp == NULL)
110 px = 0;
111 else if (strcmp(xp, "R") == 0)
112 px = c->tty.sx - 1;
113 else if (strcmp(xp, "P") == 0) {
114 tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
115 if (wp->xoff >= ox)
116 px = wp->xoff - ox;
117 else
118 px = 0;
119 } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) {
120 if (item->shared->mouse.x > (menu->width + 4) / 2)
121 px = item->shared->mouse.x - (menu->width + 4) / 2;
122 else
123 px = 0;
124 }
125 else if (strcmp(xp, "W") == 0) {
126 if (at == -1)
127 px = 0;
128 else {
129 TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) {
130 if (sr->type != STYLE_RANGE_WINDOW)
131 continue;
132 if (sr->argument == (u_int)wl->idx)
133 break;
134 }
135 if (sr != NULL)
136 px = sr->start;
137 else
138 px = 0;
139 }
140 } else
141 px = strtoul(xp, NULL, 10);
142 if (px + menu->width + 4 >= c->tty.sx)
143 px = c->tty.sx - menu->width - 4;
144
145 yp = args_get(args, 'y');
146 if (yp == NULL)
147 py = 0;
148 else if (strcmp(yp, "P") == 0) {
149 tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
150 if (wp->yoff + wp->sy >= oy)
151 py = wp->yoff + wp->sy - oy;
152 else
153 py = 0;
154 } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid)
155 py = item->shared->mouse.y + menu->count + 2;
156 else if (strcmp(yp, "S") == 0) {
157 if (at == -1)
158 py = c->tty.sy;
159 else if (at == 0)
160 py = status_line_size(c) + menu->count + 2;
161 else
162 py = at;
163 } else
164 py = strtoul(yp, NULL, 10);
165 if (py < menu->count + 2)
166 py = 0;
167 else
168 py -= menu->count + 2;
169 if (py + menu->count + 2 >= c->tty.sy)
170 py = c->tty.sy - menu->count - 2;
171
172 flags = 0;
173 if (!item->shared->mouse.valid)
174 flags |= MENU_NOMOUSE;
175 if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0)
176 return (CMD_RETURN_NORMAL);
177 return (CMD_RETURN_WAIT);
178}
179