blob: 8d4a9f21a0703efca0dfde195c0ccffdcf16ccd2 [file] [log] [blame]
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read a list of people who contributed.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * Implementation of popup windows. See ":help popup".
12 */
13
14#include "vim.h"
15
Bram Moolenaar79648732019-07-18 21:43:07 +020016#if defined(FEAT_TEXT_PROP) || defined(PROTO)
Bram Moolenaar4d784b22019-05-25 19:51:39 +020017
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020018typedef struct {
19 char *pp_name;
20 poppos_T pp_val;
21} poppos_entry_T;
22
23static poppos_entry_T poppos_entries[] = {
24 {"botleft", POPPOS_BOTLEFT},
25 {"topleft", POPPOS_TOPLEFT},
26 {"botright", POPPOS_BOTRIGHT},
27 {"topright", POPPOS_TOPRIGHT},
28 {"center", POPPOS_CENTER}
29};
30
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020031static void popup_adjust_position(win_T *wp);
32
Bram Moolenaar4d784b22019-05-25 19:51:39 +020033/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020034 * Get option value for "key", which is "line" or "col".
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020035 * Handles "cursor+N" and "cursor-N".
Bram Moolenaar1fb08312019-08-29 20:02:11 +020036 * Returns MAXCOL if the entry is not present.
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020037 */
38 static int
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020039popup_options_one(dict_T *dict, char_u *key)
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020040{
41 dictitem_T *di;
42 char_u *val;
43 char_u *s;
44 char_u *endp;
45 int n = 0;
46
47 di = dict_find(dict, key, -1);
48 if (di == NULL)
Bram Moolenaar1fb08312019-08-29 20:02:11 +020049 return MAXCOL;
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020050
51 val = tv_get_string(&di->di_tv);
52 if (STRNCMP(val, "cursor", 6) != 0)
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020053 return dict_get_number_check(dict, key);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020054
55 setcursor_mayforce(TRUE);
56 s = val + 6;
57 if (*s != NUL)
58 {
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020059 endp = s;
60 if (*skipwhite(s) == '+' || *skipwhite(s) == '-')
61 n = strtol((char *)s, (char **)&endp, 10);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020062 if (endp != NULL && *skipwhite(endp) != NUL)
63 {
64 semsg(_(e_invexpr2), val);
65 return 0;
66 }
67 }
68
69 if (STRCMP(key, "line") == 0)
70 n = screen_screenrow() + 1 + n;
71 else // "col"
72 n = screen_screencol() + 1 + n;
73
74 if (n < 1)
75 n = 1;
76 return n;
77}
78
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020079 static void
Bram Moolenaarae943152019-06-16 22:54:14 +020080set_padding_border(dict_T *dict, int *array, char *name, int max_val)
Bram Moolenaar2fd8e352019-06-01 20:16:48 +020081{
82 dictitem_T *di;
83
Bram Moolenaar2fd8e352019-06-01 20:16:48 +020084 di = dict_find(dict, (char_u *)name, -1);
85 if (di != NULL)
86 {
87 if (di->di_tv.v_type != VAR_LIST)
88 emsg(_(e_listreq));
89 else
90 {
91 list_T *list = di->di_tv.vval.v_list;
92 listitem_T *li;
93 int i;
94 int nr;
95
96 for (i = 0; i < 4; ++i)
97 array[i] = 1;
98 if (list != NULL)
99 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
100 ++i, li = li->li_next)
101 {
102 nr = (int)tv_get_number(&li->li_tv);
103 if (nr >= 0)
104 array[i] = nr > max_val ? max_val : nr;
105 }
106 }
107 }
108}
109
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200110/*
Bram Moolenaar17627312019-06-02 19:53:44 +0200111 * Used when popup options contain "moved": set default moved values.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200112 */
113 static void
Bram Moolenaar17627312019-06-02 19:53:44 +0200114set_moved_values(win_T *wp)
115{
116 wp->w_popup_curwin = curwin;
117 wp->w_popup_lnum = curwin->w_cursor.lnum;
118 wp->w_popup_mincol = curwin->w_cursor.col;
119 wp->w_popup_maxcol = curwin->w_cursor.col;
120}
121
122/*
123 * Used when popup options contain "moved" with "word" or "WORD".
124 */
125 static void
126set_moved_columns(win_T *wp, int flags)
127{
128 char_u *ptr;
129 int len = find_ident_under_cursor(&ptr, flags | FIND_NOERROR);
130
131 if (len > 0)
132 {
133 wp->w_popup_mincol = (int)(ptr - ml_get_curline());
134 wp->w_popup_maxcol = wp->w_popup_mincol + len - 1;
135 }
136}
137
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200138/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200139 * Used when popup options contain "mousemoved": set default moved values.
140 */
141 static void
142set_mousemoved_values(win_T *wp)
143{
144 wp->w_popup_mouse_row = mouse_row;
145 wp->w_popup_mouse_mincol = mouse_col;
146 wp->w_popup_mouse_maxcol = mouse_col;
147}
148
149/*
150 * Used when popup options contain "moved" with "word" or "WORD".
151 */
152 static void
153set_mousemoved_columns(win_T *wp, int flags)
154{
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200155 win_T *textwp;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200156 char_u *text;
157 int col;
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200158 pos_T pos;
159 colnr_T mcol;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200160
161 if (find_word_under_cursor(mouse_row, mouse_col, TRUE, flags,
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200162 &textwp, &pos.lnum, &text, NULL, &col) == OK)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200163 {
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200164 // convert text column to mouse column
165 pos.col = col;
166 pos.coladd = 0;
167 getvcol(textwp, &pos, &mcol, NULL, NULL);
168 wp->w_popup_mouse_mincol = mcol;
169
Bram Moolenaar10727682019-07-12 13:59:20 +0200170 pos.col = col + (colnr_T)STRLEN(text) - 1;
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200171 getvcol(textwp, &pos, NULL, NULL, &mcol);
172 wp->w_popup_mouse_maxcol = mcol;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200173 vim_free(text);
174 }
175}
176
177/*
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200178 * Return TRUE if "row"/"col" is on the border of the popup.
179 * The values are relative to the top-left corner.
180 */
181 int
182popup_on_border(win_T *wp, int row, int col)
183{
184 return (row == 0 && wp->w_popup_border[0] > 0)
185 || (row == popup_height(wp) - 1 && wp->w_popup_border[2] > 0)
186 || (col == 0 && wp->w_popup_border[3] > 0)
187 || (col == popup_width(wp) - 1 && wp->w_popup_border[1] > 0);
188}
189
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200190/*
Bram Moolenaarf6396232019-08-24 19:36:00 +0200191 * Return TRUE and close the popup if "row"/"col" is on the "X" button of the
192 * popup and w_popup_close is POPCLOSE_BUTTON.
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200193 * The values are relative to the top-left corner.
Bram Moolenaarf6396232019-08-24 19:36:00 +0200194 * Caller should check the left mouse button was clicked.
195 * Return TRUE if the popup was closed.
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200196 */
197 int
Bram Moolenaarf6396232019-08-24 19:36:00 +0200198popup_close_if_on_X(win_T *wp, int row, int col)
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200199{
Bram Moolenaarf6396232019-08-24 19:36:00 +0200200 if (wp->w_popup_close == POPCLOSE_BUTTON
201 && row == 0 && col == popup_width(wp) - 1)
202 {
203 popup_close_for_mouse_click(wp);
204 return TRUE;
205 }
206 return FALSE;
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200207}
208
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200209// Values set when dragging a popup window starts.
210static int drag_start_row;
211static int drag_start_col;
212static int drag_start_wantline;
213static int drag_start_wantcol;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200214static int drag_on_resize_handle;
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200215
216/*
217 * Mouse down on border of popup window: start dragging it.
218 * Uses mouse_col and mouse_row.
219 */
220 void
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200221popup_start_drag(win_T *wp, int row, int col)
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200222{
223 drag_start_row = mouse_row;
224 drag_start_col = mouse_col;
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200225 if (wp->w_wantline == 0)
226 drag_start_wantline = wp->w_winrow + 1;
227 else
228 drag_start_wantline = wp->w_wantline;
229 if (wp->w_wantcol == 0)
230 drag_start_wantcol = wp->w_wincol + 1;
231 else
232 drag_start_wantcol = wp->w_wantcol;
Bram Moolenaara42d9452019-06-15 21:46:30 +0200233
234 // Stop centering the popup
235 if (wp->w_popup_pos == POPPOS_CENTER)
236 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200237
238 drag_on_resize_handle = wp->w_popup_border[1] > 0
239 && wp->w_popup_border[2] > 0
240 && row == popup_height(wp) - 1
241 && col == popup_width(wp) - 1;
242
243 if (wp->w_popup_pos != POPPOS_TOPLEFT && drag_on_resize_handle)
244 {
245 if (wp->w_popup_pos == POPPOS_TOPRIGHT
246 || wp->w_popup_pos == POPPOS_BOTRIGHT)
247 wp->w_wantcol = wp->w_wincol + 1;
248 if (wp->w_popup_pos == POPPOS_BOTLEFT)
249 wp->w_wantline = wp->w_winrow + 1;
250 wp->w_popup_pos = POPPOS_TOPLEFT;
251 }
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200252}
253
254/*
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200255 * Mouse moved while dragging a popup window: adjust the window popup position
256 * or resize.
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200257 */
258 void
259popup_drag(win_T *wp)
260{
261 // The popup may be closed before dragging stops.
262 if (!win_valid_popup(wp))
263 return;
264
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200265 if ((wp->w_popup_flags & POPF_RESIZE) && drag_on_resize_handle)
266 {
267 int width_inc = mouse_col - drag_start_col;
268 int height_inc = mouse_row - drag_start_row;
269
270 if (width_inc != 0)
271 {
272 int width = wp->w_width + width_inc;
273
274 if (width < 1)
275 width = 1;
276 wp->w_minwidth = width;
277 wp->w_maxwidth = width;
278 drag_start_col = mouse_col;
279 }
280
281 if (height_inc != 0)
282 {
283 int height = wp->w_height + height_inc;
284
285 if (height < 1)
286 height = 1;
287 wp->w_minheight = height;
288 wp->w_maxheight = height;
289 drag_start_row = mouse_row;
290 }
291
292 popup_adjust_position(wp);
293 return;
294 }
295
296 if (!(wp->w_popup_flags & POPF_DRAG))
297 return;
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200298 wp->w_wantline = drag_start_wantline + (mouse_row - drag_start_row);
299 if (wp->w_wantline < 1)
300 wp->w_wantline = 1;
301 if (wp->w_wantline > Rows)
302 wp->w_wantline = Rows;
303 wp->w_wantcol = drag_start_wantcol + (mouse_col - drag_start_col);
304 if (wp->w_wantcol < 1)
305 wp->w_wantcol = 1;
306 if (wp->w_wantcol > Columns)
307 wp->w_wantcol = Columns;
308
309 popup_adjust_position(wp);
310}
Bram Moolenaar68d48f42019-06-12 22:42:41 +0200311
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200312/*
313 * Set w_firstline to match the current "wp->w_topline".
314 */
315 void
316popup_set_firstline(win_T *wp)
317{
318 int height = wp->w_height;
319
320 wp->w_firstline = wp->w_topline;
321 popup_adjust_position(wp);
322
323 // we don't want the popup to get smaller, decrement the first line
324 // until it doesn't
325 while (wp->w_firstline > 1 && wp->w_height < height)
326 {
327 --wp->w_firstline;
328 popup_adjust_position(wp);
329 }
330}
331
332/*
Bram Moolenaar13b11ed2019-08-01 15:52:45 +0200333 * Return TRUE if the position is in the popup window scrollbar.
334 */
335 int
336popup_is_in_scrollbar(win_T *wp, int row, int col)
337{
338 return wp->w_has_scrollbar
339 && row >= wp->w_popup_border[0]
340 && row < popup_height(wp) - wp->w_popup_border[2]
341 && col == popup_width(wp) - wp->w_popup_border[1] - 1;
342}
343
344
345/*
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200346 * Handle a click in a popup window, if it is in the scrollbar.
347 */
348 void
349popup_handle_scrollbar_click(win_T *wp, int row, int col)
350{
351 int height = popup_height(wp);
352 int old_topline = wp->w_topline;
353
Bram Moolenaar13b11ed2019-08-01 15:52:45 +0200354 if (popup_is_in_scrollbar(wp, row, col))
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200355 {
356 if (row >= height / 2)
357 {
358 // Click in lower half, scroll down.
359 if (wp->w_topline < wp->w_buffer->b_ml.ml_line_count)
360 ++wp->w_topline;
361 }
362 else if (wp->w_topline > 1)
363 // click on upper half, scroll up.
364 --wp->w_topline;
365 if (wp->w_topline != old_topline)
366 {
367 popup_set_firstline(wp);
368 redraw_win_later(wp, NOT_VALID);
369 }
370 }
371}
372
Bram Moolenaar68d48f42019-06-12 22:42:41 +0200373#if defined(FEAT_TIMERS)
374 static void
375popup_add_timeout(win_T *wp, int time)
376{
377 char_u cbbuf[50];
378 char_u *ptr = cbbuf;
379 typval_T tv;
380
381 vim_snprintf((char *)cbbuf, sizeof(cbbuf),
382 "{_ -> popup_close(%d)}", wp->w_id);
383 if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
384 {
385 wp->w_popup_timer = create_timer(time, 0);
386 wp->w_popup_timer->tr_callback = get_callback(&tv);
387 clear_tv(&tv);
388 }
389}
390#endif
391
Bram Moolenaar17627312019-06-02 19:53:44 +0200392/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200393 * Shared between popup_create() and f_popup_move().
Bram Moolenaar17627312019-06-02 19:53:44 +0200394 */
395 static void
Bram Moolenaarae943152019-06-16 22:54:14 +0200396apply_move_options(win_T *wp, dict_T *d)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200397{
Bram Moolenaar12034e22019-08-25 22:25:02 +0200398 int nr;
399 char_u *str;
400 dictitem_T *di;
Bram Moolenaarae943152019-06-16 22:54:14 +0200401
402 if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0)
403 wp->w_minwidth = nr;
404 if ((nr = dict_get_number(d, (char_u *)"minheight")) > 0)
405 wp->w_minheight = nr;
406 if ((nr = dict_get_number(d, (char_u *)"maxwidth")) > 0)
407 wp->w_maxwidth = nr;
408 if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0)
409 wp->w_maxheight = nr;
Bram Moolenaar12034e22019-08-25 22:25:02 +0200410
411 nr = popup_options_one(d, (char_u *)"line");
Bram Moolenaar1fb08312019-08-29 20:02:11 +0200412 if (nr != MAXCOL)
Bram Moolenaar12034e22019-08-25 22:25:02 +0200413 wp->w_wantline = nr;
414 nr = popup_options_one(d, (char_u *)"col");
Bram Moolenaar1fb08312019-08-29 20:02:11 +0200415 if (nr != MAXCOL)
Bram Moolenaar12034e22019-08-25 22:25:02 +0200416 wp->w_wantcol = nr;
417
418 di = dict_find(d, (char_u *)"fixed", -1);
419 if (di != NULL)
420 wp->w_popup_fixed = dict_get_number(d, (char_u *)"fixed") != 0;
421
422 str = dict_get_string(d, (char_u *)"pos", FALSE);
423 if (str != NULL)
424 {
425 for (nr = 0;
426 nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
427 ++nr)
428 if (STRCMP(str, poppos_entries[nr].pp_name) == 0)
429 {
430 wp->w_popup_pos = poppos_entries[nr].pp_val;
431 nr = -1;
432 break;
433 }
434 if (nr != -1)
435 semsg(_(e_invarg2), str);
436 }
437
438 str = dict_get_string(d, (char_u *)"textprop", FALSE);
439 if (str != NULL)
440 {
441 wp->w_popup_prop_type = 0;
442 if (*str != NUL)
443 {
444 nr = find_prop_type_id(str, wp->w_buffer);
445 if (nr <= 0)
446 nr = find_prop_type_id(str, NULL);
447 if (nr <= 0)
448 semsg(_(e_invarg2), str);
449 else
450 {
451 wp->w_popup_prop_type = nr;
452 wp->w_popup_prop_win = curwin;
453
454 di = dict_find(d, (char_u *)"textpropwin", -1);
455 if (di != NULL)
456 {
457 wp->w_popup_prop_win = find_win_by_nr_or_id(&di->di_tv);
458 if (win_valid(wp->w_popup_prop_win))
459 wp->w_popup_prop_win = curwin;
460 }
461 }
462 }
463 }
464
465 di = dict_find(d, (char_u *)"textpropid", -1);
466 if (di != NULL)
467 wp->w_popup_prop_id = dict_get_number(d, (char_u *)"textpropid");
Bram Moolenaarae943152019-06-16 22:54:14 +0200468}
469
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200470 static void
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200471handle_moved_argument(win_T *wp, dictitem_T *di, int mousemoved)
472{
473 if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
474 {
475 char_u *s = di->di_tv.vval.v_string;
476 int flags = 0;
477
478 if (STRCMP(s, "word") == 0)
479 flags = FIND_IDENT | FIND_STRING;
480 else if (STRCMP(s, "WORD") == 0)
481 flags = FIND_STRING;
482 else if (STRCMP(s, "expr") == 0)
483 flags = FIND_IDENT | FIND_STRING | FIND_EVAL;
484 else if (STRCMP(s, "any") != 0)
485 semsg(_(e_invarg2), s);
486 if (flags != 0)
487 {
488 if (mousemoved)
489 set_mousemoved_columns(wp, flags);
490 else
491 set_moved_columns(wp, flags);
492 }
493 }
494 else if (di->di_tv.v_type == VAR_LIST
495 && di->di_tv.vval.v_list != NULL
496 && di->di_tv.vval.v_list->lv_len == 2)
497 {
498 list_T *l = di->di_tv.vval.v_list;
499 int mincol = tv_get_number(&l->lv_first->li_tv);
500 int maxcol = tv_get_number(&l->lv_first->li_next->li_tv);
501
502 if (mousemoved)
503 {
504 wp->w_popup_mouse_mincol = mincol;
505 wp->w_popup_mouse_maxcol = maxcol;
506 }
507 else
508 {
509 wp->w_popup_mincol = mincol;
510 wp->w_popup_maxcol = maxcol;
511 }
512 }
513 else
514 semsg(_(e_invarg2), tv_get_string(&di->di_tv));
515}
516
517 static void
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200518check_highlight(dict_T *dict, char *name, char_u **pval)
519{
520 dictitem_T *di;
521 char_u *str;
522
523 di = dict_find(dict, (char_u *)name, -1);
524 if (di != NULL)
525 {
526 if (di->di_tv.v_type != VAR_STRING)
527 semsg(_(e_invargval), name);
528 else
529 {
530 str = tv_get_string(&di->di_tv);
531 if (*str != NUL)
532 *pval = vim_strsave(str);
533 }
534 }
535}
536
Bram Moolenaarae943152019-06-16 22:54:14 +0200537/*
Bram Moolenaar79648732019-07-18 21:43:07 +0200538 * Scroll to show the line with the cursor. This assumes lines don't wrap.
539 */
540 static void
541popup_show_curline(win_T *wp)
542{
543 if (wp->w_cursor.lnum < wp->w_topline)
544 wp->w_topline = wp->w_cursor.lnum;
545 else if (wp->w_cursor.lnum >= wp->w_botline)
546 wp->w_topline = wp->w_cursor.lnum - wp->w_height + 1;
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200547
548 // Don't use "firstline" now.
549 wp->w_firstline = 0;
550}
551
552/*
553 * Get the sign group name for window "wp".
554 * Returns a pointer to a static buffer, overwritten on the next call.
555 */
556 static char_u *
557popup_get_sign_name(win_T *wp)
558{
559 static char buf[30];
560
561 vim_snprintf(buf, sizeof(buf), "popup-%d", wp->w_id);
562 return (char_u *)buf;
Bram Moolenaar79648732019-07-18 21:43:07 +0200563}
564
565/*
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200566 * Highlight the line with the cursor.
567 * Also scrolls the text to put the cursor line in view.
568 */
569 static void
570popup_highlight_curline(win_T *wp)
571{
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200572 int sign_id = 0;
573 char_u *sign_name = popup_get_sign_name(wp);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200574
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200575 buf_delete_signs(wp->w_buffer, (char_u *)"popupmenu");
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200576
577 if ((wp->w_popup_flags & POPF_CURSORLINE) != 0)
578 {
Bram Moolenaar79648732019-07-18 21:43:07 +0200579 popup_show_curline(wp);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200580
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200581 if (!sign_exists_by_name(sign_name))
582 {
583 char *linehl = "PopupSelected";
584
585 if (syn_name2id((char_u *)linehl) == 0)
586 linehl = "PmenuSel";
Bram Moolenaar62a0cb42019-08-18 16:35:23 +0200587 sign_define_by_name(sign_name, NULL, (char_u *)linehl, NULL, NULL);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200588 }
589
590 sign_place(&sign_id, (char_u *)"popupmenu", sign_name,
591 wp->w_buffer, wp->w_cursor.lnum, SIGN_DEF_PRIO);
592 redraw_win_later(wp, NOT_VALID);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200593 }
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200594 else
595 sign_undefine_by_name(sign_name, FALSE);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200596}
597
598/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200599 * Shared between popup_create() and f_popup_setoptions().
600 */
601 static void
602apply_general_options(win_T *wp, dict_T *dict)
603{
604 dictitem_T *di;
Bram Moolenaar402502d2019-05-30 22:07:36 +0200605 int nr;
606 char_u *str;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200607
Bram Moolenaarae943152019-06-16 22:54:14 +0200608 // TODO: flip
609
610 di = dict_find(dict, (char_u *)"firstline", -1);
Bram Moolenaardfa97f22019-06-15 14:31:55 +0200611 if (di != NULL)
Bram Moolenaarae943152019-06-16 22:54:14 +0200612 wp->w_firstline = dict_get_number(dict, (char_u *)"firstline");
Bram Moolenaar9e67b6a2019-08-30 17:34:08 +0200613 if (wp->w_firstline < 0)
614 wp->w_firstline = 0;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200615
Bram Moolenaar75fb0852019-06-25 05:15:58 +0200616 di = dict_find(dict, (char_u *)"scrollbar", -1);
617 if (di != NULL)
618 wp->w_want_scrollbar = dict_get_number(dict, (char_u *)"scrollbar");
619
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200620 str = dict_get_string(dict, (char_u *)"title", FALSE);
621 if (str != NULL)
622 {
623 vim_free(wp->w_popup_title);
624 wp->w_popup_title = vim_strsave(str);
625 }
626
Bram Moolenaar402502d2019-05-30 22:07:36 +0200627 di = dict_find(dict, (char_u *)"wrap", -1);
628 if (di != NULL)
629 {
630 nr = dict_get_number(dict, (char_u *)"wrap");
631 wp->w_p_wrap = nr != 0;
632 }
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200633
Bram Moolenaara42d9452019-06-15 21:46:30 +0200634 di = dict_find(dict, (char_u *)"drag", -1);
635 if (di != NULL)
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200636 {
637 nr = dict_get_number(dict, (char_u *)"drag");
638 if (nr)
639 wp->w_popup_flags |= POPF_DRAG;
640 else
641 wp->w_popup_flags &= ~POPF_DRAG;
642 }
643
644 di = dict_find(dict, (char_u *)"resize", -1);
645 if (di != NULL)
646 {
647 nr = dict_get_number(dict, (char_u *)"resize");
648 if (nr)
649 wp->w_popup_flags |= POPF_RESIZE;
650 else
651 wp->w_popup_flags &= ~POPF_RESIZE;
652 }
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200653
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200654 di = dict_find(dict, (char_u *)"close", -1);
655 if (di != NULL)
656 {
657 int ok = TRUE;
658
659 if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
660 {
661 char_u *s = di->di_tv.vval.v_string;
662
663 if (STRCMP(s, "none") == 0)
664 wp->w_popup_close = POPCLOSE_NONE;
665 else if (STRCMP(s, "button") == 0)
666 wp->w_popup_close = POPCLOSE_BUTTON;
667 else if (STRCMP(s, "click") == 0)
668 wp->w_popup_close = POPCLOSE_CLICK;
669 else
670 ok = FALSE;
671 }
672 else
673 ok = FALSE;
674 if (!ok)
675 semsg(_(e_invargNval), "close", tv_get_string(&di->di_tv));
676 }
677
Bram Moolenaarae943152019-06-16 22:54:14 +0200678 str = dict_get_string(dict, (char_u *)"highlight", FALSE);
679 if (str != NULL)
680 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
681 str, OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200682
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200683 set_string_option_direct_in_win(wp, (char_u *)"signcolumn", -1,
684 (char_u *)"no", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaarae943152019-06-16 22:54:14 +0200685 set_padding_border(dict, wp->w_popup_padding, "padding", 999);
686 set_padding_border(dict, wp->w_popup_border, "border", 1);
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200687
Bram Moolenaar790498b2019-06-01 22:15:29 +0200688 di = dict_find(dict, (char_u *)"borderhighlight", -1);
689 if (di != NULL)
690 {
Bram Moolenaar403d0902019-07-17 21:37:32 +0200691 if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL)
Bram Moolenaar790498b2019-06-01 22:15:29 +0200692 emsg(_(e_listreq));
693 else
694 {
695 list_T *list = di->di_tv.vval.v_list;
696 listitem_T *li;
Bram Moolenaarae943152019-06-16 22:54:14 +0200697 int i;
Bram Moolenaar790498b2019-06-01 22:15:29 +0200698
Bram Moolenaar403d0902019-07-17 21:37:32 +0200699 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
700 ++i, li = li->li_next)
701 {
702 str = tv_get_string(&li->li_tv);
703 if (*str != NUL)
704 wp->w_border_highlight[i] = vim_strsave(str);
705 }
Bram Moolenaar790498b2019-06-01 22:15:29 +0200706 if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
707 for (i = 1; i < 4; ++i)
Bram Moolenaar403d0902019-07-17 21:37:32 +0200708 wp->w_border_highlight[i] =
Bram Moolenaar790498b2019-06-01 22:15:29 +0200709 vim_strsave(wp->w_border_highlight[0]);
710 }
711 }
712
Bram Moolenaar790498b2019-06-01 22:15:29 +0200713 di = dict_find(dict, (char_u *)"borderchars", -1);
714 if (di != NULL)
715 {
716 if (di->di_tv.v_type != VAR_LIST)
717 emsg(_(e_listreq));
718 else
719 {
720 list_T *list = di->di_tv.vval.v_list;
721 listitem_T *li;
Bram Moolenaarae943152019-06-16 22:54:14 +0200722 int i;
Bram Moolenaar790498b2019-06-01 22:15:29 +0200723
724 if (list != NULL)
725 for (i = 0, li = list->lv_first; i < 8 && i < list->lv_len;
726 ++i, li = li->li_next)
727 {
728 str = tv_get_string(&li->li_tv);
729 if (*str != NUL)
730 wp->w_border_char[i] = mb_ptr2char(str);
731 }
732 if (list->lv_len == 1)
733 for (i = 1; i < 8; ++i)
734 wp->w_border_char[i] = wp->w_border_char[0];
735 if (list->lv_len == 2)
736 {
737 for (i = 4; i < 8; ++i)
738 wp->w_border_char[i] = wp->w_border_char[1];
739 for (i = 1; i < 4; ++i)
740 wp->w_border_char[i] = wp->w_border_char[0];
741 }
742 }
743 }
Bram Moolenaar3397f742019-06-02 18:40:06 +0200744
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200745 check_highlight(dict, "scrollbarhighlight", &wp->w_scrollbar_highlight);
746 check_highlight(dict, "thumbhighlight", &wp->w_thumb_highlight);
747
Bram Moolenaarae943152019-06-16 22:54:14 +0200748 di = dict_find(dict, (char_u *)"zindex", -1);
749 if (di != NULL)
750 {
751 wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
752 if (wp->w_zindex < 1)
753 wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
754 if (wp->w_zindex > 32000)
755 wp->w_zindex = 32000;
756 }
757
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200758 di = dict_find(dict, (char_u *)"mask", -1);
759 if (di != NULL)
760 {
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200761 int ok = FALSE;
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200762
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200763 if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL)
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200764 {
765 listitem_T *li;
766
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200767 ok = TRUE;
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200768 for (li = di->di_tv.vval.v_list->lv_first; li != NULL;
769 li = li->li_next)
770 {
771 if (li->li_tv.v_type != VAR_LIST
772 || li->li_tv.vval.v_list == NULL
773 || li->li_tv.vval.v_list->lv_len != 4)
774 {
775 ok = FALSE;
776 break;
777 }
778 }
779 }
780 if (ok)
781 {
782 wp->w_popup_mask = di->di_tv.vval.v_list;
783 ++wp->w_popup_mask->lv_refcount;
Bram Moolenaare865dcb2019-07-26 22:15:50 +0200784 VIM_CLEAR(wp->w_popup_mask_cells);
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200785 }
786 else
787 semsg(_(e_invargval), "mask");
788 }
789
Bram Moolenaarae943152019-06-16 22:54:14 +0200790#if defined(FEAT_TIMERS)
791 // Add timer to close the popup after some time.
792 nr = dict_get_number(dict, (char_u *)"time");
793 if (nr > 0)
794 popup_add_timeout(wp, nr);
795#endif
796
Bram Moolenaar3397f742019-06-02 18:40:06 +0200797 di = dict_find(dict, (char_u *)"moved", -1);
798 if (di != NULL)
799 {
Bram Moolenaar17627312019-06-02 19:53:44 +0200800 set_moved_values(wp);
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200801 handle_moved_argument(wp, di, FALSE);
802 }
Bram Moolenaar3397f742019-06-02 18:40:06 +0200803
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200804 di = dict_find(dict, (char_u *)"mousemoved", -1);
805 if (di != NULL)
806 {
807 set_mousemoved_values(wp);
808 handle_moved_argument(wp, di, TRUE);
Bram Moolenaar3397f742019-06-02 18:40:06 +0200809 }
Bram Moolenaar33796b32019-06-08 16:01:13 +0200810
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200811 di = dict_find(dict, (char_u *)"cursorline", -1);
812 if (di != NULL)
813 {
814 if (di->di_tv.v_type == VAR_NUMBER)
815 {
816 if (di->di_tv.vval.v_number != 0)
817 wp->w_popup_flags |= POPF_CURSORLINE;
818 else
819 wp->w_popup_flags &= ~POPF_CURSORLINE;
820 }
821 else
822 semsg(_(e_invargval), "cursorline");
823 }
824
Bram Moolenaarae943152019-06-16 22:54:14 +0200825 di = dict_find(dict, (char_u *)"filter", -1);
826 if (di != NULL)
827 {
828 callback_T callback = get_callback(&di->di_tv);
829
830 if (callback.cb_name != NULL)
831 {
832 free_callback(&wp->w_filter_cb);
833 set_callback(&wp->w_filter_cb, &callback);
834 }
835 }
Bram Moolenaar749fa0a2019-08-03 16:18:07 +0200836 di = dict_find(dict, (char_u *)"mapping", -1);
837 if (di != NULL)
838 {
839 nr = dict_get_number(dict, (char_u *)"mapping");
840 if (nr)
841 wp->w_popup_flags |= POPF_MAPPING;
842 else
843 wp->w_popup_flags &= ~POPF_MAPPING;
844 }
Bram Moolenaarae943152019-06-16 22:54:14 +0200845
846 di = dict_find(dict, (char_u *)"callback", -1);
847 if (di != NULL)
848 {
849 callback_T callback = get_callback(&di->di_tv);
850
851 if (callback.cb_name != NULL)
852 {
853 free_callback(&wp->w_close_cb);
854 set_callback(&wp->w_close_cb, &callback);
855 }
856 }
857}
858
859/*
860 * Go through the options in "dict" and apply them to popup window "wp".
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200861 * Only used when creating a new popup window.
Bram Moolenaarae943152019-06-16 22:54:14 +0200862 */
863 static void
864apply_options(win_T *wp, dict_T *dict)
865{
866 int nr;
867
868 apply_move_options(wp, dict);
869 apply_general_options(wp, dict);
870
Bram Moolenaar6313c4f2019-06-16 20:39:13 +0200871 nr = dict_get_number(dict, (char_u *)"hidden");
872 if (nr > 0)
873 {
874 wp->w_popup_flags |= POPF_HIDDEN;
875 --wp->w_buffer->b_nwindows;
876 }
877
Bram Moolenaar33796b32019-06-08 16:01:13 +0200878 popup_mask_refresh = TRUE;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200879 popup_highlight_curline(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200880}
881
882/*
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200883 * Add lines to the popup from a list of strings.
884 */
885 static void
886add_popup_strings(buf_T *buf, list_T *l)
887{
888 listitem_T *li;
889 linenr_T lnum = 0;
890 char_u *p;
891
892 for (li = l->lv_first; li != NULL; li = li->li_next)
893 if (li->li_tv.v_type == VAR_STRING)
894 {
895 p = li->li_tv.vval.v_string;
896 ml_append_buf(buf, lnum++,
897 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
898 }
899}
900
901/*
902 * Add lines to the popup from a list of dictionaries.
903 */
904 static void
905add_popup_dicts(buf_T *buf, list_T *l)
906{
907 listitem_T *li;
908 listitem_T *pli;
909 linenr_T lnum = 0;
910 char_u *p;
911 dict_T *dict;
912
913 // first add the text lines
914 for (li = l->lv_first; li != NULL; li = li->li_next)
915 {
916 if (li->li_tv.v_type != VAR_DICT)
917 {
918 emsg(_(e_dictreq));
919 return;
920 }
921 dict = li->li_tv.vval.v_dict;
922 p = dict == NULL ? NULL
923 : dict_get_string(dict, (char_u *)"text", FALSE);
924 ml_append_buf(buf, lnum++,
925 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
926 }
927
928 // add the text properties
929 lnum = 1;
930 for (li = l->lv_first; li != NULL; li = li->li_next, ++lnum)
931 {
932 dictitem_T *di;
933 list_T *plist;
934
935 dict = li->li_tv.vval.v_dict;
936 di = dict_find(dict, (char_u *)"props", -1);
937 if (di != NULL)
938 {
939 if (di->di_tv.v_type != VAR_LIST)
940 {
941 emsg(_(e_listreq));
942 return;
943 }
944 plist = di->di_tv.vval.v_list;
945 if (plist != NULL)
946 {
947 for (pli = plist->lv_first; pli != NULL; pli = pli->li_next)
948 {
949 if (pli->li_tv.v_type != VAR_DICT)
950 {
951 emsg(_(e_dictreq));
952 return;
953 }
954 dict = pli->li_tv.vval.v_dict;
955 if (dict != NULL)
956 {
957 int col = dict_get_number(dict, (char_u *)"col");
958
959 prop_add_common( lnum, col, dict, buf, NULL);
960 }
961 }
962 }
963 }
964 }
965}
966
967/*
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200968 * Get the padding plus border at the top, adjusted to 1 if there is a title.
969 */
Bram Moolenaarbd483b32019-08-21 15:13:41 +0200970 int
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200971popup_top_extra(win_T *wp)
972{
973 int extra = wp->w_popup_border[0] + wp->w_popup_padding[0];
974
975 if (extra == 0 && wp->w_popup_title != NULL && *wp->w_popup_title != NUL)
976 return 1;
977 return extra;
978}
979
980/*
Bram Moolenaard529ba52019-07-02 23:13:53 +0200981 * Return the height of popup window "wp", including border and padding.
982 */
983 int
984popup_height(win_T *wp)
985{
986 return wp->w_height
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200987 + popup_top_extra(wp)
988 + wp->w_popup_padding[2] + wp->w_popup_border[2];
Bram Moolenaard529ba52019-07-02 23:13:53 +0200989}
990
991/*
992 * Return the width of popup window "wp", including border, padding and
993 * scrollbar.
994 */
995 int
996popup_width(win_T *wp)
997{
Bram Moolenaar017c2692019-07-13 14:17:51 +0200998 // w_leftcol is how many columns of the core are left of the screen
999 // w_popup_rightoff is how many columns of the core are right of the screen
Bram Moolenaard529ba52019-07-02 23:13:53 +02001000 return wp->w_width + wp->w_leftcol
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001001 + popup_extra_width(wp)
1002 + wp->w_popup_rightoff;
1003}
1004
1005/*
1006 * Return the extra width of popup window "wp": border, padding and scrollbar.
1007 */
1008 int
1009popup_extra_width(win_T *wp)
1010{
1011 return wp->w_popup_padding[3] + wp->w_popup_border[3]
1012 + wp->w_popup_padding[1] + wp->w_popup_border[1]
1013 + wp->w_has_scrollbar;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001014}
1015
1016/*
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001017 * Adjust the position and size of the popup to fit on the screen.
1018 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001019 static void
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001020popup_adjust_position(win_T *wp)
1021{
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001022 linenr_T lnum;
1023 int wrapped = 0;
1024 int maxwidth;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001025 int maxspace;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001026 int center_vert = FALSE;
1027 int center_hor = FALSE;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001028 int allow_adjust_left = !wp->w_popup_fixed;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001029 int top_extra = popup_top_extra(wp);
Bram Moolenaar399d8982019-06-02 15:34:29 +02001030 int right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1];
1031 int bot_extra = wp->w_popup_border[2] + wp->w_popup_padding[2];
1032 int left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
1033 int extra_height = top_extra + bot_extra;
1034 int extra_width = left_extra + right_extra;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001035 int org_winrow = wp->w_winrow;
1036 int org_wincol = wp->w_wincol;
1037 int org_width = wp->w_width;
1038 int org_height = wp->w_height;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001039 int org_leftcol = wp->w_leftcol;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001040 int org_leftoff = wp->w_popup_leftoff;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001041 int minwidth;
Bram Moolenaar12034e22019-08-25 22:25:02 +02001042 int wantline = wp->w_wantline; // adjusted for textprop
1043 int wantcol = wp->w_wantcol; // adjusted for textprop
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001044
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001045 wp->w_winrow = 0;
1046 wp->w_wincol = 0;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001047 wp->w_leftcol = 0;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001048 wp->w_popup_leftoff = 0;
1049 wp->w_popup_rightoff = 0;
Bram Moolenaar12034e22019-08-25 22:25:02 +02001050
1051 // If no line was specified default to vertical centering.
1052 if (wantline == 0)
1053 center_vert = TRUE;
1054
1055 if (wp->w_popup_prop_type > 0 && win_valid(wp->w_popup_prop_win))
1056 {
1057 win_T *prop_win = wp->w_popup_prop_win;
1058 textprop_T prop;
1059 linenr_T prop_lnum;
1060 pos_T pos;
1061 int screen_row;
1062 int screen_scol;
1063 int screen_ccol;
1064 int screen_ecol;
1065
1066 // Popup window is positioned relative to a text property.
1067 if (find_visible_prop(prop_win,
1068 wp->w_popup_prop_type, wp->w_popup_prop_id,
1069 &prop, &prop_lnum) == FAIL)
1070 {
1071 // Text property is no longer visible, hide the popup.
1072 // Unhiding the popup is done in check_popup_unhidden().
1073 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
1074 {
1075 wp->w_popup_flags |= POPF_HIDDEN;
1076 --wp->w_buffer->b_nwindows;
1077 if (win_valid(wp->w_popup_prop_win))
1078 redraw_win_later(wp->w_popup_prop_win, SOME_VALID);
1079 }
1080 return;
1081 }
1082
1083 // Compute the desired position from the position of the text
1084 // property. Use "wantline" and "wantcol" as offsets.
1085 pos.lnum = prop_lnum;
1086 pos.col = prop.tp_col;
1087 if (wp->w_popup_pos == POPPOS_TOPLEFT
1088 || wp->w_popup_pos == POPPOS_BOTLEFT)
1089 pos.col += prop.tp_len - 1;
1090 textpos2screenpos(prop_win, &pos, &screen_row,
1091 &screen_scol, &screen_ccol, &screen_ecol);
1092
1093 if (wp->w_popup_pos == POPPOS_TOPLEFT
1094 || wp->w_popup_pos == POPPOS_TOPRIGHT)
1095 // below the text
1096 wantline = screen_row + wantline + 1;
1097 else
1098 // above the text
1099 wantline = screen_row + wantline - 1;
1100 center_vert = FALSE;
1101 if (wp->w_popup_pos == POPPOS_TOPLEFT
1102 || wp->w_popup_pos == POPPOS_BOTLEFT)
1103 // right of the text
1104 wantcol = screen_ecol + wantcol;
1105 else
1106 // left of the text
Bram Moolenaarbc2d4c12019-08-28 22:18:30 +02001107 wantcol = screen_scol + wantcol - 2;
Bram Moolenaar12034e22019-08-25 22:25:02 +02001108 }
1109
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001110 if (wp->w_popup_pos == POPPOS_CENTER)
1111 {
1112 // center after computing the size
1113 center_vert = TRUE;
1114 center_hor = TRUE;
1115 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001116 else
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001117 {
Bram Moolenaar1fb08312019-08-29 20:02:11 +02001118 if (wantline > 0 && (wp->w_popup_pos == POPPOS_TOPLEFT
Bram Moolenaar12034e22019-08-25 22:25:02 +02001119 || wp->w_popup_pos == POPPOS_TOPRIGHT))
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001120 {
Bram Moolenaar12034e22019-08-25 22:25:02 +02001121 wp->w_winrow = wantline - 1;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001122 if (wp->w_winrow >= Rows)
1123 wp->w_winrow = Rows - 1;
1124 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001125
Bram Moolenaar12034e22019-08-25 22:25:02 +02001126 if (wantcol == 0)
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001127 center_hor = TRUE;
Bram Moolenaar1fb08312019-08-29 20:02:11 +02001128 else if (wantcol > 0 && (wp->w_popup_pos == POPPOS_TOPLEFT
1129 || wp->w_popup_pos == POPPOS_BOTLEFT))
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001130 {
Bram Moolenaar12034e22019-08-25 22:25:02 +02001131 wp->w_wincol = wantcol - 1;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001132 if (wp->w_wincol >= Columns - 3)
1133 wp->w_wincol = Columns - 3;
1134 }
1135 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001136
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001137 // When centering or right aligned, use maximum width.
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001138 // When left aligned use the space available, but shift to the left when we
1139 // hit the right of the screen.
Bram Moolenaard529ba52019-07-02 23:13:53 +02001140 maxspace = Columns - wp->w_wincol - left_extra;
1141 maxwidth = maxspace;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001142 if (wp->w_maxwidth > 0 && maxwidth > wp->w_maxwidth)
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001143 {
1144 allow_adjust_left = FALSE;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001145 maxwidth = wp->w_maxwidth;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001146 }
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001147
Bram Moolenaar8d241042019-06-12 23:40:01 +02001148 // start at the desired first line
Bram Moolenaar79648732019-07-18 21:43:07 +02001149 if (wp->w_firstline != 0)
1150 wp->w_topline = wp->w_firstline;
Bram Moolenaar8d241042019-06-12 23:40:01 +02001151 if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count)
1152 wp->w_topline = wp->w_buffer->b_ml.ml_line_count;
1153
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001154 // Compute width based on longest text line and the 'wrap' option.
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001155 // Use a minimum width of one, so that something shows when there is no
1156 // text.
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001157 // TODO: more accurate wrapping
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001158 wp->w_width = 1;
Bram Moolenaar8d241042019-06-12 23:40:01 +02001159 for (lnum = wp->w_topline; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001160 {
Bram Moolenaar331bafd2019-07-20 17:46:05 +02001161 int len;
1162 int w_width = wp->w_width;
1163
1164 // Count Tabs for what they are worth and compute the length based on
1165 // the maximum width (matters when 'showbreak' is set).
1166 if (wp->w_width < maxwidth)
1167 wp->w_width = maxwidth;
1168 len = win_linetabsize(wp, ml_get_buf(wp->w_buffer, lnum, FALSE),
Bram Moolenaare089c3f2019-07-09 20:25:25 +02001169 (colnr_T)MAXCOL);
Bram Moolenaar331bafd2019-07-20 17:46:05 +02001170 wp->w_width = w_width;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001171
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001172 if (wp->w_p_wrap)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001173 {
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001174 while (len > maxwidth)
1175 {
1176 ++wrapped;
1177 len -= maxwidth;
1178 wp->w_width = maxwidth;
1179 }
1180 }
1181 else if (len > maxwidth
1182 && allow_adjust_left
1183 && (wp->w_popup_pos == POPPOS_TOPLEFT
1184 || wp->w_popup_pos == POPPOS_BOTLEFT))
1185 {
1186 // adjust leftwise to fit text on screen
Bram Moolenaar51c31312019-06-15 22:27:23 +02001187 int shift_by = len - maxwidth;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001188
Bram Moolenaar51c31312019-06-15 22:27:23 +02001189 if (shift_by > wp->w_wincol)
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001190 {
1191 int truncate_shift = shift_by - wp->w_wincol;
Bram Moolenaar51c31312019-06-15 22:27:23 +02001192
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001193 len -= truncate_shift;
1194 shift_by -= truncate_shift;
1195 }
1196
1197 wp->w_wincol -= shift_by;
1198 maxwidth += shift_by;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001199 wp->w_width = maxwidth;
1200 }
1201 if (wp->w_width < len)
Bram Moolenaar017c2692019-07-13 14:17:51 +02001202 {
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001203 wp->w_width = len;
Bram Moolenaar017c2692019-07-13 14:17:51 +02001204 if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
1205 wp->w_width = wp->w_maxwidth;
1206 }
Bram Moolenaar8d241042019-06-12 23:40:01 +02001207 // do not use the width of lines we're not going to show
Bram Moolenaare296e312019-07-03 23:20:18 +02001208 if (wp->w_maxheight > 0
1209 && lnum - wp->w_topline + 1 + wrapped > wp->w_maxheight)
Bram Moolenaar8d241042019-06-12 23:40:01 +02001210 break;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001211 }
1212
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001213 wp->w_has_scrollbar = wp->w_want_scrollbar
1214 && (wp->w_topline > 1 || lnum <= wp->w_buffer->b_ml.ml_line_count);
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001215 if (wp->w_has_scrollbar)
Bram Moolenaarfe6e7612019-08-21 20:57:20 +02001216 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001217 ++right_extra;
Bram Moolenaarfe6e7612019-08-21 20:57:20 +02001218 ++extra_width;
1219 }
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001220
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001221 minwidth = wp->w_minwidth;
1222 if (wp->w_popup_title != NULL && *wp->w_popup_title != NUL)
1223 {
1224 int title_len = vim_strsize(wp->w_popup_title) + 2 - extra_width;
1225
1226 if (minwidth < title_len)
1227 minwidth = title_len;
1228 }
1229
1230 if (minwidth > 0 && wp->w_width < minwidth)
1231 wp->w_width = minwidth;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001232 if (wp->w_width > maxwidth)
Bram Moolenaard529ba52019-07-02 23:13:53 +02001233 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001234 if (wp->w_width > maxspace && !wp->w_p_wrap)
Bram Moolenaard529ba52019-07-02 23:13:53 +02001235 // some columns cut off on the right
1236 wp->w_popup_rightoff = wp->w_width - maxspace;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001237 wp->w_width = maxwidth;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001238 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001239 if (center_hor)
Bram Moolenaara730e552019-06-16 19:05:31 +02001240 {
1241 wp->w_wincol = (Columns - wp->w_width - extra_width) / 2;
1242 if (wp->w_wincol < 0)
1243 wp->w_wincol = 0;
1244 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001245 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
1246 || wp->w_popup_pos == POPPOS_TOPRIGHT)
1247 {
Bram Moolenaar12034e22019-08-25 22:25:02 +02001248 int leftoff = wantcol - (wp->w_width + extra_width);
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001249
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001250 // Right aligned: move to the right if needed.
1251 // No truncation, because that would change the height.
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001252 if (leftoff >= 0)
1253 wp->w_wincol = leftoff;
1254 else if (wp->w_popup_fixed)
1255 {
1256 // "col" specifies the right edge, but popup doesn't fit, skip some
Bram Moolenaard529ba52019-07-02 23:13:53 +02001257 // columns when displaying the window, minus left border and
1258 // padding.
1259 if (-leftoff > left_extra)
1260 wp->w_leftcol = -leftoff - left_extra;
1261 wp->w_width -= wp->w_leftcol;
1262 wp->w_popup_leftoff = -leftoff;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001263 if (wp->w_width < 0)
1264 wp->w_width = 0;
1265 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001266 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001267
Bram Moolenaar8edf0e32019-07-30 21:19:26 +02001268 if (wp->w_p_wrap || (!wp->w_popup_fixed
1269 && (wp->w_popup_pos == POPPOS_TOPLEFT
1270 || wp->w_popup_pos == POPPOS_BOTLEFT)))
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001271 {
1272 int want_col = 0;
1273
Bram Moolenaar8c8b88d2019-07-30 20:32:41 +02001274 // try to show the right border and any scrollbar
1275 want_col = left_extra + wp->w_width + right_extra;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001276 if (want_col > 0 && wp->w_wincol > 0
1277 && wp->w_wincol + want_col >= Columns)
1278 {
1279 wp->w_wincol = Columns - want_col;
1280 if (wp->w_wincol < 0)
1281 wp->w_wincol = 0;
1282 }
1283 }
1284
Bram Moolenaar8d241042019-06-12 23:40:01 +02001285 wp->w_height = wp->w_buffer->b_ml.ml_line_count - wp->w_topline
1286 + 1 + wrapped;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001287 if (wp->w_minheight > 0 && wp->w_height < wp->w_minheight)
1288 wp->w_height = wp->w_minheight;
1289 if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
1290 wp->w_height = wp->w_maxheight;
1291 if (wp->w_height > Rows - wp->w_winrow)
1292 wp->w_height = Rows - wp->w_winrow;
Bram Moolenaar17146962019-05-30 00:12:11 +02001293
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001294 if (center_vert)
Bram Moolenaara730e552019-06-16 19:05:31 +02001295 {
1296 wp->w_winrow = (Rows - wp->w_height - extra_height) / 2;
1297 if (wp->w_winrow < 0)
1298 wp->w_winrow = 0;
1299 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001300 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
1301 || wp->w_popup_pos == POPPOS_BOTLEFT)
1302 {
Bram Moolenaar12034e22019-08-25 22:25:02 +02001303 if ((wp->w_height + extra_height) <= wantline)
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001304 // bottom aligned: may move down
Bram Moolenaar12034e22019-08-25 22:25:02 +02001305 wp->w_winrow = wantline - (wp->w_height + extra_height);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001306 else
1307 // not enough space, make top aligned
Bram Moolenaar12034e22019-08-25 22:25:02 +02001308 wp->w_winrow = wantline + 1;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001309 }
Bram Moolenaar12034e22019-08-25 22:25:02 +02001310 if (wp->w_winrow >= Rows)
1311 wp->w_winrow = Rows - 1;
1312 else if (wp->w_winrow < 0)
1313 wp->w_winrow = 0;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001314
Bram Moolenaar17146962019-05-30 00:12:11 +02001315 wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
Bram Moolenaar12034e22019-08-25 22:25:02 +02001316 if (win_valid(wp->w_popup_prop_win))
1317 {
1318 wp->w_popup_prop_changedtick =
1319 CHANGEDTICK(wp->w_popup_prop_win->w_buffer);
1320 wp->w_popup_prop_topline = wp->w_popup_prop_win->w_topline;
1321 }
Bram Moolenaar33796b32019-06-08 16:01:13 +02001322
1323 // Need to update popup_mask if the position or size changed.
Bram Moolenaar356375f2019-08-24 14:46:29 +02001324 // And redraw windows and statuslines that were behind the popup.
Bram Moolenaar33796b32019-06-08 16:01:13 +02001325 if (org_winrow != wp->w_winrow
1326 || org_wincol != wp->w_wincol
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001327 || org_leftcol != wp->w_leftcol
Bram Moolenaard529ba52019-07-02 23:13:53 +02001328 || org_leftoff != wp->w_popup_leftoff
Bram Moolenaar33796b32019-06-08 16:01:13 +02001329 || org_width != wp->w_width
1330 || org_height != wp->w_height)
1331 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001332 redraw_win_later(wp, NOT_VALID);
1333 if (wp->w_popup_flags & POPF_ON_CMDLINE)
1334 clear_cmdline = TRUE;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001335 popup_mask_refresh = TRUE;
1336 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001337}
1338
Bram Moolenaar17627312019-06-02 19:53:44 +02001339typedef enum
1340{
1341 TYPE_NORMAL,
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001342 TYPE_ATCURSOR,
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001343 TYPE_BEVAL,
Bram Moolenaara42d9452019-06-15 21:46:30 +02001344 TYPE_NOTIFICATION,
Bram Moolenaara730e552019-06-16 19:05:31 +02001345 TYPE_DIALOG,
Bram Moolenaar79648732019-07-18 21:43:07 +02001346 TYPE_MENU,
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001347 TYPE_PREVIEW, // preview window
1348 TYPE_INFO // popup menu info
Bram Moolenaar17627312019-06-02 19:53:44 +02001349} create_type_T;
1350
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001351/*
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001352 * Make "buf" empty and set the contents to "text".
1353 * Used by popup_create() and popup_settext().
1354 */
1355 static void
1356popup_set_buffer_text(buf_T *buf, typval_T text)
1357{
1358 int lnum;
1359
1360 // Clear the buffer, then replace the lines.
1361 curbuf = buf;
1362 for (lnum = buf->b_ml.ml_line_count; lnum > 0; --lnum)
1363 ml_delete(lnum, FALSE);
1364 curbuf = curwin->w_buffer;
1365
1366 // Add text to the buffer.
1367 if (text.v_type == VAR_STRING)
1368 {
1369 // just a string
1370 ml_append_buf(buf, 0, text.vval.v_string, (colnr_T)0, TRUE);
1371 }
1372 else
1373 {
1374 list_T *l = text.vval.v_list;
1375
1376 if (l->lv_len > 0)
1377 {
1378 if (l->lv_first->li_tv.v_type == VAR_STRING)
1379 // list of strings
1380 add_popup_strings(buf, l);
1381 else
1382 // list of dictionaries
1383 add_popup_dicts(buf, l);
1384 }
1385 }
1386
1387 // delete the line that was in the empty buffer
1388 curbuf = buf;
1389 ml_delete(buf->b_ml.ml_line_count, FALSE);
1390 curbuf = curwin->w_buffer;
1391}
1392
1393/*
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001394 * Parse the 'previewpopup' or 'completepopup' option and apply the values to
1395 * window "wp" if it is not NULL.
Bram Moolenaar79648732019-07-18 21:43:07 +02001396 * Return FAIL if the parsing fails.
1397 */
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001398 static int
1399parse_popup_option(win_T *wp, int is_preview)
Bram Moolenaar79648732019-07-18 21:43:07 +02001400{
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001401 char_u *p =
1402#ifdef FEAT_QUICKFIX
1403 !is_preview ? p_cpp :
1404#endif
1405 p_pvp;
Bram Moolenaar79648732019-07-18 21:43:07 +02001406
Bram Moolenaar258cef52019-08-21 17:29:29 +02001407 if (wp != NULL)
1408 wp->w_popup_flags &= ~POPF_INFO_MENU;
1409
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001410 for ( ; *p != NUL; p += (*p == ',' ? 1 : 0))
Bram Moolenaar79648732019-07-18 21:43:07 +02001411 {
1412 char_u *e, *dig;
1413 char_u *s = p;
1414 int x;
1415
1416 e = vim_strchr(p, ':');
1417 if (e == NULL || e[1] == NUL)
1418 return FAIL;
1419
1420 p = vim_strchr(e, ',');
1421 if (p == NULL)
1422 p = e + STRLEN(e);
1423 dig = e + 1;
1424 x = getdigits(&dig);
Bram Moolenaar79648732019-07-18 21:43:07 +02001425
1426 if (STRNCMP(s, "height:", 7) == 0)
1427 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001428 if (dig != p)
1429 return FAIL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001430 if (wp != NULL)
1431 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001432 if (is_preview)
1433 wp->w_minheight = x;
Bram Moolenaar79648732019-07-18 21:43:07 +02001434 wp->w_maxheight = x;
1435 }
1436 }
1437 else if (STRNCMP(s, "width:", 6) == 0)
1438 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001439 if (dig != p)
1440 return FAIL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001441 if (wp != NULL)
1442 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001443 if (is_preview)
1444 wp->w_minwidth = x;
Bram Moolenaar79648732019-07-18 21:43:07 +02001445 wp->w_maxwidth = x;
1446 }
1447 }
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001448 else if (STRNCMP(s, "highlight:", 10) == 0)
1449 {
1450 if (wp != NULL)
1451 {
1452 int c = *p;
1453
1454 *p = NUL;
1455 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
1456 s + 10, OPT_FREE|OPT_LOCAL, 0);
1457 *p = c;
1458 }
1459 }
Bram Moolenaarbd483b32019-08-21 15:13:41 +02001460 else if (STRNCMP(s, "border:", 7) == 0)
1461 {
1462 char_u *arg = s + 7;
1463 int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
1464 int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
1465 int i;
1466
1467 if (!on && !off)
1468 return FAIL;
1469 if (wp != NULL)
1470 {
1471 for (i = 0; i < 4; ++i)
1472 wp->w_popup_border[i] = on ? 1 : 0;
1473 if (off)
1474 // only show the X for close when there is a border
1475 wp->w_popup_close = POPCLOSE_NONE;
1476 }
1477 }
Bram Moolenaar258cef52019-08-21 17:29:29 +02001478 else if (STRNCMP(s, "align:", 6) == 0)
1479 {
1480 char_u *arg = s + 6;
1481 int item = STRNCMP(arg, "item", 4) == 0 && arg + 4 == p;
1482 int menu = STRNCMP(arg, "menu", 4) == 0 && arg + 4 == p;
1483
1484 if (!menu && !item)
1485 return FAIL;
1486 if (wp != NULL && menu)
1487 wp->w_popup_flags |= POPF_INFO_MENU;
1488 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001489 else
1490 return FAIL;
1491 }
1492 return OK;
1493}
1494
1495/*
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001496 * Parse the 'previewpopup' option and apply the values to window "wp" if it
1497 * is not NULL.
1498 * Return FAIL if the parsing fails.
1499 */
1500 int
1501parse_previewpopup(win_T *wp)
1502{
1503 return parse_popup_option(wp, TRUE);
1504}
1505
1506/*
1507 * Parse the 'completepopup' option and apply the values to window "wp" if it
1508 * is not NULL.
1509 * Return FAIL if the parsing fails.
1510 */
1511 int
1512parse_completepopup(win_T *wp)
1513{
1514 return parse_popup_option(wp, FALSE);
1515}
1516
1517/*
Bram Moolenaar79648732019-07-18 21:43:07 +02001518 * Set w_wantline and w_wantcol for the cursor position in the current window.
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001519 * Keep at least "width" columns from the right of the screen.
Bram Moolenaar79648732019-07-18 21:43:07 +02001520 */
1521 void
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001522popup_set_wantpos_cursor(win_T *wp, int width)
Bram Moolenaar79648732019-07-18 21:43:07 +02001523{
1524 setcursor_mayforce(TRUE);
1525 wp->w_wantline = curwin->w_winrow + curwin->w_wrow;
1526 if (wp->w_wantline == 0) // cursor in first line
1527 {
1528 wp->w_wantline = 2;
1529 wp->w_popup_pos = POPPOS_TOPLEFT;
1530 }
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001531
Bram Moolenaar79648732019-07-18 21:43:07 +02001532 wp->w_wantcol = curwin->w_wincol + curwin->w_wcol + 1;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001533 if (wp->w_wantcol > Columns - width)
1534 {
1535 wp->w_wantcol = Columns - width;
1536 if (wp->w_wantcol < 1)
1537 wp->w_wantcol = 1;
1538 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001539 popup_adjust_position(wp);
1540}
1541
1542/*
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001543 * Set w_wantline and w_wantcol for the a given screen position.
1544 * Caller must take care of running into the window border.
1545 */
1546 void
1547popup_set_wantpos_rowcol(win_T *wp, int row, int col)
1548{
1549 wp->w_wantline = row;
1550 wp->w_wantcol = col;
1551 popup_adjust_position(wp);
1552}
1553
1554/*
1555 * Add a border and lef&right padding.
1556 */
1557 static void
1558add_border_left_right_padding(win_T *wp)
1559{
1560 int i;
1561
1562 for (i = 0; i < 4; ++i)
1563 {
1564 wp->w_popup_border[i] = 1;
1565 wp->w_popup_padding[i] = (i & 1) ? 1 : 0;
1566 }
1567}
1568
1569/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001570 * popup_create({text}, {options})
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001571 * popup_atcursor({text}, {options})
Bram Moolenaar79648732019-07-18 21:43:07 +02001572 * etc.
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001573 * When creating a preview or info popup "argvars" and "rettv" are NULL.
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001574 */
Bram Moolenaara730e552019-06-16 19:05:31 +02001575 static win_T *
Bram Moolenaar17627312019-06-02 19:53:44 +02001576popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001577{
Bram Moolenaara3fce622019-06-20 02:31:49 +02001578 win_T *wp;
1579 tabpage_T *tp = NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001580 int tabnr = 0;
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001581 int new_buffer;
1582 buf_T *buf = NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001583 dict_T *d = NULL;
Bram Moolenaara3fce622019-06-20 02:31:49 +02001584 int nr;
1585 int i;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001586
Bram Moolenaar79648732019-07-18 21:43:07 +02001587 if (argvars != NULL)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001588 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001589 // Check that arguments look OK.
Bram Moolenaar79648732019-07-18 21:43:07 +02001590 if (argvars[0].v_type == VAR_NUMBER)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001591 {
Bram Moolenaar79648732019-07-18 21:43:07 +02001592 buf = buflist_findnr( argvars[0].vval.v_number);
1593 if (buf == NULL)
1594 {
1595 semsg(_(e_nobufnr), argvars[0].vval.v_number);
1596 return NULL;
1597 }
1598 }
1599 else if (!(argvars[0].v_type == VAR_STRING
1600 && argvars[0].vval.v_string != NULL)
1601 && !(argvars[0].v_type == VAR_LIST
1602 && argvars[0].vval.v_list != NULL))
1603 {
1604 emsg(_(e_listreq));
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001605 return NULL;
1606 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001607 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
Bram Moolenaara3fce622019-06-20 02:31:49 +02001608 {
Bram Moolenaar79648732019-07-18 21:43:07 +02001609 emsg(_(e_dictreq));
Bram Moolenaara3fce622019-06-20 02:31:49 +02001610 return NULL;
1611 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001612 d = argvars[1].vval.v_dict;
1613 }
1614
1615 if (d != NULL)
1616 {
1617 if (dict_find(d, (char_u *)"tabpage", -1) != NULL)
1618 tabnr = (int)dict_get_number(d, (char_u *)"tabpage");
1619 else if (type == TYPE_NOTIFICATION)
1620 tabnr = -1; // notifications are global by default
1621 else
1622 tabnr = 0;
1623 if (tabnr > 0)
1624 {
1625 tp = find_tabpage(tabnr);
1626 if (tp == NULL)
1627 {
1628 semsg(_("E997: Tabpage not found: %d"), tabnr);
1629 return NULL;
1630 }
1631 }
Bram Moolenaara3fce622019-06-20 02:31:49 +02001632 }
1633
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001634 // Create the window and buffer.
1635 wp = win_alloc_popup_win();
1636 if (wp == NULL)
Bram Moolenaara730e552019-06-16 19:05:31 +02001637 return NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001638 if (rettv != NULL)
1639 rettv->vval.v_number = wp->w_id;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001640 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02001641 wp->w_popup_flags = POPF_IS_POPUP | POPF_MAPPING;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001642
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001643 if (buf != NULL)
1644 {
1645 // use existing buffer
1646 new_buffer = FALSE;
Bram Moolenaar7866b872019-07-01 22:21:01 +02001647 win_init_popup_win(wp, buf);
Bram Moolenaar46451042019-08-24 15:50:46 +02001648 set_local_options_default(wp, FALSE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001649 buffer_ensure_loaded(buf);
1650 }
1651 else
1652 {
1653 // create a new buffer associated with the popup
1654 new_buffer = TRUE;
Bram Moolenaar00b0d6d2019-08-21 22:25:30 +02001655 buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_DUMMY|BLN_REUSE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001656 if (buf == NULL)
1657 return NULL;
1658 ml_open(buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +02001659
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001660 win_init_popup_win(wp, buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +02001661
Bram Moolenaar46451042019-08-24 15:50:46 +02001662 set_local_options_default(wp, TRUE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001663 set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001664 (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001665 set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
Bram Moolenaar79648732019-07-18 21:43:07 +02001666 (char_u *)"wipe", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001667 buf->b_p_ul = -1; // no undo
1668 buf->b_p_swf = FALSE; // no swap file
1669 buf->b_p_bl = FALSE; // unlisted buffer
1670 buf->b_locked = TRUE;
1671 wp->w_p_wrap = TRUE; // 'wrap' is default on
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001672
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001673 // Avoid that 'buftype' is reset when this buffer is entered.
1674 buf->b_p_initialized = TRUE;
1675 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001676
Bram Moolenaara3fce622019-06-20 02:31:49 +02001677 if (tp != NULL)
1678 {
1679 // popup on specified tab page
1680 wp->w_next = tp->tp_first_popupwin;
1681 tp->tp_first_popupwin = wp;
1682 }
1683 else if (tabnr == 0)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001684 {
Bram Moolenaarfc06cbb2019-06-15 14:14:31 +02001685 // popup on current tab page
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02001686 wp->w_next = curtab->tp_first_popupwin;
1687 curtab->tp_first_popupwin = wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001688 }
Bram Moolenaara3fce622019-06-20 02:31:49 +02001689 else // (tabnr < 0)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001690 {
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001691 win_T *prev = first_popupwin;
1692
1693 // Global popup: add at the end, so that it gets displayed on top of
1694 // older ones with the same zindex. Matters for notifications.
1695 if (first_popupwin == NULL)
1696 first_popupwin = wp;
1697 else
1698 {
1699 while (prev->w_next != NULL)
1700 prev = prev->w_next;
1701 prev->w_next = wp;
1702 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001703 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001704
Bram Moolenaar79648732019-07-18 21:43:07 +02001705 if (new_buffer && argvars != NULL)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001706 popup_set_buffer_text(buf, argvars[0]);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001707
Bram Moolenaar79648732019-07-18 21:43:07 +02001708 if (type == TYPE_ATCURSOR || type == TYPE_PREVIEW)
Bram Moolenaar17627312019-06-02 19:53:44 +02001709 {
1710 wp->w_popup_pos = POPPOS_BOTLEFT;
Bram Moolenaar79648732019-07-18 21:43:07 +02001711 }
1712 if (type == TYPE_ATCURSOR)
1713 {
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001714 popup_set_wantpos_cursor(wp, 0);
Bram Moolenaar17627312019-06-02 19:53:44 +02001715 set_moved_values(wp);
1716 set_moved_columns(wp, FIND_STRING);
1717 }
1718
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001719 if (type == TYPE_BEVAL)
1720 {
1721 wp->w_popup_pos = POPPOS_BOTLEFT;
1722
1723 // by default use the mouse position
1724 wp->w_wantline = mouse_row;
1725 if (wp->w_wantline <= 0) // mouse on first line
1726 {
1727 wp->w_wantline = 2;
1728 wp->w_popup_pos = POPPOS_TOPLEFT;
1729 }
1730 wp->w_wantcol = mouse_col + 1;
1731 set_mousemoved_values(wp);
1732 set_mousemoved_columns(wp, FIND_IDENT + FIND_STRING + FIND_EVAL);
1733 }
1734
Bram Moolenaar33796b32019-06-08 16:01:13 +02001735 // set default values
1736 wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001737 wp->w_popup_close = POPCLOSE_NONE;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001738
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001739 if (type == TYPE_NOTIFICATION)
1740 {
1741 win_T *twp, *nextwin;
1742 int height = buf->b_ml.ml_line_count + 3;
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001743
1744 // Try to not overlap with another global popup. Guess we need 3
1745 // more screen lines than buffer lines.
1746 wp->w_wantline = 1;
1747 for (twp = first_popupwin; twp != NULL; twp = nextwin)
1748 {
1749 nextwin = twp->w_next;
1750 if (twp != wp
1751 && twp->w_zindex == POPUPWIN_NOTIFICATION_ZINDEX
1752 && twp->w_winrow <= wp->w_wantline - 1 + height
1753 && twp->w_winrow + popup_height(twp) > wp->w_wantline - 1)
1754 {
1755 // move to below this popup and restart the loop to check for
1756 // overlap with other popups
1757 wp->w_wantline = twp->w_winrow + popup_height(twp) + 1;
1758 nextwin = first_popupwin;
1759 }
1760 }
1761 if (wp->w_wantline + height > Rows)
1762 {
1763 // can't avoid overlap, put on top in the hope that message goes
1764 // away soon.
1765 wp->w_wantline = 1;
1766 }
1767
1768 wp->w_wantcol = 10;
1769 wp->w_zindex = POPUPWIN_NOTIFICATION_ZINDEX;
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001770 wp->w_minwidth = 20;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001771 wp->w_popup_flags |= POPF_DRAG;
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001772 wp->w_popup_close = POPCLOSE_CLICK;
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001773 for (i = 0; i < 4; ++i)
1774 wp->w_popup_border[i] = 1;
1775 wp->w_popup_padding[1] = 1;
1776 wp->w_popup_padding[3] = 1;
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001777
1778 nr = syn_name2id((char_u *)"PopupNotification");
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001779 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001780 (char_u *)(nr == 0 ? "WarningMsg" : "PopupNotification"),
1781 OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001782 }
1783
Bram Moolenaara730e552019-06-16 19:05:31 +02001784 if (type == TYPE_DIALOG || type == TYPE_MENU)
Bram Moolenaara42d9452019-06-15 21:46:30 +02001785 {
Bram Moolenaara42d9452019-06-15 21:46:30 +02001786 wp->w_popup_pos = POPPOS_CENTER;
1787 wp->w_zindex = POPUPWIN_DIALOG_ZINDEX;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001788 wp->w_popup_flags |= POPF_DRAG;
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02001789 wp->w_popup_flags &= ~POPF_MAPPING;
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001790 add_border_left_right_padding(wp);
Bram Moolenaara42d9452019-06-15 21:46:30 +02001791 }
1792
Bram Moolenaara730e552019-06-16 19:05:31 +02001793 if (type == TYPE_MENU)
1794 {
1795 typval_T tv;
1796 callback_T callback;
1797
1798 tv.v_type = VAR_STRING;
1799 tv.vval.v_string = (char_u *)"popup_filter_menu";
1800 callback = get_callback(&tv);
1801 if (callback.cb_name != NULL)
1802 set_callback(&wp->w_filter_cb, &callback);
1803
1804 wp->w_p_wrap = 0;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02001805 wp->w_popup_flags |= POPF_CURSORLINE;
Bram Moolenaara730e552019-06-16 19:05:31 +02001806 }
1807
Bram Moolenaar79648732019-07-18 21:43:07 +02001808 if (type == TYPE_PREVIEW)
1809 {
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001810 wp->w_popup_flags |= POPF_DRAG | POPF_RESIZE;
Bram Moolenaar79648732019-07-18 21:43:07 +02001811 wp->w_popup_close = POPCLOSE_BUTTON;
1812 for (i = 0; i < 4; ++i)
1813 wp->w_popup_border[i] = 1;
1814 parse_previewpopup(wp);
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001815 popup_set_wantpos_cursor(wp, wp->w_minwidth);
1816 }
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001817# ifdef FEAT_QUICKFIX
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001818 if (type == TYPE_INFO)
1819 {
1820 wp->w_popup_pos = POPPOS_TOPLEFT;
1821 wp->w_popup_flags |= POPF_DRAG | POPF_RESIZE;
1822 wp->w_popup_close = POPCLOSE_BUTTON;
1823 add_border_left_right_padding(wp);
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001824 parse_completepopup(wp);
Bram Moolenaar79648732019-07-18 21:43:07 +02001825 }
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001826# endif
Bram Moolenaar79648732019-07-18 21:43:07 +02001827
Bram Moolenaarae943152019-06-16 22:54:14 +02001828 for (i = 0; i < 4; ++i)
1829 VIM_CLEAR(wp->w_border_highlight[i]);
1830 for (i = 0; i < 8; ++i)
1831 wp->w_border_char[i] = 0;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001832 wp->w_want_scrollbar = 1;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001833 wp->w_popup_fixed = 0;
Bram Moolenaarae943152019-06-16 22:54:14 +02001834
Bram Moolenaar79648732019-07-18 21:43:07 +02001835 if (d != NULL)
1836 // Deal with options.
1837 apply_options(wp, d);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001838
Bram Moolenaar0fcf26b2019-06-23 01:03:51 +02001839#ifdef FEAT_TIMERS
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001840 if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL)
1841 popup_add_timeout(wp, 3000);
Bram Moolenaar0fcf26b2019-06-23 01:03:51 +02001842#endif
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001843
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001844 popup_adjust_position(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001845
1846 wp->w_vsep_width = 0;
1847
1848 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02001849 popup_mask_refresh = TRUE;
Bram Moolenaara730e552019-06-16 19:05:31 +02001850
1851 return wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001852}
1853
1854/*
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +02001855 * popup_clear()
1856 */
1857 void
1858f_popup_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1859{
1860 close_all_popups();
1861}
1862
1863/*
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001864 * popup_create({text}, {options})
1865 */
1866 void
1867f_popup_create(typval_T *argvars, typval_T *rettv)
1868{
Bram Moolenaar17627312019-06-02 19:53:44 +02001869 popup_create(argvars, rettv, TYPE_NORMAL);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001870}
1871
1872/*
1873 * popup_atcursor({text}, {options})
1874 */
1875 void
1876f_popup_atcursor(typval_T *argvars, typval_T *rettv)
1877{
Bram Moolenaar17627312019-06-02 19:53:44 +02001878 popup_create(argvars, rettv, TYPE_ATCURSOR);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001879}
1880
1881/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001882 * popup_beval({text}, {options})
1883 */
1884 void
1885f_popup_beval(typval_T *argvars, typval_T *rettv)
1886{
1887 popup_create(argvars, rettv, TYPE_BEVAL);
1888}
1889
1890/*
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001891 * Invoke the close callback for window "wp" with value "result".
1892 * Careful: The callback may make "wp" invalid!
1893 */
1894 static void
1895invoke_popup_callback(win_T *wp, typval_T *result)
1896{
1897 typval_T rettv;
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001898 typval_T argv[3];
1899
1900 argv[0].v_type = VAR_NUMBER;
1901 argv[0].vval.v_number = (varnumber_T)wp->w_id;
1902
1903 if (result != NULL && result->v_type != VAR_UNKNOWN)
1904 copy_tv(result, &argv[1]);
1905 else
1906 {
1907 argv[1].v_type = VAR_NUMBER;
1908 argv[1].vval.v_number = 0;
1909 }
1910
1911 argv[2].v_type = VAR_UNKNOWN;
1912
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001913 call_callback(&wp->w_close_cb, -1, &rettv, 2, argv);
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001914 if (result != NULL)
1915 clear_tv(&argv[1]);
1916 clear_tv(&rettv);
1917}
1918
1919/*
Bram Moolenaar3397f742019-06-02 18:40:06 +02001920 * Close popup "wp" and invoke any close callback for it.
1921 */
1922 static void
1923popup_close_and_callback(win_T *wp, typval_T *arg)
1924{
1925 int id = wp->w_id;
1926
1927 if (wp->w_close_cb.cb_name != NULL)
1928 // Careful: This may make "wp" invalid.
1929 invoke_popup_callback(wp, arg);
1930
1931 popup_close(id);
1932}
1933
Bram Moolenaar12034e22019-08-25 22:25:02 +02001934 static void
1935popup_close_with_retval(win_T *wp, int retval)
1936{
1937 typval_T res;
1938
1939 res.v_type = VAR_NUMBER;
1940 res.vval.v_number = retval;
1941 popup_close_and_callback(wp, &res);
1942}
1943
Bram Moolenaar3397f742019-06-02 18:40:06 +02001944/*
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001945 * Close popup "wp" because of a mouse click.
1946 */
1947 void
1948popup_close_for_mouse_click(win_T *wp)
1949{
Bram Moolenaar12034e22019-08-25 22:25:02 +02001950 popup_close_with_retval(wp, -2);
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001951}
1952
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001953 static void
1954check_mouse_moved(win_T *wp, win_T *mouse_wp)
1955{
1956 // Close the popup when all if these are true:
1957 // - the mouse is not on this popup
1958 // - "mousemoved" was used
1959 // - the mouse is no longer on the same screen row or the mouse column is
1960 // outside of the relevant text
1961 if (wp != mouse_wp
1962 && wp->w_popup_mouse_row != 0
1963 && (wp->w_popup_mouse_row != mouse_row
1964 || mouse_col < wp->w_popup_mouse_mincol
1965 || mouse_col > wp->w_popup_mouse_maxcol))
1966 {
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001967 // Careful: this makes "wp" invalid.
Bram Moolenaar12034e22019-08-25 22:25:02 +02001968 popup_close_with_retval(wp, -2);
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001969 }
1970}
1971
1972/*
1973 * Called when the mouse moved: may close a popup with "mousemoved".
1974 */
1975 void
1976popup_handle_mouse_moved(void)
1977{
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001978 win_T *wp, *nextwp;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001979 win_T *mouse_wp;
1980 int row = mouse_row;
1981 int col = mouse_col;
1982
1983 // find the window where the mouse is in
1984 mouse_wp = mouse_find_win(&row, &col, FIND_POPUP);
1985
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001986 for (wp = first_popupwin; wp != NULL; wp = nextwp)
1987 {
1988 nextwp = wp->w_next;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001989 check_mouse_moved(wp, mouse_wp);
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001990 }
1991 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = nextwp)
1992 {
1993 nextwp = wp->w_next;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001994 check_mouse_moved(wp, mouse_wp);
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001995 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001996}
1997
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001998/*
Bram Moolenaara730e552019-06-16 19:05:31 +02001999 * In a filter: check if the typed key is a mouse event that is used for
2000 * dragging the popup.
2001 */
2002 static void
2003filter_handle_drag(win_T *wp, int c, typval_T *rettv)
2004{
2005 int row = mouse_row;
2006 int col = mouse_col;
2007
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02002008 if ((wp->w_popup_flags & POPF_DRAG)
Bram Moolenaara730e552019-06-16 19:05:31 +02002009 && is_mouse_key(c)
2010 && (wp == popup_dragwin
2011 || wp == mouse_find_win(&row, &col, FIND_POPUP)))
2012 // do not consume the key, allow for dragging the popup
2013 rettv->vval.v_number = 0;
2014}
2015
Bram Moolenaara730e552019-06-16 19:05:31 +02002016/*
2017 * popup_filter_menu({text}, {options})
2018 */
2019 void
2020f_popup_filter_menu(typval_T *argvars, typval_T *rettv)
2021{
2022 int id = tv_get_number(&argvars[0]);
2023 win_T *wp = win_id2wp(id);
2024 char_u *key = tv_get_string(&argvars[1]);
2025 typval_T res;
2026 int c;
2027 linenr_T old_lnum;
2028
2029 // If the popup has been closed do not consume the key.
2030 if (wp == NULL)
2031 return;
2032
2033 c = *key;
2034 if (c == K_SPECIAL && key[1] != NUL)
2035 c = TO_SPECIAL(key[1], key[2]);
2036
2037 // consume all keys until done
2038 rettv->vval.v_number = 1;
2039 res.v_type = VAR_NUMBER;
2040
2041 old_lnum = wp->w_cursor.lnum;
2042 if ((c == 'k' || c == 'K' || c == K_UP) && wp->w_cursor.lnum > 1)
2043 --wp->w_cursor.lnum;
2044 if ((c == 'j' || c == 'J' || c == K_DOWN)
2045 && wp->w_cursor.lnum < wp->w_buffer->b_ml.ml_line_count)
2046 ++wp->w_cursor.lnum;
2047 if (old_lnum != wp->w_cursor.lnum)
2048 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002049 // caller will call popup_highlight_curline()
Bram Moolenaara730e552019-06-16 19:05:31 +02002050 return;
2051 }
2052
2053 if (c == 'x' || c == 'X' || c == ESC || c == Ctrl_C)
2054 {
2055 // Cancelled, invoke callback with -1
2056 res.vval.v_number = -1;
2057 popup_close_and_callback(wp, &res);
2058 return;
2059 }
2060 if (c == ' ' || c == K_KENTER || c == CAR || c == NL)
2061 {
2062 // Invoke callback with current index.
2063 res.vval.v_number = wp->w_cursor.lnum;
2064 popup_close_and_callback(wp, &res);
2065 return;
2066 }
2067
2068 filter_handle_drag(wp, c, rettv);
2069}
2070
2071/*
Bram Moolenaara42d9452019-06-15 21:46:30 +02002072 * popup_filter_yesno({text}, {options})
2073 */
2074 void
2075f_popup_filter_yesno(typval_T *argvars, typval_T *rettv)
2076{
2077 int id = tv_get_number(&argvars[0]);
2078 win_T *wp = win_id2wp(id);
2079 char_u *key = tv_get_string(&argvars[1]);
2080 typval_T res;
Bram Moolenaara730e552019-06-16 19:05:31 +02002081 int c;
Bram Moolenaara42d9452019-06-15 21:46:30 +02002082
2083 // If the popup has been closed don't consume the key.
2084 if (wp == NULL)
2085 return;
2086
Bram Moolenaara730e552019-06-16 19:05:31 +02002087 c = *key;
2088 if (c == K_SPECIAL && key[1] != NUL)
2089 c = TO_SPECIAL(key[1], key[2]);
2090
Bram Moolenaara42d9452019-06-15 21:46:30 +02002091 // consume all keys until done
2092 rettv->vval.v_number = 1;
2093
Bram Moolenaara730e552019-06-16 19:05:31 +02002094 if (c == 'y' || c == 'Y')
Bram Moolenaara42d9452019-06-15 21:46:30 +02002095 res.vval.v_number = 1;
Bram Moolenaara730e552019-06-16 19:05:31 +02002096 else if (c == 'n' || c == 'N' || c == 'x' || c == 'X' || c == ESC)
Bram Moolenaara42d9452019-06-15 21:46:30 +02002097 res.vval.v_number = 0;
2098 else
2099 {
Bram Moolenaara730e552019-06-16 19:05:31 +02002100 filter_handle_drag(wp, c, rettv);
Bram Moolenaara42d9452019-06-15 21:46:30 +02002101 return;
2102 }
2103
2104 // Invoke callback
2105 res.v_type = VAR_NUMBER;
2106 popup_close_and_callback(wp, &res);
2107}
2108
2109/*
2110 * popup_dialog({text}, {options})
2111 */
2112 void
2113f_popup_dialog(typval_T *argvars, typval_T *rettv)
2114{
2115 popup_create(argvars, rettv, TYPE_DIALOG);
2116}
2117
2118/*
Bram Moolenaara730e552019-06-16 19:05:31 +02002119 * popup_menu({text}, {options})
2120 */
2121 void
2122f_popup_menu(typval_T *argvars, typval_T *rettv)
2123{
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002124 popup_create(argvars, rettv, TYPE_MENU);
Bram Moolenaara730e552019-06-16 19:05:31 +02002125}
2126
2127/*
Bram Moolenaara42d9452019-06-15 21:46:30 +02002128 * popup_notification({text}, {options})
2129 */
2130 void
2131f_popup_notification(typval_T *argvars, typval_T *rettv)
2132{
2133 popup_create(argvars, rettv, TYPE_NOTIFICATION);
2134}
2135
2136/*
2137 * Find the popup window with window-ID "id".
2138 * If the popup window does not exist NULL is returned.
2139 * If the window is not a popup window, and error message is given.
2140 */
2141 static win_T *
2142find_popup_win(int id)
2143{
2144 win_T *wp = win_id2wp(id);
2145
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002146 if (wp != NULL && !WIN_IS_POPUP(wp))
Bram Moolenaara42d9452019-06-15 21:46:30 +02002147 {
2148 semsg(_("E993: window %d is not a popup window"), id);
2149 return NULL;
2150 }
2151 return wp;
2152}
2153
2154/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002155 * popup_close({id})
2156 */
2157 void
2158f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
2159{
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002160 int id = (int)tv_get_number(argvars);
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002161 win_T *wp = find_popup_win(id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002162
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002163 if (wp != NULL)
Bram Moolenaar3397f742019-06-02 18:40:06 +02002164 popup_close_and_callback(wp, &argvars[1]);
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002165}
2166
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002167 static void
2168popup_hide(win_T *wp)
2169{
2170 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
2171 {
2172 wp->w_popup_flags |= POPF_HIDDEN;
2173 --wp->w_buffer->b_nwindows;
2174 redraw_all_later(NOT_VALID);
2175 popup_mask_refresh = TRUE;
2176 }
2177}
2178
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002179/*
2180 * popup_hide({id})
2181 */
2182 void
2183f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
2184{
2185 int id = (int)tv_get_number(argvars);
2186 win_T *wp = find_popup_win(id);
2187
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002188 if (wp != NULL)
2189 popup_hide(wp);
2190}
2191
2192 void
2193popup_show(win_T *wp)
2194{
2195 if ((wp->w_popup_flags & POPF_HIDDEN) != 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002196 {
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002197 wp->w_popup_flags &= ~POPF_HIDDEN;
2198 ++wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002199 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002200 popup_mask_refresh = TRUE;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002201 }
2202}
2203
2204/*
2205 * popup_show({id})
2206 */
2207 void
2208f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
2209{
2210 int id = (int)tv_get_number(argvars);
2211 win_T *wp = find_popup_win(id);
2212
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002213 if (wp != NULL)
2214 popup_show(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002215}
2216
Bram Moolenaardc2ce582019-06-16 15:32:14 +02002217/*
2218 * popup_settext({id}, {text})
2219 */
2220 void
2221f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED)
2222{
2223 int id = (int)tv_get_number(&argvars[0]);
2224 win_T *wp = find_popup_win(id);
2225
2226 if (wp != NULL)
2227 {
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002228 if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_LIST)
2229 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
2230 else
2231 {
2232 popup_set_buffer_text(wp->w_buffer, argvars[1]);
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02002233 redraw_win_later(wp, NOT_VALID);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002234 popup_adjust_position(wp);
2235 }
Bram Moolenaardc2ce582019-06-16 15:32:14 +02002236 }
2237}
2238
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002239 static void
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002240popup_free(win_T *wp)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002241{
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002242 sign_undefine_by_name(popup_get_sign_name(wp), FALSE);
Bram Moolenaar868b7b62019-05-29 21:44:40 +02002243 wp->w_buffer->b_locked = FALSE;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02002244 if (wp->w_winrow + popup_height(wp) >= cmdline_row)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002245 clear_cmdline = TRUE;
2246 win_free_popup(wp);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002247
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002248 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002249 popup_mask_refresh = TRUE;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002250}
2251
Bram Moolenaarec583842019-05-26 14:11:23 +02002252/*
2253 * Close a popup window by Window-id.
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002254 * Does not invoke the callback.
Bram Moolenaarec583842019-05-26 14:11:23 +02002255 */
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002256 void
Bram Moolenaarec583842019-05-26 14:11:23 +02002257popup_close(int id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002258{
2259 win_T *wp;
Bram Moolenaarec583842019-05-26 14:11:23 +02002260 tabpage_T *tp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002261 win_T *prev = NULL;
2262
Bram Moolenaarec583842019-05-26 14:11:23 +02002263 // go through global popups
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002264 for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
Bram Moolenaarec583842019-05-26 14:11:23 +02002265 if (wp->w_id == id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002266 {
2267 if (prev == NULL)
2268 first_popupwin = wp->w_next;
2269 else
2270 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002271 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +02002272 return;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002273 }
2274
Bram Moolenaarec583842019-05-26 14:11:23 +02002275 // go through tab-local popups
2276 FOR_ALL_TABPAGES(tp)
2277 popup_close_tabpage(tp, id);
2278}
2279
2280/*
2281 * Close a popup window with Window-id "id" in tabpage "tp".
2282 */
2283 void
2284popup_close_tabpage(tabpage_T *tp, int id)
2285{
2286 win_T *wp;
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02002287 win_T **root = &tp->tp_first_popupwin;
Bram Moolenaarec583842019-05-26 14:11:23 +02002288 win_T *prev = NULL;
2289
Bram Moolenaarec583842019-05-26 14:11:23 +02002290 for (wp = *root; wp != NULL; prev = wp, wp = wp->w_next)
2291 if (wp->w_id == id)
2292 {
2293 if (prev == NULL)
2294 *root = wp->w_next;
2295 else
2296 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002297 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +02002298 return;
2299 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002300}
2301
2302 void
2303close_all_popups(void)
2304{
2305 while (first_popupwin != NULL)
2306 popup_close(first_popupwin->w_id);
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02002307 while (curtab->tp_first_popupwin != NULL)
2308 popup_close(curtab->tp_first_popupwin->w_id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002309}
2310
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002311/*
2312 * popup_move({id}, {options})
2313 */
2314 void
2315f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
2316{
Bram Moolenaarae943152019-06-16 22:54:14 +02002317 dict_T *dict;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002318 int id = (int)tv_get_number(argvars);
2319 win_T *wp = find_popup_win(id);
2320
2321 if (wp == NULL)
2322 return; // invalid {id}
2323
2324 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
2325 {
2326 emsg(_(e_dictreq));
2327 return;
2328 }
Bram Moolenaarae943152019-06-16 22:54:14 +02002329 dict = argvars[1].vval.v_dict;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002330
Bram Moolenaarae943152019-06-16 22:54:14 +02002331 apply_move_options(wp, dict);
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002332
2333 if (wp->w_winrow + wp->w_height >= cmdline_row)
2334 clear_cmdline = TRUE;
2335 popup_adjust_position(wp);
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002336}
2337
Bram Moolenaarbc133542019-05-29 20:26:46 +02002338/*
Bram Moolenaarae943152019-06-16 22:54:14 +02002339 * popup_setoptions({id}, {options})
2340 */
2341 void
2342f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
2343{
2344 dict_T *dict;
2345 int id = (int)tv_get_number(argvars);
2346 win_T *wp = find_popup_win(id);
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002347 linenr_T old_firstline;
Bram Moolenaarae943152019-06-16 22:54:14 +02002348
2349 if (wp == NULL)
2350 return; // invalid {id}
2351
2352 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
2353 {
2354 emsg(_(e_dictreq));
2355 return;
2356 }
2357 dict = argvars[1].vval.v_dict;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002358 old_firstline = wp->w_firstline;
Bram Moolenaarae943152019-06-16 22:54:14 +02002359
2360 apply_move_options(wp, dict);
2361 apply_general_options(wp, dict);
2362
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002363 if (old_firstline != wp->w_firstline)
2364 redraw_win_later(wp, NOT_VALID);
Bram Moolenaarad24a712019-06-17 20:05:45 +02002365 popup_mask_refresh = TRUE;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002366 popup_highlight_curline(wp);
Bram Moolenaarae943152019-06-16 22:54:14 +02002367 popup_adjust_position(wp);
2368}
2369
2370/*
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002371 * popup_getpos({id})
Bram Moolenaarbc133542019-05-29 20:26:46 +02002372 */
2373 void
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002374f_popup_getpos(typval_T *argvars, typval_T *rettv)
Bram Moolenaarbc133542019-05-29 20:26:46 +02002375{
2376 dict_T *dict;
2377 int id = (int)tv_get_number(argvars);
2378 win_T *wp = find_popup_win(id);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002379 int top_extra;
2380 int left_extra;
Bram Moolenaarbc133542019-05-29 20:26:46 +02002381
2382 if (rettv_dict_alloc(rettv) == OK)
2383 {
2384 if (wp == NULL)
2385 return; // invalid {id}
Bram Moolenaareb2310d2019-06-16 20:09:10 +02002386 top_extra = popup_top_extra(wp);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002387 left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
2388
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002389 // we know how much space we need, avoid resizing halfway
Bram Moolenaarbc133542019-05-29 20:26:46 +02002390 dict = rettv->vval.v_dict;
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002391 hash_lock_size(&dict->dv_hashtab, 11);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002392
Bram Moolenaarbc133542019-05-29 20:26:46 +02002393 dict_add_number(dict, "line", wp->w_winrow + 1);
2394 dict_add_number(dict, "col", wp->w_wincol + 1);
Bram Moolenaar68d48f42019-06-12 22:42:41 +02002395 dict_add_number(dict, "width", wp->w_width + left_extra
2396 + wp->w_popup_border[1] + wp->w_popup_padding[1]);
2397 dict_add_number(dict, "height", wp->w_height + top_extra
2398 + wp->w_popup_border[2] + wp->w_popup_padding[2]);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002399
2400 dict_add_number(dict, "core_line", wp->w_winrow + 1 + top_extra);
2401 dict_add_number(dict, "core_col", wp->w_wincol + 1 + left_extra);
2402 dict_add_number(dict, "core_width", wp->w_width);
2403 dict_add_number(dict, "core_height", wp->w_height);
2404
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002405 dict_add_number(dict, "scrollbar", wp->w_has_scrollbar);
Bram Moolenaar68acb412019-06-26 03:40:36 +02002406 dict_add_number(dict, "firstline", wp->w_topline);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002407 dict_add_number(dict, "visible",
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +02002408 win_valid(wp) && (wp->w_popup_flags & POPF_HIDDEN) == 0);
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002409
2410 hash_unlock(&dict->dv_hashtab);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002411 }
2412}
Bram Moolenaarb4f06282019-07-12 21:07:54 +02002413/*
2414 * popup_locate({row}, {col})
2415 */
2416 void
2417f_popup_locate(typval_T *argvars, typval_T *rettv)
2418{
2419 int row = tv_get_number(&argvars[0]) - 1;
2420 int col = tv_get_number(&argvars[1]) - 1;
2421 win_T *wp;
2422
2423 wp = mouse_find_win(&row, &col, FIND_POPUP);
2424 if (WIN_IS_POPUP(wp))
2425 rettv->vval.v_number = wp->w_id;
2426}
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002427
2428/*
Bram Moolenaarae943152019-06-16 22:54:14 +02002429 * For popup_getoptions(): add a "border" or "padding" entry to "dict".
2430 */
2431 static void
2432get_padding_border(dict_T *dict, int *array, char *name)
2433{
2434 list_T *list;
2435 int i;
2436
2437 if (array[0] == 0 && array[1] == 0 && array[2] == 0 && array[3] == 0)
2438 return;
2439
2440 list = list_alloc();
2441 if (list != NULL)
2442 {
2443 dict_add_list(dict, name, list);
2444 if (array[0] != 1 || array[1] != 1 || array[2] != 1 || array[3] != 1)
2445 for (i = 0; i < 4; ++i)
2446 list_append_number(list, array[i]);
2447 }
2448}
2449
2450/*
2451 * For popup_getoptions(): add a "borderhighlight" entry to "dict".
2452 */
2453 static void
2454get_borderhighlight(dict_T *dict, win_T *wp)
2455{
2456 list_T *list;
2457 int i;
2458
2459 for (i = 0; i < 4; ++i)
2460 if (wp->w_border_highlight[i] != NULL)
2461 break;
2462 if (i == 4)
2463 return;
2464
2465 list = list_alloc();
2466 if (list != NULL)
2467 {
2468 dict_add_list(dict, "borderhighlight", list);
2469 for (i = 0; i < 4; ++i)
2470 list_append_string(list, wp->w_border_highlight[i], -1);
2471 }
2472}
2473
2474/*
2475 * For popup_getoptions(): add a "borderchars" entry to "dict".
2476 */
2477 static void
2478get_borderchars(dict_T *dict, win_T *wp)
2479{
2480 list_T *list;
2481 int i;
2482 char_u buf[NUMBUFLEN];
2483 int len;
2484
2485 for (i = 0; i < 8; ++i)
2486 if (wp->w_border_char[i] != 0)
2487 break;
2488 if (i == 8)
2489 return;
2490
2491 list = list_alloc();
2492 if (list != NULL)
2493 {
2494 dict_add_list(dict, "borderchars", list);
2495 for (i = 0; i < 8; ++i)
2496 {
2497 len = mb_char2bytes(wp->w_border_char[i], buf);
2498 list_append_string(list, buf, len);
2499 }
2500 }
2501}
2502
2503/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002504 * For popup_getoptions(): add a "moved" and "mousemoved" entry to "dict".
Bram Moolenaarae943152019-06-16 22:54:14 +02002505 */
2506 static void
2507get_moved_list(dict_T *dict, win_T *wp)
2508{
2509 list_T *list;
2510
2511 list = list_alloc();
2512 if (list != NULL)
2513 {
2514 dict_add_list(dict, "moved", list);
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002515 list_append_number(list, wp->w_popup_lnum);
Bram Moolenaarae943152019-06-16 22:54:14 +02002516 list_append_number(list, wp->w_popup_mincol);
2517 list_append_number(list, wp->w_popup_maxcol);
2518 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002519 list = list_alloc();
2520 if (list != NULL)
2521 {
2522 dict_add_list(dict, "mousemoved", list);
2523 list_append_number(list, wp->w_popup_mouse_row);
2524 list_append_number(list, wp->w_popup_mouse_mincol);
2525 list_append_number(list, wp->w_popup_mouse_maxcol);
2526 }
Bram Moolenaarae943152019-06-16 22:54:14 +02002527}
2528
2529/*
Bram Moolenaar33796b32019-06-08 16:01:13 +02002530 * popup_getoptions({id})
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002531 */
2532 void
2533f_popup_getoptions(typval_T *argvars, typval_T *rettv)
2534{
2535 dict_T *dict;
2536 int id = (int)tv_get_number(argvars);
2537 win_T *wp = find_popup_win(id);
Bram Moolenaara3fce622019-06-20 02:31:49 +02002538 tabpage_T *tp;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002539 int i;
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002540
2541 if (rettv_dict_alloc(rettv) == OK)
2542 {
2543 if (wp == NULL)
2544 return;
2545
2546 dict = rettv->vval.v_dict;
2547 dict_add_number(dict, "line", wp->w_wantline);
2548 dict_add_number(dict, "col", wp->w_wantcol);
2549 dict_add_number(dict, "minwidth", wp->w_minwidth);
2550 dict_add_number(dict, "minheight", wp->w_minheight);
2551 dict_add_number(dict, "maxheight", wp->w_maxheight);
2552 dict_add_number(dict, "maxwidth", wp->w_maxwidth);
Bram Moolenaar8d241042019-06-12 23:40:01 +02002553 dict_add_number(dict, "firstline", wp->w_firstline);
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002554 dict_add_number(dict, "scrollbar", wp->w_want_scrollbar);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002555 dict_add_number(dict, "zindex", wp->w_zindex);
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02002556 dict_add_number(dict, "fixed", wp->w_popup_fixed);
Bram Moolenaar12034e22019-08-25 22:25:02 +02002557 if (wp->w_popup_prop_type && win_valid(wp->w_popup_prop_win))
2558 {
2559 proptype_T *pt = text_prop_type_by_id(
2560 wp->w_popup_prop_win->w_buffer,
2561 wp->w_popup_prop_type);
2562
2563 if (pt != NULL)
2564 dict_add_string(dict, "textprop", pt->pt_name);
2565 dict_add_number(dict, "textpropwin", wp->w_popup_prop_win->w_id);
2566 dict_add_number(dict, "textpropid", wp->w_popup_prop_id);
2567 }
Bram Moolenaarae943152019-06-16 22:54:14 +02002568 dict_add_string(dict, "title", wp->w_popup_title);
2569 dict_add_number(dict, "wrap", wp->w_p_wrap);
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02002570 dict_add_number(dict, "drag", (wp->w_popup_flags & POPF_DRAG) != 0);
Bram Moolenaar12034e22019-08-25 22:25:02 +02002571 dict_add_number(dict, "mapping",
2572 (wp->w_popup_flags & POPF_MAPPING) != 0);
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02002573 dict_add_number(dict, "resize", (wp->w_popup_flags & POPF_RESIZE) != 0);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002574 dict_add_number(dict, "cursorline",
2575 (wp->w_popup_flags & POPF_CURSORLINE) != 0);
Bram Moolenaarae943152019-06-16 22:54:14 +02002576 dict_add_string(dict, "highlight", wp->w_p_wcr);
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02002577 if (wp->w_scrollbar_highlight != NULL)
2578 dict_add_string(dict, "scrollbarhighlight",
2579 wp->w_scrollbar_highlight);
2580 if (wp->w_thumb_highlight != NULL)
2581 dict_add_string(dict, "thumbhighlight", wp->w_thumb_highlight);
Bram Moolenaarae943152019-06-16 22:54:14 +02002582
Bram Moolenaara3fce622019-06-20 02:31:49 +02002583 // find the tabpage that holds this popup
2584 i = 1;
2585 FOR_ALL_TABPAGES(tp)
2586 {
2587 win_T *p;
2588
2589 for (p = tp->tp_first_popupwin; p != NULL; p = wp->w_next)
2590 if (p->w_id == id)
2591 break;
2592 if (p != NULL)
2593 break;
2594 ++i;
2595 }
2596 if (tp == NULL)
2597 i = -1; // must be global
2598 else if (tp == curtab)
2599 i = 0;
2600 dict_add_number(dict, "tabpage", i);
2601
Bram Moolenaarae943152019-06-16 22:54:14 +02002602 get_padding_border(dict, wp->w_popup_padding, "padding");
2603 get_padding_border(dict, wp->w_popup_border, "border");
2604 get_borderhighlight(dict, wp);
2605 get_borderchars(dict, wp);
2606 get_moved_list(dict, wp);
2607
2608 if (wp->w_filter_cb.cb_name != NULL)
2609 dict_add_callback(dict, "filter", &wp->w_filter_cb);
2610 if (wp->w_close_cb.cb_name != NULL)
2611 dict_add_callback(dict, "callback", &wp->w_close_cb);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002612
2613 for (i = 0; i < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
2614 ++i)
2615 if (wp->w_popup_pos == poppos_entries[i].pp_val)
2616 {
2617 dict_add_string(dict, "pos",
2618 (char_u *)poppos_entries[i].pp_name);
2619 break;
2620 }
2621
Bram Moolenaar2e62b562019-06-30 18:07:00 +02002622 dict_add_string(dict, "close", (char_u *)(
2623 wp->w_popup_close == POPCLOSE_BUTTON ? "button"
2624 : wp->w_popup_close == POPCLOSE_CLICK ? "click" : "none"));
2625
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002626# if defined(FEAT_TIMERS)
2627 dict_add_number(dict, "time", wp->w_popup_timer != NULL
2628 ? (long)wp->w_popup_timer->tr_interval : 0L);
2629# endif
Bram Moolenaarbc133542019-05-29 20:26:46 +02002630 }
2631}
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002632
2633 int
Bram Moolenaar8cdbd5b2019-06-16 15:50:45 +02002634error_if_popup_window()
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002635{
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002636 if (WIN_IS_POPUP(curwin))
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002637 {
2638 emsg(_("E994: Not allowed in a popup window"));
2639 return TRUE;
2640 }
2641 return FALSE;
2642}
2643
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002644/*
2645 * Reset all the POPF_HANDLED flags in global popup windows and popup windows
Bram Moolenaarfc06cbb2019-06-15 14:14:31 +02002646 * in the current tab page.
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002647 */
2648 void
2649popup_reset_handled()
2650{
2651 win_T *wp;
2652
2653 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2654 wp->w_popup_flags &= ~POPF_HANDLED;
2655 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
2656 wp->w_popup_flags &= ~POPF_HANDLED;
2657}
2658
2659/*
2660 * Find the next visible popup where POPF_HANDLED is not set.
2661 * Must have called popup_reset_handled() first.
2662 * When "lowest" is TRUE find the popup with the lowest zindex, otherwise the
2663 * popup with the highest zindex.
2664 */
2665 win_T *
2666find_next_popup(int lowest)
2667{
2668 win_T *wp;
2669 win_T *found_wp;
2670 int found_zindex;
2671
2672 found_zindex = lowest ? INT_MAX : 0;
2673 found_wp = NULL;
2674 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2675 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
2676 && (lowest ? wp->w_zindex < found_zindex
2677 : wp->w_zindex > found_zindex))
2678 {
2679 found_zindex = wp->w_zindex;
2680 found_wp = wp;
2681 }
2682 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
2683 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
2684 && (lowest ? wp->w_zindex < found_zindex
2685 : wp->w_zindex > found_zindex))
2686 {
2687 found_zindex = wp->w_zindex;
2688 found_wp = wp;
2689 }
2690
2691 if (found_wp != NULL)
2692 found_wp->w_popup_flags |= POPF_HANDLED;
2693 return found_wp;
2694}
2695
2696/*
2697 * Invoke the filter callback for window "wp" with typed character "c".
2698 * Uses the global "mod_mask" for modifiers.
2699 * Returns the return value of the filter.
2700 * Careful: The filter may make "wp" invalid!
2701 */
2702 static int
2703invoke_popup_filter(win_T *wp, int c)
2704{
2705 int res;
2706 typval_T rettv;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002707 typval_T argv[3];
2708 char_u buf[NUMBUFLEN];
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002709 linenr_T old_lnum = wp->w_cursor.lnum;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002710
Bram Moolenaara42d9452019-06-15 21:46:30 +02002711 // Emergency exit: CTRL-C closes the popup.
2712 if (c == Ctrl_C)
2713 {
Bram Moolenaar12034e22019-08-25 22:25:02 +02002714 popup_close_with_retval(wp, -1);
Bram Moolenaara42d9452019-06-15 21:46:30 +02002715 return 1;
2716 }
2717
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002718 argv[0].v_type = VAR_NUMBER;
2719 argv[0].vval.v_number = (varnumber_T)wp->w_id;
2720
2721 // Convert the number to a string, so that the function can use:
2722 // if a:c == "\<F2>"
2723 buf[special_to_buf(c, mod_mask, TRUE, buf)] = NUL;
2724 argv[1].v_type = VAR_STRING;
2725 argv[1].vval.v_string = vim_strsave(buf);
2726
2727 argv[2].v_type = VAR_UNKNOWN;
2728
Bram Moolenaara42d9452019-06-15 21:46:30 +02002729 // NOTE: The callback might close the popup, thus make "wp" invalid.
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002730 call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002731 if (win_valid_popup(wp) && old_lnum != wp->w_cursor.lnum)
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002732 popup_highlight_curline(wp);
2733
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002734 res = tv_get_number(&rettv);
2735 vim_free(argv[1].vval.v_string);
2736 clear_tv(&rettv);
2737 return res;
2738}
2739
2740/*
2741 * Called when "c" was typed: invoke popup filter callbacks.
2742 * Returns TRUE when the character was consumed,
2743 */
2744 int
2745popup_do_filter(int c)
2746{
2747 int res = FALSE;
Bram Moolenaara42d9452019-06-15 21:46:30 +02002748 win_T *wp;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002749
2750 popup_reset_handled();
2751
Bram Moolenaarf6396232019-08-24 19:36:00 +02002752 if (c == K_LEFTMOUSE)
2753 {
2754 int row = mouse_row;
2755 int col = mouse_col;
2756
2757 wp = mouse_find_win(&row, &col, FIND_POPUP);
2758 if (wp != NULL && popup_close_if_on_X(wp, row, col))
2759 return TRUE;
2760 }
2761
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002762 while (!res && (wp = find_next_popup(FALSE)) != NULL)
2763 if (wp->w_filter_cb.cb_name != NULL)
2764 res = invoke_popup_filter(wp, c);
2765
2766 return res;
2767}
2768
Bram Moolenaar3397f742019-06-02 18:40:06 +02002769/*
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02002770 * Return TRUE if there is a popup visible with a filter callback and the
2771 * "mapping" property off.
2772 */
2773 int
2774popup_no_mapping(void)
2775{
2776 int round;
2777 win_T *wp;
2778
2779 for (round = 1; round <= 2; ++round)
2780 for (wp = round == 1 ? first_popupwin : curtab->tp_first_popupwin;
2781 wp != NULL; wp = wp->w_next)
2782 if (wp->w_filter_cb.cb_name != NULL
2783 && (wp->w_popup_flags & (POPF_HIDDEN | POPF_MAPPING)) == 0)
2784 return TRUE;
2785 return FALSE;
2786}
2787
2788/*
Bram Moolenaar3397f742019-06-02 18:40:06 +02002789 * Called when the cursor moved: check if any popup needs to be closed if the
2790 * cursor moved far enough.
2791 */
2792 void
2793popup_check_cursor_pos()
2794{
2795 win_T *wp;
Bram Moolenaar3397f742019-06-02 18:40:06 +02002796
2797 popup_reset_handled();
2798 while ((wp = find_next_popup(TRUE)) != NULL)
2799 if (wp->w_popup_curwin != NULL
2800 && (curwin != wp->w_popup_curwin
2801 || curwin->w_cursor.lnum != wp->w_popup_lnum
2802 || curwin->w_cursor.col < wp->w_popup_mincol
2803 || curwin->w_cursor.col > wp->w_popup_maxcol))
Bram Moolenaar12034e22019-08-25 22:25:02 +02002804 popup_close_with_retval(wp, -1);
Bram Moolenaar3397f742019-06-02 18:40:06 +02002805}
2806
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002807/*
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002808 * Update "w_popup_mask_cells".
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002809 */
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002810 static void
2811popup_update_mask(win_T *wp, int width, int height)
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002812{
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002813 listitem_T *lio, *li;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002814 char_u *cells;
2815 int row, col;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002816
2817 if (wp->w_popup_mask == NULL)
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002818 return;
2819 if (wp->w_popup_mask_cells != NULL
2820 && wp->w_popup_mask_height == height
2821 && wp->w_popup_mask_width == width)
2822 return; // cache is still valid
2823
2824 vim_free(wp->w_popup_mask_cells);
2825 wp->w_popup_mask_cells = alloc_clear(width * height);
2826 if (wp->w_popup_mask_cells == NULL)
2827 return;
2828 cells = wp->w_popup_mask_cells;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002829
2830 for (lio = wp->w_popup_mask->lv_first; lio != NULL; lio = lio->li_next)
2831 {
2832 int cols, cole;
2833 int lines, linee;
2834
2835 li = lio->li_tv.vval.v_list->lv_first;
2836 cols = tv_get_number(&li->li_tv);
2837 if (cols < 0)
2838 cols = width + cols + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002839 li = li->li_next;
2840 cole = tv_get_number(&li->li_tv);
2841 if (cole < 0)
2842 cole = width + cole + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002843 li = li->li_next;
2844 lines = tv_get_number(&li->li_tv);
2845 if (lines < 0)
2846 lines = height + lines + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002847 li = li->li_next;
2848 linee = tv_get_number(&li->li_tv);
2849 if (linee < 0)
2850 linee = height + linee + 1;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002851
2852 for (row = lines - 1; row < linee && row < height; ++row)
2853 for (col = cols - 1; col < cole && col < width; ++col)
2854 cells[row * width + col] = 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002855 }
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002856}
2857
2858/*
2859 * Return TRUE if "col" / "line" matches with an entry in w_popup_mask.
2860 * "col" and "line" are screen coordinates.
2861 */
2862 static int
2863popup_masked(win_T *wp, int width, int height, int screencol, int screenline)
2864{
2865 int col = screencol - wp->w_wincol + wp->w_popup_leftoff;
2866 int line = screenline - wp->w_winrow;
2867
2868 return col >= 0 && col < width
2869 && line >= 0 && line < height
2870 && wp->w_popup_mask_cells[line * width + col];
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002871}
2872
2873/*
2874 * Set flags in popup_transparent[] for window "wp" to "val".
2875 */
2876 static void
2877update_popup_transparent(win_T *wp, int val)
2878{
2879 if (wp->w_popup_mask != NULL)
2880 {
2881 int width = popup_width(wp);
2882 int height = popup_height(wp);
2883 listitem_T *lio, *li;
2884 int cols, cole;
2885 int lines, linee;
2886 int col, line;
2887
2888 for (lio = wp->w_popup_mask->lv_first; lio != NULL; lio = lio->li_next)
2889 {
2890 li = lio->li_tv.vval.v_list->lv_first;
2891 cols = tv_get_number(&li->li_tv);
2892 if (cols < 0)
2893 cols = width + cols + 1;
2894 li = li->li_next;
2895 cole = tv_get_number(&li->li_tv);
2896 if (cole < 0)
2897 cole = width + cole + 1;
2898 li = li->li_next;
2899 lines = tv_get_number(&li->li_tv);
2900 if (lines < 0)
2901 lines = height + lines + 1;
2902 li = li->li_next;
2903 linee = tv_get_number(&li->li_tv);
2904 if (linee < 0)
2905 linee = height + linee + 1;
2906
2907 --cols;
Bram Moolenaard529ba52019-07-02 23:13:53 +02002908 cols -= wp->w_popup_leftoff;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02002909 if (cols < 0)
2910 cols = 0;
Bram Moolenaard529ba52019-07-02 23:13:53 +02002911 cole -= wp->w_popup_leftoff;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002912 --lines;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02002913 if (lines < 0)
2914 lines = 0;
Bram Moolenaarb4207472019-07-12 16:05:45 +02002915 for (line = lines; line < linee
2916 && line + wp->w_winrow < screen_Rows; ++line)
2917 for (col = cols; col < cole
2918 && col + wp->w_wincol < screen_Columns; ++col)
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002919 popup_transparent[(line + wp->w_winrow) * screen_Columns
2920 + col + wp->w_wincol] = val;
2921 }
2922 }
2923}
2924
2925/*
Bram Moolenaar12034e22019-08-25 22:25:02 +02002926 * Only called when popup window "wp" is hidden: If the window is positioned
2927 * next to a text property, and it is now visible, then unhide the popup.
2928 * We don't check if visible popups become hidden, that is done in
2929 * popup_adjust_position().
2930 * Return TRUE if the popup became unhidden.
2931 */
2932 static int
2933check_popup_unhidden(win_T *wp)
2934{
2935 if (wp->w_popup_prop_type > 0 && win_valid(wp->w_popup_prop_win))
2936 {
2937 textprop_T prop;
2938 linenr_T lnum;
2939
2940 if (find_visible_prop(wp->w_popup_prop_win,
2941 wp->w_popup_prop_type, wp->w_popup_prop_id,
2942 &prop, &lnum) == OK)
2943 {
2944 wp->w_popup_flags &= ~POPF_HIDDEN;
2945 ++wp->w_buffer->b_nwindows;
2946 wp->w_popup_prop_topline = 0; // force repositioning
2947 return TRUE;
2948 }
2949 }
2950 return FALSE;
2951}
2952
2953/*
2954 * Return TRUE if popup_adjust_position() needs to be called for "wp".
2955 * That is when the buffer in the popup was changed, or the popup is following
2956 * a textprop and the referenced buffer was changed.
2957 */
2958 static int
2959popup_need_position_adjust(win_T *wp)
2960{
2961 if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
2962 return TRUE;
2963 if (win_valid(wp->w_popup_prop_win))
2964 return wp->w_popup_prop_changedtick
2965 != CHANGEDTICK(wp->w_popup_prop_win->w_buffer)
2966 || wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline;
2967 return FALSE;
2968}
2969
2970/*
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002971 * Update "popup_mask" if needed.
2972 * Also recomputes the popup size and positions.
2973 * Also updates "popup_visible".
2974 * Also marks window lines for redrawing.
2975 */
2976 void
2977may_update_popup_mask(int type)
2978{
2979 win_T *wp;
2980 short *mask;
2981 int line, col;
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002982 int redraw_all_popups = FALSE;
2983 int redrawing_all_win;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002984
2985 // Need to recompute when switching tabs.
2986 // Also recompute when the type is CLEAR or NOT_VALID, something basic
2987 // (such as the screen size) must have changed.
2988 if (popup_mask_tab != curtab || type >= NOT_VALID)
2989 {
2990 popup_mask_refresh = TRUE;
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002991 redraw_all_popups = TRUE;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002992 }
Bram Moolenaar12034e22019-08-25 22:25:02 +02002993
2994 // Check if any popup window buffer has changed and if any popup connected
2995 // to a text property has become visible.
2996 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2997 if (wp->w_popup_flags & POPF_HIDDEN)
2998 popup_mask_refresh |= check_popup_unhidden(wp);
2999 else if (popup_need_position_adjust(wp))
3000 popup_mask_refresh = TRUE;
3001 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
3002 if (wp->w_popup_flags & POPF_HIDDEN)
3003 popup_mask_refresh |= check_popup_unhidden(wp);
3004 else if (popup_need_position_adjust(wp))
3005 popup_mask_refresh = TRUE;
3006
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003007 if (!popup_mask_refresh)
Bram Moolenaar12034e22019-08-25 22:25:02 +02003008 return;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003009
3010 // Need to update the mask, something has changed.
3011 popup_mask_refresh = FALSE;
3012 popup_mask_tab = curtab;
3013 popup_visible = FALSE;
3014
Bram Moolenaare9a891f2019-08-24 15:26:24 +02003015 // If redrawing all windows, just update "popup_mask".
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003016 // If redrawing only what is needed, update "popup_mask_next" and then
3017 // compare with "popup_mask" to see what changed.
Bram Moolenaare9a891f2019-08-24 15:26:24 +02003018 redrawing_all_win = TRUE;
3019 FOR_ALL_WINDOWS(wp)
3020 if (wp->w_redr_type < SOME_VALID)
3021 redrawing_all_win = FALSE;
3022 if (redrawing_all_win)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003023 mask = popup_mask;
3024 else
3025 mask = popup_mask_next;
3026 vim_memset(mask, 0, screen_Rows * screen_Columns * sizeof(short));
3027
3028 // Find the window with the lowest zindex that hasn't been handled yet,
3029 // so that the window with a higher zindex overwrites the value in
3030 // popup_mask.
3031 popup_reset_handled();
3032 while ((wp = find_next_popup(TRUE)) != NULL)
3033 {
Bram Moolenaard529ba52019-07-02 23:13:53 +02003034 int width;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02003035 int height;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003036
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003037 popup_visible = TRUE;
3038
Bram Moolenaar12034e22019-08-25 22:25:02 +02003039 // Recompute the position if the text changed. It may make the popup
3040 // hidden if it's attach to a text property that is no longer visible.
3041 if (redraw_all_popups || popup_need_position_adjust(wp))
3042 {
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003043 popup_adjust_position(wp);
Bram Moolenaar12034e22019-08-25 22:25:02 +02003044 if (wp->w_popup_flags & POPF_HIDDEN)
3045 continue;
3046 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003047
Bram Moolenaare865dcb2019-07-26 22:15:50 +02003048 width = popup_width(wp);
Bram Moolenaard529ba52019-07-02 23:13:53 +02003049 height = popup_height(wp);
Bram Moolenaare865dcb2019-07-26 22:15:50 +02003050 popup_update_mask(wp, width, height);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003051 for (line = wp->w_winrow;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003052 line < wp->w_winrow + height && line < screen_Rows; ++line)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003053 for (col = wp->w_wincol;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02003054 col < wp->w_wincol + width - wp->w_popup_leftoff
3055 && col < screen_Columns; ++col)
3056 if (wp->w_popup_mask_cells == NULL
3057 || !popup_masked(wp, width, height, col, line))
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003058 mask[line * screen_Columns + col] = wp->w_zindex;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003059 }
3060
3061 // Only check which lines are to be updated if not already
3062 // updating all lines.
3063 if (mask == popup_mask_next)
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02003064 {
3065 int *plines_cache = ALLOC_CLEAR_MULT(int, Rows);
3066 win_T *prev_wp = NULL;
3067
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003068 for (line = 0; line < screen_Rows; ++line)
3069 {
3070 int col_done = 0;
3071
3072 for (col = 0; col < screen_Columns; ++col)
3073 {
3074 int off = line * screen_Columns + col;
3075
3076 if (popup_mask[off] != popup_mask_next[off])
3077 {
3078 popup_mask[off] = popup_mask_next[off];
3079
3080 if (line >= cmdline_row)
3081 {
3082 // the command line needs to be cleared if text below
3083 // the popup is now visible.
3084 if (!msg_scrolled && popup_mask_next[off] == 0)
3085 clear_cmdline = TRUE;
3086 }
3087 else if (col >= col_done)
3088 {
3089 linenr_T lnum;
3090 int line_cp = line;
3091 int col_cp = col;
3092
3093 // The screen position "line" / "col" needs to be
3094 // redrawn. Figure out what window that is and update
3095 // w_redraw_top and w_redr_bot. Only needs to be done
3096 // once for each window line.
3097 wp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP);
3098 if (wp != NULL)
3099 {
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02003100 if (wp != prev_wp)
3101 {
3102 vim_memset(plines_cache, 0, sizeof(int) * Rows);
3103 prev_wp = wp;
3104 }
3105
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003106 if (line_cp >= wp->w_height)
3107 // In (or below) status line
3108 wp->w_redr_status = TRUE;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003109 else
Bram Moolenaare9a891f2019-08-24 15:26:24 +02003110 {
3111 // compute the position in the buffer line from
3112 // the position in the window
3113 mouse_comp_pos(wp, &line_cp, &col_cp,
3114 &lnum, plines_cache);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003115 redrawWinline(wp, lnum);
Bram Moolenaare9a891f2019-08-24 15:26:24 +02003116 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003117
3118 // This line is going to be redrawn, no need to
3119 // check until the right side of the window.
3120 col_done = wp->w_wincol + wp->w_width - 1;
3121 }
3122 }
3123 }
3124 }
3125 }
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02003126
3127 vim_free(plines_cache);
3128 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003129}
3130
3131/*
3132 * Return a string of "len" spaces in IObuff.
3133 */
3134 static char_u *
3135get_spaces(int len)
3136{
3137 vim_memset(IObuff, ' ', (size_t)len);
3138 IObuff[len] = NUL;
3139 return IObuff;
3140}
3141
3142/*
3143 * Update popup windows. They are drawn on top of normal windows.
3144 * "win_update" is called for each popup window, lowest zindex first.
3145 */
3146 void
3147update_popups(void (*win_update)(win_T *wp))
3148{
3149 win_T *wp;
3150 int top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003151 int left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003152 int total_width;
3153 int total_height;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003154 int top_padding;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003155 int popup_attr;
3156 int border_attr[4];
3157 int border_char[8];
3158 char_u buf[MB_MAXBYTES];
3159 int row;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003160 int wincol;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003161 int padcol = 0;
3162 int padwidth = 0;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003163 int i;
Bram Moolenaar6efd76a2019-06-26 04:06:57 +02003164 int sb_thumb_top = 0;
3165 int sb_thumb_height = 0;
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02003166 int attr_scroll = 0;
3167 int attr_thumb = 0;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003168
3169 // Find the window with the lowest zindex that hasn't been updated yet,
3170 // so that the window with a higher zindex is drawn later, thus goes on
3171 // top.
3172 popup_reset_handled();
3173 while ((wp = find_next_popup(TRUE)) != NULL)
3174 {
3175 // This drawing uses the zindex of the popup window, so that it's on
3176 // top of the text but doesn't draw when another popup with higher
3177 // zindex is on top of the character.
3178 screen_zindex = wp->w_zindex;
3179
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003180 // Set flags in popup_transparent[] for masked cells.
3181 update_popup_transparent(wp, 1);
3182
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003183 // adjust w_winrow and w_wincol for border and padding, since
3184 // win_update() doesn't handle them.
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003185 top_off = popup_top_extra(wp);
Bram Moolenaard529ba52019-07-02 23:13:53 +02003186 left_extra = wp->w_popup_padding[3] + wp->w_popup_border[3]
3187 - wp->w_popup_leftoff;
3188 if (wp->w_wincol + left_extra < 0)
3189 left_extra = -wp->w_wincol;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003190 wp->w_winrow += top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003191 wp->w_wincol += left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003192
Bram Moolenaarc2a43162019-06-26 01:03:53 +02003193 // Draw the popup text, unless it's off screen.
3194 if (wp->w_winrow < screen_Rows && wp->w_wincol < screen_Columns)
Bram Moolenaar9e67b6a2019-08-30 17:34:08 +02003195 {
Bram Moolenaarc2a43162019-06-26 01:03:53 +02003196 win_update(wp);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003197
Bram Moolenaar9e67b6a2019-08-30 17:34:08 +02003198 // move the cursor into the visible lines, otherwise executing
3199 // commands with win_execute() may cause the text to jump.
3200 if (wp->w_cursor.lnum < wp->w_topline)
3201 wp->w_cursor.lnum = wp->w_topline;
3202 else if (wp->w_cursor.lnum >= wp->w_botline)
3203 wp->w_cursor.lnum = wp->w_botline - 1;
3204 }
3205
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003206 wp->w_winrow -= top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003207 wp->w_wincol -= left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003208
Bram Moolenaard529ba52019-07-02 23:13:53 +02003209 total_width = popup_width(wp);
3210 total_height = popup_height(wp);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003211 popup_attr = get_wcr_attr(wp);
3212
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02003213 if (wp->w_winrow + total_height > cmdline_row)
3214 wp->w_popup_flags |= POPF_ON_CMDLINE;
3215 else
3216 wp->w_popup_flags &= ~POPF_ON_CMDLINE;
3217
3218
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003219 // We can only use these line drawing characters when 'encoding' is
3220 // "utf-8" and 'ambiwidth' is "single".
3221 if (enc_utf8 && *p_ambw == 's')
3222 {
3223 border_char[0] = border_char[2] = 0x2550;
3224 border_char[1] = border_char[3] = 0x2551;
3225 border_char[4] = 0x2554;
3226 border_char[5] = 0x2557;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02003227 border_char[6] = (wp->w_popup_flags & POPF_RESIZE)
3228 ? 0x21f2 : 0x255d;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003229 border_char[7] = 0x255a;
3230 }
3231 else
3232 {
3233 border_char[0] = border_char[2] = '-';
3234 border_char[1] = border_char[3] = '|';
3235 for (i = 4; i < 8; ++i)
3236 border_char[i] = '+';
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02003237 if (wp->w_popup_flags & POPF_RESIZE)
3238 border_char[6] = '@';
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003239 }
3240 for (i = 0; i < 8; ++i)
3241 if (wp->w_border_char[i] != 0)
3242 border_char[i] = wp->w_border_char[i];
3243
3244 for (i = 0; i < 4; ++i)
3245 {
3246 border_attr[i] = popup_attr;
3247 if (wp->w_border_highlight[i] != NULL)
3248 border_attr[i] = syn_name2attr(wp->w_border_highlight[i]);
3249 }
3250
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003251 wincol = wp->w_wincol - wp->w_popup_leftoff;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003252 top_padding = wp->w_popup_padding[0];
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003253 if (wp->w_popup_border[0] > 0)
3254 {
3255 // top border
3256 screen_fill(wp->w_winrow, wp->w_winrow + 1,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003257 wincol < 0 ? 0 : wincol, wincol + total_width,
3258 wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003259 ? border_char[4] : border_char[0],
3260 border_char[0], border_attr[0]);
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003261 if (wp->w_popup_border[1] > 0 && wp->w_popup_rightoff == 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003262 {
3263 buf[mb_char2bytes(border_char[5], buf)] = NUL;
3264 screen_puts(buf, wp->w_winrow,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003265 wincol + total_width - 1, border_attr[1]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003266 }
3267 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003268 else if (wp->w_popup_padding[0] == 0 && popup_top_extra(wp) > 0)
3269 top_padding = 1;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003270
Bram Moolenaard529ba52019-07-02 23:13:53 +02003271 if (top_padding > 0 || wp->w_popup_padding[2] > 0)
3272 {
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003273 padcol = wincol + wp->w_popup_border[3];
Bram Moolenaard529ba52019-07-02 23:13:53 +02003274 padwidth = wp->w_wincol + total_width - wp->w_popup_border[1]
3275 - wp->w_has_scrollbar;
3276 if (padcol < 0)
3277 {
3278 padwidth += padcol;
3279 padcol = 0;
3280 }
3281 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003282 if (top_padding > 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003283 {
3284 // top padding
3285 row = wp->w_winrow + wp->w_popup_border[0];
Bram Moolenaard529ba52019-07-02 23:13:53 +02003286 screen_fill(row, row + top_padding, padcol, padwidth,
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003287 ' ', ' ', popup_attr);
3288 }
3289
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003290 // Title goes on top of border or padding.
3291 if (wp->w_popup_title != NULL)
Bram Moolenaar5d458a72019-08-04 21:12:15 +02003292 {
3293 int len = (int)STRLEN(wp->w_popup_title) + 1;
3294 char_u *title = alloc(len);
3295
3296 trunc_string(wp->w_popup_title, title, total_width - 2, len);
3297 screen_puts(title, wp->w_winrow, wp->w_wincol + 1,
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003298 wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
Bram Moolenaar5d458a72019-08-04 21:12:15 +02003299 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003300
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003301 // Compute scrollbar thumb position and size.
3302 if (wp->w_has_scrollbar)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003303 {
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003304 linenr_T linecount = wp->w_buffer->b_ml.ml_line_count;
3305
Bram Moolenaar68acb412019-06-26 03:40:36 +02003306 sb_thumb_height = (wp->w_height * wp->w_height + linecount / 2)
3307 / linecount;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003308 if (sb_thumb_height == 0)
3309 sb_thumb_height = 1;
Bram Moolenaar437a7462019-07-05 20:17:22 +02003310 if (linecount <= wp->w_height)
3311 // it just fits, avoid divide by zero
3312 sb_thumb_top = 0;
3313 else
3314 sb_thumb_top = (wp->w_topline - 1
3315 + (linecount / wp->w_height) / 2)
Bram Moolenaar68acb412019-06-26 03:40:36 +02003316 * (wp->w_height - sb_thumb_height)
3317 / (linecount - wp->w_height);
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02003318 if (wp->w_scrollbar_highlight != NULL)
3319 attr_scroll = syn_name2attr(wp->w_scrollbar_highlight);
3320 else
3321 attr_scroll = highlight_attr[HLF_PSB];
3322 if (wp->w_thumb_highlight != NULL)
3323 attr_thumb = syn_name2attr(wp->w_thumb_highlight);
3324 else
3325 attr_thumb = highlight_attr[HLF_PST];
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003326 }
3327
3328 for (i = wp->w_popup_border[0];
3329 i < total_height - wp->w_popup_border[2]; ++i)
3330 {
Bram Moolenaard529ba52019-07-02 23:13:53 +02003331 int pad_left;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003332 // left and right padding only needed next to the body
3333 int do_padding =
3334 i >= wp->w_popup_border[0] + wp->w_popup_padding[0]
3335 && i < total_height - wp->w_popup_border[2]
3336 - wp->w_popup_padding[2];
3337
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003338 row = wp->w_winrow + i;
3339
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003340 // left border
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003341 if (wp->w_popup_border[3] > 0 && wincol >= 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003342 {
3343 buf[mb_char2bytes(border_char[3], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003344 screen_puts(buf, row, wincol, border_attr[3]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003345 }
Bram Moolenaard529ba52019-07-02 23:13:53 +02003346 if (do_padding && wp->w_popup_padding[3] > 0)
3347 {
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003348 int col = wincol + wp->w_popup_border[3];
3349
Bram Moolenaard529ba52019-07-02 23:13:53 +02003350 // left padding
Bram Moolenaard529ba52019-07-02 23:13:53 +02003351 pad_left = wp->w_popup_padding[3];
3352 if (col < 0)
3353 {
3354 pad_left += col;
3355 col = 0;
3356 }
3357 if (pad_left > 0)
3358 screen_puts(get_spaces(pad_left), row, col, popup_attr);
3359 }
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003360 // scrollbar
3361 if (wp->w_has_scrollbar)
3362 {
3363 int line = i - top_off;
3364 int scroll_col = wp->w_wincol + total_width - 1
3365 - wp->w_popup_border[1];
3366
3367 if (line >= 0 && line < wp->w_height)
3368 screen_putchar(' ', row, scroll_col,
3369 line >= sb_thumb_top
3370 && line < sb_thumb_top + sb_thumb_height
3371 ? attr_thumb : attr_scroll);
3372 else
3373 screen_putchar(' ', row, scroll_col, popup_attr);
3374 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003375 // right border
3376 if (wp->w_popup_border[1] > 0)
3377 {
3378 buf[mb_char2bytes(border_char[1], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003379 screen_puts(buf, row, wincol + total_width - 1, border_attr[1]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003380 }
3381 // right padding
Bram Moolenaard529ba52019-07-02 23:13:53 +02003382 if (do_padding && wp->w_popup_padding[1] > 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003383 screen_puts(get_spaces(wp->w_popup_padding[1]), row,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003384 wincol + wp->w_popup_border[3]
Bram Moolenaard529ba52019-07-02 23:13:53 +02003385 + wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol,
3386 popup_attr);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003387 }
3388
3389 if (wp->w_popup_padding[2] > 0)
3390 {
3391 // bottom padding
3392 row = wp->w_winrow + wp->w_popup_border[0]
3393 + wp->w_popup_padding[0] + wp->w_height;
3394 screen_fill(row, row + wp->w_popup_padding[2],
Bram Moolenaard529ba52019-07-02 23:13:53 +02003395 padcol, padwidth, ' ', ' ', popup_attr);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003396 }
3397
3398 if (wp->w_popup_border[2] > 0)
3399 {
3400 // bottom border
3401 row = wp->w_winrow + total_height - 1;
3402 screen_fill(row , row + 1,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003403 wincol < 0 ? 0 : wincol,
3404 wincol + total_width,
3405 wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003406 ? border_char[7] : border_char[2],
3407 border_char[2], border_attr[2]);
3408 if (wp->w_popup_border[1] > 0)
3409 {
3410 buf[mb_char2bytes(border_char[6], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003411 screen_puts(buf, row, wincol + total_width - 1, border_attr[2]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003412 }
3413 }
3414
Bram Moolenaar2e62b562019-06-30 18:07:00 +02003415 if (wp->w_popup_close == POPCLOSE_BUTTON)
3416 {
3417 // close button goes on top of anything at the top-right corner
3418 buf[mb_char2bytes('X', buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003419 screen_puts(buf, wp->w_winrow, wincol + total_width - 1,
Bram Moolenaar2e62b562019-06-30 18:07:00 +02003420 wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
3421 }
3422
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003423 update_popup_transparent(wp, 0);
3424
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003425 // Back to the normal zindex.
3426 screen_zindex = 0;
3427 }
3428}
3429
Bram Moolenaar75a1a942019-06-20 03:45:36 +02003430/*
3431 * Mark references in callbacks of one popup window.
3432 */
3433 static int
3434set_ref_in_one_popup(win_T *wp, int copyID)
3435{
3436 int abort = FALSE;
3437 typval_T tv;
3438
3439 if (wp->w_close_cb.cb_partial != NULL)
3440 {
3441 tv.v_type = VAR_PARTIAL;
3442 tv.vval.v_partial = wp->w_close_cb.cb_partial;
3443 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
3444 }
3445 if (wp->w_filter_cb.cb_partial != NULL)
3446 {
3447 tv.v_type = VAR_PARTIAL;
3448 tv.vval.v_partial = wp->w_filter_cb.cb_partial;
3449 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
3450 }
Bram Moolenaar7be3ab22019-06-23 01:46:15 +02003451 abort = abort || set_ref_in_list(wp->w_popup_mask, copyID);
Bram Moolenaar75a1a942019-06-20 03:45:36 +02003452 return abort;
3453}
3454
3455/*
3456 * Set reference in callbacks of popup windows.
3457 */
3458 int
3459set_ref_in_popups(int copyID)
3460{
3461 int abort = FALSE;
3462 win_T *wp;
3463 tabpage_T *tp;
3464
3465 for (wp = first_popupwin; !abort && wp != NULL; wp = wp->w_next)
3466 abort = abort || set_ref_in_one_popup(wp, copyID);
3467
3468 FOR_ALL_TABPAGES(tp)
3469 {
3470 for (wp = tp->tp_first_popupwin; !abort && wp != NULL; wp = wp->w_next)
3471 abort = abort || set_ref_in_one_popup(wp, copyID);
3472 if (abort)
3473 break;
3474 }
3475 return abort;
3476}
Bram Moolenaar79648732019-07-18 21:43:07 +02003477
3478/*
3479 * Find an existing popup used as the preview window, in the current tab page.
3480 * Return NULL if not found.
3481 */
3482 win_T *
3483popup_find_preview_window(void)
3484{
3485 win_T *wp;
3486
3487 // Preview window popup is always local to tab page.
3488 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
3489 if (wp->w_p_pvw)
3490 return wp;
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003491 return NULL;
3492}
3493
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003494 int
3495popup_is_popup(win_T *wp)
3496{
3497 return wp->w_popup_flags != 0;
3498}
3499
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003500#if defined(FEAT_QUICKFIX) || defined(PROTO)
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003501/*
3502 * Find an existing popup used as the info window, in the current tab page.
3503 * Return NULL if not found.
3504 */
3505 win_T *
3506popup_find_info_window(void)
3507{
3508 win_T *wp;
3509
3510 // info window popup is always local to tab page.
3511 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
3512 if (wp->w_popup_flags & POPF_INFO)
3513 return wp;
3514 return NULL;
3515}
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003516#endif
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003517
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003518 void
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003519f_popup_findinfo(typval_T *argvars UNUSED, typval_T *rettv)
3520{
3521 win_T *wp = popup_find_info_window();
3522
3523 rettv->vval.v_number = wp == NULL ? 0 : wp->w_id;
3524}
3525
3526 void
3527f_popup_findpreview(typval_T *argvars UNUSED, typval_T *rettv)
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003528{
3529 win_T *wp = popup_find_preview_window();
3530
3531 rettv->vval.v_number = wp == NULL ? 0 : wp->w_id;
Bram Moolenaar79648732019-07-18 21:43:07 +02003532}
3533
Bram Moolenaar79648732019-07-18 21:43:07 +02003534/*
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003535 * Create a popup to be used as the preview or info window.
Bram Moolenaar79648732019-07-18 21:43:07 +02003536 * NOTE: this makes the popup the current window, so that the file can be
3537 * edited. However, it must not remain to be the current window, the caller
3538 * must make sure of that.
3539 */
3540 int
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003541popup_create_preview_window(int info)
Bram Moolenaar79648732019-07-18 21:43:07 +02003542{
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003543 win_T *wp = popup_create(NULL, NULL, info ? TYPE_INFO : TYPE_PREVIEW);
Bram Moolenaar79648732019-07-18 21:43:07 +02003544
3545 if (wp == NULL)
3546 return FAIL;
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003547 if (info)
3548 wp->w_popup_flags |= POPF_INFO;
3549 else
3550 wp->w_p_pvw = TRUE;
Bram Moolenaar79648732019-07-18 21:43:07 +02003551
3552 // Set the width to a reasonable value, so that w_topline can be computed.
3553 if (wp->w_minwidth > 0)
3554 wp->w_width = wp->w_minwidth;
3555 else if (wp->w_maxwidth > 0)
3556 wp->w_width = wp->w_maxwidth;
3557 else
3558 wp->w_width = curwin->w_width;
3559
3560 // Will switch to another buffer soon, dummy one can be wiped.
3561 wp->w_buffer->b_locked = FALSE;
3562
3563 win_enter(wp, FALSE);
3564 return OK;
3565}
3566
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003567#if defined(FEAT_QUICKFIX) || defined(PROTO)
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003568/*
3569 * Close any preview popup.
3570 */
Bram Moolenaar79648732019-07-18 21:43:07 +02003571 void
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003572popup_close_preview(void)
Bram Moolenaar79648732019-07-18 21:43:07 +02003573{
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003574 win_T *wp = popup_find_preview_window();
Bram Moolenaar79648732019-07-18 21:43:07 +02003575
3576 if (wp != NULL)
Bram Moolenaar12034e22019-08-25 22:25:02 +02003577 popup_close_with_retval(wp, -1);
Bram Moolenaar79648732019-07-18 21:43:07 +02003578}
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003579
3580/*
3581 * Hide the info popup.
3582 */
3583 void
3584popup_hide_info(void)
3585{
3586 win_T *wp = popup_find_info_window();
3587
3588 if (wp != NULL)
3589 popup_hide(wp);
3590}
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003591#endif
Bram Moolenaar79648732019-07-18 21:43:07 +02003592
Bram Moolenaar90f3e7a2019-08-01 22:40:44 +02003593/*
Bram Moolenaar12034e22019-08-25 22:25:02 +02003594 * Close any popup for a text property associated with "win".
3595 * Return TRUE if a popup was closed.
3596 */
3597 int
3598popup_win_closed(win_T *win)
3599{
Bram Moolenaar1fb08312019-08-29 20:02:11 +02003600 int round;
3601 win_T *wp;
3602 win_T *next;
3603 int ret = FALSE;
Bram Moolenaar12034e22019-08-25 22:25:02 +02003604
Bram Moolenaar1fb08312019-08-29 20:02:11 +02003605 for (round = 1; round <= 2; ++round)
3606 for (wp = round == 1 ? first_popupwin : curtab->tp_first_popupwin;
3607 wp != NULL; wp = next)
Bram Moolenaar12034e22019-08-25 22:25:02 +02003608 {
Bram Moolenaar1fb08312019-08-29 20:02:11 +02003609 next = wp->w_next;
3610 if (wp->w_popup_prop_win == win)
3611 {
3612 popup_close_with_retval(wp, -1);
3613 ret = TRUE;
3614 }
Bram Moolenaar12034e22019-08-25 22:25:02 +02003615 }
Bram Moolenaar1fb08312019-08-29 20:02:11 +02003616 return ret;
Bram Moolenaar12034e22019-08-25 22:25:02 +02003617}
3618
3619/*
Bram Moolenaar90f3e7a2019-08-01 22:40:44 +02003620 * Set the title of the popup window to the file name.
3621 */
3622 void
3623popup_set_title(win_T *wp)
3624{
3625 if (wp->w_buffer->b_fname != NULL)
3626 {
3627 char_u dirname[MAXPATHL];
3628 size_t len;
3629
3630 mch_dirname(dirname, MAXPATHL);
3631 shorten_buf_fname(wp->w_buffer, dirname, FALSE);
3632
3633 vim_free(wp->w_popup_title);
3634 len = STRLEN(wp->w_buffer->b_fname) + 3;
3635 wp->w_popup_title = alloc((int)len);
3636 if (wp->w_popup_title != NULL)
3637 vim_snprintf((char *)wp->w_popup_title, len, " %s ",
3638 wp->w_buffer->b_fname);
3639 redraw_win_later(wp, VALID);
3640 }
3641}
3642
3643/*
3644 * If there is a preview window, update the title.
3645 * Used after changing directory.
3646 */
3647 void
3648popup_update_preview_title(void)
3649{
3650 win_T *wp = popup_find_preview_window();
3651
3652 if (wp != NULL)
3653 popup_set_title(wp);
3654}
3655
Bram Moolenaar4d784b22019-05-25 19:51:39 +02003656#endif // FEAT_TEXT_PROP