Implemented CoreText renderer
[MacVim.git] / src / MacVim / gui_macvim.m
blob085835eedb4e923be8f73ff7bce198cda3ebaecf
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  * See README.txt for an overview of the Vim source code.
9  */
11  * gui_macvim.m
12  *
13  * Hooks for the Vim gui code.  Mainly passes control on to MMBackend.
14  */
16 #import "MMBackend.h"
17 #import "MacVim.h"
18 #import "vim.h"
19 #import <Foundation/Foundation.h>
23 // NOTE: The default font is bundled with the application.
24 static NSString *MMDefaultFontName = @"DejaVu Sans Mono";
25 static int MMDefaultFontSize       = 12;
26 static int MMMinFontSize           = 6;
27 static int MMMaxFontSize           = 100;
30 static GuiFont gui_macvim_font_with_name(char_u *name);
31 static int specialKeyToNSKey(int key);
32 static int vimModMaskToEventModifierFlags(int mods);
34 NSArray *descriptor_for_menu(vimmenu_T *menu);
35 vimmenu_T *menu_for_descriptor(NSArray *desc);
39 // -- Initialization --------------------------------------------------------
41     void
42 macvim_early_init()
44     NSBundle *bundle = [NSBundle mainBundle];
45     if (bundle) {
46         // Set environment variables $VIM and $VIMRUNTIME
47         NSString *path = [[bundle resourcePath]
48                                         stringByAppendingPathComponent:@"vim"];
49         vim_setenv((char_u*)"VIM", (char_u*)[path UTF8String]);
51         path = [path stringByAppendingPathComponent:@"runtime"];
52         vim_setenv((char_u*)"VIMRUNTIME", (char_u*)[path UTF8String]);
53     }
55 #if 0   // NOTE: setlocale(LC_ALL, "") seems to work after a restart so this is
56         // not necessary.  The locale used depends on what "Region" is set
57         // inside the "Formats" tab of the "International" System Preferences
58         // pane.
59     // Try to ensure that the locale is set to match that used by NSBundle to
60     // load localized resources.  If there is a mismatch e.g. between the
61     // MacVim menu and other menus, then this code needs to change (nb. the
62     // MacVim menu is set up inside a nib file so the locale used for it is
63     // chosen by NSBundle and the other menus are set up by Vim so their locale
64     // matches whatever we set here).
65     NSLocale *loc = [NSLocale currentLocale];
66     if (loc) {
67         NSString *s = [NSString stringWithFormat:@"%@_%@.UTF-8",
68                                     [loc objectForKey:NSLocaleLanguageCode],
69                                     [loc objectForKey:NSLocaleCountryCode]];
70         setlocale(LC_ALL, [s UTF8String]);
71         fprintf(stderr, "locale=%s\n", [s UTF8String]);
72         fflush(stderr);
73     }
74 #endif
79  * Parse the GUI related command-line arguments.  Any arguments used are
80  * deleted from argv, and *argc is decremented accordingly.  This is called
81  * when vim is started, whether or not the GUI has been started.
82  * NOTE: This function will be called twice if the Vim process forks.
83  */
84     void
85 gui_mch_prepare(int *argc, char **argv)
87     int i;
88     for (i = 0; i < *argc; ++i) {
89         if (strncmp(argv[i], "--mmwaitforack", 14) == 0) {
90             [[MMBackend sharedInstance] setWaitForAck:YES];
91             --*argc;
92             if (*argc > i)
93                 mch_memmove(&argv[i], &argv[i+1], (*argc-i) * sizeof(char*));
94             break;
95         }
96     }
98 #ifdef FEAT_NETBEANS_INTG
99     for (i = 0; i < *argc; ++i) {
100         if (strncmp(argv[i], "-nb", 3) == 0) {
101             usingNetbeans++;
102             netbeansArg = argv[i];
103             --*argc;
104             if (*argc > i)
105                 mch_memmove(&argv[i], &argv[i+1], (*argc-i) * sizeof(char*));
106             break;
107         }
108     }
109 #endif
113 /* Called directly after forking (even if we didn't fork). */
114     void
115 gui_macvim_after_fork_init()
117     ASLInit();
118     ASLogDebug(@"");
120     // Restore autosaved rows & columns
121     CFIndex rows, cols;
122     Boolean rowsValid, colsValid;
123     rows = CFPreferencesGetAppIntegerValue((CFStringRef)MMAutosaveRowsKey,
124                                         kCFPreferencesCurrentApplication,
125                                         &rowsValid);
126     cols = CFPreferencesGetAppIntegerValue((CFStringRef)MMAutosaveColumnsKey,
127                                         kCFPreferencesCurrentApplication,
128                                         &colsValid);
129     if (rowsValid && colsValid
130             && (rows > 4 && rows < 1000 && cols > 29 && cols < 4000)) {
131         gui.num_rows = rows;
132         gui.num_cols = cols;
133     }
138  * Check if the GUI can be started.  Called before gvimrc is sourced.
139  * Return OK or FAIL.
140  */
141     int
142 gui_mch_init_check(void)
144     return OK;
149  * Initialise the GUI.  Create all the windows, set up all the call-backs etc.
150  * Returns OK for success, FAIL when the GUI can't be started.
151  */
152     int
153 gui_mch_init(void)
155     ASLogDebug(@"");
157     if (![[MMBackend sharedInstance] checkin]) {
158         // TODO: Kill the process if there is no terminal to fall back on,
159         // otherwise the process will run outputting to the console.
160         return FAIL;
161     }
163     // Force 'termencoding' to utf-8 (changes to 'tenc' are disallowed in
164     // 'option.c', so that ':set termencoding=...' is impossible).
165     set_option_value((char_u *)"termencoding", 0L, (char_u *)"utf-8", 0);
167     // Set values so that pixels and characters are in one-to-one
168     // correspondence (assuming all characters have the same dimensions).
169     gui.scrollbar_width = gui.scrollbar_height = 0;
171     gui.char_height = 1;
172     gui.char_width = 1;
173     gui.char_ascent = 0;
175     gui_mch_def_colors();
177     [[MMBackend sharedInstance]
178         setDefaultColorsBackground:gui.back_pixel foreground:gui.norm_pixel];
179     [[MMBackend sharedInstance] setBackgroundColor:gui.back_pixel];
180     [[MMBackend sharedInstance] setForegroundColor:gui.norm_pixel];
182     // NOTE: If this call is left out the cursor is opaque.
183     highlight_gui_started();
185     // Ensure 'linespace' option is passed along to MacVim in case it was set
186     // in [g]vimrc.
187     gui_mch_adjust_charheight();
189     return OK;
194     void
195 gui_mch_exit(int rc)
197     ASLogDebug(@"rc=%d", rc);
199     [[MMBackend sharedInstance] exit];
204  * Open the GUI window which was created by a call to gui_mch_init().
205  */
206     int
207 gui_mch_open(void)
209     return [[MMBackend sharedInstance] openGUIWindow];
213 // -- Updating --------------------------------------------------------------
217  * Catch up with any queued X events.  This may put keyboard input into the
218  * input buffer, call resize call-backs, trigger timers etc.  If there is
219  * nothing in the X event queue (& no timers pending), then we return
220  * immediately.
221  */
222     void
223 gui_mch_update(void)
225     // This function is called extremely often.  It is tempting to do nothing
226     // here to avoid reduced frame-rates but then it would not be possible to
227     // interrupt Vim by presssing Ctrl-C during lengthy operations (e.g. after
228     // entering "10gs" it would not be possible to bring Vim out of the 10 s
229     // sleep prematurely).  As a compromise we check for Ctrl-C only once per
230     // second.  Note that Cmd-. sends SIGINT so it has higher success rate at
231     // interrupting Vim.
232     static CFAbsoluteTime lastTime = 0;
234     CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
235     if (nowTime - lastTime > 1.0) {
236         [[MMBackend sharedInstance] update];
237         lastTime = nowTime;
238     }
242 /* Flush any output to the screen */
243     void
244 gui_mch_flush(void)
246     // This function is called way too often to be useful as a hint for
247     // flushing.  If we were to flush every time it was called the screen would
248     // flicker.
252     void
253 gui_macvim_flush(void)
255     // This function counts how many times it is called and only flushes the
256     // draw queue if called sufficiently often.  The first few times it is
257     // called it will flush often, but the more it is called the less likely is
258     // it that anything will be flushed.  (The counter resets itself if the
259     // function isn't called for a second.)
260     //
261     // NOTE: Should only be used in loops where it is impossible to know how
262     // often Vim needs to flush.  It was written to handle output from external
263     // commands (see mch_call_shell() in os_unix.c).
265     static CFAbsoluteTime lastTime = 0;
266     static int delay = 1;
267     static int counter = 0;
268     static int scrolls = 0;
270     CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
271     CFAbsoluteTime delta = nowTime - lastTime;
272     if (delta > 1.0)
273         delay = 1;
275     // We assume that each call corresponds roughly to one line of output.
276     // When one page has scrolled by we increase the delay before the next
277     // flush.
278     if (++scrolls > gui.num_rows) {
279         delay <<= 1;
280         if (delay > 2048)
281             delay = 2048;
282         scrolls = 0;
283     }
285     if (++counter > delay) {
286         gui_macvim_force_flush();
287         counter = 0;
288     }
290     lastTime = nowTime;
294 /* Force flush output to MacVim.  Do not call this method unless absolutely
295  * necessary. */
296     void
297 gui_macvim_force_flush(void)
299     [[MMBackend sharedInstance] flushQueue:YES];
304  * GUI input routine called by gui_wait_for_chars().  Waits for a character
305  * from the keyboard.
306  *  wtime == -1     Wait forever.
307  *  wtime == 0      This should never happen.
308  *  wtime > 0       Wait wtime milliseconds for a character.
309  * Returns OK if a character was found to be available within the given time,
310  * or FAIL otherwise.
311  */
312     int
313 gui_mch_wait_for_chars(int wtime)
315     // NOTE! In all likelihood Vim will take a nap when waitForInput: is
316     // called, so force a flush of the command queue here.
317     [[MMBackend sharedInstance] flushQueue:YES];
319     return [[MMBackend sharedInstance] waitForInput:wtime];
323 // -- Drawing ---------------------------------------------------------------
327  * Clear the whole text window.
328  */
329     void
330 gui_mch_clear_all(void)
332     [[MMBackend sharedInstance] clearAll];
337  * Clear a rectangular region of the screen from text pos (row1, col1) to
338  * (row2, col2) inclusive.
339  */
340     void
341 gui_mch_clear_block(int row1, int col1, int row2, int col2)
343     [[MMBackend sharedInstance] clearBlockFromRow:row1 column:col1
344                                                     toRow:row2 column:col2];
349  * Delete the given number of lines from the given row, scrolling up any
350  * text further down within the scroll region.
351  */
352     void
353 gui_mch_delete_lines(int row, int num_lines)
355     [[MMBackend sharedInstance] deleteLinesFromRow:row count:num_lines
356             scrollBottom:gui.scroll_region_bot
357                     left:gui.scroll_region_left
358                    right:gui.scroll_region_right];
362     void
363 gui_mch_draw_string(int row, int col, char_u *s, int len, int cells, int flags)
365 #ifdef FEAT_MBYTE
366     char_u *conv_str = NULL;
367     if (output_conv.vc_type != CONV_NONE) {
368         conv_str = string_convert(&output_conv, s, &len);
369         if (conv_str)
370             s = conv_str;
371     }
372 #endif
374     [[MMBackend sharedInstance] drawString:s
375                                     length:len
376                                        row:row
377                                     column:col
378                                      cells:cells
379                                      flags:flags];
380 #ifdef FEAT_MBYTE
381     if (conv_str)
382         vim_free(conv_str);
383 #endif
387     int
388 gui_macvim_draw_string(int row, int col, char_u *s, int len, int flags)
390     int c, cn, cl, i;
391     int start = 0;
392     int endcol = col;
393     int startcol = col;
394     BOOL wide = NO;
395     MMBackend *backend = [MMBackend sharedInstance];
396 #ifdef FEAT_MBYTE
397     char_u *conv_str = NULL;
399     if (output_conv.vc_type != CONV_NONE) {
400         conv_str = string_convert(&output_conv, s, &len);
401         if (conv_str)
402             s = conv_str;
403     }
404 #endif
406     // Loop over each character and output text when it changes from normal to
407     // wide and vice versa.
408     for (i = 0; i < len; i += cl) {
409         c = utf_ptr2char(s + i);
410         cn = utf_char2cells(c);
411         cl = utf_ptr2len(s + i);
412         if (0 == cl)
413             len = i;    // len must be wrong (shouldn't happen)
415         if (!utf_iscomposing(c)) {
416             if ((cn > 1 && !wide) || (cn <= 1 && wide)) {
417                 // Changed from normal to wide or vice versa.
418                 [backend drawString:(char*)(s+start) length:i-start
419                                    row:row column:startcol
420                                  cells:endcol-startcol
421                                  flags:(wide ? flags|DRAW_WIDE : flags)];
423                 start = i;
424                 startcol = endcol;
425             }
427             wide = cn > 1;
428             endcol += cn;
429         }
430     }
432     // Output remaining characters.
433     [backend drawString:(char*)(s+start) length:len-start
434                     row:row column:startcol cells:endcol-startcol
435                   flags:(wide ? flags|DRAW_WIDE : flags)];
437 #ifdef FEAT_MBYTE
438     if (conv_str)
439         vim_free(conv_str);
440 #endif
442     return endcol - col;
447  * Insert the given number of lines before the given row, scrolling down any
448  * following text within the scroll region.
449  */
450     void
451 gui_mch_insert_lines(int row, int num_lines)
453     [[MMBackend sharedInstance] insertLinesFromRow:row count:num_lines
454             scrollBottom:gui.scroll_region_bot
455                     left:gui.scroll_region_left
456                    right:gui.scroll_region_right];
461  * Set the current text foreground color.
462  */
463     void
464 gui_mch_set_fg_color(guicolor_T color)
466     [[MMBackend sharedInstance] setForegroundColor:color];
471  * Set the current text background color.
472  */
473     void
474 gui_mch_set_bg_color(guicolor_T color)
476     [[MMBackend sharedInstance] setBackgroundColor:color];
481  * Set the current text special color (used for underlines).
482  */
483     void
484 gui_mch_set_sp_color(guicolor_T color)
486     [[MMBackend sharedInstance] setSpecialColor:color];
491  * Set default colors.
492  */
493     void
494 gui_mch_def_colors()
496     MMBackend *backend = [MMBackend sharedInstance];
498     // The default colors are taken from system values
499     gui.def_norm_pixel = gui.norm_pixel = 
500         [backend lookupColorWithKey:@"MacTextColor"];
501     gui.def_back_pixel = gui.back_pixel = 
502         [backend lookupColorWithKey:@"MacTextBackgroundColor"];
507  * Called when the foreground or background color has been changed.
508  */
509     void
510 gui_mch_new_colors(void)
512     gui.def_back_pixel = gui.back_pixel;
513     gui.def_norm_pixel = gui.norm_pixel;
515     ASLogDebug(@"back=%x norm=%x", gui.def_back_pixel, gui.def_norm_pixel);
517     [[MMBackend sharedInstance]
518         setDefaultColorsBackground:gui.def_back_pixel
519                         foreground:gui.def_norm_pixel];
523  * Invert a rectangle from row r, column c, for nr rows and nc columns.
524  */
525     void
526 gui_mch_invert_rectangle(int r, int c, int nr, int nc, int invert)
528     [[MMBackend sharedInstance] drawInvertedRectAtRow:r column:c numRows:nr
529             numColumns:nc invert:invert];
534 // -- Tabline ---------------------------------------------------------------
538  * Set the current tab to "nr".  First tab is 1.
539  */
540     void
541 gui_mch_set_curtab(int nr)
543     [[MMBackend sharedInstance] selectTab:nr];
548  * Return TRUE when tabline is displayed.
549  */
550     int
551 gui_mch_showing_tabline(void)
553     return [[MMBackend sharedInstance] tabBarVisible];
557  * Update the labels of the tabline.
558  */
559     void
560 gui_mch_update_tabline(void)
562     [[MMBackend sharedInstance] updateTabBar];
566  * Show or hide the tabline.
567  */
568     void
569 gui_mch_show_tabline(int showit)
571     [[MMBackend sharedInstance] showTabBar:showit];
575 // -- Clipboard -------------------------------------------------------------
578     void
579 clip_mch_lose_selection(VimClipboard *cbd)
584     int
585 clip_mch_own_selection(VimClipboard *cbd)
587     return 0;
591     void
592 clip_mch_request_selection(VimClipboard *cbd)
594     NSPasteboard *pb = [NSPasteboard generalPasteboard];
595     NSArray *supportedTypes = [NSArray arrayWithObjects:VimPBoardType,
596             NSStringPboardType, nil];
597     NSString *bestType = [pb availableTypeFromArray:supportedTypes];
598     if (!bestType) return;
600     int motion_type = MCHAR;
601     NSString *string = nil;
603     if ([bestType isEqual:VimPBoardType]) {
604         // This type should consist of an array with two objects:
605         //   1. motion type (NSNumber)
606         //   2. text (NSString)
607         // If this is not the case we fall back on using NSStringPboardType.
608         id plist = [pb propertyListForType:VimPBoardType];
609         if ([plist isKindOfClass:[NSArray class]] && [plist count] == 2) {
610             id obj = [plist objectAtIndex:1];
611             if ([obj isKindOfClass:[NSString class]]) {
612                 motion_type = [[plist objectAtIndex:0] intValue];
613                 string = obj;
614             }
615         }
616     }
618     if (!string) {
619         // Use NSStringPboardType.  The motion type is set to line-wise if the
620         // string contains at least one EOL character, otherwise it is set to
621         // character-wise (block-wise is never used).
622         NSMutableString *mstring =
623                 [[pb stringForType:NSStringPboardType] mutableCopy];
624         if (!mstring) return;
626         // Replace unrecognized end-of-line sequences with \x0a (line feed).
627         NSRange range = { 0, [mstring length] };
628         unsigned n = [mstring replaceOccurrencesOfString:@"\x0d\x0a"
629                                              withString:@"\x0a" options:0
630                                                   range:range];
631         if (0 == n) {
632             n = [mstring replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
633                                            options:0 range:range];
634         }
635         
636         // Scan for newline character to decide whether the string should be
637         // pasted line-wise or character-wise.
638         motion_type = MCHAR;
639         if (0 < n || NSNotFound != [mstring rangeOfString:@"\n"].location)
640             motion_type = MLINE;
642         string = mstring;
643     }
645     if (!(MCHAR == motion_type || MLINE == motion_type || MBLOCK == motion_type
646             || MAUTO == motion_type))
647         motion_type = MCHAR;
649     char_u *str = (char_u*)[string UTF8String];
650     int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
652 #ifdef FEAT_MBYTE
653     if (input_conv.vc_type != CONV_NONE)
654         str = string_convert(&input_conv, str, &len);
655 #endif
657     if (str)
658         clip_yank_selection(motion_type, str, len, cbd);
660 #ifdef FEAT_MBYTE
661     if (input_conv.vc_type != CONV_NONE)
662         vim_free(str);
663 #endif
668  * Send the current selection to the clipboard.
669  */
670     void
671 clip_mch_set_selection(VimClipboard *cbd)
673     // If the '*' register isn't already filled in, fill it in now.
674     cbd->owned = TRUE;
675     clip_get_selection(cbd);
676     cbd->owned = FALSE;
677     
678     // Get the text to put on the pasteboard.
679     long_u llen = 0; char_u *str = 0;
680     int motion_type = clip_convert_selection(&str, &llen, cbd);
681     if (motion_type < 0)
682         return;
684     // TODO: Avoid overflow.
685     int len = (int)llen;
686 #ifdef FEAT_MBYTE
687     if (output_conv.vc_type != CONV_NONE) {
688         char_u *conv_str = string_convert(&output_conv, str, &len);
689         if (conv_str) {
690             vim_free(str);
691             str = conv_str;
692         }
693     }
694 #endif
696     if (len > 0) {
697         NSString *string = [[NSString alloc]
698             initWithBytes:str length:len encoding:NSUTF8StringEncoding];
700         // See clip_mch_request_selection() for info on pasteboard types.
701         NSPasteboard *pb = [NSPasteboard generalPasteboard];
702         NSArray *supportedTypes = [NSArray arrayWithObjects:VimPBoardType,
703                 NSStringPboardType, nil];
704         [pb declareTypes:supportedTypes owner:nil];
706         NSNumber *motion = [NSNumber numberWithInt:motion_type];
707         NSArray *plist = [NSArray arrayWithObjects:motion, string, nil];
708         [pb setPropertyList:plist forType:VimPBoardType];
710         [pb setString:string forType:NSStringPboardType];
711         
712         [string release];
713     }
715     vim_free(str);
719 // -- Menu ------------------------------------------------------------------
723  * A menu descriptor represents the "address" of a menu as an array of strings.
724  * E.g. the menu "File->Close" has descriptor { "File", "Close" }.
725  */
726     NSArray *
727 descriptor_for_menu(vimmenu_T *menu)
729     if (!menu) return nil;
731     NSMutableArray *desc = [NSMutableArray array];
732     while (menu) {
733         NSString *name = [NSString stringWithVimString:menu->dname];
734         [desc insertObject:name atIndex:0];
735         menu = menu->parent;
736     }
738     return desc;
741     vimmenu_T *
742 menu_for_descriptor(NSArray *desc)
744     if (!(desc && [desc count] > 0)) return NULL;
746     vimmenu_T *menu = root_menu;
747     int i, count = [desc count];
749     for (i = 0; i < count; ++i) {
750         NSString *component = [desc objectAtIndex:i];
751         while (menu) {
752             NSString *name = [NSString stringWithVimString:menu->dname];
753             if ([component isEqual:name]) {
754                 if (i+1 == count)
755                     return menu;    // Matched all components, so return menu
756                 menu = menu->children;
757                 break;
758             }
759             menu = menu->next;
760         }
761     }
763     return NULL;
767  * Add a submenu to the menu bar, toolbar, or a popup menu.
768  */
769     void
770 gui_mch_add_menu(vimmenu_T *menu, int idx)
772     NSArray *desc = descriptor_for_menu(menu);
773     [[MMBackend sharedInstance] queueMessage:AddMenuMsgID properties:
774         [NSDictionary dictionaryWithObjectsAndKeys:
775             desc, @"descriptor",
776             [NSNumber numberWithInt:idx], @"index",
777             nil]];
781 // Taken from gui_gtk.c (slightly modified)
782     static int
783 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
785     expand_env(iconfile, dest, MAXPATHL);
787     if (mch_isFullName(dest))
788         return vim_fexists(dest);
790     static const char   suffixes[][4] = {"png", "bmp"};
791     char_u              buf[MAXPATHL];
792     unsigned int        i;
794     for (i = 0; i < sizeof(suffixes)/sizeof(suffixes[0]); ++i)
795         if (gui_find_bitmap(dest, buf, (char *)suffixes[i]) == OK) {
796             STRCPY(dest, buf);
797             return TRUE;
798         }
800     return FALSE;
805  * Add a menu item to a menu
806  */
807     void
808 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
810     char_u *tip = menu->strings[MENU_INDEX_TIP]
811             ? menu->strings[MENU_INDEX_TIP] : menu->actext;
812     NSArray *desc = descriptor_for_menu(menu);
813     NSString *keyEquivalent = menu->mac_key
814         ? [NSString stringWithFormat:@"%C", specialKeyToNSKey(menu->mac_key)]
815         : [NSString string];
816     int modifierMask = vimModMaskToEventModifierFlags(menu->mac_mods);
817     char_u *icon = NULL;
819     if (menu_is_toolbar(menu->parent->name)) {
820         char_u fname[MAXPATHL];
822         // Try to use the icon=.. argument
823         if (menu->iconfile && lookup_menu_iconfile(menu->iconfile, fname))
824             icon = fname;
826         // If not found and not builtin specified try using the menu name
827         if (!icon && !menu->icon_builtin
828                                     && lookup_menu_iconfile(menu->name, fname))
829             icon = fname;
831         // Still no icon found, try using a builtin icon.  (If this also fails,
832         // then a warning icon will be displayed).
833         if (!icon)
834             icon = lookup_toolbar_item(menu->iconidx);
835     }
837     [[MMBackend sharedInstance] queueMessage:AddMenuItemMsgID properties:
838         [NSDictionary dictionaryWithObjectsAndKeys:
839             desc, @"descriptor",
840             [NSNumber numberWithInt:idx], @"index",
841             [NSString stringWithVimString:tip], @"tip",
842             [NSString stringWithVimString:icon], @"icon",
843             keyEquivalent, @"keyEquivalent",
844             [NSNumber numberWithInt:modifierMask], @"modifierMask",
845             [NSString stringWithVimString:menu->mac_action], @"action",
846             [NSNumber numberWithBool:menu->mac_alternate], @"isAlternate",
847             nil]];
852  * Destroy the machine specific menu widget.
853  */
854     void
855 gui_mch_destroy_menu(vimmenu_T *menu)
857     NSArray *desc = descriptor_for_menu(menu);
858     [[MMBackend sharedInstance] queueMessage:RemoveMenuItemMsgID properties:
859         [NSDictionary dictionaryWithObject:desc forKey:@"descriptor"]];
864  * Make a menu either grey or not grey.
865  */
866     void
867 gui_mch_menu_grey(vimmenu_T *menu, int grey)
869     /* Only update menu if the 'grey' state has changed to avoid having to pass
870      * lots of unnecessary data to MacVim.  (Skipping this test makes MacVim
871      * pause noticably on mode changes. */
872     NSArray *desc = descriptor_for_menu(menu);
873     if (menu->was_grey == grey)
874         return;
876     menu->was_grey = grey;
878     [[MMBackend sharedInstance] queueMessage:EnableMenuItemMsgID properties:
879         [NSDictionary dictionaryWithObjectsAndKeys:
880             desc, @"descriptor",
881             [NSNumber numberWithInt:!grey], @"enable",
882             nil]];
887  * Make menu item hidden or not hidden
888  */
889     void
890 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
892     // HACK! There is no (obvious) way to hide a menu item, so simply
893     // enable/disable it instead.
894     gui_mch_menu_grey(menu, hidden);
899  * This is called when user right clicks.
900  */
901     void
902 gui_mch_show_popupmenu(vimmenu_T *menu)
904     NSArray *desc = descriptor_for_menu(menu);
905     [[MMBackend sharedInstance] queueMessage:ShowPopupMenuMsgID properties:
906         [NSDictionary dictionaryWithObject:desc forKey:@"descriptor"]];
911  * This is called when a :popup command is executed.
912  */
913     void
914 gui_make_popup(char_u *path_name, int mouse_pos)
916     vimmenu_T *menu = gui_find_menu(path_name);
917     if (!(menu && menu->children)) return;
919     NSArray *desc = descriptor_for_menu(menu);
920     NSDictionary *p = (mouse_pos || NULL == curwin)
921         ? [NSDictionary dictionaryWithObject:desc forKey:@"descriptor"]
922         : [NSDictionary dictionaryWithObjectsAndKeys:
923             desc, @"descriptor",
924             [NSNumber numberWithInt:curwin->w_wrow], @"row",
925             [NSNumber numberWithInt:curwin->w_wcol], @"column",
926             nil];
928     [[MMBackend sharedInstance] queueMessage:ShowPopupMenuMsgID properties:p];
933  * This is called after setting all the menus to grey/hidden or not.
934  */
935     void
936 gui_mch_draw_menubar(void)
938     // The (main) menu draws itself in Mac OS X.
942     void
943 gui_mch_enable_menu(int flag)
945     // The (main) menu is always enabled in Mac OS X.
949 #if 0
950     void
951 gui_mch_set_menu_pos(int x, int y, int w, int h)
953     // The (main) menu cannot be moved in Mac OS X.
955 #endif
958     void
959 gui_mch_show_toolbar(int showit)
961     int flags = 0;
962     if (toolbar_flags & TOOLBAR_TEXT) flags |= ToolbarLabelFlag;
963     if (toolbar_flags & TOOLBAR_ICONS) flags |= ToolbarIconFlag;
964     if (tbis_flags & (TBIS_MEDIUM|TBIS_LARGE)) flags |= ToolbarSizeRegularFlag;
966     [[MMBackend sharedInstance] showToolbar:showit flags:flags];
972 // -- Fonts -----------------------------------------------------------------
976  * If a font is not going to be used, free its structure.
977  */
978     void
979 gui_mch_free_font(font)
980     GuiFont     font;
982     if (font != NOFONT) {
983         ASLogDebug(@"font=0x%x", font);
984         [(id)font release];
985     }
989     GuiFont
990 gui_mch_retain_font(GuiFont font)
992     return (GuiFont)[(id)font retain];
997  * Get a font structure for highlighting.
998  */
999     GuiFont
1000 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
1002     ASLogDebug(@"name='%s' giveErrorIfMissing=%d", name, giveErrorIfMissing);
1004     GuiFont font = gui_macvim_font_with_name(name);
1005     if (font != NOFONT)
1006         return font;
1008     if (giveErrorIfMissing)
1009         EMSG2(_(e_font), name);
1011     return NOFONT;
1015 #if defined(FEAT_EVAL) || defined(PROTO)
1017  * Return the name of font "font" in allocated memory.
1018  * TODO: use 'font' instead of 'name'?
1019  */
1020     char_u *
1021 gui_mch_get_fontname(GuiFont font, char_u *name)
1023     return name ? vim_strsave(name) : NULL;
1025 #endif
1029  * Initialise vim to use the font with the given name.  Return FAIL if the font
1030  * could not be loaded, OK otherwise.
1031  */
1032     int
1033 gui_mch_init_font(char_u *font_name, int fontset)
1035     ASLogDebug(@"font_name='%s' fontset=%d", font_name, fontset);
1037     if (font_name && STRCMP(font_name, "*") == 0) {
1038         // :set gfn=* shows the font panel.
1039         do_cmdline_cmd((char_u*)":macaction orderFrontFontPanel:");
1040         return FAIL;
1041     }
1043     GuiFont font = gui_macvim_font_with_name(font_name);
1044     if (font == NOFONT)
1045         return FAIL;
1047     gui_mch_free_font(gui.norm_font);
1048     gui.norm_font = font;
1050     // NOTE: MacVim keeps separate track of the normal and wide fonts.
1051     // Unless the user changes 'guifontwide' manually, they are based on
1052     // the same (normal) font.  Also note that each time the normal font is
1053     // set, the advancement may change so the wide font needs to be updated
1054     // as well (so that it is always twice the width of the normal font).
1055     [[MMBackend sharedInstance] setFont:font wide:NO];
1056     [[MMBackend sharedInstance] setFont:(NOFONT != gui.wide_font ? gui.wide_font
1057                                                                  : font)
1058                                    wide:YES];
1060     return OK;
1065  * Set the current text font.
1066  */
1067     void
1068 gui_mch_set_font(GuiFont font)
1070     // Font selection is done inside MacVim...nothing here to do.
1075  * Return GuiFont in allocated memory.  The caller must free it using
1076  * gui_mch_free_font().
1077  */
1078     GuiFont
1079 gui_macvim_font_with_name(char_u *name)
1081     if (!name)
1082         return (GuiFont)[[NSString alloc] initWithFormat:@"%@:%d",
1083                                         MMDefaultFontName, MMDefaultFontSize];
1085     NSString *fontName = [NSString stringWithVimString:name];
1086     int size = MMDefaultFontSize;
1087     BOOL parseFailed = NO;
1089     NSArray *components = [fontName componentsSeparatedByString:@":"];
1090     if ([components count] == 2) {
1091         NSString *sizeString = [components lastObject];
1092         if ([sizeString length] > 0
1093                 && [sizeString characterAtIndex:0] == 'h') {
1094             sizeString = [sizeString substringFromIndex:1];
1095             if ([sizeString length] > 0) {
1096                 size = (int)round([sizeString floatValue]);
1097                 fontName = [components objectAtIndex:0];
1098             }
1099         } else {
1100             parseFailed = YES;
1101         }
1102     } else if ([components count] > 2) {
1103         parseFailed = YES;
1104     }
1106     if (!parseFailed) {
1107         // Replace underscores with spaces.
1108         fontName = [[fontName componentsSeparatedByString:@"_"]
1109                                  componentsJoinedByString:@" "];
1110     }
1112     if (!parseFailed && [fontName length] > 0) {
1113         if (size < MMMinFontSize) size = MMMinFontSize;
1114         if (size > MMMaxFontSize) size = MMMaxFontSize;
1116         // If the default font is requested we don't check if NSFont can load
1117         // it since the font most likely isn't loaded anyway (it may only be
1118         // available to the MacVim binary).  If it is not the default font we
1119         // ask NSFont if it can load it.
1120         if ([fontName isEqualToString:MMDefaultFontName]
1121                 || [NSFont fontWithName:fontName size:size])
1122             return [[NSString alloc] initWithFormat:@"%@:%d", fontName, size];
1123     }
1125     return NOFONT;
1128 // -- Scrollbars ------------------------------------------------------------
1130 // NOTE: Even though scrollbar identifiers are 'long' we tacitly assume that
1131 // they only use 32 bits (in particular when compiling for 64 bit).  This is
1132 // justified since identifiers are generated from a 32 bit counter in
1133 // gui_create_scrollbar().  However if that code changes we may be in trouble
1134 // (if ever that many scrollbars are allocated...).  The reason behind this is
1135 // that we pass scrollbar identifers over process boundaries so the width of
1136 // the variable needs to be fixed (and why fix at 64 bit when only 32 are
1137 // really used?).
1139     void
1140 gui_mch_create_scrollbar(
1141         scrollbar_T *sb,
1142         int orient)     /* SBAR_VERT or SBAR_HORIZ */
1144     [[MMBackend sharedInstance] 
1145             createScrollbarWithIdentifier:(int32_t)sb->ident type:sb->type];
1149     void
1150 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1152     [[MMBackend sharedInstance] 
1153             destroyScrollbarWithIdentifier:(int32_t)sb->ident];
1157     void
1158 gui_mch_enable_scrollbar(
1159         scrollbar_T     *sb,
1160         int             flag)
1162     [[MMBackend sharedInstance] 
1163             showScrollbarWithIdentifier:(int32_t)sb->ident state:flag];
1167     void
1168 gui_mch_set_scrollbar_pos(
1169         scrollbar_T *sb,
1170         int x,
1171         int y,
1172         int w,
1173         int h)
1175     int pos = y;
1176     int len = h;
1177     if (SBAR_BOTTOM == sb->type) {
1178         pos = x;
1179         len = w; 
1180     }
1182     [[MMBackend sharedInstance] 
1183             setScrollbarPosition:pos length:len identifier:(int32_t)sb->ident];
1187     void
1188 gui_mch_set_scrollbar_thumb(
1189         scrollbar_T *sb,
1190         long val,
1191         long size,
1192         long max)
1194     [[MMBackend sharedInstance] 
1195             setScrollbarThumbValue:val
1196                               size:size
1197                                max:max
1198                         identifier:(int32_t)sb->ident];
1202 // -- Cursor ----------------------------------------------------------------
1206  * Draw a cursor without focus.
1207  */
1208     void
1209 gui_mch_draw_hollow_cursor(guicolor_T color)
1211     return [[MMBackend sharedInstance]
1212         drawCursorAtRow:gui.row column:gui.col shape:MMInsertionPointHollow
1213                fraction:100 color:color];
1218  * Draw part of a cursor, only w pixels wide, and h pixels high.
1219  */
1220     void
1221 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
1223     // HACK!  'w' and 'h' are always 1 since we do not tell Vim about the exact
1224     // font dimensions.  Thus these parameters are useless.  Instead we look at
1225     // the shape_table to determine the shape and size of the cursor (just like
1226     // gui_update_cursor() does).
1228 #ifdef FEAT_RIGHTLEFT
1229     // If 'rl' is set the insert mode cursor must be drawn on the right-hand
1230     // side of a text cell.
1231     int rl = curwin ? curwin->w_p_rl : FALSE;
1232 #else
1233     int rl = FALSE;
1234 #endif
1235     int idx = get_shape_idx(FALSE);
1236     int shape = MMInsertionPointBlock;
1237     switch (shape_table[idx].shape) {
1238         case SHAPE_HOR:
1239             shape = MMInsertionPointHorizontal;
1240             break;
1241         case SHAPE_VER:
1242             shape = rl ? MMInsertionPointVerticalRight
1243                        : MMInsertionPointVertical;
1244             break;
1245     }
1247     return [[MMBackend sharedInstance]
1248         drawCursorAtRow:gui.row column:gui.col shape:shape
1249                fraction:shape_table[idx].percentage color:color];
1254  * Cursor blink functions.
1256  * This is a simple state machine:
1257  * BLINK_NONE   not blinking at all
1258  * BLINK_OFF    blinking, cursor is not shown
1259  * BLINK_ON blinking, cursor is shown
1260  */
1261     void
1262 gui_mch_set_blinking(long wait, long on, long off)
1264     [[MMBackend sharedInstance] setBlinkWait:wait on:on off:off];
1269  * Start the cursor blinking.  If it was already blinking, this restarts the
1270  * waiting time and shows the cursor.
1271  */
1272     void
1273 gui_mch_start_blink(void)
1275     [[MMBackend sharedInstance] startBlink];
1280  * Stop the cursor blinking.  Show the cursor if it wasn't shown.
1281  */
1282     void
1283 gui_mch_stop_blink(void)
1285     [[MMBackend sharedInstance] stopBlink];
1289 // -- Mouse -----------------------------------------------------------------
1293  * Get current mouse coordinates in text window.
1294  */
1295     void
1296 gui_mch_getmouse(int *x, int *y)
1298     ASLogInfo(@"Not implemented!");
1302     void
1303 gui_mch_setmouse(int x, int y)
1305     ASLogInfo(@"Not implemented!");
1309     void
1310 mch_set_mouse_shape(int shape)
1312     [[MMBackend sharedInstance] setMouseShape:shape];
1318 // -- Input Method ----------------------------------------------------------
1320 #if defined(USE_IM_CONTROL)
1322     void
1323 im_set_position(int row, int col)
1325     // The pre-edit area is a popup window which is displayed by MMTextView.
1326     [[MMBackend sharedInstance] setPreEditRow:row column:col];
1330     void
1331 im_set_control(int enable)
1333     // Tell frontend whether it should notify us when the input method changes
1334     // or not (called when 'imd' is toggled).
1335     int msgid = enable ? EnableImControlMsgID : DisableImControlMsgID;
1336     [[MMBackend sharedInstance] queueMessage:msgid properties:nil];
1340     void
1341 im_set_active(int active)
1343     // Tell frontend to enable/disable IM (called e.g. when the mode changes).
1344     if (!p_imdisable) {
1345         int msgid = active ? ActivateKeyScriptMsgID : DeactivateKeyScriptMsgID;
1346         [[MMBackend sharedInstance] setImState:active];
1347         [[MMBackend sharedInstance] queueMessage:msgid properties:nil];
1348     }
1352     int
1353 im_get_status(void)
1355     return [[MMBackend sharedInstance] imState];
1358 #endif // defined(USE_IM_CONTROL)
1363 // -- Find & Replace dialog -------------------------------------------------
1365 #ifdef FIND_REPLACE_DIALOG
1367     static void
1368 macvim_find_and_replace(char_u *arg, BOOL replace)
1370     // TODO: Specialized dialog for find without replace?
1371     int wholeWord = FALSE;
1372     int matchCase = !p_ic;
1373     char_u *text  = get_find_dialog_text(arg, &wholeWord, &matchCase);
1375     int flags = 0;
1376     if (wholeWord) flags |= FRD_WHOLE_WORD;
1377     if (matchCase) flags |= FRD_MATCH_CASE;
1379     NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
1380             [NSString stringWithVimString:text],    @"text",
1381             [NSNumber numberWithInt:flags],         @"flags",
1382             nil];
1384     [[MMBackend sharedInstance] queueMessage:ShowFindReplaceDialogMsgID
1385                                   properties:args];
1388     void
1389 gui_mch_find_dialog(exarg_T *eap)
1391     macvim_find_and_replace(eap->arg, NO);
1394     void
1395 gui_mch_replace_dialog(exarg_T *eap)
1397     macvim_find_and_replace(eap->arg, YES);
1400 #endif // FIND_REPLACE_DIALOG
1405 // -- Unsorted --------------------------------------------------------------
1408     void
1409 ex_macaction(eap)
1410     exarg_T     *eap;
1412     if (!gui.in_use) {
1413         EMSG(_("E???: Command only available in GUI mode"));
1414         return;
1415     }
1417     char_u *arg = eap->arg;
1418 #ifdef FEAT_MBYTE
1419     arg = CONVERT_TO_UTF8(arg);
1420 #endif
1422     NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict];
1423     NSString *name = [NSString stringWithUTF8String:(char*)arg];
1424     if (actionDict && [actionDict objectForKey:name] != nil) {
1425         [[MMBackend sharedInstance] executeActionWithName:name];
1426     } else {
1427         EMSG2(_("E???: Invalid action: %s"), eap->arg);
1428     }
1430 #ifdef FEAT_MBYTE
1431     arg = CONVERT_TO_UTF8(arg);
1432 #endif
1437  * Adjust gui.char_height (after 'linespace' was changed).
1438  */
1439     int
1440 gui_mch_adjust_charheight(void)
1442     [[MMBackend sharedInstance] adjustLinespace:p_linespace];
1443     return OK;
1447     void
1448 gui_mch_beep(void)
1450     NSBeep();
1455 #ifdef FEAT_BROWSE
1457  * Pop open a file browser and return the file selected, in allocated memory,
1458  * or NULL if Cancel is hit.
1459  *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
1460  *  title   - Title message for the file browser dialog.
1461  *  dflt    - Default name of file.
1462  *  ext     - Default extension to be added to files without extensions.
1463  *  initdir - directory in which to open the browser (NULL = current dir)
1464  *  filter  - Filter for matched files to choose from.
1465  *  Has a format like this:
1466  *  "C Files (*.c)\0*.c\0"
1467  *  "All Files\0*.*\0\0"
1468  *  If these two strings were concatenated, then a choice of two file
1469  *  filters will be selectable to the user.  Then only matching files will
1470  *  be shown in the browser.  If NULL, the default allows all files.
1472  *  *NOTE* - the filter string must be terminated with TWO nulls.
1473  */
1474     char_u *
1475 gui_mch_browse(
1476     int saving,
1477     char_u *title,
1478     char_u *dflt,
1479     char_u *ext,
1480     char_u *initdir,
1481     char_u *filter)
1483     ASLogDebug(@"saving=%d title='%s' dflt='%s' ext='%s' initdir='%s' "
1484                "filter='%s'", saving, title, dflt, ext, initdir, filter);
1486     // Ensure no data is on the output queue before presenting the dialog.
1487     gui_macvim_force_flush();
1489     NSMutableDictionary *attr = [NSMutableDictionary
1490         dictionaryWithObject:[NSNumber numberWithBool:saving]
1491                       forKey:@"saving"];
1492     if (initdir)
1493         [attr setObject:[NSString stringWithVimString:initdir] forKey:@"dir"];
1495     char_u *s = (char_u*)[[MMBackend sharedInstance]
1496                             browseForFileWithAttributes:attr];
1498     return s;
1500 #endif /* FEAT_BROWSE */
1504     int
1505 gui_mch_dialog(
1506     int         type,
1507     char_u      *title,
1508     char_u      *message,
1509     char_u      *buttons,
1510     int         dfltbutton,
1511     char_u      *textfield)
1513     ASLogDebug(@"type=%d title='%s' message='%s' buttons='%s' dfltbutton=%d "
1514                "textfield='%s'", type, title, message, buttons, dfltbutton,
1515                textfield);
1517     // Ensure no data is on the output queue before presenting the dialog.
1518     gui_macvim_force_flush();
1520     int style = NSInformationalAlertStyle;
1521     if (VIM_WARNING == type) style = NSWarningAlertStyle;
1522     else if (VIM_ERROR == type) style = NSCriticalAlertStyle;
1524     NSMutableDictionary *attr = [NSMutableDictionary
1525                         dictionaryWithObject:[NSNumber numberWithInt:style]
1526                                       forKey:@"alertStyle"];
1528     if (buttons) {
1529         // 'buttons' is a string of '\n'-separated button titles 
1530         NSString *string = [NSString stringWithVimString:buttons];
1531         NSArray *array = [string componentsSeparatedByString:@"\n"];
1532         [attr setObject:array forKey:@"buttonTitles"];
1533     }
1535     NSString *messageText = nil;
1536     if (title)
1537         messageText = [NSString stringWithVimString:title];
1539     if (message) {
1540         NSString *informativeText = [NSString stringWithVimString:message];
1541         if (!messageText) {
1542             // HACK! If there is a '\n\n' or '\n' sequence in the message, then
1543             // make the part up to there into the title.  We only do this
1544             // because Vim has lots of dialogs without a title and they look
1545             // ugly that way.
1546             // TODO: Fix the actual dialog texts.
1547             NSRange eolRange = [informativeText rangeOfString:@"\n\n"];
1548             if (NSNotFound == eolRange.location)
1549                 eolRange = [informativeText rangeOfString:@"\n"];
1550             if (NSNotFound != eolRange.location) {
1551                 messageText = [informativeText substringToIndex:
1552                                                         eolRange.location];
1553                 informativeText = [informativeText substringFromIndex:
1554                                                         NSMaxRange(eolRange)];
1555             }
1556         }
1558         [attr setObject:informativeText forKey:@"informativeText"];
1559     }
1561     if (messageText)
1562         [attr setObject:messageText forKey:@"messageText"];
1564     if (textfield) {
1565         NSString *string = [NSString stringWithVimString:textfield];
1566         [attr setObject:string forKey:@"textFieldString"];
1567     }
1569     return [[MMBackend sharedInstance] showDialogWithAttributes:attr
1570                                                     textField:(char*)textfield];
1574     void
1575 gui_mch_flash(int msec)
1581  * Return the Pixel value (color) for the given color name.  This routine was
1582  * pretty much taken from example code in the Silicon Graphics OSF/Motif
1583  * Programmer's Guide.
1584  * Return INVALCOLOR when failed.
1585  */
1586     guicolor_T
1587 gui_mch_get_color(char_u *name)
1589 #ifdef FEAT_MBYTE
1590     name = CONVERT_TO_UTF8(name);
1591 #endif
1593     NSString *key = [NSString stringWithUTF8String:(char*)name];
1594     guicolor_T col = [[MMBackend sharedInstance] lookupColorWithKey:key];
1596 #ifdef FEAT_MBYTE
1597     CONVERT_TO_UTF8_FREE(name);
1598 #endif
1600     return col;
1605  * Return the RGB value of a pixel as long.
1606  */
1607     long_u
1608 gui_mch_get_rgb(guicolor_T pixel)
1610     // This is only implemented so that vim can guess the correct value for
1611     // 'background' (which otherwise defaults to 'dark'); it is not used for
1612     // anything else (as far as I know).
1613     // The implementation is simple since colors are stored in an int as
1614     // "rrggbb".
1615     return pixel;
1620  * Get the screen dimensions.
1621  * Allow 10 pixels for horizontal borders, 40 for vertical borders.
1622  * Is there no way to find out how wide the borders really are?
1623  * TODO: Add live udate of those value on suspend/resume.
1624  */
1625     void
1626 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
1628     ASLogDebug(@"Columns=%d Rows=%d", Columns, Rows);
1629     *screen_w = Columns;
1630     *screen_h = Rows;
1635  * Get the position of the top left corner of the window.
1636  */
1637     int
1638 gui_mch_get_winpos(int *x, int *y)
1640     *x = *y = 0;
1641     return OK;
1646  * Return OK if the key with the termcap name "name" is supported.
1647  */
1648     int
1649 gui_mch_haskey(char_u *name)
1651     return [[MMBackend sharedInstance] hasSpecialKeyWithValue:name];
1656  * Iconify the GUI window.
1657  */
1658     void
1659 gui_mch_iconify(void)
1664 #if defined(FEAT_EVAL) || defined(PROTO)
1666  * Bring the Vim window to the foreground.
1667  */
1668     void
1669 gui_mch_set_foreground(void)
1671     [[MMBackend sharedInstance] activate];
1673 #endif
1677     void
1678 gui_mch_set_shellsize(
1679     int         width,
1680     int         height,
1681     int         min_width,
1682     int         min_height,
1683     int         base_width,
1684     int         base_height,
1685     int         direction)
1687     ASLogDebug(@"width=%d height=%d min_width=%d min_height=%d base_width=%d "
1688                "base_height=%d direction=%d", width, height, min_width,
1689                min_height, base_width, base_height, direction);
1690     [[MMBackend sharedInstance] setRows:height columns:width];
1694     void
1695 gui_mch_set_text_area_pos(int x, int y, int w, int h)
1700  * Set the position of the top left corner of the window to the given
1701  * coordinates.
1702  */
1703     void
1704 gui_mch_set_winpos(int x, int y)
1709 #ifdef FEAT_TITLE
1711  * Set the window title and icon.
1712  * (The icon is not taken care of).
1713  */
1714     void
1715 gui_mch_settitle(char_u *title, char_u *icon)
1717     ASLogDebug(@"title='%s' icon='%s'", title, icon);
1719 #ifdef FEAT_MBYTE
1720     title = CONVERT_TO_UTF8(title);
1721 #endif
1723     MMBackend *backend = [MMBackend sharedInstance];
1724     [backend setWindowTitle:(char*)title];
1726     // TODO: Convert filename to UTF-8?
1727     if (curbuf)
1728         [backend setDocumentFilename:(char*)curbuf->b_ffname];
1730 #ifdef FEAT_MBYTE
1731     CONVERT_TO_UTF8_FREE(title);
1732 #endif
1734 #endif
1737     void
1738 gui_mch_toggle_tearoffs(int enable)
1744     void
1745 gui_mch_enter_fullscreen(int fuoptions_flags, guicolor_T bg)
1747     [[MMBackend sharedInstance] enterFullscreen:fuoptions_flags background:bg];
1751     void
1752 gui_mch_leave_fullscreen()
1754     [[MMBackend sharedInstance] leaveFullscreen];
1758     void
1759 gui_mch_fuopt_update()
1761     if (!gui.in_use)
1762         return;
1764     guicolor_T fg, bg;
1765     if (fuoptions_flags & FUOPT_BGCOLOR_HLGROUP) {
1766         syn_id2colors(fuoptions_bgcolor, &fg, &bg);
1767     } else {
1768         bg = fuoptions_bgcolor;
1769     }
1771     [[MMBackend sharedInstance] setFullscreenBackgroundColor:bg];
1775     void
1776 gui_macvim_update_modified_flag()
1778     [[MMBackend sharedInstance] updateModifiedFlag];
1782  * Add search pattern 'pat' to the OS X find pasteboard.  This allows other
1783  * apps access the last pattern searched for (hitting <D-g> in another app will
1784  * initiate a search for the same pattern).
1785  */
1786     void
1787 gui_macvim_add_to_find_pboard(char_u *pat)
1789     if (!pat) return;
1791 #ifdef FEAT_MBYTE
1792     pat = CONVERT_TO_UTF8(pat);
1793 #endif
1794     NSString *s = [NSString stringWithUTF8String:(char*)pat];
1795 #ifdef FEAT_MBYTE
1796     CONVERT_TO_UTF8_FREE(pat);
1797 #endif
1799     if (!s) return;
1801     NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSFindPboard];
1802     [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
1803     [pb setString:s forType:NSStringPboardType];
1806     void
1807 gui_macvim_set_antialias(int antialias)
1809     [[MMBackend sharedInstance] setAntialias:antialias];
1813     void
1814 gui_macvim_wait_for_startup()
1816     MMBackend *backend = [MMBackend sharedInstance];
1817     if ([backend waitForAck])
1818         [backend waitForConnectionAcknowledgement];
1821 void gui_macvim_get_window_layout(int *count, int *layout)
1823     if (!(count && layout)) return;
1825     // NOTE: Only set 'layout' if the backend has requested a != 0 layout, else
1826     // any command line arguments (-p/-o) would be ignored.
1827     int window_layout = [[MMBackend sharedInstance] initialWindowLayout];
1828     if (window_layout > 0 && window_layout < 4) {
1829         // The window_layout numbers must match the WIN_* defines in main.c.
1830         *count = 0;
1831         *layout = window_layout;
1832     }
1836 // -- Client/Server ---------------------------------------------------------
1838 #ifdef MAC_CLIENTSERVER
1841 // NOTE: Client/Server is only fully supported with a GUI.  Theoretically it
1842 // would be possible to make the server code work with terminal Vim, but it
1843 // would require that a run-loop is set up and checked.  This should not be
1844 // difficult to implement, simply call gui_mch_update() at opportune moments
1845 // and it will take care of the run-loop.  Another (bigger) problem with
1846 // supporting servers in terminal mode is that the server listing code talks to
1847 // MacVim (the GUI) to figure out which servers are running.
1852  * Register connection with 'name'.  The actual connection is named something
1853  * like 'org.vim.MacVim.VIM3', whereas the server is called 'VIM3'.
1854  */
1855     void
1856 serverRegisterName(char_u *name)
1858 #ifdef FEAT_MBYTE
1859     name = CONVERT_TO_UTF8(name);
1860 #endif
1862     NSString *svrName = [NSString stringWithUTF8String:(char*)name];
1863     [[MMBackend sharedInstance] registerServerWithName:svrName];
1865 #ifdef FEAT_MBYTE
1866     CONVERT_TO_UTF8_FREE(name);
1867 #endif
1872  * Send to an instance of Vim.
1873  * Returns 0 for OK, negative for an error.
1874  */
1875     int
1876 serverSendToVim(char_u *name, char_u *cmd, char_u **result,
1877         int *port, int asExpr, int silent)
1879 #ifdef FEAT_MBYTE
1880     name = CONVERT_TO_UTF8(name);
1881     cmd = CONVERT_TO_UTF8(cmd);
1882 #endif
1884     BOOL ok = [[MMBackend sharedInstance]
1885             sendToServer:[NSString stringWithUTF8String:(char*)name]
1886                   string:[NSString stringWithUTF8String:(char*)cmd]
1887                    reply:result
1888                     port:port
1889               expression:asExpr
1890                   silent:silent];
1892 #ifdef FEAT_MBYTE
1893     CONVERT_TO_UTF8_FREE(name);
1894     CONVERT_TO_UTF8_FREE(cmd);
1895 #endif
1897     return ok ? 0 : -1;
1902  * Ask MacVim for the names of all Vim servers.
1903  */
1904     char_u *
1905 serverGetVimNames(void)
1907     char_u *names = NULL;
1908     NSArray *list = [[MMBackend sharedInstance] serverList];
1910     if (list) {
1911         NSString *string = [list componentsJoinedByString:@"\n"];
1912         names = [string vimStringSave];
1913     }
1915     return names;
1920  * 'str' is a hex int representing the send port of the connection.
1921  */
1922     int
1923 serverStrToPort(char_u *str)
1925     int port = 0;
1927     sscanf((char *)str, "0x%x", &port);
1928     if (!port)
1929         EMSG2(_("E573: Invalid server id used: %s"), str);
1931     return port;
1936  * Check for replies from server with send port 'port'.
1937  * Return TRUE and a non-malloc'ed string if there is.  Else return FALSE.
1938  */
1939     int
1940 serverPeekReply(int port, char_u **str)
1942     NSString *reply = [[MMBackend sharedInstance] peekForReplyOnPort:port];
1943     int len = [reply lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1945     if (str && len > 0) {
1946         *str = (char_u*)[reply UTF8String];
1948 #ifdef FEAT_MBYTE
1949         if (input_conv.vc_type != CONV_NONE) {
1950             char_u *s = string_convert(&input_conv, *str, &len);
1952             if (len > 0) {
1953                 // HACK! Since 's' needs to be freed we cannot simply set
1954                 // '*str = s' or memory will leak.  Instead, create a dummy
1955                 // NSData and return its 'bytes' pointer, then autorelease the
1956                 // NSData.
1957                 NSData *data = [NSData dataWithBytes:s length:len+1];
1958                 *str = (char_u*)[data bytes];
1959             }
1961             vim_free(s);
1962         }
1963 #endif
1964     }
1966     return reply != nil;
1971  * Wait for replies from server with send port 'port'.
1972  * Return 0 and the malloc'ed string when a reply is available.
1973  * Return -1 on error.
1974  */
1975     int
1976 serverReadReply(int port, char_u **str)
1978     NSString *reply = [[MMBackend sharedInstance] waitForReplyOnPort:port];
1979     if (reply && str) {
1980         *str = [reply vimStringSave];
1981         return 0;
1982     }
1984     return -1;
1989  * Send a reply string (notification) to client with port given by "serverid".
1990  * Return -1 if the window is invalid.
1991  */
1992     int
1993 serverSendReply(char_u *serverid, char_u *reply)
1995     int retval = -1;
1996     int port = serverStrToPort(serverid);
1997     if (port > 0 && reply) {
1998 #ifdef FEAT_MBYTE
1999         reply = CONVERT_TO_UTF8(reply);
2000 #endif
2001         BOOL ok = [[MMBackend sharedInstance]
2002                 sendReply:[NSString stringWithUTF8String:(char*)reply]
2003                    toPort:port];
2004         retval = ok ? 0 : -1;
2005 #ifdef FEAT_MBYTE
2006         CONVERT_TO_UTF8_FREE(reply);
2007 #endif
2008     }
2010     return retval;
2013 #endif // MAC_CLIENTSERVER
2018 // -- ODB Editor Support ----------------------------------------------------
2020 #ifdef FEAT_ODB_EDITOR
2022  * The ODB Editor protocol works like this:
2023  * - An external program (the server) asks MacVim to open a file and associates
2024  *   three things with this file: (1) a server id (a four character code that
2025  *   identifies the server), (2) a path that can be used as window title for
2026  *   the file (optional), (3) an arbitrary token (optional)
2027  * - When a file is saved or closed, MacVim should tell the server about which
2028  *   file was modified and also pass back the token
2030  * All communication between MacVim and the server goes via Apple Events.
2031  */
2033     static int16_t
2034 odb_event(buf_T *buf, const AEEventID action)
2036     if (!(buf->b_odb_server_id && buf->b_ffname))
2037         return noErr;
2039     NSAppleEventDescriptor *targetDesc = [NSAppleEventDescriptor
2040             descriptorWithDescriptorType:typeApplSignature
2041                                    bytes:&buf->b_odb_server_id
2042                                   length:sizeof(uint32_t)];
2044     // TODO: Convert b_ffname to UTF-8?
2045     NSString *path = [NSString stringWithUTF8String:(char*)buf->b_ffname];
2046     NSData *pathData = [[[NSURL fileURLWithPath:path] absoluteString]
2047             dataUsingEncoding:NSUTF8StringEncoding];
2048     NSAppleEventDescriptor *pathDesc = [NSAppleEventDescriptor
2049             descriptorWithDescriptorType:typeFileURL data:pathData];
2051     NSAppleEventDescriptor *event = [NSAppleEventDescriptor
2052             appleEventWithEventClass:kODBEditorSuite
2053                              eventID:action
2054                     targetDescriptor:targetDesc
2055                             returnID:kAutoGenerateReturnID
2056                        transactionID:kAnyTransactionID];
2058     [event setParamDescriptor:pathDesc forKeyword:keyDirectObject];
2060     if (buf->b_odb_token)
2061         [event setParamDescriptor:buf->b_odb_token forKeyword:keySenderToken];
2063     return AESendMessage([event aeDesc], NULL, kAENoReply | kAENeverInteract,
2064             kAEDefaultTimeout);
2067     int16_t
2068 odb_buffer_close(buf_T *buf)
2070     int16_t err = noErr;
2071     if (buf) {
2072         err = odb_event(buf, kAEClosedFile);
2074         buf->b_odb_server_id = 0;
2076         if (buf->b_odb_token) {
2077             [(NSAppleEventDescriptor *)(buf->b_odb_token) release];
2078             buf->b_odb_token = NULL;
2079         }
2081         if (buf->b_odb_fname) {
2082             vim_free(buf->b_odb_fname);
2083             buf->b_odb_fname = NULL;
2084         }
2085     }
2087     return err;
2090     int16_t
2091 odb_post_buffer_write(buf_T *buf)
2093     return buf ? odb_event(buf, kAEModifiedFile) : noErr;
2096     void
2097 odb_end(void)
2099     buf_T *buf;
2100     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2101         odb_buffer_close(buf);
2104 #endif // FEAT_ODB_EDITOR
2107     char_u *
2108 get_macaction_name(expand_T *xp, int idx)
2110     static char_u *str = NULL;
2111     NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict];
2113     if (nil == actionDict || idx < 0 || idx >= [actionDict count])
2114         return NULL;
2116     NSString *string = [[actionDict allKeys] objectAtIndex:idx];
2117     if (!string)
2118         return NULL;
2120     char_u *plainStr = (char_u*)[string UTF8String];
2122 #ifdef FEAT_MBYTE
2123     if (str) {
2124         vim_free(str);
2125         str = NULL;
2126     }
2127     if (input_conv.vc_type != CONV_NONE) {
2128         int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2129         str = string_convert(&input_conv, plainStr, &len);
2130         plainStr = str;
2131     }
2132 #endif
2134     return plainStr;
2138     int
2139 is_valid_macaction(char_u *action)
2141     int isValid = NO;
2142     NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict];
2143     if (actionDict) {
2144 #ifdef FEAT_MBYTE
2145         action = CONVERT_TO_UTF8(action);
2146 #endif
2147         NSString *string = [NSString stringWithUTF8String:(char*)action];
2148         isValid = (nil != [actionDict objectForKey:string]);
2149 #ifdef FEAT_MBYTE
2150         CONVERT_TO_UTF8_FREE(action);
2151 #endif
2152     }
2154     return isValid;
2157 static int specialKeyToNSKey(int key)
2159     if (!IS_SPECIAL(key))
2160         return key;
2162     static struct {
2163         int special;
2164         int nskey;
2165     } sp2ns[] = {
2166         { K_UP, NSUpArrowFunctionKey },
2167         { K_DOWN, NSDownArrowFunctionKey },
2168         { K_LEFT, NSLeftArrowFunctionKey },
2169         { K_RIGHT, NSRightArrowFunctionKey },
2170         { K_F1, NSF1FunctionKey },
2171         { K_F2, NSF2FunctionKey },
2172         { K_F3, NSF3FunctionKey },
2173         { K_F4, NSF4FunctionKey },
2174         { K_F5, NSF5FunctionKey },
2175         { K_F6, NSF6FunctionKey },
2176         { K_F7, NSF7FunctionKey },
2177         { K_F8, NSF8FunctionKey },
2178         { K_F9, NSF9FunctionKey },
2179         { K_F10, NSF10FunctionKey },
2180         { K_F11, NSF11FunctionKey },
2181         { K_F12, NSF12FunctionKey },
2182         { K_F13, NSF13FunctionKey },
2183         { K_F14, NSF14FunctionKey },
2184         { K_F15, NSF15FunctionKey },
2185         { K_F16, NSF16FunctionKey },
2186         { K_F17, NSF17FunctionKey },
2187         { K_F18, NSF18FunctionKey },
2188         { K_F19, NSF19FunctionKey },
2189         { K_F20, NSF20FunctionKey },
2190         { K_F21, NSF21FunctionKey },
2191         { K_F22, NSF22FunctionKey },
2192         { K_F23, NSF23FunctionKey },
2193         { K_F24, NSF24FunctionKey },
2194         { K_F25, NSF25FunctionKey },
2195         { K_F26, NSF26FunctionKey },
2196         { K_F27, NSF27FunctionKey },
2197         { K_F28, NSF28FunctionKey },
2198         { K_F29, NSF29FunctionKey },
2199         { K_F30, NSF30FunctionKey },
2200         { K_F31, NSF31FunctionKey },
2201         { K_F32, NSF32FunctionKey },
2202         { K_F33, NSF33FunctionKey },
2203         { K_F34, NSF34FunctionKey },
2204         { K_F35, NSF35FunctionKey },
2205         { K_DEL, NSBackspaceCharacter },
2206         { K_BS, NSDeleteCharacter },
2207         { K_HOME, NSHomeFunctionKey },
2208         { K_END, NSEndFunctionKey },
2209         { K_PAGEUP, NSPageUpFunctionKey },
2210         { K_PAGEDOWN, NSPageDownFunctionKey }
2211     };
2213     int i;
2214     for (i = 0; i < sizeof(sp2ns)/sizeof(sp2ns[0]); ++i) {
2215         if (sp2ns[i].special == key)
2216             return sp2ns[i].nskey;
2217     }
2219     return 0;
2222 static int vimModMaskToEventModifierFlags(int mods)
2224     int flags = 0;
2226     if (mods & MOD_MASK_SHIFT)
2227         flags |= NSShiftKeyMask;
2228     if (mods & MOD_MASK_CTRL)
2229         flags |= NSControlKeyMask;
2230     if (mods & MOD_MASK_ALT)
2231         flags |= NSAlternateKeyMask;
2232     if (mods & MOD_MASK_CMD)
2233         flags |= NSCommandKeyMask;
2235     return flags;
2240 // -- NetBeans Support ------------------------------------------------------
2242 #ifdef FEAT_NETBEANS_INTG
2244 /* Set NetBeans socket to CFRunLoop */
2245     void
2246 gui_macvim_set_netbeans_socket(int socket)
2248     [[MMBackend sharedInstance] setNetbeansSocket:socket];
2251 #endif // FEAT_NETBEANS_INTG