blob: 116efcb23f45049802d8a024abc6c240c6641e22 [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
18/*
19 * Go through the options in "dict" and apply them to buffer "buf" displayed in
20 * popup window "wp".
21 */
22 static void
23apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
24{
Bram Moolenaar51fe3b12019-05-26 20:10:06 +020025 int nr;
26
Bram Moolenaar4d784b22019-05-25 19:51:39 +020027 wp->w_maxwidth = dict_get_number(dict, (char_u *)"maxwidth");
28 wp->w_maxheight = dict_get_number(dict, (char_u *)"maxheight");
29 wp->w_winrow = dict_get_number(dict, (char_u *)"line");
30 wp->w_wincol = dict_get_number(dict, (char_u *)"col");
31 wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
Bram Moolenaar51fe3b12019-05-26 20:10:06 +020032
Bram Moolenaar35d5af62019-05-26 20:44:10 +020033#if defined(FEAT_TIMERS)
Bram Moolenaar51fe3b12019-05-26 20:10:06 +020034 // Add timer to close the popup after some time.
35 nr = dict_get_number(dict, (char_u *)"time");
36 if (nr > 0)
37 {
38 char_u cbbuf[50];
39 char_u *ptr = cbbuf;
40 typval_T tv;
41
42 vim_snprintf((char *)cbbuf, sizeof(cbbuf),
43 "{_ -> popup_close(%d)}", wp->w_id);
44 if (get_lambda_tv(&ptr, &tv, TRUE) == OK)
45 {
46 wp->w_popup_timer = create_timer(nr, 0);
47 wp->w_popup_timer->tr_callback =
48 vim_strsave(partial_name(tv.vval.v_partial));
49 func_ref(wp->w_popup_timer->tr_callback);
50 wp->w_popup_timer->tr_partial = tv.vval.v_partial;
51 }
52 }
Bram Moolenaar35d5af62019-05-26 20:44:10 +020053#endif
Bram Moolenaar51fe3b12019-05-26 20:10:06 +020054
Bram Moolenaar4d784b22019-05-25 19:51:39 +020055}
56
57/*
58 * popup_create({text}, {options})
59 */
60 void
61f_popup_create(typval_T *argvars, typval_T *rettv)
62{
63 win_T *wp;
64 buf_T *buf;
65 dict_T *d;
66 int nr;
67
68 // Check arguments look OK.
69 if (!(argvars[0].v_type == VAR_STRING
70 && argvars[0].vval.v_string != NULL
71 && STRLEN(argvars[0].vval.v_string) > 0)
72 && !(argvars[0].v_type == VAR_LIST
73 && argvars[0].vval.v_list != NULL
74 && argvars[0].vval.v_list->lv_len > 0))
75 {
76 emsg(_(e_listreq));
77 return;
78 }
79 if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
80 {
81 emsg(_(e_dictreq));
82 return;
83 }
84 d = argvars[1].vval.v_dict;
85
86 // Create the window and buffer.
87 wp = win_alloc_popup_win();
88 if (wp == NULL)
89 return;
90 rettv->vval.v_number = wp->w_id;
91 wp->w_p_wrap = TRUE; // 'wrap' is default on
92
93 buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
94 if (buf == NULL)
95 return;
96 ml_open(buf);
97 curbuf = buf;
98 set_string_option_direct((char_u *)"buftype", -1,
99 (char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
100 set_string_option_direct((char_u *)"bufhidden", -1,
101 (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
102 curbuf = curwin->w_buffer;
103 buf->b_p_ul = -1; // no undo
104 buf->b_p_swf = FALSE; // no swap file
105 buf->b_p_bl = FALSE; // unlisted buffer
106
107 win_init_popup_win(wp, buf);
108
109 nr = (int)dict_get_number(d, (char_u *)"tab");
110 if (nr == 0)
111 {
112 // popup on current tab
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200113 wp->w_next = curtab->tp_first_popupwin;
114 curtab->tp_first_popupwin = wp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200115 }
116 else if (nr < 0)
117 {
118 // global popup
119 wp->w_next = first_popupwin;
120 first_popupwin = wp;
121 }
122 else
123 // TODO: find tab page "nr"
124 emsg("Not implemented yet");
125
126 // Add text to the buffer.
127 if (argvars[0].v_type == VAR_STRING)
128 // just a string
129 ml_append_buf(buf, 0, argvars[0].vval.v_string, (colnr_T)0, TRUE);
130 else if (argvars[0].vval.v_list->lv_first->li_tv.v_type == VAR_STRING)
131 {
132 listitem_T *li;
133 linenr_T lnum = 0;
134 char_u *p;
135
136 // list of strings
137 for (li = argvars[0].vval.v_list->lv_first; li != NULL;
138 li = li->li_next)
139 if (li->li_tv.v_type == VAR_STRING)
140 {
141 p = li->li_tv.vval.v_string;
142 ml_append_buf(buf, lnum++,
143 p == NULL ? (char_u *)"" : p, (colnr_T)0, TRUE);
144 }
145 }
146 else
147 // TODO: handle a list of dictionaries
148 emsg("Not implemented yet");
149
150 // Delete the line of the empty buffer.
151 curbuf = buf;
152 ml_delete(buf->b_ml.ml_line_count, FALSE);
153 curbuf = curwin->w_buffer;
154
155 // Deal with options.
156 apply_options(wp, buf, argvars[1].vval.v_dict);
157
158 // set default values
159 if (wp->w_zindex == 0)
160 wp->w_zindex = 50;
161
162 // TODO: Compute the size and position properly.
163
164 // Default position is in middle of the screen, assuming a small popup
165 if (wp->w_winrow == 0)
166 wp->w_winrow = Rows > 5 ? Rows / 2 - 2 : 0;
167 else
168 --wp->w_winrow; // option value is one-based
169 if (wp->w_wincol == 0)
170 wp->w_wincol = Columns > 20 ? Columns / 2 - 10 : 0;
171 else
172 --wp->w_wincol; // option value is one-based
173
174
175 // TODO: set width based on longest text line and the 'wrap' option
176 wp->w_width = wp->w_maxwidth == 0 ? 20 : wp->w_maxwidth;
177 if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
178 wp->w_width = wp->w_maxwidth;
179 if (wp->w_width > Columns - wp->w_wincol)
180 wp->w_width = Columns - wp->w_wincol;
181
182 // TODO: adjust height for wrapped lines
183 wp->w_height = buf->b_ml.ml_line_count;
184 if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
185 wp->w_height = wp->w_maxheight;
186 if (wp->w_height > Rows - wp->w_winrow)
187 wp->w_height = Rows - wp->w_winrow;
188
189 wp->w_vsep_width = 0;
190
191 redraw_all_later(NOT_VALID);
192}
193
194/*
195 * popup_close({id})
196 */
197 void
198f_popup_close(typval_T *argvars, typval_T *rettv UNUSED)
199{
200 int nr = (int)tv_get_number(argvars);
201
202 popup_close(nr);
203}
204
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200205 static void
206popup_undisplay(win_T *wp)
207{
208 if (wp->w_winrow + wp->w_height >= cmdline_row)
209 clear_cmdline = TRUE;
210 win_free_popup(wp);
211 redraw_all_later(NOT_VALID);
212}
213
Bram Moolenaarec583842019-05-26 14:11:23 +0200214/*
215 * Close a popup window by Window-id.
216 */
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200217 void
Bram Moolenaarec583842019-05-26 14:11:23 +0200218popup_close(int id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200219{
220 win_T *wp;
Bram Moolenaarec583842019-05-26 14:11:23 +0200221 tabpage_T *tp;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200222 win_T *prev = NULL;
223
Bram Moolenaarec583842019-05-26 14:11:23 +0200224 // go through global popups
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200225 for (wp = first_popupwin; wp != NULL; prev = wp, wp = wp->w_next)
Bram Moolenaarec583842019-05-26 14:11:23 +0200226 if (wp->w_id == id)
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200227 {
228 if (prev == NULL)
229 first_popupwin = wp->w_next;
230 else
231 prev->w_next = wp->w_next;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200232 popup_undisplay(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200233 return;
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200234 }
235
Bram Moolenaarec583842019-05-26 14:11:23 +0200236 // go through tab-local popups
237 FOR_ALL_TABPAGES(tp)
238 popup_close_tabpage(tp, id);
239}
240
241/*
242 * Close a popup window with Window-id "id" in tabpage "tp".
243 */
244 void
245popup_close_tabpage(tabpage_T *tp, int id)
246{
247 win_T *wp;
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200248 win_T **root = &tp->tp_first_popupwin;
Bram Moolenaarec583842019-05-26 14:11:23 +0200249 win_T *prev = NULL;
250
Bram Moolenaarec583842019-05-26 14:11:23 +0200251 for (wp = *root; wp != NULL; prev = wp, wp = wp->w_next)
252 if (wp->w_id == id)
253 {
254 if (prev == NULL)
255 *root = wp->w_next;
256 else
257 prev->w_next = wp->w_next;
Bram Moolenaar51fe3b12019-05-26 20:10:06 +0200258 popup_undisplay(wp);
Bram Moolenaarec583842019-05-26 14:11:23 +0200259 return;
260 }
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200261}
262
263 void
264close_all_popups(void)
265{
266 while (first_popupwin != NULL)
267 popup_close(first_popupwin->w_id);
Bram Moolenaar9c27b1c2019-05-26 18:48:13 +0200268 while (curtab->tp_first_popupwin != NULL)
269 popup_close(curtab->tp_first_popupwin->w_id);
Bram Moolenaar4d784b22019-05-25 19:51:39 +0200270}
271
272 void
273ex_popupclear(exarg_T *eap UNUSED)
274{
275 close_all_popups();
276}
277
278#endif // FEAT_TEXT_PROP