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