Annotation of src/usr.bin/vim/gui.c, Revision 1.3
1.3 ! downsj 1: /* $OpenBSD: gui.c,v 1.2 1996/09/21 06:23:01 downsj Exp $ */
1.1 downsj 2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: * GUI/Motif support by Robert Webb
6: *
7: * Do ":help uganda" in Vim to read copying and usage conditions.
8: * Do ":help credits" in Vim to see a list of people who contributed.
9: */
10:
11: #include "vim.h"
12: #include "globals.h"
13: #include "proto.h"
14: #include "option.h"
15:
16: /* Structure containing all the GUI information */
17: Gui gui;
18:
19: /* Set to TRUE after adding/removing menus to ensure they are updated */
20: int force_menu_update = FALSE;
21:
22:
23: static void gui_check_screen __ARGS((void));
24: static void gui_outstr __ARGS((char_u *, int));
25: static void gui_update_selection __ARGS((void));
26: static int gui_get_menu_cmd_modes __ARGS((char_u *, int, int *, int *));
27: static int gui_add_menu_path __ARGS((char_u *, int, void (*)(), char_u *, int));
28: static int gui_remove_menu __ARGS((GuiMenu **, char_u *, int));
29: static void gui_free_menu __ARGS((GuiMenu *));
30: static void gui_free_menu_string __ARGS((GuiMenu *, int));
31: static int gui_show_menus __ARGS((char_u *, int));
32: static void gui_show_menus_recursive __ARGS((GuiMenu *, int, int));
33: static char_u *gui_menu_name_skip __ARGS((char_u *name));
34: static void gui_create_initial_menus __ARGS((GuiMenu *, GuiMenu *));
35: static void gui_update_scrollbars __ARGS((void));
36: static void gui_update_horiz_scrollbar __ARGS((void));
37:
38: /*
39: * The Athena scrollbars can move the thumb to after the end of the scrollbar,
40: * this makes the thumb indicate the part of the text that is shown. Motif
41: * can't do this.
42: */
43: #ifdef USE_GUI_ATHENA
44: # define SCROLL_PAST_END
45: #endif
46:
47: /*
48: * gui_start -- Called when user wants to start the GUI.
49: */
50: void
51: gui_start()
52: {
53: char_u *old_term;
1.2 downsj 54: int pid;
1.1 downsj 55:
56: old_term = strsave(term_strings[KS_NAME]);
57: mch_setmouse(FALSE); /* first switch mouse off */
58:
1.2 downsj 59: /*
60: * Set_termname() will call gui_init() to start the GUI.
61: * Set the "starting" flag, to indicate that the GUI will start.
62: */
63: gui.starting = TRUE;
1.1 downsj 64: termcapinit((char_u *)"builtin_gui");
1.2 downsj 65: gui.starting = FALSE;
1.1 downsj 66:
67: if (!gui.in_use) /* failed to start GUI */
68: termcapinit(old_term);
69:
70: vim_free(old_term);
71:
72: /*
73: * Quit the current process and continue in the child.
74: * Makes "gvim file" disconnect from the shell it was started in.
75: * Don't do this when Vim was started with "-f" or the 'f' flag is present
76: * in 'guioptions'.
77: */
1.2 downsj 78: if (gui.in_use && gui.dofork && vim_strchr(p_guioptions, GO_FORG) == NULL)
79: {
80: pid = fork();
81: if (pid > 0) /* parent */
82: exit(0);
83: #if defined(HAVE_SETSID) || defined(HAVE_SETPGID)
84: /*
85: * Change our process group. On some systems/shells a CTRL-C in the
86: * shell where Vim was started would otherwise kill gvim!
87: */
88: if (pid == 0) /* child */
89: # if defined(HAVE_SETSID)
90: (void)setsid();
91: # else
92: (void)setpgid(0, 0);
93: # endif
94: #endif
95: }
1.1 downsj 96: }
97:
98: /*
99: * Call this when vim starts up, whether or not the GUI is started
100: */
101: void
102: gui_prepare(argc, argv)
103: int *argc;
104: char **argv;
105: {
106: /* Menu items may be added before the GUI is started, so set this now */
107: gui.root_menu = NULL;
108: gui.in_use = FALSE; /* No GUI yet (maybe later) */
109: gui.starting = FALSE; /* No GUI yet (maybe later) */
110: gui.dofork = TRUE; /* default is to use fork() */
111: gui_mch_prepare(argc, argv);
112: }
113:
114: static struct default_menu
115: {
116: char *name; /* name of menu item */
117: int mode; /* mode where menu is valid */
118: char *command; /* resulting command */
119: } default_menus[] =
120: {
121: /* Help menu. Some reason Motif is happier if this is added first. */
122: {"Help.Overview <F1>", MENU_NORMAL_MODE, ":help\r"},
123: {"Help.Overview <F1>", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
124: "\033:help\r"},
125: {"Help.How to\\.\\.\\.",MENU_NORMAL_MODE, ":help how_to\r"},
126: {"Help.How to\\.\\.\\.",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
127: "\033:help how_to\r"},
128: {"Help.GUI", MENU_NORMAL_MODE, ":help gui\r"},
129: {"Help.GUI", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
130: "\033:help gui\r"},
131: {"Help.Version", MENU_NORMAL_MODE, ":version\r"},
132: {"Help.Version", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
133: "\033:version\r"},
134: {"Help.Credits", MENU_NORMAL_MODE, ":help credits\r"},
135: {"Help.Credits", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
136: "\033:help credits\r"},
137: {"Help.Copying", MENU_NORMAL_MODE, ":help uganda\r"},
138: {"Help.Copying", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
139: "\033:help uganda\r"},
140:
141: /* File menu */
142: {"File.Save :w", MENU_NORMAL_MODE, ":w\r"},
143: {"File.Save :w", MENU_INSERT_MODE, "\017:w\r"},
144:
145: {"File.Close :q", MENU_NORMAL_MODE, ":q\r"},
146: {"File.Close :q", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
147: "\033:q\r"},
148:
149: {"File.Quit :qa", MENU_NORMAL_MODE, ":qa\r"},
150: {"File.Quit :qa", MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
151: "\033:qa\r"},
152:
153: {"File.Save-Quit :wqa",MENU_NORMAL_MODE, ":wqa\r"},
154: {"File.Save-Quit :wqa",MENU_VISUAL_MODE|MENU_INSERT_MODE|MENU_CMDLINE_MODE,
155: "\033:wqa\r"},
156:
157: /* Edit menu */
158: {"Edit.Undo", MENU_NORMAL_MODE, "u"},
159: {"Edit.Redo", MENU_NORMAL_MODE, "\022"},
160:
161: {"Edit.Cut", MENU_VISUAL_MODE, "x"},
162: {"Edit.Copy", MENU_VISUAL_MODE, "y"},
163: {"Edit.Put Before", MENU_NORMAL_MODE, "[p"},
164: {"Edit.Put Before", MENU_INSERT_MODE, "\017[p"},
165: {"Edit.Put After", MENU_NORMAL_MODE, "]p"},
166: {"Edit.Put After", MENU_INSERT_MODE, "\017]p"},
167: {"Edit.Paste", MENU_NORMAL_MODE, "i\022*\033"}, /* CTRL-R * */
168: {"Edit.Paste", MENU_INSERT_MODE|MENU_CMDLINE_MODE,
169: "\022*"}, /* CTRL-R * */
170: {NULL, 0, NULL}
171: };
172:
173: /*
174: * This is the call which starts the GUI.
175: */
176: void
177: gui_init()
178: {
179: char_u *env_str;
180: int i;
181:
182: gui.dying = FALSE;
183: gui.in_focus = FALSE;
184: gui.dragged_sb = SB_NONE;
185: gui.dragged_wp = NULL;
186: gui.col = gui.num_cols = 0;
187: gui.row = gui.num_rows = 0;
188:
189: /* Initialise gui.cursor_row: */
190: INVALIDATE_CURSOR();
191: gui.scroll_region_top = 0;
192: gui.scroll_region_bot = Rows - 1;
193: gui.highlight_mask = HL_NORMAL;
194: gui.char_width = 0;
195: gui.char_height = 0;
196: gui.char_ascent = 0;
197: gui.border_width = 0;
198:
199: gui.selection.owned = FALSE;
200: gui.selection.start.lnum = 0;
201: gui.selection.start.col = 0;
202: gui.selection.end.lnum = 0;
203: gui.selection.end.col = 0;
204: gui.selection.state = SELECT_CLEARED;
205:
206: gui.root_menu = NULL;
207: gui.menu_is_active = TRUE; /* default: include menu */
208:
209: gui.scrollbar_width = SB_DEFAULT_WIDTH;
210: gui.menu_height = MENU_DEFAULT_HEIGHT;
211: for (i = 0; i < 3; i++)
212: gui.new_sb[i] = FALSE;
213:
214: gui.prev_wrap = -1;
215:
216: for (i = 0; default_menus[i].name != NULL; ++i)
217: gui_add_menu_path((char_u *)default_menus[i].name,
218: default_menus[i].mode, gui_menu_cb,
219: (char_u *)default_menus[i].command, TRUE);
220:
221: /*
222: * Switch on the mouse by default.
223: * This can be changed in the .gvimrc.
224: */
225: set_string_option((char_u *)"mouse", -1, (char_u *)"a", TRUE);
226:
227: /*
228: * Get system wide defaults for gvim (Unix only)
229: */
230: #ifdef HAVE_CONFIG_H
231: do_source(sys_gvimrc_fname, FALSE);
232: #endif
233:
234: /*
235: * Try to read GUI initialization commands from the following places:
236: * - environment variable GVIMINIT
237: * - the user gvimrc file (~/.gvimrc for Unix)
238: * The first that exists is used, the rest is ignored.
239: */
240: if ((env_str = vim_getenv((char_u *)"GVIMINIT")) != NULL && *env_str != NUL)
241: {
242: sourcing_name = (char_u *)"GVIMINIT";
243: do_cmdline(env_str, TRUE, TRUE);
244: sourcing_name = NULL;
245: }
246: else
247: do_source((char_u *)USR_GVIMRC_FILE, FALSE);
248:
249: /*
250: * Read initialization commands from ".gvimrc" in current directory. This
251: * is only done if the 'exrc' option is set. Because of security reasons
252: * we disallow shell and write commands now, except for unix if the file is
253: * owned by the user or 'secure' option has been reset in environment of
254: * global ".gvimrc". Only do this if GVIMRC_FILE is not the same as
255: * USR_GVIMRC_FILE or sys_gvimrc_fname.
256: */
257: if (p_exrc)
258: {
259: #ifdef UNIX
260: {
261: struct stat s;
262:
263: /* if ".gvimrc" file is not owned by user, set 'secure' mode */
264: if (stat(GVIMRC_FILE, &s) || s.st_uid != getuid())
265: secure = p_secure;
266: }
267: #else
268: secure = p_secure;
269: #endif
270:
271: i = FAIL;
272: if (fullpathcmp((char_u *)USR_GVIMRC_FILE,
273: (char_u *)GVIMRC_FILE) != FPC_SAME
274: #ifdef HAVE_CONFIG_H
275: && fullpathcmp(sys_gvimrc_fname,
276: (char_u *)GVIMRC_FILE) != FPC_SAME
277: #endif
278: )
279: i = do_source((char_u *)GVIMRC_FILE, FALSE);
1.3 ! downsj 280:
! 281: if (secure == 2)
! 282: need_wait_return = TRUE;
! 283: secure = 0;
1.1 downsj 284: }
285:
286: /*
287: * Actually start the GUI itself.
288: */
289: gui.in_use = TRUE; /* Must be set after menus have been set up */
290: if (gui_mch_init() == FAIL)
291: {
292: gui.in_use = FALSE;
293: return;
294: }
295:
296: maketitle();
297:
298: gui_create_initial_menus(gui.root_menu, NULL);
299: }
300:
301: void
302: gui_exit()
303: {
304: gui.in_use = FALSE;
305: gui_mch_exit();
306: }
307:
308: /*
309: * Set the font. Uses the 'font' option. The first font name that works is
310: * used. If none is found, use the default font.
311: */
312: int
313: gui_init_font()
314: {
315: #define FONTLEN 100
316: char_u *font_list;
317: char_u font_name[FONTLEN];
318:
319: if (!gui.in_use)
320: return FAIL;
321:
322: for (font_list = p_guifont; *font_list != NUL; )
323: {
324: /* Isolate one font name */
325: (void)copy_option_part(&font_list, font_name, FONTLEN, ",");
326: if (gui_mch_init_font(font_name) == OK)
327: return OK;
328: }
329:
330: /*
331: * Couldn't load any font in 'font', tell gui_mch_init_font() to try and
332: * find a font we can load.
333: */
334: return gui_mch_init_font(NULL);
335: }
336:
337: void
338: gui_set_cursor(row, col)
339: int row;
340: int col;
341: {
342: gui.row = row;
343: gui.col = col;
344: }
345:
346: /*
347: * gui_check_screen - check if the cursor is on the screen.
348: */
349: static void
350: gui_check_screen()
351: {
352: if (gui.row >= Rows)
353: gui.row = Rows - 1;
354: if (gui.col >= Columns)
355: gui.col = Columns - 1;
356: if (gui.cursor_row >= Rows || gui.cursor_col >= Columns)
357: INVALIDATE_CURSOR();
358: }
359:
360: void
361: gui_update_cursor()
362: {
363: gui_check_screen();
364: if (gui.row != gui.cursor_row || gui.col != gui.cursor_col)
365: {
366: gui_undraw_cursor();
367: gui.cursor_row = gui.row;
368: gui.cursor_col = gui.col;
369: gui_mch_draw_cursor();
370: }
371: }
372:
373: /*
374: * Should be called after the GUI window has been resized. Its arguments are
375: * the new width and height of the window in pixels.
376: */
377: void
378: gui_resize_window(pixel_width, pixel_height)
379: int pixel_width;
380: int pixel_height;
381: {
382: gui.num_cols = (pixel_width - 2 * gui.border_offset) / gui.char_width;
383: gui.num_rows = (pixel_height - 2 * gui.border_offset) / gui.char_height;
384:
385: gui_reset_scroll_region();
386: /*
387: * At the "more" prompt there is no redraw, put the cursor at the last
1.2 downsj 388: * line here (why does it have to be one row too low?).
1.1 downsj 389: */
390: if (State == ASKMORE)
391: gui.row = gui.num_rows;
392:
393: if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns)
394: set_winsize(0, 0, FALSE);
395: gui_update_cursor();
396: }
397:
398: /*
399: * Make scroll region cover whole screen.
400: */
401: void
402: gui_reset_scroll_region()
403: {
404: gui.scroll_region_top = 0;
405: gui.scroll_region_bot = gui.num_rows - 1;
406: }
407:
408: void
409: gui_start_highlight(mask)
410: long_u mask;
411: {
412: gui.highlight_mask |= mask;
413: }
414:
415: void
416: gui_stop_highlight(mask)
417: long_u mask;
418: {
419: gui.highlight_mask &= ~mask;
420: }
421:
422: void
423: gui_write(s, len)
424: char_u *s;
425: int len;
426: {
427: char_u *p;
428: int arg1 = 0, arg2 = 0;
429:
430: /* #define DEBUG_GUI_WRITE */
431: #ifdef DEBUG_GUI_WRITE
432: {
433: int i;
434: char_u *str;
435:
436: printf("gui_write(%d):\n ", len);
437: for (i = 0; i < len; i++)
438: if (s[i] == ESC)
439: {
440: if (i != 0)
441: printf("\n ");
442: printf("<ESC>");
443: }
444: else
445: {
446: str = transchar(s[i]);
447: if (str[0] && str[1])
448: printf("<%s>", (char *)str);
449: else
450: printf("%s", (char *)str);
451: }
452: printf("\n");
453: }
454: #endif
455: while (len)
456: {
457: if (s[0] == '\n')
458: {
459: len--;
460: s++;
461: gui.col = 0;
462: if (gui.row < gui.scroll_region_bot)
463: gui.row++;
464: else
465: gui_mch_delete_lines(gui.scroll_region_top, 1);
466: }
467: else if (s[0] == '\r')
468: {
469: len--;
470: s++;
471: gui.col = 0;
472: }
473: else if (s[0] == Ctrl('G')) /* Beep */
474: {
475: gui_mch_beep();
476: len--;
477: s++;
478: }
479: else if (s[0] == ESC && s[1] == '|')
480: {
481: p = s + 2;
482: if (isdigit(*p))
483: {
484: arg1 = getdigits(&p);
485: if (p > s + len)
486: break;
487: if (*p == ';')
488: {
489: ++p;
490: arg2 = getdigits(&p);
491: if (p > s + len)
492: break;
493: }
494: }
495: switch (*p)
496: {
497: case 'C': /* Clear screen */
498: gui_mch_clear_block(0, 0, Rows - 1, Columns - 1);
499: break;
500: case 'M': /* Move cursor */
501: gui_set_cursor(arg1, arg2);
502: break;
503: case 'R': /* Set scroll region */
504: if (arg1 < arg2)
505: {
506: gui.scroll_region_top = arg1;
507: gui.scroll_region_bot = arg2;
508: }
509: else
510: {
511: gui.scroll_region_top = arg2;
512: gui.scroll_region_bot = arg1;
513: }
514: break;
515: case 'd': /* Delete line */
516: gui_mch_delete_lines(gui.row, 1);
517: break;
518: case 'D': /* Delete lines */
519: gui_mch_delete_lines(gui.row, arg1);
520: break;
521: case 'i': /* Insert line */
522: gui_mch_insert_lines(gui.row, 1);
523: break;
524: case 'I': /* Insert lines */
525: gui_mch_insert_lines(gui.row, arg1);
526: break;
527: case '$': /* Clear to end-of-line */
528: gui_mch_clear_block(gui.row, gui.col, gui.row, Columns - 1);
529: break;
530: case 'h': /* Turn on highlighting */
531: gui_start_highlight(arg1);
532: break;
533: case 'H': /* Turn off highlighting */
534: gui_stop_highlight(arg1);
535: break;
536: case 'f': /* flash the window (visual bell) */
537: gui_mch_flash();
538: break;
539: default:
540: p = s + 1; /* Skip the ESC */
541: break;
542: }
543: len -= ++p - s;
544: s = p;
545: }
546: else if (s[0] < 0x20) /* Ctrl character, shouldn't happen */
547: {
548: /*
549: * For some reason vim sends me a ^M after hitting return on the
550: * ':' line. Make sure we ignore this here.
551: */
552: len--; /* Skip this char */
553: s++;
554: }
555: else
556: {
557: p = s;
558: while (len && *p >= 0x20)
559: {
560: len--;
561: p++;
562: }
563: gui_outstr(s, p - s);
564: s = p;
565: }
566: }
567: gui_update_cursor();
568: gui_update_scrollbars();
569: gui_update_horiz_scrollbar();
570:
571: /*
572: * We need to make sure this is cleared since Athena doesn't tell us when
573: * he is done dragging.
574: */
575: gui.dragged_sb = SB_NONE;
576:
577: if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
578: gui_update_selection();
579: gui_mch_flush(); /* In case vim decides to take a nap */
580: }
581:
582: static void
583: gui_outstr(s, len)
584: char_u *s;
585: int len;
586: {
587: int this_len;
588:
589: if (len == 0)
590: return;
591:
592: if (len < 0)
593: len = STRLEN(s);
594:
595: while (gui.col + len > Columns)
596: {
597: this_len = Columns - gui.col;
598: gui_mch_outstr_nowrap(s, this_len, TRUE, FALSE);
599: s += this_len;
600: len -= this_len;
601: }
602: gui_mch_outstr_nowrap(s, len, TRUE, FALSE);
603: }
604:
605: /*
606: * Un-draw the cursor. Actually this just redraws the character at the given
607: * position.
608: */
609: void
610: gui_undraw_cursor()
611: {
612: if (IS_CURSOR_VALID())
613: gui_redraw_block(gui.cursor_row, gui.cursor_col, gui.cursor_row,
614: gui.cursor_col);
615: }
616:
617: void
618: gui_redraw(x, y, w, h)
619: int x;
620: int y;
621: int w;
622: int h;
623: {
624: int row1, col1, row2, col2;
625:
626: row1 = Y_2_ROW(y);
627: col1 = X_2_COL(x);
628: row2 = Y_2_ROW(y + h - 1);
629: col2 = X_2_COL(x + w - 1);
630:
631: gui_redraw_block(row1, col1, row2, col2);
632:
633: /* We may need to redraw the cursor */
634: gui_update_cursor();
635: }
636:
637: void
638: gui_redraw_block(row1, col1, row2, col2)
639: int row1;
640: int col1;
641: int row2;
642: int col2;
643: {
644: int old_row, old_col;
645: long_u old_hl_mask;
646: char_u *screenp, *attrp, first_attr;
647: int idx, len;
648:
649: /* Don't try to draw outside the window! */
650: /* Check everything, strange values may be caused by big border width */
651: col1 = check_col(col1);
652: col2 = check_col(col2);
653: row1 = check_row(row1);
654: row2 = check_row(row2);
655:
656: /* Don't try to update when NextScreen is not valid */
657: if (!screen_cleared || NextScreen == NULL)
658: return;
659:
660: /* Remember where our cursor was */
661: old_row = gui.row;
662: old_col = gui.col;
663: old_hl_mask = gui.highlight_mask;
664:
665: for (gui.row = row1; gui.row <= row2; gui.row++)
666: {
667: gui.col = col1;
668: screenp = LinePointers[gui.row] + gui.col;
669: attrp = screenp + Columns;
670: len = col2 - col1 + 1;
671: while (len > 0)
672: {
673: switch (attrp[0])
674: {
675: case CHAR_INVERT:
676: gui.highlight_mask = HL_INVERSE;
677: break;
678: case CHAR_UNDERL:
679: gui.highlight_mask = HL_UNDERLINE;
680: break;
681: case CHAR_BOLD:
682: gui.highlight_mask = HL_BOLD;
683: break;
684: case CHAR_STDOUT:
685: gui.highlight_mask = HL_STANDOUT;
686: break;
687: case CHAR_ITALIC:
688: gui.highlight_mask = HL_ITAL;
689: break;
690: case CHAR_NORMAL:
691: default:
692: gui.highlight_mask = HL_NORMAL;
693: break;
694: }
695: first_attr = attrp[0];
696: for (idx = 0; len > 0 && attrp[idx] == first_attr; idx++)
697: --len;
698: gui_mch_outstr_nowrap(screenp, idx, FALSE, FALSE);
699: screenp += idx;
700: attrp += idx;
701: }
702: }
703:
704: /* Put the cursor back where it was */
705: gui.row = old_row;
706: gui.col = old_col;
707: gui.highlight_mask = old_hl_mask;
708: }
709:
710: /*
711: * Check bounds for column number
712: */
713: int
714: check_col(col)
715: int col;
716: {
717: if (col < 0)
718: return 0;
719: if (col >= (int)Columns)
720: return (int)Columns - 1;
721: return col;
722: }
723:
724: /*
725: * Check bounds for row number
726: */
727: int
728: check_row(row)
729: int row;
730: {
731: if (row < 0)
732: return 0;
733: if (row >= (int)Rows)
734: return (int)Rows - 1;
735: return row;
736: }
737:
738: /*
739: * Generic mouse support function. Add a mouse event to the input buffer with
740: * the given properties.
741: * button --- may be any of MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT,
742: * MOUSE_DRAG, or MOUSE_RELEASE.
743: * x, y --- Coordinates of mouse in pixels.
744: * repeated_click --- TRUE if this click comes only a short time after a
745: * previous click.
746: * modifiers --- Bit field which may be any of the following modifiers
747: * or'ed together: MOUSE_SHIFT | MOUSE_CTRL | MOUSE_ALT.
748: * This function will ignore drag events where the mouse has not moved to a new
749: * character.
750: */
751: void
752: gui_send_mouse_event(button, x, y, repeated_click, modifiers)
753: int button;
754: int x;
755: int y;
756: int repeated_click;
757: int_u modifiers;
758: {
759: static int prev_row = 0, prev_col = 0;
760: static int prev_button = -1;
761: static linenr_t prev_topline = 0;
762: static int num_clicks = 1;
763: char_u string[6];
764: int row, col;
765:
766: row = Y_2_ROW(y);
767: col = X_2_COL(x);
768:
769: /*
770: * If we are dragging and the mouse hasn't moved far enough to be on a
771: * different character, then don't send an event to vim.
772: */
773: if (button == MOUSE_DRAG && row == prev_row && col == prev_col)
774: return;
775:
776: /*
777: * If topline has changed (window scrolled) since the last click, reset
778: * repeated_click, because we don't want starting Visual mode when
779: * clicking on a different character in the text.
780: */
781: if (curwin->w_topline != prev_topline)
782: repeated_click = FALSE;
783:
784: string[0] = CSI; /* this sequence is recognized by check_termcode() */
785: string[1] = KS_MOUSE;
786: string[2] = K_FILLER;
787: if (button != MOUSE_DRAG && button != MOUSE_RELEASE)
788: {
789: if (repeated_click)
790: {
791: /*
792: * Handle multiple clicks. They only count if the mouse is still
793: * pointing at the same character.
794: */
795: if (button != prev_button || row != prev_row || col != prev_col)
796: num_clicks = 1;
797: else if (++num_clicks > 4)
798: num_clicks = 1;
799: }
800: else
801: num_clicks = 1;
802: prev_button = button;
803: prev_topline = curwin->w_topline;
804:
805: string[3] = (char_u)(button | 0x20);
806: SET_NUM_MOUSE_CLICKS(string[3], num_clicks);
807: }
808: else
809: string[3] = (char_u)button;
810:
811: string[3] |= modifiers;
812: string[4] = (char_u)(col + ' ' + 1);
813: string[5] = (char_u)(row + ' ' + 1);
814: add_to_input_buf(string, 6);
815:
816: prev_row = row;
817: prev_col = col;
818: }
819:
820: /*
821: * Selection stuff, for cutting and pasting text to other windows.
822: */
823:
824: /*
825: * Check whether the VIsual area has changed, and if so try to become the owner
826: * of the selection, and free any old converted selection we may still have
827: * lying around. If the VIsual mode has ended, make a copy of what was
828: * selected so we can still give it to others. Will probably have to make sure
829: * this is called whenever VIsual mode is ended.
830: */
831: static void
832: gui_update_selection()
833: {
834: /* If visual mode is only due to a redo command ("."), then ignore it */
835: if (redo_VIsual_busy)
836: return;
837: if (!VIsual_active)
838: {
839: gui_mch_clear_selection();
840: gui.selection.start = gui.selection.end = VIsual;
841: }
842: else if (lt(VIsual, curwin->w_cursor))
843: {
844: if (!equal(gui.selection.start, VIsual) ||
845: !equal(gui.selection.end, curwin->w_cursor))
846: {
847: gui_mch_clear_selection();
848: gui.selection.start = VIsual;
849: gui.selection.end = curwin->w_cursor;
850: gui_free_selection();
851: gui_own_selection();
852: }
853: }
854: else
855: {
856: if (!equal(gui.selection.start, curwin->w_cursor) ||
857: !equal(gui.selection.end, VIsual))
858: {
859: gui_mch_clear_selection();
860: gui.selection.start = curwin->w_cursor;
861: gui.selection.end = VIsual;
862: gui_free_selection();
863: gui_own_selection();
864: }
865: }
866: }
867:
868: void
869: gui_own_selection()
870: {
871: /*
872: * Also want to check somehow that we are reading from the keyboard rather
873: * than a mapping etc.
874: */
875: if (!gui.selection.owned && gui_mch_own_selection())
876: {
877: gui_free_selection();
878: gui.selection.owned = TRUE;
879: }
880: }
881:
882: void
883: gui_lose_selection()
884: {
885: gui_free_selection();
886: gui.selection.owned = FALSE;
887: gui_mch_lose_selection();
888: }
889:
890: void
891: gui_copy_selection()
892: {
893: if (VIsual_active)
894: {
895: if (vim_strchr(p_guioptions, GO_ASEL) == NULL)
896: gui_update_selection();
897: gui_own_selection();
898: if (gui.selection.owned)
899: gui_get_selection();
900: }
901: }
902:
903: void
904: gui_auto_select()
905: {
906: if (vim_strchr(p_guioptions, GO_ASEL) != NULL)
907: gui_copy_selection();
908: }
909:
910: /*
911: * Menu stuff.
912: */
913:
914: void
915: gui_menu_cb(menu)
916: GuiMenu *menu;
917: {
918: char_u bytes[3 + sizeof(long_u)];
919:
920: bytes[0] = CSI;
921: bytes[1] = KS_MENU;
922: bytes[2] = K_FILLER;
923: add_long_to_buf((long_u)menu, bytes + 3);
924: add_to_input_buf(bytes, 3 + sizeof(long_u));
925: }
926:
927: /*
928: * Return the index into the menu->strings or menu->noremap arrays for the
929: * current state. Returns MENU_INDEX_INVALID if there is no mapping for the
930: * given menu in the current mode.
931: */
932: int
933: gui_get_menu_index(menu, state)
934: GuiMenu *menu;
935: int state;
936: {
937: int idx;
938:
939: if (VIsual_active)
940: idx = MENU_INDEX_VISUAL;
941: else if ((state & NORMAL))
942: idx = MENU_INDEX_NORMAL;
943: else if ((state & INSERT))
944: idx = MENU_INDEX_INSERT;
945: else if ((state & CMDLINE))
946: idx = MENU_INDEX_CMDLINE;
947: else
948: idx = MENU_INDEX_INVALID;
949:
950: if (idx != MENU_INDEX_INVALID && menu->strings[idx] == NULL)
951: idx = MENU_INDEX_INVALID;
952: return idx;
953: }
954:
955: /*
956: * Return the modes specified by the given menu command (eg :menu! returns
957: * MENU_CMDLINE_MODE | MENU_INSERT_MODE). If noremap is not NULL, then the
958: * flag it points to is set according to whether the command is a "nore"
959: * command. If unmenu is not NULL, then the flag it points to is set
960: * according to whether the command is an "unmenu" command.
961: */
962: static int
1.2 downsj 963: gui_get_menu_cmd_modes(cmd, forceit, noremap, unmenu)
1.1 downsj 964: char_u *cmd;
1.2 downsj 965: int forceit; /* Was there a "!" after the command? */
1.1 downsj 966: int *noremap;
967: int *unmenu;
968: {
969: int modes = 0x0;
970:
971: if (*cmd == 'n' && cmd[1] != 'o') /* nmenu, nnoremenu */
972: {
973: modes |= MENU_NORMAL_MODE;
974: cmd++;
975: }
976: else if (*cmd == 'v') /* vmenu, vnoremenu */
977: {
978: modes |= MENU_VISUAL_MODE;
979: cmd++;
980: }
981: else if (*cmd == 'i') /* imenu, inoremenu */
982: {
983: modes |= MENU_INSERT_MODE;
984: cmd++;
985: }
986: else if (*cmd == 'c') /* cmenu, cnoremenu */
987: {
988: modes |= MENU_CMDLINE_MODE;
989: cmd++;
990: }
1.2 downsj 991: else if (forceit) /* menu!, noremenu! */
1.1 downsj 992: modes |= MENU_INSERT_MODE | MENU_CMDLINE_MODE;
993: else /* menu, noremenu */
994: modes |= MENU_NORMAL_MODE | MENU_VISUAL_MODE;
995:
996: if (noremap != NULL)
997: *noremap = (*cmd == 'n');
998: if (unmenu != NULL)
999: *unmenu = (*cmd == 'u');
1000: return modes;
1001: }
1002:
1003: /*
1004: * Do the :menu commands.
1005: */
1006: void
1.2 downsj 1007: gui_do_menu(cmd, arg, forceit)
1.1 downsj 1008: char_u *cmd;
1009: char_u *arg;
1.2 downsj 1010: int forceit;
1.1 downsj 1011: {
1012: char_u *menu_path;
1013: int modes;
1014: char_u *map_to;
1015: int noremap;
1016: int unmenu;
1017: char_u *map_buf;
1018:
1.2 downsj 1019: modes = gui_get_menu_cmd_modes(cmd, forceit, &noremap, &unmenu);
1.1 downsj 1020: menu_path = arg;
1021: if (*menu_path == NUL)
1022: {
1023: gui_show_menus(menu_path, modes);
1024: return;
1025: }
1026: while (*arg && !vim_iswhite(*arg))
1027: {
1028: if ((*arg == '\\' || *arg == Ctrl('V')) && arg[1] != NUL)
1029: arg++;
1030: arg++;
1031: }
1032: if (*arg != NUL)
1033: *arg++ = NUL;
1034: arg = skipwhite(arg);
1035: map_to = arg;
1036: if (*map_to == NUL && !unmenu)
1037: {
1038: gui_show_menus(menu_path, modes);
1039: return;
1040: }
1041: else if (*map_to != NUL && unmenu)
1042: {
1043: EMSG("Trailing characters");
1044: return;
1045: }
1046: if (unmenu)
1047: {
1048: if (STRCMP(menu_path, "*") == 0) /* meaning: remove all menus */
1049: menu_path = (char_u *)"";
1050: gui_remove_menu(&gui.root_menu, menu_path, modes);
1051: }
1052: else
1053: {
1054: /* Replace special key codes */
1055: map_to = replace_termcodes(map_to, &map_buf, FALSE);
1056: gui_add_menu_path(menu_path, modes, gui_menu_cb, map_to, noremap);
1057: vim_free(map_buf);
1058: }
1059: }
1060:
1061: /*
1062: * Add the menu with the given name to the menu hierarchy
1063: */
1064: static int
1065: gui_add_menu_path(path_name, modes, call_back, call_data, noremap)
1066: char_u *path_name;
1067: int modes;
1068: void (*call_back)();
1069: char_u *call_data;
1070: int noremap;
1071: {
1072: GuiMenu **menup;
1073: GuiMenu *menu = NULL;
1074: GuiMenu *parent;
1075: char_u *p;
1076: char_u *name;
1077: int i;
1078:
1079: /* Make a copy so we can stuff around with it, since it could be const */
1080: path_name = strsave(path_name);
1081: if (path_name == NULL)
1082: return FAIL;
1083: menup = &gui.root_menu;
1084: parent = NULL;
1085: name = path_name;
1086: while (*name)
1087: {
1088: /* Get name of this element in the menu hierarchy */
1089: p = gui_menu_name_skip(name);
1090:
1091: /* See if it's already there */
1092: menu = *menup;
1093: while (menu != NULL)
1094: {
1095: if (STRCMP(name, menu->name) == 0)
1096: {
1097: if (*p == NUL && menu->children != NULL)
1098: {
1099: EMSG("Menu path must not lead to a sub-menu");
1100: vim_free(path_name);
1101: return FAIL;
1102: }
1103: else if (*p != NUL && menu->children == NULL)
1104: {
1105: EMSG("Part of menu-item path is not sub-menu");
1106: vim_free(path_name);
1107: return FAIL;
1108: }
1109: break;
1110: }
1111: menup = &menu->next;
1112: menu = menu->next;
1113: }
1114: if (menu == NULL)
1115: {
1116: if (*p == NUL && parent == NULL)
1117: {
1118: EMSG("Must not add menu items directly to menu bar");
1119: vim_free(path_name);
1120: return FAIL;
1121: }
1122:
1123: /* Not already there, so lets add it */
1124: menu = (GuiMenu *)alloc(sizeof(GuiMenu));
1125: if (menu == NULL)
1126: {
1127: vim_free(path_name);
1128: return FAIL;
1129: }
1130: menu->modes = modes;
1131: menu->name = strsave(name);
1132: menu->cb = NULL;
1133: for (i = 0; i < 4; i++)
1134: {
1135: menu->strings[i] = NULL;
1136: menu->noremap[i] = FALSE;
1137: }
1138: menu->children = NULL;
1139: menu->next = NULL;
1140: if (gui.in_use) /* Otherwise it will be added when GUI starts */
1141: {
1142: if (*p == NUL)
1143: {
1144: /* Real menu item, not sub-menu */
1145: gui_mch_add_menu_item(menu, parent);
1146:
1147: /* Want to update menus now even if mode not changed */
1148: force_menu_update = TRUE;
1149: }
1150: else
1151: {
1152: /* Sub-menu (not at end of path yet) */
1153: gui_mch_add_menu(menu, parent);
1154: }
1155: }
1156: *menup = menu;
1157: }
1158: else
1159: {
1160: /*
1161: * If this menu option was previously only available in other
1162: * modes, then make sure it's available for this one now
1163: */
1164: menu->modes |= modes;
1165: }
1166:
1167: menup = &menu->children;
1168: parent = menu;
1169: name = p;
1170: }
1171: vim_free(path_name);
1172:
1173: if (menu != NULL)
1174: {
1175: menu->cb = call_back;
1176: p = (call_data == NULL) ? NULL : strsave(call_data);
1177:
1178: /* May match more than one of these */
1179: if (modes & MENU_NORMAL_MODE)
1180: {
1181: gui_free_menu_string(menu, MENU_INDEX_NORMAL);
1182: menu->strings[MENU_INDEX_NORMAL] = p;
1183: menu->noremap[MENU_INDEX_NORMAL] = noremap;
1184: }
1185: if (modes & MENU_VISUAL_MODE)
1186: {
1187: gui_free_menu_string(menu, MENU_INDEX_VISUAL);
1188: menu->strings[MENU_INDEX_VISUAL] = p;
1189: menu->noremap[MENU_INDEX_VISUAL] = noremap;
1190: }
1191: if (modes & MENU_INSERT_MODE)
1192: {
1193: gui_free_menu_string(menu, MENU_INDEX_INSERT);
1194: menu->strings[MENU_INDEX_INSERT] = p;
1195: menu->noremap[MENU_INDEX_INSERT] = noremap;
1196: }
1197: if (modes & MENU_CMDLINE_MODE)
1198: {
1199: gui_free_menu_string(menu, MENU_INDEX_CMDLINE);
1200: menu->strings[MENU_INDEX_CMDLINE] = p;
1201: menu->noremap[MENU_INDEX_CMDLINE] = noremap;
1202: }
1203: }
1204: return OK;
1205: }
1206:
1207: /*
1208: * Remove the (sub)menu with the given name from the menu hierarchy
1209: * Called recursively.
1210: */
1211: static int
1212: gui_remove_menu(menup, name, modes)
1213: GuiMenu **menup;
1214: char_u *name;
1215: int modes;
1216: {
1217: GuiMenu *menu;
1218: GuiMenu *child;
1219: char_u *p;
1220:
1221: if (*menup == NULL)
1222: return OK; /* Got to bottom of hierarchy */
1223:
1224: /* Get name of this element in the menu hierarchy */
1225: p = gui_menu_name_skip(name);
1226:
1227: /* Find the menu */
1228: menu = *menup;
1229: while (menu != NULL)
1230: {
1231: if (*name == NUL || STRCMP(name, menu->name) == 0)
1232: {
1233: if (*p != NUL && menu->children == NULL)
1234: {
1235: EMSG("Part of menu-item path is not sub-menu");
1236: return FAIL;
1237: }
1238: if ((menu->modes & modes) != 0x0)
1239: {
1240: if (gui_remove_menu(&menu->children, p, modes) == FAIL)
1241: return FAIL;
1242: }
1243: else if (*name != NUL)
1244: {
1245: EMSG("Menu only exists in another mode");
1246: return FAIL;
1247: }
1248:
1249: /*
1250: * When name is empty, we are removing all menu items for the given
1251: * modes, so keep looping, otherwise we are just removing the named
1252: * menu item (which has been found) so break here.
1253: */
1254: if (*name != NUL)
1255: break;
1256:
1257: /* Remove the menu item for the given mode[s] */
1258: menu->modes &= ~modes;
1259:
1260: if (menu->modes == 0x0)
1261: {
1262: /* The menu item is no longer valid in ANY mode, so delete it */
1263: *menup = menu->next;
1264: gui_free_menu(menu);
1265: }
1266: else
1267: menup = &menu->next;
1268: }
1269: else
1270: menup = &menu->next;
1271: menu = *menup;
1272: }
1273: if (*name != NUL)
1274: {
1275: if (menu == NULL)
1276: {
1277: EMSG("No menu of that name");
1278: return FAIL;
1279: }
1280:
1281: /* Recalculate modes for menu based on the new updated children */
1282: menu->modes = 0x0;
1283: for (child = menu->children; child != NULL; child = child->next)
1284: menu->modes |= child->modes;
1285: if (menu->modes == 0x0)
1286: {
1287: /* The menu item is no longer valid in ANY mode, so delete it */
1288: *menup = menu->next;
1289: gui_free_menu(menu);
1290: }
1291: }
1292:
1293: return OK;
1294: }
1295:
1296: /*
1297: * Free the given menu structure
1298: */
1299: static void
1300: gui_free_menu(menu)
1301: GuiMenu *menu;
1302: {
1303: int i;
1304:
1.2 downsj 1305: /* Free machine specific menu structures (only when already created) */
1306: if (gui.in_use)
1307: gui_mch_destroy_menu(menu);
1.1 downsj 1308: vim_free(menu->name);
1309: for (i = 0; i < 4; i++)
1310: gui_free_menu_string(menu, i);
1311: vim_free(menu);
1312:
1313: /* Want to update menus now even if mode not changed */
1314: force_menu_update = TRUE;
1315: }
1316:
1317: /*
1318: * Free the menu->string with the given index.
1319: */
1320: static void
1321: gui_free_menu_string(menu, idx)
1322: GuiMenu *menu;
1323: int idx;
1324: {
1325: int count = 0;
1326: int i;
1327:
1328: for (i = 0; i < 4; i++)
1329: if (menu->strings[i] == menu->strings[idx])
1330: count++;
1331: if (count == 1)
1332: vim_free(menu->strings[idx]);
1333: menu->strings[idx] = NULL;
1334: }
1335:
1336: /*
1337: * Show the mapping associated with a menu item or hierarchy in a sub-menu.
1338: */
1339: static int
1340: gui_show_menus(path_name, modes)
1341: char_u *path_name;
1342: int modes;
1343: {
1344: char_u *p;
1345: char_u *name;
1346: GuiMenu *menu;
1347: GuiMenu *parent = NULL;
1348:
1349: menu = gui.root_menu;
1350: name = path_name = strsave(path_name);
1351: if (path_name == NULL)
1352: return FAIL;
1353:
1354: /* First, find the (sub)menu with the given name */
1355: while (*name)
1356: {
1357: p = gui_menu_name_skip(name);
1358: while (menu != NULL)
1359: {
1360: if (STRCMP(name, menu->name) == 0)
1361: {
1362: /* Found menu */
1363: if (*p != NUL && menu->children == NULL)
1364: {
1365: EMSG("Part of menu-item path is not sub-menu");
1366: vim_free(path_name);
1367: return FAIL;
1368: }
1369: else if ((menu->modes & modes) == 0x0)
1370: {
1371: EMSG("Menu only exists in another mode");
1372: vim_free(path_name);
1373: return FAIL;
1374: }
1375: break;
1376: }
1377: menu = menu->next;
1378: }
1379: if (menu == NULL)
1380: {
1381: EMSG("No menu of that name");
1382: vim_free(path_name);
1383: return FAIL;
1384: }
1385: name = p;
1386: parent = menu;
1387: menu = menu->children;
1388: }
1389:
1390: /* Now we have found the matching menu, and we list the mappings */
1391: set_highlight('t'); /* Highlight title */
1392: start_highlight();
1393: MSG_OUTSTR("\n--- Menus ---");
1394: stop_highlight();
1395:
1396: gui_show_menus_recursive(parent, modes, 0);
1397: return OK;
1398: }
1399:
1400: /*
1401: * Recursively show the mappings associated with the menus under the given one
1402: */
1403: static void
1404: gui_show_menus_recursive(menu, modes, depth)
1405: GuiMenu *menu;
1406: int modes;
1407: int depth;
1408: {
1409: int i;
1410: int bit;
1411:
1412: if (menu != NULL && (menu->modes & modes) == 0x0)
1413: return;
1414:
1415: if (menu != NULL)
1416: {
1417: msg_outchar('\n');
1418: if (got_int) /* "q" hit for "--more--" */
1419: return;
1420: for (i = 0; i < depth; i++)
1421: MSG_OUTSTR(" ");
1422: set_highlight('d'); /* Same as for directories!? */
1423: start_highlight();
1424: msg_outstr(menu->name);
1425: stop_highlight();
1426: }
1427:
1428: if (menu != NULL && menu->children == NULL)
1429: {
1430: for (bit = 0; bit < 4; bit++)
1431: if ((menu->modes & modes & (1 << bit)) != 0)
1432: {
1433: msg_outchar('\n');
1434: if (got_int) /* "q" hit for "--more--" */
1435: return;
1436: for (i = 0; i < depth + 2; i++)
1437: MSG_OUTSTR(" ");
1438: msg_outchar("nvic"[bit]);
1439: if (menu->noremap[bit])
1440: msg_outchar('*');
1441: else
1442: msg_outchar(' ');
1443: MSG_OUTSTR(" ");
1444: msg_outtrans_special(menu->strings[bit], TRUE);
1445: }
1446: }
1447: else
1448: {
1449: if (menu == NULL)
1450: {
1451: menu = gui.root_menu;
1452: depth--;
1453: }
1454: else
1455: menu = menu->children;
1456: for (; menu != NULL; menu = menu->next)
1457: gui_show_menus_recursive(menu, modes, depth + 1);
1458: }
1459: }
1460:
1461: /*
1462: * Used when expanding menu names.
1463: */
1464: static GuiMenu *expand_menu = NULL;
1465: static int expand_modes = 0x0;
1466:
1467: /*
1468: * Work out what to complete when doing command line completion of menu names.
1469: */
1470: char_u *
1.2 downsj 1471: gui_set_context_in_menu_cmd(cmd, arg, forceit)
1.1 downsj 1472: char_u *cmd;
1473: char_u *arg;
1.2 downsj 1474: int forceit;
1.1 downsj 1475: {
1476: char_u *after_dot;
1477: char_u *p;
1478: char_u *path_name = NULL;
1479: char_u *name;
1480: int unmenu;
1481: GuiMenu *menu;
1482:
1483: expand_context = EXPAND_UNSUCCESSFUL;
1484:
1485: after_dot = arg;
1486: for (p = arg; *p && !vim_iswhite(*p); ++p)
1487: {
1488: if ((*p == '\\' || *p == Ctrl('V')) && p[1] != NUL)
1489: p++;
1490: else if (*p == '.')
1491: after_dot = p + 1;
1492: }
1493: if (*p == NUL) /* Complete the menu name */
1494: {
1495: /*
1496: * With :unmenu, you only want to match menus for the appropriate mode.
1497: * With :menu though you might want to add a menu with the same name as
1498: * one in another mode, so match menus fom other modes too.
1499: */
1.2 downsj 1500: expand_modes = gui_get_menu_cmd_modes(cmd, forceit, NULL, &unmenu);
1.1 downsj 1501: if (!unmenu)
1502: expand_modes = MENU_ALL_MODES;
1503:
1504: menu = gui.root_menu;
1505: if (after_dot != arg)
1506: {
1507: path_name = alloc(after_dot - arg);
1508: if (path_name == NULL)
1509: return NULL;
1510: STRNCPY(path_name, arg, after_dot - arg - 1);
1511: path_name[after_dot - arg - 1] = NUL;
1512: }
1513: name = path_name;
1514: while (name != NULL && *name)
1515: {
1516: p = gui_menu_name_skip(name);
1517: while (menu != NULL)
1518: {
1519: if (STRCMP(name, menu->name) == 0)
1520: {
1521: /* Found menu */
1522: if ((*p != NUL && menu->children == NULL)
1523: || ((menu->modes & expand_modes) == 0x0))
1524: {
1525: /*
1526: * Menu path continues, but we have reached a leaf.
1527: * Or menu exists only in another mode.
1528: */
1529: vim_free(path_name);
1530: return NULL;
1531: }
1532: break;
1533: }
1534: menu = menu->next;
1535: }
1536: if (menu == NULL)
1537: {
1538: /* No menu found with the name we were looking for */
1539: vim_free(path_name);
1540: return NULL;
1541: }
1542: name = p;
1543: menu = menu->children;
1544: }
1545:
1546: expand_context = EXPAND_MENUS;
1547: expand_pattern = after_dot;
1548: expand_menu = menu;
1549: }
1550: else /* We're in the mapping part */
1551: expand_context = EXPAND_NOTHING;
1552: return NULL;
1553: }
1554:
1555: /*
1556: * Expand the menu names.
1557: */
1558: int
1559: gui_ExpandMenuNames(prog, num_file, file)
1560: regexp *prog;
1561: int *num_file;
1562: char_u ***file;
1563: {
1564: GuiMenu *menu;
1565: int round;
1566: int count;
1567:
1568: /*
1569: * round == 1: Count the matches.
1570: * round == 2: Save the matches into the array.
1571: */
1572: for (round = 1; round <= 2; ++round)
1573: {
1574: count = 0;
1575: for (menu = expand_menu; menu != NULL; menu = menu->next)
1576: if ((menu->modes & expand_modes) != 0x0
1577: && vim_regexec(prog, menu->name, TRUE))
1578: {
1579: if (round == 1)
1580: count++;
1581: else
1582: (*file)[count++] = strsave_escaped(menu->name,
1583: (char_u *)" \t\\.");
1584: }
1585: if (round == 1)
1586: {
1587: *num_file = count;
1588: if (count == 0 || (*file = (char_u **)
1589: alloc((unsigned)(count * sizeof(char_u *)))) == NULL)
1590: return FAIL;
1591: }
1592: }
1593: return OK;
1594: }
1595:
1596: /*
1597: * Skip over this element of the menu path and return the start of the next
1598: * element. Any \ and ^Vs are removed from the current element.
1599: */
1600: static char_u *
1601: gui_menu_name_skip(name)
1602: char_u *name;
1603: {
1604: char_u *p;
1605:
1606: for (p = name; *p && *p != '.'; p++)
1607: if (*p == '\\' || *p == Ctrl('V'))
1608: {
1609: STRCPY(p, p + 1);
1610: if (*p == NUL)
1611: break;
1612: }
1613: if (*p)
1614: *p++ = NUL;
1615: return p;
1616: }
1617:
1618: /*
1619: * After we have started the GUI, then we can create any menus that have been
1620: * defined. This is done once here. gui_add_menu_path() may have already been
1621: * called to define these menus, and may be called again. This function calls
1622: * itself recursively. Should be called at the top level with:
1623: * gui_create_initial_menus(gui.root_menu, NULL);
1624: */
1625: static void
1626: gui_create_initial_menus(menu, parent)
1627: GuiMenu *menu;
1628: GuiMenu *parent;
1629: {
1630: while (menu)
1631: {
1632: if (menu->children != NULL)
1633: {
1634: gui_mch_add_menu(menu, parent);
1635: gui_create_initial_menus(menu->children, menu);
1636: }
1637: else
1638: gui_mch_add_menu_item(menu, parent);
1639: menu = menu->next;
1640: }
1641: }
1642:
1643:
1644: /*
1645: * Set which components are present.
1646: * If "oldval" is not NULL, "oldval" is the previous value, the new * value is
1647: * in p_guioptions.
1648: */
1649: void
1650: gui_init_which_components(oldval)
1651: char_u *oldval;
1652: {
1653: char_u *p;
1654: int i;
1655: int grey_old, grey_new;
1656: char_u *temp;
1657:
1.2 downsj 1658: if (oldval != NULL && gui.in_use)
1.1 downsj 1659: {
1660: /*
1661: * Check if the menu's go from grey to non-grey or vise versa.
1662: */
1663: grey_old = (vim_strchr(oldval, GO_GREY) != NULL);
1664: grey_new = (vim_strchr(p_guioptions, GO_GREY) != NULL);
1665: if (grey_old != grey_new)
1666: {
1667: temp = p_guioptions;
1668: p_guioptions = oldval;
1669: gui_x11_update_menus(MENU_ALL_MODES);
1670: p_guioptions = temp;
1671: }
1672: }
1673:
1674: gui.menu_is_active = FALSE;
1675: for (i = 0; i < 3; i++)
1676: gui.which_scrollbars[i] = FALSE;
1677: for (p = p_guioptions; *p; p++)
1678: switch (*p)
1679: {
1680: case GO_LEFT:
1681: gui.which_scrollbars[SB_LEFT] = TRUE;
1682: break;
1683: case GO_RIGHT:
1684: gui.which_scrollbars[SB_RIGHT] = TRUE;
1685: break;
1686: case GO_BOT:
1687: gui.which_scrollbars[SB_BOTTOM] = TRUE;
1688: break;
1689: case GO_MENUS:
1690: gui.menu_is_active = TRUE;
1691: break;
1692: case GO_GREY:
1693: /* make menu's have grey items, ignored here */
1694: break;
1695: default:
1696: /* Should give error message for internal error */
1697: break;
1698: }
1699: if (gui.in_use)
1700: gui_mch_create_which_components();
1701: }
1702:
1703:
1704: /*
1705: * Vertical scrollbar stuff:
1706: */
1707:
1708: static void
1709: gui_update_scrollbars()
1710: {
1711: WIN *wp;
1712: int worst_update = SB_UPDATE_NOTHING;
1713: int val, size, max;
1714: int which_sb;
1715: int cmdline_height;
1716:
1717: /*
1718: * Don't want to update a scrollbar while we're dragging it. But if we
1719: * have both a left and right scrollbar, and we drag one of them, we still
1720: * need to update the other one.
1721: */
1722: if ((gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT) &&
1723: (!gui.which_scrollbars[SB_LEFT] || !gui.which_scrollbars[SB_RIGHT]))
1724: return;
1725:
1726: if (gui.dragged_sb == SB_LEFT || gui.dragged_sb == SB_RIGHT)
1727: {
1728: /*
1729: * If we have two scrollbars and one of them is being dragged, just
1730: * copy the scrollbar position from the dragged one to the other one.
1731: */
1732: which_sb = SB_LEFT + SB_RIGHT - gui.dragged_sb;
1733: if (gui.dragged_wp != NULL)
1734: gui.dragged_wp->w_scrollbar.update[which_sb] = SB_UPDATE_VALUE;
1735: else
1736: gui.cmdline_sb.update[which_sb] = SB_UPDATE_VALUE;
1737:
1738: gui_mch_update_scrollbars(SB_UPDATE_VALUE, which_sb);
1739: return;
1740: }
1741:
1742: /* Return straight away if there is neither a left nor right scrollbar */
1743: if (!gui.which_scrollbars[SB_LEFT] && !gui.which_scrollbars[SB_RIGHT])
1744: return;
1745:
1746: cmdline_height = Rows;
1747: for (wp = firstwin; wp; wp = wp->w_next)
1748: {
1749: cmdline_height -= wp->w_height + wp->w_status_height;
1750: if (wp->w_buffer == NULL) /* just in case */
1751: continue;
1752: #ifdef SCROLL_PAST_END
1753: max = wp->w_buffer->b_ml.ml_line_count;
1754: #else
1755: max = wp->w_buffer->b_ml.ml_line_count + wp->w_height - 1;
1756: #endif
1757: if (max < 1) /* empty buffer */
1758: max = 1;
1759: val = wp->w_topline;
1760: size = wp->w_height;
1761: #ifdef SCROLL_PAST_END
1762: if (val > max) /* just in case */
1763: val = max;
1764: #else
1765: if (size > max) /* just in case */
1766: size = max;
1767: if (val > max - size + 1)
1768: {
1769: val = max - size + 1;
1770: if (val < 1) /* minimal value is 1 */
1771: val = 1;
1772: }
1773: #endif
1774: if (size < 1 || wp->w_botline - 1 > max)
1775: {
1776: /*
1777: * This can happen during changing files. Just don't update the
1778: * scrollbar for now.
1779: */
1780: }
1781: else if (wp->w_scrollbar.height == 0)
1782: {
1783: /* Must be a new window */
1784: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
1785: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
1786: wp->w_scrollbar.value = val;
1787: wp->w_scrollbar.size = size;
1788: wp->w_scrollbar.max = max;
1789: wp->w_scrollbar.top = wp->w_winpos;
1790: wp->w_scrollbar.height = wp->w_height;
1791: wp->w_scrollbar.status_height = wp->w_status_height;
1792: gui.num_scrollbars++;
1793: worst_update = SB_UPDATE_CREATE;
1794: }
1795: else if (wp->w_scrollbar.top != wp->w_winpos
1796: || wp->w_scrollbar.height != wp->w_height
1797: || wp->w_scrollbar.status_height != wp->w_status_height)
1798: {
1799: /* Height of scrollbar has changed */
1800: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
1801: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
1802: wp->w_scrollbar.value = val;
1803: wp->w_scrollbar.size = size;
1804: wp->w_scrollbar.max = max;
1805: wp->w_scrollbar.top = wp->w_winpos;
1806: wp->w_scrollbar.height = wp->w_height;
1807: wp->w_scrollbar.status_height = wp->w_status_height;
1808: if (worst_update < SB_UPDATE_HEIGHT)
1809: worst_update = SB_UPDATE_HEIGHT;
1810: }
1811: else if (wp->w_scrollbar.value != val
1812: || wp->w_scrollbar.size != size
1813: || wp->w_scrollbar.max != max)
1814: {
1815: /* Thumb of scrollbar has moved */
1816: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_VALUE;
1817: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_VALUE;
1818: wp->w_scrollbar.value = val;
1819: wp->w_scrollbar.size = size;
1820: wp->w_scrollbar.max = max;
1821: if (worst_update < SB_UPDATE_VALUE)
1822: worst_update = SB_UPDATE_VALUE;
1823: }
1824:
1825: /*
1826: * We may have just created the left scrollbar say, when we already had
1827: * the right one.
1828: */
1829: if (gui.new_sb[SB_LEFT])
1830: wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_CREATE;
1831: if (gui.new_sb[SB_RIGHT])
1832: wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_CREATE;
1833: }
1834: if (cmdline_height < 1)
1835: cmdline_height = 1; /* Shouldn't happen, but just in case */
1836:
1837: /* Check the command line scrollbar */
1838: if (gui.cmdline_sb.height != cmdline_height
1839: || gui.cmdline_sb.status_height != lastwin->w_status_height)
1840: {
1841: /* Height of scrollbar has changed */
1842: gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
1843: gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
1844: gui.cmdline_sb.value = 0;
1845: gui.cmdline_sb.size = 1; /* No thumb */
1846: gui.cmdline_sb.max = 0;
1847: gui.cmdline_sb.top = Rows - cmdline_height;
1848: gui.cmdline_sb.height = cmdline_height;
1849: gui.cmdline_sb.status_height = lastwin->w_status_height;
1850: if (worst_update < SB_UPDATE_HEIGHT)
1851: worst_update = SB_UPDATE_HEIGHT;
1852: }
1853:
1854: /*
1855: * If we have just created the left or right scrollbar-box, then we need to
1856: * update the height of the command line scrollbar (it will already be
1857: * created).
1858: */
1859: if (gui.new_sb[SB_LEFT])
1860: {
1861: gui.cmdline_sb.update[SB_LEFT] = SB_UPDATE_HEIGHT;
1862: worst_update = SB_UPDATE_CREATE;
1863: gui.new_sb[SB_LEFT] = FALSE;
1864: }
1865: if (gui.new_sb[SB_RIGHT])
1866: {
1867: gui.cmdline_sb.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
1868: worst_update = SB_UPDATE_CREATE;
1869: gui.new_sb[SB_RIGHT] = FALSE;
1870: }
1871:
1872: if (worst_update != SB_UPDATE_NOTHING)
1873: {
1874: if (gui.which_scrollbars[SB_LEFT] && gui.dragged_sb != SB_LEFT)
1875: gui_mch_update_scrollbars(worst_update, SB_LEFT);
1876: if (gui.which_scrollbars[SB_RIGHT] && gui.dragged_sb != SB_RIGHT)
1877: gui_mch_update_scrollbars(worst_update, SB_RIGHT);
1878: }
1879: }
1880:
1881: /*
1882: * Scroll a window according to the values set in the globals current_scrollbar
1883: * and scrollbar_value. Return TRUE if the cursor in the current window moved
1884: * or FALSE otherwise.
1885: */
1886: int
1887: gui_do_scroll()
1888: {
1889: WIN *wp, *old_wp;
1890: int i;
1891: FPOS old_cursor;
1892:
1893: for (wp = firstwin, i = 0; i < current_scrollbar; i++)
1894: {
1895: if (wp == NULL)
1896: break;
1897: wp = wp->w_next;
1898: }
1899: if (wp != NULL)
1900: {
1901: old_cursor = curwin->w_cursor;
1902: old_wp = curwin;
1903: curwin = wp;
1904: curbuf = wp->w_buffer;
1905: i = (long)scrollbar_value - (long)wp->w_topline;
1906: if (i < 0)
1907: scrolldown(-i);
1908: else if (i > 0)
1909: scrollup(i);
1910: if (p_so)
1911: cursor_correct();
1912: coladvance(curwin->w_curswant);
1913:
1914: curwin = old_wp;
1915: curbuf = old_wp->w_buffer;
1916:
1917: if (wp == curwin)
1918: cursupdate(); /* fix window for 'so' */
1919: wp->w_redr_type = VALID;
1920: updateWindow(wp); /* update window, status line, and cmdline */
1921:
1922: return !equal(curwin->w_cursor, old_cursor);
1923: }
1924: else
1925: {
1926: /* Command-line scrollbar, unimplemented */
1927: return FALSE;
1928: }
1929: }
1930:
1931:
1932: /*
1933: * Horizontal scrollbar stuff:
1934: */
1935:
1936: static void
1937: gui_update_horiz_scrollbar()
1938: {
1939: int value, size, max;
1940:
1941: if (!gui.which_scrollbars[SB_BOTTOM])
1942: return;
1943:
1944: if (gui.dragged_sb == SB_BOTTOM)
1945: return;
1946:
1947: if (curwin->w_p_wrap && gui.prev_wrap)
1948: return;
1949:
1950: /*
1951: * It is possible for the cursor to be invalid if we're in the middle of
1952: * something (like changing files). If so, don't do anything for now.
1953: */
1954: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
1955: return;
1956:
1957: if (curwin->w_p_wrap)
1958: {
1959: value = 0;
1960: size = Columns;
1961: #ifdef SCROLL_PAST_END
1962: max = 0;
1963: #else
1964: max = Columns - 1;
1965: #endif
1966: }
1967: else
1968: {
1969: value = curwin->w_leftcol;
1970: size = Columns;
1971: #ifdef SCROLL_PAST_END
1972: max = gui_get_max_horiz_scroll();
1973: #else
1974: max = gui_get_max_horiz_scroll() + Columns - 1;
1975: #endif
1976: }
1977: gui_mch_update_horiz_scrollbar(value, size, max + 1);
1978: gui.prev_wrap = curwin->w_p_wrap;
1979: }
1980:
1981: /*
1982: * Determine the maximum value for scrolling right.
1983: */
1984: int
1985: gui_get_max_horiz_scroll()
1986: {
1987: int max = 0;
1988: char_u *p;
1989:
1990: p = ml_get_curline();
1991: if (p[0] != NUL)
1992: while (p[1] != NUL) /* Don't count last character */
1993: max += chartabsize(*p++, (colnr_t)max);
1994: return max;
1995: }
1996:
1997: /*
1998: * Do a horizontal scroll. Return TRUE if the cursor moved, or FALSE otherwise
1999: */
2000: int
2001: gui_do_horiz_scroll()
2002: {
2003: char_u *p;
2004: int i;
2005: int vcol;
2006: int ret_val = FALSE;
1.2 downsj 2007: int width;
1.1 downsj 2008:
2009: /* no wrapping, no scrolling */
2010: if (curwin->w_p_wrap)
2011: return FALSE;
2012:
2013: curwin->w_leftcol = scrollbar_value;
2014:
1.2 downsj 2015: width = Columns;
2016: if (curwin->w_p_nu) /* 8 characters of window used by line number */
2017: width -= 8;
1.1 downsj 2018: i = 0;
2019: vcol = 0;
2020: p = ml_get_curline();
2021: while (p[i] && i < curwin->w_cursor.col && vcol < curwin->w_leftcol)
2022: vcol += chartabsize(p[i++], (colnr_t)vcol);
2023: if (vcol < curwin->w_leftcol)
2024: {
2025: /*
2026: * Cursor is on a character that is at least partly off the left hand
2027: * side of the screen.
2028: */
2029: while (p[i] && vcol < curwin->w_leftcol)
2030: vcol += chartabsize(p[i++], (colnr_t)vcol);
2031: curwin->w_cursor.col = i;
2032: curwin->w_set_curswant = TRUE;
2033: ret_val = TRUE;
2034: }
2035:
2036: while (p[i] && i <= curwin->w_cursor.col
1.2 downsj 2037: && vcol <= curwin->w_leftcol + width)
1.1 downsj 2038: vcol += chartabsize(p[i++], (colnr_t)vcol);
1.2 downsj 2039: if (vcol > curwin->w_leftcol + width)
1.1 downsj 2040: {
2041: /*
2042: * Cursor is on a character that is at least partly off the right hand
2043: * side of the screen.
2044: */
2045: if (i < 2)
2046: i = 0;
2047: else
2048: i -= 2;
2049: curwin->w_cursor.col = i;
2050: curwin->w_set_curswant = TRUE;
2051: ret_val = TRUE;
2052: }
2053: updateScreen(NOT_VALID);
2054: return ret_val;
2055: }