Mercurial > vim
comparison src/popupmnu.c @ 800:d8f905020502 v7.0b
updated for version 7.0b
author | vimboss |
---|---|
date | Fri, 24 Mar 2006 22:46:53 +0000 |
parents | |
children | 4a79d6d376f0 |
comparison
equal
deleted
inserted
replaced
799:6beb2c667935 | 800:d8f905020502 |
---|---|
1 /* vi:set ts=8 sts=4 sw=4: | |
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 * popupmnu.c: Popup menu (PUM) | |
12 */ | |
13 #include "vim.h" | |
14 | |
15 #if defined(FEAT_INS_EXPAND) || defined(PROTO) | |
16 | |
17 static pumitem_T *pum_array = NULL; /* items of displayed pum */ | |
18 static int pum_size; /* nr of items in "pum_array" */ | |
19 static int pum_selected; /* index of selected item or -1 */ | |
20 static int pum_first = 0; /* index of top item */ | |
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_scrollbar; /* TRUE when scrollbar present */ | |
27 | |
28 static int pum_row; /* top row of pum */ | |
29 static int pum_col; /* left column of pum */ | |
30 | |
31 static int pum_do_redraw = FALSE; /* do redraw anyway */ | |
32 | |
33 static int pum_set_selected __ARGS((int n)); | |
34 | |
35 #define PUM_DEF_HEIGHT 10 | |
36 #define PUM_DEF_WIDTH 15 | |
37 | |
38 /* | |
39 * Show the popup menu with items "array[size]". | |
40 * "array" must remain valid until pum_undisplay() is called! | |
41 * When possible the leftmost character is aligned with screen column "col". | |
42 * The menu appears above the screen line "row" or at "row" + "height" - 1. | |
43 */ | |
44 void | |
45 pum_display(array, size, selected) | |
46 pumitem_T *array; | |
47 int size; | |
48 int selected; /* index of initially selected item, none if | |
49 out of range */ | |
50 { | |
51 int w; | |
52 int def_width; | |
53 int max_width; | |
54 int kind_width; | |
55 int extra_width; | |
56 int i; | |
57 int top_clear; | |
58 int row; | |
59 int height; | |
60 int col; | |
61 int above_row = cmdline_row; | |
62 | |
63 redo: | |
64 def_width = PUM_DEF_WIDTH; | |
65 max_width = 0; | |
66 kind_width = 0; | |
67 extra_width = 0; | |
68 | |
69 /* Pretend the pum is already there to avoid that must_redraw is set when | |
70 * 'cuc' is on. */ | |
71 pum_array = (pumitem_T *)1; | |
72 validate_cursor_col(); | |
73 pum_array = NULL; | |
74 | |
75 row = curwin->w_cline_row + W_WINROW(curwin); | |
76 height = curwin->w_cline_height; | |
77 col = curwin->w_wcol + W_WINCOL(curwin) - curwin->w_leftcol; | |
78 | |
79 if (firstwin->w_p_pvw) | |
80 top_clear = firstwin->w_height; | |
81 else | |
82 top_clear = 0; | |
83 | |
84 /* When the preview window is at the bottom stop just above it. Also | |
85 * avoid drawing over the status line so that it's clear there is a window | |
86 * boundary. */ | |
87 if (lastwin->w_p_pvw) | |
88 above_row -= lastwin->w_height + lastwin->w_status_height + 1; | |
89 | |
90 /* | |
91 * Figure out the size and position of the pum. | |
92 */ | |
93 if (size < PUM_DEF_HEIGHT) | |
94 pum_height = size; | |
95 else | |
96 pum_height = PUM_DEF_HEIGHT; | |
97 if (p_ph > 0 && pum_height > p_ph) | |
98 pum_height = p_ph; | |
99 | |
100 /* Put the pum below "row" if possible. If there are few lines decide on | |
101 * where there is more room. */ | |
102 if (row >= above_row - pum_height | |
103 && row > (above_row - top_clear - height) / 2) | |
104 { | |
105 /* pum above "row" */ | |
106 if (row >= size) | |
107 { | |
108 pum_row = row - size; | |
109 pum_height = size; | |
110 } | |
111 else | |
112 { | |
113 pum_row = 0; | |
114 pum_height = row; | |
115 } | |
116 if (p_ph > 0 && pum_height > p_ph) | |
117 { | |
118 pum_row += pum_height - p_ph; | |
119 pum_height = p_ph; | |
120 } | |
121 } | |
122 else | |
123 { | |
124 /* pum below "row" */ | |
125 pum_row = row + height; | |
126 if (size > above_row - pum_row) | |
127 pum_height = above_row - pum_row; | |
128 else | |
129 pum_height = size; | |
130 if (p_ph > 0 && pum_height > p_ph) | |
131 pum_height = p_ph; | |
132 } | |
133 | |
134 /* don't display when we only have room for one line */ | |
135 if (pum_height < 1 || (pum_height == 1 && size > 1)) | |
136 return; | |
137 | |
138 /* If there is a preview window at the top avoid drawing over it. */ | |
139 if (firstwin->w_p_pvw | |
140 && pum_row < firstwin->w_height | |
141 && pum_height > firstwin->w_height + 4) | |
142 { | |
143 pum_row += firstwin->w_height; | |
144 pum_height -= firstwin->w_height; | |
145 } | |
146 | |
147 /* Compute the width of the widest match and the widest extra. */ | |
148 for (i = 0; i < size; ++i) | |
149 { | |
150 w = vim_strsize(array[i].pum_text); | |
151 if (max_width < w) | |
152 max_width = w; | |
153 if (array[i].pum_kind != NULL) | |
154 { | |
155 w = vim_strsize(array[i].pum_kind) + 1; | |
156 if (kind_width < w) | |
157 kind_width = w; | |
158 } | |
159 if (array[i].pum_extra != NULL) | |
160 { | |
161 w = vim_strsize(array[i].pum_extra) + 1; | |
162 if (extra_width < w) | |
163 extra_width = w; | |
164 } | |
165 } | |
166 pum_base_width = max_width; | |
167 pum_kind_width = kind_width; | |
168 | |
169 /* if there are more items than room we need a scrollbar */ | |
170 if (pum_height < size) | |
171 { | |
172 pum_scrollbar = 1; | |
173 ++max_width; | |
174 } | |
175 else | |
176 pum_scrollbar = 0; | |
177 | |
178 if (def_width < max_width) | |
179 def_width = max_width; | |
180 | |
181 if (col < Columns - PUM_DEF_WIDTH || col < Columns - max_width) | |
182 { | |
183 /* align pum column with "col" */ | |
184 pum_col = col; | |
185 pum_width = Columns - pum_col - pum_scrollbar; | |
186 if (pum_width > max_width + kind_width + extra_width + 1 | |
187 && pum_width > PUM_DEF_WIDTH) | |
188 { | |
189 pum_width = max_width + kind_width + extra_width + 1; | |
190 if (pum_width < PUM_DEF_WIDTH) | |
191 pum_width = PUM_DEF_WIDTH; | |
192 } | |
193 } | |
194 else if (Columns < def_width) | |
195 { | |
196 /* not enough room, will use what we have */ | |
197 pum_col = 0; | |
198 pum_width = Columns - 1; | |
199 } | |
200 else | |
201 { | |
202 if (max_width > PUM_DEF_WIDTH) | |
203 max_width = PUM_DEF_WIDTH; /* truncate */ | |
204 pum_col = Columns - max_width; | |
205 pum_width = max_width - pum_scrollbar; | |
206 } | |
207 | |
208 pum_array = array; | |
209 pum_size = size; | |
210 | |
211 /* Set selected item and redraw. If the window size changed need to redo | |
212 * the positioning. */ | |
213 if (pum_set_selected(selected)) | |
214 goto redo; | |
215 } | |
216 | |
217 /* | |
218 * Redraw the popup menu, using "pum_first" and "pum_selected". | |
219 */ | |
220 void | |
221 pum_redraw() | |
222 { | |
223 int row = pum_row; | |
224 int col; | |
225 int attr_norm = highlight_attr[HLF_PNI]; | |
226 int attr_select = highlight_attr[HLF_PSI]; | |
227 int attr_scroll = highlight_attr[HLF_PSB]; | |
228 int attr_thumb = highlight_attr[HLF_PST]; | |
229 int attr; | |
230 int i; | |
231 int idx; | |
232 char_u *s; | |
233 char_u *p = NULL; | |
234 int totwidth, width, w; | |
235 int thumb_pos = 0; | |
236 int thumb_heigth = 1; | |
237 int round; | |
238 int n; | |
239 | |
240 if (pum_scrollbar) | |
241 { | |
242 thumb_heigth = pum_height * pum_height / pum_size; | |
243 if (thumb_heigth == 0) | |
244 thumb_heigth = 1; | |
245 thumb_pos = (pum_first * (pum_height - thumb_heigth) | |
246 + (pum_size - pum_height) / 2) | |
247 / (pum_size - pum_height); | |
248 } | |
249 | |
250 for (i = 0; i < pum_height; ++i) | |
251 { | |
252 idx = i + pum_first; | |
253 attr = (idx == pum_selected) ? attr_select : attr_norm; | |
254 | |
255 /* prepend a space if there is room */ | |
256 if (pum_col > 0) | |
257 screen_putchar(' ', row, pum_col - 1, attr); | |
258 | |
259 /* Display each entry, use two spaces for a Tab. | |
260 * Do this 3 times: For the main text, kind and extra info */ | |
261 col = pum_col; | |
262 totwidth = 0; | |
263 for (round = 1; round <= 3; ++round) | |
264 { | |
265 width = 0; | |
266 s = NULL; | |
267 switch (round) | |
268 { | |
269 case 1: p = pum_array[idx].pum_text; break; | |
270 case 2: p = pum_array[idx].pum_kind; break; | |
271 case 3: p = pum_array[idx].pum_extra; break; | |
272 } | |
273 if (p != NULL) | |
274 for ( ; ; mb_ptr_adv(p)) | |
275 { | |
276 if (s == NULL) | |
277 s = p; | |
278 w = ptr2cells(p); | |
279 if (*p == NUL || *p == TAB || totwidth + w > pum_width) | |
280 { | |
281 /* Display the text that fits or comes before a Tab. */ | |
282 screen_puts_len(s, p - s, row, col, attr); | |
283 col += width; | |
284 | |
285 if (*p != TAB) | |
286 break; | |
287 | |
288 /* Display two spaces for a Tab. */ | |
289 screen_puts_len((char_u *)" ", 2, row, col, attr); | |
290 col += 2; | |
291 totwidth += 2; | |
292 s = NULL; /* start text at next char */ | |
293 width = 0; | |
294 } | |
295 else | |
296 width += w; | |
297 } | |
298 | |
299 if (round > 1) | |
300 n = pum_kind_width + 1; | |
301 else | |
302 n = 1; | |
303 | |
304 /* Stop when there is nothing more to display. */ | |
305 if (round == 3 | |
306 || (round == 2 && pum_array[idx].pum_extra == NULL) | |
307 || (round == 1 && pum_array[idx].pum_kind == NULL | |
308 && pum_array[idx].pum_extra == NULL) | |
309 || pum_base_width + n >= pum_width) | |
310 break; | |
311 screen_fill(row, row + 1, col, pum_col + pum_base_width + n, | |
312 ' ', ' ', attr); | |
313 col = pum_col + pum_base_width + n; | |
314 totwidth = pum_base_width + n; | |
315 } | |
316 | |
317 screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ', attr); | |
318 if (pum_scrollbar > 0) | |
319 screen_putchar(' ', row, pum_col + pum_width, | |
320 i >= thumb_pos && i < thumb_pos + thumb_heigth | |
321 ? attr_thumb : attr_scroll); | |
322 | |
323 ++row; | |
324 } | |
325 } | |
326 | |
327 #if 0 /* not used yet */ | |
328 /* | |
329 * Return the index of the currently selected item. | |
330 */ | |
331 int | |
332 pum_get_selected() | |
333 { | |
334 return pum_selected; | |
335 } | |
336 #endif | |
337 | |
338 /* | |
339 * Set the index of the currently selected item. The menu will scroll when | |
340 * necessary. When "n" is out of range don't scroll. | |
341 * Returns TRUE when the window was resized and the location of the popup menu | |
342 * must be recomputed. | |
343 */ | |
344 static int | |
345 pum_set_selected(n) | |
346 int n; | |
347 { | |
348 int resized = FALSE; | |
349 int context = pum_height / 2; | |
350 | |
351 pum_selected = n; | |
352 | |
353 if (pum_selected >= 0 && pum_selected < pum_size) | |
354 { | |
355 if (pum_first > pum_selected - 4) | |
356 { | |
357 /* scroll down; when we did a jump it's probably a PageUp then | |
358 * scroll a whole page */ | |
359 if (pum_first > pum_selected - 2) | |
360 { | |
361 pum_first -= pum_height - 2; | |
362 if (pum_first < 0) | |
363 pum_first = 0; | |
364 else if (pum_first > pum_selected) | |
365 pum_first = pum_selected; | |
366 } | |
367 else | |
368 pum_first = pum_selected; | |
369 } | |
370 else if (pum_first < pum_selected - pum_height + 5) | |
371 { | |
372 /* scroll up; when we did a jump it's probably a PageDown then | |
373 * scroll a whole page */ | |
374 if (pum_first < pum_selected - pum_height + 1 + 2) | |
375 { | |
376 pum_first += pum_height - 2; | |
377 if (pum_first < pum_selected - pum_height + 1) | |
378 pum_first = pum_selected - pum_height + 1; | |
379 } | |
380 else | |
381 pum_first = pum_selected - pum_height + 1; | |
382 } | |
383 | |
384 /* Give a few lines of context when possible. */ | |
385 if (context > 3) | |
386 context = 3; | |
387 if (pum_height > 2) | |
388 { | |
389 if (pum_first > pum_selected - context) | |
390 { | |
391 /* scroll down */ | |
392 pum_first = pum_selected - context; | |
393 if (pum_first < 0) | |
394 pum_first = 0; | |
395 } | |
396 else if (pum_first < pum_selected + context - pum_height + 1) | |
397 { | |
398 /* scroll up */ | |
399 pum_first = pum_selected + context - pum_height + 1; | |
400 } | |
401 } | |
402 | |
403 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) | |
404 /* Show extra info in the preview window if there is something and | |
405 * 'completeopt' contains "preview". */ | |
406 if (pum_array[pum_selected].pum_info != NULL | |
407 && vim_strchr(p_cot, 'p') != NULL) | |
408 { | |
409 win_T *curwin_save = curwin; | |
410 int res = OK; | |
411 | |
412 /* Open a preview window. 3 lines by default. */ | |
413 g_do_tagpreview = 3; | |
414 resized = prepare_tagpreview(); | |
415 g_do_tagpreview = 0; | |
416 | |
417 if (curwin->w_p_pvw) | |
418 { | |
419 if (curbuf->b_fname == NULL | |
420 && curbuf->b_p_bt[0] == 'n' && curbuf->b_p_bt[2] == 'f' | |
421 && curbuf->b_p_bh[0] == 'w') | |
422 { | |
423 /* Already a "wipeout" buffer, make it empty. */ | |
424 while (!bufempty()) | |
425 ml_delete((linenr_T)1, FALSE); | |
426 } | |
427 else if ((res = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 0)) | |
428 == OK) | |
429 { | |
430 /* Edit a new, empty buffer. Set options for a "wipeout" | |
431 * buffer. */ | |
432 set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); | |
433 set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", | |
434 OPT_LOCAL); | |
435 set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", | |
436 OPT_LOCAL); | |
437 set_option_value((char_u *)"diff", 0L, (char_u *)"", | |
438 OPT_LOCAL); | |
439 } | |
440 if (res == OK) | |
441 { | |
442 char_u *p, *e; | |
443 linenr_T lnum = 0; | |
444 | |
445 for (p = pum_array[pum_selected].pum_info; *p != NUL; ) | |
446 { | |
447 e = vim_strchr(p, '\n'); | |
448 if (e == NULL) | |
449 { | |
450 ml_append(lnum++, p, 0, FALSE); | |
451 break; | |
452 } | |
453 else | |
454 { | |
455 *e = NUL; | |
456 ml_append(lnum++, p, e - p + 1, FALSE); | |
457 *e = '\n'; | |
458 p = e + 1; | |
459 } | |
460 } | |
461 | |
462 /* Increase the height of the preview window to show the | |
463 * text, but no more than 'previewheight' lines. */ | |
464 if (lnum > p_pvh) | |
465 lnum = p_pvh; | |
466 if (curwin->w_height < lnum) | |
467 { | |
468 win_setheight((int)lnum); | |
469 resized = TRUE; | |
470 } | |
471 | |
472 curbuf->b_changed = 0; | |
473 curbuf->b_p_ma = FALSE; | |
474 curwin->w_cursor.lnum = 0; | |
475 curwin->w_cursor.col = 0; | |
476 | |
477 if (curwin != curwin_save && win_valid(curwin_save)) | |
478 { | |
479 /* Return cursor to where we were */ | |
480 validate_cursor(); | |
481 redraw_later(SOME_VALID); | |
482 | |
483 /* When the preview window was resized we need to | |
484 * update the view on the buffer. Only go back to | |
485 * the window when needed, otherwise it will always be | |
486 * redraw. */ | |
487 if (resized) | |
488 { | |
489 win_enter(curwin_save, TRUE); | |
490 update_topline(); | |
491 } | |
492 | |
493 /* Update the screen before drawing the popup menu. | |
494 * Enable updating the status lines. */ | |
495 pum_do_redraw = TRUE; | |
496 update_screen(0); | |
497 pum_do_redraw = FALSE; | |
498 | |
499 if (win_valid(curwin_save)) | |
500 win_enter(curwin_save, TRUE); | |
501 | |
502 /* May need to update the screen again when there are | |
503 * autocommands involved. */ | |
504 pum_do_redraw = TRUE; | |
505 update_screen(0); | |
506 pum_do_redraw = FALSE; | |
507 } | |
508 } | |
509 } | |
510 } | |
511 #endif | |
512 } | |
513 | |
514 /* Never display more than we have */ | |
515 if (pum_first > pum_size - pum_height) | |
516 pum_first = pum_size - pum_height; | |
517 | |
518 if (!resized) | |
519 pum_redraw(); | |
520 | |
521 return resized; | |
522 } | |
523 | |
524 /* | |
525 * Undisplay the popup menu (later). | |
526 */ | |
527 void | |
528 pum_undisplay() | |
529 { | |
530 pum_array = NULL; | |
531 redraw_all_later(SOME_VALID); | |
532 status_redraw_all(); | |
533 } | |
534 | |
535 /* | |
536 * Clear the popup menu. Currently only resets the offset to the first | |
537 * displayed item. | |
538 */ | |
539 void | |
540 pum_clear() | |
541 { | |
542 pum_first = 0; | |
543 } | |
544 | |
545 /* | |
546 * Return TRUE if the popup menu is displayed. | |
547 * Overruled when "pum_do_redraw" is set, used to redraw the status lines. | |
548 */ | |
549 int | |
550 pum_visible() | |
551 { | |
552 return !pum_do_redraw && pum_array != NULL; | |
553 } | |
554 | |
555 /* | |
556 * Return the height of the popup menu, the number of entries visible. | |
557 * Only valid when pum_visible() returns TRUE! | |
558 */ | |
559 int | |
560 pum_get_height() | |
561 { | |
562 return pum_height; | |
563 } | |
564 | |
565 #endif |