blob: 7aef2735842499c77be12e7abc0d352735a454d0 [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".
36 */
37 static int
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020038popup_options_one(dict_T *dict, char_u *key)
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020039{
40 dictitem_T *di;
41 char_u *val;
42 char_u *s;
43 char_u *endp;
44 int n = 0;
45
46 di = dict_find(dict, key, -1);
47 if (di == NULL)
48 return 0;
49
50 val = tv_get_string(&di->di_tv);
51 if (STRNCMP(val, "cursor", 6) != 0)
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020052 return dict_get_number_check(dict, key);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020053
54 setcursor_mayforce(TRUE);
55 s = val + 6;
56 if (*s != NUL)
57 {
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020058 endp = s;
59 if (*skipwhite(s) == '+' || *skipwhite(s) == '-')
60 n = strtol((char *)s, (char **)&endp, 10);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020061 if (endp != NULL && *skipwhite(endp) != NUL)
62 {
63 semsg(_(e_invexpr2), val);
64 return 0;
65 }
66 }
67
68 if (STRCMP(key, "line") == 0)
69 n = screen_screenrow() + 1 + n;
70 else // "col"
71 n = screen_screencol() + 1 + n;
72
73 if (n < 1)
74 n = 1;
75 return n;
76}
77
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020078 static void
79get_pos_options(win_T *wp, dict_T *dict)
80{
81 char_u *str;
82 int nr;
Bram Moolenaar711d02c2019-06-28 04:06:50 +020083 dictitem_T *di;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020084
85 nr = popup_options_one(dict, (char_u *)"line");
86 if (nr > 0)
87 wp->w_wantline = nr;
88 nr = popup_options_one(dict, (char_u *)"col");
89 if (nr > 0)
90 wp->w_wantcol = nr;
91
Bram Moolenaar711d02c2019-06-28 04:06:50 +020092 di = dict_find(dict, (char_u *)"fixed", -1);
93 if (di != NULL)
94 wp->w_popup_fixed = dict_get_number(dict, (char_u *)"fixed") != 0;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +020095
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020096 str = dict_get_string(dict, (char_u *)"pos", FALSE);
97 if (str != NULL)
98 {
99 for (nr = 0;
100 nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
101 ++nr)
102 if (STRCMP(str, poppos_entries[nr].pp_name) == 0)
103 {
104 wp->w_popup_pos = poppos_entries[nr].pp_val;
105 nr = -1;
106 break;
107 }
108 if (nr != -1)
109 semsg(_(e_invarg2), str);
110 }
111}
112
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200113 static void
Bram Moolenaarae943152019-06-16 22:54:14 +0200114set_padding_border(dict_T *dict, int *array, char *name, int max_val)
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200115{
116 dictitem_T *di;
117
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200118 di = dict_find(dict, (char_u *)name, -1);
119 if (di != NULL)
120 {
121 if (di->di_tv.v_type != VAR_LIST)
122 emsg(_(e_listreq));
123 else
124 {
125 list_T *list = di->di_tv.vval.v_list;
126 listitem_T *li;
127 int i;
128 int nr;
129
130 for (i = 0; i < 4; ++i)
131 array[i] = 1;
132 if (list != NULL)
133 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
134 ++i, li = li->li_next)
135 {
136 nr = (int)tv_get_number(&li->li_tv);
137 if (nr >= 0)
138 array[i] = nr > max_val ? max_val : nr;
139 }
140 }
141 }
142}
143
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200144/*
Bram Moolenaar17627312019-06-02 19:53:44 +0200145 * Used when popup options contain "moved": set default moved values.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200146 */
147 static void
Bram Moolenaar17627312019-06-02 19:53:44 +0200148set_moved_values(win_T *wp)
149{
150 wp->w_popup_curwin = curwin;
151 wp->w_popup_lnum = curwin->w_cursor.lnum;
152 wp->w_popup_mincol = curwin->w_cursor.col;
153 wp->w_popup_maxcol = curwin->w_cursor.col;
154}
155
156/*
157 * Used when popup options contain "moved" with "word" or "WORD".
158 */
159 static void
160set_moved_columns(win_T *wp, int flags)
161{
162 char_u *ptr;
163 int len = find_ident_under_cursor(&ptr, flags | FIND_NOERROR);
164
165 if (len > 0)
166 {
167 wp->w_popup_mincol = (int)(ptr - ml_get_curline());
168 wp->w_popup_maxcol = wp->w_popup_mincol + len - 1;
169 }
170}
171
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200172/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200173 * Used when popup options contain "mousemoved": set default moved values.
174 */
175 static void
176set_mousemoved_values(win_T *wp)
177{
178 wp->w_popup_mouse_row = mouse_row;
179 wp->w_popup_mouse_mincol = mouse_col;
180 wp->w_popup_mouse_maxcol = mouse_col;
181}
182
183/*
184 * Used when popup options contain "moved" with "word" or "WORD".
185 */
186 static void
187set_mousemoved_columns(win_T *wp, int flags)
188{
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200189 win_T *textwp;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200190 char_u *text;
191 int col;
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200192 pos_T pos;
193 colnr_T mcol;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200194
195 if (find_word_under_cursor(mouse_row, mouse_col, TRUE, flags,
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200196 &textwp, &pos.lnum, &text, NULL, &col) == OK)
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200197 {
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200198 // convert text column to mouse column
199 pos.col = col;
200 pos.coladd = 0;
201 getvcol(textwp, &pos, &mcol, NULL, NULL);
202 wp->w_popup_mouse_mincol = mcol;
203
Bram Moolenaar10727682019-07-12 13:59:20 +0200204 pos.col = col + (colnr_T)STRLEN(text) - 1;
Bram Moolenaarb05caa72019-07-10 21:55:54 +0200205 getvcol(textwp, &pos, NULL, NULL, &mcol);
206 wp->w_popup_mouse_maxcol = mcol;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200207 vim_free(text);
208 }
209}
210
211/*
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200212 * Return TRUE if "row"/"col" is on the border of the popup.
213 * The values are relative to the top-left corner.
214 */
215 int
216popup_on_border(win_T *wp, int row, int col)
217{
218 return (row == 0 && wp->w_popup_border[0] > 0)
219 || (row == popup_height(wp) - 1 && wp->w_popup_border[2] > 0)
220 || (col == 0 && wp->w_popup_border[3] > 0)
221 || (col == popup_width(wp) - 1 && wp->w_popup_border[1] > 0);
222}
223
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200224/*
Bram Moolenaarf6396232019-08-24 19:36:00 +0200225 * Return TRUE and close the popup if "row"/"col" is on the "X" button of the
226 * popup and w_popup_close is POPCLOSE_BUTTON.
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200227 * The values are relative to the top-left corner.
Bram Moolenaarf6396232019-08-24 19:36:00 +0200228 * Caller should check the left mouse button was clicked.
229 * Return TRUE if the popup was closed.
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200230 */
231 int
Bram Moolenaarf6396232019-08-24 19:36:00 +0200232popup_close_if_on_X(win_T *wp, int row, int col)
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200233{
Bram Moolenaarf6396232019-08-24 19:36:00 +0200234 if (wp->w_popup_close == POPCLOSE_BUTTON
235 && row == 0 && col == popup_width(wp) - 1)
236 {
237 popup_close_for_mouse_click(wp);
238 return TRUE;
239 }
240 return FALSE;
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200241}
242
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200243// Values set when dragging a popup window starts.
244static int drag_start_row;
245static int drag_start_col;
246static int drag_start_wantline;
247static int drag_start_wantcol;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200248static int drag_on_resize_handle;
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200249
250/*
251 * Mouse down on border of popup window: start dragging it.
252 * Uses mouse_col and mouse_row.
253 */
254 void
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200255popup_start_drag(win_T *wp, int row, int col)
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200256{
257 drag_start_row = mouse_row;
258 drag_start_col = mouse_col;
259 // TODO: handle using different corner
260 if (wp->w_wantline == 0)
261 drag_start_wantline = wp->w_winrow + 1;
262 else
263 drag_start_wantline = wp->w_wantline;
264 if (wp->w_wantcol == 0)
265 drag_start_wantcol = wp->w_wincol + 1;
266 else
267 drag_start_wantcol = wp->w_wantcol;
Bram Moolenaara42d9452019-06-15 21:46:30 +0200268
269 // Stop centering the popup
270 if (wp->w_popup_pos == POPPOS_CENTER)
271 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200272
273 drag_on_resize_handle = wp->w_popup_border[1] > 0
274 && wp->w_popup_border[2] > 0
275 && row == popup_height(wp) - 1
276 && col == popup_width(wp) - 1;
277
278 if (wp->w_popup_pos != POPPOS_TOPLEFT && drag_on_resize_handle)
279 {
280 if (wp->w_popup_pos == POPPOS_TOPRIGHT
281 || wp->w_popup_pos == POPPOS_BOTRIGHT)
282 wp->w_wantcol = wp->w_wincol + 1;
283 if (wp->w_popup_pos == POPPOS_BOTLEFT)
284 wp->w_wantline = wp->w_winrow + 1;
285 wp->w_popup_pos = POPPOS_TOPLEFT;
286 }
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200287}
288
289/*
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200290 * Mouse moved while dragging a popup window: adjust the window popup position
291 * or resize.
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200292 */
293 void
294popup_drag(win_T *wp)
295{
296 // The popup may be closed before dragging stops.
297 if (!win_valid_popup(wp))
298 return;
299
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200300 if ((wp->w_popup_flags & POPF_RESIZE) && drag_on_resize_handle)
301 {
302 int width_inc = mouse_col - drag_start_col;
303 int height_inc = mouse_row - drag_start_row;
304
305 if (width_inc != 0)
306 {
307 int width = wp->w_width + width_inc;
308
309 if (width < 1)
310 width = 1;
311 wp->w_minwidth = width;
312 wp->w_maxwidth = width;
313 drag_start_col = mouse_col;
314 }
315
316 if (height_inc != 0)
317 {
318 int height = wp->w_height + height_inc;
319
320 if (height < 1)
321 height = 1;
322 wp->w_minheight = height;
323 wp->w_maxheight = height;
324 drag_start_row = mouse_row;
325 }
326
327 popup_adjust_position(wp);
328 return;
329 }
330
331 if (!(wp->w_popup_flags & POPF_DRAG))
332 return;
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200333 wp->w_wantline = drag_start_wantline + (mouse_row - drag_start_row);
334 if (wp->w_wantline < 1)
335 wp->w_wantline = 1;
336 if (wp->w_wantline > Rows)
337 wp->w_wantline = Rows;
338 wp->w_wantcol = drag_start_wantcol + (mouse_col - drag_start_col);
339 if (wp->w_wantcol < 1)
340 wp->w_wantcol = 1;
341 if (wp->w_wantcol > Columns)
342 wp->w_wantcol = Columns;
343
344 popup_adjust_position(wp);
345}
Bram Moolenaar68d48f42019-06-12 22:42:41 +0200346
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200347/*
348 * Set w_firstline to match the current "wp->w_topline".
349 */
350 void
351popup_set_firstline(win_T *wp)
352{
353 int height = wp->w_height;
354
355 wp->w_firstline = wp->w_topline;
356 popup_adjust_position(wp);
357
358 // we don't want the popup to get smaller, decrement the first line
359 // until it doesn't
360 while (wp->w_firstline > 1 && wp->w_height < height)
361 {
362 --wp->w_firstline;
363 popup_adjust_position(wp);
364 }
365}
366
367/*
Bram Moolenaar13b11ed2019-08-01 15:52:45 +0200368 * Return TRUE if the position is in the popup window scrollbar.
369 */
370 int
371popup_is_in_scrollbar(win_T *wp, int row, int col)
372{
373 return wp->w_has_scrollbar
374 && row >= wp->w_popup_border[0]
375 && row < popup_height(wp) - wp->w_popup_border[2]
376 && col == popup_width(wp) - wp->w_popup_border[1] - 1;
377}
378
379
380/*
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200381 * Handle a click in a popup window, if it is in the scrollbar.
382 */
383 void
384popup_handle_scrollbar_click(win_T *wp, int row, int col)
385{
386 int height = popup_height(wp);
387 int old_topline = wp->w_topline;
388
Bram Moolenaar13b11ed2019-08-01 15:52:45 +0200389 if (popup_is_in_scrollbar(wp, row, col))
Bram Moolenaarf9c85f52019-06-29 07:41:35 +0200390 {
391 if (row >= height / 2)
392 {
393 // Click in lower half, scroll down.
394 if (wp->w_topline < wp->w_buffer->b_ml.ml_line_count)
395 ++wp->w_topline;
396 }
397 else if (wp->w_topline > 1)
398 // click on upper half, scroll up.
399 --wp->w_topline;
400 if (wp->w_topline != old_topline)
401 {
402 popup_set_firstline(wp);
403 redraw_win_later(wp, NOT_VALID);
404 }
405 }
406}
407
Bram Moolenaar68d48f42019-06-12 22:42:41 +0200408#if defined(FEAT_TIMERS)
409 static void
410popup_add_timeout(win_T *wp, int time)
411{
412 char_u cbbuf[50];
413 char_u *ptr = cbbuf;
414 typval_T tv;
415
416 vim_snprintf((char *)cbbuf, sizeof(cbbuf),
417 "{_ -> popup_close(%d)}", wp->w_id);
418 if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
419 {
420 wp->w_popup_timer = create_timer(time, 0);
421 wp->w_popup_timer->tr_callback = get_callback(&tv);
422 clear_tv(&tv);
423 }
424}
425#endif
426
Bram Moolenaar17627312019-06-02 19:53:44 +0200427/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200428 * Shared between popup_create() and f_popup_move().
Bram Moolenaar17627312019-06-02 19:53:44 +0200429 */
430 static void
Bram Moolenaarae943152019-06-16 22:54:14 +0200431apply_move_options(win_T *wp, dict_T *d)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200432{
Bram Moolenaarae943152019-06-16 22:54:14 +0200433 int nr;
434
435 if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0)
436 wp->w_minwidth = nr;
437 if ((nr = dict_get_number(d, (char_u *)"minheight")) > 0)
438 wp->w_minheight = nr;
439 if ((nr = dict_get_number(d, (char_u *)"maxwidth")) > 0)
440 wp->w_maxwidth = nr;
441 if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0)
442 wp->w_maxheight = nr;
443 get_pos_options(wp, d);
444}
445
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200446 static void
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200447handle_moved_argument(win_T *wp, dictitem_T *di, int mousemoved)
448{
449 if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
450 {
451 char_u *s = di->di_tv.vval.v_string;
452 int flags = 0;
453
454 if (STRCMP(s, "word") == 0)
455 flags = FIND_IDENT | FIND_STRING;
456 else if (STRCMP(s, "WORD") == 0)
457 flags = FIND_STRING;
458 else if (STRCMP(s, "expr") == 0)
459 flags = FIND_IDENT | FIND_STRING | FIND_EVAL;
460 else if (STRCMP(s, "any") != 0)
461 semsg(_(e_invarg2), s);
462 if (flags != 0)
463 {
464 if (mousemoved)
465 set_mousemoved_columns(wp, flags);
466 else
467 set_moved_columns(wp, flags);
468 }
469 }
470 else if (di->di_tv.v_type == VAR_LIST
471 && di->di_tv.vval.v_list != NULL
472 && di->di_tv.vval.v_list->lv_len == 2)
473 {
474 list_T *l = di->di_tv.vval.v_list;
475 int mincol = tv_get_number(&l->lv_first->li_tv);
476 int maxcol = tv_get_number(&l->lv_first->li_next->li_tv);
477
478 if (mousemoved)
479 {
480 wp->w_popup_mouse_mincol = mincol;
481 wp->w_popup_mouse_maxcol = maxcol;
482 }
483 else
484 {
485 wp->w_popup_mincol = mincol;
486 wp->w_popup_maxcol = maxcol;
487 }
488 }
489 else
490 semsg(_(e_invarg2), tv_get_string(&di->di_tv));
491}
492
493 static void
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200494check_highlight(dict_T *dict, char *name, char_u **pval)
495{
496 dictitem_T *di;
497 char_u *str;
498
499 di = dict_find(dict, (char_u *)name, -1);
500 if (di != NULL)
501 {
502 if (di->di_tv.v_type != VAR_STRING)
503 semsg(_(e_invargval), name);
504 else
505 {
506 str = tv_get_string(&di->di_tv);
507 if (*str != NUL)
508 *pval = vim_strsave(str);
509 }
510 }
511}
512
Bram Moolenaarae943152019-06-16 22:54:14 +0200513/*
Bram Moolenaar79648732019-07-18 21:43:07 +0200514 * Scroll to show the line with the cursor. This assumes lines don't wrap.
515 */
516 static void
517popup_show_curline(win_T *wp)
518{
519 if (wp->w_cursor.lnum < wp->w_topline)
520 wp->w_topline = wp->w_cursor.lnum;
521 else if (wp->w_cursor.lnum >= wp->w_botline)
522 wp->w_topline = wp->w_cursor.lnum - wp->w_height + 1;
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200523
524 // Don't use "firstline" now.
525 wp->w_firstline = 0;
526}
527
528/*
529 * Get the sign group name for window "wp".
530 * Returns a pointer to a static buffer, overwritten on the next call.
531 */
532 static char_u *
533popup_get_sign_name(win_T *wp)
534{
535 static char buf[30];
536
537 vim_snprintf(buf, sizeof(buf), "popup-%d", wp->w_id);
538 return (char_u *)buf;
Bram Moolenaar79648732019-07-18 21:43:07 +0200539}
540
541/*
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200542 * Highlight the line with the cursor.
543 * Also scrolls the text to put the cursor line in view.
544 */
545 static void
546popup_highlight_curline(win_T *wp)
547{
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200548 int sign_id = 0;
549 char_u *sign_name = popup_get_sign_name(wp);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200550
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200551 buf_delete_signs(wp->w_buffer, (char_u *)"popupmenu");
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200552
553 if ((wp->w_popup_flags & POPF_CURSORLINE) != 0)
554 {
Bram Moolenaar79648732019-07-18 21:43:07 +0200555 popup_show_curline(wp);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200556
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200557 if (!sign_exists_by_name(sign_name))
558 {
559 char *linehl = "PopupSelected";
560
561 if (syn_name2id((char_u *)linehl) == 0)
562 linehl = "PmenuSel";
Bram Moolenaar62a0cb42019-08-18 16:35:23 +0200563 sign_define_by_name(sign_name, NULL, (char_u *)linehl, NULL, NULL);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200564 }
565
566 sign_place(&sign_id, (char_u *)"popupmenu", sign_name,
567 wp->w_buffer, wp->w_cursor.lnum, SIGN_DEF_PRIO);
568 redraw_win_later(wp, NOT_VALID);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200569 }
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200570 else
571 sign_undefine_by_name(sign_name, FALSE);
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200572}
573
574/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200575 * Shared between popup_create() and f_popup_setoptions().
576 */
577 static void
578apply_general_options(win_T *wp, dict_T *dict)
579{
580 dictitem_T *di;
Bram Moolenaar402502d2019-05-30 22:07:36 +0200581 int nr;
582 char_u *str;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200583
Bram Moolenaarae943152019-06-16 22:54:14 +0200584 // TODO: flip
585
586 di = dict_find(dict, (char_u *)"firstline", -1);
Bram Moolenaardfa97f22019-06-15 14:31:55 +0200587 if (di != NULL)
Bram Moolenaarae943152019-06-16 22:54:14 +0200588 wp->w_firstline = dict_get_number(dict, (char_u *)"firstline");
589 if (wp->w_firstline < 1)
590 wp->w_firstline = 1;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200591
Bram Moolenaar75fb0852019-06-25 05:15:58 +0200592 di = dict_find(dict, (char_u *)"scrollbar", -1);
593 if (di != NULL)
594 wp->w_want_scrollbar = dict_get_number(dict, (char_u *)"scrollbar");
595
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200596 str = dict_get_string(dict, (char_u *)"title", FALSE);
597 if (str != NULL)
598 {
599 vim_free(wp->w_popup_title);
600 wp->w_popup_title = vim_strsave(str);
601 }
602
Bram Moolenaar402502d2019-05-30 22:07:36 +0200603 di = dict_find(dict, (char_u *)"wrap", -1);
604 if (di != NULL)
605 {
606 nr = dict_get_number(dict, (char_u *)"wrap");
607 wp->w_p_wrap = nr != 0;
608 }
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200609
Bram Moolenaara42d9452019-06-15 21:46:30 +0200610 di = dict_find(dict, (char_u *)"drag", -1);
611 if (di != NULL)
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +0200612 {
613 nr = dict_get_number(dict, (char_u *)"drag");
614 if (nr)
615 wp->w_popup_flags |= POPF_DRAG;
616 else
617 wp->w_popup_flags &= ~POPF_DRAG;
618 }
619
620 di = dict_find(dict, (char_u *)"resize", -1);
621 if (di != NULL)
622 {
623 nr = dict_get_number(dict, (char_u *)"resize");
624 if (nr)
625 wp->w_popup_flags |= POPF_RESIZE;
626 else
627 wp->w_popup_flags &= ~POPF_RESIZE;
628 }
Bram Moolenaarb53fb312019-06-13 23:59:52 +0200629
Bram Moolenaar2e62b562019-06-30 18:07:00 +0200630 di = dict_find(dict, (char_u *)"close", -1);
631 if (di != NULL)
632 {
633 int ok = TRUE;
634
635 if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
636 {
637 char_u *s = di->di_tv.vval.v_string;
638
639 if (STRCMP(s, "none") == 0)
640 wp->w_popup_close = POPCLOSE_NONE;
641 else if (STRCMP(s, "button") == 0)
642 wp->w_popup_close = POPCLOSE_BUTTON;
643 else if (STRCMP(s, "click") == 0)
644 wp->w_popup_close = POPCLOSE_CLICK;
645 else
646 ok = FALSE;
647 }
648 else
649 ok = FALSE;
650 if (!ok)
651 semsg(_(e_invargNval), "close", tv_get_string(&di->di_tv));
652 }
653
Bram Moolenaarae943152019-06-16 22:54:14 +0200654 str = dict_get_string(dict, (char_u *)"highlight", FALSE);
655 if (str != NULL)
656 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
657 str, OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200658
Bram Moolenaarcb5ff342019-07-20 16:51:19 +0200659 set_string_option_direct_in_win(wp, (char_u *)"signcolumn", -1,
660 (char_u *)"no", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaarae943152019-06-16 22:54:14 +0200661 set_padding_border(dict, wp->w_popup_padding, "padding", 999);
662 set_padding_border(dict, wp->w_popup_border, "border", 1);
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200663
Bram Moolenaar790498b2019-06-01 22:15:29 +0200664 di = dict_find(dict, (char_u *)"borderhighlight", -1);
665 if (di != NULL)
666 {
Bram Moolenaar403d0902019-07-17 21:37:32 +0200667 if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL)
Bram Moolenaar790498b2019-06-01 22:15:29 +0200668 emsg(_(e_listreq));
669 else
670 {
671 list_T *list = di->di_tv.vval.v_list;
672 listitem_T *li;
Bram Moolenaarae943152019-06-16 22:54:14 +0200673 int i;
Bram Moolenaar790498b2019-06-01 22:15:29 +0200674
Bram Moolenaar403d0902019-07-17 21:37:32 +0200675 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
676 ++i, li = li->li_next)
677 {
678 str = tv_get_string(&li->li_tv);
679 if (*str != NUL)
680 wp->w_border_highlight[i] = vim_strsave(str);
681 }
Bram Moolenaar790498b2019-06-01 22:15:29 +0200682 if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
683 for (i = 1; i < 4; ++i)
Bram Moolenaar403d0902019-07-17 21:37:32 +0200684 wp->w_border_highlight[i] =
Bram Moolenaar790498b2019-06-01 22:15:29 +0200685 vim_strsave(wp->w_border_highlight[0]);
686 }
687 }
688
Bram Moolenaar790498b2019-06-01 22:15:29 +0200689 di = dict_find(dict, (char_u *)"borderchars", -1);
690 if (di != NULL)
691 {
692 if (di->di_tv.v_type != VAR_LIST)
693 emsg(_(e_listreq));
694 else
695 {
696 list_T *list = di->di_tv.vval.v_list;
697 listitem_T *li;
Bram Moolenaarae943152019-06-16 22:54:14 +0200698 int i;
Bram Moolenaar790498b2019-06-01 22:15:29 +0200699
700 if (list != NULL)
701 for (i = 0, li = list->lv_first; i < 8 && i < list->lv_len;
702 ++i, li = li->li_next)
703 {
704 str = tv_get_string(&li->li_tv);
705 if (*str != NUL)
706 wp->w_border_char[i] = mb_ptr2char(str);
707 }
708 if (list->lv_len == 1)
709 for (i = 1; i < 8; ++i)
710 wp->w_border_char[i] = wp->w_border_char[0];
711 if (list->lv_len == 2)
712 {
713 for (i = 4; i < 8; ++i)
714 wp->w_border_char[i] = wp->w_border_char[1];
715 for (i = 1; i < 4; ++i)
716 wp->w_border_char[i] = wp->w_border_char[0];
717 }
718 }
719 }
Bram Moolenaar3397f742019-06-02 18:40:06 +0200720
Bram Moolenaar4cd583c2019-06-26 05:13:57 +0200721 check_highlight(dict, "scrollbarhighlight", &wp->w_scrollbar_highlight);
722 check_highlight(dict, "thumbhighlight", &wp->w_thumb_highlight);
723
Bram Moolenaarae943152019-06-16 22:54:14 +0200724 di = dict_find(dict, (char_u *)"zindex", -1);
725 if (di != NULL)
726 {
727 wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
728 if (wp->w_zindex < 1)
729 wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
730 if (wp->w_zindex > 32000)
731 wp->w_zindex = 32000;
732 }
733
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200734 di = dict_find(dict, (char_u *)"mask", -1);
735 if (di != NULL)
736 {
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200737 int ok = FALSE;
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200738
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200739 if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL)
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200740 {
741 listitem_T *li;
742
Bram Moolenaarcfdbc5a2019-07-17 21:27:52 +0200743 ok = TRUE;
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200744 for (li = di->di_tv.vval.v_list->lv_first; li != NULL;
745 li = li->li_next)
746 {
747 if (li->li_tv.v_type != VAR_LIST
748 || li->li_tv.vval.v_list == NULL
749 || li->li_tv.vval.v_list->lv_len != 4)
750 {
751 ok = FALSE;
752 break;
753 }
754 }
755 }
756 if (ok)
757 {
758 wp->w_popup_mask = di->di_tv.vval.v_list;
759 ++wp->w_popup_mask->lv_refcount;
Bram Moolenaare865dcb2019-07-26 22:15:50 +0200760 VIM_CLEAR(wp->w_popup_mask_cells);
Bram Moolenaarc662ec92019-06-23 00:15:57 +0200761 }
762 else
763 semsg(_(e_invargval), "mask");
764 }
765
Bram Moolenaarae943152019-06-16 22:54:14 +0200766#if defined(FEAT_TIMERS)
767 // Add timer to close the popup after some time.
768 nr = dict_get_number(dict, (char_u *)"time");
769 if (nr > 0)
770 popup_add_timeout(wp, nr);
771#endif
772
Bram Moolenaar3397f742019-06-02 18:40:06 +0200773 di = dict_find(dict, (char_u *)"moved", -1);
774 if (di != NULL)
775 {
Bram Moolenaar17627312019-06-02 19:53:44 +0200776 set_moved_values(wp);
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200777 handle_moved_argument(wp, di, FALSE);
778 }
Bram Moolenaar3397f742019-06-02 18:40:06 +0200779
Bram Moolenaarb3d17a22019-07-07 18:28:14 +0200780 di = dict_find(dict, (char_u *)"mousemoved", -1);
781 if (di != NULL)
782 {
783 set_mousemoved_values(wp);
784 handle_moved_argument(wp, di, TRUE);
Bram Moolenaar3397f742019-06-02 18:40:06 +0200785 }
Bram Moolenaar33796b32019-06-08 16:01:13 +0200786
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200787 di = dict_find(dict, (char_u *)"cursorline", -1);
788 if (di != NULL)
789 {
790 if (di->di_tv.v_type == VAR_NUMBER)
791 {
792 if (di->di_tv.vval.v_number != 0)
793 wp->w_popup_flags |= POPF_CURSORLINE;
794 else
795 wp->w_popup_flags &= ~POPF_CURSORLINE;
796 }
797 else
798 semsg(_(e_invargval), "cursorline");
799 }
800
Bram Moolenaarae943152019-06-16 22:54:14 +0200801 di = dict_find(dict, (char_u *)"filter", -1);
802 if (di != NULL)
803 {
804 callback_T callback = get_callback(&di->di_tv);
805
806 if (callback.cb_name != NULL)
807 {
808 free_callback(&wp->w_filter_cb);
809 set_callback(&wp->w_filter_cb, &callback);
810 }
811 }
Bram Moolenaar749fa0a2019-08-03 16:18:07 +0200812 di = dict_find(dict, (char_u *)"mapping", -1);
813 if (di != NULL)
814 {
815 nr = dict_get_number(dict, (char_u *)"mapping");
816 if (nr)
817 wp->w_popup_flags |= POPF_MAPPING;
818 else
819 wp->w_popup_flags &= ~POPF_MAPPING;
820 }
Bram Moolenaarae943152019-06-16 22:54:14 +0200821
822 di = dict_find(dict, (char_u *)"callback", -1);
823 if (di != NULL)
824 {
825 callback_T callback = get_callback(&di->di_tv);
826
827 if (callback.cb_name != NULL)
828 {
829 free_callback(&wp->w_close_cb);
830 set_callback(&wp->w_close_cb, &callback);
831 }
832 }
833}
834
835/*
836 * Go through the options in "dict" and apply them to popup window "wp".
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200837 * Only used when creating a new popup window.
Bram Moolenaarae943152019-06-16 22:54:14 +0200838 */
839 static void
840apply_options(win_T *wp, dict_T *dict)
841{
842 int nr;
843
844 apply_move_options(wp, dict);
845 apply_general_options(wp, dict);
846
Bram Moolenaar6313c4f2019-06-16 20:39:13 +0200847 nr = dict_get_number(dict, (char_u *)"hidden");
848 if (nr > 0)
849 {
850 wp->w_popup_flags |= POPF_HIDDEN;
851 --wp->w_buffer->b_nwindows;
852 }
853
Bram Moolenaar33796b32019-06-08 16:01:13 +0200854 popup_mask_refresh = TRUE;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +0200855 popup_highlight_curline(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200856}
857
858/*
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200859 * Add lines to the popup from a list of strings.
860 */
861 static void
862add_popup_strings(buf_T *buf, list_T *l)
863{
864 listitem_T *li;
865 linenr_T lnum = 0;
866 char_u *p;
867
868 for (li = l->lv_first; li != NULL; li = li->li_next)
869 if (li->li_tv.v_type == VAR_STRING)
870 {
871 p = li->li_tv.vval.v_string;
872 ml_append_buf(buf, lnum++,
873 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
874 }
875}
876
877/*
878 * Add lines to the popup from a list of dictionaries.
879 */
880 static void
881add_popup_dicts(buf_T *buf, list_T *l)
882{
883 listitem_T *li;
884 listitem_T *pli;
885 linenr_T lnum = 0;
886 char_u *p;
887 dict_T *dict;
888
889 // first add the text lines
890 for (li = l->lv_first; li != NULL; li = li->li_next)
891 {
892 if (li->li_tv.v_type != VAR_DICT)
893 {
894 emsg(_(e_dictreq));
895 return;
896 }
897 dict = li->li_tv.vval.v_dict;
898 p = dict == NULL ? NULL
899 : dict_get_string(dict, (char_u *)"text", FALSE);
900 ml_append_buf(buf, lnum++,
901 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
902 }
903
904 // add the text properties
905 lnum = 1;
906 for (li = l->lv_first; li != NULL; li = li->li_next, ++lnum)
907 {
908 dictitem_T *di;
909 list_T *plist;
910
911 dict = li->li_tv.vval.v_dict;
912 di = dict_find(dict, (char_u *)"props", -1);
913 if (di != NULL)
914 {
915 if (di->di_tv.v_type != VAR_LIST)
916 {
917 emsg(_(e_listreq));
918 return;
919 }
920 plist = di->di_tv.vval.v_list;
921 if (plist != NULL)
922 {
923 for (pli = plist->lv_first; pli != NULL; pli = pli->li_next)
924 {
925 if (pli->li_tv.v_type != VAR_DICT)
926 {
927 emsg(_(e_dictreq));
928 return;
929 }
930 dict = pli->li_tv.vval.v_dict;
931 if (dict != NULL)
932 {
933 int col = dict_get_number(dict, (char_u *)"col");
934
935 prop_add_common( lnum, col, dict, buf, NULL);
936 }
937 }
938 }
939 }
940 }
941}
942
943/*
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200944 * Get the padding plus border at the top, adjusted to 1 if there is a title.
945 */
Bram Moolenaarbd483b32019-08-21 15:13:41 +0200946 int
Bram Moolenaareb2310d2019-06-16 20:09:10 +0200947popup_top_extra(win_T *wp)
948{
949 int extra = wp->w_popup_border[0] + wp->w_popup_padding[0];
950
951 if (extra == 0 && wp->w_popup_title != NULL && *wp->w_popup_title != NUL)
952 return 1;
953 return extra;
954}
955
956/*
Bram Moolenaard529ba52019-07-02 23:13:53 +0200957 * Return the height of popup window "wp", including border and padding.
958 */
959 int
960popup_height(win_T *wp)
961{
962 return wp->w_height
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200963 + popup_top_extra(wp)
964 + wp->w_popup_padding[2] + wp->w_popup_border[2];
Bram Moolenaard529ba52019-07-02 23:13:53 +0200965}
966
967/*
968 * Return the width of popup window "wp", including border, padding and
969 * scrollbar.
970 */
971 int
972popup_width(win_T *wp)
973{
Bram Moolenaar017c2692019-07-13 14:17:51 +0200974 // w_leftcol is how many columns of the core are left of the screen
975 // w_popup_rightoff is how many columns of the core are right of the screen
Bram Moolenaard529ba52019-07-02 23:13:53 +0200976 return wp->w_width + wp->w_leftcol
Bram Moolenaar576a4a62019-08-18 15:25:17 +0200977 + popup_extra_width(wp)
978 + wp->w_popup_rightoff;
979}
980
981/*
982 * Return the extra width of popup window "wp": border, padding and scrollbar.
983 */
984 int
985popup_extra_width(win_T *wp)
986{
987 return wp->w_popup_padding[3] + wp->w_popup_border[3]
988 + wp->w_popup_padding[1] + wp->w_popup_border[1]
989 + wp->w_has_scrollbar;
Bram Moolenaard529ba52019-07-02 23:13:53 +0200990}
991
992/*
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200993 * Adjust the position and size of the popup to fit on the screen.
994 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200995 static void
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200996popup_adjust_position(win_T *wp)
997{
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200998 linenr_T lnum;
999 int wrapped = 0;
1000 int maxwidth;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001001 int maxspace;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001002 int center_vert = FALSE;
1003 int center_hor = FALSE;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001004 int allow_adjust_left = !wp->w_popup_fixed;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001005 int top_extra = popup_top_extra(wp);
Bram Moolenaar399d8982019-06-02 15:34:29 +02001006 int right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1];
1007 int bot_extra = wp->w_popup_border[2] + wp->w_popup_padding[2];
1008 int left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
1009 int extra_height = top_extra + bot_extra;
1010 int extra_width = left_extra + right_extra;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001011 int org_winrow = wp->w_winrow;
1012 int org_wincol = wp->w_wincol;
1013 int org_width = wp->w_width;
1014 int org_height = wp->w_height;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001015 int org_leftcol = wp->w_leftcol;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001016 int org_leftoff = wp->w_popup_leftoff;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001017 int minwidth;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001018
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001019 wp->w_winrow = 0;
1020 wp->w_wincol = 0;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001021 wp->w_leftcol = 0;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001022 wp->w_popup_leftoff = 0;
1023 wp->w_popup_rightoff = 0;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001024 if (wp->w_popup_pos == POPPOS_CENTER)
1025 {
1026 // center after computing the size
1027 center_vert = TRUE;
1028 center_hor = TRUE;
1029 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001030 else
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001031 {
1032 if (wp->w_wantline == 0)
1033 center_vert = TRUE;
1034 else if (wp->w_popup_pos == POPPOS_TOPLEFT
1035 || wp->w_popup_pos == POPPOS_TOPRIGHT)
1036 {
1037 wp->w_winrow = wp->w_wantline - 1;
1038 if (wp->w_winrow >= Rows)
1039 wp->w_winrow = Rows - 1;
1040 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001041
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001042 if (wp->w_wantcol == 0)
1043 center_hor = TRUE;
1044 else if (wp->w_popup_pos == POPPOS_TOPLEFT
1045 || wp->w_popup_pos == POPPOS_BOTLEFT)
1046 {
1047 wp->w_wincol = wp->w_wantcol - 1;
1048 if (wp->w_wincol >= Columns - 3)
1049 wp->w_wincol = Columns - 3;
1050 }
1051 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001052
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001053 // When centering or right aligned, use maximum width.
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001054 // When left aligned use the space available, but shift to the left when we
1055 // hit the right of the screen.
Bram Moolenaard529ba52019-07-02 23:13:53 +02001056 maxspace = Columns - wp->w_wincol - left_extra;
1057 maxwidth = maxspace;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001058 if (wp->w_maxwidth > 0 && maxwidth > wp->w_maxwidth)
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001059 {
1060 allow_adjust_left = FALSE;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001061 maxwidth = wp->w_maxwidth;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001062 }
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001063
Bram Moolenaar8d241042019-06-12 23:40:01 +02001064 // start at the desired first line
Bram Moolenaar79648732019-07-18 21:43:07 +02001065 if (wp->w_firstline != 0)
1066 wp->w_topline = wp->w_firstline;
Bram Moolenaar8d241042019-06-12 23:40:01 +02001067 if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count)
1068 wp->w_topline = wp->w_buffer->b_ml.ml_line_count;
1069
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001070 // Compute width based on longest text line and the 'wrap' option.
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001071 // Use a minimum width of one, so that something shows when there is no
1072 // text.
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001073 // TODO: more accurate wrapping
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001074 wp->w_width = 1;
Bram Moolenaar8d241042019-06-12 23:40:01 +02001075 for (lnum = wp->w_topline; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001076 {
Bram Moolenaar331bafd2019-07-20 17:46:05 +02001077 int len;
1078 int w_width = wp->w_width;
1079
1080 // Count Tabs for what they are worth and compute the length based on
1081 // the maximum width (matters when 'showbreak' is set).
1082 if (wp->w_width < maxwidth)
1083 wp->w_width = maxwidth;
1084 len = win_linetabsize(wp, ml_get_buf(wp->w_buffer, lnum, FALSE),
Bram Moolenaare089c3f2019-07-09 20:25:25 +02001085 (colnr_T)MAXCOL);
Bram Moolenaar331bafd2019-07-20 17:46:05 +02001086 wp->w_width = w_width;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001087
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001088 if (wp->w_p_wrap)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001089 {
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001090 while (len > maxwidth)
1091 {
1092 ++wrapped;
1093 len -= maxwidth;
1094 wp->w_width = maxwidth;
1095 }
1096 }
1097 else if (len > maxwidth
1098 && allow_adjust_left
1099 && (wp->w_popup_pos == POPPOS_TOPLEFT
1100 || wp->w_popup_pos == POPPOS_BOTLEFT))
1101 {
1102 // adjust leftwise to fit text on screen
Bram Moolenaar51c31312019-06-15 22:27:23 +02001103 int shift_by = len - maxwidth;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001104
Bram Moolenaar51c31312019-06-15 22:27:23 +02001105 if (shift_by > wp->w_wincol)
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001106 {
1107 int truncate_shift = shift_by - wp->w_wincol;
Bram Moolenaar51c31312019-06-15 22:27:23 +02001108
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001109 len -= truncate_shift;
1110 shift_by -= truncate_shift;
1111 }
1112
1113 wp->w_wincol -= shift_by;
1114 maxwidth += shift_by;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001115 wp->w_width = maxwidth;
1116 }
1117 if (wp->w_width < len)
Bram Moolenaar017c2692019-07-13 14:17:51 +02001118 {
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001119 wp->w_width = len;
Bram Moolenaar017c2692019-07-13 14:17:51 +02001120 if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
1121 wp->w_width = wp->w_maxwidth;
1122 }
Bram Moolenaar8d241042019-06-12 23:40:01 +02001123 // do not use the width of lines we're not going to show
Bram Moolenaare296e312019-07-03 23:20:18 +02001124 if (wp->w_maxheight > 0
1125 && lnum - wp->w_topline + 1 + wrapped > wp->w_maxheight)
Bram Moolenaar8d241042019-06-12 23:40:01 +02001126 break;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001127 }
1128
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001129 wp->w_has_scrollbar = wp->w_want_scrollbar
1130 && (wp->w_topline > 1 || lnum <= wp->w_buffer->b_ml.ml_line_count);
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001131 if (wp->w_has_scrollbar)
Bram Moolenaarfe6e7612019-08-21 20:57:20 +02001132 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001133 ++right_extra;
Bram Moolenaarfe6e7612019-08-21 20:57:20 +02001134 ++extra_width;
1135 }
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001136
Bram Moolenaareb2310d2019-06-16 20:09:10 +02001137 minwidth = wp->w_minwidth;
1138 if (wp->w_popup_title != NULL && *wp->w_popup_title != NUL)
1139 {
1140 int title_len = vim_strsize(wp->w_popup_title) + 2 - extra_width;
1141
1142 if (minwidth < title_len)
1143 minwidth = title_len;
1144 }
1145
1146 if (minwidth > 0 && wp->w_width < minwidth)
1147 wp->w_width = minwidth;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001148 if (wp->w_width > maxwidth)
Bram Moolenaard529ba52019-07-02 23:13:53 +02001149 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001150 if (wp->w_width > maxspace && !wp->w_p_wrap)
Bram Moolenaard529ba52019-07-02 23:13:53 +02001151 // some columns cut off on the right
1152 wp->w_popup_rightoff = wp->w_width - maxspace;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +02001153 wp->w_width = maxwidth;
Bram Moolenaard529ba52019-07-02 23:13:53 +02001154 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001155 if (center_hor)
Bram Moolenaara730e552019-06-16 19:05:31 +02001156 {
1157 wp->w_wincol = (Columns - wp->w_width - extra_width) / 2;
1158 if (wp->w_wincol < 0)
1159 wp->w_wincol = 0;
1160 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001161 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
1162 || wp->w_popup_pos == POPPOS_TOPRIGHT)
1163 {
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001164 int leftoff = wp->w_wantcol - (wp->w_width + extra_width);
1165
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001166 // Right aligned: move to the right if needed.
1167 // No truncation, because that would change the height.
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001168 if (leftoff >= 0)
1169 wp->w_wincol = leftoff;
1170 else if (wp->w_popup_fixed)
1171 {
1172 // "col" specifies the right edge, but popup doesn't fit, skip some
Bram Moolenaard529ba52019-07-02 23:13:53 +02001173 // columns when displaying the window, minus left border and
1174 // padding.
1175 if (-leftoff > left_extra)
1176 wp->w_leftcol = -leftoff - left_extra;
1177 wp->w_width -= wp->w_leftcol;
1178 wp->w_popup_leftoff = -leftoff;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001179 if (wp->w_width < 0)
1180 wp->w_width = 0;
1181 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001182 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001183
Bram Moolenaar8edf0e32019-07-30 21:19:26 +02001184 if (wp->w_p_wrap || (!wp->w_popup_fixed
1185 && (wp->w_popup_pos == POPPOS_TOPLEFT
1186 || wp->w_popup_pos == POPPOS_BOTLEFT)))
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001187 {
1188 int want_col = 0;
1189
Bram Moolenaar8c8b88d2019-07-30 20:32:41 +02001190 // try to show the right border and any scrollbar
1191 want_col = left_extra + wp->w_width + right_extra;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001192 if (want_col > 0 && wp->w_wincol > 0
1193 && wp->w_wincol + want_col >= Columns)
1194 {
1195 wp->w_wincol = Columns - want_col;
1196 if (wp->w_wincol < 0)
1197 wp->w_wincol = 0;
1198 }
1199 }
1200
Bram Moolenaar8d241042019-06-12 23:40:01 +02001201 wp->w_height = wp->w_buffer->b_ml.ml_line_count - wp->w_topline
1202 + 1 + wrapped;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001203 if (wp->w_minheight > 0 && wp->w_height < wp->w_minheight)
1204 wp->w_height = wp->w_minheight;
1205 if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
1206 wp->w_height = wp->w_maxheight;
1207 if (wp->w_height > Rows - wp->w_winrow)
1208 wp->w_height = Rows - wp->w_winrow;
Bram Moolenaar17146962019-05-30 00:12:11 +02001209
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001210 if (center_vert)
Bram Moolenaara730e552019-06-16 19:05:31 +02001211 {
1212 wp->w_winrow = (Rows - wp->w_height - extra_height) / 2;
1213 if (wp->w_winrow < 0)
1214 wp->w_winrow = 0;
1215 }
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001216 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
1217 || wp->w_popup_pos == POPPOS_BOTLEFT)
1218 {
Bram Moolenaar399d8982019-06-02 15:34:29 +02001219 if ((wp->w_height + extra_height) <= wp->w_wantline)
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001220 // bottom aligned: may move down
Bram Moolenaar399d8982019-06-02 15:34:29 +02001221 wp->w_winrow = wp->w_wantline - (wp->w_height + extra_height);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001222 else
1223 // not enough space, make top aligned
1224 wp->w_winrow = wp->w_wantline + 1;
1225 }
1226
Bram Moolenaar17146962019-05-30 00:12:11 +02001227 wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
Bram Moolenaar33796b32019-06-08 16:01:13 +02001228
1229 // Need to update popup_mask if the position or size changed.
Bram Moolenaar356375f2019-08-24 14:46:29 +02001230 // And redraw windows and statuslines that were behind the popup.
Bram Moolenaar33796b32019-06-08 16:01:13 +02001231 if (org_winrow != wp->w_winrow
1232 || org_wincol != wp->w_wincol
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001233 || org_leftcol != wp->w_leftcol
Bram Moolenaard529ba52019-07-02 23:13:53 +02001234 || org_leftoff != wp->w_popup_leftoff
Bram Moolenaar33796b32019-06-08 16:01:13 +02001235 || org_width != wp->w_width
1236 || org_height != wp->w_height)
1237 {
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001238 redraw_win_later(wp, NOT_VALID);
1239 if (wp->w_popup_flags & POPF_ON_CMDLINE)
1240 clear_cmdline = TRUE;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001241 popup_mask_refresh = TRUE;
1242 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001243}
1244
Bram Moolenaar17627312019-06-02 19:53:44 +02001245typedef enum
1246{
1247 TYPE_NORMAL,
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001248 TYPE_ATCURSOR,
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001249 TYPE_BEVAL,
Bram Moolenaara42d9452019-06-15 21:46:30 +02001250 TYPE_NOTIFICATION,
Bram Moolenaara730e552019-06-16 19:05:31 +02001251 TYPE_DIALOG,
Bram Moolenaar79648732019-07-18 21:43:07 +02001252 TYPE_MENU,
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001253 TYPE_PREVIEW, // preview window
1254 TYPE_INFO // popup menu info
Bram Moolenaar17627312019-06-02 19:53:44 +02001255} create_type_T;
1256
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001257/*
Bram Moolenaardc2ce582019-06-16 15:32:14 +02001258 * Make "buf" empty and set the contents to "text".
1259 * Used by popup_create() and popup_settext().
1260 */
1261 static void
1262popup_set_buffer_text(buf_T *buf, typval_T text)
1263{
1264 int lnum;
1265
1266 // Clear the buffer, then replace the lines.
1267 curbuf = buf;
1268 for (lnum = buf->b_ml.ml_line_count; lnum > 0; --lnum)
1269 ml_delete(lnum, FALSE);
1270 curbuf = curwin->w_buffer;
1271
1272 // Add text to the buffer.
1273 if (text.v_type == VAR_STRING)
1274 {
1275 // just a string
1276 ml_append_buf(buf, 0, text.vval.v_string, (colnr_T)0, TRUE);
1277 }
1278 else
1279 {
1280 list_T *l = text.vval.v_list;
1281
1282 if (l->lv_len > 0)
1283 {
1284 if (l->lv_first->li_tv.v_type == VAR_STRING)
1285 // list of strings
1286 add_popup_strings(buf, l);
1287 else
1288 // list of dictionaries
1289 add_popup_dicts(buf, l);
1290 }
1291 }
1292
1293 // delete the line that was in the empty buffer
1294 curbuf = buf;
1295 ml_delete(buf->b_ml.ml_line_count, FALSE);
1296 curbuf = curwin->w_buffer;
1297}
1298
1299/*
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001300 * Parse the 'previewpopup' or 'completepopup' option and apply the values to
1301 * window "wp" if it is not NULL.
Bram Moolenaar79648732019-07-18 21:43:07 +02001302 * Return FAIL if the parsing fails.
1303 */
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001304 static int
1305parse_popup_option(win_T *wp, int is_preview)
Bram Moolenaar79648732019-07-18 21:43:07 +02001306{
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001307 char_u *p =
1308#ifdef FEAT_QUICKFIX
1309 !is_preview ? p_cpp :
1310#endif
1311 p_pvp;
Bram Moolenaar79648732019-07-18 21:43:07 +02001312
Bram Moolenaar258cef52019-08-21 17:29:29 +02001313 if (wp != NULL)
1314 wp->w_popup_flags &= ~POPF_INFO_MENU;
1315
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001316 for ( ; *p != NUL; p += (*p == ',' ? 1 : 0))
Bram Moolenaar79648732019-07-18 21:43:07 +02001317 {
1318 char_u *e, *dig;
1319 char_u *s = p;
1320 int x;
1321
1322 e = vim_strchr(p, ':');
1323 if (e == NULL || e[1] == NUL)
1324 return FAIL;
1325
1326 p = vim_strchr(e, ',');
1327 if (p == NULL)
1328 p = e + STRLEN(e);
1329 dig = e + 1;
1330 x = getdigits(&dig);
Bram Moolenaar79648732019-07-18 21:43:07 +02001331
1332 if (STRNCMP(s, "height:", 7) == 0)
1333 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001334 if (dig != p)
1335 return FAIL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001336 if (wp != NULL)
1337 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001338 if (is_preview)
1339 wp->w_minheight = x;
Bram Moolenaar79648732019-07-18 21:43:07 +02001340 wp->w_maxheight = x;
1341 }
1342 }
1343 else if (STRNCMP(s, "width:", 6) == 0)
1344 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001345 if (dig != p)
1346 return FAIL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001347 if (wp != NULL)
1348 {
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001349 if (is_preview)
1350 wp->w_minwidth = x;
Bram Moolenaar79648732019-07-18 21:43:07 +02001351 wp->w_maxwidth = x;
1352 }
1353 }
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001354 else if (STRNCMP(s, "highlight:", 10) == 0)
1355 {
1356 if (wp != NULL)
1357 {
1358 int c = *p;
1359
1360 *p = NUL;
1361 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
1362 s + 10, OPT_FREE|OPT_LOCAL, 0);
1363 *p = c;
1364 }
1365 }
Bram Moolenaarbd483b32019-08-21 15:13:41 +02001366 else if (STRNCMP(s, "border:", 7) == 0)
1367 {
1368 char_u *arg = s + 7;
1369 int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
1370 int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
1371 int i;
1372
1373 if (!on && !off)
1374 return FAIL;
1375 if (wp != NULL)
1376 {
1377 for (i = 0; i < 4; ++i)
1378 wp->w_popup_border[i] = on ? 1 : 0;
1379 if (off)
1380 // only show the X for close when there is a border
1381 wp->w_popup_close = POPCLOSE_NONE;
1382 }
1383 }
Bram Moolenaar258cef52019-08-21 17:29:29 +02001384 else if (STRNCMP(s, "align:", 6) == 0)
1385 {
1386 char_u *arg = s + 6;
1387 int item = STRNCMP(arg, "item", 4) == 0 && arg + 4 == p;
1388 int menu = STRNCMP(arg, "menu", 4) == 0 && arg + 4 == p;
1389
1390 if (!menu && !item)
1391 return FAIL;
1392 if (wp != NULL && menu)
1393 wp->w_popup_flags |= POPF_INFO_MENU;
1394 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001395 else
1396 return FAIL;
1397 }
1398 return OK;
1399}
1400
1401/*
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001402 * Parse the 'previewpopup' option and apply the values to window "wp" if it
1403 * is not NULL.
1404 * Return FAIL if the parsing fails.
1405 */
1406 int
1407parse_previewpopup(win_T *wp)
1408{
1409 return parse_popup_option(wp, TRUE);
1410}
1411
1412/*
1413 * Parse the 'completepopup' option and apply the values to window "wp" if it
1414 * is not NULL.
1415 * Return FAIL if the parsing fails.
1416 */
1417 int
1418parse_completepopup(win_T *wp)
1419{
1420 return parse_popup_option(wp, FALSE);
1421}
1422
1423/*
Bram Moolenaar79648732019-07-18 21:43:07 +02001424 * Set w_wantline and w_wantcol for the cursor position in the current window.
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001425 * Keep at least "width" columns from the right of the screen.
Bram Moolenaar79648732019-07-18 21:43:07 +02001426 */
1427 void
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001428popup_set_wantpos_cursor(win_T *wp, int width)
Bram Moolenaar79648732019-07-18 21:43:07 +02001429{
1430 setcursor_mayforce(TRUE);
1431 wp->w_wantline = curwin->w_winrow + curwin->w_wrow;
1432 if (wp->w_wantline == 0) // cursor in first line
1433 {
1434 wp->w_wantline = 2;
1435 wp->w_popup_pos = POPPOS_TOPLEFT;
1436 }
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001437
Bram Moolenaar79648732019-07-18 21:43:07 +02001438 wp->w_wantcol = curwin->w_wincol + curwin->w_wcol + 1;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02001439 if (wp->w_wantcol > Columns - width)
1440 {
1441 wp->w_wantcol = Columns - width;
1442 if (wp->w_wantcol < 1)
1443 wp->w_wantcol = 1;
1444 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001445 popup_adjust_position(wp);
1446}
1447
1448/*
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001449 * Set w_wantline and w_wantcol for the a given screen position.
1450 * Caller must take care of running into the window border.
1451 */
1452 void
1453popup_set_wantpos_rowcol(win_T *wp, int row, int col)
1454{
1455 wp->w_wantline = row;
1456 wp->w_wantcol = col;
1457 popup_adjust_position(wp);
1458}
1459
1460/*
1461 * Add a border and lef&right padding.
1462 */
1463 static void
1464add_border_left_right_padding(win_T *wp)
1465{
1466 int i;
1467
1468 for (i = 0; i < 4; ++i)
1469 {
1470 wp->w_popup_border[i] = 1;
1471 wp->w_popup_padding[i] = (i & 1) ? 1 : 0;
1472 }
1473}
1474
1475/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001476 * popup_create({text}, {options})
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001477 * popup_atcursor({text}, {options})
Bram Moolenaar79648732019-07-18 21:43:07 +02001478 * etc.
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001479 * When creating a preview or info popup "argvars" and "rettv" are NULL.
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001480 */
Bram Moolenaara730e552019-06-16 19:05:31 +02001481 static win_T *
Bram Moolenaar17627312019-06-02 19:53:44 +02001482popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001483{
Bram Moolenaara3fce622019-06-20 02:31:49 +02001484 win_T *wp;
1485 tabpage_T *tp = NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001486 int tabnr = 0;
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001487 int new_buffer;
1488 buf_T *buf = NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001489 dict_T *d = NULL;
Bram Moolenaara3fce622019-06-20 02:31:49 +02001490 int nr;
1491 int i;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001492
Bram Moolenaar79648732019-07-18 21:43:07 +02001493 if (argvars != NULL)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001494 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001495 // Check that arguments look OK.
Bram Moolenaar79648732019-07-18 21:43:07 +02001496 if (argvars[0].v_type == VAR_NUMBER)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001497 {
Bram Moolenaar79648732019-07-18 21:43:07 +02001498 buf = buflist_findnr( argvars[0].vval.v_number);
1499 if (buf == NULL)
1500 {
1501 semsg(_(e_nobufnr), argvars[0].vval.v_number);
1502 return NULL;
1503 }
1504 }
1505 else if (!(argvars[0].v_type == VAR_STRING
1506 && argvars[0].vval.v_string != NULL)
1507 && !(argvars[0].v_type == VAR_LIST
1508 && argvars[0].vval.v_list != NULL))
1509 {
1510 emsg(_(e_listreq));
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001511 return NULL;
1512 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001513 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
Bram Moolenaara3fce622019-06-20 02:31:49 +02001514 {
Bram Moolenaar79648732019-07-18 21:43:07 +02001515 emsg(_(e_dictreq));
Bram Moolenaara3fce622019-06-20 02:31:49 +02001516 return NULL;
1517 }
Bram Moolenaar79648732019-07-18 21:43:07 +02001518 d = argvars[1].vval.v_dict;
1519 }
1520
1521 if (d != NULL)
1522 {
1523 if (dict_find(d, (char_u *)"tabpage", -1) != NULL)
1524 tabnr = (int)dict_get_number(d, (char_u *)"tabpage");
1525 else if (type == TYPE_NOTIFICATION)
1526 tabnr = -1; // notifications are global by default
1527 else
1528 tabnr = 0;
1529 if (tabnr > 0)
1530 {
1531 tp = find_tabpage(tabnr);
1532 if (tp == NULL)
1533 {
1534 semsg(_("E997: Tabpage not found: %d"), tabnr);
1535 return NULL;
1536 }
1537 }
Bram Moolenaara3fce622019-06-20 02:31:49 +02001538 }
1539
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001540 // Create the window and buffer.
1541 wp = win_alloc_popup_win();
1542 if (wp == NULL)
Bram Moolenaara730e552019-06-16 19:05:31 +02001543 return NULL;
Bram Moolenaar79648732019-07-18 21:43:07 +02001544 if (rettv != NULL)
1545 rettv->vval.v_number = wp->w_id;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001546 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02001547 wp->w_popup_flags = POPF_IS_POPUP | POPF_MAPPING;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001548
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001549 if (buf != NULL)
1550 {
1551 // use existing buffer
1552 new_buffer = FALSE;
Bram Moolenaar7866b872019-07-01 22:21:01 +02001553 win_init_popup_win(wp, buf);
Bram Moolenaar46451042019-08-24 15:50:46 +02001554 set_local_options_default(wp, FALSE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001555 buffer_ensure_loaded(buf);
1556 }
1557 else
1558 {
1559 // create a new buffer associated with the popup
1560 new_buffer = TRUE;
Bram Moolenaar00b0d6d2019-08-21 22:25:30 +02001561 buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_DUMMY|BLN_REUSE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001562 if (buf == NULL)
1563 return NULL;
1564 ml_open(buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +02001565
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001566 win_init_popup_win(wp, buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +02001567
Bram Moolenaar46451042019-08-24 15:50:46 +02001568 set_local_options_default(wp, TRUE);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001569 set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001570 (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001571 set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
Bram Moolenaar79648732019-07-18 21:43:07 +02001572 (char_u *)"wipe", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001573 buf->b_p_ul = -1; // no undo
1574 buf->b_p_swf = FALSE; // no swap file
1575 buf->b_p_bl = FALSE; // unlisted buffer
1576 buf->b_locked = TRUE;
1577 wp->w_p_wrap = TRUE; // 'wrap' is default on
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001578
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001579 // Avoid that 'buftype' is reset when this buffer is entered.
1580 buf->b_p_initialized = TRUE;
1581 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001582
Bram Moolenaara3fce622019-06-20 02:31:49 +02001583 if (tp != NULL)
1584 {
1585 // popup on specified tab page
1586 wp->w_next = tp->tp_first_popupwin;
1587 tp->tp_first_popupwin = wp;
1588 }
1589 else if (tabnr == 0)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001590 {
Bram Moolenaarfc06cbb2019-06-15 14:14:31 +02001591 // popup on current tab page
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02001592 wp->w_next = curtab->tp_first_popupwin;
1593 curtab->tp_first_popupwin = wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001594 }
Bram Moolenaara3fce622019-06-20 02:31:49 +02001595 else // (tabnr < 0)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001596 {
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001597 win_T *prev = first_popupwin;
1598
1599 // Global popup: add at the end, so that it gets displayed on top of
1600 // older ones with the same zindex. Matters for notifications.
1601 if (first_popupwin == NULL)
1602 first_popupwin = wp;
1603 else
1604 {
1605 while (prev->w_next != NULL)
1606 prev = prev->w_next;
1607 prev->w_next = wp;
1608 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001609 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001610
Bram Moolenaar79648732019-07-18 21:43:07 +02001611 if (new_buffer && argvars != NULL)
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02001612 popup_set_buffer_text(buf, argvars[0]);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001613
Bram Moolenaar79648732019-07-18 21:43:07 +02001614 if (type == TYPE_ATCURSOR || type == TYPE_PREVIEW)
Bram Moolenaar17627312019-06-02 19:53:44 +02001615 {
1616 wp->w_popup_pos = POPPOS_BOTLEFT;
Bram Moolenaar79648732019-07-18 21:43:07 +02001617 }
1618 if (type == TYPE_ATCURSOR)
1619 {
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001620 popup_set_wantpos_cursor(wp, 0);
Bram Moolenaar17627312019-06-02 19:53:44 +02001621 set_moved_values(wp);
1622 set_moved_columns(wp, FIND_STRING);
1623 }
1624
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001625 if (type == TYPE_BEVAL)
1626 {
1627 wp->w_popup_pos = POPPOS_BOTLEFT;
1628
1629 // by default use the mouse position
1630 wp->w_wantline = mouse_row;
1631 if (wp->w_wantline <= 0) // mouse on first line
1632 {
1633 wp->w_wantline = 2;
1634 wp->w_popup_pos = POPPOS_TOPLEFT;
1635 }
1636 wp->w_wantcol = mouse_col + 1;
1637 set_mousemoved_values(wp);
1638 set_mousemoved_columns(wp, FIND_IDENT + FIND_STRING + FIND_EVAL);
1639 }
1640
Bram Moolenaar33796b32019-06-08 16:01:13 +02001641 // set default values
1642 wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001643 wp->w_popup_close = POPCLOSE_NONE;
Bram Moolenaar33796b32019-06-08 16:01:13 +02001644
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001645 if (type == TYPE_NOTIFICATION)
1646 {
1647 win_T *twp, *nextwin;
1648 int height = buf->b_ml.ml_line_count + 3;
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001649
1650 // Try to not overlap with another global popup. Guess we need 3
1651 // more screen lines than buffer lines.
1652 wp->w_wantline = 1;
1653 for (twp = first_popupwin; twp != NULL; twp = nextwin)
1654 {
1655 nextwin = twp->w_next;
1656 if (twp != wp
1657 && twp->w_zindex == POPUPWIN_NOTIFICATION_ZINDEX
1658 && twp->w_winrow <= wp->w_wantline - 1 + height
1659 && twp->w_winrow + popup_height(twp) > wp->w_wantline - 1)
1660 {
1661 // move to below this popup and restart the loop to check for
1662 // overlap with other popups
1663 wp->w_wantline = twp->w_winrow + popup_height(twp) + 1;
1664 nextwin = first_popupwin;
1665 }
1666 }
1667 if (wp->w_wantline + height > Rows)
1668 {
1669 // can't avoid overlap, put on top in the hope that message goes
1670 // away soon.
1671 wp->w_wantline = 1;
1672 }
1673
1674 wp->w_wantcol = 10;
1675 wp->w_zindex = POPUPWIN_NOTIFICATION_ZINDEX;
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001676 wp->w_minwidth = 20;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001677 wp->w_popup_flags |= POPF_DRAG;
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001678 wp->w_popup_close = POPCLOSE_CLICK;
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001679 for (i = 0; i < 4; ++i)
1680 wp->w_popup_border[i] = 1;
1681 wp->w_popup_padding[1] = 1;
1682 wp->w_popup_padding[3] = 1;
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001683
1684 nr = syn_name2id((char_u *)"PopupNotification");
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001685 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
Bram Moolenaardfa97f22019-06-15 14:31:55 +02001686 (char_u *)(nr == 0 ? "WarningMsg" : "PopupNotification"),
1687 OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001688 }
1689
Bram Moolenaara730e552019-06-16 19:05:31 +02001690 if (type == TYPE_DIALOG || type == TYPE_MENU)
Bram Moolenaara42d9452019-06-15 21:46:30 +02001691 {
Bram Moolenaara42d9452019-06-15 21:46:30 +02001692 wp->w_popup_pos = POPPOS_CENTER;
1693 wp->w_zindex = POPUPWIN_DIALOG_ZINDEX;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001694 wp->w_popup_flags |= POPF_DRAG;
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02001695 wp->w_popup_flags &= ~POPF_MAPPING;
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001696 add_border_left_right_padding(wp);
Bram Moolenaara42d9452019-06-15 21:46:30 +02001697 }
1698
Bram Moolenaara730e552019-06-16 19:05:31 +02001699 if (type == TYPE_MENU)
1700 {
1701 typval_T tv;
1702 callback_T callback;
1703
1704 tv.v_type = VAR_STRING;
1705 tv.vval.v_string = (char_u *)"popup_filter_menu";
1706 callback = get_callback(&tv);
1707 if (callback.cb_name != NULL)
1708 set_callback(&wp->w_filter_cb, &callback);
1709
1710 wp->w_p_wrap = 0;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02001711 wp->w_popup_flags |= POPF_CURSORLINE;
Bram Moolenaara730e552019-06-16 19:05:31 +02001712 }
1713
Bram Moolenaar79648732019-07-18 21:43:07 +02001714 if (type == TYPE_PREVIEW)
1715 {
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001716 wp->w_popup_flags |= POPF_DRAG | POPF_RESIZE;
Bram Moolenaar79648732019-07-18 21:43:07 +02001717 wp->w_popup_close = POPCLOSE_BUTTON;
1718 for (i = 0; i < 4; ++i)
1719 wp->w_popup_border[i] = 1;
1720 parse_previewpopup(wp);
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001721 popup_set_wantpos_cursor(wp, wp->w_minwidth);
1722 }
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001723# ifdef FEAT_QUICKFIX
Bram Moolenaar576a4a62019-08-18 15:25:17 +02001724 if (type == TYPE_INFO)
1725 {
1726 wp->w_popup_pos = POPPOS_TOPLEFT;
1727 wp->w_popup_flags |= POPF_DRAG | POPF_RESIZE;
1728 wp->w_popup_close = POPCLOSE_BUTTON;
1729 add_border_left_right_padding(wp);
Bram Moolenaar62a0cb42019-08-18 16:35:23 +02001730 parse_completepopup(wp);
Bram Moolenaar79648732019-07-18 21:43:07 +02001731 }
Bram Moolenaar36e4d982019-08-20 21:12:16 +02001732# endif
Bram Moolenaar79648732019-07-18 21:43:07 +02001733
Bram Moolenaarae943152019-06-16 22:54:14 +02001734 for (i = 0; i < 4; ++i)
1735 VIM_CLEAR(wp->w_border_highlight[i]);
1736 for (i = 0; i < 8; ++i)
1737 wp->w_border_char[i] = 0;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02001738 wp->w_want_scrollbar = 1;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02001739 wp->w_popup_fixed = 0;
Bram Moolenaarae943152019-06-16 22:54:14 +02001740
Bram Moolenaar79648732019-07-18 21:43:07 +02001741 if (d != NULL)
1742 // Deal with options.
1743 apply_options(wp, d);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001744
Bram Moolenaar0fcf26b2019-06-23 01:03:51 +02001745#ifdef FEAT_TIMERS
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001746 if (type == TYPE_NOTIFICATION && wp->w_popup_timer == NULL)
1747 popup_add_timeout(wp, 3000);
Bram Moolenaar0fcf26b2019-06-23 01:03:51 +02001748#endif
Bram Moolenaar68d48f42019-06-12 22:42:41 +02001749
Bram Moolenaar60cdb302019-05-27 21:54:10 +02001750 popup_adjust_position(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001751
1752 wp->w_vsep_width = 0;
1753
1754 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02001755 popup_mask_refresh = TRUE;
Bram Moolenaara730e552019-06-16 19:05:31 +02001756
1757 return wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001758}
1759
1760/*
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +02001761 * popup_clear()
1762 */
1763 void
1764f_popup_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1765{
1766 close_all_popups();
1767}
1768
1769/*
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001770 * popup_create({text}, {options})
1771 */
1772 void
1773f_popup_create(typval_T *argvars, typval_T *rettv)
1774{
Bram Moolenaar17627312019-06-02 19:53:44 +02001775 popup_create(argvars, rettv, TYPE_NORMAL);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001776}
1777
1778/*
1779 * popup_atcursor({text}, {options})
1780 */
1781 void
1782f_popup_atcursor(typval_T *argvars, typval_T *rettv)
1783{
Bram Moolenaar17627312019-06-02 19:53:44 +02001784 popup_create(argvars, rettv, TYPE_ATCURSOR);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +02001785}
1786
1787/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001788 * popup_beval({text}, {options})
1789 */
1790 void
1791f_popup_beval(typval_T *argvars, typval_T *rettv)
1792{
1793 popup_create(argvars, rettv, TYPE_BEVAL);
1794}
1795
1796/*
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001797 * Invoke the close callback for window "wp" with value "result".
1798 * Careful: The callback may make "wp" invalid!
1799 */
1800 static void
1801invoke_popup_callback(win_T *wp, typval_T *result)
1802{
1803 typval_T rettv;
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001804 typval_T argv[3];
1805
1806 argv[0].v_type = VAR_NUMBER;
1807 argv[0].vval.v_number = (varnumber_T)wp->w_id;
1808
1809 if (result != NULL && result->v_type != VAR_UNKNOWN)
1810 copy_tv(result, &argv[1]);
1811 else
1812 {
1813 argv[1].v_type = VAR_NUMBER;
1814 argv[1].vval.v_number = 0;
1815 }
1816
1817 argv[2].v_type = VAR_UNKNOWN;
1818
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02001819 call_callback(&wp->w_close_cb, -1, &rettv, 2, argv);
Bram Moolenaar9eaac892019-06-01 22:49:29 +02001820 if (result != NULL)
1821 clear_tv(&argv[1]);
1822 clear_tv(&rettv);
1823}
1824
1825/*
Bram Moolenaar3397f742019-06-02 18:40:06 +02001826 * Close popup "wp" and invoke any close callback for it.
1827 */
1828 static void
1829popup_close_and_callback(win_T *wp, typval_T *arg)
1830{
1831 int id = wp->w_id;
1832
1833 if (wp->w_close_cb.cb_name != NULL)
1834 // Careful: This may make "wp" invalid.
1835 invoke_popup_callback(wp, arg);
1836
1837 popup_close(id);
1838}
1839
1840/*
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001841 * Close popup "wp" because of a mouse click.
1842 */
1843 void
1844popup_close_for_mouse_click(win_T *wp)
1845{
1846 typval_T res;
1847
1848 res.v_type = VAR_NUMBER;
1849 res.vval.v_number = -2;
1850 popup_close_and_callback(wp, &res);
1851}
1852
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001853 static void
1854check_mouse_moved(win_T *wp, win_T *mouse_wp)
1855{
1856 // Close the popup when all if these are true:
1857 // - the mouse is not on this popup
1858 // - "mousemoved" was used
1859 // - the mouse is no longer on the same screen row or the mouse column is
1860 // outside of the relevant text
1861 if (wp != mouse_wp
1862 && wp->w_popup_mouse_row != 0
1863 && (wp->w_popup_mouse_row != mouse_row
1864 || mouse_col < wp->w_popup_mouse_mincol
1865 || mouse_col > wp->w_popup_mouse_maxcol))
1866 {
1867 typval_T res;
1868
1869 res.v_type = VAR_NUMBER;
1870 res.vval.v_number = -2;
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001871 // Careful: this makes "wp" invalid.
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001872 popup_close_and_callback(wp, &res);
1873 }
1874}
1875
1876/*
1877 * Called when the mouse moved: may close a popup with "mousemoved".
1878 */
1879 void
1880popup_handle_mouse_moved(void)
1881{
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001882 win_T *wp, *nextwp;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001883 win_T *mouse_wp;
1884 int row = mouse_row;
1885 int col = mouse_col;
1886
1887 // find the window where the mouse is in
1888 mouse_wp = mouse_find_win(&row, &col, FIND_POPUP);
1889
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001890 for (wp = first_popupwin; wp != NULL; wp = nextwp)
1891 {
1892 nextwp = wp->w_next;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001893 check_mouse_moved(wp, mouse_wp);
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001894 }
1895 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = nextwp)
1896 {
1897 nextwp = wp->w_next;
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001898 check_mouse_moved(wp, mouse_wp);
Bram Moolenaar3e35d052019-07-07 20:43:34 +02001899 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02001900}
1901
Bram Moolenaar2e62b562019-06-30 18:07:00 +02001902/*
Bram Moolenaara730e552019-06-16 19:05:31 +02001903 * In a filter: check if the typed key is a mouse event that is used for
1904 * dragging the popup.
1905 */
1906 static void
1907filter_handle_drag(win_T *wp, int c, typval_T *rettv)
1908{
1909 int row = mouse_row;
1910 int col = mouse_col;
1911
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02001912 if ((wp->w_popup_flags & POPF_DRAG)
Bram Moolenaara730e552019-06-16 19:05:31 +02001913 && is_mouse_key(c)
1914 && (wp == popup_dragwin
1915 || wp == mouse_find_win(&row, &col, FIND_POPUP)))
1916 // do not consume the key, allow for dragging the popup
1917 rettv->vval.v_number = 0;
1918}
1919
Bram Moolenaara730e552019-06-16 19:05:31 +02001920/*
1921 * popup_filter_menu({text}, {options})
1922 */
1923 void
1924f_popup_filter_menu(typval_T *argvars, typval_T *rettv)
1925{
1926 int id = tv_get_number(&argvars[0]);
1927 win_T *wp = win_id2wp(id);
1928 char_u *key = tv_get_string(&argvars[1]);
1929 typval_T res;
1930 int c;
1931 linenr_T old_lnum;
1932
1933 // If the popup has been closed do not consume the key.
1934 if (wp == NULL)
1935 return;
1936
1937 c = *key;
1938 if (c == K_SPECIAL && key[1] != NUL)
1939 c = TO_SPECIAL(key[1], key[2]);
1940
1941 // consume all keys until done
1942 rettv->vval.v_number = 1;
1943 res.v_type = VAR_NUMBER;
1944
1945 old_lnum = wp->w_cursor.lnum;
1946 if ((c == 'k' || c == 'K' || c == K_UP) && wp->w_cursor.lnum > 1)
1947 --wp->w_cursor.lnum;
1948 if ((c == 'j' || c == 'J' || c == K_DOWN)
1949 && wp->w_cursor.lnum < wp->w_buffer->b_ml.ml_line_count)
1950 ++wp->w_cursor.lnum;
1951 if (old_lnum != wp->w_cursor.lnum)
1952 {
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02001953 // caller will call popup_highlight_curline()
Bram Moolenaara730e552019-06-16 19:05:31 +02001954 return;
1955 }
1956
1957 if (c == 'x' || c == 'X' || c == ESC || c == Ctrl_C)
1958 {
1959 // Cancelled, invoke callback with -1
1960 res.vval.v_number = -1;
1961 popup_close_and_callback(wp, &res);
1962 return;
1963 }
1964 if (c == ' ' || c == K_KENTER || c == CAR || c == NL)
1965 {
1966 // Invoke callback with current index.
1967 res.vval.v_number = wp->w_cursor.lnum;
1968 popup_close_and_callback(wp, &res);
1969 return;
1970 }
1971
1972 filter_handle_drag(wp, c, rettv);
1973}
1974
1975/*
Bram Moolenaara42d9452019-06-15 21:46:30 +02001976 * popup_filter_yesno({text}, {options})
1977 */
1978 void
1979f_popup_filter_yesno(typval_T *argvars, typval_T *rettv)
1980{
1981 int id = tv_get_number(&argvars[0]);
1982 win_T *wp = win_id2wp(id);
1983 char_u *key = tv_get_string(&argvars[1]);
1984 typval_T res;
Bram Moolenaara730e552019-06-16 19:05:31 +02001985 int c;
Bram Moolenaara42d9452019-06-15 21:46:30 +02001986
1987 // If the popup has been closed don't consume the key.
1988 if (wp == NULL)
1989 return;
1990
Bram Moolenaara730e552019-06-16 19:05:31 +02001991 c = *key;
1992 if (c == K_SPECIAL && key[1] != NUL)
1993 c = TO_SPECIAL(key[1], key[2]);
1994
Bram Moolenaara42d9452019-06-15 21:46:30 +02001995 // consume all keys until done
1996 rettv->vval.v_number = 1;
1997
Bram Moolenaara730e552019-06-16 19:05:31 +02001998 if (c == 'y' || c == 'Y')
Bram Moolenaara42d9452019-06-15 21:46:30 +02001999 res.vval.v_number = 1;
Bram Moolenaara730e552019-06-16 19:05:31 +02002000 else if (c == 'n' || c == 'N' || c == 'x' || c == 'X' || c == ESC)
Bram Moolenaara42d9452019-06-15 21:46:30 +02002001 res.vval.v_number = 0;
2002 else
2003 {
Bram Moolenaara730e552019-06-16 19:05:31 +02002004 filter_handle_drag(wp, c, rettv);
Bram Moolenaara42d9452019-06-15 21:46:30 +02002005 return;
2006 }
2007
2008 // Invoke callback
2009 res.v_type = VAR_NUMBER;
2010 popup_close_and_callback(wp, &res);
2011}
2012
2013/*
2014 * popup_dialog({text}, {options})
2015 */
2016 void
2017f_popup_dialog(typval_T *argvars, typval_T *rettv)
2018{
2019 popup_create(argvars, rettv, TYPE_DIALOG);
2020}
2021
2022/*
Bram Moolenaara730e552019-06-16 19:05:31 +02002023 * popup_menu({text}, {options})
2024 */
2025 void
2026f_popup_menu(typval_T *argvars, typval_T *rettv)
2027{
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002028 popup_create(argvars, rettv, TYPE_MENU);
Bram Moolenaara730e552019-06-16 19:05:31 +02002029}
2030
2031/*
Bram Moolenaara42d9452019-06-15 21:46:30 +02002032 * popup_notification({text}, {options})
2033 */
2034 void
2035f_popup_notification(typval_T *argvars, typval_T *rettv)
2036{
2037 popup_create(argvars, rettv, TYPE_NOTIFICATION);
2038}
2039
2040/*
2041 * Find the popup window with window-ID "id".
2042 * If the popup window does not exist NULL is returned.
2043 * If the window is not a popup window, and error message is given.
2044 */
2045 static win_T *
2046find_popup_win(int id)
2047{
2048 win_T *wp = win_id2wp(id);
2049
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002050 if (wp != NULL && !WIN_IS_POPUP(wp))
Bram Moolenaara42d9452019-06-15 21:46:30 +02002051 {
2052 semsg(_("E993: window %d is not a popup window"), id);
2053 return NULL;
2054 }
2055 return wp;
2056}
2057
2058/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002059 * popup_close({id})
2060 */
2061 void
2062f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
2063{
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002064 int id = (int)tv_get_number(argvars);
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002065 win_T *wp = find_popup_win(id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002066
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002067 if (wp != NULL)
Bram Moolenaar3397f742019-06-02 18:40:06 +02002068 popup_close_and_callback(wp, &argvars[1]);
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002069}
2070
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002071 static void
2072popup_hide(win_T *wp)
2073{
2074 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
2075 {
2076 wp->w_popup_flags |= POPF_HIDDEN;
2077 --wp->w_buffer->b_nwindows;
2078 redraw_all_later(NOT_VALID);
2079 popup_mask_refresh = TRUE;
2080 }
2081}
2082
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002083/*
2084 * popup_hide({id})
2085 */
2086 void
2087f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
2088{
2089 int id = (int)tv_get_number(argvars);
2090 win_T *wp = find_popup_win(id);
2091
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002092 if (wp != NULL)
2093 popup_hide(wp);
2094}
2095
2096 void
2097popup_show(win_T *wp)
2098{
2099 if ((wp->w_popup_flags & POPF_HIDDEN) != 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002100 {
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002101 wp->w_popup_flags &= ~POPF_HIDDEN;
2102 ++wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002103 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002104 popup_mask_refresh = TRUE;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002105 }
2106}
2107
2108/*
2109 * popup_show({id})
2110 */
2111 void
2112f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
2113{
2114 int id = (int)tv_get_number(argvars);
2115 win_T *wp = find_popup_win(id);
2116
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02002117 if (wp != NULL)
2118 popup_show(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002119}
2120
Bram Moolenaardc2ce582019-06-16 15:32:14 +02002121/*
2122 * popup_settext({id}, {text})
2123 */
2124 void
2125f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED)
2126{
2127 int id = (int)tv_get_number(&argvars[0]);
2128 win_T *wp = find_popup_win(id);
2129
2130 if (wp != NULL)
2131 {
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002132 if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_LIST)
2133 semsg(_(e_invarg2), tv_get_string(&argvars[1]));
2134 else
2135 {
2136 popup_set_buffer_text(wp->w_buffer, argvars[1]);
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02002137 redraw_win_later(wp, NOT_VALID);
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002138 popup_adjust_position(wp);
2139 }
Bram Moolenaardc2ce582019-06-16 15:32:14 +02002140 }
2141}
2142
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002143 static void
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002144popup_free(win_T *wp)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002145{
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002146 sign_undefine_by_name(popup_get_sign_name(wp), FALSE);
Bram Moolenaar868b7b62019-05-29 21:44:40 +02002147 wp->w_buffer->b_locked = FALSE;
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02002148 if (wp->w_winrow + popup_height(wp) >= cmdline_row)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002149 clear_cmdline = TRUE;
2150 win_free_popup(wp);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002151
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002152 redraw_all_later(NOT_VALID);
Bram Moolenaar33796b32019-06-08 16:01:13 +02002153 popup_mask_refresh = TRUE;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +02002154}
2155
Bram Moolenaarec583842019-05-26 14:11:23 +02002156/*
2157 * Close a popup window by Window-id.
Bram Moolenaar9eaac892019-06-01 22:49:29 +02002158 * Does not invoke the callback.
Bram Moolenaarec583842019-05-26 14:11:23 +02002159 */
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002160 void
Bram Moolenaarec583842019-05-26 14:11:23 +02002161popup_close(int id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002162{
2163 win_T *wp;
Bram Moolenaarec583842019-05-26 14:11:23 +02002164 tabpage_T *tp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002165 win_T *prev = NULL;
2166
Bram Moolenaarec583842019-05-26 14:11:23 +02002167 // go through global popups
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002168 for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
Bram Moolenaarec583842019-05-26 14:11:23 +02002169 if (wp->w_id == id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002170 {
2171 if (prev == NULL)
2172 first_popupwin = wp->w_next;
2173 else
2174 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002175 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +02002176 return;
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002177 }
2178
Bram Moolenaarec583842019-05-26 14:11:23 +02002179 // go through tab-local popups
2180 FOR_ALL_TABPAGES(tp)
2181 popup_close_tabpage(tp, id);
2182}
2183
2184/*
2185 * Close a popup window with Window-id "id" in tabpage "tp".
2186 */
2187 void
2188popup_close_tabpage(tabpage_T *tp, int id)
2189{
2190 win_T *wp;
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02002191 win_T **root = &tp->tp_first_popupwin;
Bram Moolenaarec583842019-05-26 14:11:23 +02002192 win_T *prev = NULL;
2193
Bram Moolenaarec583842019-05-26 14:11:23 +02002194 for (wp = *root; wp != NULL; prev = wp, wp = wp->w_next)
2195 if (wp->w_id == id)
2196 {
2197 if (prev == NULL)
2198 *root = wp->w_next;
2199 else
2200 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +02002201 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +02002202 return;
2203 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002204}
2205
2206 void
2207close_all_popups(void)
2208{
2209 while (first_popupwin != NULL)
2210 popup_close(first_popupwin->w_id);
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +02002211 while (curtab->tp_first_popupwin != NULL)
2212 popup_close(curtab->tp_first_popupwin->w_id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +02002213}
2214
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002215/*
2216 * popup_move({id}, {options})
2217 */
2218 void
2219f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
2220{
Bram Moolenaarae943152019-06-16 22:54:14 +02002221 dict_T *dict;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002222 int id = (int)tv_get_number(argvars);
2223 win_T *wp = find_popup_win(id);
2224
2225 if (wp == NULL)
2226 return; // invalid {id}
2227
2228 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
2229 {
2230 emsg(_(e_dictreq));
2231 return;
2232 }
Bram Moolenaarae943152019-06-16 22:54:14 +02002233 dict = argvars[1].vval.v_dict;
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002234
Bram Moolenaarae943152019-06-16 22:54:14 +02002235 apply_move_options(wp, dict);
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002236
2237 if (wp->w_winrow + wp->w_height >= cmdline_row)
2238 clear_cmdline = TRUE;
2239 popup_adjust_position(wp);
Bram Moolenaar60cdb302019-05-27 21:54:10 +02002240}
2241
Bram Moolenaarbc133542019-05-29 20:26:46 +02002242/*
Bram Moolenaarae943152019-06-16 22:54:14 +02002243 * popup_setoptions({id}, {options})
2244 */
2245 void
2246f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
2247{
2248 dict_T *dict;
2249 int id = (int)tv_get_number(argvars);
2250 win_T *wp = find_popup_win(id);
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002251 linenr_T old_firstline;
Bram Moolenaarae943152019-06-16 22:54:14 +02002252
2253 if (wp == NULL)
2254 return; // invalid {id}
2255
2256 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
2257 {
2258 emsg(_(e_dictreq));
2259 return;
2260 }
2261 dict = argvars[1].vval.v_dict;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002262 old_firstline = wp->w_firstline;
Bram Moolenaarae943152019-06-16 22:54:14 +02002263
2264 apply_move_options(wp, dict);
2265 apply_general_options(wp, dict);
2266
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002267 if (old_firstline != wp->w_firstline)
2268 redraw_win_later(wp, NOT_VALID);
Bram Moolenaarad24a712019-06-17 20:05:45 +02002269 popup_mask_refresh = TRUE;
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002270 popup_highlight_curline(wp);
Bram Moolenaarae943152019-06-16 22:54:14 +02002271 popup_adjust_position(wp);
2272}
2273
2274/*
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002275 * popup_getpos({id})
Bram Moolenaarbc133542019-05-29 20:26:46 +02002276 */
2277 void
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002278f_popup_getpos(typval_T *argvars, typval_T *rettv)
Bram Moolenaarbc133542019-05-29 20:26:46 +02002279{
2280 dict_T *dict;
2281 int id = (int)tv_get_number(argvars);
2282 win_T *wp = find_popup_win(id);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002283 int top_extra;
2284 int left_extra;
Bram Moolenaarbc133542019-05-29 20:26:46 +02002285
2286 if (rettv_dict_alloc(rettv) == OK)
2287 {
2288 if (wp == NULL)
2289 return; // invalid {id}
Bram Moolenaareb2310d2019-06-16 20:09:10 +02002290 top_extra = popup_top_extra(wp);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002291 left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
2292
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002293 // we know how much space we need, avoid resizing halfway
Bram Moolenaarbc133542019-05-29 20:26:46 +02002294 dict = rettv->vval.v_dict;
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002295 hash_lock_size(&dict->dv_hashtab, 11);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002296
Bram Moolenaarbc133542019-05-29 20:26:46 +02002297 dict_add_number(dict, "line", wp->w_winrow + 1);
2298 dict_add_number(dict, "col", wp->w_wincol + 1);
Bram Moolenaar68d48f42019-06-12 22:42:41 +02002299 dict_add_number(dict, "width", wp->w_width + left_extra
2300 + wp->w_popup_border[1] + wp->w_popup_padding[1]);
2301 dict_add_number(dict, "height", wp->w_height + top_extra
2302 + wp->w_popup_border[2] + wp->w_popup_padding[2]);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +02002303
2304 dict_add_number(dict, "core_line", wp->w_winrow + 1 + top_extra);
2305 dict_add_number(dict, "core_col", wp->w_wincol + 1 + left_extra);
2306 dict_add_number(dict, "core_width", wp->w_width);
2307 dict_add_number(dict, "core_height", wp->w_height);
2308
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002309 dict_add_number(dict, "scrollbar", wp->w_has_scrollbar);
Bram Moolenaar68acb412019-06-26 03:40:36 +02002310 dict_add_number(dict, "firstline", wp->w_topline);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002311 dict_add_number(dict, "visible",
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +02002312 win_valid(wp) && (wp->w_popup_flags & POPF_HIDDEN) == 0);
Bram Moolenaar7b73d7e2019-07-26 21:26:34 +02002313
2314 hash_unlock(&dict->dv_hashtab);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002315 }
2316}
Bram Moolenaarb4f06282019-07-12 21:07:54 +02002317/*
2318 * popup_locate({row}, {col})
2319 */
2320 void
2321f_popup_locate(typval_T *argvars, typval_T *rettv)
2322{
2323 int row = tv_get_number(&argvars[0]) - 1;
2324 int col = tv_get_number(&argvars[1]) - 1;
2325 win_T *wp;
2326
2327 wp = mouse_find_win(&row, &col, FIND_POPUP);
2328 if (WIN_IS_POPUP(wp))
2329 rettv->vval.v_number = wp->w_id;
2330}
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002331
2332/*
Bram Moolenaarae943152019-06-16 22:54:14 +02002333 * For popup_getoptions(): add a "border" or "padding" entry to "dict".
2334 */
2335 static void
2336get_padding_border(dict_T *dict, int *array, char *name)
2337{
2338 list_T *list;
2339 int i;
2340
2341 if (array[0] == 0 && array[1] == 0 && array[2] == 0 && array[3] == 0)
2342 return;
2343
2344 list = list_alloc();
2345 if (list != NULL)
2346 {
2347 dict_add_list(dict, name, list);
2348 if (array[0] != 1 || array[1] != 1 || array[2] != 1 || array[3] != 1)
2349 for (i = 0; i < 4; ++i)
2350 list_append_number(list, array[i]);
2351 }
2352}
2353
2354/*
2355 * For popup_getoptions(): add a "borderhighlight" entry to "dict".
2356 */
2357 static void
2358get_borderhighlight(dict_T *dict, win_T *wp)
2359{
2360 list_T *list;
2361 int i;
2362
2363 for (i = 0; i < 4; ++i)
2364 if (wp->w_border_highlight[i] != NULL)
2365 break;
2366 if (i == 4)
2367 return;
2368
2369 list = list_alloc();
2370 if (list != NULL)
2371 {
2372 dict_add_list(dict, "borderhighlight", list);
2373 for (i = 0; i < 4; ++i)
2374 list_append_string(list, wp->w_border_highlight[i], -1);
2375 }
2376}
2377
2378/*
2379 * For popup_getoptions(): add a "borderchars" entry to "dict".
2380 */
2381 static void
2382get_borderchars(dict_T *dict, win_T *wp)
2383{
2384 list_T *list;
2385 int i;
2386 char_u buf[NUMBUFLEN];
2387 int len;
2388
2389 for (i = 0; i < 8; ++i)
2390 if (wp->w_border_char[i] != 0)
2391 break;
2392 if (i == 8)
2393 return;
2394
2395 list = list_alloc();
2396 if (list != NULL)
2397 {
2398 dict_add_list(dict, "borderchars", list);
2399 for (i = 0; i < 8; ++i)
2400 {
2401 len = mb_char2bytes(wp->w_border_char[i], buf);
2402 list_append_string(list, buf, len);
2403 }
2404 }
2405}
2406
2407/*
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002408 * For popup_getoptions(): add a "moved" and "mousemoved" entry to "dict".
Bram Moolenaarae943152019-06-16 22:54:14 +02002409 */
2410 static void
2411get_moved_list(dict_T *dict, win_T *wp)
2412{
2413 list_T *list;
2414
2415 list = list_alloc();
2416 if (list != NULL)
2417 {
2418 dict_add_list(dict, "moved", list);
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002419 list_append_number(list, wp->w_popup_lnum);
Bram Moolenaarae943152019-06-16 22:54:14 +02002420 list_append_number(list, wp->w_popup_mincol);
2421 list_append_number(list, wp->w_popup_maxcol);
2422 }
Bram Moolenaarb3d17a22019-07-07 18:28:14 +02002423 list = list_alloc();
2424 if (list != NULL)
2425 {
2426 dict_add_list(dict, "mousemoved", list);
2427 list_append_number(list, wp->w_popup_mouse_row);
2428 list_append_number(list, wp->w_popup_mouse_mincol);
2429 list_append_number(list, wp->w_popup_mouse_maxcol);
2430 }
Bram Moolenaarae943152019-06-16 22:54:14 +02002431}
2432
2433/*
Bram Moolenaar33796b32019-06-08 16:01:13 +02002434 * popup_getoptions({id})
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002435 */
2436 void
2437f_popup_getoptions(typval_T *argvars, typval_T *rettv)
2438{
2439 dict_T *dict;
2440 int id = (int)tv_get_number(argvars);
2441 win_T *wp = find_popup_win(id);
Bram Moolenaara3fce622019-06-20 02:31:49 +02002442 tabpage_T *tp;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002443 int i;
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002444
2445 if (rettv_dict_alloc(rettv) == OK)
2446 {
2447 if (wp == NULL)
2448 return;
2449
2450 dict = rettv->vval.v_dict;
2451 dict_add_number(dict, "line", wp->w_wantline);
2452 dict_add_number(dict, "col", wp->w_wantcol);
2453 dict_add_number(dict, "minwidth", wp->w_minwidth);
2454 dict_add_number(dict, "minheight", wp->w_minheight);
2455 dict_add_number(dict, "maxheight", wp->w_maxheight);
2456 dict_add_number(dict, "maxwidth", wp->w_maxwidth);
Bram Moolenaar8d241042019-06-12 23:40:01 +02002457 dict_add_number(dict, "firstline", wp->w_firstline);
Bram Moolenaar75fb0852019-06-25 05:15:58 +02002458 dict_add_number(dict, "scrollbar", wp->w_want_scrollbar);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002459 dict_add_number(dict, "zindex", wp->w_zindex);
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02002460 dict_add_number(dict, "fixed", wp->w_popup_fixed);
Bram Moolenaarae943152019-06-16 22:54:14 +02002461 dict_add_string(dict, "title", wp->w_popup_title);
2462 dict_add_number(dict, "wrap", wp->w_p_wrap);
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02002463 dict_add_number(dict, "drag", (wp->w_popup_flags & POPF_DRAG) != 0);
Bram Moolenaarb8350ab2019-08-04 17:59:49 +02002464 dict_add_number(dict, "mapping", (wp->w_popup_flags & POPF_MAPPING) != 0);
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02002465 dict_add_number(dict, "resize", (wp->w_popup_flags & POPF_RESIZE) != 0);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002466 dict_add_number(dict, "cursorline",
2467 (wp->w_popup_flags & POPF_CURSORLINE) != 0);
Bram Moolenaarae943152019-06-16 22:54:14 +02002468 dict_add_string(dict, "highlight", wp->w_p_wcr);
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02002469 if (wp->w_scrollbar_highlight != NULL)
2470 dict_add_string(dict, "scrollbarhighlight",
2471 wp->w_scrollbar_highlight);
2472 if (wp->w_thumb_highlight != NULL)
2473 dict_add_string(dict, "thumbhighlight", wp->w_thumb_highlight);
Bram Moolenaarae943152019-06-16 22:54:14 +02002474
Bram Moolenaara3fce622019-06-20 02:31:49 +02002475 // find the tabpage that holds this popup
2476 i = 1;
2477 FOR_ALL_TABPAGES(tp)
2478 {
2479 win_T *p;
2480
2481 for (p = tp->tp_first_popupwin; p != NULL; p = wp->w_next)
2482 if (p->w_id == id)
2483 break;
2484 if (p != NULL)
2485 break;
2486 ++i;
2487 }
2488 if (tp == NULL)
2489 i = -1; // must be global
2490 else if (tp == curtab)
2491 i = 0;
2492 dict_add_number(dict, "tabpage", i);
2493
Bram Moolenaarae943152019-06-16 22:54:14 +02002494 get_padding_border(dict, wp->w_popup_padding, "padding");
2495 get_padding_border(dict, wp->w_popup_border, "border");
2496 get_borderhighlight(dict, wp);
2497 get_borderchars(dict, wp);
2498 get_moved_list(dict, wp);
2499
2500 if (wp->w_filter_cb.cb_name != NULL)
2501 dict_add_callback(dict, "filter", &wp->w_filter_cb);
2502 if (wp->w_close_cb.cb_name != NULL)
2503 dict_add_callback(dict, "callback", &wp->w_close_cb);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02002504
2505 for (i = 0; i < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
2506 ++i)
2507 if (wp->w_popup_pos == poppos_entries[i].pp_val)
2508 {
2509 dict_add_string(dict, "pos",
2510 (char_u *)poppos_entries[i].pp_name);
2511 break;
2512 }
2513
Bram Moolenaar2e62b562019-06-30 18:07:00 +02002514 dict_add_string(dict, "close", (char_u *)(
2515 wp->w_popup_close == POPCLOSE_BUTTON ? "button"
2516 : wp->w_popup_close == POPCLOSE_CLICK ? "click" : "none"));
2517
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02002518# if defined(FEAT_TIMERS)
2519 dict_add_number(dict, "time", wp->w_popup_timer != NULL
2520 ? (long)wp->w_popup_timer->tr_interval : 0L);
2521# endif
Bram Moolenaarbc133542019-05-29 20:26:46 +02002522 }
2523}
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002524
2525 int
Bram Moolenaar8cdbd5b2019-06-16 15:50:45 +02002526error_if_popup_window()
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002527{
Bram Moolenaar5b8cfed2019-06-30 22:16:10 +02002528 if (WIN_IS_POPUP(curwin))
Bram Moolenaar815b76b2019-06-01 14:15:52 +02002529 {
2530 emsg(_("E994: Not allowed in a popup window"));
2531 return TRUE;
2532 }
2533 return FALSE;
2534}
2535
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002536/*
2537 * Reset all the POPF_HANDLED flags in global popup windows and popup windows
Bram Moolenaarfc06cbb2019-06-15 14:14:31 +02002538 * in the current tab page.
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002539 */
2540 void
2541popup_reset_handled()
2542{
2543 win_T *wp;
2544
2545 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2546 wp->w_popup_flags &= ~POPF_HANDLED;
2547 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
2548 wp->w_popup_flags &= ~POPF_HANDLED;
2549}
2550
2551/*
2552 * Find the next visible popup where POPF_HANDLED is not set.
2553 * Must have called popup_reset_handled() first.
2554 * When "lowest" is TRUE find the popup with the lowest zindex, otherwise the
2555 * popup with the highest zindex.
2556 */
2557 win_T *
2558find_next_popup(int lowest)
2559{
2560 win_T *wp;
2561 win_T *found_wp;
2562 int found_zindex;
2563
2564 found_zindex = lowest ? INT_MAX : 0;
2565 found_wp = NULL;
2566 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2567 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
2568 && (lowest ? wp->w_zindex < found_zindex
2569 : wp->w_zindex > found_zindex))
2570 {
2571 found_zindex = wp->w_zindex;
2572 found_wp = wp;
2573 }
2574 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
2575 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
2576 && (lowest ? wp->w_zindex < found_zindex
2577 : wp->w_zindex > found_zindex))
2578 {
2579 found_zindex = wp->w_zindex;
2580 found_wp = wp;
2581 }
2582
2583 if (found_wp != NULL)
2584 found_wp->w_popup_flags |= POPF_HANDLED;
2585 return found_wp;
2586}
2587
2588/*
2589 * Invoke the filter callback for window "wp" with typed character "c".
2590 * Uses the global "mod_mask" for modifiers.
2591 * Returns the return value of the filter.
2592 * Careful: The filter may make "wp" invalid!
2593 */
2594 static int
2595invoke_popup_filter(win_T *wp, int c)
2596{
2597 int res;
2598 typval_T rettv;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002599 typval_T argv[3];
2600 char_u buf[NUMBUFLEN];
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002601 linenr_T old_lnum = wp->w_cursor.lnum;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002602
Bram Moolenaara42d9452019-06-15 21:46:30 +02002603 // Emergency exit: CTRL-C closes the popup.
2604 if (c == Ctrl_C)
2605 {
2606 rettv.v_type = VAR_NUMBER;
2607 rettv.vval.v_number = -1;
2608 popup_close_and_callback(wp, &rettv);
2609 return 1;
2610 }
2611
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002612 argv[0].v_type = VAR_NUMBER;
2613 argv[0].vval.v_number = (varnumber_T)wp->w_id;
2614
2615 // Convert the number to a string, so that the function can use:
2616 // if a:c == "\<F2>"
2617 buf[special_to_buf(c, mod_mask, TRUE, buf)] = NUL;
2618 argv[1].v_type = VAR_STRING;
2619 argv[1].vval.v_string = vim_strsave(buf);
2620
2621 argv[2].v_type = VAR_UNKNOWN;
2622
Bram Moolenaara42d9452019-06-15 21:46:30 +02002623 // NOTE: The callback might close the popup, thus make "wp" invalid.
Bram Moolenaarc6538bc2019-08-03 18:17:11 +02002624 call_callback(&wp->w_filter_cb, -1, &rettv, 2, argv);
Bram Moolenaarcb5ff342019-07-20 16:51:19 +02002625 if (win_valid_popup(wp) && old_lnum != wp->w_cursor.lnum)
Bram Moolenaardf9c6ca2019-07-18 13:46:42 +02002626 popup_highlight_curline(wp);
2627
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002628 res = tv_get_number(&rettv);
2629 vim_free(argv[1].vval.v_string);
2630 clear_tv(&rettv);
2631 return res;
2632}
2633
2634/*
2635 * Called when "c" was typed: invoke popup filter callbacks.
2636 * Returns TRUE when the character was consumed,
2637 */
2638 int
2639popup_do_filter(int c)
2640{
2641 int res = FALSE;
Bram Moolenaara42d9452019-06-15 21:46:30 +02002642 win_T *wp;
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002643
2644 popup_reset_handled();
2645
Bram Moolenaarf6396232019-08-24 19:36:00 +02002646 if (c == K_LEFTMOUSE)
2647 {
2648 int row = mouse_row;
2649 int col = mouse_col;
2650
2651 wp = mouse_find_win(&row, &col, FIND_POPUP);
2652 if (wp != NULL && popup_close_if_on_X(wp, row, col))
2653 return TRUE;
2654 }
2655
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02002656 while (!res && (wp = find_next_popup(FALSE)) != NULL)
2657 if (wp->w_filter_cb.cb_name != NULL)
2658 res = invoke_popup_filter(wp, c);
2659
2660 return res;
2661}
2662
Bram Moolenaar3397f742019-06-02 18:40:06 +02002663/*
Bram Moolenaar749fa0a2019-08-03 16:18:07 +02002664 * Return TRUE if there is a popup visible with a filter callback and the
2665 * "mapping" property off.
2666 */
2667 int
2668popup_no_mapping(void)
2669{
2670 int round;
2671 win_T *wp;
2672
2673 for (round = 1; round <= 2; ++round)
2674 for (wp = round == 1 ? first_popupwin : curtab->tp_first_popupwin;
2675 wp != NULL; wp = wp->w_next)
2676 if (wp->w_filter_cb.cb_name != NULL
2677 && (wp->w_popup_flags & (POPF_HIDDEN | POPF_MAPPING)) == 0)
2678 return TRUE;
2679 return FALSE;
2680}
2681
2682/*
Bram Moolenaar3397f742019-06-02 18:40:06 +02002683 * Called when the cursor moved: check if any popup needs to be closed if the
2684 * cursor moved far enough.
2685 */
2686 void
2687popup_check_cursor_pos()
2688{
2689 win_T *wp;
2690 typval_T tv;
2691
2692 popup_reset_handled();
2693 while ((wp = find_next_popup(TRUE)) != NULL)
2694 if (wp->w_popup_curwin != NULL
2695 && (curwin != wp->w_popup_curwin
2696 || curwin->w_cursor.lnum != wp->w_popup_lnum
2697 || curwin->w_cursor.col < wp->w_popup_mincol
2698 || curwin->w_cursor.col > wp->w_popup_maxcol))
2699 {
2700 tv.v_type = VAR_NUMBER;
2701 tv.vval.v_number = -1;
2702 popup_close_and_callback(wp, &tv);
2703 }
2704}
2705
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002706/*
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002707 * Update "w_popup_mask_cells".
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002708 */
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002709 static void
2710popup_update_mask(win_T *wp, int width, int height)
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002711{
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002712 listitem_T *lio, *li;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002713 char_u *cells;
2714 int row, col;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002715
2716 if (wp->w_popup_mask == NULL)
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002717 return;
2718 if (wp->w_popup_mask_cells != NULL
2719 && wp->w_popup_mask_height == height
2720 && wp->w_popup_mask_width == width)
2721 return; // cache is still valid
2722
2723 vim_free(wp->w_popup_mask_cells);
2724 wp->w_popup_mask_cells = alloc_clear(width * height);
2725 if (wp->w_popup_mask_cells == NULL)
2726 return;
2727 cells = wp->w_popup_mask_cells;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002728
2729 for (lio = wp->w_popup_mask->lv_first; lio != NULL; lio = lio->li_next)
2730 {
2731 int cols, cole;
2732 int lines, linee;
2733
2734 li = lio->li_tv.vval.v_list->lv_first;
2735 cols = tv_get_number(&li->li_tv);
2736 if (cols < 0)
2737 cols = width + cols + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002738 li = li->li_next;
2739 cole = tv_get_number(&li->li_tv);
2740 if (cole < 0)
2741 cole = width + cole + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002742 li = li->li_next;
2743 lines = tv_get_number(&li->li_tv);
2744 if (lines < 0)
2745 lines = height + lines + 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002746 li = li->li_next;
2747 linee = tv_get_number(&li->li_tv);
2748 if (linee < 0)
2749 linee = height + linee + 1;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002750
2751 for (row = lines - 1; row < linee && row < height; ++row)
2752 for (col = cols - 1; col < cole && col < width; ++col)
2753 cells[row * width + col] = 1;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002754 }
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002755}
2756
2757/*
2758 * Return TRUE if "col" / "line" matches with an entry in w_popup_mask.
2759 * "col" and "line" are screen coordinates.
2760 */
2761 static int
2762popup_masked(win_T *wp, int width, int height, int screencol, int screenline)
2763{
2764 int col = screencol - wp->w_wincol + wp->w_popup_leftoff;
2765 int line = screenline - wp->w_winrow;
2766
2767 return col >= 0 && col < width
2768 && line >= 0 && line < height
2769 && wp->w_popup_mask_cells[line * width + col];
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002770}
2771
2772/*
2773 * Set flags in popup_transparent[] for window "wp" to "val".
2774 */
2775 static void
2776update_popup_transparent(win_T *wp, int val)
2777{
2778 if (wp->w_popup_mask != NULL)
2779 {
2780 int width = popup_width(wp);
2781 int height = popup_height(wp);
2782 listitem_T *lio, *li;
2783 int cols, cole;
2784 int lines, linee;
2785 int col, line;
2786
2787 for (lio = wp->w_popup_mask->lv_first; lio != NULL; lio = lio->li_next)
2788 {
2789 li = lio->li_tv.vval.v_list->lv_first;
2790 cols = tv_get_number(&li->li_tv);
2791 if (cols < 0)
2792 cols = width + cols + 1;
2793 li = li->li_next;
2794 cole = tv_get_number(&li->li_tv);
2795 if (cole < 0)
2796 cole = width + cole + 1;
2797 li = li->li_next;
2798 lines = tv_get_number(&li->li_tv);
2799 if (lines < 0)
2800 lines = height + lines + 1;
2801 li = li->li_next;
2802 linee = tv_get_number(&li->li_tv);
2803 if (linee < 0)
2804 linee = height + linee + 1;
2805
2806 --cols;
Bram Moolenaard529ba52019-07-02 23:13:53 +02002807 cols -= wp->w_popup_leftoff;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02002808 if (cols < 0)
2809 cols = 0;
Bram Moolenaard529ba52019-07-02 23:13:53 +02002810 cole -= wp->w_popup_leftoff;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002811 --lines;
Bram Moolenaar711d02c2019-06-28 04:06:50 +02002812 if (lines < 0)
2813 lines = 0;
Bram Moolenaarb4207472019-07-12 16:05:45 +02002814 for (line = lines; line < linee
2815 && line + wp->w_winrow < screen_Rows; ++line)
2816 for (col = cols; col < cole
2817 && col + wp->w_wincol < screen_Columns; ++col)
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002818 popup_transparent[(line + wp->w_winrow) * screen_Columns
2819 + col + wp->w_wincol] = val;
2820 }
2821 }
2822}
2823
2824/*
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002825 * Update "popup_mask" if needed.
2826 * Also recomputes the popup size and positions.
2827 * Also updates "popup_visible".
2828 * Also marks window lines for redrawing.
2829 */
2830 void
2831may_update_popup_mask(int type)
2832{
2833 win_T *wp;
2834 short *mask;
2835 int line, col;
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002836 int redraw_all_popups = FALSE;
2837 int redrawing_all_win;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002838
2839 // Need to recompute when switching tabs.
2840 // Also recompute when the type is CLEAR or NOT_VALID, something basic
2841 // (such as the screen size) must have changed.
2842 if (popup_mask_tab != curtab || type >= NOT_VALID)
2843 {
2844 popup_mask_refresh = TRUE;
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002845 redraw_all_popups = TRUE;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002846 }
2847 if (!popup_mask_refresh)
2848 {
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002849 // Check if any popup window buffer has changed.
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002850 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
2851 if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
2852 popup_mask_refresh = TRUE;
2853 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
2854 if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
2855 popup_mask_refresh = TRUE;
2856 if (!popup_mask_refresh)
2857 return;
2858 }
2859
2860 // Need to update the mask, something has changed.
2861 popup_mask_refresh = FALSE;
2862 popup_mask_tab = curtab;
2863 popup_visible = FALSE;
2864
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002865 // If redrawing all windows, just update "popup_mask".
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002866 // If redrawing only what is needed, update "popup_mask_next" and then
2867 // compare with "popup_mask" to see what changed.
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002868 redrawing_all_win = TRUE;
2869 FOR_ALL_WINDOWS(wp)
2870 if (wp->w_redr_type < SOME_VALID)
2871 redrawing_all_win = FALSE;
2872 if (redrawing_all_win)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002873 mask = popup_mask;
2874 else
2875 mask = popup_mask_next;
2876 vim_memset(mask, 0, screen_Rows * screen_Columns * sizeof(short));
2877
2878 // Find the window with the lowest zindex that hasn't been handled yet,
2879 // so that the window with a higher zindex overwrites the value in
2880 // popup_mask.
2881 popup_reset_handled();
2882 while ((wp = find_next_popup(TRUE)) != NULL)
2883 {
Bram Moolenaard529ba52019-07-02 23:13:53 +02002884 int width;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002885 int height;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002886
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002887 popup_visible = TRUE;
2888
2889 // Recompute the position if the text changed.
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002890 if (redraw_all_popups
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002891 || wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
2892 popup_adjust_position(wp);
2893
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002894 width = popup_width(wp);
Bram Moolenaard529ba52019-07-02 23:13:53 +02002895 height = popup_height(wp);
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002896 popup_update_mask(wp, width, height);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002897 for (line = wp->w_winrow;
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002898 line < wp->w_winrow + height && line < screen_Rows; ++line)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002899 for (col = wp->w_wincol;
Bram Moolenaare865dcb2019-07-26 22:15:50 +02002900 col < wp->w_wincol + width - wp->w_popup_leftoff
2901 && col < screen_Columns; ++col)
2902 if (wp->w_popup_mask_cells == NULL
2903 || !popup_masked(wp, width, height, col, line))
Bram Moolenaarc662ec92019-06-23 00:15:57 +02002904 mask[line * screen_Columns + col] = wp->w_zindex;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002905 }
2906
2907 // Only check which lines are to be updated if not already
2908 // updating all lines.
2909 if (mask == popup_mask_next)
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02002910 {
2911 int *plines_cache = ALLOC_CLEAR_MULT(int, Rows);
2912 win_T *prev_wp = NULL;
2913
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002914 for (line = 0; line < screen_Rows; ++line)
2915 {
2916 int col_done = 0;
2917
2918 for (col = 0; col < screen_Columns; ++col)
2919 {
2920 int off = line * screen_Columns + col;
2921
2922 if (popup_mask[off] != popup_mask_next[off])
2923 {
2924 popup_mask[off] = popup_mask_next[off];
2925
2926 if (line >= cmdline_row)
2927 {
2928 // the command line needs to be cleared if text below
2929 // the popup is now visible.
2930 if (!msg_scrolled && popup_mask_next[off] == 0)
2931 clear_cmdline = TRUE;
2932 }
2933 else if (col >= col_done)
2934 {
2935 linenr_T lnum;
2936 int line_cp = line;
2937 int col_cp = col;
2938
2939 // The screen position "line" / "col" needs to be
2940 // redrawn. Figure out what window that is and update
2941 // w_redraw_top and w_redr_bot. Only needs to be done
2942 // once for each window line.
2943 wp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP);
2944 if (wp != NULL)
2945 {
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02002946 if (wp != prev_wp)
2947 {
2948 vim_memset(plines_cache, 0, sizeof(int) * Rows);
2949 prev_wp = wp;
2950 }
2951
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002952 if (line_cp >= wp->w_height)
2953 // In (or below) status line
2954 wp->w_redr_status = TRUE;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002955 else
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002956 {
2957 // compute the position in the buffer line from
2958 // the position in the window
2959 mouse_comp_pos(wp, &line_cp, &col_cp,
2960 &lnum, plines_cache);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002961 redrawWinline(wp, lnum);
Bram Moolenaare9a891f2019-08-24 15:26:24 +02002962 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002963
2964 // This line is going to be redrawn, no need to
2965 // check until the right side of the window.
2966 col_done = wp->w_wincol + wp->w_width - 1;
2967 }
2968 }
2969 }
2970 }
2971 }
Bram Moolenaar9d5ffce2019-07-26 21:01:29 +02002972
2973 vim_free(plines_cache);
2974 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002975}
2976
2977/*
2978 * Return a string of "len" spaces in IObuff.
2979 */
2980 static char_u *
2981get_spaces(int len)
2982{
2983 vim_memset(IObuff, ' ', (size_t)len);
2984 IObuff[len] = NUL;
2985 return IObuff;
2986}
2987
2988/*
2989 * Update popup windows. They are drawn on top of normal windows.
2990 * "win_update" is called for each popup window, lowest zindex first.
2991 */
2992 void
2993update_popups(void (*win_update)(win_T *wp))
2994{
2995 win_T *wp;
2996 int top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02002997 int left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02002998 int total_width;
2999 int total_height;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003000 int top_padding;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003001 int popup_attr;
3002 int border_attr[4];
3003 int border_char[8];
3004 char_u buf[MB_MAXBYTES];
3005 int row;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003006 int wincol;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003007 int padcol = 0;
3008 int padwidth = 0;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003009 int i;
Bram Moolenaar6efd76a2019-06-26 04:06:57 +02003010 int sb_thumb_top = 0;
3011 int sb_thumb_height = 0;
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02003012 int attr_scroll = 0;
3013 int attr_thumb = 0;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003014
3015 // Find the window with the lowest zindex that hasn't been updated yet,
3016 // so that the window with a higher zindex is drawn later, thus goes on
3017 // top.
3018 popup_reset_handled();
3019 while ((wp = find_next_popup(TRUE)) != NULL)
3020 {
3021 // This drawing uses the zindex of the popup window, so that it's on
3022 // top of the text but doesn't draw when another popup with higher
3023 // zindex is on top of the character.
3024 screen_zindex = wp->w_zindex;
3025
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003026 // Set flags in popup_transparent[] for masked cells.
3027 update_popup_transparent(wp, 1);
3028
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003029 // adjust w_winrow and w_wincol for border and padding, since
3030 // win_update() doesn't handle them.
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003031 top_off = popup_top_extra(wp);
Bram Moolenaard529ba52019-07-02 23:13:53 +02003032 left_extra = wp->w_popup_padding[3] + wp->w_popup_border[3]
3033 - wp->w_popup_leftoff;
3034 if (wp->w_wincol + left_extra < 0)
3035 left_extra = -wp->w_wincol;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003036 wp->w_winrow += top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003037 wp->w_wincol += left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003038
Bram Moolenaarc2a43162019-06-26 01:03:53 +02003039 // Draw the popup text, unless it's off screen.
3040 if (wp->w_winrow < screen_Rows && wp->w_wincol < screen_Columns)
3041 win_update(wp);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003042
3043 wp->w_winrow -= top_off;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003044 wp->w_wincol -= left_extra;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003045
Bram Moolenaard529ba52019-07-02 23:13:53 +02003046 total_width = popup_width(wp);
3047 total_height = popup_height(wp);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003048 popup_attr = get_wcr_attr(wp);
3049
Bram Moolenaar13d5c3f2019-07-28 21:42:38 +02003050 if (wp->w_winrow + total_height > cmdline_row)
3051 wp->w_popup_flags |= POPF_ON_CMDLINE;
3052 else
3053 wp->w_popup_flags &= ~POPF_ON_CMDLINE;
3054
3055
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003056 // We can only use these line drawing characters when 'encoding' is
3057 // "utf-8" and 'ambiwidth' is "single".
3058 if (enc_utf8 && *p_ambw == 's')
3059 {
3060 border_char[0] = border_char[2] = 0x2550;
3061 border_char[1] = border_char[3] = 0x2551;
3062 border_char[4] = 0x2554;
3063 border_char[5] = 0x2557;
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02003064 border_char[6] = (wp->w_popup_flags & POPF_RESIZE)
3065 ? 0x21f2 : 0x255d;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003066 border_char[7] = 0x255a;
3067 }
3068 else
3069 {
3070 border_char[0] = border_char[2] = '-';
3071 border_char[1] = border_char[3] = '|';
3072 for (i = 4; i < 8; ++i)
3073 border_char[i] = '+';
Bram Moolenaar9bcb70c2019-08-01 21:11:05 +02003074 if (wp->w_popup_flags & POPF_RESIZE)
3075 border_char[6] = '@';
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003076 }
3077 for (i = 0; i < 8; ++i)
3078 if (wp->w_border_char[i] != 0)
3079 border_char[i] = wp->w_border_char[i];
3080
3081 for (i = 0; i < 4; ++i)
3082 {
3083 border_attr[i] = popup_attr;
3084 if (wp->w_border_highlight[i] != NULL)
3085 border_attr[i] = syn_name2attr(wp->w_border_highlight[i]);
3086 }
3087
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003088 wincol = wp->w_wincol - wp->w_popup_leftoff;
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003089 top_padding = wp->w_popup_padding[0];
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003090 if (wp->w_popup_border[0] > 0)
3091 {
3092 // top border
3093 screen_fill(wp->w_winrow, wp->w_winrow + 1,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003094 wincol < 0 ? 0 : wincol, wincol + total_width,
3095 wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003096 ? border_char[4] : border_char[0],
3097 border_char[0], border_attr[0]);
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003098 if (wp->w_popup_border[1] > 0 && wp->w_popup_rightoff == 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003099 {
3100 buf[mb_char2bytes(border_char[5], buf)] = NUL;
3101 screen_puts(buf, wp->w_winrow,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003102 wincol + total_width - 1, border_attr[1]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003103 }
3104 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003105 else if (wp->w_popup_padding[0] == 0 && popup_top_extra(wp) > 0)
3106 top_padding = 1;
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003107
Bram Moolenaard529ba52019-07-02 23:13:53 +02003108 if (top_padding > 0 || wp->w_popup_padding[2] > 0)
3109 {
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003110 padcol = wincol + wp->w_popup_border[3];
Bram Moolenaard529ba52019-07-02 23:13:53 +02003111 padwidth = wp->w_wincol + total_width - wp->w_popup_border[1]
3112 - wp->w_has_scrollbar;
3113 if (padcol < 0)
3114 {
3115 padwidth += padcol;
3116 padcol = 0;
3117 }
3118 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003119 if (top_padding > 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003120 {
3121 // top padding
3122 row = wp->w_winrow + wp->w_popup_border[0];
Bram Moolenaard529ba52019-07-02 23:13:53 +02003123 screen_fill(row, row + top_padding, padcol, padwidth,
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003124 ' ', ' ', popup_attr);
3125 }
3126
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003127 // Title goes on top of border or padding.
3128 if (wp->w_popup_title != NULL)
Bram Moolenaar5d458a72019-08-04 21:12:15 +02003129 {
3130 int len = (int)STRLEN(wp->w_popup_title) + 1;
3131 char_u *title = alloc(len);
3132
3133 trunc_string(wp->w_popup_title, title, total_width - 2, len);
3134 screen_puts(title, wp->w_winrow, wp->w_wincol + 1,
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003135 wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
Bram Moolenaar5d458a72019-08-04 21:12:15 +02003136 }
Bram Moolenaareb2310d2019-06-16 20:09:10 +02003137
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003138 // Compute scrollbar thumb position and size.
3139 if (wp->w_has_scrollbar)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003140 {
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003141 linenr_T linecount = wp->w_buffer->b_ml.ml_line_count;
3142
Bram Moolenaar68acb412019-06-26 03:40:36 +02003143 sb_thumb_height = (wp->w_height * wp->w_height + linecount / 2)
3144 / linecount;
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003145 if (sb_thumb_height == 0)
3146 sb_thumb_height = 1;
Bram Moolenaar437a7462019-07-05 20:17:22 +02003147 if (linecount <= wp->w_height)
3148 // it just fits, avoid divide by zero
3149 sb_thumb_top = 0;
3150 else
3151 sb_thumb_top = (wp->w_topline - 1
3152 + (linecount / wp->w_height) / 2)
Bram Moolenaar68acb412019-06-26 03:40:36 +02003153 * (wp->w_height - sb_thumb_height)
3154 / (linecount - wp->w_height);
Bram Moolenaar4cd583c2019-06-26 05:13:57 +02003155 if (wp->w_scrollbar_highlight != NULL)
3156 attr_scroll = syn_name2attr(wp->w_scrollbar_highlight);
3157 else
3158 attr_scroll = highlight_attr[HLF_PSB];
3159 if (wp->w_thumb_highlight != NULL)
3160 attr_thumb = syn_name2attr(wp->w_thumb_highlight);
3161 else
3162 attr_thumb = highlight_attr[HLF_PST];
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003163 }
3164
3165 for (i = wp->w_popup_border[0];
3166 i < total_height - wp->w_popup_border[2]; ++i)
3167 {
Bram Moolenaard529ba52019-07-02 23:13:53 +02003168 int pad_left;
Bram Moolenaard529ba52019-07-02 23:13:53 +02003169 // left and right padding only needed next to the body
3170 int do_padding =
3171 i >= wp->w_popup_border[0] + wp->w_popup_padding[0]
3172 && i < total_height - wp->w_popup_border[2]
3173 - wp->w_popup_padding[2];
3174
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003175 row = wp->w_winrow + i;
3176
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003177 // left border
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003178 if (wp->w_popup_border[3] > 0 && wincol >= 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003179 {
3180 buf[mb_char2bytes(border_char[3], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003181 screen_puts(buf, row, wincol, border_attr[3]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003182 }
Bram Moolenaard529ba52019-07-02 23:13:53 +02003183 if (do_padding && wp->w_popup_padding[3] > 0)
3184 {
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003185 int col = wincol + wp->w_popup_border[3];
3186
Bram Moolenaard529ba52019-07-02 23:13:53 +02003187 // left padding
Bram Moolenaard529ba52019-07-02 23:13:53 +02003188 pad_left = wp->w_popup_padding[3];
3189 if (col < 0)
3190 {
3191 pad_left += col;
3192 col = 0;
3193 }
3194 if (pad_left > 0)
3195 screen_puts(get_spaces(pad_left), row, col, popup_attr);
3196 }
Bram Moolenaar75fb0852019-06-25 05:15:58 +02003197 // scrollbar
3198 if (wp->w_has_scrollbar)
3199 {
3200 int line = i - top_off;
3201 int scroll_col = wp->w_wincol + total_width - 1
3202 - wp->w_popup_border[1];
3203
3204 if (line >= 0 && line < wp->w_height)
3205 screen_putchar(' ', row, scroll_col,
3206 line >= sb_thumb_top
3207 && line < sb_thumb_top + sb_thumb_height
3208 ? attr_thumb : attr_scroll);
3209 else
3210 screen_putchar(' ', row, scroll_col, popup_attr);
3211 }
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003212 // right border
3213 if (wp->w_popup_border[1] > 0)
3214 {
3215 buf[mb_char2bytes(border_char[1], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003216 screen_puts(buf, row, wincol + total_width - 1, border_attr[1]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003217 }
3218 // right padding
Bram Moolenaard529ba52019-07-02 23:13:53 +02003219 if (do_padding && wp->w_popup_padding[1] > 0)
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003220 screen_puts(get_spaces(wp->w_popup_padding[1]), row,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003221 wincol + wp->w_popup_border[3]
Bram Moolenaard529ba52019-07-02 23:13:53 +02003222 + wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol,
3223 popup_attr);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003224 }
3225
3226 if (wp->w_popup_padding[2] > 0)
3227 {
3228 // bottom padding
3229 row = wp->w_winrow + wp->w_popup_border[0]
3230 + wp->w_popup_padding[0] + wp->w_height;
3231 screen_fill(row, row + wp->w_popup_padding[2],
Bram Moolenaard529ba52019-07-02 23:13:53 +02003232 padcol, padwidth, ' ', ' ', popup_attr);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003233 }
3234
3235 if (wp->w_popup_border[2] > 0)
3236 {
3237 // bottom border
3238 row = wp->w_winrow + total_height - 1;
3239 screen_fill(row , row + 1,
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003240 wincol < 0 ? 0 : wincol,
3241 wincol + total_width,
3242 wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003243 ? border_char[7] : border_char[2],
3244 border_char[2], border_attr[2]);
3245 if (wp->w_popup_border[1] > 0)
3246 {
3247 buf[mb_char2bytes(border_char[6], buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003248 screen_puts(buf, row, wincol + total_width - 1, border_attr[2]);
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003249 }
3250 }
3251
Bram Moolenaar2e62b562019-06-30 18:07:00 +02003252 if (wp->w_popup_close == POPCLOSE_BUTTON)
3253 {
3254 // close button goes on top of anything at the top-right corner
3255 buf[mb_char2bytes('X', buf)] = NUL;
Bram Moolenaarba45f1f2019-07-03 22:50:41 +02003256 screen_puts(buf, wp->w_winrow, wincol + total_width - 1,
Bram Moolenaar2e62b562019-06-30 18:07:00 +02003257 wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
3258 }
3259
Bram Moolenaarc662ec92019-06-23 00:15:57 +02003260 update_popup_transparent(wp, 0);
3261
Bram Moolenaara540f8a2019-06-14 19:23:57 +02003262 // Back to the normal zindex.
3263 screen_zindex = 0;
3264 }
3265}
3266
Bram Moolenaar75a1a942019-06-20 03:45:36 +02003267/*
3268 * Mark references in callbacks of one popup window.
3269 */
3270 static int
3271set_ref_in_one_popup(win_T *wp, int copyID)
3272{
3273 int abort = FALSE;
3274 typval_T tv;
3275
3276 if (wp->w_close_cb.cb_partial != NULL)
3277 {
3278 tv.v_type = VAR_PARTIAL;
3279 tv.vval.v_partial = wp->w_close_cb.cb_partial;
3280 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
3281 }
3282 if (wp->w_filter_cb.cb_partial != NULL)
3283 {
3284 tv.v_type = VAR_PARTIAL;
3285 tv.vval.v_partial = wp->w_filter_cb.cb_partial;
3286 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
3287 }
Bram Moolenaar7be3ab22019-06-23 01:46:15 +02003288 abort = abort || set_ref_in_list(wp->w_popup_mask, copyID);
Bram Moolenaar75a1a942019-06-20 03:45:36 +02003289 return abort;
3290}
3291
3292/*
3293 * Set reference in callbacks of popup windows.
3294 */
3295 int
3296set_ref_in_popups(int copyID)
3297{
3298 int abort = FALSE;
3299 win_T *wp;
3300 tabpage_T *tp;
3301
3302 for (wp = first_popupwin; !abort && wp != NULL; wp = wp->w_next)
3303 abort = abort || set_ref_in_one_popup(wp, copyID);
3304
3305 FOR_ALL_TABPAGES(tp)
3306 {
3307 for (wp = tp->tp_first_popupwin; !abort && wp != NULL; wp = wp->w_next)
3308 abort = abort || set_ref_in_one_popup(wp, copyID);
3309 if (abort)
3310 break;
3311 }
3312 return abort;
3313}
Bram Moolenaar79648732019-07-18 21:43:07 +02003314
3315/*
3316 * Find an existing popup used as the preview window, in the current tab page.
3317 * Return NULL if not found.
3318 */
3319 win_T *
3320popup_find_preview_window(void)
3321{
3322 win_T *wp;
3323
3324 // Preview window popup is always local to tab page.
3325 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
3326 if (wp->w_p_pvw)
3327 return wp;
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003328 return NULL;
3329}
3330
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003331 int
3332popup_is_popup(win_T *wp)
3333{
3334 return wp->w_popup_flags != 0;
3335}
3336
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003337#if defined(FEAT_QUICKFIX) || defined(PROTO)
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003338/*
3339 * Find an existing popup used as the info window, in the current tab page.
3340 * Return NULL if not found.
3341 */
3342 win_T *
3343popup_find_info_window(void)
3344{
3345 win_T *wp;
3346
3347 // info window popup is always local to tab page.
3348 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
3349 if (wp->w_popup_flags & POPF_INFO)
3350 return wp;
3351 return NULL;
3352}
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003353#endif
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003354
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003355 void
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003356f_popup_findinfo(typval_T *argvars UNUSED, typval_T *rettv)
3357{
3358 win_T *wp = popup_find_info_window();
3359
3360 rettv->vval.v_number = wp == NULL ? 0 : wp->w_id;
3361}
3362
3363 void
3364f_popup_findpreview(typval_T *argvars UNUSED, typval_T *rettv)
Bram Moolenaar56c0c472019-07-28 17:57:43 +02003365{
3366 win_T *wp = popup_find_preview_window();
3367
3368 rettv->vval.v_number = wp == NULL ? 0 : wp->w_id;
Bram Moolenaar79648732019-07-18 21:43:07 +02003369}
3370
Bram Moolenaar79648732019-07-18 21:43:07 +02003371/*
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003372 * Create a popup to be used as the preview or info window.
Bram Moolenaar79648732019-07-18 21:43:07 +02003373 * NOTE: this makes the popup the current window, so that the file can be
3374 * edited. However, it must not remain to be the current window, the caller
3375 * must make sure of that.
3376 */
3377 int
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003378popup_create_preview_window(int info)
Bram Moolenaar79648732019-07-18 21:43:07 +02003379{
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003380 win_T *wp = popup_create(NULL, NULL, info ? TYPE_INFO : TYPE_PREVIEW);
Bram Moolenaar79648732019-07-18 21:43:07 +02003381
3382 if (wp == NULL)
3383 return FAIL;
Bram Moolenaar576a4a62019-08-18 15:25:17 +02003384 if (info)
3385 wp->w_popup_flags |= POPF_INFO;
3386 else
3387 wp->w_p_pvw = TRUE;
Bram Moolenaar79648732019-07-18 21:43:07 +02003388
3389 // Set the width to a reasonable value, so that w_topline can be computed.
3390 if (wp->w_minwidth > 0)
3391 wp->w_width = wp->w_minwidth;
3392 else if (wp->w_maxwidth > 0)
3393 wp->w_width = wp->w_maxwidth;
3394 else
3395 wp->w_width = curwin->w_width;
3396
3397 // Will switch to another buffer soon, dummy one can be wiped.
3398 wp->w_buffer->b_locked = FALSE;
3399
3400 win_enter(wp, FALSE);
3401 return OK;
3402}
3403
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003404#if defined(FEAT_QUICKFIX) || defined(PROTO)
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003405/*
3406 * Close any preview popup.
3407 */
Bram Moolenaar79648732019-07-18 21:43:07 +02003408 void
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003409popup_close_preview(void)
Bram Moolenaar79648732019-07-18 21:43:07 +02003410{
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003411 win_T *wp = popup_find_preview_window();
Bram Moolenaar79648732019-07-18 21:43:07 +02003412
3413 if (wp != NULL)
3414 {
3415 typval_T res;
3416
3417 res.v_type = VAR_NUMBER;
3418 res.vval.v_number = -1;
3419 popup_close_and_callback(wp, &res);
3420 }
3421}
Bram Moolenaarc7c5f102019-08-21 18:31:03 +02003422
3423/*
3424 * Hide the info popup.
3425 */
3426 void
3427popup_hide_info(void)
3428{
3429 win_T *wp = popup_find_info_window();
3430
3431 if (wp != NULL)
3432 popup_hide(wp);
3433}
Bram Moolenaar36e4d982019-08-20 21:12:16 +02003434#endif
Bram Moolenaar79648732019-07-18 21:43:07 +02003435
Bram Moolenaar90f3e7a2019-08-01 22:40:44 +02003436/*
3437 * Set the title of the popup window to the file name.
3438 */
3439 void
3440popup_set_title(win_T *wp)
3441{
3442 if (wp->w_buffer->b_fname != NULL)
3443 {
3444 char_u dirname[MAXPATHL];
3445 size_t len;
3446
3447 mch_dirname(dirname, MAXPATHL);
3448 shorten_buf_fname(wp->w_buffer, dirname, FALSE);
3449
3450 vim_free(wp->w_popup_title);
3451 len = STRLEN(wp->w_buffer->b_fname) + 3;
3452 wp->w_popup_title = alloc((int)len);
3453 if (wp->w_popup_title != NULL)
3454 vim_snprintf((char *)wp->w_popup_title, len, " %s ",
3455 wp->w_buffer->b_fname);
3456 redraw_win_later(wp, VALID);
3457 }
3458}
3459
3460/*
3461 * If there is a preview window, update the title.
3462 * Used after changing directory.
3463 */
3464 void
3465popup_update_preview_title(void)
3466{
3467 win_T *wp = popup_find_preview_window();
3468
3469 if (wp != NULL)
3470 popup_set_title(wp);
3471}
3472
Bram Moolenaar4d784b22019-05-25 19:51:39 +02003473#endif // FEAT_TEXT_PROP