Mercurial > vim
comparison src/popupmenu.c @ 18174:1ec6539cef68 v8.1.2082
patch 8.1.2082: some files have a weird name to fit in 8.3 characters
Commit: https://github.com/vim/vim/commit/30e8e73506e4522ef4aebf7d525c0e6ffe8805fd
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Sep 27 13:08:36 2019 +0200
patch 8.1.2082: some files have a weird name to fit in 8.3 characters
Problem: Some files have a weird name to fit in 8.3 characters.
Solution: Use a nicer names.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 27 Sep 2019 13:15:03 +0200 |
parents | src/popupmnu.c@bb0e25a8b5d7 |
children | c8a53c0daeed |
comparison
equal
deleted
inserted
replaced
18173:f046fb05b08f | 18174:1ec6539cef68 |
---|---|
1 /* 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 copying and usage conditions. | |
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 * popupmenu.c: Popup menu (PUM) | |
12 */ | |
13 #include "vim.h" | |
14 | |
15 static pumitem_T *pum_array = NULL; /* items of displayed pum */ | |
16 static int pum_size; /* nr of items in "pum_array" */ | |
17 static int pum_selected; /* index of selected item or -1 */ | |
18 static int pum_first = 0; /* index of top item */ | |
19 | |
20 static int call_update_screen = FALSE; | |
21 | |
22 static int pum_height; /* nr of displayed pum items */ | |
23 static int pum_width; /* width of displayed pum items */ | |
24 static int pum_base_width; /* width of pum items base */ | |
25 static int pum_kind_width; /* width of pum items kind column */ | |
26 static int pum_extra_width; /* width of extra stuff */ | |
27 static int pum_scrollbar; /* TRUE when scrollbar present */ | |
28 | |
29 static int pum_row; /* top row of pum */ | |
30 static int pum_col; /* left column of pum */ | |
31 | |
32 static win_T *pum_window = NULL; | |
33 static int pum_win_row; | |
34 static int pum_win_height; | |
35 static int pum_win_col; | |
36 static int pum_win_wcol; | |
37 static int pum_win_width; | |
38 | |
39 static int pum_do_redraw = FALSE; // do redraw anyway | |
40 static int pum_skip_redraw = FALSE; // skip redraw | |
41 | |
42 static int pum_set_selected(int n, int repeat); | |
43 | |
44 #define PUM_DEF_HEIGHT 10 | |
45 | |
46 static void | |
47 pum_compute_size(void) | |
48 { | |
49 int i; | |
50 int w; | |
51 | |
52 /* Compute the width of the widest match and the widest extra. */ | |
53 pum_base_width = 0; | |
54 pum_kind_width = 0; | |
55 pum_extra_width = 0; | |
56 for (i = 0; i < pum_size; ++i) | |
57 { | |
58 w = vim_strsize(pum_array[i].pum_text); | |
59 if (pum_base_width < w) | |
60 pum_base_width = w; | |
61 if (pum_array[i].pum_kind != NULL) | |
62 { | |
63 w = vim_strsize(pum_array[i].pum_kind) + 1; | |
64 if (pum_kind_width < w) | |
65 pum_kind_width = w; | |
66 } | |
67 if (pum_array[i].pum_extra != NULL) | |
68 { | |
69 w = vim_strsize(pum_array[i].pum_extra) + 1; | |
70 if (pum_extra_width < w) | |
71 pum_extra_width = w; | |
72 } | |
73 } | |
74 } | |
75 | |
76 /* | |
77 * Show the popup menu with items "array[size]". | |
78 * "array" must remain valid until pum_undisplay() is called! | |
79 * When possible the leftmost character is aligned with cursor column. | |
80 * The menu appears above the screen line "row" or at "row" + "height" - 1. | |
81 */ | |
82 void | |
83 pum_display( | |
84 pumitem_T *array, | |
85 int size, | |
86 int selected) /* index of initially selected item, none if | |
87 out of range */ | |
88 { | |
89 int def_width; | |
90 int max_width; | |
91 int context_lines; | |
92 int cursor_col; | |
93 int above_row; | |
94 int below_row; | |
95 int redo_count = 0; | |
96 #if defined(FEAT_QUICKFIX) | |
97 win_T *pvwin; | |
98 #endif | |
99 | |
100 do | |
101 { | |
102 def_width = p_pw; | |
103 above_row = 0; | |
104 below_row = cmdline_row; | |
105 | |
106 /* Pretend the pum is already there to avoid that must_redraw is set | |
107 * when 'cuc' is on. */ | |
108 pum_array = (pumitem_T *)1; | |
109 validate_cursor_col(); | |
110 pum_array = NULL; | |
111 | |
112 // Remember the essential parts of the window position and size, so we | |
113 // can decide when to reposition the popup menu. | |
114 pum_window = curwin; | |
115 pum_win_row = curwin->w_wrow + W_WINROW(curwin); | |
116 pum_win_height = curwin->w_height; | |
117 pum_win_col = curwin->w_wincol; | |
118 pum_win_wcol = curwin->w_wcol; | |
119 pum_win_width = curwin->w_width; | |
120 | |
121 #if defined(FEAT_QUICKFIX) | |
122 FOR_ALL_WINDOWS(pvwin) | |
123 if (pvwin->w_p_pvw) | |
124 break; | |
125 if (pvwin != NULL) | |
126 { | |
127 if (W_WINROW(pvwin) < W_WINROW(curwin)) | |
128 above_row = W_WINROW(pvwin) + pvwin->w_height; | |
129 else if (W_WINROW(pvwin) > W_WINROW(curwin) + curwin->w_height) | |
130 below_row = W_WINROW(pvwin); | |
131 } | |
132 #endif | |
133 | |
134 /* | |
135 * Figure out the size and position of the pum. | |
136 */ | |
137 if (size < PUM_DEF_HEIGHT) | |
138 pum_height = size; | |
139 else | |
140 pum_height = PUM_DEF_HEIGHT; | |
141 if (p_ph > 0 && pum_height > p_ph) | |
142 pum_height = p_ph; | |
143 | |
144 /* Put the pum below "pum_win_row" if possible. If there are few lines | |
145 * decide on where there is more room. */ | |
146 if (pum_win_row + 2 >= below_row - pum_height | |
147 && pum_win_row - above_row > (below_row - above_row) / 2) | |
148 { | |
149 /* pum above "pum_win_row" */ | |
150 | |
151 /* Leave two lines of context if possible */ | |
152 if (curwin->w_wrow - curwin->w_cline_row >= 2) | |
153 context_lines = 2; | |
154 else | |
155 context_lines = curwin->w_wrow - curwin->w_cline_row; | |
156 | |
157 if (pum_win_row >= size + context_lines) | |
158 { | |
159 pum_row = pum_win_row - size - context_lines; | |
160 pum_height = size; | |
161 } | |
162 else | |
163 { | |
164 pum_row = 0; | |
165 pum_height = pum_win_row - context_lines; | |
166 } | |
167 if (p_ph > 0 && pum_height > p_ph) | |
168 { | |
169 pum_row += pum_height - p_ph; | |
170 pum_height = p_ph; | |
171 } | |
172 } | |
173 else | |
174 { | |
175 /* pum below "pum_win_row" */ | |
176 | |
177 /* Leave two lines of context if possible */ | |
178 if (curwin->w_cline_row | |
179 + curwin->w_cline_height - curwin->w_wrow >= 3) | |
180 context_lines = 3; | |
181 else | |
182 context_lines = curwin->w_cline_row | |
183 + curwin->w_cline_height - curwin->w_wrow; | |
184 | |
185 pum_row = pum_win_row + context_lines; | |
186 if (size > below_row - pum_row) | |
187 pum_height = below_row - pum_row; | |
188 else | |
189 pum_height = size; | |
190 if (p_ph > 0 && pum_height > p_ph) | |
191 pum_height = p_ph; | |
192 } | |
193 | |
194 /* don't display when we only have room for one line */ | |
195 if (pum_height < 1 || (pum_height == 1 && size > 1)) | |
196 return; | |
197 | |
198 #if defined(FEAT_QUICKFIX) | |
199 // If there is a preview window above avoid drawing over it. | |
200 if (pvwin != NULL && pum_row < above_row && pum_height > above_row) | |
201 { | |
202 pum_row = above_row; | |
203 pum_height = pum_win_row - above_row; | |
204 } | |
205 #endif | |
206 | |
207 pum_array = array; | |
208 pum_size = size; | |
209 pum_compute_size(); | |
210 max_width = pum_base_width; | |
211 | |
212 /* Calculate column */ | |
213 #ifdef FEAT_RIGHTLEFT | |
214 if (curwin->w_p_rl) | |
215 cursor_col = curwin->w_wincol + curwin->w_width | |
216 - curwin->w_wcol - 1; | |
217 else | |
218 #endif | |
219 cursor_col = curwin->w_wincol + curwin->w_wcol; | |
220 | |
221 /* if there are more items than room we need a scrollbar */ | |
222 if (pum_height < size) | |
223 { | |
224 pum_scrollbar = 1; | |
225 ++max_width; | |
226 } | |
227 else | |
228 pum_scrollbar = 0; | |
229 | |
230 if (def_width < max_width) | |
231 def_width = max_width; | |
232 | |
233 if (((cursor_col < Columns - p_pw | |
234 || cursor_col < Columns - max_width) | |
235 #ifdef FEAT_RIGHTLEFT | |
236 && !curwin->w_p_rl) | |
237 || (curwin->w_p_rl | |
238 && (cursor_col > p_pw || cursor_col > max_width) | |
239 #endif | |
240 )) | |
241 { | |
242 /* align pum with "cursor_col" */ | |
243 pum_col = cursor_col; | |
244 | |
245 /* start with the maximum space available */ | |
246 #ifdef FEAT_RIGHTLEFT | |
247 if (curwin->w_p_rl) | |
248 pum_width = pum_col - pum_scrollbar + 1; | |
249 else | |
250 #endif | |
251 pum_width = Columns - pum_col - pum_scrollbar; | |
252 | |
253 if (pum_width > max_width + pum_kind_width + pum_extra_width + 1 | |
254 && pum_width > p_pw) | |
255 { | |
256 /* the width is more than needed for the items, make it | |
257 * narrower */ | |
258 pum_width = max_width + pum_kind_width + pum_extra_width + 1; | |
259 if (pum_width < p_pw) | |
260 pum_width = p_pw; | |
261 } | |
262 else if (((cursor_col > p_pw || cursor_col > max_width) | |
263 #ifdef FEAT_RIGHTLEFT | |
264 && !curwin->w_p_rl) | |
265 || (curwin->w_p_rl && (cursor_col < Columns - p_pw | |
266 || cursor_col < Columns - max_width) | |
267 #endif | |
268 )) | |
269 { | |
270 /* align pum edge with "cursor_col" */ | |
271 #ifdef FEAT_RIGHTLEFT | |
272 if (curwin->w_p_rl | |
273 && W_ENDCOL(curwin) < max_width + pum_scrollbar + 1) | |
274 { | |
275 pum_col = cursor_col + max_width + pum_scrollbar + 1; | |
276 if (pum_col >= Columns) | |
277 pum_col = Columns - 1; | |
278 } | |
279 else if (!curwin->w_p_rl) | |
280 #endif | |
281 { | |
282 if (curwin->w_wincol > Columns - max_width - pum_scrollbar | |
283 && max_width <= p_pw) | |
284 { | |
285 /* use full width to end of the screen */ | |
286 pum_col = Columns - max_width - pum_scrollbar; | |
287 if (pum_col < 0) | |
288 pum_col = 0; | |
289 } | |
290 } | |
291 | |
292 #ifdef FEAT_RIGHTLEFT | |
293 if (curwin->w_p_rl) | |
294 pum_width = pum_col - pum_scrollbar + 1; | |
295 else | |
296 #endif | |
297 pum_width = Columns - pum_col - pum_scrollbar; | |
298 | |
299 if (pum_width < p_pw) | |
300 { | |
301 pum_width = p_pw; | |
302 #ifdef FEAT_RIGHTLEFT | |
303 if (curwin->w_p_rl) | |
304 { | |
305 if (pum_width > pum_col) | |
306 pum_width = pum_col; | |
307 } | |
308 else | |
309 #endif | |
310 { | |
311 if (pum_width >= Columns - pum_col) | |
312 pum_width = Columns - pum_col - 1; | |
313 } | |
314 } | |
315 else if (pum_width > max_width + pum_kind_width | |
316 + pum_extra_width + 1 | |
317 && pum_width > p_pw) | |
318 { | |
319 pum_width = max_width + pum_kind_width | |
320 + pum_extra_width + 1; | |
321 if (pum_width < p_pw) | |
322 pum_width = p_pw; | |
323 } | |
324 } | |
325 | |
326 } | |
327 else if (Columns < def_width) | |
328 { | |
329 /* not enough room, will use what we have */ | |
330 #ifdef FEAT_RIGHTLEFT | |
331 if (curwin->w_p_rl) | |
332 pum_col = Columns - 1; | |
333 else | |
334 #endif | |
335 pum_col = 0; | |
336 pum_width = Columns - 1; | |
337 } | |
338 else | |
339 { | |
340 if (max_width > p_pw) | |
341 max_width = p_pw; /* truncate */ | |
342 #ifdef FEAT_RIGHTLEFT | |
343 if (curwin->w_p_rl) | |
344 pum_col = max_width - 1; | |
345 else | |
346 #endif | |
347 pum_col = Columns - max_width; | |
348 pum_width = max_width - pum_scrollbar; | |
349 } | |
350 | |
351 /* Set selected item and redraw. If the window size changed need to | |
352 * redo the positioning. Limit this to two times, when there is not | |
353 * much room the window size will keep changing. */ | |
354 } while (pum_set_selected(selected, redo_count) && ++redo_count <= 2); | |
355 } | |
356 | |
357 /* | |
358 * Set a flag that when pum_redraw() is called it first calls update_screen(). | |
359 * This will avoid clearing and redrawing the popup menu, prevent flicker. | |
360 */ | |
361 void | |
362 pum_call_update_screen() | |
363 { | |
364 call_update_screen = TRUE; | |
365 | |
366 // Update the cursor position to be able to compute the popup menu | |
367 // position. The cursor line length may have changed because of the | |
368 // inserted completion. | |
369 curwin->w_valid &= ~(VALID_CROW|VALID_CHEIGHT); | |
370 validate_cursor(); | |
371 } | |
372 | |
373 /* | |
374 * Return TRUE if we are going to redraw the popup menu and the screen position | |
375 * "row"/"col" is under the popup menu. | |
376 */ | |
377 int | |
378 pum_under_menu(int row, int col) | |
379 { | |
380 return pum_skip_redraw | |
381 && row >= pum_row | |
382 && row < pum_row + pum_height | |
383 && col >= pum_col - 1 | |
384 && col < pum_col + pum_width; | |
385 } | |
386 | |
387 /* | |
388 * Redraw the popup menu, using "pum_first" and "pum_selected". | |
389 */ | |
390 void | |
391 pum_redraw(void) | |
392 { | |
393 int row = pum_row; | |
394 int col; | |
395 int attr_norm = highlight_attr[HLF_PNI]; | |
396 int attr_select = highlight_attr[HLF_PSI]; | |
397 int attr_scroll = highlight_attr[HLF_PSB]; | |
398 int attr_thumb = highlight_attr[HLF_PST]; | |
399 int attr; | |
400 int i; | |
401 int idx; | |
402 char_u *s; | |
403 char_u *p = NULL; | |
404 int totwidth, width, w; | |
405 int thumb_pos = 0; | |
406 int thumb_height = 1; | |
407 int round; | |
408 int n; | |
409 | |
410 if (call_update_screen) | |
411 { | |
412 call_update_screen = FALSE; | |
413 pum_skip_redraw = TRUE; // do not redraw in pum_may_redraw(). | |
414 update_screen(0); | |
415 pum_skip_redraw = FALSE; | |
416 } | |
417 | |
418 // never display more than we have | |
419 if (pum_first > pum_size - pum_height) | |
420 pum_first = pum_size - pum_height; | |
421 | |
422 if (pum_scrollbar) | |
423 { | |
424 thumb_height = pum_height * pum_height / pum_size; | |
425 if (thumb_height == 0) | |
426 thumb_height = 1; | |
427 thumb_pos = (pum_first * (pum_height - thumb_height) | |
428 + (pum_size - pum_height) / 2) | |
429 / (pum_size - pum_height); | |
430 } | |
431 | |
432 #ifdef FEAT_TEXT_PROP | |
433 // The popup menu is drawn over popup menus with zindex under | |
434 // POPUPMENU_ZINDEX. | |
435 screen_zindex = POPUPMENU_ZINDEX; | |
436 #endif | |
437 | |
438 for (i = 0; i < pum_height; ++i) | |
439 { | |
440 idx = i + pum_first; | |
441 attr = (idx == pum_selected) ? attr_select : attr_norm; | |
442 | |
443 /* prepend a space if there is room */ | |
444 #ifdef FEAT_RIGHTLEFT | |
445 if (curwin->w_p_rl) | |
446 { | |
447 if (pum_col < curwin->w_wincol + curwin->w_width - 1) | |
448 screen_putchar(' ', row, pum_col + 1, attr); | |
449 } | |
450 else | |
451 #endif | |
452 if (pum_col > 0) | |
453 screen_putchar(' ', row, pum_col - 1, attr); | |
454 | |
455 /* Display each entry, use two spaces for a Tab. | |
456 * Do this 3 times: For the main text, kind and extra info */ | |
457 col = pum_col; | |
458 totwidth = 0; | |
459 for (round = 1; round <= 3; ++round) | |
460 { | |
461 width = 0; | |
462 s = NULL; | |
463 switch (round) | |
464 { | |
465 case 1: p = pum_array[idx].pum_text; break; | |
466 case 2: p = pum_array[idx].pum_kind; break; | |
467 case 3: p = pum_array[idx].pum_extra; break; | |
468 } | |
469 if (p != NULL) | |
470 for ( ; ; MB_PTR_ADV(p)) | |
471 { | |
472 if (s == NULL) | |
473 s = p; | |
474 w = ptr2cells(p); | |
475 if (*p == NUL || *p == TAB || totwidth + w > pum_width) | |
476 { | |
477 /* Display the text that fits or comes before a Tab. | |
478 * First convert it to printable characters. */ | |
479 char_u *st; | |
480 int saved = *p; | |
481 | |
482 if (saved != NUL) | |
483 *p = NUL; | |
484 st = transstr(s); | |
485 if (saved != NUL) | |
486 *p = saved; | |
487 #ifdef FEAT_RIGHTLEFT | |
488 if (curwin->w_p_rl) | |
489 { | |
490 if (st != NULL) | |
491 { | |
492 char_u *rt = reverse_text(st); | |
493 | |
494 if (rt != NULL) | |
495 { | |
496 char_u *rt_start = rt; | |
497 int size; | |
498 | |
499 size = vim_strsize(rt); | |
500 if (size > pum_width) | |
501 { | |
502 do | |
503 { | |
504 size -= has_mbyte | |
505 ? (*mb_ptr2cells)(rt) : 1; | |
506 MB_PTR_ADV(rt); | |
507 } while (size > pum_width); | |
508 | |
509 if (size < pum_width) | |
510 { | |
511 /* Most left character requires | |
512 * 2-cells but only 1 cell is | |
513 * available on screen. Put a | |
514 * '<' on the left of the pum | |
515 * item */ | |
516 *(--rt) = '<'; | |
517 size++; | |
518 } | |
519 } | |
520 screen_puts_len(rt, (int)STRLEN(rt), | |
521 row, col - size + 1, attr); | |
522 vim_free(rt_start); | |
523 } | |
524 vim_free(st); | |
525 } | |
526 col -= width; | |
527 } | |
528 else | |
529 #endif | |
530 { | |
531 if (st != NULL) | |
532 { | |
533 screen_puts_len(st, (int)STRLEN(st), row, col, | |
534 attr); | |
535 vim_free(st); | |
536 } | |
537 col += width; | |
538 } | |
539 | |
540 if (*p != TAB) | |
541 break; | |
542 | |
543 /* Display two spaces for a Tab. */ | |
544 #ifdef FEAT_RIGHTLEFT | |
545 if (curwin->w_p_rl) | |
546 { | |
547 screen_puts_len((char_u *)" ", 2, row, col - 1, | |
548 attr); | |
549 col -= 2; | |
550 } | |
551 else | |
552 #endif | |
553 { | |
554 screen_puts_len((char_u *)" ", 2, row, col, attr); | |
555 col += 2; | |
556 } | |
557 totwidth += 2; | |
558 s = NULL; /* start text at next char */ | |
559 width = 0; | |
560 } | |
561 else | |
562 width += w; | |
563 } | |
564 | |
565 if (round > 1) | |
566 n = pum_kind_width + 1; | |
567 else | |
568 n = 1; | |
569 | |
570 /* Stop when there is nothing more to display. */ | |
571 if (round == 3 | |
572 || (round == 2 && pum_array[idx].pum_extra == NULL) | |
573 || (round == 1 && pum_array[idx].pum_kind == NULL | |
574 && pum_array[idx].pum_extra == NULL) | |
575 || pum_base_width + n >= pum_width) | |
576 break; | |
577 #ifdef FEAT_RIGHTLEFT | |
578 if (curwin->w_p_rl) | |
579 { | |
580 screen_fill(row, row + 1, pum_col - pum_base_width - n + 1, | |
581 col + 1, ' ', ' ', attr); | |
582 col = pum_col - pum_base_width - n + 1; | |
583 } | |
584 else | |
585 #endif | |
586 { | |
587 screen_fill(row, row + 1, col, pum_col + pum_base_width + n, | |
588 ' ', ' ', attr); | |
589 col = pum_col + pum_base_width + n; | |
590 } | |
591 totwidth = pum_base_width + n; | |
592 } | |
593 | |
594 #ifdef FEAT_RIGHTLEFT | |
595 if (curwin->w_p_rl) | |
596 screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ', | |
597 ' ', attr); | |
598 else | |
599 #endif | |
600 screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', | |
601 attr); | |
602 if (pum_scrollbar > 0) | |
603 { | |
604 #ifdef FEAT_RIGHTLEFT | |
605 if (curwin->w_p_rl) | |
606 screen_putchar(' ', row, pum_col - pum_width, | |
607 i >= thumb_pos && i < thumb_pos + thumb_height | |
608 ? attr_thumb : attr_scroll); | |
609 else | |
610 #endif | |
611 screen_putchar(' ', row, pum_col + pum_width, | |
612 i >= thumb_pos && i < thumb_pos + thumb_height | |
613 ? attr_thumb : attr_scroll); | |
614 } | |
615 | |
616 ++row; | |
617 } | |
618 | |
619 #ifdef FEAT_TEXT_PROP | |
620 screen_zindex = 0; | |
621 #endif | |
622 } | |
623 | |
624 #if defined(FEAT_TEXT_PROP) && defined(FEAT_QUICKFIX) | |
625 static void | |
626 pum_position_info_popup(void) | |
627 { | |
628 int col = pum_col + pum_width + 1; | |
629 int row = pum_row; | |
630 int botpos = POPPOS_BOTLEFT; | |
631 | |
632 curwin->w_popup_pos = POPPOS_TOPLEFT; | |
633 if (Columns - col < 20 && Columns - col < pum_col) | |
634 { | |
635 col = pum_col - 1; | |
636 curwin->w_popup_pos = POPPOS_TOPRIGHT; | |
637 botpos = POPPOS_BOTRIGHT; | |
638 curwin->w_maxwidth = pum_col - 1; | |
639 } | |
640 else | |
641 curwin->w_maxwidth = Columns - col + 1; | |
642 curwin->w_maxwidth -= popup_extra_width(curwin); | |
643 | |
644 row -= popup_top_extra(curwin); | |
645 if (curwin->w_popup_flags & POPF_INFO_MENU) | |
646 { | |
647 if (pum_row < pum_win_row) | |
648 { | |
649 // menu above cursor line, align with bottom | |
650 row += pum_height; | |
651 curwin->w_popup_pos = botpos; | |
652 } | |
653 else | |
654 // menu below cursor line, align with top | |
655 row += 1; | |
656 } | |
657 else | |
658 // align with the selected item | |
659 row += pum_selected - pum_first + 1; | |
660 | |
661 popup_set_wantpos_rowcol(curwin, row, col); | |
662 } | |
663 #endif | |
664 | |
665 /* | |
666 * Set the index of the currently selected item. The menu will scroll when | |
667 * necessary. When "n" is out of range don't scroll. | |
668 * This may be repeated when the preview window is used: | |
669 * "repeat" == 0: open preview window normally | |
670 * "repeat" == 1: open preview window but don't set the size | |
671 * "repeat" == 2: don't open preview window | |
672 * Returns TRUE when the window was resized and the location of the popup menu | |
673 * must be recomputed. | |
674 */ | |
675 static int | |
676 pum_set_selected(int n, int repeat UNUSED) | |
677 { | |
678 int resized = FALSE; | |
679 int context = pum_height / 2; | |
680 #ifdef FEAT_QUICKFIX | |
681 int prev_selected = pum_selected; | |
682 #endif | |
683 #ifdef FEAT_TEXT_PROP | |
684 int has_info = FALSE; | |
685 #endif | |
686 | |
687 pum_selected = n; | |
688 | |
689 if (pum_selected >= 0 && pum_selected < pum_size) | |
690 { | |
691 if (pum_first > pum_selected - 4) | |
692 { | |
693 /* scroll down; when we did a jump it's probably a PageUp then | |
694 * scroll a whole page */ | |
695 if (pum_first > pum_selected - 2) | |
696 { | |
697 pum_first -= pum_height - 2; | |
698 if (pum_first < 0) | |
699 pum_first = 0; | |
700 else if (pum_first > pum_selected) | |
701 pum_first = pum_selected; | |
702 } | |
703 else | |
704 pum_first = pum_selected; | |
705 } | |
706 else if (pum_first < pum_selected - pum_height + 5) | |
707 { | |
708 /* scroll up; when we did a jump it's probably a PageDown then | |
709 * scroll a whole page */ | |
710 if (pum_first < pum_selected - pum_height + 1 + 2) | |
711 { | |
712 pum_first += pum_height - 2; | |
713 if (pum_first < pum_selected - pum_height + 1) | |
714 pum_first = pum_selected - pum_height + 1; | |
715 } | |
716 else | |
717 pum_first = pum_selected - pum_height + 1; | |
718 } | |
719 | |
720 /* Give a few lines of context when possible. */ | |
721 if (context > 3) | |
722 context = 3; | |
723 if (pum_height > 2) | |
724 { | |
725 if (pum_first > pum_selected - context) | |
726 { | |
727 /* scroll down */ | |
728 pum_first = pum_selected - context; | |
729 if (pum_first < 0) | |
730 pum_first = 0; | |
731 } | |
732 else if (pum_first < pum_selected + context - pum_height + 1) | |
733 { | |
734 /* scroll up */ | |
735 pum_first = pum_selected + context - pum_height + 1; | |
736 } | |
737 } | |
738 // adjust for the number of lines displayed | |
739 if (pum_first > pum_size - pum_height) | |
740 pum_first = pum_size - pum_height; | |
741 | |
742 #if defined(FEAT_QUICKFIX) | |
743 /* | |
744 * Show extra info in the preview window if there is something and | |
745 * 'completeopt' contains "preview" or "popup". | |
746 * Skip this when tried twice already. | |
747 * Skip this also when there is not much room. | |
748 * NOTE: Be very careful not to sync undo! | |
749 */ | |
750 if (pum_array[pum_selected].pum_info != NULL | |
751 && Rows > 10 | |
752 && repeat <= 1 | |
753 && vim_strchr(p_cot, 'p') != NULL) | |
754 { | |
755 win_T *curwin_save = curwin; | |
756 tabpage_T *curtab_save = curtab; | |
757 int res = OK; | |
758 # ifdef FEAT_TEXT_PROP | |
759 int use_popup = strstr((char *)p_cot, "popup") != NULL; | |
760 # else | |
761 # define use_popup 0 | |
762 # endif | |
763 # ifdef FEAT_TEXT_PROP | |
764 has_info = TRUE; | |
765 # endif | |
766 // Open a preview window. 3 lines by default. Prefer | |
767 // 'previewheight' if set and smaller. | |
768 g_do_tagpreview = 3; | |
769 if (p_pvh > 0 && p_pvh < g_do_tagpreview) | |
770 g_do_tagpreview = p_pvh; | |
771 ++RedrawingDisabled; | |
772 // Prevent undo sync here, if an autocommand syncs undo weird | |
773 // things can happen to the undo tree. | |
774 ++no_u_sync; | |
775 resized = prepare_tagpreview(FALSE, FALSE, use_popup); | |
776 --no_u_sync; | |
777 --RedrawingDisabled; | |
778 g_do_tagpreview = 0; | |
779 | |
780 if (curwin->w_p_pvw | |
781 # ifdef FEAT_TEXT_PROP | |
782 || (curwin->w_popup_flags & POPF_INFO) | |
783 # endif | |
784 ) | |
785 { | |
786 if (!resized | |
787 && curbuf->b_nwindows == 1 | |
788 && curbuf->b_fname == NULL | |
789 && bt_nofile(curbuf) | |
790 && curbuf->b_p_bh[0] == 'w') | |
791 { | |
792 // Already a "wipeout" buffer, make it empty. | |
793 while (!BUFEMPTY()) | |
794 ml_delete((linenr_T)1, FALSE); | |
795 } | |
796 else | |
797 { | |
798 // Don't want to sync undo in the current buffer. | |
799 ++no_u_sync; | |
800 res = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 0, NULL); | |
801 --no_u_sync; | |
802 if (res == OK) | |
803 { | |
804 // Edit a new, empty buffer. Set options for a "wipeout" | |
805 // buffer. | |
806 set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); | |
807 set_option_value((char_u *)"bt", 0L, | |
808 (char_u *)"nofile", OPT_LOCAL); | |
809 set_option_value((char_u *)"bh", 0L, | |
810 (char_u *)"wipe", OPT_LOCAL); | |
811 set_option_value((char_u *)"diff", 0L, | |
812 NULL, OPT_LOCAL); | |
813 } | |
814 } | |
815 if (res == OK) | |
816 { | |
817 char_u *p, *e; | |
818 linenr_T lnum = 0; | |
819 | |
820 for (p = pum_array[pum_selected].pum_info; *p != NUL; ) | |
821 { | |
822 e = vim_strchr(p, '\n'); | |
823 if (e == NULL) | |
824 { | |
825 ml_append(lnum++, p, 0, FALSE); | |
826 break; | |
827 } | |
828 else | |
829 { | |
830 *e = NUL; | |
831 ml_append(lnum++, p, (int)(e - p + 1), FALSE); | |
832 *e = '\n'; | |
833 p = e + 1; | |
834 } | |
835 } | |
836 // delete the empty last line | |
837 ml_delete(curbuf->b_ml.ml_line_count, FALSE); | |
838 | |
839 /* Increase the height of the preview window to show the | |
840 * text, but no more than 'previewheight' lines. */ | |
841 if (repeat == 0 && !use_popup) | |
842 { | |
843 if (lnum > p_pvh) | |
844 lnum = p_pvh; | |
845 if (curwin->w_height < lnum) | |
846 { | |
847 win_setheight((int)lnum); | |
848 resized = TRUE; | |
849 } | |
850 } | |
851 | |
852 curbuf->b_changed = 0; | |
853 curbuf->b_p_ma = FALSE; | |
854 if (pum_selected != prev_selected) | |
855 { | |
856 # ifdef FEAT_TEXT_PROP | |
857 curwin->w_firstline = 1; | |
858 # endif | |
859 curwin->w_topline = 1; | |
860 } | |
861 else if (curwin->w_topline > curbuf->b_ml.ml_line_count) | |
862 curwin->w_topline = curbuf->b_ml.ml_line_count; | |
863 curwin->w_cursor.lnum = curwin->w_topline; | |
864 curwin->w_cursor.col = 0; | |
865 # ifdef FEAT_TEXT_PROP | |
866 if (use_popup) | |
867 { | |
868 pum_position_info_popup(); | |
869 if (win_valid(curwin_save)) | |
870 redraw_win_later(curwin_save, SOME_VALID); | |
871 } | |
872 # endif | |
873 if ((curwin != curwin_save && win_valid(curwin_save)) | |
874 || (curtab != curtab_save | |
875 && valid_tabpage(curtab_save))) | |
876 { | |
877 if (curtab != curtab_save && valid_tabpage(curtab_save)) | |
878 goto_tabpage_tp(curtab_save, FALSE, FALSE); | |
879 | |
880 /* When the first completion is done and the preview | |
881 * window is not resized, skip the preview window's | |
882 * status line redrawing. */ | |
883 if (ins_compl_active() && !resized) | |
884 curwin->w_redr_status = FALSE; | |
885 | |
886 /* Return cursor to where we were */ | |
887 validate_cursor(); | |
888 redraw_later(SOME_VALID); | |
889 | |
890 /* When the preview window was resized we need to | |
891 * update the view on the buffer. Only go back to | |
892 * the window when needed, otherwise it will always be | |
893 * redraw. */ | |
894 if (resized && win_valid(curwin_save)) | |
895 { | |
896 ++no_u_sync; | |
897 win_enter(curwin_save, TRUE); | |
898 --no_u_sync; | |
899 update_topline(); | |
900 } | |
901 | |
902 /* Update the screen before drawing the popup menu. | |
903 * Enable updating the status lines. */ | |
904 pum_do_redraw = TRUE; | |
905 update_screen(0); | |
906 pum_do_redraw = FALSE; | |
907 | |
908 if (!resized && win_valid(curwin_save)) | |
909 { | |
910 ++no_u_sync; | |
911 win_enter(curwin_save, TRUE); | |
912 --no_u_sync; | |
913 } | |
914 | |
915 /* May need to update the screen again when there are | |
916 * autocommands involved. */ | |
917 pum_do_redraw = TRUE; | |
918 update_screen(0); | |
919 pum_do_redraw = FALSE; | |
920 call_update_screen = FALSE; | |
921 } | |
922 } | |
923 } | |
924 # if defined(FEAT_TEXT_PROP) && defined(FEAT_QUICKFIX) | |
925 if (WIN_IS_POPUP(curwin)) | |
926 // can't keep focus in a popup window | |
927 win_enter(firstwin, TRUE); | |
928 # endif | |
929 } | |
930 #endif | |
931 } | |
932 #if defined(FEAT_TEXT_PROP) && defined(FEAT_QUICKFIX) | |
933 if (!has_info) | |
934 // hide any popup info window | |
935 popup_hide_info(); | |
936 #endif | |
937 | |
938 if (!resized) | |
939 pum_redraw(); | |
940 | |
941 return resized; | |
942 } | |
943 | |
944 /* | |
945 * Undisplay the popup menu (later). | |
946 */ | |
947 void | |
948 pum_undisplay(void) | |
949 { | |
950 pum_array = NULL; | |
951 redraw_all_later(NOT_VALID); | |
952 redraw_tabline = TRUE; | |
953 status_redraw_all(); | |
954 #if defined(FEAT_TEXT_PROP) && defined(FEAT_QUICKFIX) | |
955 // hide any popup info window | |
956 popup_hide_info(); | |
957 #endif | |
958 } | |
959 | |
960 /* | |
961 * Clear the popup menu. Currently only resets the offset to the first | |
962 * displayed item. | |
963 */ | |
964 void | |
965 pum_clear(void) | |
966 { | |
967 pum_first = 0; | |
968 } | |
969 | |
970 /* | |
971 * Return TRUE if the popup menu is displayed. | |
972 * Overruled when "pum_do_redraw" is set, used to redraw the status lines. | |
973 */ | |
974 int | |
975 pum_visible(void) | |
976 { | |
977 return !pum_do_redraw && pum_array != NULL; | |
978 } | |
979 | |
980 /* | |
981 * Reposition the popup menu to adjust for window layout changes. | |
982 */ | |
983 void | |
984 pum_may_redraw(void) | |
985 { | |
986 pumitem_T *array = pum_array; | |
987 int len = pum_size; | |
988 int selected = pum_selected; | |
989 | |
990 if (!pum_visible() || pum_skip_redraw) | |
991 return; // nothing to do | |
992 | |
993 if (pum_window != curwin | |
994 || (pum_win_row == curwin->w_wrow + W_WINROW(curwin) | |
995 && pum_win_height == curwin->w_height | |
996 && pum_win_col == curwin->w_wincol | |
997 && pum_win_width == curwin->w_width)) | |
998 { | |
999 // window position didn't change, redraw in the same position | |
1000 pum_redraw(); | |
1001 } | |
1002 else | |
1003 { | |
1004 int wcol = curwin->w_wcol; | |
1005 | |
1006 // Window layout changed, recompute the position. | |
1007 // Use the remembered w_wcol value, the cursor may have moved when a | |
1008 // completion was inserted, but we want the menu in the same position. | |
1009 pum_undisplay(); | |
1010 curwin->w_wcol = pum_win_wcol; | |
1011 curwin->w_valid |= VALID_WCOL; | |
1012 pum_display(array, len, selected); | |
1013 curwin->w_wcol = wcol; | |
1014 } | |
1015 } | |
1016 | |
1017 /* | |
1018 * Return the height of the popup menu, the number of entries visible. | |
1019 * Only valid when pum_visible() returns TRUE! | |
1020 */ | |
1021 int | |
1022 pum_get_height(void) | |
1023 { | |
1024 return pum_height; | |
1025 } | |
1026 | |
1027 #if defined(FEAT_EVAL) || defined(PROTO) | |
1028 /* | |
1029 * Add size information about the pum to "dict". | |
1030 */ | |
1031 void | |
1032 pum_set_event_info(dict_T *dict) | |
1033 { | |
1034 if (!pum_visible()) | |
1035 return; | |
1036 dict_add_number(dict, "height", pum_height); | |
1037 dict_add_number(dict, "width", pum_width); | |
1038 dict_add_number(dict, "row", pum_row); | |
1039 dict_add_number(dict, "col", pum_col); | |
1040 dict_add_number(dict, "size", pum_size); | |
1041 dict_add_special(dict, "scrollbar", pum_scrollbar ? VVAL_TRUE : VVAL_FALSE); | |
1042 } | |
1043 #endif | |
1044 | |
1045 #if defined(FEAT_BEVAL_TERM) || defined(FEAT_TERM_POPUP_MENU) || defined(PROTO) | |
1046 static void | |
1047 pum_position_at_mouse(int min_width) | |
1048 { | |
1049 if (Rows - mouse_row > pum_size) | |
1050 { | |
1051 /* Enough space below the mouse row. */ | |
1052 pum_row = mouse_row + 1; | |
1053 if (pum_height > Rows - pum_row) | |
1054 pum_height = Rows - pum_row; | |
1055 } | |
1056 else | |
1057 { | |
1058 /* Show above the mouse row, reduce height if it does not fit. */ | |
1059 pum_row = mouse_row - pum_size; | |
1060 if (pum_row < 0) | |
1061 { | |
1062 pum_height += pum_row; | |
1063 pum_row = 0; | |
1064 } | |
1065 } | |
1066 if (Columns - mouse_col >= pum_base_width | |
1067 || Columns - mouse_col > min_width) | |
1068 /* Enough space to show at mouse column. */ | |
1069 pum_col = mouse_col; | |
1070 else | |
1071 /* Not enough space, right align with window. */ | |
1072 pum_col = Columns - (pum_base_width > min_width | |
1073 ? min_width : pum_base_width); | |
1074 | |
1075 pum_width = Columns - pum_col; | |
1076 if (pum_width > pum_base_width + 1) | |
1077 pum_width = pum_base_width + 1; | |
1078 | |
1079 // Do not redraw at cursor position. | |
1080 pum_window = NULL; | |
1081 } | |
1082 | |
1083 #endif | |
1084 | |
1085 #if defined(FEAT_BEVAL_TERM) || defined(PROTO) | |
1086 static pumitem_T *balloon_array = NULL; | |
1087 static int balloon_arraysize; | |
1088 | |
1089 # define BALLOON_MIN_WIDTH 50 | |
1090 # define BALLOON_MIN_HEIGHT 10 | |
1091 | |
1092 typedef struct { | |
1093 char_u *start; | |
1094 int bytelen; | |
1095 int cells; | |
1096 int indent; | |
1097 } balpart_T; | |
1098 | |
1099 /* | |
1100 * Split a string into parts to display in the balloon. | |
1101 * Aimed at output from gdb. Attempts to split at white space, preserve quoted | |
1102 * strings and make a struct look good. | |
1103 * Resulting array is stored in "array" and returns the size of the array. | |
1104 */ | |
1105 int | |
1106 split_message(char_u *mesg, pumitem_T **array) | |
1107 { | |
1108 garray_T ga; | |
1109 char_u *p; | |
1110 balpart_T *item; | |
1111 int quoted = FALSE; | |
1112 int height; | |
1113 int line; | |
1114 int item_idx; | |
1115 int indent = 0; | |
1116 int max_cells = 0; | |
1117 int max_height = Rows / 2 - 2; | |
1118 int long_item_count = 0; | |
1119 int split_long_items = FALSE; | |
1120 | |
1121 ga_init2(&ga, sizeof(balpart_T), 20); | |
1122 p = mesg; | |
1123 | |
1124 while (*p != NUL) | |
1125 { | |
1126 if (ga_grow(&ga, 1) == FAIL) | |
1127 goto failed; | |
1128 item = ((balpart_T *)ga.ga_data) + ga.ga_len; | |
1129 item->start = p; | |
1130 item->indent = indent; | |
1131 item->cells = indent * 2; | |
1132 ++ga.ga_len; | |
1133 while (*p != NUL) | |
1134 { | |
1135 if (*p == '"') | |
1136 quoted = !quoted; | |
1137 else if (*p == '\\' && p[1] != NUL) | |
1138 ++p; | |
1139 else if (!quoted) | |
1140 { | |
1141 if ((*p == ',' && p[1] == ' ') || *p == '{' || *p == '}') | |
1142 { | |
1143 /* Looks like a good point to break. */ | |
1144 if (*p == '{') | |
1145 ++indent; | |
1146 else if (*p == '}' && indent > 0) | |
1147 --indent; | |
1148 ++item->cells; | |
1149 p = skipwhite(p + 1); | |
1150 break; | |
1151 } | |
1152 } | |
1153 item->cells += ptr2cells(p); | |
1154 p += MB_PTR2LEN(p); | |
1155 } | |
1156 item->bytelen = p - item->start; | |
1157 if (item->cells > max_cells) | |
1158 max_cells = item->cells; | |
1159 long_item_count += (item->cells - 1) / BALLOON_MIN_WIDTH; | |
1160 } | |
1161 | |
1162 height = 2 + ga.ga_len; | |
1163 | |
1164 /* If there are long items and the height is below the limit: split lines */ | |
1165 if (long_item_count > 0 && height + long_item_count <= max_height) | |
1166 { | |
1167 split_long_items = TRUE; | |
1168 height += long_item_count; | |
1169 } | |
1170 | |
1171 /* Limit to half the window height, it has to fit above or below the mouse | |
1172 * position. */ | |
1173 if (height > max_height) | |
1174 height = max_height; | |
1175 *array = ALLOC_CLEAR_MULT(pumitem_T, height); | |
1176 if (*array == NULL) | |
1177 goto failed; | |
1178 | |
1179 /* Add an empty line above and below, looks better. */ | |
1180 (*array)->pum_text = vim_strsave((char_u *)""); | |
1181 (*array + height - 1)->pum_text = vim_strsave((char_u *)""); | |
1182 | |
1183 for (line = 1, item_idx = 0; line < height - 1; ++item_idx) | |
1184 { | |
1185 int skip; | |
1186 int thislen; | |
1187 int copylen; | |
1188 int ind; | |
1189 int cells; | |
1190 | |
1191 item = ((balpart_T *)ga.ga_data) + item_idx; | |
1192 for (skip = 0; skip < item->bytelen; skip += thislen) | |
1193 { | |
1194 if (split_long_items && item->cells >= BALLOON_MIN_WIDTH) | |
1195 { | |
1196 cells = item->indent * 2; | |
1197 for (p = item->start + skip; p < item->start + item->bytelen; | |
1198 p += MB_PTR2LEN(p)) | |
1199 if ((cells += ptr2cells(p)) > BALLOON_MIN_WIDTH) | |
1200 break; | |
1201 thislen = p - (item->start + skip); | |
1202 } | |
1203 else | |
1204 thislen = item->bytelen; | |
1205 | |
1206 // put indent at the start | |
1207 p = alloc(thislen + item->indent * 2 + 1); | |
1208 if (p == NULL) | |
1209 { | |
1210 for (line = 0; line <= height - 1; ++line) | |
1211 vim_free((*array)[line].pum_text); | |
1212 vim_free(*array); | |
1213 goto failed; | |
1214 } | |
1215 for (ind = 0; ind < item->indent * 2; ++ind) | |
1216 p[ind] = ' '; | |
1217 | |
1218 // exclude spaces at the end of the string | |
1219 for (copylen = thislen; copylen > 0; --copylen) | |
1220 if (item->start[skip + copylen - 1] != ' ') | |
1221 break; | |
1222 | |
1223 vim_strncpy(p + ind, item->start + skip, copylen); | |
1224 (*array)[line].pum_text = p; | |
1225 item->indent = 0; /* wrapped line has no indent */ | |
1226 ++line; | |
1227 } | |
1228 } | |
1229 ga_clear(&ga); | |
1230 return height; | |
1231 | |
1232 failed: | |
1233 ga_clear(&ga); | |
1234 return 0; | |
1235 } | |
1236 | |
1237 void | |
1238 ui_remove_balloon(void) | |
1239 { | |
1240 if (balloon_array != NULL) | |
1241 { | |
1242 pum_undisplay(); | |
1243 while (balloon_arraysize > 0) | |
1244 vim_free(balloon_array[--balloon_arraysize].pum_text); | |
1245 VIM_CLEAR(balloon_array); | |
1246 } | |
1247 } | |
1248 | |
1249 /* | |
1250 * Terminal version of a balloon, uses the popup menu code. | |
1251 */ | |
1252 void | |
1253 ui_post_balloon(char_u *mesg, list_T *list) | |
1254 { | |
1255 ui_remove_balloon(); | |
1256 | |
1257 if (mesg == NULL && list == NULL) | |
1258 { | |
1259 pum_undisplay(); | |
1260 return; | |
1261 } | |
1262 if (list != NULL) | |
1263 { | |
1264 listitem_T *li; | |
1265 int idx; | |
1266 | |
1267 balloon_arraysize = list->lv_len; | |
1268 balloon_array = ALLOC_CLEAR_MULT(pumitem_T, list->lv_len); | |
1269 if (balloon_array == NULL) | |
1270 return; | |
1271 for (idx = 0, li = list->lv_first; li != NULL; li = li->li_next, ++idx) | |
1272 { | |
1273 char_u *text = tv_get_string_chk(&li->li_tv); | |
1274 | |
1275 balloon_array[idx].pum_text = vim_strsave( | |
1276 text == NULL ? (char_u *)"" : text); | |
1277 } | |
1278 } | |
1279 else | |
1280 balloon_arraysize = split_message(mesg, &balloon_array); | |
1281 | |
1282 if (balloon_arraysize > 0) | |
1283 { | |
1284 pum_array = balloon_array; | |
1285 pum_size = balloon_arraysize; | |
1286 pum_compute_size(); | |
1287 pum_scrollbar = 0; | |
1288 pum_height = balloon_arraysize; | |
1289 | |
1290 pum_position_at_mouse(BALLOON_MIN_WIDTH); | |
1291 pum_selected = -1; | |
1292 pum_first = 0; | |
1293 pum_redraw(); | |
1294 } | |
1295 } | |
1296 | |
1297 /* | |
1298 * Called when the mouse moved, may remove any displayed balloon. | |
1299 */ | |
1300 void | |
1301 ui_may_remove_balloon(void) | |
1302 { | |
1303 // For now: remove the balloon whenever the mouse moves to another screen | |
1304 // cell. | |
1305 ui_remove_balloon(); | |
1306 } | |
1307 #endif | |
1308 | |
1309 #if defined(FEAT_TERM_POPUP_MENU) || defined(PROTO) | |
1310 /* | |
1311 * Select the pum entry at the mouse position. | |
1312 */ | |
1313 static void | |
1314 pum_select_mouse_pos(void) | |
1315 { | |
1316 int idx = mouse_row - pum_row; | |
1317 | |
1318 if (idx < 0 || idx >= pum_size) | |
1319 pum_selected = -1; | |
1320 else if (*pum_array[idx].pum_text != NUL) | |
1321 pum_selected = idx; | |
1322 } | |
1323 | |
1324 /* | |
1325 * Execute the currently selected popup menu item. | |
1326 */ | |
1327 static void | |
1328 pum_execute_menu(vimmenu_T *menu, int mode) | |
1329 { | |
1330 vimmenu_T *mp; | |
1331 int idx = 0; | |
1332 exarg_T ea; | |
1333 | |
1334 for (mp = menu->children; mp != NULL; mp = mp->next) | |
1335 if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) | |
1336 { | |
1337 vim_memset(&ea, 0, sizeof(ea)); | |
1338 execute_menu(&ea, mp, -1); | |
1339 break; | |
1340 } | |
1341 } | |
1342 | |
1343 /* | |
1344 * Open the terminal version of the popup menu and don't return until it is | |
1345 * closed. | |
1346 */ | |
1347 void | |
1348 pum_show_popupmenu(vimmenu_T *menu) | |
1349 { | |
1350 vimmenu_T *mp; | |
1351 int idx = 0; | |
1352 pumitem_T *array; | |
1353 # ifdef FEAT_BEVAL_TERM | |
1354 int save_bevalterm = p_bevalterm; | |
1355 # endif | |
1356 int mode; | |
1357 | |
1358 pum_undisplay(); | |
1359 pum_size = 0; | |
1360 mode = get_menu_mode_flag(); | |
1361 | |
1362 for (mp = menu->children; mp != NULL; mp = mp->next) | |
1363 if (menu_is_separator(mp->dname) | |
1364 || (mp->modes & mp->enabled & mode)) | |
1365 ++pum_size; | |
1366 | |
1367 // When there are only Terminal mode menus, using "popup Edit" results in | |
1368 // pum_size being zero. | |
1369 if (pum_size <= 0) | |
1370 { | |
1371 emsg(e_menuothermode); | |
1372 return; | |
1373 } | |
1374 | |
1375 array = ALLOC_CLEAR_MULT(pumitem_T, pum_size); | |
1376 if (array == NULL) | |
1377 return; | |
1378 | |
1379 for (mp = menu->children; mp != NULL; mp = mp->next) | |
1380 if (menu_is_separator(mp->dname)) | |
1381 array[idx++].pum_text = (char_u *)""; | |
1382 else if (mp->modes & mp->enabled & mode) | |
1383 array[idx++].pum_text = mp->dname; | |
1384 | |
1385 pum_array = array; | |
1386 pum_compute_size(); | |
1387 pum_scrollbar = 0; | |
1388 pum_height = pum_size; | |
1389 pum_position_at_mouse(20); | |
1390 | |
1391 pum_selected = -1; | |
1392 pum_first = 0; | |
1393 # ifdef FEAT_BEVAL_TERM | |
1394 p_bevalterm = TRUE; /* track mouse movement */ | |
1395 mch_setmouse(TRUE); | |
1396 # endif | |
1397 | |
1398 for (;;) | |
1399 { | |
1400 int c; | |
1401 | |
1402 pum_redraw(); | |
1403 setcursor_mayforce(TRUE); | |
1404 out_flush(); | |
1405 | |
1406 c = vgetc(); | |
1407 | |
1408 // Bail out when typing Esc, CTRL-C or some callback closed the popup | |
1409 // menu. | |
1410 if (c == ESC || c == Ctrl_C || pum_array == NULL) | |
1411 break; | |
1412 else if (c == CAR || c == NL) | |
1413 { | |
1414 /* enter: select current item, if any, and close */ | |
1415 pum_execute_menu(menu, mode); | |
1416 break; | |
1417 } | |
1418 else if (c == 'k' || c == K_UP || c == K_MOUSEUP) | |
1419 { | |
1420 /* cursor up: select previous item */ | |
1421 while (pum_selected > 0) | |
1422 { | |
1423 --pum_selected; | |
1424 if (*array[pum_selected].pum_text != NUL) | |
1425 break; | |
1426 } | |
1427 } | |
1428 else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN) | |
1429 { | |
1430 /* cursor down: select next item */ | |
1431 while (pum_selected < pum_size - 1) | |
1432 { | |
1433 ++pum_selected; | |
1434 if (*array[pum_selected].pum_text != NUL) | |
1435 break; | |
1436 } | |
1437 } | |
1438 else if (c == K_RIGHTMOUSE) | |
1439 { | |
1440 /* Right mouse down: reposition the menu. */ | |
1441 vungetc(c); | |
1442 break; | |
1443 } | |
1444 else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE) | |
1445 { | |
1446 /* mouse moved: select item in the mouse row */ | |
1447 pum_select_mouse_pos(); | |
1448 } | |
1449 else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE) | |
1450 { | |
1451 /* left mouse click: select clicked item, if any, and close; | |
1452 * right mouse release: select clicked item, close if any */ | |
1453 pum_select_mouse_pos(); | |
1454 if (pum_selected >= 0) | |
1455 { | |
1456 pum_execute_menu(menu, mode); | |
1457 break; | |
1458 } | |
1459 if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) | |
1460 break; | |
1461 } | |
1462 } | |
1463 | |
1464 vim_free(array); | |
1465 pum_undisplay(); | |
1466 # ifdef FEAT_BEVAL_TERM | |
1467 p_bevalterm = save_bevalterm; | |
1468 mch_setmouse(TRUE); | |
1469 # endif | |
1470 } | |
1471 | |
1472 void | |
1473 pum_make_popup(char_u *path_name, int use_mouse_pos) | |
1474 { | |
1475 vimmenu_T *menu; | |
1476 | |
1477 if (!use_mouse_pos) | |
1478 { | |
1479 /* Hack: set mouse position at the cursor so that the menu pops up | |
1480 * around there. */ | |
1481 mouse_row = curwin->w_winrow + curwin->w_wrow; | |
1482 mouse_col = curwin->w_wincol + curwin->w_wcol; | |
1483 } | |
1484 | |
1485 menu = gui_find_menu(path_name); | |
1486 if (menu != NULL) | |
1487 pum_show_popupmenu(menu); | |
1488 } | |
1489 #endif |