blob: 2ebfda2d9ccd43f90b931f1d9647ceeb9df549da [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 Moolenaarcc31ad92019-05-30 19:25:06 +020032 * Get option value for"key", which is "line" or "col".
33 * 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)
50 return dict_get_number(dict, key);
51
52 setcursor_mayforce(TRUE);
53 s = val + 6;
54 if (*s != NUL)
55 {
56 n = strtol((char *)s, (char **)&endp, 10);
57 if (endp != NULL && *skipwhite(endp) != NUL)
58 {
59 semsg(_(e_invexpr2), val);
60 return 0;
61 }
62 }
63
64 if (STRCMP(key, "line") == 0)
65 n = screen_screenrow() + 1 + n;
66 else // "col"
67 n = screen_screencol() + 1 + n;
68
69 if (n < 1)
70 n = 1;
71 return n;
72}
73
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +020074 static void
75get_pos_options(win_T *wp, dict_T *dict)
76{
77 char_u *str;
78 int nr;
79
80 nr = popup_options_one(dict, (char_u *)"line");
81 if (nr > 0)
82 wp->w_wantline = nr;
83 nr = popup_options_one(dict, (char_u *)"col");
84 if (nr > 0)
85 wp->w_wantcol = nr;
86
87 str = dict_get_string(dict, (char_u *)"pos", FALSE);
88 if (str != NULL)
89 {
90 for (nr = 0;
91 nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
92 ++nr)
93 if (STRCMP(str, poppos_entries[nr].pp_name) == 0)
94 {
95 wp->w_popup_pos = poppos_entries[nr].pp_val;
96 nr = -1;
97 break;
98 }
99 if (nr != -1)
100 semsg(_(e_invarg2), str);
101 }
102}
103
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200104/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200105 * Go through the options in "dict" and apply them to buffer "buf" displayed in
106 * popup window "wp".
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200107 * When called from f_popup_atcursor() "atcursor" is TRUE.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200108 */
109 static void
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200110apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict, int atcursor)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200111{
Bram Moolenaar402502d2019-05-30 22:07:36 +0200112 int nr;
113 char_u *str;
114 dictitem_T *di;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200115
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200116 wp->w_minwidth = dict_get_number(dict, (char_u *)"minwidth");
117 wp->w_minheight = dict_get_number(dict, (char_u *)"minheight");
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200118 wp->w_maxwidth = dict_get_number(dict, (char_u *)"maxwidth");
119 wp->w_maxheight = dict_get_number(dict, (char_u *)"maxheight");
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200120
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200121 if (atcursor)
122 {
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200123 wp->w_popup_pos = POPPOS_BOTLEFT;
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200124 setcursor_mayforce(TRUE);
125 wp->w_wantline = screen_screenrow();
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200126 if (wp->w_wantline == 0) // cursor in first line
127 {
128 wp->w_wantline = 2;
129 wp->w_popup_pos = POPPOS_TOPLEFT;
130 }
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200131 wp->w_wantcol = screen_screencol() + 1;
132 }
133
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200134 get_pos_options(wp, dict);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200135
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200136 wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200137
Bram Moolenaar35d5af62019-05-26 20:44:10 +0200138#if defined(FEAT_TIMERS)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200139 // Add timer to close the popup after some time.
140 nr = dict_get_number(dict, (char_u *)"time");
141 if (nr > 0)
142 {
143 char_u cbbuf[50];
144 char_u *ptr = cbbuf;
145 typval_T tv;
146
147 vim_snprintf((char *)cbbuf, sizeof(cbbuf),
148 "{_ -> popup_close(%d)}", wp->w_id);
149 if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
150 {
151 wp->w_popup_timer = create_timer(nr, 0);
Bram Moolenaar3a97bb32019-06-01 13:28:35 +0200152 wp->w_popup_timer->tr_callback.cb_name =
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200153 vim_strsave(partial_name(tv.vval.v_partial));
Bram Moolenaar3a97bb32019-06-01 13:28:35 +0200154 func_ref(wp->w_popup_timer->tr_callback.cb_name);
155 wp->w_popup_timer->tr_callback.cb_partial = tv.vval.v_partial;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200156 }
157 }
Bram Moolenaar35d5af62019-05-26 20:44:10 +0200158#endif
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200159
Bram Moolenaar402502d2019-05-30 22:07:36 +0200160 // Option values resulting in setting an option.
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200161 str = dict_get_string(dict, (char_u *)"highlight", TRUE);
162 if (str != NULL)
163 set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
164 str, OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar402502d2019-05-30 22:07:36 +0200165 di = dict_find(dict, (char_u *)"wrap", -1);
166 if (di != NULL)
167 {
168 nr = dict_get_number(dict, (char_u *)"wrap");
169 wp->w_p_wrap = nr != 0;
170 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200171}
172
173/*
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200174 * Add lines to the popup from a list of strings.
175 */
176 static void
177add_popup_strings(buf_T *buf, list_T *l)
178{
179 listitem_T *li;
180 linenr_T lnum = 0;
181 char_u *p;
182
183 for (li = l->lv_first; li != NULL; li = li->li_next)
184 if (li->li_tv.v_type == VAR_STRING)
185 {
186 p = li->li_tv.vval.v_string;
187 ml_append_buf(buf, lnum++,
188 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
189 }
190}
191
192/*
193 * Add lines to the popup from a list of dictionaries.
194 */
195 static void
196add_popup_dicts(buf_T *buf, list_T *l)
197{
198 listitem_T *li;
199 listitem_T *pli;
200 linenr_T lnum = 0;
201 char_u *p;
202 dict_T *dict;
203
204 // first add the text lines
205 for (li = l->lv_first; li != NULL; li = li->li_next)
206 {
207 if (li->li_tv.v_type != VAR_DICT)
208 {
209 emsg(_(e_dictreq));
210 return;
211 }
212 dict = li->li_tv.vval.v_dict;
213 p = dict == NULL ? NULL
214 : dict_get_string(dict, (char_u *)"text", FALSE);
215 ml_append_buf(buf, lnum++,
216 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
217 }
218
219 // add the text properties
220 lnum = 1;
221 for (li = l->lv_first; li != NULL; li = li->li_next, ++lnum)
222 {
223 dictitem_T *di;
224 list_T *plist;
225
226 dict = li->li_tv.vval.v_dict;
227 di = dict_find(dict, (char_u *)"props", -1);
228 if (di != NULL)
229 {
230 if (di->di_tv.v_type != VAR_LIST)
231 {
232 emsg(_(e_listreq));
233 return;
234 }
235 plist = di->di_tv.vval.v_list;
236 if (plist != NULL)
237 {
238 for (pli = plist->lv_first; pli != NULL; pli = pli->li_next)
239 {
240 if (pli->li_tv.v_type != VAR_DICT)
241 {
242 emsg(_(e_dictreq));
243 return;
244 }
245 dict = pli->li_tv.vval.v_dict;
246 if (dict != NULL)
247 {
248 int col = dict_get_number(dict, (char_u *)"col");
249
250 prop_add_common( lnum, col, dict, buf, NULL);
251 }
252 }
253 }
254 }
255 }
256}
257
258/*
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200259 * Adjust the position and size of the popup to fit on the screen.
260 */
Bram Moolenaar17146962019-05-30 00:12:11 +0200261 void
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200262popup_adjust_position(win_T *wp)
263{
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200264 linenr_T lnum;
265 int wrapped = 0;
266 int maxwidth;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200267 int center_vert = FALSE;
268 int center_hor = FALSE;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200269
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200270 wp->w_winrow = 0;
271 wp->w_wincol = 0;
272 if (wp->w_popup_pos == POPPOS_CENTER)
273 {
274 // center after computing the size
275 center_vert = TRUE;
276 center_hor = TRUE;
277 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200278 else
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200279 {
280 if (wp->w_wantline == 0)
281 center_vert = TRUE;
282 else if (wp->w_popup_pos == POPPOS_TOPLEFT
283 || wp->w_popup_pos == POPPOS_TOPRIGHT)
284 {
285 wp->w_winrow = wp->w_wantline - 1;
286 if (wp->w_winrow >= Rows)
287 wp->w_winrow = Rows - 1;
288 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200289
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200290 if (wp->w_wantcol == 0)
291 center_hor = TRUE;
292 else if (wp->w_popup_pos == POPPOS_TOPLEFT
293 || wp->w_popup_pos == POPPOS_BOTLEFT)
294 {
295 wp->w_wincol = wp->w_wantcol - 1;
296 if (wp->w_wincol >= Columns - 3)
297 wp->w_wincol = Columns - 3;
298 }
299 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200300
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200301 // When centering or right aligned, use maximum width.
302 // When left aligned use the space available.
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200303 maxwidth = Columns - wp->w_wincol;
304 if (wp->w_maxwidth > 0 && maxwidth > wp->w_maxwidth)
305 maxwidth = wp->w_maxwidth;
306
307 // Compute width based on longest text line and the 'wrap' option.
308 // TODO: more accurate wrapping
309 wp->w_width = 0;
310 for (lnum = 1; lnum <= wp->w_buffer->b_ml.ml_line_count; ++lnum)
311 {
312 int len = vim_strsize(ml_get_buf(wp->w_buffer, lnum, FALSE));
313
314 while (wp->w_p_wrap && len > maxwidth)
315 {
316 ++wrapped;
317 len -= maxwidth;
318 wp->w_width = maxwidth;
319 }
320 if (wp->w_width < len)
321 wp->w_width = len;
322 }
323
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200324 if (wp->w_minwidth > 0 && wp->w_width < wp->w_minwidth)
325 wp->w_width = wp->w_minwidth;
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200326 if (wp->w_width > maxwidth)
327 wp->w_width = maxwidth;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200328 if (center_hor)
329 wp->w_wincol = (Columns - wp->w_width) / 2;
330 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
331 || wp->w_popup_pos == POPPOS_TOPRIGHT)
332 {
333 // Right aligned: move to the right if needed.
334 // No truncation, because that would change the height.
335 if (wp->w_width < wp->w_wantcol)
336 wp->w_wincol = wp->w_wantcol - wp->w_width;
337 }
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200338
339 if (wp->w_height <= 1)
Bram Moolenaar88c4e1f2019-05-29 23:14:28 +0200340 wp->w_height = wp->w_buffer->b_ml.ml_line_count + wrapped;
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200341 if (wp->w_minheight > 0 && wp->w_height < wp->w_minheight)
342 wp->w_height = wp->w_minheight;
343 if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
344 wp->w_height = wp->w_maxheight;
345 if (wp->w_height > Rows - wp->w_winrow)
346 wp->w_height = Rows - wp->w_winrow;
Bram Moolenaar17146962019-05-30 00:12:11 +0200347
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200348 if (center_vert)
349 wp->w_winrow = (Rows - wp->w_height) / 2;
350 else if (wp->w_popup_pos == POPPOS_BOTRIGHT
351 || wp->w_popup_pos == POPPOS_BOTLEFT)
352 {
353 if (wp->w_height <= wp->w_wantline)
354 // bottom aligned: may move down
355 wp->w_winrow = wp->w_wantline - wp->w_height;
356 else
357 // not enough space, make top aligned
358 wp->w_winrow = wp->w_wantline + 1;
359 }
360
Bram Moolenaar17146962019-05-30 00:12:11 +0200361 wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200362}
363
364/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200365 * popup_create({text}, {options})
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200366 * popup_atcursor({text}, {options})
367 * When called from f_popup_atcursor() "atcursor" is TRUE.
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200368 */
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200369 static void
370popup_create(typval_T *argvars, typval_T *rettv, int atcursor)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200371{
372 win_T *wp;
373 buf_T *buf;
374 dict_T *d;
375 int nr;
376
377 // Check arguments look OK.
378 if (!(argvars[0].v_type == VAR_STRING
379 && argvars[0].vval.v_string != NULL
380 && STRLEN(argvars[0].vval.v_string) > 0)
381 && !(argvars[0].v_type == VAR_LIST
382 && argvars[0].vval.v_list != NULL
383 && argvars[0].vval.v_list->lv_len > 0))
384 {
385 emsg(_(e_listreq));
386 return;
387 }
388 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
389 {
390 emsg(_(e_dictreq));
391 return;
392 }
393 d = argvars[1].vval.v_dict;
394
395 // Create the window and buffer.
396 wp = win_alloc_popup_win();
397 if (wp == NULL)
398 return;
399 rettv->vval.v_number = wp->w_id;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200400 wp->w_popup_pos = POPPOS_TOPLEFT;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200401
402 buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
403 if (buf == NULL)
404 return;
405 ml_open(buf);
Bram Moolenaarcacc6a52019-05-30 15:22:43 +0200406
407 win_init_popup_win(wp, buf);
408
409 set_local_options_default(wp);
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200410 set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200411 (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar20c023a2019-05-26 21:03:24 +0200412 set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200413 (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200414 buf->b_p_ul = -1; // no undo
415 buf->b_p_swf = FALSE; // no swap file
416 buf->b_p_bl = FALSE; // unlisted buffer
Bram Moolenaar868b7b62019-05-29 21:44:40 +0200417 buf->b_locked = TRUE;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200418 wp->w_p_wrap = TRUE; // 'wrap' is default on
419
Bram Moolenaar54fabd42019-05-30 19:03:22 +0200420 // Avoid that 'buftype' is reset when this buffer is entered.
421 buf->b_p_initialized = TRUE;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200422
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200423 nr = (int)dict_get_number(d, (char_u *)"tab");
424 if (nr == 0)
425 {
426 // popup on current tab
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200427 wp->w_next = curtab->tp_first_popupwin;
428 curtab->tp_first_popupwin = wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200429 }
430 else if (nr < 0)
431 {
432 // global popup
433 wp->w_next = first_popupwin;
434 first_popupwin = wp;
435 }
436 else
437 // TODO: find tab page "nr"
438 emsg("Not implemented yet");
439
440 // Add text to the buffer.
441 if (argvars[0].v_type == VAR_STRING)
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200442 {
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200443 // just a string
444 ml_append_buf(buf, 0, argvars[0].vval.v_string, (colnr_T)0, TRUE);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200445 }
446 else
Bram Moolenaar7a8d0272019-05-26 23:32:06 +0200447 {
448 list_T *l = argvars[0].vval.v_list;
449
450 if (l->lv_first->li_tv.v_type == VAR_STRING)
451 // list of strings
452 add_popup_strings(buf, l);
453 else
454 // list of dictionaries
455 add_popup_dicts(buf, l);
456 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200457
458 // Delete the line of the empty buffer.
459 curbuf = buf;
460 ml_delete(buf->b_ml.ml_line_count, FALSE);
461 curbuf = curwin->w_buffer;
462
463 // Deal with options.
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200464 apply_options(wp, buf, argvars[1].vval.v_dict, atcursor);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200465
466 // set default values
467 if (wp->w_zindex == 0)
468 wp->w_zindex = 50;
469
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200470 popup_adjust_position(wp);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200471
472 wp->w_vsep_width = 0;
473
474 redraw_all_later(NOT_VALID);
475}
476
477/*
Bram Moolenaarcc31ad92019-05-30 19:25:06 +0200478 * popup_create({text}, {options})
479 */
480 void
481f_popup_create(typval_T *argvars, typval_T *rettv)
482{
483 popup_create(argvars, rettv, FALSE);
484}
485
486/*
487 * popup_atcursor({text}, {options})
488 */
489 void
490f_popup_atcursor(typval_T *argvars, typval_T *rettv)
491{
492 popup_create(argvars, rettv, TRUE);
493}
494
495/*
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200496 * Find the popup window with window-ID "id".
497 * If the popup window does not exist NULL is returned.
498 * If the window is not a popup window, and error message is given.
499 */
500 static win_T *
501find_popup_win(int id)
502{
503 win_T *wp = win_id2wp(id);
504
505 if (wp != NULL && !bt_popup(wp->w_buffer))
506 {
507 semsg(_("E993: window %d is not a popup window"), id);
508 return NULL;
509 }
510 return wp;
511}
512
513/*
514 * Return TRUE if there any popups that are not hidden.
515 */
516 int
517popup_any_visible(void)
518{
519 win_T *wp;
520
521 for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200522 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200523 return TRUE;
524 for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next)
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200525 if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200526 return TRUE;
527 return FALSE;
528}
529
530/*
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200531 * popup_close({id})
532 */
533 void
534f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
535{
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200536 int id = (int)tv_get_number(argvars);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200537
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200538 popup_close(id);
539}
540
541/*
542 * popup_hide({id})
543 */
544 void
545f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED)
546{
547 int id = (int)tv_get_number(argvars);
548 win_T *wp = find_popup_win(id);
549
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200550 if (wp != NULL && (wp->w_popup_flags & POPF_HIDDEN) == 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200551 {
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200552 wp->w_popup_flags |= POPF_HIDDEN;
Bram Moolenaarc6896e22019-05-30 22:32:34 +0200553 --wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200554 redraw_all_later(NOT_VALID);
555 }
556}
557
558/*
559 * popup_show({id})
560 */
561 void
562f_popup_show(typval_T *argvars, typval_T *rettv UNUSED)
563{
564 int id = (int)tv_get_number(argvars);
565 win_T *wp = find_popup_win(id);
566
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200567 if (wp != NULL && (wp->w_popup_flags & POPF_HIDDEN) != 0)
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200568 {
Bram Moolenaarbf0ecb22019-05-27 10:04:40 +0200569 wp->w_popup_flags &= ~POPF_HIDDEN;
Bram Moolenaarc6896e22019-05-30 22:32:34 +0200570 ++wp->w_buffer->b_nwindows;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200571 redraw_all_later(NOT_VALID);
572 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200573}
574
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200575 static void
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200576popup_free(win_T *wp)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200577{
Bram Moolenaar868b7b62019-05-29 21:44:40 +0200578 wp->w_buffer->b_locked = FALSE;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200579 if (wp->w_winrow + wp->w_height >= cmdline_row)
580 clear_cmdline = TRUE;
581 win_free_popup(wp);
582 redraw_all_later(NOT_VALID);
583}
584
Bram Moolenaarec583842019-05-26 14:11:23 +0200585/*
586 * Close a popup window by Window-id.
587 */
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200588 void
Bram Moolenaarec583842019-05-26 14:11:23 +0200589popup_close(int id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200590{
591 win_T *wp;
Bram Moolenaarec583842019-05-26 14:11:23 +0200592 tabpage_T *tp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200593 win_T *prev = NULL;
594
Bram Moolenaarec583842019-05-26 14:11:23 +0200595 // go through global popups
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200596 for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
Bram Moolenaarec583842019-05-26 14:11:23 +0200597 if (wp->w_id == id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200598 {
599 if (prev == NULL)
600 first_popupwin = wp->w_next;
601 else
602 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200603 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200604 return;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200605 }
606
Bram Moolenaarec583842019-05-26 14:11:23 +0200607 // go through tab-local popups
608 FOR_ALL_TABPAGES(tp)
609 popup_close_tabpage(tp, id);
610}
611
612/*
613 * Close a popup window with Window-id "id" in tabpage "tp".
614 */
615 void
616popup_close_tabpage(tabpage_T *tp, int id)
617{
618 win_T *wp;
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200619 win_T **root = &tp->tp_first_popupwin;
Bram Moolenaarec583842019-05-26 14:11:23 +0200620 win_T *prev = NULL;
621
Bram Moolenaarec583842019-05-26 14:11:23 +0200622 for (wp = *root; wp != NULL; prev = wp, wp = wp->w_next)
623 if (wp->w_id == id)
624 {
625 if (prev == NULL)
626 *root = wp->w_next;
627 else
628 prev->w_next = wp->w_next;
Bram Moolenaar2cd0dce2019-05-26 22:17:52 +0200629 popup_free(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200630 return;
631 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200632}
633
634 void
635close_all_popups(void)
636{
637 while (first_popupwin != NULL)
638 popup_close(first_popupwin->w_id);
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200639 while (curtab->tp_first_popupwin != NULL)
640 popup_close(curtab->tp_first_popupwin->w_id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200641}
642
643 void
644ex_popupclear(exarg_T *eap UNUSED)
645{
646 close_all_popups();
647}
648
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200649/*
650 * popup_move({id}, {options})
651 */
652 void
653f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
654{
655 dict_T *d;
656 int nr;
657 int id = (int)tv_get_number(argvars);
658 win_T *wp = find_popup_win(id);
659
660 if (wp == NULL)
661 return; // invalid {id}
662
663 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
664 {
665 emsg(_(e_dictreq));
666 return;
667 }
668 d = argvars[1].vval.v_dict;
669
670 if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0)
671 wp->w_minwidth = nr;
672 if ((nr = dict_get_number(d, (char_u *)"minheight")) > 0)
673 wp->w_minheight = nr;
674 if ((nr = dict_get_number(d, (char_u *)"maxwidth")) > 0)
675 wp->w_maxwidth = nr;
676 if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0)
677 wp->w_maxheight = nr;
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200678 get_pos_options(wp, d);
Bram Moolenaar60cdb302019-05-27 21:54:10 +0200679
680 if (wp->w_winrow + wp->w_height >= cmdline_row)
681 clear_cmdline = TRUE;
682 popup_adjust_position(wp);
683 redraw_all_later(NOT_VALID);
684}
685
Bram Moolenaarbc133542019-05-29 20:26:46 +0200686/*
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200687 * popup_getpos({id})
Bram Moolenaarbc133542019-05-29 20:26:46 +0200688 */
689 void
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200690f_popup_getpos(typval_T *argvars, typval_T *rettv)
Bram Moolenaarbc133542019-05-29 20:26:46 +0200691{
692 dict_T *dict;
693 int id = (int)tv_get_number(argvars);
694 win_T *wp = find_popup_win(id);
695
696 if (rettv_dict_alloc(rettv) == OK)
697 {
698 if (wp == NULL)
699 return; // invalid {id}
700 dict = rettv->vval.v_dict;
701 dict_add_number(dict, "line", wp->w_winrow + 1);
702 dict_add_number(dict, "col", wp->w_wincol + 1);
703 dict_add_number(dict, "width", wp->w_width);
704 dict_add_number(dict, "height", wp->w_height);
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200705 dict_add_number(dict, "visible",
706 (wp->w_popup_flags & POPF_HIDDEN) == 0);
707 }
708}
709
710/*
711 * f_popup_getoptions({id})
712 */
713 void
714f_popup_getoptions(typval_T *argvars, typval_T *rettv)
715{
716 dict_T *dict;
717 int id = (int)tv_get_number(argvars);
718 win_T *wp = find_popup_win(id);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200719 int i;
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200720
721 if (rettv_dict_alloc(rettv) == OK)
722 {
723 if (wp == NULL)
724 return;
725
726 dict = rettv->vval.v_dict;
727 dict_add_number(dict, "line", wp->w_wantline);
728 dict_add_number(dict, "col", wp->w_wantcol);
729 dict_add_number(dict, "minwidth", wp->w_minwidth);
730 dict_add_number(dict, "minheight", wp->w_minheight);
731 dict_add_number(dict, "maxheight", wp->w_maxheight);
732 dict_add_number(dict, "maxwidth", wp->w_maxwidth);
733 dict_add_number(dict, "zindex", wp->w_zindex);
Bram Moolenaarac1f1bc2019-05-30 21:24:26 +0200734
735 for (i = 0; i < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T));
736 ++i)
737 if (wp->w_popup_pos == poppos_entries[i].pp_val)
738 {
739 dict_add_string(dict, "pos",
740 (char_u *)poppos_entries[i].pp_name);
741 break;
742 }
743
Bram Moolenaar8c2a6002019-05-30 14:29:45 +0200744# if defined(FEAT_TIMERS)
745 dict_add_number(dict, "time", wp->w_popup_timer != NULL
746 ? (long)wp->w_popup_timer->tr_interval : 0L);
747# endif
Bram Moolenaarbc133542019-05-29 20:26:46 +0200748 }
749}
Bram Moolenaar815b76b2019-06-01 14:15:52 +0200750
751 int
752not_in_popup_window()
753{
754 if (bt_popup(curwin->w_buffer))
755 {
756 emsg(_("E994: Not allowed in a popup window"));
757 return TRUE;
758 }
759 return FALSE;
760}
761
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200762#endif // FEAT_TEXT_PROP