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