blob: 973358c55d07362ef7ba52d5f607015723e2fbff [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
16#ifdef FEAT_TEXT_PROP
17
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 Moolenaar4d784b22019-05-25 19:51:39 +020031/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020032 * Get option value for "key", which is "line" or "col".
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020033 * Handles "cursor+N" and "cursor-N".
34 */
35 static int
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020036popup_options_one(dict_T *dict, char_u *key)
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020037{
38 dictitem_T *di;
39 char_u *val;
40 char_u *s;
41 char_u *endp;
42 int n = 0;
43
44 di = dict_find(dict, key, -1);
45 if (di == NULL)
46 return 0;
47
48 val = tv_get_string(&di->di_tv);
49 if (STRNCMP(val, "cursor", 6) != 0)
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020050 return dict_get_number_check(dict, key);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020051
52 setcursor_mayforce(TRUE);
53 s = val + 6;
54 if (*s != NUL)
55 {
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +020056 endp = s;
57 if (*skipwhite(s) == '+' || *skipwhite(s) == '-')
58 n = strtol((char *)s, (char **)&endp, 10);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +020059 if (endp != NULL && *skipwhite(endp) != NUL)
60 {
61 semsg(_(e_invexpr2), val);
62 return 0;
63 }
64 }
65
66 if (STRCMP(key, "line") == 0)
67 n = screen_screenrow() + 1 + n;
68 else // "col"
69 n = screen_screencol() + 1 + n;
70
71 if (n < 1)
72 n = 1;
73 return n;
74}
75
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020076 static void
77get_pos_options(win_T *wp, dict_T *dict)
78{
79 char_u *str;
80 int nr;
81
82 nr = popup_options_one(dict, (char_u *)"line");
83 if (nr > 0)
84 wp->w_wantline = nr;
85 nr = popup_options_one(dict, (char_u *)"col");
86 if (nr > 0)
87 wp->w_wantcol = nr;
88
Bram Moolenaar042fb4b2019-06-02 14:49:56 +020089 wp->w_popup_fixed = dict_get_number(dict, (char_u *)"fixed") != 0;
90
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020091 str = dict_get_string(dict, (char_u *)"pos", FALSE);
92 if (str != NULL)
93 {
94 for (nr = 0;
95 nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
96 ++nr)
97 if (STRCMP(str, poppos_entries[nr].pp_name) == 0)
98 {
99 wp->w_popup_pos = poppos_entries[nr].pp_val;
100 nr = -1;
101 break;
102 }
103 if (nr != -1)
104 semsg(_(e_invarg2), str);
105 }
106}
107
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200108 static void
109get_padding_border(dict_T *dict, int *array, char *name, int max_val)
110{
111 dictitem_T *di;
112
113 vim_memset(array, 0, sizeof(int) * 4);
114 di = dict_find(dict, (char_u *)name, -1);
115 if (di != NULL)
116 {
117 if (di->di_tv.v_type != VAR_LIST)
118 emsg(_(e_listreq));
119 else
120 {
121 list_T *list = di->di_tv.vval.v_list;
122 listitem_T *li;
123 int i;
124 int nr;
125
126 for (i = 0; i < 4; ++i)
127 array[i] = 1;
128 if (list != NULL)
129 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
130 ++i, li = li->li_next)
131 {
132 nr = (int)tv_get_number(&li->li_tv);
133 if (nr >= 0)
134 array[i] = nr > max_val ? max_val : nr;
135 }
136 }
137 }
138}
139
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200140/*
Bram Moolenaar17627312019-06-02 19:53:44 +0200141 * Used when popup options contain "moved": set default moved values.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200142 */
143 static void
Bram Moolenaar17627312019-06-02 19:53:44 +0200144set_moved_values(win_T *wp)
145{
146 wp->w_popup_curwin = curwin;
147 wp->w_popup_lnum = curwin->w_cursor.lnum;
148 wp->w_popup_mincol = curwin->w_cursor.col;
149 wp->w_popup_maxcol = curwin->w_cursor.col;
150}
151
152/*
153 * Used when popup options contain "moved" with "word" or "WORD".
154 */
155 static void
156set_moved_columns(win_T *wp, int flags)
157{
158 char_u *ptr;
159 int len = find_ident_under_cursor(&ptr, flags | FIND_NOERROR);
160
161 if (len > 0)
162 {
163 wp->w_popup_mincol = (int)(ptr - ml_get_curline());
164 wp->w_popup_maxcol = wp->w_popup_mincol + len - 1;
165 }
166}
167
168/*
169 * Go through the options in "dict" and apply them to buffer "buf" displayed in
170 * popup window "wp".
171 */
172 static void
173apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200174{
Bram Moolenaar402502d2019-05-30 22:07:36 +0200175 int nr;
176 char_u *str;
177 dictitem_T *di;
Bram Moolenaar790498b2019-06-01 22:15:29 +0200178 int i;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200179
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200180 wp->w_minwidth = dict_get_number(dict, (char_u *)"minwidth");
181 wp->w_minheight = dict_get_number(dict, (char_u *)"minheight");
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200182 wp->w_maxwidth = dict_get_number(dict, (char_u *)"maxwidth");
183 wp->w_maxheight = dict_get_number(dict, (char_u *)"maxheight");
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200184
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200185 get_pos_options(wp, dict);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200186
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200187 wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200188
Bram Moolenaar35d5af62019-05-26 20:44:10 +0200189#if defined(FEAT_TIMERS)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200190 // Add timer to close the popup after some time.
191 nr = dict_get_number(dict, (char_u *)"time");
192 if (nr > 0)
193 {
194 char_u cbbuf[50];
195 char_u *ptr = cbbuf;
196 typval_T tv;
197
198 vim_snprintf((char *)cbbuf, sizeof(cbbuf),
199 "{_ -> popup_close(%d)}", wp->w_id);
200 if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
201 {
202 wp->w_popup_timer = create_timer(nr, 0);
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200203 wp->w_popup_timer->tr_callback = get_callback(&tv);
204 clear_tv(&tv);
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200205 }
206 }
Bram Moolenaar35d5af62019-05-26 20:44:10 +0200207#endif
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200208
Bram Moolenaar402502d2019-05-30 22:07:36 +0200209 // Option values resulting in setting an option.
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200210 str = dict_get_string(dict, (char_u *)"highlight", FALSE);
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200211 if (str != NULL)
212 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
213 str, OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200214
Bram Moolenaar402502d2019-05-30 22:07:36 +0200215 di = dict_find(dict, (char_u *)"wrap", -1);
216 if (di != NULL)
217 {
218 nr = dict_get_number(dict, (char_u *)"wrap");
219 wp->w_p_wrap = nr != 0;
220 }
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200221
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200222 di = dict_find(dict, (char_u *)"callback", -1);
223 if (di != NULL)
224 {
225 callback_T callback = get_callback(&di->di_tv);
226
227 if (callback.cb_name != NULL)
228 set_callback(&wp->w_close_cb, &callback);
229 }
230
Bram Moolenaarbf0eff02019-06-01 17:13:36 +0200231 di = dict_find(dict, (char_u *)"filter", -1);
232 if (di != NULL)
233 {
234 callback_T callback = get_callback(&di->di_tv);
235
236 if (callback.cb_name != NULL)
237 set_callback(&wp->w_filter_cb, &callback);
238 }
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200239
240 get_padding_border(dict, wp->w_popup_padding, "padding", 999);
241 get_padding_border(dict, wp->w_popup_border, "border", 1);
Bram Moolenaar790498b2019-06-01 22:15:29 +0200242
243 for (i = 0; i < 4; ++i)
244 VIM_CLEAR(wp->w_border_highlight[i]);
245 di = dict_find(dict, (char_u *)"borderhighlight", -1);
246 if (di != NULL)
247 {
248 if (di->di_tv.v_type != VAR_LIST)
249 emsg(_(e_listreq));
250 else
251 {
252 list_T *list = di->di_tv.vval.v_list;
253 listitem_T *li;
254
255 if (list != NULL)
256 for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
257 ++i, li = li->li_next)
258 {
259 str = tv_get_string(&li->li_tv);
260 if (*str != NUL)
261 wp->w_border_highlight[i] = vim_strsave(str);
262 }
263 if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
264 for (i = 1; i < 4; ++i)
265 wp->w_border_highlight[i] =
266 vim_strsave(wp->w_border_highlight[0]);
267 }
268 }
269
270 for (i = 0; i < 8; ++i)
271 wp->w_border_char[i] = 0;
272 di = dict_find(dict, (char_u *)"borderchars", -1);
273 if (di != NULL)
274 {
275 if (di->di_tv.v_type != VAR_LIST)
276 emsg(_(e_listreq));
277 else
278 {
279 list_T *list = di->di_tv.vval.v_list;
280 listitem_T *li;
281
282 if (list != NULL)
283 for (i = 0, li = list->lv_first; i < 8 && i < list->lv_len;
284 ++i, li = li->li_next)
285 {
286 str = tv_get_string(&li->li_tv);
287 if (*str != NUL)
288 wp->w_border_char[i] = mb_ptr2char(str);
289 }
290 if (list->lv_len == 1)
291 for (i = 1; i < 8; ++i)
292 wp->w_border_char[i] = wp->w_border_char[0];
293 if (list->lv_len == 2)
294 {
295 for (i = 4; i < 8; ++i)
296 wp->w_border_char[i] = wp->w_border_char[1];
297 for (i = 1; i < 4; ++i)
298 wp->w_border_char[i] = wp->w_border_char[0];
299 }
300 }
301 }
Bram Moolenaar3397f742019-06-02 18:40:06 +0200302
303 di = dict_find(dict, (char_u *)"moved", -1);
304 if (di != NULL)
305 {
Bram Moolenaar17627312019-06-02 19:53:44 +0200306 set_moved_values(wp);
Bram Moolenaar3397f742019-06-02 18:40:06 +0200307 if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
308 {
309 char_u *s = di->di_tv.vval.v_string;
310 int flags = 0;
311
312 if (STRCMP(s, "word") == 0)
313 flags = FIND_IDENT | FIND_STRING;
314 else if (STRCMP(s, "WORD") == 0)
315 flags = FIND_STRING;
316 else if (STRCMP(s, "any") != 0)
317 semsg(_(e_invarg2), s);
318 if (flags != 0)
Bram Moolenaar17627312019-06-02 19:53:44 +0200319 set_moved_columns(wp, flags);
Bram Moolenaar3397f742019-06-02 18:40:06 +0200320 }
321 else if (di->di_tv.v_type == VAR_LIST
322 && di->di_tv.vval.v_list != NULL
323 && di->di_tv.vval.v_list->lv_len == 2)
324 {
325 list_T *l = di->di_tv.vval.v_list;
326
327 wp->w_popup_mincol = tv_get_number(&l->lv_first->li_tv);
328 wp->w_popup_maxcol = tv_get_number(&l->lv_first->li_next->li_tv);
329 }
330 else
331 semsg(_(e_invarg2), tv_get_string(&di->di_tv));
332 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200333}
334
335/*
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200336 * Add lines to the popup from a list of strings.
337 */
338 static void
339add_popup_strings(buf_T *buf, list_T *l)
340{
341 listitem_T *li;
342 linenr_T lnum = 0;
343 char_u *p;
344
345 for (li = l->lv_first; li != NULL; li = li->li_next)
346 if (li->li_tv.v_type == VAR_STRING)
347 {
348 p = li->li_tv.vval.v_string;
349 ml_append_buf(buf, lnum++,
350 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
351 }
352}
353
354/*
355 * Add lines to the popup from a list of dictionaries.
356 */
357 static void
358add_popup_dicts(buf_T *buf, list_T *l)
359{
360 listitem_T *li;
361 listitem_T *pli;
362 linenr_T lnum = 0;
363 char_u *p;
364 dict_T *dict;
365
366 // first add the text lines
367 for (li = l->lv_first; li != NULL; li = li->li_next)
368 {
369 if (li->li_tv.v_type != VAR_DICT)
370 {
371 emsg(_(e_dictreq));
372 return;
373 }
374 dict = li->li_tv.vval.v_dict;
375 p = dict == NULL ? NULL
376 : dict_get_string(dict, (char_u *)"text", FALSE);
377 ml_append_buf(buf, lnum++,
378 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
379 }
380
381 // add the text properties
382 lnum = 1;
383 for (li = l->lv_first; li != NULL; li = li->li_next, ++lnum)
384 {
385 dictitem_T *di;
386 list_T *plist;
387
388 dict = li->li_tv.vval.v_dict;
389 di = dict_find(dict, (char_u *)"props", -1);
390 if (di != NULL)
391 {
392 if (di->di_tv.v_type != VAR_LIST)
393 {
394 emsg(_(e_listreq));
395 return;
396 }
397 plist = di->di_tv.vval.v_list;
398 if (plist != NULL)
399 {
400 for (pli = plist->lv_first; pli != NULL; pli = pli->li_next)
401 {
402 if (pli->li_tv.v_type != VAR_DICT)
403 {
404 emsg(_(e_dictreq));
405 return;
406 }
407 dict = pli->li_tv.vval.v_dict;
408 if (dict != NULL)
409 {
410 int col = dict_get_number(dict, (char_u *)"col");
411
412 prop_add_common( lnum, col, dict, buf, NULL);
413 }
414 }
415 }
416 }
417 }
418}
419
420/*
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200421 * Adjust the position and size of the popup to fit on the screen.
422 */
Bram Moolenaar17146962019-05-30 00:12:11 +0200423 void
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200424popup_adjust_position(win_T *wp)
425{
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200426 linenr_T lnum;
427 int wrapped = 0;
428 int maxwidth;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200429 int center_vert = FALSE;
430 int center_hor = FALSE;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200431 int allow_adjust_left = !wp->w_popup_fixed;
Bram Moolenaar399d8982019-06-02 15:34:29 +0200432 int top_extra = wp->w_popup_border[0] + wp->w_popup_padding[0];
433 int right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1];
434 int bot_extra = wp->w_popup_border[2] + wp->w_popup_padding[2];
435 int left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
436 int extra_height = top_extra + bot_extra;
437 int extra_width = left_extra + right_extra;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200438
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200439 wp->w_winrow = 0;
440 wp->w_wincol = 0;
441 if (wp->w_popup_pos == POPPOS_CENTER)
442 {
443 // center after computing the size
444 center_vert = TRUE;
445 center_hor = TRUE;
446 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200447 else
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200448 {
449 if (wp->w_wantline == 0)
450 center_vert = TRUE;
451 else if (wp->w_popup_pos == POPPOS_TOPLEFT
452 || wp->w_popup_pos == POPPOS_TOPRIGHT)
453 {
454 wp->w_winrow = wp->w_wantline - 1;
455 if (wp->w_winrow >= Rows)
456 wp->w_winrow = Rows - 1;
457 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200458
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200459 if (wp->w_wantcol == 0)
460 center_hor = TRUE;
461 else if (wp->w_popup_pos == POPPOS_TOPLEFT
462 || wp->w_popup_pos == POPPOS_BOTLEFT)
463 {
464 wp->w_wincol = wp->w_wantcol - 1;
465 if (wp->w_wincol >= Columns - 3)
466 wp->w_wincol = Columns - 3;
467 }
468 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200469
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200470 // When centering or right aligned, use maximum width.
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200471 // When left aligned use the space available, but shift to the left when we
472 // hit the right of the screen.
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200473 maxwidth = Columns - wp->w_wincol;
474 if (wp->w_maxwidth > 0 && maxwidth > wp->w_maxwidth)
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200475 {
476 allow_adjust_left = FALSE;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200477 maxwidth = wp->w_maxwidth;
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200478 }
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200479
480 // Compute width based on longest text line and the 'wrap' option.
481 // TODO: more accurate wrapping
482 wp->w_width = 0;
483 for (lnum = 1; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
484 {
485 int len = vim_strsize(ml_get_buf(wp->w_buffer, lnum, FALSE));
486
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200487 if (wp->w_p_wrap)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200488 {
Bram Moolenaar042fb4b2019-06-02 14:49:56 +0200489 while (len > maxwidth)
490 {
491 ++wrapped;
492 len -= maxwidth;
493 wp->w_width = maxwidth;
494 }
495 }
496 else if (len > maxwidth
497 && allow_adjust_left
498 && (wp->w_popup_pos == POPPOS_TOPLEFT
499 || wp->w_popup_pos == POPPOS_BOTLEFT))
500 {
501 // adjust leftwise to fit text on screen
502 int shift_by = ( len - maxwidth );
503
504 if ( shift_by > wp->w_wincol )
505 {
506 int truncate_shift = shift_by - wp->w_wincol;
507 len -= truncate_shift;
508 shift_by -= truncate_shift;
509 }
510
511 wp->w_wincol -= shift_by;
512 maxwidth += shift_by;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200513 wp->w_width = maxwidth;
514 }
515 if (wp->w_width < len)
516 wp->w_width = len;
517 }
518
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200519 if (wp->w_minwidth > 0 && wp->w_width < wp->w_minwidth)
520 wp->w_width = wp->w_minwidth;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200521 if (wp->w_width > maxwidth)
522 wp->w_width = maxwidth;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200523 if (center_hor)
524 wp->w_wincol = (Columns - wp->w_width) / 2;
525 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
526 || wp->w_popup_pos == POPPOS_TOPRIGHT)
527 {
528 // Right aligned: move to the right if needed.
529 // No truncation, because that would change the height.
Bram Moolenaar399d8982019-06-02 15:34:29 +0200530 if (wp->w_width + extra_width < wp->w_wantcol)
531 wp->w_wincol = wp->w_wantcol - (wp->w_width + extra_width);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200532 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200533
534 if (wp->w_height <= 1)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200535 wp->w_height = wp->w_buffer->b_ml.ml_line_count + wrapped;
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200536 if (wp->w_minheight > 0 && wp->w_height < wp->w_minheight)
537 wp->w_height = wp->w_minheight;
538 if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
539 wp->w_height = wp->w_maxheight;
540 if (wp->w_height > Rows - wp->w_winrow)
541 wp->w_height = Rows - wp->w_winrow;
Bram Moolenaar17146962019-05-30 00:12:11 +0200542
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200543 if (center_vert)
544 wp->w_winrow = (Rows - wp->w_height) / 2;
545 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
546 || wp->w_popup_pos == POPPOS_BOTLEFT)
547 {
Bram Moolenaar399d8982019-06-02 15:34:29 +0200548 if ((wp->w_height + extra_height) <= wp->w_wantline)
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200549 // bottom aligned: may move down
Bram Moolenaar399d8982019-06-02 15:34:29 +0200550 wp->w_winrow = wp->w_wantline - (wp->w_height + extra_height);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200551 else
552 // not enough space, make top aligned
553 wp->w_winrow = wp->w_wantline + 1;
554 }
555
Bram Moolenaar17146962019-05-30 00:12:11 +0200556 wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200557}
558
Bram Moolenaar17627312019-06-02 19:53:44 +0200559typedef enum
560{
561 TYPE_NORMAL,
562 TYPE_ATCURSOR
563} create_type_T;
564
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200565/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200566 * popup_create({text}, {options})
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200567 * popup_atcursor({text}, {options})
568 * When called from f_popup_atcursor() "atcursor" is TRUE.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200569 */
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200570 static void
Bram Moolenaar17627312019-06-02 19:53:44 +0200571popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200572{
573 win_T *wp;
574 buf_T *buf;
575 dict_T *d;
576 int nr;
577
578 // Check arguments look OK.
Bram Moolenaar7b29dd82019-06-02 13:22:11 +0200579 if (!(argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL)
580 && !(argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list != NULL))
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200581 {
582 emsg(_(e_listreq));
583 return;
584 }
585 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
586 {
587 emsg(_(e_dictreq));
588 return;
589 }
590 d = argvars[1].vval.v_dict;
591
592 // Create the window and buffer.
593 wp = win_alloc_popup_win();
594 if (wp == NULL)
595 return;
596 rettv->vval.v_number = wp->w_id;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200597 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200598
599 buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
600 if (buf == NULL)
601 return;
602 ml_open(buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +0200603
604 win_init_popup_win(wp, buf);
605
606 set_local_options_default(wp);
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200607 set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200608 (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200609 set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200610 (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200611 buf->b_p_ul = -1; // no undo
612 buf->b_p_swf = FALSE; // no swap file
613 buf->b_p_bl = FALSE; // unlisted buffer
Bram Moolenaar868b7b62019-05-29 21:44:40 +0200614 buf->b_locked = TRUE;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200615 wp->w_p_wrap = TRUE; // 'wrap' is default on
616
Bram Moolenaar54fabd42019-05-30 19:03:22 +0200617 // Avoid that 'buftype' is reset when this buffer is entered.
618 buf->b_p_initialized = TRUE;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200619
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200620 nr = (int)dict_get_number(d, (char_u *)"tab");
621 if (nr == 0)
622 {
623 // popup on current tab
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200624 wp->w_next = curtab->tp_first_popupwin;
625 curtab->tp_first_popupwin = wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200626 }
627 else if (nr < 0)
628 {
629 // global popup
630 wp->w_next = first_popupwin;
631 first_popupwin = wp;
632 }
633 else
634 // TODO: find tab page "nr"
635 emsg("Not implemented yet");
636
637 // Add text to the buffer.
638 if (argvars[0].v_type == VAR_STRING)
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200639 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200640 // just a string
641 ml_append_buf(buf, 0, argvars[0].vval.v_string, (colnr_T)0, TRUE);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200642 }
643 else
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200644 {
645 list_T *l = argvars[0].vval.v_list;
646
Bram Moolenaar7b29dd82019-06-02 13:22:11 +0200647 if (l->lv_len > 0)
648 {
649 if (l->lv_first->li_tv.v_type == VAR_STRING)
650 // list of strings
651 add_popup_strings(buf, l);
652 else
653 // list of dictionaries
654 add_popup_dicts(buf, l);
655 }
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200656 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200657
658 // Delete the line of the empty buffer.
659 curbuf = buf;
660 ml_delete(buf->b_ml.ml_line_count, FALSE);
661 curbuf = curwin->w_buffer;
662
Bram Moolenaar17627312019-06-02 19:53:44 +0200663 if (type == TYPE_ATCURSOR)
664 {
665 wp->w_popup_pos = POPPOS_BOTLEFT;
666 setcursor_mayforce(TRUE);
667 wp->w_wantline = screen_screenrow();
668 if (wp->w_wantline == 0) // cursor in first line
669 {
670 wp->w_wantline = 2;
671 wp->w_popup_pos = POPPOS_TOPLEFT;
672 }
673 wp->w_wantcol = screen_screencol() + 1;
674 set_moved_values(wp);
675 set_moved_columns(wp, FIND_STRING);
676 }
677
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200678 // Deal with options.
Bram Moolenaar17627312019-06-02 19:53:44 +0200679 apply_options(wp, buf, argvars[1].vval.v_dict);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200680
681 // set default values
682 if (wp->w_zindex == 0)
683 wp->w_zindex = 50;
684
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200685 popup_adjust_position(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200686
687 wp->w_vsep_width = 0;
688
689 redraw_all_later(NOT_VALID);
690}
691
692/*
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200693 * popup_create({text}, {options})
694 */
695 void
696f_popup_create(typval_T *argvars, typval_T *rettv)
697{
Bram Moolenaar17627312019-06-02 19:53:44 +0200698 popup_create(argvars, rettv, TYPE_NORMAL);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200699}
700
701/*
702 * popup_atcursor({text}, {options})
703 */
704 void
705f_popup_atcursor(typval_T *argvars, typval_T *rettv)
706{
Bram Moolenaar17627312019-06-02 19:53:44 +0200707 popup_create(argvars, rettv, TYPE_ATCURSOR);
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200708}
709
710/*
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200711 * Find the popup window with window-ID "id".
712 * If the popup window does not exist NULL is returned.
713 * If the window is not a popup window, and error message is given.
714 */
715 static win_T *
716find_popup_win(int id)
717{
718 win_T *wp = win_id2wp(id);
719
720 if (wp != NULL && !bt_popup(wp->w_buffer))
721 {
722 semsg(_("E993: window %d is not a popup window"), id);
723 return NULL;
724 }
725 return wp;
726}
727
728/*
729 * Return TRUE if there any popups that are not hidden.
730 */
731 int
732popup_any_visible(void)
733{
734 win_T *wp;
735
736 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200737 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200738 return TRUE;
739 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200740 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200741 return TRUE;
742 return FALSE;
743}
744
745/*
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200746 * Invoke the close callback for window "wp" with value "result".
747 * Careful: The callback may make "wp" invalid!
748 */
749 static void
750invoke_popup_callback(win_T *wp, typval_T *result)
751{
752 typval_T rettv;
753 int dummy;
754 typval_T argv[3];
755
756 argv[0].v_type = VAR_NUMBER;
757 argv[0].vval.v_number = (varnumber_T)wp->w_id;
758
759 if (result != NULL && result->v_type != VAR_UNKNOWN)
760 copy_tv(result, &argv[1]);
761 else
762 {
763 argv[1].v_type = VAR_NUMBER;
764 argv[1].vval.v_number = 0;
765 }
766
767 argv[2].v_type = VAR_UNKNOWN;
768
769 call_callback(&wp->w_close_cb, -1,
770 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
771 if (result != NULL)
772 clear_tv(&argv[1]);
773 clear_tv(&rettv);
774}
775
776/*
Bram Moolenaar3397f742019-06-02 18:40:06 +0200777 * Close popup "wp" and invoke any close callback for it.
778 */
779 static void
780popup_close_and_callback(win_T *wp, typval_T *arg)
781{
782 int id = wp->w_id;
783
784 if (wp->w_close_cb.cb_name != NULL)
785 // Careful: This may make "wp" invalid.
786 invoke_popup_callback(wp, arg);
787
788 popup_close(id);
789}
790
791/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200792 * popup_close({id})
793 */
794 void
795f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
796{
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200797 int id = (int)tv_get_number(argvars);
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200798 win_T *wp = find_popup_win(id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200799
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200800 if (wp != NULL)
Bram Moolenaar3397f742019-06-02 18:40:06 +0200801 popup_close_and_callback(wp, &argvars[1]);
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200802}
803
804/*
805 * popup_hide({id})
806 */
807 void
808f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
809{
810 int id = (int)tv_get_number(argvars);
811 win_T *wp = find_popup_win(id);
812
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200813 if (wp != NULL && (wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200814 {
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200815 wp->w_popup_flags |= POPF_HIDDEN;
Bram Moolenaarc6896e22019-05-30 22:32:34 +0200816 --wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200817 redraw_all_later(NOT_VALID);
818 }
819}
820
821/*
822 * popup_show({id})
823 */
824 void
825f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
826{
827 int id = (int)tv_get_number(argvars);
828 win_T *wp = find_popup_win(id);
829
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200830 if (wp != NULL && (wp->w_popup_flags & POPF_HIDDEN) != 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200831 {
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200832 wp->w_popup_flags &= ~POPF_HIDDEN;
Bram Moolenaarc6896e22019-05-30 22:32:34 +0200833 ++wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200834 redraw_all_later(NOT_VALID);
835 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200836}
837
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200838 static void
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200839popup_free(win_T *wp)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200840{
Bram Moolenaar868b7b62019-05-29 21:44:40 +0200841 wp->w_buffer->b_locked = FALSE;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200842 if (wp->w_winrow + wp->w_height >= cmdline_row)
843 clear_cmdline = TRUE;
844 win_free_popup(wp);
845 redraw_all_later(NOT_VALID);
846}
847
Bram Moolenaarec583842019-05-26 14:11:23 +0200848/*
849 * Close a popup window by Window-id.
Bram Moolenaar9eaac892019-06-01 22:49:29 +0200850 * Does not invoke the callback.
Bram Moolenaarec583842019-05-26 14:11:23 +0200851 */
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200852 void
Bram Moolenaarec583842019-05-26 14:11:23 +0200853popup_close(int id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200854{
855 win_T *wp;
Bram Moolenaarec583842019-05-26 14:11:23 +0200856 tabpage_T *tp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200857 win_T *prev = NULL;
858
Bram Moolenaarec583842019-05-26 14:11:23 +0200859 // go through global popups
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200860 for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
Bram Moolenaarec583842019-05-26 14:11:23 +0200861 if (wp->w_id == id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200862 {
863 if (prev == NULL)
864 first_popupwin = wp->w_next;
865 else
866 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200867 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200868 return;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200869 }
870
Bram Moolenaarec583842019-05-26 14:11:23 +0200871 // go through tab-local popups
872 FOR_ALL_TABPAGES(tp)
873 popup_close_tabpage(tp, id);
874}
875
876/*
877 * Close a popup window with Window-id "id" in tabpage "tp".
878 */
879 void
880popup_close_tabpage(tabpage_T *tp, int id)
881{
882 win_T *wp;
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200883 win_T **root = &tp->tp_first_popupwin;
Bram Moolenaarec583842019-05-26 14:11:23 +0200884 win_T *prev = NULL;
885
Bram Moolenaarec583842019-05-26 14:11:23 +0200886 for (wp = *root; wp != NULL; prev = wp, wp = wp->w_next)
887 if (wp->w_id == id)
888 {
889 if (prev == NULL)
890 *root = wp->w_next;
891 else
892 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200893 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200894 return;
895 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200896}
897
898 void
899close_all_popups(void)
900{
901 while (first_popupwin != NULL)
902 popup_close(first_popupwin->w_id);
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200903 while (curtab->tp_first_popupwin != NULL)
904 popup_close(curtab->tp_first_popupwin->w_id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200905}
906
907 void
908ex_popupclear(exarg_T *eap UNUSED)
909{
910 close_all_popups();
911}
912
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200913/*
914 * popup_move({id}, {options})
915 */
916 void
917f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
918{
919 dict_T *d;
920 int nr;
921 int id = (int)tv_get_number(argvars);
922 win_T *wp = find_popup_win(id);
923
924 if (wp == NULL)
925 return; // invalid {id}
926
927 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
928 {
929 emsg(_(e_dictreq));
930 return;
931 }
932 d = argvars[1].vval.v_dict;
933
934 if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0)
935 wp->w_minwidth = nr;
936 if ((nr = dict_get_number(d, (char_u *)"minheight")) > 0)
937 wp->w_minheight = nr;
938 if ((nr = dict_get_number(d, (char_u *)"maxwidth")) > 0)
939 wp->w_maxwidth = nr;
940 if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0)
941 wp->w_maxheight = nr;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200942 get_pos_options(wp, d);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200943
944 if (wp->w_winrow + wp->w_height >= cmdline_row)
945 clear_cmdline = TRUE;
946 popup_adjust_position(wp);
947 redraw_all_later(NOT_VALID);
948}
949
Bram Moolenaarbc133542019-05-29 20:26:46 +0200950/*
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200951 * popup_getpos({id})
Bram Moolenaarbc133542019-05-29 20:26:46 +0200952 */
953 void
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200954f_popup_getpos(typval_T *argvars, typval_T *rettv)
Bram Moolenaarbc133542019-05-29 20:26:46 +0200955{
956 dict_T *dict;
957 int id = (int)tv_get_number(argvars);
958 win_T *wp = find_popup_win(id);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200959 int top_extra;
960 int left_extra;
Bram Moolenaarbc133542019-05-29 20:26:46 +0200961
962 if (rettv_dict_alloc(rettv) == OK)
963 {
964 if (wp == NULL)
965 return; // invalid {id}
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200966 top_extra = wp->w_popup_border[0] + wp->w_popup_padding[0];
967 left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
968
Bram Moolenaarbc133542019-05-29 20:26:46 +0200969 dict = rettv->vval.v_dict;
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200970
Bram Moolenaarbc133542019-05-29 20:26:46 +0200971 dict_add_number(dict, "line", wp->w_winrow + 1);
972 dict_add_number(dict, "col", wp->w_wincol + 1);
Bram Moolenaar2fd8e352019-06-01 20:16:48 +0200973 dict_add_number(dict, "width", wp->w_width + left_extra + wp->w_popup_border[1] + wp->w_popup_padding[1]);
974 dict_add_number(dict, "height", wp->w_height + top_extra + wp->w_popup_border[2] + wp->w_popup_padding[2]);
975
976 dict_add_number(dict, "core_line", wp->w_winrow + 1 + top_extra);
977 dict_add_number(dict, "core_col", wp->w_wincol + 1 + left_extra);
978 dict_add_number(dict, "core_width", wp->w_width);
979 dict_add_number(dict, "core_height", wp->w_height);
980
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200981 dict_add_number(dict, "visible",
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200982 win_valid(wp) && (wp->w_popup_flags & POPF_HIDDEN) == 0);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200983 }
984}
985
986/*
987 * f_popup_getoptions({id})
988 */
989 void
990f_popup_getoptions(typval_T *argvars, typval_T *rettv)
991{
992 dict_T *dict;
993 int id = (int)tv_get_number(argvars);
994 win_T *wp = find_popup_win(id);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200995 int i;
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200996
997 if (rettv_dict_alloc(rettv) == OK)
998 {
999 if (wp == NULL)
1000 return;
1001
1002 dict = rettv->vval.v_dict;
1003 dict_add_number(dict, "line", wp->w_wantline);
1004 dict_add_number(dict, "col", wp->w_wantcol);
1005 dict_add_number(dict, "minwidth", wp->w_minwidth);
1006 dict_add_number(dict, "minheight", wp->w_minheight);
1007 dict_add_number(dict, "maxheight", wp->w_maxheight);
1008 dict_add_number(dict, "maxwidth", wp->w_maxwidth);
1009 dict_add_number(dict, "zindex", wp->w_zindex);
Bram Moolenaar042fb4b2019-06-02 14:49:56 +02001010 dict_add_number(dict, "fixed", wp->w_popup_fixed);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +02001011
1012 for (i = 0; i < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
1013 ++i)
1014 if (wp->w_popup_pos == poppos_entries[i].pp_val)
1015 {
1016 dict_add_string(dict, "pos",
1017 (char_u *)poppos_entries[i].pp_name);
1018 break;
1019 }
1020
Bram Moolenaar8c2a6002019-05-30 14:29:45 +02001021# if defined(FEAT_TIMERS)
1022 dict_add_number(dict, "time", wp->w_popup_timer != NULL
1023 ? (long)wp->w_popup_timer->tr_interval : 0L);
1024# endif
Bram Moolenaarbc133542019-05-29 20:26:46 +02001025 }
1026}
Bram Moolenaar815b76b2019-06-01 14:15:52 +02001027
1028 int
1029not_in_popup_window()
1030{
1031 if (bt_popup(curwin->w_buffer))
1032 {
1033 emsg(_("E994: Not allowed in a popup window"));
1034 return TRUE;
1035 }
1036 return FALSE;
1037}
1038
Bram Moolenaarbf0eff02019-06-01 17:13:36 +02001039/*
1040 * Reset all the POPF_HANDLED flags in global popup windows and popup windows
1041 * in the current tab.
1042 */
1043 void
1044popup_reset_handled()
1045{
1046 win_T *wp;
1047
1048 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
1049 wp->w_popup_flags &= ~POPF_HANDLED;
1050 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
1051 wp->w_popup_flags &= ~POPF_HANDLED;
1052}
1053
1054/*
1055 * Find the next visible popup where POPF_HANDLED is not set.
1056 * Must have called popup_reset_handled() first.
1057 * When "lowest" is TRUE find the popup with the lowest zindex, otherwise the
1058 * popup with the highest zindex.
1059 */
1060 win_T *
1061find_next_popup(int lowest)
1062{
1063 win_T *wp;
1064 win_T *found_wp;
1065 int found_zindex;
1066
1067 found_zindex = lowest ? INT_MAX : 0;
1068 found_wp = NULL;
1069 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
1070 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
1071 && (lowest ? wp->w_zindex < found_zindex
1072 : wp->w_zindex > found_zindex))
1073 {
1074 found_zindex = wp->w_zindex;
1075 found_wp = wp;
1076 }
1077 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
1078 if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0
1079 && (lowest ? wp->w_zindex < found_zindex
1080 : wp->w_zindex > found_zindex))
1081 {
1082 found_zindex = wp->w_zindex;
1083 found_wp = wp;
1084 }
1085
1086 if (found_wp != NULL)
1087 found_wp->w_popup_flags |= POPF_HANDLED;
1088 return found_wp;
1089}
1090
1091/*
1092 * Invoke the filter callback for window "wp" with typed character "c".
1093 * Uses the global "mod_mask" for modifiers.
1094 * Returns the return value of the filter.
1095 * Careful: The filter may make "wp" invalid!
1096 */
1097 static int
1098invoke_popup_filter(win_T *wp, int c)
1099{
1100 int res;
1101 typval_T rettv;
1102 int dummy;
1103 typval_T argv[3];
1104 char_u buf[NUMBUFLEN];
1105
1106 argv[0].v_type = VAR_NUMBER;
1107 argv[0].vval.v_number = (varnumber_T)wp->w_id;
1108
1109 // Convert the number to a string, so that the function can use:
1110 // if a:c == "\<F2>"
1111 buf[special_to_buf(c, mod_mask, TRUE, buf)] = NUL;
1112 argv[1].v_type = VAR_STRING;
1113 argv[1].vval.v_string = vim_strsave(buf);
1114
1115 argv[2].v_type = VAR_UNKNOWN;
1116
1117 call_callback(&wp->w_filter_cb, -1,
1118 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
1119 res = tv_get_number(&rettv);
1120 vim_free(argv[1].vval.v_string);
1121 clear_tv(&rettv);
1122 return res;
1123}
1124
1125/*
1126 * Called when "c" was typed: invoke popup filter callbacks.
1127 * Returns TRUE when the character was consumed,
1128 */
1129 int
1130popup_do_filter(int c)
1131{
1132 int res = FALSE;
1133 win_T *wp;
1134
1135 popup_reset_handled();
1136
1137 while (!res && (wp = find_next_popup(FALSE)) != NULL)
1138 if (wp->w_filter_cb.cb_name != NULL)
1139 res = invoke_popup_filter(wp, c);
1140
1141 return res;
1142}
1143
Bram Moolenaar3397f742019-06-02 18:40:06 +02001144/*
1145 * Called when the cursor moved: check if any popup needs to be closed if the
1146 * cursor moved far enough.
1147 */
1148 void
1149popup_check_cursor_pos()
1150{
1151 win_T *wp;
1152 typval_T tv;
1153
1154 popup_reset_handled();
1155 while ((wp = find_next_popup(TRUE)) != NULL)
1156 if (wp->w_popup_curwin != NULL
1157 && (curwin != wp->w_popup_curwin
1158 || curwin->w_cursor.lnum != wp->w_popup_lnum
1159 || curwin->w_cursor.col < wp->w_popup_mincol
1160 || curwin->w_cursor.col > wp->w_popup_maxcol))
1161 {
1162 tv.v_type = VAR_NUMBER;
1163 tv.vval.v_number = -1;
1164 popup_close_and_callback(wp, &tv);
1165 }
1166}
1167
Bram Moolenaar4d784b22019-05-25 19:51:39 +02001168#endif // FEAT_TEXT_PROP