Tizen 2.0 Release
[external/vim.git] / src / gui_mac.c
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              GUI/Motif support by Robert Webb
5  *                              Macintosh port by Dany St-Amant
6  *                                            and Axel Kielhorn
7  *                              Port to MPW by Bernhard Pruemmer
8  *                              Initial Carbon port by Ammon Skidmore
9  *
10  * Do ":help uganda"  in Vim to read copying and usage conditions.
11  * Do ":help credits" in Vim to see a list of people who contributed.
12  * See README.txt for an overview of the Vim source code.
13  */
14
15 /*
16  * NOTES: - Vim 7+ does not support classic MacOS. Please use Vim 6.x
17  *        - Comments mentioning FAQ refer to the book:
18  *          "Macworld Mac Programming FAQs" from "IDG Books"
19  */
20
21 /*
22  * TODO: Change still to merge from the macvim's iDisk
23  *
24  * error_ga, mch_errmsg, Navigation's changes in gui_mch_browse
25  * uses of MenuItemIndex, changes in gui_mch_set_shellsize,
26  * ScrapManager error handling.
27  * Comments about function remaining to Carbonize.
28  *
29  */
30
31 /* TODO (Jussi)
32  *   * Clipboard does not work (at least some cases)
33  *   * ATSU font rendering has some problems
34  *   * Investigate and remove dead code (there is still lots of that)
35  */
36
37 #include <Devices.h> /* included first to avoid CR problems */
38 #include "vim.h"
39
40 #define USE_CARBONIZED
41 #define USE_AEVENT              /* Enable AEVENT */
42 #undef USE_OFFSETED_WINDOW      /* Debugging feature: start Vim window OFFSETed */
43
44 /* Compile as CodeWarior External Editor */
45 #if defined(FEAT_CW_EDITOR) && !defined(USE_AEVENT)
46 # define USE_AEVENT /* Need Apple Event Support */
47 #endif
48
49 /* Vim's Scrap flavor. */
50 #define VIMSCRAPFLAVOR 'VIM!'
51 #ifdef FEAT_MBYTE
52 # define SCRAPTEXTFLAVOR kScrapFlavorTypeUnicode
53 #else
54 # define SCRAPTEXTFLAVOR kScrapFlavorTypeText
55 #endif
56
57 static EventHandlerUPP mouseWheelHandlerUPP = NULL;
58 SInt32 gMacSystemVersion;
59
60 #ifdef MACOS_CONVERT
61 # define USE_CARBONKEYHANDLER
62
63 static int im_is_active = FALSE;
64 #if 0
65     /* TODO: Implement me! */
66 static int im_start_row = 0;
67 static int im_start_col = 0;
68 #endif
69
70 #define NR_ELEMS(x)     (sizeof(x) / sizeof(x[0]))
71
72 static TSMDocumentID gTSMDocument;
73
74 static void im_on_window_switch(int active);
75 static EventHandlerUPP keyEventHandlerUPP = NULL;
76 static EventHandlerUPP winEventHandlerUPP = NULL;
77
78 static pascal OSStatus gui_mac_handle_window_activate(
79         EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
80
81 static pascal OSStatus gui_mac_handle_text_input(
82         EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
83
84 static pascal OSStatus gui_mac_update_input_area(
85         EventHandlerCallRef nextHandler, EventRef theEvent);
86
87 static pascal OSStatus gui_mac_unicode_key_event(
88         EventHandlerCallRef nextHandler, EventRef theEvent);
89
90 #endif
91
92
93 /* Include some file. TODO: move into os_mac.h */
94 #include <Menus.h>
95 #include <Resources.h>
96 #include <Processes.h>
97 #ifdef USE_AEVENT
98 # include <AppleEvents.h>
99 # include <AERegistry.h>
100 #endif
101 # include <Gestalt.h>
102 #if UNIVERSAL_INTERFACES_VERSION >= 0x0330
103 # include <ControlDefinitions.h>
104 # include <Navigation.h>  /* Navigation only part of ?? */
105 #endif
106
107 /* Help Manager (balloon.h, HM prefixed functions) are not supported
108  * under Carbon (Jussi) */
109 #  if 0
110 /* New Help Interface for Mac, not implemented yet.*/
111 #    include <MacHelp.h>
112 #  endif
113
114 /*
115  * These seem to be rectangle options. Why are they not found in
116  * headers? (Jussi)
117  */
118 #define kNothing 0
119 #define kCreateEmpty 2 /*1*/
120 #define kCreateRect 2
121 #define kDestroy 3
122
123 /*
124  * Dany: Don't like those...
125  */
126 #define topLeft(r)      (((Point*)&(r))[0])
127 #define botRight(r)     (((Point*)&(r))[1])
128
129
130 /* Time of last mouse click, to detect double-click */
131 static long lastMouseTick = 0;
132
133 /* ??? */
134 static RgnHandle cursorRgn;
135 static RgnHandle dragRgn;
136 static Rect dragRect;
137 static short dragRectEnbl;
138 static short dragRectControl;
139
140 /* This variable is set when waiting for an event, which is the only moment
141  * scrollbar dragging can be done directly.  It's not allowed while commands
142  * are executed, because it may move the cursor and that may cause unexpected
143  * problems (e.g., while ":s" is working).
144  */
145 static int allow_scrollbar = FALSE;
146
147 /* Last mouse click caused contextual menu, (to provide proper release) */
148 static short clickIsPopup;
149
150 /* Feedback Action for Scrollbar */
151 ControlActionUPP gScrollAction;
152 ControlActionUPP gScrollDrag;
153
154 /* Keeping track of which scrollbar is being dragged */
155 static ControlHandle dragged_sb = NULL;
156
157 /* Vector of char_u --> control index for hotkeys in dialogs */
158 static short *gDialogHotKeys;
159
160 static struct
161 {
162     FMFontFamily family;
163     FMFontSize size;
164     FMFontStyle style;
165     Boolean isPanelVisible;
166 } gFontPanelInfo = { 0, 0, 0, false };
167
168 #ifdef MACOS_CONVERT
169 # define USE_ATSUI_DRAWING
170 int         p_macatsui_last;
171 ATSUStyle   gFontStyle;
172 # ifdef FEAT_MBYTE
173 ATSUStyle   gWideFontStyle;
174 # endif
175 Boolean     gIsFontFallbackSet;
176 UInt32      useAntialias_cached = 0x0;
177 #endif
178
179 /* Colors Macros */
180 #define RGB(r,g,b)      ((r) << 16) + ((g) << 8) + (b)
181 #define Red(c)          ((c & 0x00FF0000) >> 16)
182 #define Green(c)        ((c & 0x0000FF00) >>  8)
183 #define Blue(c)         ((c & 0x000000FF) >>  0)
184
185 /* Key mapping */
186
187 #define vk_Esc          0x35    /* -> 1B */
188
189 #define vk_F1           0x7A    /* -> 10 */
190 #define vk_F2           0x78  /*0x63*/
191 #define vk_F3           0x63  /*0x76*/
192 #define vk_F4           0x76  /*0x60*/
193 #define vk_F5           0x60  /*0x61*/
194 #define vk_F6           0x61  /*0x62*/
195 #define vk_F7           0x62  /*0x63*/  /*?*/
196 #define vk_F8           0x64
197 #define vk_F9           0x65
198 #define vk_F10          0x6D
199 #define vk_F11          0x67
200 #define vk_F12          0x6F
201 #define vk_F13          0x69
202 #define vk_F14          0x6B
203 #define vk_F15          0x71
204
205 #define vk_Clr          0x47    /* -> 1B (ESC) */
206 #define vk_Enter        0x4C    /* -> 03 */
207
208 #define vk_Space        0x31    /* -> 20 */
209 #define vk_Tab          0x30    /* -> 09 */
210 #define vk_Return       0x24    /* -> 0D */
211 /* This is wrong for OSX, what is it for? */
212 #define vk_Delete       0X08    /* -> 08 BackSpace */
213
214 #define vk_Help         0x72    /* -> 05 */
215 #define vk_Home         0x73    /* -> 01 */
216 #define vk_PageUp       0x74    /* -> 0D */
217 #define vk_FwdDelete    0x75    /* -> 7F */
218 #define vk_End          0x77    /* -> 04 */
219 #define vk_PageDown     0x79    /* -> 0C */
220
221 #define vk_Up           0x7E    /* -> 1E */
222 #define vk_Down         0x7D    /* -> 1F */
223 #define vk_Left         0x7B    /* -> 1C */
224 #define vk_Right        0x7C    /* -> 1D */
225
226 #define vk_Undo         vk_F1
227 #define vk_Cut          vk_F2
228 #define vk_Copy         vk_F3
229 #define vk_Paste        vk_F4
230 #define vk_PrintScreen  vk_F13
231 #define vk_SCrollLock   vk_F14
232 #define vk_Pause        vk_F15
233 #define vk_NumLock      vk_Clr
234 #define vk_Insert       vk_Help
235
236 #define KeySym  char
237
238 static struct
239 {
240     KeySym  key_sym;
241     char_u  vim_code0;
242     char_u  vim_code1;
243 } special_keys[] =
244 {
245     {vk_Up,             'k', 'u'},
246     {vk_Down,           'k', 'd'},
247     {vk_Left,           'k', 'l'},
248     {vk_Right,          'k', 'r'},
249
250     {vk_F1,             'k', '1'},
251     {vk_F2,             'k', '2'},
252     {vk_F3,             'k', '3'},
253     {vk_F4,             'k', '4'},
254     {vk_F5,             'k', '5'},
255     {vk_F6,             'k', '6'},
256     {vk_F7,             'k', '7'},
257     {vk_F8,             'k', '8'},
258     {vk_F9,             'k', '9'},
259     {vk_F10,            'k', ';'},
260
261     {vk_F11,            'F', '1'},
262     {vk_F12,            'F', '2'},
263     {vk_F13,            'F', '3'},
264     {vk_F14,            'F', '4'},
265     {vk_F15,            'F', '5'},
266
267 /*  {XK_Help,           '%', '1'}, */
268 /*  {XK_Undo,           '&', '8'}, */
269 /*  {XK_BackSpace,      'k', 'b'}, */
270 #ifndef MACOS_X
271     {vk_Delete,         'k', 'b'},
272 #endif
273     {vk_Insert,         'k', 'I'},
274     {vk_FwdDelete,      'k', 'D'},
275     {vk_Home,           'k', 'h'},
276     {vk_End,            '@', '7'},
277 /*  {XK_Prior,          'k', 'P'}, */
278 /*  {XK_Next,           'k', 'N'}, */
279 /*  {XK_Print,          '%', '9'}, */
280
281     {vk_PageUp,         'k', 'P'},
282     {vk_PageDown,       'k', 'N'},
283
284     /* End of list marker: */
285     {(KeySym)0,         0, 0}
286 };
287
288 /*
289  * ------------------------------------------------------------
290  * Forward declaration (for those needed)
291  * ------------------------------------------------------------
292  */
293
294 #ifdef USE_AEVENT
295 OSErr HandleUnusedParms(const AppleEvent *theAEvent);
296 #endif
297
298 #ifdef FEAT_GUI_TABLINE
299 static void initialise_tabline(void);
300 static WindowRef drawer = NULL; // TODO: put into gui.h
301 #endif
302
303 #ifdef USE_ATSUI_DRAWING
304 static void gui_mac_set_font_attributes(GuiFont font);
305 static void gui_mac_dispose_atsui_style(void);
306 #endif
307
308 /*
309  * ------------------------------------------------------------
310  * Conversion Utility
311  * ------------------------------------------------------------
312  */
313
314 /*
315  * C2Pascal_save
316  *
317  * Allocate memory and convert the C-String passed in
318  * into a pascal string
319  *
320  */
321
322     char_u *
323 C2Pascal_save(char_u *Cstring)
324 {
325     char_u  *PascalString;
326     int     len;
327
328     if (Cstring == NULL)
329         return NULL;
330
331     len = STRLEN(Cstring);
332
333     if (len > 255) /* Truncate if necessary */
334         len = 255;
335
336     PascalString = alloc(len + 1);
337     if (PascalString != NULL)
338     {
339         mch_memmove(PascalString + 1, Cstring, len);
340         PascalString[0] = len;
341     }
342
343     return PascalString;
344 }
345
346 /*
347  * C2Pascal_save_and_remove_backslash
348  *
349  * Allocate memory and convert the C-String passed in
350  * into a pascal string. Also remove the backslash at the same time
351  *
352  */
353
354     char_u *
355 C2Pascal_save_and_remove_backslash(char_u *Cstring)
356 {
357     char_u  *PascalString;
358     int     len;
359     char_u  *p, *c;
360
361     len = STRLEN(Cstring);
362
363     if (len > 255) /* Truncate if necessary */
364         len = 255;
365
366     PascalString = alloc(len + 1);
367     if (PascalString != NULL)
368     {
369         for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++)
370         {
371             if ((*c == '\\') && (c[1] != 0))
372             {
373                 c++;
374             }
375             *p = *c;
376             p++;
377             len++;
378         }
379         PascalString[0] = len;
380     }
381
382     return PascalString;
383 }
384
385 /*
386  * Convert the modifiers of an Event into vim's modifiers (mouse)
387  */
388
389     int_u
390 EventModifiers2VimMouseModifiers(EventModifiers macModifiers)
391 {
392     int_u vimModifiers = 0x00;
393
394     if (macModifiers & (shiftKey | rightShiftKey))
395         vimModifiers |= MOUSE_SHIFT;
396     if (macModifiers & (controlKey | rightControlKey))
397         vimModifiers |= MOUSE_CTRL;
398     if (macModifiers & (optionKey | rightOptionKey))
399         vimModifiers |= MOUSE_ALT;
400 #if 0
401     /* Not yet supported */
402     if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
403         vimModifiers |= MOUSE_CMD;
404 #endif
405     return (vimModifiers);
406 }
407
408 /*
409  * Convert the modifiers of an Event into vim's modifiers (keys)
410  */
411
412     static int_u
413 EventModifiers2VimModifiers(EventModifiers macModifiers)
414 {
415     int_u vimModifiers = 0x00;
416
417     if (macModifiers & (shiftKey | rightShiftKey))
418         vimModifiers |= MOD_MASK_SHIFT;
419     if (macModifiers & (controlKey | rightControlKey))
420         vimModifiers |= MOD_MASK_CTRL;
421     if (macModifiers & (optionKey | rightOptionKey))
422         vimModifiers |= MOD_MASK_ALT;
423 #ifdef USE_CMD_KEY
424     if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
425         vimModifiers |= MOD_MASK_CMD;
426 #endif
427     return (vimModifiers);
428 }
429
430 /* Convert a string representing a point size into pixels. The string should
431  * be a positive decimal number, with an optional decimal point (eg, "12", or
432  * "10.5"). The pixel value is returned, and a pointer to the next unconverted
433  * character is stored in *end. The flag "vertical" says whether this
434  * calculation is for a vertical (height) size or a horizontal (width) one.
435  *
436  * From gui_w48.c
437  */
438     static int
439 points_to_pixels(char_u *str, char_u **end, int vertical)
440 {
441     int         pixels;
442     int         points = 0;
443     int         divisor = 0;
444
445     while (*str)
446     {
447         if (*str == '.' && divisor == 0)
448         {
449             /* Start keeping a divisor, for later */
450             divisor = 1;
451             continue;
452         }
453
454         if (!isdigit(*str))
455             break;
456
457         points *= 10;
458         points += *str - '0';
459         divisor *= 10;
460
461         ++str;
462     }
463
464     if (divisor == 0)
465         divisor = 1;
466
467     pixels = points/divisor;
468     *end = str;
469     return pixels;
470 }
471
472 #ifdef MACOS_CONVERT
473 /*
474  * Deletes all traces of any Windows-style mnemonic text (including any
475  * parentheses) from a menu item and returns the cleaned menu item title.
476  * The caller is responsible for releasing the returned string.
477  */
478     static CFStringRef
479 menu_title_removing_mnemonic(vimmenu_T *menu)
480 {
481     CFStringRef         name;
482     size_t              menuTitleLen;
483     CFIndex             displayLen;
484     CFRange             mnemonicStart;
485     CFRange             mnemonicEnd;
486     CFMutableStringRef  cleanedName;
487
488     menuTitleLen = STRLEN(menu->dname);
489     name = (CFStringRef) mac_enc_to_cfstring(menu->dname, menuTitleLen);
490
491     if (name)
492     {
493         /* Simple mnemonic-removal algorithm, assumes single parenthesized
494          * mnemonic character towards the end of the menu text */
495         mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards);
496         displayLen = CFStringGetLength(name);
497
498         if (mnemonicStart.location != kCFNotFound
499                 && (mnemonicStart.location + 2) < displayLen
500                 && CFStringGetCharacterAtIndex(name,
501                        mnemonicStart.location + 1) == (UniChar)menu->mnemonic)
502         {
503             if (CFStringFindWithOptions(name, CFSTR(")"),
504                         CFRangeMake(mnemonicStart.location + 1,
505                             displayLen - mnemonicStart.location - 1),
506                         kCFCompareBackwards, &mnemonicEnd) &&
507                     (mnemonicStart.location + 2) == mnemonicEnd.location)
508             {
509                 cleanedName = CFStringCreateMutableCopy(NULL, 0, name);
510                 if (cleanedName)
511                 {
512                     CFStringDelete(cleanedName,
513                             CFRangeMake(mnemonicStart.location,
514                                 mnemonicEnd.location + 1 -
515                                 mnemonicStart.location));
516
517                     CFRelease(name);
518                     name = cleanedName;
519                 }
520             }
521         }
522     }
523
524     return name;
525 }
526 #endif
527
528 /*
529  * Convert a list of FSSpec aliases into a list of fullpathname
530  * character strings.
531  */
532
533     char_u **
534 new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error)
535 {
536     char_u      **fnames = NULL;
537     OSErr       newError;
538     long        fileCount;
539     FSSpec      fileToOpen;
540     long        actualSize;
541     AEKeyword   dummyKeyword;
542     DescType    dummyType;
543
544     /* Get number of files in list */
545     *error = AECountItems(theList, numFiles);
546     if (*error)
547         return fnames;
548
549     /* Allocate the pointer list */
550     fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
551
552     /* Empty out the list */
553     for (fileCount = 0; fileCount < *numFiles; fileCount++)
554         fnames[fileCount] = NULL;
555
556     /* Scan the list of FSSpec */
557     for (fileCount = 1; fileCount <= *numFiles; fileCount++)
558     {
559         /* Get the alias for the nth file, convert to an FSSpec */
560         newError = AEGetNthPtr(theList, fileCount, typeFSS,
561                                 &dummyKeyword, &dummyType,
562                                 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize);
563         if (newError)
564         {
565             /* Caller is able to clean up */
566             /* TODO: Should be clean up or not? For safety. */
567             return fnames;
568         }
569
570         /* Convert the FSSpec to a pathname */
571         fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen);
572     }
573
574     return (fnames);
575 }
576
577 /*
578  * ------------------------------------------------------------
579  * CodeWarrior External Editor Support
580  * ------------------------------------------------------------
581  */
582 #ifdef FEAT_CW_EDITOR
583
584 /*
585  * Handle the Window Search event from CodeWarrior
586  *
587  * Description
588  * -----------
589  *
590  * The IDE sends the Window Search AppleEvent to the editor when it
591  * needs to know whether a particular file is open in the editor.
592  *
593  * Event Reply
594  * -----------
595  *
596  * None. Put data in the location specified in the structure received.
597  *
598  * Remarks
599  * -------
600  *
601  * When the editor receives this event, determine whether the specified
602  * file is open. If it is, return the modification date/time for that file
603  * in the appropriate location specified in the structure. If the file is
604  * not opened, put the value fnfErr(file not found) in that location.
605  *
606  */
607
608 typedef struct WindowSearch WindowSearch;
609 struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/
610 {
611     FSSpec theFile; // identifies the file
612     long *theDate; // where to put the modification date/time
613 };
614
615     pascal OSErr
616 Handle_KAHL_SRCH_AE(
617         const AppleEvent    *theAEvent,
618         AppleEvent          *theReply,
619         long                refCon)
620 {
621     OSErr       error = noErr;
622     buf_T       *buf;
623     int         foundFile = false;
624     DescType    typeCode;
625     WindowSearch SearchData;
626     Size        actualSize;
627
628     error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize);
629     if (error)
630         return error;
631
632     error = HandleUnusedParms(theAEvent);
633     if (error)
634         return error;
635
636     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
637         if (buf->b_ml.ml_mfp != NULL
638                 && SearchData.theFile.parID == buf->b_FSSpec.parID
639                 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0]
640                 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0)
641             {
642                 foundFile = true;
643                 break;
644             }
645
646     if (foundFile == false)
647         *SearchData.theDate = fnfErr;
648     else
649         *SearchData.theDate = buf->b_mtime;
650
651     return error;
652 };
653
654 /*
655  * Handle the Modified (from IDE to Editor) event from CodeWarrior
656  *
657  * Description
658  * -----------
659  *
660  * The IDE sends this event to the external editor when it wants to
661  * know which files that are open in the editor have been modified.
662  *
663  * Parameters   None.
664  * ----------
665  *
666  * Event Reply
667  * -----------
668  * The reply for this event is:
669  *
670  * keyDirectObject typeAEList required
671  *  each element in the list is a structure of typeChar
672  *
673  * Remarks
674  * -------
675  *
676  * When building the reply event, include one element in the list for
677  * each open file that has been modified.
678  *
679  */
680
681 typedef struct ModificationInfo ModificationInfo;
682 struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/
683 {
684     FSSpec theFile; // identifies the file
685     long theDate; // the date/time the file was last modified
686     short saved; // set this to zero when replying, unused
687 };
688
689     pascal OSErr
690 Handle_KAHL_MOD_AE(
691         const AppleEvent    *theAEvent,
692         AppleEvent          *theReply,
693         long                refCon)
694 {
695     OSErr       error = noErr;
696     AEDescList  replyList;
697     long        numFiles;
698     ModificationInfo theFile;
699     buf_T       *buf;
700
701     theFile.saved = 0;
702
703     error = HandleUnusedParms(theAEvent);
704     if (error)
705         return error;
706
707     /* Send the reply */
708 /*  replyObject.descriptorType = typeNull;
709     replyObject.dataHandle     = nil;*/
710
711 /* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */
712     error = AECreateList(nil, 0, false, &replyList);
713     if (error)
714         return error;
715
716 #if 0
717     error = AECountItems(&replyList, &numFiles);
718
719     /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
720      * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
721      * sizeof(DescType))
722      */
723
724     /* AEPutDesc */
725 #endif
726
727     numFiles = 0;
728     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
729         if (buf->b_ml.ml_mfp != NULL)
730         {
731             /* Add this file to the list */
732             theFile.theFile = buf->b_FSSpec;
733             theFile.theDate = buf->b_mtime;
734 /*          theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */
735             error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile));
736         };
737
738 #if 0
739     error = AECountItems(&replyList, &numFiles);
740 #endif
741
742     /* We can add data only if something to reply */
743     error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
744
745     if (replyList.dataHandle)
746         AEDisposeDesc(&replyList);
747
748     return error;
749 };
750
751 /*
752  * Handle the Get Text event from CodeWarrior
753  *
754  * Description
755  * -----------
756  *
757  * The IDE sends the Get Text AppleEvent to the editor when it needs
758  * the source code from a file. For example, when the user issues a
759  * Check Syntax or Compile command, the compiler needs access to
760  * the source code contained in the file.
761  *
762  * Event Reply
763  * -----------
764  *
765  * None. Put data in locations specified in the structure received.
766  *
767  * Remarks
768  * -------
769  *
770  * When the editor receives this event, it must set the size of the handle
771  * in theText to fit the data in the file. It must then copy the entire
772  * contents of the specified file into the memory location specified in
773  * theText.
774  *
775  */
776
777 typedef struct CW_GetText CW_GetText;
778 struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/
779 {
780     FSSpec theFile; /* identifies the file */
781     Handle theText; /* the location where you return the text (must be resized properly) */
782     long *unused;   /* 0 (not used) */
783     long *theDate;  /* where to put the modification date/time */
784 };
785
786     pascal OSErr
787 Handle_KAHL_GTTX_AE(
788         const AppleEvent    *theAEvent,
789         AppleEvent          *theReply,
790         long                refCon)
791 {
792     OSErr       error = noErr;
793     buf_T       *buf;
794     int         foundFile = false;
795     DescType    typeCode;
796     CW_GetText  GetTextData;
797     Size        actualSize;
798     char_u      *line;
799     char_u      *fullbuffer = NULL;
800     long        linesize;
801     long        lineStart;
802     long        BufferSize;
803     long        lineno;
804
805     error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize);
806
807     if (error)
808         return error;
809
810     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
811         if (buf->b_ml.ml_mfp != NULL)
812             if (GetTextData.theFile.parID == buf->b_FSSpec.parID)
813             {
814                 foundFile = true;
815                 break;
816             }
817
818     if (foundFile)
819     {
820         BufferSize = 0; /* GetHandleSize(GetTextData.theText); */
821         for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++)
822         {
823             /* Must use the right buffer */
824             line = ml_get_buf(buf, (linenr_T) lineno, FALSE);
825             linesize = STRLEN(line) + 1;
826             lineStart = BufferSize;
827             BufferSize += linesize;
828             /* Resize handle to linesize+1 to include the linefeed */
829             SetHandleSize(GetTextData.theText, BufferSize);
830             if (GetHandleSize(GetTextData.theText) != BufferSize)
831             {
832                 break; /* Simple handling for now */
833             }
834             else
835             {
836                 HLock(GetTextData.theText);
837                 fullbuffer = (char_u *) *GetTextData.theText;
838                 STRCPY((char_u *)(fullbuffer + lineStart), line);
839                 fullbuffer[BufferSize-1] = '\r';
840                 HUnlock(GetTextData.theText);
841             }
842         }
843         if (fullbuffer != NULL)
844         {
845             HLock(GetTextData.theText);
846             fullbuffer[BufferSize-1] = 0;
847             HUnlock(GetTextData.theText);
848         }
849         if (foundFile == false)
850             *GetTextData.theDate = fnfErr;
851         else
852 /*          *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/
853             *GetTextData.theDate = buf->b_mtime;
854     }
855
856     error = HandleUnusedParms(theAEvent);
857
858     return error;
859 }
860
861 /*
862  *
863  */
864
865 /* Taken from MoreAppleEvents:ProcessHelpers*/
866     pascal      OSErr
867 FindProcessBySignature(
868         const OSType            targetType,
869         const OSType            targetCreator,
870         ProcessSerialNumberPtr  psnPtr)
871 {
872     OSErr       anErr = noErr;
873     Boolean     lookingForProcess = true;
874
875     ProcessInfoRec  infoRec;
876
877     infoRec.processInfoLength = sizeof(ProcessInfoRec);
878     infoRec.processName = nil;
879     infoRec.processAppSpec = nil;
880
881     psnPtr->lowLongOfPSN = kNoProcess;
882     psnPtr->highLongOfPSN = kNoProcess;
883
884     while (lookingForProcess)
885     {
886         anErr = GetNextProcess(psnPtr);
887         if (anErr != noErr)
888             lookingForProcess = false;
889         else
890         {
891             anErr = GetProcessInformation(psnPtr, &infoRec);
892             if ((anErr == noErr)
893                     && (infoRec.processType == targetType)
894                     && (infoRec.processSignature == targetCreator))
895                 lookingForProcess = false;
896         }
897     }
898
899     return anErr;
900 }//end FindProcessBySignature
901
902     void
903 Send_KAHL_MOD_AE(buf_T *buf)
904 {
905     OSErr       anErr = noErr;
906     AEDesc      targetAppDesc = { typeNull, nil };
907     ProcessSerialNumber     psn = { kNoProcess, kNoProcess };
908     AppleEvent  theReply = { typeNull, nil };
909     AESendMode  sendMode;
910     AppleEvent  theEvent = {typeNull, nil };
911     AEIdleUPP   idleProcUPP = nil;
912     ModificationInfo ModData;
913
914
915     anErr = FindProcessBySignature('APPL', 'CWIE', &psn);
916     if (anErr == noErr)
917     {
918         anErr = AECreateDesc(typeProcessSerialNumber, &psn,
919                               sizeof(ProcessSerialNumber), &targetAppDesc);
920
921         if (anErr == noErr)
922         {
923             anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc,
924                                         kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
925         }
926
927         AEDisposeDesc(&targetAppDesc);
928
929         /* Add the parms */
930         ModData.theFile = buf->b_FSSpec;
931         ModData.theDate = buf->b_mtime;
932
933         if (anErr == noErr)
934             anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData));
935
936         if (idleProcUPP == nil)
937             sendMode = kAENoReply;
938         else
939             sendMode = kAEWaitReply;
940
941         if (anErr == noErr)
942             anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil);
943         if (anErr == noErr  &&  sendMode == kAEWaitReply)
944         {
945 /*          anErr =  AEHGetHandlerError(&theReply);*/
946         }
947         (void) AEDisposeDesc(&theReply);
948     }
949 }
950 #endif /* FEAT_CW_EDITOR */
951
952 /*
953  * ------------------------------------------------------------
954  * Apple Event Handling procedure
955  * ------------------------------------------------------------
956  */
957 #ifdef USE_AEVENT
958
959 /*
960  * Handle the Unused parms of an AppleEvent
961  */
962
963     OSErr
964 HandleUnusedParms(const AppleEvent *theAEvent)
965 {
966     OSErr       error;
967     long        actualSize;
968     DescType    dummyType;
969     AEKeyword   missedKeyword;
970
971     /* Get the "missed keyword" attribute from the AppleEvent. */
972     error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr,
973                               typeKeyword, &dummyType,
974                               (Ptr)&missedKeyword, sizeof(missedKeyword),
975                               &actualSize);
976
977     /* If the descriptor isn't found, then we got the required parameters. */
978     if (error == errAEDescNotFound)
979     {
980         error = noErr;
981     }
982     else
983     {
984 #if 0
985         /* Why is this removed? */
986         error = errAEEventNotHandled;
987 #endif
988     }
989
990     return error;
991 }
992
993
994 /*
995  * Handle the ODoc AppleEvent
996  *
997  * Deals with all files dragged to the application icon.
998  *
999  */
1000
1001 typedef struct SelectionRange SelectionRange;
1002 struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */
1003 {
1004     short unused1; // 0 (not used)
1005     short lineNum; // line to select (<0 to specify range)
1006     long startRange; // start of selection range (if line < 0)
1007     long endRange; // end of selection range (if line < 0)
1008     long unused2; // 0 (not used)
1009     long theDate; // modification date/time
1010 };
1011
1012 /* The IDE uses the optional keyAEPosition parameter to tell the ed-
1013    itor the selection range. If lineNum is zero or greater, scroll the text
1014    to the specified line. If lineNum is less than zero, use the values in
1015    startRange and endRange to select the specified characters. Scroll
1016    the text to display the selection. If lineNum, startRange, and
1017    endRange are all negative, there is no selection range specified.
1018  */
1019
1020     pascal OSErr
1021 HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
1022 {
1023     /*
1024      * TODO: Clean up the code with convert the AppleEvent into
1025      *       a ":args"
1026      */
1027     OSErr       error = noErr;
1028 //    OSErr     firstError = noErr;
1029 //    short     numErrors = 0;
1030     AEDesc      theList;
1031     DescType    typeCode;
1032     long        numFiles;
1033  //   long       fileCount;
1034     char_u      **fnames;
1035 //    char_u    fname[256];
1036     Size        actualSize;
1037     SelectionRange thePosition;
1038     short       gotPosition = false;
1039     long        lnum;
1040
1041     /* the direct object parameter is the list of aliases to files (one or more) */
1042     error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
1043     if (error)
1044         return error;
1045
1046
1047     error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize);
1048     if (error == noErr)
1049         gotPosition = true;
1050     if (error == errAEDescNotFound)
1051         error = noErr;
1052     if (error)
1053         return error;
1054
1055 /*
1056     error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
1057
1058     if (^error) then
1059     {
1060         if (thePosition.lineNum >= 0)
1061         {
1062           // Goto this line
1063         }
1064         else
1065         {
1066           // Set the range char wise
1067         }
1068     }
1069  */
1070
1071
1072 #ifdef FEAT_VISUAL
1073     reset_VIsual();
1074 #endif
1075
1076     fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error);
1077
1078     if (error)
1079     {
1080       /* TODO: empty fnames[] first */
1081       vim_free(fnames);
1082       return (error);
1083     }
1084
1085     if (starting > 0)
1086     {
1087         int i;
1088         char_u *p;
1089         int fnum = -1;
1090
1091         /* these are the initial files dropped on the Vim icon */
1092         for (i = 0 ; i < numFiles; i++)
1093         {
1094             if (ga_grow(&global_alist.al_ga, 1) == FAIL
1095                                       || (p = vim_strsave(fnames[i])) == NULL)
1096                 mch_exit(2);
1097             else
1098                 alist_add(&global_alist, p, 2);
1099             if (fnum == -1)
1100                 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum;
1101         }
1102
1103         /* If the file name was already in the buffer list we need to switch
1104          * to it. */
1105         if (curbuf->b_fnum != fnum)
1106         {
1107             char_u cmd[30];
1108
1109             vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum);
1110             do_cmdline_cmd(cmd);
1111         }
1112
1113         /* Change directory to the location of the first file. */
1114         if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1115             shorten_fnames(TRUE);
1116
1117         goto finished;
1118     }
1119
1120     /* Handle the drop, :edit to get to the file */
1121     handle_drop(numFiles, fnames, FALSE);
1122
1123     /* TODO: Handle the goto/select line more cleanly */
1124     if ((numFiles == 1) & (gotPosition))
1125     {
1126         if (thePosition.lineNum >= 0)
1127         {
1128             lnum = thePosition.lineNum + 1;
1129         /*  oap->motion_type = MLINE;
1130             setpcmark();*/
1131             if (lnum < 1L)
1132                 lnum = 1L;
1133             else if (lnum > curbuf->b_ml.ml_line_count)
1134                 lnum = curbuf->b_ml.ml_line_count;
1135             curwin->w_cursor.lnum = lnum;
1136             curwin->w_cursor.col = 0;
1137         /*  beginline(BL_SOL | BL_FIX);*/
1138         }
1139         else
1140             goto_byte(thePosition.startRange + 1);
1141     }
1142
1143     /* Update the screen display */
1144     update_screen(NOT_VALID);
1145 #ifdef FEAT_VISUAL
1146     /* Select the text if possible */
1147     if (gotPosition)
1148     {
1149         VIsual_active = TRUE;
1150         VIsual_select = FALSE;
1151         VIsual = curwin->w_cursor;
1152         if (thePosition.lineNum < 0)
1153         {
1154             VIsual_mode = 'v';
1155             goto_byte(thePosition.endRange);
1156         }
1157         else
1158         {
1159             VIsual_mode = 'V';
1160             VIsual.col = 0;
1161         }
1162     }
1163 #endif
1164     setcursor();
1165     out_flush();
1166
1167     /* Fake mouse event to wake from stall */
1168     PostEvent(mouseUp, 0);
1169
1170 finished:
1171     AEDisposeDesc(&theList); /* dispose what we allocated */
1172
1173     error = HandleUnusedParms(theAEvent);
1174     return error;
1175 }
1176
1177 /*
1178  *
1179  */
1180
1181     pascal OSErr
1182 Handle_aevt_oapp_AE(
1183         const AppleEvent    *theAEvent,
1184         AppleEvent          *theReply,
1185         long                refCon)
1186 {
1187     OSErr       error = noErr;
1188
1189     error = HandleUnusedParms(theAEvent);
1190     return error;
1191 }
1192
1193 /*
1194  *
1195  */
1196
1197     pascal OSErr
1198 Handle_aevt_quit_AE(
1199         const AppleEvent    *theAEvent,
1200         AppleEvent          *theReply,
1201         long                refCon)
1202 {
1203     OSErr       error = noErr;
1204
1205     error = HandleUnusedParms(theAEvent);
1206     if (error)
1207         return error;
1208
1209     /* Need to fake a :confirm qa */
1210     do_cmdline_cmd((char_u *)"confirm qa");
1211
1212     return error;
1213 }
1214
1215 /*
1216  *
1217  */
1218
1219     pascal OSErr
1220 Handle_aevt_pdoc_AE(
1221         const AppleEvent    *theAEvent,
1222         AppleEvent          *theReply,
1223         long                refCon)
1224 {
1225     OSErr       error = noErr;
1226
1227     error = HandleUnusedParms(theAEvent);
1228
1229     return error;
1230 }
1231
1232 /*
1233  * Handling of unknown AppleEvent
1234  *
1235  * (Just get rid of all the parms)
1236  */
1237     pascal OSErr
1238 Handle_unknown_AE(
1239         const AppleEvent    *theAEvent,
1240         AppleEvent          *theReply,
1241         long                refCon)
1242 {
1243     OSErr       error = noErr;
1244
1245     error = HandleUnusedParms(theAEvent);
1246
1247     return error;
1248 }
1249
1250
1251 /*
1252  * Install the various AppleEvent Handlers
1253  */
1254     OSErr
1255 InstallAEHandlers(void)
1256 {
1257     OSErr   error;
1258
1259     /* install open application handler */
1260     error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1261                     NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1262     if (error)
1263     {
1264         return error;
1265     }
1266
1267     /* install quit application handler */
1268     error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1269                     NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1270     if (error)
1271     {
1272         return error;
1273     }
1274
1275     /* install open document handler */
1276     error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1277                     NewAEEventHandlerUPP(HandleODocAE), 0, false);
1278     if (error)
1279     {
1280         return error;
1281     }
1282
1283     /* install print document handler */
1284     error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1285                     NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1286
1287 /* Install Core Suite */
1288 /*  error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1289                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1290
1291     error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1292                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1293
1294     error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1295                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1296
1297     error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1298                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1299
1300     error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1301                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1302
1303     error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1304                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1305
1306     error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1307                     NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1308
1309     error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1310                     NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1311
1312     error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1313                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1314
1315     error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1316                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1317
1318     error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1319                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1320
1321     error = AEInstallEventHandler(kAECoreSuite, kAESave,
1322                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1323
1324     error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1325                     NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1326 */
1327
1328 #ifdef FEAT_CW_EDITOR
1329     /*
1330      * Bind codewarrior support handlers
1331      */
1332     error = AEInstallEventHandler('KAHL', 'GTTX',
1333                     NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1334     if (error)
1335     {
1336         return error;
1337     }
1338     error = AEInstallEventHandler('KAHL', 'SRCH',
1339                     NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1340     if (error)
1341     {
1342         return error;
1343     }
1344     error = AEInstallEventHandler('KAHL', 'MOD ',
1345                     NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1346     if (error)
1347     {
1348         return error;
1349     }
1350 #endif
1351
1352     return error;
1353
1354 }
1355 #endif /* USE_AEVENT */
1356
1357
1358 /*
1359  * Callback function, installed by InstallFontPanelHandler(), below,
1360  * to handle Font Panel events.
1361  */
1362     static OSStatus
1363 FontPanelHandler(
1364         EventHandlerCallRef inHandlerCallRef,
1365         EventRef inEvent,
1366         void *inUserData)
1367 {
1368     if (GetEventKind(inEvent) == kEventFontPanelClosed)
1369     {
1370         gFontPanelInfo.isPanelVisible = false;
1371         return noErr;
1372     }
1373
1374     if (GetEventKind(inEvent) == kEventFontSelection)
1375     {
1376         OSStatus status;
1377         FMFontFamily newFamily;
1378         FMFontSize newSize;
1379         FMFontStyle newStyle;
1380
1381         /* Retrieve the font family ID number. */
1382         status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1383                 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1384                 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1385                 &newFamily);
1386         if (status == noErr)
1387             gFontPanelInfo.family = newFamily;
1388
1389         /* Retrieve the font size. */
1390         status = GetEventParameter(inEvent, kEventParamFMFontSize,
1391                 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1392         if (status == noErr)
1393             gFontPanelInfo.size = newSize;
1394
1395         /* Retrieve the font style (bold, etc.).  Currently unused. */
1396         status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1397                 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1398         if (status == noErr)
1399             gFontPanelInfo.style = newStyle;
1400     }
1401     return noErr;
1402 }
1403
1404
1405     static void
1406 InstallFontPanelHandler(void)
1407 {
1408     EventTypeSpec eventTypes[2];
1409     EventHandlerUPP handlerUPP;
1410     /* EventHandlerRef handlerRef; */
1411
1412     eventTypes[0].eventClass = kEventClassFont;
1413     eventTypes[0].eventKind  = kEventFontSelection;
1414     eventTypes[1].eventClass = kEventClassFont;
1415     eventTypes[1].eventKind  = kEventFontPanelClosed;
1416
1417     handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1418
1419     InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1420             /*userData=*/NULL, /*handlerRef=*/NULL);
1421 }
1422
1423
1424 /*
1425  * Fill the buffer pointed to by outName with the name and size
1426  * of the font currently selected in the Font Panel.
1427  */
1428 #define FONT_STYLE_BUFFER_SIZE 32
1429     static void
1430 GetFontPanelSelection(char_u *outName)
1431 {
1432     Str255          buf;
1433     ByteCount       fontNameLen = 0;
1434     ATSUFontID      fid;
1435     char_u          styleString[FONT_STYLE_BUFFER_SIZE];
1436
1437     if (!outName)
1438         return;
1439
1440     if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1441     {
1442         /* Canonicalize localized font names */
1443         if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1444                     gFontPanelInfo.style, &fid, NULL) != noErr)
1445             return;
1446
1447         /* Request font name with Mac encoding (otherwise we could
1448          * get an unwanted utf-16 name) */
1449         if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1450                     kFontNoScriptCode, kFontNoLanguageCode,
1451                     255, (char *)outName, &fontNameLen, NULL) != noErr)
1452             return;
1453
1454         /* Only encode font size, because style (bold, italic, etc) is
1455          * already part of the font full name */
1456         vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1457                 gFontPanelInfo.size/*,
1458                 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1459                 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1460                 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1461
1462         if ((fontNameLen + STRLEN(styleString)) < 255)
1463             STRCPY(outName + fontNameLen, styleString);
1464     }
1465     else
1466     {
1467         *outName = NUL;
1468     }
1469 }
1470
1471
1472 /*
1473  * ------------------------------------------------------------
1474  * Unfiled yet
1475  * ------------------------------------------------------------
1476  */
1477
1478 /*
1479  *  gui_mac_get_menu_item_index
1480  *
1481  *  Returns the index inside the menu wher
1482  */
1483     short /* Should we return MenuItemIndex? */
1484 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1485 {
1486     short       index;
1487     short       itemIndex = -1;
1488     vimmenu_T   *pBrother;
1489
1490     /* Only menu without parent are the:
1491      * -menu in the menubar
1492      * -popup menu
1493      * -toolbar (guess)
1494      *
1495      * Which are not items anyway.
1496      */
1497     if (pMenu->parent)
1498     {
1499         /* Start from the Oldest Brother */
1500         pBrother = pMenu->parent->children;
1501         index = 1;
1502         while ((pBrother) && (itemIndex == -1))
1503         {
1504             if (pBrother == pMenu)
1505                 itemIndex = index;
1506             index++;
1507             pBrother = pBrother->next;
1508         }
1509     }
1510     return itemIndex;
1511 }
1512
1513     static vimmenu_T *
1514 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1515 {
1516     short       index;
1517     vimmenu_T   *pChildMenu;
1518     vimmenu_T   *pElder = pMenu->parent;
1519
1520
1521     /* Only menu without parent are the:
1522      * -menu in the menubar
1523      * -popup menu
1524      * -toolbar (guess)
1525      *
1526      * Which are not items anyway.
1527      */
1528
1529     if ((pElder) && (pElder->submenu_id == menuID))
1530     {
1531         for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1532             pMenu = pMenu->next;
1533     }
1534     else
1535     {
1536         for (; pMenu != NULL; pMenu = pMenu->next)
1537         {
1538             if (pMenu->children != NULL)
1539             {
1540                 pChildMenu = gui_mac_get_vim_menu
1541                            (menuID, itemIndex, pMenu->children);
1542                 if (pChildMenu)
1543                 {
1544                     pMenu = pChildMenu;
1545                     break;
1546                 }
1547             }
1548         }
1549     }
1550     return pMenu;
1551 }
1552
1553 /*
1554  * ------------------------------------------------------------
1555  * MacOS Feedback procedures
1556  * ------------------------------------------------------------
1557  */
1558     pascal
1559     void
1560 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1561 {
1562     scrollbar_T         *sb;
1563     int                 value, dragging;
1564     ControlHandle       theControlToUse;
1565     int                 dont_scroll_save = dont_scroll;
1566
1567     theControlToUse = dragged_sb;
1568
1569     sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1570
1571     if (sb == NULL)
1572         return;
1573
1574     /* Need to find value by diff between Old Poss New Pos */
1575     value = GetControl32BitValue(theControlToUse);
1576     dragging = (partCode != 0);
1577
1578     /* When "allow_scrollbar" is FALSE still need to remember the new
1579      * position, but don't actually scroll by setting "dont_scroll". */
1580     dont_scroll = !allow_scrollbar;
1581     gui_drag_scrollbar(sb, value, dragging);
1582     dont_scroll = dont_scroll_save;
1583 }
1584
1585     pascal
1586     void
1587 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1588 {
1589     /* TODO: have live support */
1590     scrollbar_T *sb, *sb_info;
1591     long        data;
1592     long        value;
1593     int         page;
1594     int         dragging = FALSE;
1595     int         dont_scroll_save = dont_scroll;
1596
1597     sb = gui_find_scrollbar((long)GetControlReference(theControl));
1598
1599     if (sb == NULL)
1600         return;
1601
1602     if (sb->wp != NULL)         /* Left or right scrollbar */
1603     {
1604         /*
1605          * Careful: need to get scrollbar info out of first (left) scrollbar
1606          * for window, but keep real scrollbar too because we must pass it to
1607          * gui_drag_scrollbar().
1608          */
1609         sb_info = &sb->wp->w_scrollbars[0];
1610
1611         if (sb_info->size > 5)
1612             page = sb_info->size - 2;   /* use two lines of context */
1613         else
1614             page = sb_info->size;
1615     }
1616     else                        /* Bottom scrollbar */
1617     {
1618         sb_info = sb;
1619         page = W_WIDTH(curwin) - 5;
1620     }
1621
1622     switch (partCode)
1623     {
1624         case  kControlUpButtonPart:   data = -1;    break;
1625         case  kControlDownButtonPart: data = 1;     break;
1626         case  kControlPageDownPart:   data = page;  break;
1627         case  kControlPageUpPart:     data = -page; break;
1628                     default: data = 0; break;
1629     }
1630
1631     value = sb_info->value + data;
1632 /*  if (value > sb_info->max)
1633         value = sb_info->max;
1634     else if (value < 0)
1635         value = 0;*/
1636
1637     /* When "allow_scrollbar" is FALSE still need to remember the new
1638      * position, but don't actually scroll by setting "dont_scroll". */
1639     dont_scroll = !allow_scrollbar;
1640     gui_drag_scrollbar(sb, value, dragging);
1641     dont_scroll = dont_scroll_save;
1642
1643     out_flush();
1644     gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1645
1646 /*  if (sb_info->wp != NULL)
1647     {
1648         win_T   *wp;
1649         int     sb_num;
1650
1651         sb_num = 0;
1652         for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1653         sb_num++;
1654
1655         if (wp != NULL)
1656         {
1657             current_scrollbar = sb_num;
1658             scrollbar_value = value;
1659             gui_do_scroll();
1660             gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1661         }
1662     }*/
1663 }
1664
1665 /*
1666  * ------------------------------------------------------------
1667  * MacOS Click Handling procedures
1668  * ------------------------------------------------------------
1669  */
1670
1671
1672 /*
1673  * Handle a click inside the window, it may happens in the
1674  * scrollbar or the contents.
1675  *
1676  * TODO: Add support for potential TOOLBAR
1677  */
1678     void
1679 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1680 {
1681     Point               thePoint;
1682     int_u               vimModifiers;
1683     short               thePortion;
1684     ControlHandle       theControl;
1685     int                 vimMouseButton;
1686     short               dblClick;
1687
1688     thePoint = theEvent->where;
1689     GlobalToLocal(&thePoint);
1690     SelectWindow(whichWindow);
1691
1692     thePortion = FindControl(thePoint, whichWindow, &theControl);
1693
1694     if (theControl != NUL)
1695     {
1696         /* We hit a scollbar */
1697
1698         if (thePortion != kControlIndicatorPart)
1699         {
1700             dragged_sb = theControl;
1701             TrackControl(theControl, thePoint, gScrollAction);
1702             dragged_sb = NULL;
1703         }
1704         else
1705         {
1706             dragged_sb = theControl;
1707 #if 1
1708             TrackControl(theControl, thePoint, gScrollDrag);
1709 #else
1710             TrackControl(theControl, thePoint, NULL);
1711 #endif
1712             /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1713              * button has been released */
1714             gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1715             dragged_sb = NULL;
1716         }
1717     }
1718     else
1719     {
1720         /* We are inside the contents */
1721
1722         /* Convert the CTRL, OPTION, SHIFT and CMD key */
1723         vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1724
1725         /* Defaults to MOUSE_LEFT as there's only one mouse button */
1726         vimMouseButton = MOUSE_LEFT;
1727
1728         /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1729         /* TODO: NEEDED? */
1730         clickIsPopup = FALSE;
1731
1732         if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1733         {
1734             vimMouseButton = MOUSE_RIGHT;
1735             vimModifiers &= ~MOUSE_CTRL;
1736             clickIsPopup = TRUE;
1737         }
1738
1739         /* Is it a double click ? */
1740         dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1741
1742         /* Send the mouse click to Vim */
1743         gui_send_mouse_event(vimMouseButton, thePoint.h,
1744                                           thePoint.v, dblClick, vimModifiers);
1745
1746         /* Create the rectangle around the cursor to detect
1747          * the mouse dragging
1748          */
1749 #if 0
1750         /* TODO: Do we need to this even for the contextual menu?
1751          * It may be require for popup_setpos, but for popup?
1752          */
1753         if (vimMouseButton == MOUSE_LEFT)
1754 #endif
1755         {
1756             SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1757                                 FILL_Y(Y_2_ROW(thePoint.v)),
1758                                 FILL_X(X_2_COL(thePoint.h)+1),
1759                                 FILL_Y(Y_2_ROW(thePoint.v)+1));
1760
1761             dragRectEnbl = TRUE;
1762             dragRectControl = kCreateRect;
1763         }
1764     }
1765 }
1766
1767 /*
1768  * Handle the click in the titlebar (to move the window)
1769  */
1770     void
1771 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1772 {
1773     Rect        movingLimits;
1774     Rect        *movingLimitsPtr = &movingLimits;
1775
1776     /* TODO: may try to prevent move outside screen? */
1777     movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1778     DragWindow(whichWindow, where, movingLimitsPtr);
1779 }
1780
1781 /*
1782  * Handle the click in the grow box
1783  */
1784     void
1785 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1786 {
1787
1788     long            newSize;
1789     unsigned short  newWidth;
1790     unsigned short  newHeight;
1791     Rect            resizeLimits;
1792     Rect            *resizeLimitsPtr = &resizeLimits;
1793     Rect            NewContentRect;
1794
1795     resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1796
1797     /* Set the minimum size */
1798     /* TODO: Should this come from Vim? */
1799     resizeLimits.top = 100;
1800     resizeLimits.left = 100;
1801
1802     newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1803     newWidth  = NewContentRect.right - NewContentRect.left;
1804     newHeight = NewContentRect.bottom - NewContentRect.top;
1805     gui_resize_shell(newWidth, newHeight);
1806     gui_mch_set_bg_color(gui.back_pixel);
1807     gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1808 }
1809
1810 /*
1811  * Handle the click in the zoom box
1812  */
1813     static void
1814 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1815 {
1816     Rect        r;
1817     Point       p;
1818     short       thePart;
1819
1820     /* ideal width is current */
1821     p.h = Columns * gui.char_width + 2 * gui.border_offset;
1822     if (gui.which_scrollbars[SBAR_LEFT])
1823         p.h += gui.scrollbar_width;
1824     if (gui.which_scrollbars[SBAR_RIGHT])
1825         p.h += gui.scrollbar_width;
1826     /* ideal height is as high as we can get */
1827     p.v = 15 * 1024;
1828
1829     thePart = IsWindowInStandardState(whichWindow, &p, &r)
1830                                                        ? inZoomIn : inZoomOut;
1831
1832     if (!TrackBox(whichWindow, theEvent->where, thePart))
1833         return;
1834
1835     /* use returned width */
1836     p.h = r.right - r.left;
1837     /* adjust returned height */
1838     p.v = r.bottom - r.top - 2 * gui.border_offset;
1839     if (gui.which_scrollbars[SBAR_BOTTOM])
1840         p.v -= gui.scrollbar_height;
1841     p.v -= p.v % gui.char_height;
1842     p.v += 2 * gui.border_width;
1843     if (gui.which_scrollbars[SBAR_BOTTOM])
1844         p.v += gui.scrollbar_height;
1845
1846     ZoomWindowIdeal(whichWindow, thePart, &p);
1847
1848     GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1849     gui_resize_shell(r.right - r.left, r.bottom - r.top);
1850     gui_mch_set_bg_color(gui.back_pixel);
1851     gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1852 }
1853
1854 /*
1855  * ------------------------------------------------------------
1856  * MacOS Event Handling procedure
1857  * ------------------------------------------------------------
1858  */
1859
1860 /*
1861  * Handle the Update Event
1862  */
1863
1864     void
1865 gui_mac_doUpdateEvent(EventRecord *event)
1866 {
1867     WindowPtr   whichWindow;
1868     GrafPtr     savePort;
1869     RgnHandle   updateRgn;
1870     Rect        updateRect;
1871     Rect        *updateRectPtr;
1872     Rect        rc;
1873     Rect        growRect;
1874     RgnHandle   saveRgn;
1875
1876
1877     updateRgn = NewRgn();
1878     if (updateRgn == NULL)
1879         return;
1880
1881     /* This could be done by the caller as we
1882      * don't require anything else out of the event
1883      */
1884     whichWindow = (WindowPtr) event->message;
1885
1886     /* Save Current Port */
1887     GetPort(&savePort);
1888
1889     /* Select the Window's Port */
1890     SetPortWindowPort(whichWindow);
1891
1892     /* Let's update the window */
1893       BeginUpdate(whichWindow);
1894         /* Redraw the biggest rectangle covering the area
1895          * to be updated.
1896          */
1897         GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1898 # if 0
1899         /* Would be more appropriate to use the following but doesn't
1900          * seem to work under MacOS X (Dany)
1901          */
1902         GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1903 # endif
1904
1905         /* Use the HLock useless in Carbon? Is it harmful?*/
1906         HLock((Handle) updateRgn);
1907
1908           updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1909 # if 0
1910           /* Code from original Carbon Port (using GetWindowRegion.
1911            * I believe the UpdateRgn is already in local (Dany)
1912            */
1913           GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1914           GlobalToLocal(&botRight(updateRect));
1915 # endif
1916           /* Update the content (i.e. the text) */
1917           gui_redraw(updateRectPtr->left, updateRectPtr->top,
1918                       updateRectPtr->right - updateRectPtr->left,
1919                       updateRectPtr->bottom   - updateRectPtr->top);
1920           /* Clear the border areas if needed */
1921           gui_mch_set_bg_color(gui.back_pixel);
1922           if (updateRectPtr->left < FILL_X(0))
1923           {
1924             SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1925             EraseRect(&rc);
1926           }
1927           if (updateRectPtr->top < FILL_Y(0))
1928           {
1929             SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1930             EraseRect(&rc);
1931           }
1932           if (updateRectPtr->right > FILL_X(Columns))
1933           {
1934             SetRect(&rc, FILL_X(Columns), 0,
1935                            FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1936             EraseRect(&rc);
1937           }
1938           if (updateRectPtr->bottom > FILL_Y(Rows))
1939           {
1940             SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1941                                             FILL_Y(Rows) + gui.border_offset);
1942             EraseRect(&rc);
1943           }
1944         HUnlock((Handle) updateRgn);
1945         DisposeRgn(updateRgn);
1946
1947         /* Update scrollbars */
1948         DrawControls(whichWindow);
1949
1950         /* Update the GrowBox */
1951         /* Taken from FAQ 33-27 */
1952         saveRgn = NewRgn();
1953         GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1954         GetClip(saveRgn);
1955         ClipRect(&growRect);
1956         DrawGrowIcon(whichWindow);
1957         SetClip(saveRgn);
1958         DisposeRgn(saveRgn);
1959       EndUpdate(whichWindow);
1960
1961     /* Restore original Port */
1962     SetPort(savePort);
1963 }
1964
1965 /*
1966  * Handle the activate/deactivate event
1967  * (apply to a window)
1968  */
1969     void
1970 gui_mac_doActivateEvent(EventRecord *event)
1971 {
1972     WindowPtr   whichWindow;
1973
1974     whichWindow = (WindowPtr) event->message;
1975     /* Dim scrollbars */
1976     if (whichWindow == gui.VimWindow)
1977     {
1978         ControlRef rootControl;
1979         GetRootControl(gui.VimWindow, &rootControl);
1980         if ((event->modifiers) & activeFlag)
1981             ActivateControl(rootControl);
1982         else
1983             DeactivateControl(rootControl);
1984     }
1985
1986     /* Activate */
1987     gui_focus_change((event->modifiers) & activeFlag);
1988 }
1989
1990
1991 /*
1992  * Handle the suspend/resume event
1993  * (apply to the application)
1994  */
1995     void
1996 gui_mac_doSuspendEvent(EventRecord *event)
1997 {
1998     /* The frontmost application just changed */
1999
2000     /* NOTE: the suspend may happen before the deactivate
2001      *       seen on MacOS X
2002      */
2003
2004     /* May not need to change focus as the window will
2005      * get an activate/deactivate event
2006      */
2007     if (event->message & 1)
2008         /* Resume */
2009         gui_focus_change(TRUE);
2010     else
2011         /* Suspend */
2012         gui_focus_change(FALSE);
2013 }
2014
2015 /*
2016  * Handle the key
2017  */
2018 #ifdef USE_CARBONKEYHANDLER
2019     static pascal OSStatus
2020 gui_mac_handle_window_activate(
2021         EventHandlerCallRef nextHandler,
2022         EventRef            theEvent,
2023         void                *data)
2024 {
2025     UInt32 eventClass = GetEventClass(theEvent);
2026     UInt32 eventKind  = GetEventKind(theEvent);
2027
2028     if (eventClass == kEventClassWindow)
2029     {
2030         switch (eventKind)
2031         {
2032             case kEventWindowActivated:
2033 #if defined(USE_IM_CONTROL)
2034                 im_on_window_switch(TRUE);
2035 #endif
2036                 return noErr;
2037
2038             case kEventWindowDeactivated:
2039 #if defined(USE_IM_CONTROL)
2040                 im_on_window_switch(FALSE);
2041 #endif
2042                 return noErr;
2043         }
2044     }
2045
2046     return eventNotHandledErr;
2047 }
2048
2049     static pascal OSStatus
2050 gui_mac_handle_text_input(
2051         EventHandlerCallRef nextHandler,
2052         EventRef            theEvent,
2053         void                *data)
2054 {
2055     UInt32 eventClass = GetEventClass(theEvent);
2056     UInt32 eventKind  = GetEventKind(theEvent);
2057
2058     if (eventClass != kEventClassTextInput)
2059         return eventNotHandledErr;
2060
2061     if ((kEventTextInputUpdateActiveInputArea != eventKind) &&
2062         (kEventTextInputUnicodeForKeyEvent    != eventKind) &&
2063         (kEventTextInputOffsetToPos           != eventKind) &&
2064         (kEventTextInputPosToOffset           != eventKind) &&
2065         (kEventTextInputGetSelectedText       != eventKind))
2066               return eventNotHandledErr;
2067
2068     switch (eventKind)
2069     {
2070     case kEventTextInputUpdateActiveInputArea:
2071         return gui_mac_update_input_area(nextHandler, theEvent);
2072     case kEventTextInputUnicodeForKeyEvent:
2073         return gui_mac_unicode_key_event(nextHandler, theEvent);
2074
2075     case kEventTextInputOffsetToPos:
2076     case kEventTextInputPosToOffset:
2077     case kEventTextInputGetSelectedText:
2078         break;
2079     }
2080
2081     return eventNotHandledErr;
2082 }
2083
2084     static pascal
2085 OSStatus gui_mac_update_input_area(
2086         EventHandlerCallRef nextHandler,
2087         EventRef            theEvent)
2088 {
2089     return eventNotHandledErr;
2090 }
2091
2092 static int dialog_busy = FALSE;     /* TRUE when gui_mch_dialog() wants the
2093                                        keys */
2094
2095 # define INLINE_KEY_BUFFER_SIZE 80
2096     static pascal OSStatus
2097 gui_mac_unicode_key_event(
2098         EventHandlerCallRef nextHandler,
2099         EventRef            theEvent)
2100 {
2101     /* Multibyte-friendly key event handler */
2102     OSStatus    err = -1;
2103     UInt32      actualSize;
2104     UniChar     *text;
2105     char_u      result[INLINE_KEY_BUFFER_SIZE];
2106     short       len = 0;
2107     UInt32      key_sym;
2108     char        charcode;
2109     int         key_char;
2110     UInt32      modifiers, vimModifiers;
2111     size_t      encLen;
2112     char_u      *to = NULL;
2113     Boolean     isSpecial = FALSE;
2114     int         i;
2115     EventRef    keyEvent;
2116
2117     /* Mask the mouse (as per user setting) */
2118     if (p_mh)
2119         ObscureCursor();
2120
2121     /* Don't use the keys when the dialog wants them. */
2122     if (dialog_busy)
2123         return eventNotHandledErr;
2124
2125     if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2126                 typeUnicodeText, NULL, 0, &actualSize, NULL))
2127         return eventNotHandledErr;
2128
2129     text = (UniChar *)alloc(actualSize);
2130     if (!text)
2131         return eventNotHandledErr;
2132
2133     err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2134             typeUnicodeText, NULL, actualSize, NULL, text);
2135     require_noerr(err, done);
2136
2137     err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2138             typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2139     require_noerr(err, done);
2140
2141     err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2142             typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2143     require_noerr(err, done);
2144
2145     err = GetEventParameter(keyEvent, kEventParamKeyCode,
2146             typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2147     require_noerr(err, done);
2148
2149     err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2150             typeChar, NULL, sizeof(char), NULL, &charcode);
2151     require_noerr(err, done);
2152
2153 #ifndef USE_CMD_KEY
2154     if (modifiers & cmdKey)
2155         goto done;  /* Let system handle Cmd+... */
2156 #endif
2157
2158     key_char = charcode;
2159     vimModifiers = EventModifiers2VimModifiers(modifiers);
2160
2161     /* Find the special key (eg., for cursor keys) */
2162     if (actualSize <= sizeof(UniChar) &&
2163             ((text[0] < 0x20) || (text[0] == 0x7f)))
2164     {
2165         for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2166             if (special_keys[i].key_sym == key_sym)
2167             {
2168                 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2169                         special_keys[i].vim_code1);
2170                 key_char = simplify_key(key_char,
2171                         (int *)&vimModifiers);
2172                 isSpecial = TRUE;
2173                 break;
2174             }
2175     }
2176
2177     /* Intercept CMD-. and CTRL-c */
2178     if (((modifiers & controlKey) && key_char == 'c') ||
2179             ((modifiers & cmdKey) && key_char == '.'))
2180         got_int = TRUE;
2181
2182     if (!isSpecial)
2183     {
2184         /* remove SHIFT for keys that are already shifted, e.g.,
2185          * '(' and '*' */
2186         if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2187             vimModifiers &= ~MOD_MASK_SHIFT;
2188
2189         /* remove CTRL from keys that already have it */
2190         if (key_char < 0x20)
2191             vimModifiers &= ~MOD_MASK_CTRL;
2192
2193         /* don't process unicode characters here */
2194         if (!IS_SPECIAL(key_char))
2195         {
2196             /* Following code to simplify and consolidate vimModifiers
2197              * taken liberally from gui_w48.c */
2198             key_char = simplify_key(key_char, (int *)&vimModifiers);
2199
2200             /* Interpret META, include SHIFT, etc. */
2201             key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2202             if (key_char == CSI)
2203                 key_char = K_CSI;
2204
2205             if (IS_SPECIAL(key_char))
2206                 isSpecial = TRUE;
2207         }
2208     }
2209
2210     if (vimModifiers)
2211     {
2212         result[len++] = CSI;
2213         result[len++] = KS_MODIFIER;
2214         result[len++] = vimModifiers;
2215     }
2216
2217     if (isSpecial && IS_SPECIAL(key_char))
2218     {
2219         result[len++] = CSI;
2220         result[len++] = K_SECOND(key_char);
2221         result[len++] = K_THIRD(key_char);
2222     }
2223     else
2224     {
2225         encLen = actualSize;
2226         to = mac_utf16_to_enc(text, actualSize, &encLen);
2227         if (to)
2228         {
2229             /* This is basically add_to_input_buf_csi() */
2230             for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2231             {
2232                 result[len++] = to[i];
2233                 if (to[i] == CSI)
2234                 {
2235                     result[len++] = KS_EXTRA;
2236                     result[len++] = (int)KE_CSI;
2237                 }
2238             }
2239             vim_free(to);
2240         }
2241     }
2242
2243     add_to_input_buf(result, len);
2244     err = noErr;
2245
2246 done:
2247     vim_free(text);
2248     if (err == noErr)
2249     {
2250         /* Fake event to wake up WNE (required to get
2251          * key repeat working */
2252         PostEvent(keyUp, 0);
2253         return noErr;
2254     }
2255
2256     return eventNotHandledErr;
2257 }
2258 #else
2259     void
2260 gui_mac_doKeyEvent(EventRecord *theEvent)
2261 {
2262     /* TODO: add support for COMMAND KEY */
2263     long                menu;
2264     unsigned char       string[20];
2265     short               num, i;
2266     short               len = 0;
2267     KeySym              key_sym;
2268     int                 key_char;
2269     int                 modifiers;
2270     int                 simplify = FALSE;
2271
2272     /* Mask the mouse (as per user setting) */
2273     if (p_mh)
2274         ObscureCursor();
2275
2276     /* Get the key code and it's ASCII representation */
2277     key_sym = ((theEvent->message & keyCodeMask) >> 8);
2278     key_char = theEvent->message & charCodeMask;
2279     num = 1;
2280
2281     /* Intercept CTRL-C */
2282     if (theEvent->modifiers & controlKey)
2283     {
2284         if (key_char == Ctrl_C && ctrl_c_interrupts)
2285             got_int = TRUE;
2286         else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2287                 && (key_char == '2' || key_char == '6'))
2288         {
2289             /* CTRL-^ and CTRL-@ don't work in the normal way. */
2290             if (key_char == '2')
2291                 key_char = Ctrl_AT;
2292             else
2293                 key_char = Ctrl_HAT;
2294             theEvent->modifiers = 0;
2295         }
2296     }
2297
2298     /* Intercept CMD-. */
2299     if (theEvent->modifiers & cmdKey)
2300         if (key_char == '.')
2301             got_int = TRUE;
2302
2303     /* Handle command key as per menu */
2304     /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2305     if (theEvent->modifiers & cmdKey)
2306         /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2307          * Why the mouse button? */
2308         if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2309         {
2310             menu = MenuKey(key_char);
2311             if (HiWord(menu))
2312             {
2313                 gui_mac_handle_menu(menu);
2314                 return;
2315             }
2316         }
2317
2318     /* Convert the modifiers */
2319     modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2320
2321
2322     /* Handle special keys. */
2323 #if 0
2324     /* Why has this been removed? */
2325     if  (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2326 #endif
2327     {
2328         /* Find the special key (for non-printable keyt_char) */
2329         if  ((key_char < 0x20) || (key_char == 0x7f))
2330             for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2331                 if (special_keys[i].key_sym == key_sym)
2332                 {
2333 # if 0
2334                     /* We currently don't have not so special key */
2335                     if (special_keys[i].vim_code1 == NUL)
2336                         key_char = special_keys[i].vim_code0;
2337                     else
2338 # endif
2339                         key_char = TO_SPECIAL(special_keys[i].vim_code0,
2340                                                 special_keys[i].vim_code1);
2341                     simplify = TRUE;
2342                     break;
2343                 }
2344     }
2345
2346     /* For some keys the modifier is included in the char itself. */
2347     if (simplify || key_char == TAB || key_char == ' ')
2348         key_char = simplify_key(key_char, &modifiers);
2349
2350     /* Add the modifier to the input bu if needed */
2351     /* Do not want SHIFT-A or CTRL-A with modifier */
2352     if (!IS_SPECIAL(key_char)
2353             && key_sym != vk_Space
2354             && key_sym != vk_Tab
2355             && key_sym != vk_Return
2356             && key_sym != vk_Enter
2357             && key_sym != vk_Esc)
2358     {
2359 #if 1
2360     /* Clear modifiers when only one modifier is set */
2361         if ((modifiers == MOD_MASK_SHIFT)
2362                 || (modifiers == MOD_MASK_CTRL)
2363                 || (modifiers == MOD_MASK_ALT))
2364             modifiers = 0;
2365 #else
2366         if (modifiers & MOD_MASK_CTRL)
2367             modifiers = modifiers & ~MOD_MASK_CTRL;
2368         if (modifiers & MOD_MASK_ALT)
2369             modifiers = modifiers & ~MOD_MASK_ALT;
2370         if (modifiers & MOD_MASK_SHIFT)
2371             modifiers = modifiers & ~MOD_MASK_SHIFT;
2372 #endif
2373     }
2374         if (modifiers)
2375         {
2376             string[len++] = CSI;
2377             string[len++] = KS_MODIFIER;
2378             string[len++] = modifiers;
2379         }
2380
2381         if (IS_SPECIAL(key_char))
2382         {
2383             string[len++] = CSI;
2384             string[len++] = K_SECOND(key_char);
2385             string[len++] = K_THIRD(key_char);
2386         }
2387         else
2388         {
2389 #ifdef FEAT_MBYTE
2390             /* Convert characters when needed (e.g., from MacRoman to latin1).
2391              * This doesn't work for the NUL byte. */
2392             if (input_conv.vc_type != CONV_NONE && key_char > 0)
2393             {
2394                 char_u  from[2], *to;
2395                 int     l;
2396
2397                 from[0] = key_char;
2398                 from[1] = NUL;
2399                 l = 1;
2400                 to = string_convert(&input_conv, from, &l);
2401                 if (to != NULL)
2402                 {
2403                     for (i = 0; i < l && len < 19; i++)
2404                     {
2405                         if (to[i] == CSI)
2406                         {
2407                             string[len++] = KS_EXTRA;
2408                             string[len++] = KE_CSI;
2409                         }
2410                         else
2411                             string[len++] = to[i];
2412                     }
2413                     vim_free(to);
2414                 }
2415                 else
2416                     string[len++] = key_char;
2417             }
2418             else
2419 #endif
2420                 string[len++] = key_char;
2421         }
2422
2423         if (len == 1 && string[0] == CSI)
2424         {
2425             /* Turn CSI into K_CSI. */
2426             string[ len++ ] = KS_EXTRA;
2427             string[ len++ ] = KE_CSI;
2428         }
2429
2430     add_to_input_buf(string, len);
2431 }
2432 #endif
2433
2434 /*
2435  * Handle MouseClick
2436  */
2437     void
2438 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2439 {
2440     short               thePart;
2441     WindowPtr           whichWindow;
2442
2443     thePart = FindWindow(theEvent->where, &whichWindow);
2444
2445 #ifdef FEAT_GUI_TABLINE
2446     /* prevent that the vim window size changes if it's activated by a
2447        click into the tab pane */
2448     if (whichWindow == drawer)
2449         return;
2450 #endif
2451
2452     switch (thePart)
2453     {
2454         case (inDesk):
2455             /* TODO: what to do? */
2456             break;
2457
2458         case (inMenuBar):
2459             gui_mac_handle_menu(MenuSelect(theEvent->where));
2460             break;
2461
2462         case (inContent):
2463             gui_mac_doInContentClick(theEvent, whichWindow);
2464             break;
2465
2466         case (inDrag):
2467             gui_mac_doInDragClick(theEvent->where, whichWindow);
2468             break;
2469
2470         case (inGrow):
2471             gui_mac_doInGrowClick(theEvent->where, whichWindow);
2472             break;
2473
2474         case (inGoAway):
2475             if (TrackGoAway(whichWindow, theEvent->where))
2476                 gui_shell_closed();
2477             break;
2478
2479         case (inZoomIn):
2480         case (inZoomOut):
2481             gui_mac_doInZoomClick(theEvent, whichWindow);
2482             break;
2483     }
2484 }
2485
2486 /*
2487  * Handle MouseMoved
2488  * [this event is a moving in and out of a region]
2489  */
2490     void
2491 gui_mac_doMouseMovedEvent(EventRecord *event)
2492 {
2493     Point   thePoint;
2494     int_u   vimModifiers;
2495
2496     thePoint = event->where;
2497     GlobalToLocal(&thePoint);
2498     vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2499
2500     if (!Button())
2501         gui_mouse_moved(thePoint.h, thePoint.v);
2502     else
2503         if (!clickIsPopup)
2504             gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2505                                              thePoint.v, FALSE, vimModifiers);
2506
2507     /* Reset the region from which we move in and out */
2508     SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2509                         FILL_Y(Y_2_ROW(thePoint.v)),
2510                         FILL_X(X_2_COL(thePoint.h)+1),
2511                         FILL_Y(Y_2_ROW(thePoint.v)+1));
2512
2513     if (dragRectEnbl)
2514         dragRectControl = kCreateRect;
2515
2516 }
2517
2518 /*
2519  * Handle the mouse release
2520  */
2521     void
2522 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2523 {
2524     Point   thePoint;
2525     int_u   vimModifiers;
2526
2527     /* TODO: Properly convert the Contextual menu mouse-up */
2528     /*       Potential source of the double menu */
2529     lastMouseTick = theEvent->when;
2530     dragRectEnbl = FALSE;
2531     dragRectControl = kCreateEmpty;
2532     thePoint = theEvent->where;
2533     GlobalToLocal(&thePoint);
2534
2535     vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2536     if (clickIsPopup)
2537     {
2538         vimModifiers &= ~MOUSE_CTRL;
2539         clickIsPopup = FALSE;
2540     }
2541     gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2542 }
2543
2544     static pascal OSStatus
2545 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2546                                                                    void *data)
2547 {
2548     Point       point;
2549     Rect        bounds;
2550     UInt32      mod;
2551     SInt32      delta;
2552     int_u       vim_mod;
2553     EventMouseWheelAxis axis;
2554
2555     if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis,
2556                           typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis)
2557             && axis != kEventMouseWheelAxisY)
2558         goto bail; /* Vim only does up-down scrolling */
2559
2560     if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta,
2561                               typeSInt32, NULL, sizeof(SInt32), NULL, &delta))
2562         goto bail;
2563     if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation,
2564                               typeQDPoint, NULL, sizeof(Point), NULL, &point))
2565         goto bail;
2566     if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers,
2567                                 typeUInt32, NULL, sizeof(UInt32), NULL, &mod))
2568         goto bail;
2569
2570     vim_mod = 0;
2571     if (mod & shiftKey)
2572         vim_mod |= MOUSE_SHIFT;
2573     if (mod & controlKey)
2574         vim_mod |= MOUSE_CTRL;
2575     if (mod & optionKey)
2576         vim_mod |= MOUSE_ALT;
2577
2578     if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2579     {
2580         point.h -= bounds.left;
2581         point.v -= bounds.top;
2582     }
2583
2584     gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2585                                             point.h, point.v, FALSE, vim_mod);
2586
2587     /* post a bogus event to wake up WaitNextEvent */
2588     PostEvent(keyUp, 0);
2589
2590     return noErr;
2591
2592 bail:
2593     /*
2594      * when we fail give any additional callback handler a chance to perform
2595      * it's actions
2596      */
2597     return CallNextEventHandler(nextHandler, theEvent);
2598 }
2599
2600      void
2601 gui_mch_mousehide(int hide)
2602 {
2603     /* TODO */
2604 }
2605
2606 #if 0
2607
2608 /*
2609  * This would be the normal way of invoking the contextual menu
2610  * but the Vim API doesn't seem to a support a request to get
2611  * the menu that we should display
2612  */
2613     void
2614 gui_mac_handle_contextual_menu(event)
2615     EventRecord *event;
2616 {
2617 /*
2618  *  Clone PopUp to use menu
2619  *  Create a object descriptor for the current selection
2620  *  Call the procedure
2621  */
2622
2623 //  Call to Handle Popup
2624     OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2625
2626     if (status != noErr)
2627         return;
2628
2629     if (CntxType == kCMMenuItemSelected)
2630     {
2631         /* Handle the menu CntxMenuID, CntxMenuItem */
2632         /* The submenu can be handle directly by gui_mac_handle_menu */
2633         /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2634         gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2635     }
2636     else if (CntxMenuID == kCMShowHelpSelected)
2637     {
2638         /* Should come up with the help */
2639     }
2640
2641 }
2642 #endif
2643
2644 /*
2645  * Handle menubar selection
2646  */
2647     void
2648 gui_mac_handle_menu(long menuChoice)
2649 {
2650     short       menu = HiWord(menuChoice);
2651     short       item = LoWord(menuChoice);
2652     vimmenu_T   *theVimMenu = root_menu;
2653
2654     if (menu == 256)  /* TODO: use constant or gui.xyz */
2655     {
2656         if (item == 1)
2657             gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2658     }
2659     else if (item != 0)
2660     {
2661         theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2662
2663         if (theVimMenu)
2664             gui_menu_cb(theVimMenu);
2665     }
2666     HiliteMenu(0);
2667 }
2668
2669 /*
2670  * Dispatch the event to proper handler
2671  */
2672
2673     void
2674 gui_mac_handle_event(EventRecord *event)
2675 {
2676     OSErr       error;
2677
2678     /* Handle contextual menu right now (if needed) */
2679     if (IsShowContextualMenuClick(event))
2680     {
2681 # if 0
2682         gui_mac_handle_contextual_menu(event);
2683 # else
2684         gui_mac_doMouseDownEvent(event);
2685 # endif
2686         return;
2687     }
2688
2689     /* Handle normal event */
2690     switch (event->what)
2691     {
2692 #ifndef USE_CARBONKEYHANDLER
2693         case (keyDown):
2694         case (autoKey):
2695             gui_mac_doKeyEvent(event);
2696             break;
2697 #endif
2698         case (keyUp):
2699             /* We don't care about when the key is released */
2700             break;
2701
2702         case (mouseDown):
2703             gui_mac_doMouseDownEvent(event);
2704             break;
2705
2706         case (mouseUp):
2707             gui_mac_doMouseUpEvent(event);
2708             break;
2709
2710         case (updateEvt):
2711             gui_mac_doUpdateEvent(event);
2712             break;
2713
2714         case (diskEvt):
2715             /* We don't need special handling for disk insertion */
2716             break;
2717
2718         case (activateEvt):
2719             gui_mac_doActivateEvent(event);
2720             break;
2721
2722         case (osEvt):
2723             switch ((event->message >> 24) & 0xFF)
2724             {
2725                 case (0xFA): /* mouseMovedMessage */
2726                     gui_mac_doMouseMovedEvent(event);
2727                     break;
2728                 case (0x01): /* suspendResumeMessage */
2729                     gui_mac_doSuspendEvent(event);
2730                     break;
2731             }
2732             break;
2733
2734 #ifdef USE_AEVENT
2735         case (kHighLevelEvent):
2736             /* Someone's talking to us, through AppleEvents */
2737             error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2738             break;
2739 #endif
2740     }
2741 }
2742
2743 /*
2744  * ------------------------------------------------------------
2745  * Unknown Stuff
2746  * ------------------------------------------------------------
2747  */
2748
2749
2750     GuiFont
2751 gui_mac_find_font(char_u *font_name)
2752 {
2753     char_u      c;
2754     char_u      *p;
2755     char_u      pFontName[256];
2756     Str255      systemFontname;
2757     short       font_id;
2758     short       size=9;
2759     GuiFont     font;
2760 #if 0
2761     char_u      *fontNamePtr;
2762 #endif
2763
2764     for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2765         ;
2766
2767     c = *p;
2768     *p = 0;
2769
2770 #if 1
2771     STRCPY(&pFontName[1], font_name);
2772     pFontName[0] = STRLEN(font_name);
2773     *p = c;
2774
2775     /* Get the font name, minus the style suffix (:h, etc) */
2776     char_u fontName[256];
2777     char_u *styleStart = vim_strchr(font_name, ':');
2778     size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2779     vim_strncpy(fontName, font_name, fontNameLen);
2780
2781     ATSUFontID fontRef;
2782     FMFontStyle fontStyle;
2783     font_id = 0;
2784
2785     if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2786                 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2787                 &fontRef) == noErr)
2788     {
2789         if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2790             font_id = 0;
2791     }
2792
2793     if (font_id == 0)
2794     {
2795         /*
2796          * Try again, this time replacing underscores in the font name
2797          * with spaces (:set guifont allows the two to be used
2798          * interchangeably; the Font Manager doesn't).
2799          */
2800         int i, changed = FALSE;
2801
2802         for (i = pFontName[0]; i > 0; --i)
2803         {
2804             if (pFontName[i] == '_')
2805             {
2806                 pFontName[i] = ' ';
2807                 changed = TRUE;
2808             }
2809         }
2810         if (changed)
2811             if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2812                         kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2813                         kFontNoLanguageCode, &fontRef) == noErr)
2814             {
2815                 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2816                     font_id = 0;
2817             }
2818     }
2819
2820 #else
2821     /* name = C2Pascal_save(menu->dname); */
2822     fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2823
2824     GetFNum(fontNamePtr, &font_id);
2825 #endif
2826
2827
2828     if (font_id == 0)
2829     {
2830         /* Oups, the system font was it the one the user want */
2831
2832         if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2833             return NOFONT;
2834         if (!EqualString(pFontName, systemFontname, false, false))
2835             return NOFONT;
2836     }
2837     if (*p == ':')
2838     {
2839         p++;
2840         /* Set the values found after ':' */
2841         while (*p)
2842         {
2843             switch (*p++)
2844             {
2845                 case 'h':
2846                     size = points_to_pixels(p, &p, TRUE);
2847                     break;
2848                     /*
2849                      * TODO: Maybe accept width and styles
2850                      */
2851             }
2852             while (*p == ':')
2853                 p++;
2854         }
2855     }
2856
2857     if (size < 1)
2858         size = 1;   /* Avoid having a size of 0 with system font */
2859
2860     font = (size << 16) + ((long) font_id & 0xFFFF);
2861
2862     return font;
2863 }
2864
2865 /*
2866  * ------------------------------------------------------------
2867  * GUI_MCH functionality
2868  * ------------------------------------------------------------
2869  */
2870
2871 /*
2872  * Parse the GUI related command-line arguments.  Any arguments used are
2873  * deleted from argv, and *argc is decremented accordingly.  This is called
2874  * when vim is started, whether or not the GUI has been started.
2875  */
2876     void
2877 gui_mch_prepare(int *argc, char **argv)
2878 {
2879     /* TODO: Move most of this stuff toward gui_mch_init */
2880 #ifdef USE_EXE_NAME
2881     FSSpec      applDir;
2882 # ifndef USE_FIND_BUNDLE_PATH
2883     short       applVRefNum;
2884     long        applDirID;
2885     Str255      volName;
2886 # else
2887     ProcessSerialNumber psn;
2888     FSRef       applFSRef;
2889 # endif
2890 #endif
2891
2892 #if 0
2893     InitCursor();
2894
2895     RegisterAppearanceClient();
2896
2897 #ifdef USE_AEVENT
2898     (void) InstallAEHandlers();
2899 #endif
2900
2901     pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2902
2903     AppendMenu(pomme, "\pAbout VIM");
2904
2905     InsertMenu(pomme, 0);
2906
2907     DrawMenuBar();
2908
2909
2910 #ifndef USE_OFFSETED_WINDOW
2911     SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2912 #else
2913     SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2914 #endif
2915
2916
2917     CreateNewWindow(kDocumentWindowClass,
2918                 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2919                 &windRect, &gui.VimWindow);
2920     SetPortWindowPort(gui.VimWindow);
2921
2922     gui.char_width = 7;
2923     gui.char_height = 11;
2924     gui.char_ascent = 6;
2925     gui.num_rows = 24;
2926     gui.num_cols = 80;
2927     gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2928
2929     gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2930     gScrollDrag   = NewControlActionUPP(gui_mac_drag_thumb);
2931
2932     dragRectEnbl = FALSE;
2933     dragRgn = NULL;
2934     dragRectControl = kCreateEmpty;
2935     cursorRgn = NewRgn();
2936 #endif
2937 #ifdef USE_EXE_NAME
2938 # ifndef USE_FIND_BUNDLE_PATH
2939     HGetVol(volName, &applVRefNum, &applDirID);
2940     /* TN2015: mention a possible bad VRefNum */
2941     FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2942 # else
2943     /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2944      * of TN2015
2945      */
2946     (void)GetCurrentProcess(&psn);
2947     /* if (err != noErr) return err; */
2948
2949     (void)GetProcessBundleLocation(&psn, &applFSRef);
2950     /* if (err != noErr) return err; */
2951
2952     (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2953
2954     /* This technic return NIL when we disallow_gui */
2955 # endif
2956     exe_name = FullPathFromFSSpec_save(applDir);
2957 #endif
2958 }
2959
2960 #ifndef ALWAYS_USE_GUI
2961 /*
2962  * Check if the GUI can be started.  Called before gvimrc is sourced.
2963  * Return OK or FAIL.
2964  */
2965     int
2966 gui_mch_init_check(void)
2967 {
2968     /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2969      * using the >console
2970      */
2971     if (disallow_gui) /* see main.c for reason to disallow */
2972         return FAIL;
2973     return OK;
2974 }
2975 #endif
2976
2977     static OSErr
2978 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2979 {
2980     int         x, y;
2981     int_u       modifiers;
2982     char_u      **fnames = NULL;
2983     int         count;
2984     int         i, j;
2985
2986     /* Get drop position, modifiers and count of items */
2987     {
2988         Point   point;
2989         SInt16  mouseUpModifiers;
2990         UInt16  countItem;
2991
2992         GetDragMouse(theDrag, &point, NULL);
2993         GlobalToLocal(&point);
2994         x = point.h;
2995         y = point.v;
2996         GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2997         modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2998         CountDragItems(theDrag, &countItem);
2999         count = countItem;
3000     }
3001
3002     fnames = (char_u **)alloc(count * sizeof(char_u *));
3003     if (fnames == NULL)
3004         return dragNotAcceptedErr;
3005
3006     /* Get file names dropped */
3007     for (i = j = 0; i < count; ++i)
3008     {
3009         DragItemRef     item;
3010         OSErr           err;
3011         Size            size;
3012         FlavorType      type = flavorTypeHFS;
3013         HFSFlavor       hfsFlavor;
3014
3015         fnames[i] = NULL;
3016         GetDragItemReferenceNumber(theDrag, i + 1, &item);
3017         err = GetFlavorDataSize(theDrag, item, type, &size);
3018         if (err != noErr || size > sizeof(hfsFlavor))
3019             continue;
3020         err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
3021         if (err != noErr)
3022             continue;
3023         fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
3024     }
3025     count = j;
3026
3027     gui_handle_drop(x, y, modifiers, fnames, count);
3028
3029     /* Fake mouse event to wake from stall */
3030     PostEvent(mouseUp, 0);
3031
3032     return noErr;
3033 }
3034
3035 /*
3036  * Initialise the GUI.  Create all the windows, set up all the call-backs
3037  * etc.
3038  */
3039     int
3040 gui_mch_init(void)
3041 {
3042     /* TODO: Move most of this stuff toward gui_mch_init */
3043     Rect            windRect;
3044     MenuHandle      pomme;
3045     EventHandlerRef mouseWheelHandlerRef;
3046     EventTypeSpec   eventTypeSpec;
3047     ControlRef      rootControl;
3048
3049     if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
3050         gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
3051
3052 #if 1
3053     InitCursor();
3054
3055     RegisterAppearanceClient();
3056
3057 #ifdef USE_AEVENT
3058     (void) InstallAEHandlers();
3059 #endif
3060
3061     pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
3062
3063     AppendMenu(pomme, "\pAbout VIM");
3064
3065     InsertMenu(pomme, 0);
3066
3067     DrawMenuBar();
3068
3069
3070 #ifndef USE_OFFSETED_WINDOW
3071     SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
3072 #else
3073     SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
3074 #endif
3075
3076     gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
3077                         zoomDocProc,
3078                         (WindowPtr)-1L, true, 0);
3079     CreateRootControl(gui.VimWindow, &rootControl);
3080     InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
3081             gui.VimWindow, NULL);
3082     SetPortWindowPort(gui.VimWindow);
3083
3084     gui.char_width = 7;
3085     gui.char_height = 11;
3086     gui.char_ascent = 6;
3087     gui.num_rows = 24;
3088     gui.num_cols = 80;
3089     gui.in_focus = TRUE; /* For the moment -> syn. of front application */
3090
3091     gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
3092     gScrollDrag   = NewControlActionUPP(gui_mac_drag_thumb);
3093
3094     /* Install Carbon event callbacks. */
3095     (void)InstallFontPanelHandler();
3096
3097     dragRectEnbl = FALSE;
3098     dragRgn = NULL;
3099     dragRectControl = kCreateEmpty;
3100     cursorRgn = NewRgn();
3101 #endif
3102     /* Display any pending error messages */
3103     display_errors();
3104
3105     /* Get background/foreground colors from system */
3106     /* TODO: do the appropriate call to get real defaults */
3107     gui.norm_pixel = 0x00000000;
3108     gui.back_pixel = 0x00FFFFFF;
3109
3110     /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3111      * file). */
3112     set_normal_colors();
3113
3114     /*
3115      * Check that none of the colors are the same as the background color.
3116      * Then store the current values as the defaults.
3117      */
3118     gui_check_colors();
3119     gui.def_norm_pixel = gui.norm_pixel;
3120     gui.def_back_pixel = gui.back_pixel;
3121
3122     /* Get the colors for the highlight groups (gui_check_colors() might have
3123      * changed them) */
3124     highlight_gui_started();
3125
3126     /*
3127      * Setting the gui constants
3128      */
3129 #ifdef FEAT_MENU
3130     gui.menu_height = 0;
3131 #endif
3132     gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3133     gui.border_offset = gui.border_width = 2;
3134
3135     /* If Quartz-style text anti aliasing is available (see
3136        gui_mch_draw_string() below), enable it for all font sizes. */
3137     vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3138
3139     eventTypeSpec.eventClass = kEventClassMouse;
3140     eventTypeSpec.eventKind = kEventMouseWheelMoved;
3141     mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3142     if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3143                                  &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3144     {
3145         mouseWheelHandlerRef = NULL;
3146         DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3147         mouseWheelHandlerUPP = NULL;
3148     }
3149
3150 #ifdef USE_CARBONKEYHANDLER
3151     InterfaceTypeList supportedServices = { kUnicodeDocument };
3152     NewTSMDocument(1, supportedServices, &gTSMDocument, 0);
3153
3154     /* We don't support inline input yet, use input window by default */
3155     UseInputWindow(gTSMDocument, TRUE);
3156
3157     /* Should we activate the document by default? */
3158     // ActivateTSMDocument(gTSMDocument);
3159
3160     EventTypeSpec textEventTypes[] = {
3161         { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
3162         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
3163         { kEventClassTextInput, kEventTextInputPosToOffset },
3164         { kEventClassTextInput, kEventTextInputOffsetToPos },
3165     };
3166
3167     keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input);
3168     if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP,
3169                                                 NR_ELEMS(textEventTypes),
3170                                                 textEventTypes, NULL, NULL))
3171     {
3172         DisposeEventHandlerUPP(keyEventHandlerUPP);
3173         keyEventHandlerUPP = NULL;
3174     }
3175
3176     EventTypeSpec windowEventTypes[] = {
3177         { kEventClassWindow, kEventWindowActivated },
3178         { kEventClassWindow, kEventWindowDeactivated },
3179     };
3180
3181     /* Install window event handler to support TSMDocument activate and
3182      * deactivate */
3183     winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate);
3184     if (noErr != InstallWindowEventHandler(gui.VimWindow,
3185                                            winEventHandlerUPP,
3186                                            NR_ELEMS(windowEventTypes),
3187                                            windowEventTypes, NULL, NULL))
3188     {
3189         DisposeEventHandlerUPP(winEventHandlerUPP);
3190         winEventHandlerUPP = NULL;
3191     }
3192 #endif
3193
3194 /*
3195 #ifdef FEAT_MBYTE
3196     set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3197 #endif
3198 */
3199
3200 #ifdef FEAT_GUI_TABLINE
3201     /*
3202      * Create the tabline
3203      */
3204     initialise_tabline();
3205 #endif
3206
3207     /* TODO: Load bitmap if using TOOLBAR */
3208     return OK;
3209 }
3210
3211 /*
3212  * Called when the foreground or background color has been changed.
3213  */
3214     void
3215 gui_mch_new_colors(void)
3216 {
3217     /* TODO:
3218      * This proc is called when Normal is set to a value
3219      * so what must be done? I don't know
3220      */
3221 }
3222
3223 /*
3224  * Open the GUI window which was created by a call to gui_mch_init().
3225  */
3226     int
3227 gui_mch_open(void)
3228 {
3229     ShowWindow(gui.VimWindow);
3230
3231     if (gui_win_x != -1 && gui_win_y != -1)
3232         gui_mch_set_winpos(gui_win_x, gui_win_y);
3233
3234     /*
3235      * Make the GUI the foreground process (in case it was launched
3236      * from the Terminal or via :gui).
3237      */
3238     {
3239         ProcessSerialNumber psn;
3240         if (GetCurrentProcess(&psn) == noErr)
3241             SetFrontProcess(&psn);
3242     }
3243
3244     return OK;
3245 }
3246
3247 #ifdef USE_ATSUI_DRAWING
3248     static void
3249 gui_mac_dispose_atsui_style(void)
3250 {
3251     if (p_macatsui && gFontStyle)
3252         ATSUDisposeStyle(gFontStyle);
3253 #ifdef FEAT_MBYTE
3254     if (p_macatsui && gWideFontStyle)
3255         ATSUDisposeStyle(gWideFontStyle);
3256 #endif
3257 }
3258 #endif
3259
3260     void
3261 gui_mch_exit(int rc)
3262 {
3263     /* TODO: find out all what is missing here? */
3264     DisposeRgn(cursorRgn);
3265
3266 #ifdef USE_CARBONKEYHANDLER
3267     if (keyEventHandlerUPP)
3268         DisposeEventHandlerUPP(keyEventHandlerUPP);
3269 #endif
3270
3271     if (mouseWheelHandlerUPP != NULL)
3272         DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3273
3274 #ifdef USE_ATSUI_DRAWING
3275     gui_mac_dispose_atsui_style();
3276 #endif
3277
3278 #ifdef USE_CARBONKEYHANDLER
3279     FixTSMDocument(gTSMDocument);
3280     DeactivateTSMDocument(gTSMDocument);
3281     DeleteTSMDocument(gTSMDocument);
3282 #endif
3283
3284     /* Exit to shell? */
3285     exit(rc);
3286 }
3287
3288 /*
3289  * Get the position of the top left corner of the window.
3290  */
3291     int
3292 gui_mch_get_winpos(int *x, int *y)
3293 {
3294     /* TODO */
3295     Rect        bounds;
3296     OSStatus    status;
3297
3298     /* Carbon >= 1.0.2, MacOS >= 8.5 */
3299     status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3300
3301     if (status != noErr)
3302         return FAIL;
3303     *x = bounds.left;
3304     *y = bounds.top;
3305     return OK;
3306 }
3307
3308 /*
3309  * Set the position of the top left corner of the window to the given
3310  * coordinates.
3311  */
3312     void
3313 gui_mch_set_winpos(int x, int y)
3314 {
3315     /* TODO:  Should make sure the window is move within range
3316      *        e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3317      */
3318     MoveWindowStructure(gui.VimWindow, x, y);
3319 }
3320
3321     void
3322 gui_mch_set_shellsize(
3323     int         width,
3324     int         height,
3325     int         min_width,
3326     int         min_height,
3327     int         base_width,
3328     int         base_height,
3329     int         direction)
3330 {
3331     CGrafPtr    VimPort;
3332     Rect        VimBound;
3333
3334     if (gui.which_scrollbars[SBAR_LEFT])
3335     {
3336         VimPort = GetWindowPort(gui.VimWindow);
3337         GetPortBounds(VimPort, &VimBound);
3338         VimBound.left = -gui.scrollbar_width; /* + 1;*/
3339         SetPortBounds(VimPort, &VimBound);
3340     /*  GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3341     }
3342     else
3343     {
3344         VimPort = GetWindowPort(gui.VimWindow);
3345         GetPortBounds(VimPort, &VimBound);
3346         VimBound.left = 0;
3347         SetPortBounds(VimPort, &VimBound);
3348     }
3349
3350     SizeWindow(gui.VimWindow, width, height, TRUE);
3351
3352     gui_resize_shell(width, height);
3353 }
3354
3355 /*
3356  * Get the screen dimensions.
3357  * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3358  * Is there no way to find out how wide the borders really are?
3359  * TODO: Add live update of those value on suspend/resume.
3360  */
3361     void
3362 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3363 {
3364     GDHandle    dominantDevice = GetMainDevice();
3365     Rect        screenRect = (**dominantDevice).gdRect;
3366
3367     *screen_w = screenRect.right - 10;
3368     *screen_h = screenRect.bottom - 40;
3369 }
3370
3371
3372 /*
3373  * Open the Font Panel and wait for the user to select a font and
3374  * close the panel.  Then fill the buffer pointed to by font_name with
3375  * the name and size of the selected font and return the font's handle,
3376  * or NOFONT in case of an error.
3377  */
3378     static GuiFont
3379 gui_mac_select_font(char_u *font_name)
3380 {
3381     GuiFont                 selected_font = NOFONT;
3382     OSStatus                status;
3383     FontSelectionQDStyle    curr_font;
3384
3385     /* Initialize the Font Panel with the current font. */
3386     curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3387     curr_font.size = (gui.norm_font >> 16);
3388     /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3389     curr_font.instance.fontStyle = 0;
3390     curr_font.hasColor = false;
3391     curr_font.version = 0; /* version number of the style structure */
3392     status = SetFontInfoForSelection(kFontSelectionQDType,
3393             /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3394
3395     gFontPanelInfo.family = curr_font.instance.fontFamily;
3396     gFontPanelInfo.style = curr_font.instance.fontStyle;
3397     gFontPanelInfo.size = curr_font.size;
3398
3399     /* Pop up the Font Panel. */
3400     status = FPShowHideFontPanel();
3401     if (status == noErr)
3402     {
3403         /*
3404          * The Font Panel is modeless.  We really need it to be modal,
3405          * so we spin in an event loop until the panel is closed.
3406          */
3407         gFontPanelInfo.isPanelVisible = true;
3408         while (gFontPanelInfo.isPanelVisible)
3409         {
3410             EventRecord e;
3411             WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3412         }
3413
3414         GetFontPanelSelection(font_name);
3415         selected_font = gui_mac_find_font(font_name);
3416     }
3417     return selected_font;
3418 }
3419
3420 #ifdef USE_ATSUI_DRAWING
3421     static void
3422 gui_mac_create_atsui_style(void)
3423 {
3424     if (p_macatsui && gFontStyle == NULL)
3425     {
3426         if (ATSUCreateStyle(&gFontStyle) != noErr)
3427             gFontStyle = NULL;
3428     }
3429 #ifdef FEAT_MBYTE
3430     if (p_macatsui && gWideFontStyle == NULL)
3431     {
3432         if (ATSUCreateStyle(&gWideFontStyle) != noErr)
3433             gWideFontStyle = NULL;
3434     }
3435 #endif
3436
3437     p_macatsui_last = p_macatsui;
3438 }
3439 #endif
3440
3441 /*
3442  * Initialise vim to use the font with the given name.  Return FAIL if the font
3443  * could not be loaded, OK otherwise.
3444  */
3445     int
3446 gui_mch_init_font(char_u *font_name, int fontset)
3447 {
3448     /* TODO: Add support for bold italic underline proportional etc... */
3449     Str255      suggestedFont = "\pMonaco";
3450     int         suggestedSize = 10;
3451     FontInfo    font_info;
3452     short       font_id;
3453     GuiFont     font;
3454     char_u      used_font_name[512];
3455
3456 #ifdef USE_ATSUI_DRAWING
3457     gui_mac_create_atsui_style();
3458 #endif
3459
3460     if (font_name == NULL)
3461     {
3462         /* First try to get the suggested font */
3463         GetFNum(suggestedFont, &font_id);
3464
3465         if (font_id == 0)
3466         {
3467             /* Then pickup the standard application font */
3468             font_id = GetAppFont();
3469             STRCPY(used_font_name, "default");
3470         }
3471         else
3472             STRCPY(used_font_name, "Monaco");
3473         font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3474     }
3475     else if (STRCMP(font_name, "*") == 0)
3476     {
3477         char_u *new_p_guifont;
3478
3479         font = gui_mac_select_font(used_font_name);
3480         if (font == NOFONT)
3481             return FAIL;
3482
3483         /* Set guifont to the name of the selected font. */
3484         new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3485         if (new_p_guifont != NULL)
3486         {
3487             STRCPY(new_p_guifont, used_font_name);
3488             vim_free(p_guifont);
3489             p_guifont = new_p_guifont;
3490             /* Replace spaces in the font name with underscores. */
3491             for ( ; *new_p_guifont; ++new_p_guifont)
3492             {
3493                 if (*new_p_guifont == ' ')
3494                     *new_p_guifont = '_';
3495             }
3496         }
3497     }
3498     else
3499     {
3500         font = gui_mac_find_font(font_name);
3501         vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3502
3503         if (font == NOFONT)
3504             return FAIL;
3505     }
3506
3507     gui.norm_font = font;
3508
3509     hl_set_font_name(used_font_name);
3510
3511     TextSize(font >> 16);
3512     TextFont(font & 0xFFFF);
3513
3514     GetFontInfo(&font_info);
3515
3516     gui.char_ascent = font_info.ascent;
3517     gui.char_width  = CharWidth('_');
3518     gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3519
3520 #ifdef USE_ATSUI_DRAWING
3521     if (p_macatsui && gFontStyle)
3522         gui_mac_set_font_attributes(font);
3523 #endif
3524
3525     return OK;
3526 }
3527
3528 /*
3529  * Adjust gui.char_height (after 'linespace' was changed).
3530  */
3531     int
3532 gui_mch_adjust_charheight(void)
3533 {
3534     FontInfo    font_info;
3535
3536     GetFontInfo(&font_info);
3537     gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3538     gui.char_ascent = font_info.ascent + p_linespace / 2;
3539     return OK;
3540 }
3541
3542 /*
3543  * Get a font structure for highlighting.
3544  */
3545     GuiFont
3546 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3547 {
3548     GuiFont font;
3549
3550     font = gui_mac_find_font(name);
3551
3552     if (font == NOFONT)
3553     {
3554         if (giveErrorIfMissing)
3555             EMSG2(_(e_font), name);
3556         return NOFONT;
3557     }
3558     /*
3559      * TODO : Accept only monospace
3560      */
3561
3562     return font;
3563 }
3564
3565 #if defined(FEAT_EVAL) || defined(PROTO)
3566 /*
3567  * Return the name of font "font" in allocated memory.
3568  * Don't know how to get the actual name, thus use the provided name.
3569  */
3570     char_u *
3571 gui_mch_get_fontname(GuiFont font, char_u *name)
3572 {
3573     if (name == NULL)
3574         return NULL;
3575     return vim_strsave(name);
3576 }
3577 #endif
3578
3579 #ifdef USE_ATSUI_DRAWING
3580     static void
3581 gui_mac_set_font_attributes(GuiFont font)
3582 {
3583     ATSUFontID  fontID;
3584     Fixed       fontSize;
3585     Fixed       fontWidth;
3586
3587     fontID    = font & 0xFFFF;
3588     fontSize  = Long2Fix(font >> 16);
3589     fontWidth = Long2Fix(gui.char_width);
3590
3591     ATSUAttributeTag attribTags[] =
3592     {
3593         kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
3594         kATSUMaxATSUITagValue + 1
3595     };
3596
3597     ByteCount attribSizes[] =
3598     {
3599         sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
3600         sizeof(font)
3601     };
3602
3603     ATSUAttributeValuePtr attribValues[] =
3604     {
3605         &fontID, &fontSize, &fontWidth, &font
3606     };
3607
3608     if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3609     {
3610         if (ATSUSetAttributes(gFontStyle,
3611                     (sizeof attribTags) / sizeof(ATSUAttributeTag),
3612                     attribTags, attribSizes, attribValues) != noErr)
3613         {
3614 # ifndef NDEBUG
3615             fprintf(stderr, "couldn't set font style\n");
3616 # endif
3617             ATSUDisposeStyle(gFontStyle);
3618             gFontStyle = NULL;
3619         }
3620
3621 #ifdef FEAT_MBYTE
3622         if (has_mbyte)
3623         {
3624             /* FIXME: we should use a more mbyte sensitive way to support
3625              * wide font drawing */
3626             fontWidth = Long2Fix(gui.char_width * 2);
3627
3628             if (ATSUSetAttributes(gWideFontStyle,
3629                         (sizeof attribTags) / sizeof(ATSUAttributeTag),
3630                         attribTags, attribSizes, attribValues) != noErr)
3631             {
3632                 ATSUDisposeStyle(gWideFontStyle);
3633                 gWideFontStyle = NULL;
3634             }
3635         }
3636 #endif
3637     }
3638 }
3639 #endif
3640
3641 /*
3642  * Set the current text font.
3643  */
3644     void
3645 gui_mch_set_font(GuiFont font)
3646 {
3647 #ifdef USE_ATSUI_DRAWING
3648     GuiFont                     currFont;
3649     ByteCount                   actualFontByteCount;
3650
3651     if (p_macatsui && gFontStyle)
3652     {
3653         /* Avoid setting same font again */
3654         if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1,
3655                     sizeof(font), &currFont, &actualFontByteCount) == noErr
3656                 && actualFontByteCount == (sizeof font))
3657         {
3658             if (currFont == font)
3659                 return;
3660         }
3661
3662         gui_mac_set_font_attributes(font);
3663     }
3664
3665     if (p_macatsui && !gIsFontFallbackSet)
3666     {
3667         /* Setup automatic font substitution. The user's guifontwide
3668          * is tried first, then the system tries other fonts. */
3669 /*
3670         ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3671         ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3672         ATSUCreateFontFallbacks(&gFontFallbacks);
3673         ATSUSetObjFontFallbacks(gFontFallbacks, );
3674 */
3675         if (gui.wide_font)
3676         {
3677             ATSUFontID fallbackFonts;
3678             gIsFontFallbackSet = TRUE;
3679
3680             if (FMGetFontFromFontFamilyInstance(
3681                         (gui.wide_font & 0xFFFF),
3682                         0,
3683                         &fallbackFonts,
3684                         NULL) == noErr)
3685             {
3686                 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID),
3687                                      &fallbackFonts,
3688                                      kATSUSequentialFallbacksPreferred);
3689             }
3690 /*
3691         ATSUAttributeValuePtr fallbackValues[] = { };
3692 */
3693         }
3694     }
3695 #endif
3696     TextSize(font >> 16);
3697     TextFont(font & 0xFFFF);
3698 }
3699
3700 /*
3701  * If a font is not going to be used, free its structure.
3702  */
3703     void
3704 gui_mch_free_font(font)
3705     GuiFont     font;
3706 {
3707     /*
3708      * Free font when "font" is not 0.
3709      * Nothing to do in the current implementation, since
3710      * nothing is allocated for each font used.
3711      */
3712 }
3713
3714     static int
3715 hex_digit(int c)
3716 {
3717     if (isdigit(c))
3718         return c - '0';
3719     c = TOLOWER_ASC(c);
3720     if (c >= 'a' && c <= 'f')
3721         return c - 'a' + 10;
3722     return -1000;
3723 }
3724
3725 /*
3726  * Return the Pixel value (color) for the given color name.  This routine was
3727  * pretty much taken from example code in the Silicon Graphics OSF/Motif
3728  * Programmer's Guide.
3729  * Return INVALCOLOR when failed.
3730  */
3731     guicolor_T
3732 gui_mch_get_color(char_u *name)
3733 {
3734     /* TODO: Add support for the new named color of MacOS 8
3735      */
3736     RGBColor    MacColor;
3737 //    guicolor_T        color = 0;
3738
3739     typedef struct guicolor_tTable
3740     {
3741         char        *name;
3742         guicolor_T  color;
3743     } guicolor_tTable;
3744
3745     /*
3746      * The comment at the end of each line is the source
3747      * (Mac, Window, Unix) and the number is the unix rgb.txt value
3748      */
3749     static guicolor_tTable table[] =
3750     {
3751         {"Black",       RGB(0x00, 0x00, 0x00)},
3752         {"darkgray",    RGB(0x80, 0x80, 0x80)}, /*W*/
3753         {"darkgrey",    RGB(0x80, 0x80, 0x80)}, /*W*/
3754         {"Gray",        RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3755         {"Grey",        RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3756         {"lightgray",   RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3757         {"lightgrey",   RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3758         {"gray10",      RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3759         {"grey10",      RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3760         {"gray20",      RGB(0x33, 0x33, 0x33)}, /*W*/
3761         {"grey20",      RGB(0x33, 0x33, 0x33)}, /*W*/
3762         {"gray30",      RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3763         {"grey30",      RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3764         {"gray40",      RGB(0x66, 0x66, 0x66)}, /*W*/
3765         {"grey40",      RGB(0x66, 0x66, 0x66)}, /*W*/
3766         {"gray50",      RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3767         {"grey50",      RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3768         {"gray60",      RGB(0x99, 0x99, 0x99)}, /*W*/
3769         {"grey60",      RGB(0x99, 0x99, 0x99)}, /*W*/
3770         {"gray70",      RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3771         {"grey70",      RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3772         {"gray80",      RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3773         {"grey80",      RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3774         {"gray90",      RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3775         {"grey90",      RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3776         {"white",       RGB(0xFF, 0xFF, 0xFF)},
3777         {"darkred",     RGB(0x80, 0x00, 0x00)}, /*W*/
3778         {"red",         RGB(0xDD, 0x08, 0x06)}, /*M*/
3779         {"lightred",    RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3780         {"DarkBlue",    RGB(0x00, 0x00, 0x80)}, /*W*/
3781         {"Blue",        RGB(0x00, 0x00, 0xD4)}, /*M*/
3782         {"lightblue",   RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3783         {"DarkGreen",   RGB(0x00, 0x80, 0x00)}, /*W*/
3784         {"Green",       RGB(0x00, 0x64, 0x11)}, /*M*/
3785         {"lightgreen",  RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3786         {"DarkCyan",    RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3787         {"cyan",        RGB(0x02, 0xAB, 0xEA)}, /*M*/
3788         {"lightcyan",   RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3789         {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3790         {"magenta",     RGB(0xF2, 0x08, 0x84)}, /*M*/
3791         {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3792         {"brown",       RGB(0x80, 0x40, 0x40)}, /*W*/
3793         {"yellow",      RGB(0xFC, 0xF3, 0x05)}, /*M*/
3794         {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3795         {"darkyellow",  RGB(0xBB, 0xBB, 0x00)}, /*U*/
3796         {"SeaGreen",    RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3797         {"orange",      RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3798         {"Purple",      RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3799         {"SlateBlue",   RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3800         {"Violet",      RGB(0x8D, 0x38, 0xC9)}, /*U*/
3801     };
3802
3803     int         r, g, b;
3804     int         i;
3805
3806     if (name[0] == '#' && strlen((char *) name) == 7)
3807     {
3808         /* Name is in "#rrggbb" format */
3809         r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3810         g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3811         b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3812         if (r < 0 || g < 0 || b < 0)
3813             return INVALCOLOR;
3814         return RGB(r, g, b);
3815     }
3816     else
3817     {
3818         if (STRICMP(name, "hilite") == 0)
3819         {
3820             LMGetHiliteRGB(&MacColor);
3821             return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3822         }
3823         /* Check if the name is one of the colors we know */
3824         for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3825             if (STRICMP(name, table[i].name) == 0)
3826                 return table[i].color;
3827     }
3828
3829     /*
3830      * Last attempt. Look in the file "$VIM/rgb.txt".
3831      */
3832     {
3833 #define LINE_LEN 100
3834         FILE    *fd;
3835         char    line[LINE_LEN];
3836         char_u  *fname;
3837
3838         fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3839         if (fname == NULL)
3840             return INVALCOLOR;
3841
3842         fd = fopen((char *)fname, "rt");
3843         vim_free(fname);
3844         if (fd == NULL)
3845             return INVALCOLOR;
3846
3847         while (!feof(fd))
3848         {
3849             int         len;
3850             int         pos;
3851             char        *color;
3852
3853             fgets(line, LINE_LEN, fd);
3854             len = strlen(line);
3855
3856             if (len <= 1 || line[len-1] != '\n')
3857                 continue;
3858
3859             line[len-1] = '\0';
3860
3861             i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3862             if (i != 3)
3863                 continue;
3864
3865             color = line + pos;
3866
3867             if (STRICMP(color, name) == 0)
3868             {
3869                 fclose(fd);
3870                 return (guicolor_T) RGB(r, g, b);
3871             }
3872         }
3873         fclose(fd);
3874     }
3875
3876     return INVALCOLOR;
3877 }
3878
3879 /*
3880  * Set the current text foreground color.
3881  */
3882     void
3883 gui_mch_set_fg_color(guicolor_T color)
3884 {
3885     RGBColor TheColor;
3886
3887     TheColor.red = Red(color) * 0x0101;
3888     TheColor.green = Green(color) * 0x0101;
3889     TheColor.blue = Blue(color) * 0x0101;
3890
3891     RGBForeColor(&TheColor);
3892 }
3893
3894 /*
3895  * Set the current text background color.
3896  */
3897     void
3898 gui_mch_set_bg_color(guicolor_T color)
3899 {
3900     RGBColor TheColor;
3901
3902     TheColor.red = Red(color) * 0x0101;
3903     TheColor.green = Green(color) * 0x0101;
3904     TheColor.blue = Blue(color) * 0x0101;
3905
3906     RGBBackColor(&TheColor);
3907 }
3908
3909 RGBColor specialColor;
3910
3911 /*
3912  * Set the current text special color.
3913  */
3914     void
3915 gui_mch_set_sp_color(guicolor_T color)
3916 {
3917     specialColor.red = Red(color) * 0x0101;
3918     specialColor.green = Green(color) * 0x0101;
3919     specialColor.blue = Blue(color) * 0x0101;
3920 }
3921
3922 /*
3923  * Draw undercurl at the bottom of the character cell.
3924  */
3925     static void
3926 draw_undercurl(int flags, int row, int col, int cells)
3927 {
3928     int                 x;
3929     int                 offset;
3930     const static int    val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3931     int                 y = FILL_Y(row + 1) - 1;
3932
3933     RGBForeColor(&specialColor);
3934
3935     offset = val[FILL_X(col) % 8];
3936     MoveTo(FILL_X(col), y - offset);
3937
3938     for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3939     {
3940         offset = val[x % 8];
3941         LineTo(x, y - offset);
3942     }
3943 }
3944
3945
3946     static void
3947 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3948 {
3949 #ifdef FEAT_MBYTE
3950     char_u      *tofree = NULL;
3951
3952     if (output_conv.vc_type != CONV_NONE)
3953     {
3954         tofree = string_convert(&output_conv, s, &len);
3955         if (tofree != NULL)
3956             s = tofree;
3957     }
3958 #endif
3959
3960     /*
3961      * On OS X, try using Quartz-style text antialiasing.
3962      */
3963     if (gMacSystemVersion >= 0x1020)
3964     {
3965         /* Quartz antialiasing is available only in OS 10.2 and later. */
3966         UInt32 qd_flags = (p_antialias ?
3967                              kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3968         QDSwapTextFlags(qd_flags);
3969     }
3970
3971     /*
3972      * When antialiasing we're using srcOr mode, we have to clear the block
3973      * before drawing the text.
3974      * Also needed when 'linespace' is non-zero to remove the cursor and
3975      * underlining.
3976      * But not when drawing transparently.
3977      * The following is like calling gui_mch_clear_block(row, col, row, col +
3978      * len - 1), but without setting the bg color to gui.back_pixel.
3979      */
3980     if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3981             && !(flags & DRAW_TRANSP))
3982     {
3983         Rect rc;
3984
3985         rc.left = FILL_X(col);
3986         rc.top = FILL_Y(row);
3987 #ifdef FEAT_MBYTE
3988         /* Multibyte computation taken from gui_w32.c */
3989         if (has_mbyte)
3990         {
3991             /* Compute the length in display cells. */
3992             rc.right = FILL_X(col + mb_string2cells(s, len));
3993         }
3994         else
3995 #endif
3996         rc.right = FILL_X(col + len) + (col + len == Columns);
3997         rc.bottom = FILL_Y(row + 1);
3998         EraseRect(&rc);
3999     }
4000
4001     if (gMacSystemVersion >= 0x1020 && p_antialias)
4002     {
4003         StyleParameter face;
4004
4005         face = normal;
4006         if (flags & DRAW_BOLD)
4007             face |= bold;
4008         if (flags & DRAW_UNDERL)
4009             face |= underline;
4010         TextFace(face);
4011
4012         /* Quartz antialiasing works only in srcOr transfer mode. */
4013         TextMode(srcOr);
4014
4015         MoveTo(TEXT_X(col), TEXT_Y(row));
4016         DrawText((char*)s, 0, len);
4017     }
4018     else
4019     {
4020         /* Use old-style, non-antialiased QuickDraw text rendering. */
4021         TextMode(srcCopy);
4022         TextFace(normal);
4023
4024     /*  SelectFont(hdc, gui.currFont); */
4025
4026         if (flags & DRAW_TRANSP)
4027         {
4028             TextMode(srcOr);
4029         }
4030
4031         MoveTo(TEXT_X(col), TEXT_Y(row));
4032         DrawText((char *)s, 0, len);
4033
4034         if (flags & DRAW_BOLD)
4035         {
4036             TextMode(srcOr);
4037             MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
4038             DrawText((char *)s, 0, len);
4039         }
4040
4041         if (flags & DRAW_UNDERL)
4042         {
4043             MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
4044             LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
4045         }
4046     }
4047
4048     if (flags & DRAW_UNDERC)
4049         draw_undercurl(flags, row, col, len);
4050
4051 #ifdef FEAT_MBYTE
4052     vim_free(tofree);
4053 #endif
4054 }
4055
4056 #ifdef USE_ATSUI_DRAWING
4057
4058     static void
4059 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
4060 {
4061     /* ATSUI requires utf-16 strings */
4062     UniCharCount utf16_len;
4063     UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
4064     utf16_len /= sizeof(UniChar);
4065
4066     /* - ATSUI automatically antialiases text (Someone)
4067      * - for some reason it does not work... (Jussi) */
4068 #ifdef MAC_ATSUI_DEBUG
4069     fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n",
4070             row, col, len, len == 1 ? s[0] : ' ');
4071 #endif
4072     /*
4073      * When antialiasing we're using srcOr mode, we have to clear the block
4074      * before drawing the text.
4075      * Also needed when 'linespace' is non-zero to remove the cursor and
4076      * underlining.
4077      * But not when drawing transparently.
4078      * The following is like calling gui_mch_clear_block(row, col, row, col +
4079      * len - 1), but without setting the bg color to gui.back_pixel.
4080      */
4081     if ((flags & DRAW_TRANSP) == 0)
4082     {
4083         Rect rc;
4084
4085         rc.left = FILL_X(col);
4086         rc.top = FILL_Y(row);
4087         /* Multibyte computation taken from gui_w32.c */
4088         if (has_mbyte)
4089         {
4090             /* Compute the length in display cells. */
4091             rc.right = FILL_X(col + mb_string2cells(s, len));
4092         }
4093         else
4094             rc.right = FILL_X(col + len) + (col + len == Columns);
4095
4096         rc.bottom = FILL_Y(row + 1);
4097         EraseRect(&rc);
4098     }
4099
4100     {
4101         TextMode(srcCopy);
4102         TextFace(normal);
4103
4104         /*  SelectFont(hdc, gui.currFont); */
4105         if (flags & DRAW_TRANSP)
4106         {
4107             TextMode(srcOr);
4108         }
4109
4110         MoveTo(TEXT_X(col), TEXT_Y(row));
4111
4112         if (gFontStyle && flags & DRAW_BOLD)
4113         {
4114             Boolean attValue = true;
4115             ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4116             ByteCount attribSizes[] = { sizeof(Boolean) };
4117             ATSUAttributeValuePtr attribValues[] = { &attValue };
4118
4119             ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues);
4120         }
4121
4122         UInt32 useAntialias = p_antialias ? kATSStyleApplyAntiAliasing
4123                                           : kATSStyleNoAntiAliasing;
4124         if (useAntialias != useAntialias_cached)
4125         {
4126             ATSUAttributeTag attribTags[] = { kATSUStyleRenderingOptionsTag };
4127             ByteCount attribSizes[] = { sizeof(UInt32) };
4128             ATSUAttributeValuePtr attribValues[] = { &useAntialias };
4129
4130             if (gFontStyle)
4131                 ATSUSetAttributes(gFontStyle, 1, attribTags,
4132                                                    attribSizes, attribValues);
4133             if (gWideFontStyle)
4134                 ATSUSetAttributes(gWideFontStyle, 1, attribTags,
4135                                                    attribSizes, attribValues);
4136
4137             useAntialias_cached = useAntialias;
4138         }
4139
4140 #ifdef FEAT_MBYTE
4141         if (has_mbyte)
4142         {
4143             int n, width_in_cell, last_width_in_cell;
4144             UniCharArrayOffset offset = 0;
4145             UniCharCount yet_to_draw = 0;
4146             ATSUTextLayout textLayout;
4147             ATSUStyle      textStyle;
4148
4149             last_width_in_cell = 1;
4150             ATSUCreateTextLayout(&textLayout);
4151             ATSUSetTextPointerLocation(textLayout, tofree,
4152                                        kATSUFromTextBeginning,
4153                                        kATSUToTextEnd, utf16_len);
4154             /*
4155                ATSUSetRunStyle(textLayout, gFontStyle,
4156                kATSUFromTextBeginning, kATSUToTextEnd); */
4157
4158             /* Compute the length in display cells. */
4159             for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4160             {
4161                 width_in_cell = (*mb_ptr2cells)(s + n);
4162
4163                 /* probably we are switching from single byte character
4164                  * to multibyte characters (which requires more than one
4165                  * cell to draw) */
4166                 if (width_in_cell != last_width_in_cell)
4167                 {
4168 #ifdef MAC_ATSUI_DEBUG
4169                     fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4170                             n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4171 #endif
4172                     textStyle = last_width_in_cell > 1 ? gWideFontStyle
4173                                                                  : gFontStyle;
4174
4175                     ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw);
4176                     offset += yet_to_draw;
4177                     yet_to_draw = 0;
4178                     last_width_in_cell = width_in_cell;
4179                 }
4180
4181                 yet_to_draw++;
4182             }
4183
4184             if (yet_to_draw)
4185             {
4186 #ifdef MAC_ATSUI_DEBUG
4187                 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4188                         n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4189 #endif
4190                 /* finish the rest style */
4191                 textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle;
4192                 ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd);
4193             }
4194
4195             ATSUSetTransientFontMatching(textLayout, TRUE);
4196             ATSUDrawText(textLayout,
4197                          kATSUFromTextBeginning, kATSUToTextEnd,
4198                          kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4199             ATSUDisposeTextLayout(textLayout);
4200         }
4201         else
4202 #endif
4203         {
4204             ATSUTextLayout textLayout;
4205
4206             if (ATSUCreateTextLayoutWithTextPtr(tofree,
4207                         kATSUFromTextBeginning, kATSUToTextEnd,
4208                         utf16_len,
4209                         (gFontStyle ? 1 : 0), &utf16_len,
4210                         (gFontStyle ? &gFontStyle : NULL),
4211                         &textLayout) == noErr)
4212             {
4213                 ATSUSetTransientFontMatching(textLayout, TRUE);
4214
4215                 ATSUDrawText(textLayout,
4216                         kATSUFromTextBeginning, kATSUToTextEnd,
4217                         kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4218
4219                 ATSUDisposeTextLayout(textLayout);
4220             }
4221         }
4222
4223         /* drawing is done, now reset bold to normal */
4224         if (gFontStyle && flags & DRAW_BOLD)
4225         {
4226             Boolean attValue = false;
4227
4228             ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4229             ByteCount attribSizes[] = { sizeof(Boolean) };
4230             ATSUAttributeValuePtr attribValues[] = { &attValue };
4231
4232             ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes,
4233                                                                 attribValues);
4234         }
4235     }
4236
4237     if (flags & DRAW_UNDERC)
4238         draw_undercurl(flags, row, col, len);
4239
4240     vim_free(tofree);
4241 }
4242 #endif
4243
4244     void
4245 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4246 {
4247 #if defined(USE_ATSUI_DRAWING)
4248     if (p_macatsui == 0 && p_macatsui_last != 0)
4249         /* switch from macatsui to nomacatsui */
4250         gui_mac_dispose_atsui_style();
4251     else if (p_macatsui != 0 && p_macatsui_last == 0)
4252         /* switch from nomacatsui to macatsui */
4253         gui_mac_create_atsui_style();
4254
4255     if (p_macatsui)
4256         draw_string_ATSUI(row, col, s, len, flags);
4257     else
4258 #endif
4259         draw_string_QD(row, col, s, len, flags);
4260 }
4261
4262 /*
4263  * Return OK if the key with the termcap name "name" is supported.
4264  */
4265     int
4266 gui_mch_haskey(char_u *name)
4267 {
4268     int i;
4269
4270     for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4271         if (name[0] == special_keys[i].vim_code0 &&
4272                                          name[1] == special_keys[i].vim_code1)
4273             return OK;
4274     return FAIL;
4275 }
4276
4277     void
4278 gui_mch_beep(void)
4279 {
4280     SysBeep(1); /* Should this be 0? (????) */
4281 }
4282
4283     void
4284 gui_mch_flash(int msec)
4285 {
4286     /* Do a visual beep by reversing the foreground and background colors */
4287     Rect    rc;
4288
4289     /*
4290      * Note: InvertRect() excludes right and bottom of rectangle.
4291      */
4292     rc.left = 0;
4293     rc.top = 0;
4294     rc.right = gui.num_cols * gui.char_width;
4295     rc.bottom = gui.num_rows * gui.char_height;
4296     InvertRect(&rc);
4297
4298     ui_delay((long)msec, TRUE);         /* wait for some msec */
4299
4300     InvertRect(&rc);
4301 }
4302
4303 /*
4304  * Invert a rectangle from row r, column c, for nr rows and nc columns.
4305  */
4306     void
4307 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4308 {
4309     Rect        rc;
4310
4311     /*
4312      * Note: InvertRect() excludes right and bottom of rectangle.
4313      */
4314     rc.left = FILL_X(c);
4315     rc.top = FILL_Y(r);
4316     rc.right = rc.left + nc * gui.char_width;
4317     rc.bottom = rc.top + nr * gui.char_height;
4318     InvertRect(&rc);
4319 }
4320
4321 /*
4322  * Iconify the GUI window.
4323  */
4324     void
4325 gui_mch_iconify(void)
4326 {
4327     /* TODO: find out what could replace iconify
4328      *       -window shade?
4329      *       -hide application?
4330      */
4331 }
4332
4333 #if defined(FEAT_EVAL) || defined(PROTO)
4334 /*
4335  * Bring the Vim window to the foreground.
4336  */
4337     void
4338 gui_mch_set_foreground(void)
4339 {
4340     /* TODO */
4341 }
4342 #endif
4343
4344 /*
4345  * Draw a cursor without focus.
4346  */
4347     void
4348 gui_mch_draw_hollow_cursor(guicolor_T color)
4349 {
4350     Rect rc;
4351
4352     /*
4353      * Note: FrameRect() excludes right and bottom of rectangle.
4354      */
4355     rc.left = FILL_X(gui.col);
4356     rc.top = FILL_Y(gui.row);
4357     rc.right = rc.left + gui.char_width;
4358 #ifdef FEAT_MBYTE
4359     if (mb_lefthalve(gui.row, gui.col))
4360         rc.right += gui.char_width;
4361 #endif
4362     rc.bottom = rc.top + gui.char_height;
4363
4364     gui_mch_set_fg_color(color);
4365
4366     FrameRect(&rc);
4367 }
4368
4369 /*
4370  * Draw part of a cursor, only w pixels wide, and h pixels high.
4371  */
4372     void
4373 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4374 {
4375     Rect rc;
4376
4377 #ifdef FEAT_RIGHTLEFT
4378     /* vertical line should be on the right of current point */
4379     if (CURSOR_BAR_RIGHT)
4380         rc.left = FILL_X(gui.col + 1) - w;
4381     else
4382 #endif
4383         rc.left = FILL_X(gui.col);
4384     rc.top = FILL_Y(gui.row) + gui.char_height - h;
4385     rc.right = rc.left + w;
4386     rc.bottom = rc.top + h;
4387
4388     gui_mch_set_fg_color(color);
4389
4390     FrameRect(&rc);
4391 //    PaintRect(&rc);
4392 }
4393
4394
4395
4396 /*
4397  * Catch up with any queued X events.  This may put keyboard input into the
4398  * input buffer, call resize call-backs, trigger timers etc.  If there is
4399  * nothing in the X event queue (& no timers pending), then we return
4400  * immediately.
4401  */
4402     void
4403 gui_mch_update(void)
4404 {
4405     /* TODO: find what to do
4406      *       maybe call gui_mch_wait_for_chars (0)
4407      *       more like look at EventQueue then
4408      *       call heart of gui_mch_wait_for_chars;
4409      *
4410      *  if (eventther)
4411      *      gui_mac_handle_event(&event);
4412      */
4413     EventRecord theEvent;
4414
4415     if (EventAvail(everyEvent, &theEvent))
4416         if (theEvent.what != nullEvent)
4417             gui_mch_wait_for_chars(0);
4418 }
4419
4420 /*
4421  * Simple wrapper to neglect more easily the time
4422  * spent inside WaitNextEvent while profiling.
4423  */
4424
4425     pascal
4426     Boolean
4427 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4428 {
4429     if (((long) sleep) < -1)
4430         sleep = 32767;
4431     return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4432 }
4433
4434 /*
4435  * GUI input routine called by gui_wait_for_chars().  Waits for a character
4436  * from the keyboard.
4437  *  wtime == -1     Wait forever.
4438  *  wtime == 0      This should never happen.
4439  *  wtime > 0       Wait wtime milliseconds for a character.
4440  * Returns OK if a character was found to be available within the given time,
4441  * or FAIL otherwise.
4442  */
4443     int
4444 gui_mch_wait_for_chars(int wtime)
4445 {
4446     EventMask   mask  = (everyEvent);
4447     EventRecord event;
4448     long        entryTick;
4449     long        currentTick;
4450     long        sleeppyTick;
4451
4452     /* If we are providing life feedback with the scrollbar,
4453      * we don't want to try to wait for an event, or else
4454      * there won't be any life feedback.
4455      */
4456     if (dragged_sb != NULL)
4457         return FAIL;
4458         /* TODO: Check if FAIL is the proper return code */
4459
4460     entryTick = TickCount();
4461
4462     allow_scrollbar = TRUE;
4463
4464     do
4465     {
4466 /*      if (dragRectControl == kCreateEmpty)
4467         {
4468             dragRgn = NULL;
4469             dragRectControl = kNothing;
4470         }
4471         else*/ if (dragRectControl == kCreateRect)
4472         {
4473             dragRgn = cursorRgn;
4474             RectRgn(dragRgn, &dragRect);
4475             dragRectControl = kNothing;
4476         }
4477         /*
4478          * Don't use gui_mch_update() because then we will spin-lock until a
4479          * char arrives, instead we use WaitNextEventWrp() to hang until an
4480          * event arrives.  No need to check for input_buf_full because we are
4481          * returning as soon as it contains a single char.
4482          */
4483         /* TODO: reduce wtime accordingly???  */
4484         if (wtime > -1)
4485             sleeppyTick = 60 * wtime / 1000;
4486         else
4487             sleeppyTick = 32767;
4488
4489         if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4490         {
4491             gui_mac_handle_event(&event);
4492             if (input_available())
4493             {
4494                 allow_scrollbar = FALSE;
4495                 return OK;
4496             }
4497         }
4498         currentTick = TickCount();
4499     }
4500     while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4501
4502     allow_scrollbar = FALSE;
4503     return FAIL;
4504 }
4505
4506 /*
4507  * Output routines.
4508  */
4509
4510 /* Flush any output to the screen */
4511     void
4512 gui_mch_flush(void)
4513 {
4514     /* TODO: Is anything needed here? */
4515 }
4516
4517 /*
4518  * Clear a rectangular region of the screen from text pos (row1, col1) to
4519  * (row2, col2) inclusive.
4520  */
4521     void
4522 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4523 {
4524     Rect rc;
4525
4526     /*
4527      * Clear one extra pixel at the far right, for when bold characters have
4528      * spilled over to the next column.
4529      */
4530     rc.left = FILL_X(col1);
4531     rc.top = FILL_Y(row1);
4532     rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4533     rc.bottom = FILL_Y(row2 + 1);
4534
4535     gui_mch_set_bg_color(gui.back_pixel);
4536     EraseRect(&rc);
4537 }
4538
4539 /*
4540  * Clear the whole text window.
4541  */
4542     void
4543 gui_mch_clear_all(void)
4544 {
4545     Rect        rc;
4546
4547     rc.left = 0;
4548     rc.top = 0;
4549     rc.right = Columns * gui.char_width + 2 * gui.border_width;
4550     rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4551
4552     gui_mch_set_bg_color(gui.back_pixel);
4553     EraseRect(&rc);
4554 /*  gui_mch_set_fg_color(gui.norm_pixel);
4555     FrameRect(&rc);
4556 */
4557 }
4558
4559 /*
4560  * Delete the given number of lines from the given row, scrolling up any
4561  * text further down within the scroll region.
4562  */
4563     void
4564 gui_mch_delete_lines(int row, int num_lines)
4565 {
4566     Rect        rc;
4567
4568     /* changed without checking! */
4569     rc.left = FILL_X(gui.scroll_region_left);
4570     rc.right = FILL_X(gui.scroll_region_right + 1);
4571     rc.top = FILL_Y(row);
4572     rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4573
4574     gui_mch_set_bg_color(gui.back_pixel);
4575     ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4576
4577     gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4578                                                        gui.scroll_region_left,
4579         gui.scroll_region_bot, gui.scroll_region_right);
4580 }
4581
4582 /*
4583  * Insert the given number of lines before the given row, scrolling down any
4584  * following text within the scroll region.
4585  */
4586     void
4587 gui_mch_insert_lines(int row, int num_lines)
4588 {
4589     Rect rc;
4590
4591     rc.left = FILL_X(gui.scroll_region_left);
4592     rc.right = FILL_X(gui.scroll_region_right + 1);
4593     rc.top = FILL_Y(row);
4594     rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4595
4596     gui_mch_set_bg_color(gui.back_pixel);
4597
4598     ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4599
4600     /* Update gui.cursor_row if the cursor scrolled or copied over */
4601     if (gui.cursor_row >= gui.row
4602             && gui.cursor_col >= gui.scroll_region_left
4603             && gui.cursor_col <= gui.scroll_region_right)
4604     {
4605         if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4606             gui.cursor_row += num_lines;
4607         else if (gui.cursor_row <= gui.scroll_region_bot)
4608             gui.cursor_is_valid = FALSE;
4609     }
4610
4611     gui_clear_block(row, gui.scroll_region_left,
4612                                 row + num_lines - 1, gui.scroll_region_right);
4613 }
4614
4615     /*
4616      * TODO: add a vim format to the clipboard which remember
4617      *       LINEWISE, CHARWISE, BLOCKWISE
4618      */
4619
4620     void
4621 clip_mch_request_selection(VimClipboard *cbd)
4622 {
4623
4624     Handle      textOfClip;
4625     int         flavor = 0;
4626     Size        scrapSize;
4627     ScrapFlavorFlags    scrapFlags;
4628     ScrapRef    scrap = nil;
4629     OSStatus    error;
4630     int         type;
4631     char        *searchCR;
4632     char_u      *tempclip;
4633
4634
4635     error = GetCurrentScrap(&scrap);
4636     if (error != noErr)
4637         return;
4638
4639     error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4640     if (error == noErr)
4641     {
4642         error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4643         if (error == noErr && scrapSize > 1)
4644             flavor = 1;
4645     }
4646
4647     if (flavor == 0)
4648     {
4649         error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4650         if (error != noErr)
4651             return;
4652
4653         error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4654         if (error != noErr)
4655             return;
4656     }
4657
4658     ReserveMem(scrapSize);
4659
4660     /* In CARBON we don't need a Handle, a pointer is good */
4661     textOfClip = NewHandle(scrapSize);
4662
4663     /* tempclip = lalloc(scrapSize+1, TRUE); */
4664     HLock(textOfClip);
4665     error = GetScrapFlavorData(scrap,
4666             flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4667             &scrapSize, *textOfClip);
4668     scrapSize -= flavor;
4669
4670     if (flavor)
4671         type = **textOfClip;
4672     else
4673         type = MAUTO;
4674
4675     tempclip = lalloc(scrapSize + 1, TRUE);
4676     mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4677     tempclip[scrapSize] = 0;
4678
4679 #ifdef MACOS_CONVERT
4680     {
4681         /* Convert from utf-16 (clipboard) */
4682         size_t encLen = 0;
4683         char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4684
4685         if (to != NULL)
4686         {
4687             scrapSize = encLen;
4688             vim_free(tempclip);
4689             tempclip = to;
4690         }
4691     }
4692 #endif
4693
4694     searchCR = (char *)tempclip;
4695     while (searchCR != NULL)
4696     {
4697         searchCR = strchr(searchCR, '\r');
4698         if (searchCR != NULL)
4699             *searchCR = '\n';
4700     }
4701
4702     clip_yank_selection(type, tempclip, scrapSize, cbd);
4703
4704     vim_free(tempclip);
4705     HUnlock(textOfClip);
4706
4707     DisposeHandle(textOfClip);
4708 }
4709
4710     void
4711 clip_mch_lose_selection(VimClipboard *cbd)
4712 {
4713     /*
4714      * TODO: Really nothing to do?
4715      */
4716 }
4717
4718     int
4719 clip_mch_own_selection(VimClipboard *cbd)
4720 {
4721     return OK;
4722 }
4723
4724 /*
4725  * Send the current selection to the clipboard.
4726  */
4727     void
4728 clip_mch_set_selection(VimClipboard *cbd)
4729 {
4730     Handle      textOfClip;
4731     long        scrapSize;
4732     int         type;
4733     ScrapRef    scrap;
4734
4735     char_u      *str = NULL;
4736
4737     if (!cbd->owned)
4738         return;
4739
4740     clip_get_selection(cbd);
4741
4742     /*
4743      * Once we set the clipboard, lose ownership.  If another application sets
4744      * the clipboard, we don't want to think that we still own it.
4745      */
4746     cbd->owned = FALSE;
4747
4748     type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4749
4750 #ifdef MACOS_CONVERT
4751     size_t utf16_len = 0;
4752     UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4753     if (to)
4754     {
4755         scrapSize = utf16_len;
4756         vim_free(str);
4757         str = (char_u *)to;
4758     }
4759 #endif
4760
4761     if (type >= 0)
4762     {
4763         ClearCurrentScrap();
4764
4765         textOfClip = NewHandle(scrapSize + 1);
4766         HLock(textOfClip);
4767
4768         **textOfClip = type;
4769         mch_memmove(*textOfClip + 1, str, scrapSize);
4770         GetCurrentScrap(&scrap);
4771         PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4772                 scrapSize, *textOfClip + 1);
4773         PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4774                 scrapSize + 1, *textOfClip);
4775         HUnlock(textOfClip);
4776         DisposeHandle(textOfClip);
4777     }
4778
4779     vim_free(str);
4780 }
4781
4782     void
4783 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4784 {
4785     Rect        VimBound;
4786
4787 /*  HideWindow(gui.VimWindow); */
4788     GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4789
4790     if (gui.which_scrollbars[SBAR_LEFT])
4791     {
4792         VimBound.left = -gui.scrollbar_width + 1;
4793     }
4794     else
4795     {
4796         VimBound.left = 0;
4797     }
4798
4799     SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4800
4801     ShowWindow(gui.VimWindow);
4802 }
4803
4804 /*
4805  * Menu stuff.
4806  */
4807
4808     void
4809 gui_mch_enable_menu(int flag)
4810 {
4811     /*
4812      * Menu is always active.
4813      */
4814 }
4815
4816     void
4817 gui_mch_set_menu_pos(int x, int y, int w, int h)
4818 {
4819     /*
4820      * The menu is always at the top of the screen.
4821      */
4822 }
4823
4824 /*
4825  * Add a sub menu to the menu bar.
4826  */
4827     void
4828 gui_mch_add_menu(vimmenu_T *menu, int idx)
4829 {
4830     /*
4831      * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4832      * TODO: use menu->mnemonic and menu->actext
4833      * TODO: Try to reuse menu id
4834      *       Carbon Help suggest to use only id between 1 and 235
4835      */
4836     static long  next_avail_id = 128;
4837     long         menu_after_me = 0; /* Default to the end */
4838 #if defined(FEAT_MBYTE)
4839     CFStringRef name;
4840 #else
4841     char_u      *name;
4842 #endif
4843     short        index;
4844     vimmenu_T   *parent = menu->parent;
4845     vimmenu_T   *brother = menu->next;
4846
4847     /* Cannot add a menu if ... */
4848     if ((parent != NULL && parent->submenu_id == 0))
4849         return;
4850
4851     /* menu ID greater than 1024 are reserved for ??? */
4852     if (next_avail_id == 1024)
4853         return;
4854
4855     /* My brother could be the PopUp, find my real brother */
4856     while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4857         brother = brother->next;
4858
4859     /*  Find where to insert the menu (for MenuBar) */
4860     if ((parent == NULL) && (brother != NULL))
4861         menu_after_me = brother->submenu_id;
4862
4863     /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4864     if (!menu_is_menubar(menu->name))
4865         menu_after_me = hierMenu;
4866
4867     /* Convert the name */
4868 #ifdef MACOS_CONVERT
4869     name = menu_title_removing_mnemonic(menu);
4870 #else
4871     name = C2Pascal_save(menu->dname);
4872 #endif
4873     if (name == NULL)
4874         return;
4875
4876     /* Create the menu unless it's the help menu */
4877     {
4878         /* Carbon suggest use of
4879          * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4880          * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4881          */
4882         menu->submenu_id = next_avail_id;
4883 #if defined(FEAT_MBYTE)
4884         if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4885             SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4886 #else
4887         menu->submenu_handle = NewMenu(menu->submenu_id, name);
4888 #endif
4889         next_avail_id++;
4890     }
4891
4892     if (parent == NULL)
4893     {
4894         /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4895
4896         /* TODO: Verify if we could only Insert Menu if really part of the
4897          * menubar The Inserted menu are scanned or the Command-key combos
4898          */
4899
4900         /* Insert the menu */
4901         InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4902 #if 1
4903         /* Vim should normally update it. TODO: verify */
4904         DrawMenuBar();
4905 #endif
4906     }
4907     else
4908     {
4909         /* Adding as a submenu */
4910
4911         index = gui_mac_get_menu_item_index(menu);
4912
4913         /* Call InsertMenuItem followed by SetMenuItemText
4914          * to avoid special character recognition by InsertMenuItem
4915          */
4916         InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4917 #if defined(FEAT_MBYTE)
4918         SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4919 #else
4920         SetMenuItemText(parent->submenu_handle, idx+1, name);
4921 #endif
4922         SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4923         SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4924         InsertMenu(menu->submenu_handle, hierMenu);
4925     }
4926
4927 #if defined(FEAT_MBYTE)
4928     CFRelease(name);
4929 #else
4930     vim_free(name);
4931 #endif
4932
4933 #if 0
4934     /* Done by Vim later on */
4935     DrawMenuBar();
4936 #endif
4937 }
4938
4939 /*
4940  * Add a menu item to a menu
4941  */
4942     void
4943 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4944 {
4945 #if defined(FEAT_MBYTE)
4946     CFStringRef name;
4947 #else
4948     char_u      *name;
4949 #endif
4950     vimmenu_T   *parent = menu->parent;
4951     int         menu_inserted;
4952
4953     /* Cannot add item, if the menu have not been created */
4954     if (parent->submenu_id == 0)
4955         return;
4956
4957     /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4958        for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4959
4960     /* Convert the name */
4961 #ifdef MACOS_CONVERT
4962     name = menu_title_removing_mnemonic(menu);
4963 #else
4964     name = C2Pascal_save(menu->dname);
4965 #endif
4966
4967     /* Where are just a menu item, so no handle, no id */
4968     menu->submenu_id = 0;
4969     menu->submenu_handle = NULL;
4970
4971     menu_inserted = 0;
4972     if (menu->actext)
4973     {
4974         /* If the accelerator text for the menu item looks like it describes
4975          * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4976          * item's command equivalent.
4977          */
4978         int         key = 0;
4979         int         modifiers = 0;
4980         char_u      *p_actext;
4981
4982         p_actext = menu->actext;
4983         key = find_special_key(&p_actext, &modifiers, FALSE, FALSE);
4984         if (*p_actext != 0)
4985             key = 0; /* error: trailing text */
4986         /* find_special_key() returns a keycode with as many of the
4987          * specified modifiers as appropriate already applied (e.g., for
4988          * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4989          * as the only modifier).  Since we want to display all of the
4990          * modifiers, we need to convert the keycode back to a printable
4991          * character plus modifiers.
4992          * TODO: Write an alternative find_special_key() that doesn't
4993          * apply modifiers.
4994          */
4995         if (key > 0 && key < 32)
4996         {
4997             /* Convert a control key to an uppercase letter.  Note that
4998              * by this point it is no longer possible to distinguish
4999              * between, e.g., Ctrl-S and Ctrl-Shift-S.
5000              */
5001             modifiers |= MOD_MASK_CTRL;
5002             key += '@';
5003         }
5004         /* If the keycode is an uppercase letter, set the Shift modifier.
5005          * If it is a lowercase letter, don't set the modifier, but convert
5006          * the letter to uppercase for display in the menu.
5007          */
5008         else if (key >= 'A' && key <= 'Z')
5009             modifiers |= MOD_MASK_SHIFT;
5010         else if (key >= 'a' && key <= 'z')
5011             key += 'A' - 'a';
5012         /* Note: keycodes below 0x22 are reserved by Apple. */
5013         if (key >= 0x22 && vim_isprintc_strict(key))
5014         {
5015             int         valid = 1;
5016             char_u      mac_mods = kMenuNoModifiers;
5017             /* Convert Vim modifier codes to Menu Manager equivalents. */
5018             if (modifiers & MOD_MASK_SHIFT)
5019                 mac_mods |= kMenuShiftModifier;
5020             if (modifiers & MOD_MASK_CTRL)
5021                 mac_mods |= kMenuControlModifier;
5022             if (!(modifiers & MOD_MASK_CMD))
5023                 mac_mods |= kMenuNoCommandModifier;
5024             if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
5025                 valid = 0; /* TODO: will Alt someday map to Option? */
5026             if (valid)
5027             {
5028                 char_u      item_txt[10];
5029                 /* Insert the menu item after idx, with its command key. */
5030                 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
5031                 item_txt[3] = key;
5032                 InsertMenuItem(parent->submenu_handle, item_txt, idx);
5033                 /* Set the modifier keys. */
5034                 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
5035                 menu_inserted = 1;
5036             }
5037         }
5038     }
5039     /* Call InsertMenuItem followed by SetMenuItemText
5040      * to avoid special character recognition by InsertMenuItem
5041      */
5042     if (!menu_inserted)
5043         InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
5044     /* Set the menu item name. */
5045 #if defined(FEAT_MBYTE)
5046     SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
5047 #else
5048     SetMenuItemText(parent->submenu_handle, idx+1, name);
5049 #endif
5050
5051 #if 0
5052     /* Called by Vim */
5053     DrawMenuBar();
5054 #endif
5055
5056 #if defined(FEAT_MBYTE)
5057     CFRelease(name);
5058 #else
5059     /* TODO: Can name be freed? */
5060     vim_free(name);
5061 #endif
5062 }
5063
5064     void
5065 gui_mch_toggle_tearoffs(int enable)
5066 {
5067     /* no tearoff menus */
5068 }
5069
5070 /*
5071  * Destroy the machine specific menu widget.
5072  */
5073     void
5074 gui_mch_destroy_menu(vimmenu_T *menu)
5075 {
5076     short       index = gui_mac_get_menu_item_index(menu);
5077
5078     if (index > 0)
5079     {
5080       if (menu->parent)
5081       {
5082         {
5083             /* For now just don't delete help menu items. (Huh? Dany) */
5084             DeleteMenuItem(menu->parent->submenu_handle, index);
5085
5086             /* Delete the Menu if it was a hierarchical Menu */
5087             if (menu->submenu_id != 0)
5088             {
5089                 DeleteMenu(menu->submenu_id);
5090                 DisposeMenu(menu->submenu_handle);
5091             }
5092         }
5093       }
5094 #ifdef DEBUG_MAC_MENU
5095       else
5096       {
5097         printf("gmdm 2\n");
5098       }
5099 #endif
5100     }
5101     else
5102     {
5103         {
5104             DeleteMenu(menu->submenu_id);
5105             DisposeMenu(menu->submenu_handle);
5106         }
5107     }
5108     /* Shouldn't this be already done by Vim. TODO: Check */
5109     DrawMenuBar();
5110 }
5111
5112 /*
5113  * Make a menu either grey or not grey.
5114  */
5115     void
5116 gui_mch_menu_grey(vimmenu_T *menu, int grey)
5117 {
5118     /* TODO: Check if menu really exists */
5119     short index = gui_mac_get_menu_item_index(menu);
5120 /*
5121     index = menu->index;
5122 */
5123     if (grey)
5124     {
5125         if (menu->children)
5126             DisableMenuItem(menu->submenu_handle, index);
5127         if (menu->parent)
5128           if (menu->parent->submenu_handle)
5129             DisableMenuItem(menu->parent->submenu_handle, index);
5130     }
5131     else
5132     {
5133         if (menu->children)
5134             EnableMenuItem(menu->submenu_handle, index);
5135         if (menu->parent)
5136           if (menu->parent->submenu_handle)
5137             EnableMenuItem(menu->parent->submenu_handle, index);
5138     }
5139 }
5140
5141 /*
5142  * Make menu item hidden or not hidden
5143  */
5144     void
5145 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5146 {
5147     /* There's no hidden mode on MacOS */
5148     gui_mch_menu_grey(menu, hidden);
5149 }
5150
5151
5152 /*
5153  * This is called after setting all the menus to grey/hidden or not.
5154  */
5155     void
5156 gui_mch_draw_menubar(void)
5157 {
5158     DrawMenuBar();
5159 }
5160
5161
5162 /*
5163  * Scrollbar stuff.
5164  */
5165
5166     void
5167 gui_mch_enable_scrollbar(
5168         scrollbar_T     *sb,
5169         int             flag)
5170 {
5171     if (flag)
5172         ShowControl(sb->id);
5173     else
5174         HideControl(sb->id);
5175
5176 #ifdef DEBUG_MAC_SB
5177     printf("enb_sb (%x) %x\n",sb->id, flag);
5178 #endif
5179 }
5180
5181     void
5182 gui_mch_set_scrollbar_thumb(
5183         scrollbar_T *sb,
5184         long val,
5185         long size,
5186         long max)
5187 {
5188     SetControl32BitMaximum (sb->id, max);
5189     SetControl32BitMinimum (sb->id, 0);
5190     SetControl32BitValue   (sb->id, val);
5191     SetControlViewSize     (sb->id, size);
5192 #ifdef DEBUG_MAC_SB
5193     printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
5194 #endif
5195 }
5196
5197     void
5198 gui_mch_set_scrollbar_pos(
5199         scrollbar_T *sb,
5200         int x,
5201         int y,
5202         int w,
5203         int h)
5204 {
5205     gui_mch_set_bg_color(gui.back_pixel);
5206 /*  if (gui.which_scrollbars[SBAR_LEFT])
5207     {
5208         MoveControl(sb->id, x-16, y);
5209         SizeControl(sb->id, w + 1, h);
5210     }
5211     else
5212     {
5213         MoveControl(sb->id, x, y);
5214         SizeControl(sb->id, w + 1, h);
5215     }*/
5216     if (sb == &gui.bottom_sbar)
5217         h += 1;
5218     else
5219         w += 1;
5220
5221     if (gui.which_scrollbars[SBAR_LEFT])
5222         x -= 15;
5223
5224     MoveControl(sb->id, x, y);
5225     SizeControl(sb->id, w, h);
5226 #ifdef DEBUG_MAC_SB
5227     printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
5228 #endif
5229 }
5230
5231     void
5232 gui_mch_create_scrollbar(
5233         scrollbar_T *sb,
5234         int orient)     /* SBAR_VERT or SBAR_HORIZ */
5235 {
5236     Rect bounds;
5237
5238     bounds.top = -16;
5239     bounds.bottom = -10;
5240     bounds.right = -10;
5241     bounds.left = -16;
5242
5243     sb->id = NewControl(gui.VimWindow,
5244                          &bounds,
5245                          "\pScrollBar",
5246                          TRUE,
5247                          0, /* current*/
5248                          0, /* top */
5249                          0, /* bottom */
5250                          kControlScrollBarLiveProc,
5251                          (long) sb->ident);
5252 #ifdef DEBUG_MAC_SB
5253     printf("create_sb (%x) %x\n",sb->id, orient);
5254 #endif
5255 }
5256
5257     void
5258 gui_mch_destroy_scrollbar(scrollbar_T *sb)
5259 {
5260     gui_mch_set_bg_color(gui.back_pixel);
5261     DisposeControl(sb->id);
5262 #ifdef DEBUG_MAC_SB
5263     printf("dest_sb (%x) \n",sb->id);
5264 #endif
5265 }
5266
5267
5268 /*
5269  * Cursor blink functions.
5270  *
5271  * This is a simple state machine:
5272  * BLINK_NONE   not blinking at all
5273  * BLINK_OFF    blinking, cursor is not shown
5274  * BLINK_ON blinking, cursor is shown
5275  */
5276     void
5277 gui_mch_set_blinking(long wait, long on, long off)
5278 {
5279     /* TODO: TODO: TODO: TODO: */
5280 /*    blink_waittime = wait;
5281     blink_ontime = on;
5282     blink_offtime = off;*/
5283 }
5284
5285 /*
5286  * Stop the cursor blinking.  Show the cursor if it wasn't shown.
5287  */
5288     void
5289 gui_mch_stop_blink(void)
5290 {
5291     gui_update_cursor(TRUE, FALSE);
5292     /* TODO: TODO: TODO: TODO: */
5293 /*    gui_w32_rm_blink_timer();
5294     if (blink_state == BLINK_OFF)
5295     gui_update_cursor(TRUE, FALSE);
5296     blink_state = BLINK_NONE;*/
5297 }
5298
5299 /*
5300  * Start the cursor blinking.  If it was already blinking, this restarts the
5301  * waiting time and shows the cursor.
5302  */
5303     void
5304 gui_mch_start_blink(void)
5305 {
5306     gui_update_cursor(TRUE, FALSE);
5307     /* TODO: TODO: TODO: TODO: */
5308 /*    gui_w32_rm_blink_timer(); */
5309
5310     /* Only switch blinking on if none of the times is zero */
5311 /*    if (blink_waittime && blink_ontime && blink_offtime)
5312     {
5313     blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5314                             (TIMERPROC)_OnBlinkTimer);
5315     blink_state = BLINK_ON;
5316     gui_update_cursor(TRUE, FALSE);
5317     }*/
5318 }
5319
5320 /*
5321  * Return the RGB value of a pixel as long.
5322  */
5323     long_u
5324 gui_mch_get_rgb(guicolor_T pixel)
5325 {
5326     return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5327 }
5328
5329
5330
5331 #ifdef FEAT_BROWSE
5332 /*
5333  * Pop open a file browser and return the file selected, in allocated memory,
5334  * or NULL if Cancel is hit.
5335  *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
5336  *  title   - Title message for the file browser dialog.
5337  *  dflt    - Default name of file.
5338  *  ext     - Default extension to be added to files without extensions.
5339  *  initdir - directory in which to open the browser (NULL = current dir)
5340  *  filter  - Filter for matched files to choose from.
5341  *  Has a format like this:
5342  *  "C Files (*.c)\0*.c\0"
5343  *  "All Files\0*.*\0\0"
5344  *  If these two strings were concatenated, then a choice of two file
5345  *  filters will be selectable to the user.  Then only matching files will
5346  *  be shown in the browser.  If NULL, the default allows all files.
5347  *
5348  *  *NOTE* - the filter string must be terminated with TWO nulls.
5349  */
5350     char_u *
5351 gui_mch_browse(
5352     int saving,
5353     char_u *title,
5354     char_u *dflt,
5355     char_u *ext,
5356     char_u *initdir,
5357     char_u *filter)
5358 {
5359     /* TODO: Add Ammon's safety checl (Dany) */
5360     NavReplyRecord      reply;
5361     char_u              *fname = NULL;
5362     char_u              **fnames = NULL;
5363     long                numFiles;
5364     NavDialogOptions    navOptions;
5365     OSErr               error;
5366
5367     /* Get Navigation Service Defaults value */
5368     NavGetDefaultDialogOptions(&navOptions);
5369
5370
5371     /* TODO: If we get a :browse args, set the Multiple bit. */
5372     navOptions.dialogOptionFlags =  kNavAllowInvisibleFiles
5373                                  |  kNavDontAutoTranslate
5374                                  |  kNavDontAddTranslateItems
5375                             /*   |  kNavAllowMultipleFiles */
5376                                  |  kNavAllowStationery;
5377
5378     (void) C2PascalString(title,   &navOptions.message);
5379     (void) C2PascalString(dflt,    &navOptions.savedFileName);
5380     /* Could set clientName?
5381      *           windowTitle? (there's no title bar?)
5382      */
5383
5384     if (saving)
5385     {
5386         /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5387         NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5388         if (!reply.validRecord)
5389             return NULL;
5390     }
5391     else
5392     {
5393         /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5394         NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5395         if (!reply.validRecord)
5396             return NULL;
5397     }
5398
5399     fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5400
5401     NavDisposeReply(&reply);
5402
5403     if (fnames)
5404     {
5405         fname = fnames[0];
5406         vim_free(fnames);
5407     }
5408
5409     /* TODO: Shorten the file name if possible */
5410     return fname;
5411 }
5412 #endif /* FEAT_BROWSE */
5413
5414 #ifdef FEAT_GUI_DIALOG
5415 /*
5416  * Stuff for dialogues
5417  */
5418
5419 /*
5420  * Create a dialogue dynamically from the parameter strings.
5421  * type       = type of dialogue (question, alert, etc.)
5422  * title      = dialogue title. may be NULL for default title.
5423  * message    = text to display. Dialogue sizes to accommodate it.
5424  * buttons    = '\n' separated list of button captions, default first.
5425  * dfltbutton = number of default button.
5426  *
5427  * This routine returns 1 if the first button is pressed,
5428  *          2 for the second, etc.
5429  *
5430  *          0 indicates Esc was pressed.
5431  *          -1 for unexpected error
5432  *
5433  * If stubbing out this fn, return 1.
5434  */
5435
5436 typedef struct
5437 {
5438     short   idx;
5439     short   width;      /* Size of the text in pixel */
5440     Rect    box;
5441 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5442
5443 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5444
5445     static void
5446 macMoveDialogItem(
5447     DialogRef   theDialog,
5448     short       itemNumber,
5449     short       X,
5450     short       Y,
5451     Rect        *inBox)
5452 {
5453 #if 0 /* USE_CARBONIZED */
5454     /* Untested */
5455     MoveDialogItem(theDialog, itemNumber, X, Y);
5456     if (inBox != nil)
5457         GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5458 #else
5459     short       itemType;
5460     Handle      itemHandle;
5461     Rect        localBox;
5462     Rect        *itemBox = &localBox;
5463
5464     if (inBox != nil)
5465         itemBox = inBox;
5466
5467     GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5468     OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5469     OffsetRect(itemBox, X, Y);
5470     /* To move a control (like a button) we need to call both
5471      * MoveControl and SetDialogItem. FAQ 6-18 */
5472     if (1) /*(itemType & kControlDialogItem) */
5473         MoveControl((ControlRef) itemHandle, X, Y);
5474     SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5475 #endif
5476 }
5477
5478     static void
5479 macSizeDialogItem(
5480     DialogRef   theDialog,
5481     short       itemNumber,
5482     short       width,
5483     short       height)
5484 {
5485     short       itemType;
5486     Handle      itemHandle;
5487     Rect        itemBox;
5488
5489     GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5490
5491     /* When width or height is zero do not change it */
5492     if (width  == 0)
5493         width  = itemBox.right  - itemBox.left;
5494     if (height == 0)
5495         height = itemBox.bottom - itemBox.top;
5496
5497 #if 0 /* USE_CARBONIZED */
5498     SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5499 #else
5500     /* Resize the bounding box */
5501     itemBox.right  = itemBox.left + width;
5502     itemBox.bottom = itemBox.top  + height;
5503
5504     /* To resize a control (like a button) we need to call both
5505      * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5506     if (itemType & kControlDialogItem)
5507         SizeControl((ControlRef) itemHandle, width, height);
5508
5509     /* Configure back the item */
5510     SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5511 #endif
5512 }
5513
5514     static void
5515 macSetDialogItemText(
5516     DialogRef   theDialog,
5517     short       itemNumber,
5518     Str255      itemName)
5519 {
5520     short       itemType;
5521     Handle      itemHandle;
5522     Rect        itemBox;
5523
5524     GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5525
5526     if (itemType & kControlDialogItem)
5527         SetControlTitle((ControlRef) itemHandle, itemName);
5528     else
5529         SetDialogItemText(itemHandle, itemName);
5530 }
5531
5532
5533 /* ModalDialog() handler for message dialogs that have hotkey accelerators.
5534  * Expects a mapping of hotkey char to control index in gDialogHotKeys;
5535  * setting gDialogHotKeys to NULL disables any hotkey handling.
5536  */
5537     static pascal Boolean
5538 DialogHotkeyFilterProc (
5539     DialogRef       theDialog,
5540     EventRecord     *event,
5541     DialogItemIndex *itemHit)
5542 {
5543     char_u keyHit;
5544
5545     if (event->what == keyDown || event->what == autoKey)
5546     {
5547         keyHit = (event->message & charCodeMask);
5548
5549         if (gDialogHotKeys && gDialogHotKeys[keyHit])
5550         {
5551 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5552             printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]);
5553 #endif
5554             *itemHit = gDialogHotKeys[keyHit];
5555
5556             /* When handing off to StdFilterProc, pretend that the user
5557              * clicked the control manually. Note that this is also supposed
5558              * to cause the button to hilite briefly (to give some user
5559              * feedback), but this seems not to actually work (or it's too
5560              * fast to be seen).
5561              */
5562             event->what = kEventControlSimulateHit;
5563
5564             return true; /* we took care of it */
5565         }
5566
5567         /* Defer to the OS's standard behavior for this event.
5568          * This ensures that Enter will still activate the default button. */
5569         return StdFilterProc(theDialog, event, itemHit);
5570     }
5571     return false;      /* Let ModalDialog deal with it */
5572 }
5573
5574
5575 /* TODO: There have been some crashes with dialogs, check your inbox
5576  * (Jussi)
5577  */
5578     int
5579 gui_mch_dialog(
5580     int         type,
5581     char_u      *title,
5582     char_u      *message,
5583     char_u      *buttons,
5584     int         dfltbutton,
5585     char_u      *textfield,
5586     int         ex_cmd)
5587 {
5588     Handle      buttonDITL;
5589     Handle      iconDITL;
5590     Handle      inputDITL;
5591     Handle      messageDITL;
5592     Handle      itemHandle;
5593     Handle      iconHandle;
5594     DialogPtr   theDialog;
5595     char_u      len;
5596     char_u      PascalTitle[256];       /* place holder for the title */
5597     char_u      name[256];
5598     GrafPtr     oldPort;
5599     short       itemHit;
5600     char_u      *buttonChar;
5601     short       hotKeys[256];           /* map of hotkey -> control ID */
5602     char_u      aHotKey;
5603     Rect        box;
5604     short       button;
5605     short       lastButton;
5606     short       itemType;
5607     short       useIcon;
5608     short       width;
5609     short       totalButtonWidth = 0;   /* the width of all buttons together
5610                                            including spacing */
5611     short       widestButton = 0;
5612     short       dfltButtonEdge     = 20;  /* gut feeling */
5613     short       dfltElementSpacing = 13;  /* from IM:V.2-29 */
5614     short       dfltIconSideSpace  = 23;  /* from IM:V.2-29 */
5615     short       maximumWidth       = 400; /* gut feeling */
5616     short       maxButtonWidth     = 175; /* gut feeling */
5617
5618     short       vertical;
5619     short       dialogHeight;
5620     short       messageLines = 3;
5621     FontInfo    textFontInfo;
5622
5623     vgmDlgItm   iconItm;
5624     vgmDlgItm   messageItm;
5625     vgmDlgItm   inputItm;
5626     vgmDlgItm   buttonItm;
5627
5628     WindowRef   theWindow;
5629
5630     ModalFilterUPP dialogUPP;
5631
5632     /* Check 'v' flag in 'guioptions': vertical button placement. */
5633     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5634
5635     /* Create a new Dialog Box from template. */
5636     theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5637
5638     /* Get the WindowRef */
5639     theWindow = GetDialogWindow(theDialog);
5640
5641     /* Hide the window.
5642      * 1. to avoid seeing slow drawing
5643      * 2. to prevent a problem seen while moving dialog item
5644      *    within a visible window. (non-Carbon MacOS 9)
5645      * Could be avoided by changing the resource.
5646      */
5647     HideWindow(theWindow);
5648
5649     /* Change the graphical port to the dialog,
5650      * so we can measure the text with the proper font */
5651     GetPort(&oldPort);
5652     SetPortDialogPort(theDialog);
5653
5654     /* Get the info about the default text,
5655      * used to calculate the height of the message
5656      * and of the  text field */
5657     GetFontInfo(&textFontInfo);
5658
5659     /*  Set the dialog title */
5660     if (title != NULL)
5661     {
5662         (void) C2PascalString(title, &PascalTitle);
5663         SetWTitle(theWindow, PascalTitle);
5664     }
5665
5666     /* Creates the buttons and add them to the Dialog Box. */
5667     buttonDITL = GetResource('DITL', 130);
5668     buttonChar = buttons;
5669     button = 0;
5670
5671     /* initialize the hotkey mapping */
5672     vim_memset(hotKeys, 0, sizeof(hotKeys));
5673
5674     for (;*buttonChar != 0;)
5675     {
5676         /* Get the name of the button */
5677         button++;
5678         len = 0;
5679         for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5680         {
5681             if (*buttonChar != DLG_HOTKEY_CHAR)
5682                 name[++len] = *buttonChar;
5683             else
5684             {
5685                 aHotKey = (char_u)*(buttonChar+1);
5686                 if (aHotKey >= 'A' && aHotKey <= 'Z')
5687                     aHotKey = (char_u)((int)aHotKey + (int)'a' - (int)'A');
5688                 hotKeys[aHotKey] = button;
5689 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5690                 printf("### hotKey for button %d is '%c'\n", button, aHotKey);
5691 #endif
5692             }
5693         }
5694
5695         if (*buttonChar != 0)
5696           buttonChar++;
5697         name[0] = len;
5698
5699         /* Add the button */
5700         AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5701
5702         /* Change the button's name */
5703         macSetDialogItemText(theDialog, button, name);
5704
5705         /* Resize the button to fit its name */
5706         width = StringWidth(name) + 2 * dfltButtonEdge;
5707         /* Limite the size of any button to an acceptable value. */
5708         /* TODO: Should be based on the message width */
5709         if (width > maxButtonWidth)
5710             width = maxButtonWidth;
5711         macSizeDialogItem(theDialog, button, width, 0);
5712
5713         totalButtonWidth += width;
5714
5715         if (width > widestButton)
5716             widestButton = width;
5717     }
5718     ReleaseResource(buttonDITL);
5719     lastButton = button;
5720
5721     /* Add the icon to the Dialog Box. */
5722     iconItm.idx = lastButton + 1;
5723     iconDITL = GetResource('DITL', 131);
5724     switch (type)
5725     {
5726         case VIM_GENERIC:
5727         case VIM_INFO:
5728         case VIM_QUESTION: useIcon = kNoteIcon; break;
5729         case VIM_WARNING:  useIcon = kCautionIcon; break;
5730         case VIM_ERROR:    useIcon = kStopIcon; break;
5731         default:           useIcon = kStopIcon;
5732     }
5733     AppendDITL(theDialog, iconDITL, overlayDITL);
5734     ReleaseResource(iconDITL);
5735     GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5736     /* TODO: Should the item be freed? */
5737     iconHandle = GetIcon(useIcon);
5738     SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5739
5740     /* Add the message to the Dialog box. */
5741     messageItm.idx = lastButton + 2;
5742     messageDITL = GetResource('DITL', 132);
5743     AppendDITL(theDialog, messageDITL, overlayDITL);
5744     ReleaseResource(messageDITL);
5745     GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5746     (void) C2PascalString(message, &name);
5747     SetDialogItemText(itemHandle, name);
5748     messageItm.width = StringWidth(name);
5749
5750     /* Add the input box if needed */
5751     if (textfield != NULL)
5752     {
5753         /* Cheat for now reuse the message and convert to text edit */
5754         inputItm.idx = lastButton + 3;
5755         inputDITL = GetResource('DITL', 132);
5756         AppendDITL(theDialog, inputDITL, overlayDITL);
5757         ReleaseResource(inputDITL);
5758         GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5759 /*        SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5760         (void) C2PascalString(textfield, &name);
5761         SetDialogItemText(itemHandle, name);
5762         inputItm.width = StringWidth(name);
5763
5764         /* Hotkeys don't make sense if there's a text field */
5765         gDialogHotKeys = NULL;
5766     }
5767     else
5768         /* Install hotkey table */
5769         gDialogHotKeys = (short *)&hotKeys;
5770
5771     /* Set the <ENTER> and <ESC> button. */
5772     SetDialogDefaultItem(theDialog, dfltbutton);
5773     SetDialogCancelItem(theDialog, 0);
5774
5775     /* Reposition element */
5776
5777     /* Check if we need to force vertical */
5778     if (totalButtonWidth > maximumWidth)
5779         vertical = TRUE;
5780
5781     /* Place icon */
5782     macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5783     iconItm.box.right = box.right;
5784     iconItm.box.bottom = box.bottom;
5785
5786     /* Place Message */
5787     messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5788     macSizeDialogItem(theDialog, messageItm.idx, 0,  messageLines * (textFontInfo.ascent + textFontInfo.descent));
5789     macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5790
5791     /* Place Input */
5792     if (textfield != NULL)
5793     {
5794         inputItm.box.left = messageItm.box.left;
5795         inputItm.box.top  = messageItm.box.bottom + dfltElementSpacing;
5796         macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5797         macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5798         /* Convert the static text into a text edit.
5799          * For some reason this change need to be done last (Dany) */
5800         GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5801         SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5802         SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5803     }
5804
5805     /* Place Button */
5806     if (textfield != NULL)
5807     {
5808         buttonItm.box.left = inputItm.box.left;
5809         buttonItm.box.top  = inputItm.box.bottom + dfltElementSpacing;
5810     }
5811     else
5812     {
5813         buttonItm.box.left = messageItm.box.left;
5814         buttonItm.box.top  = messageItm.box.bottom + dfltElementSpacing;
5815     }
5816
5817     for (button=1; button <= lastButton; button++)
5818     {
5819
5820         macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5821         /* With vertical, it's better to have all buttons the same length */
5822         if (vertical)
5823         {
5824             macSizeDialogItem(theDialog, button, widestButton, 0);
5825             GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5826         }
5827         /* Calculate position of next button */
5828         if (vertical)
5829             buttonItm.box.top  = box.bottom + dfltElementSpacing;
5830         else
5831             buttonItm.box.left  = box.right + dfltElementSpacing;
5832     }
5833
5834     /* Resize the dialog box */
5835     dialogHeight = box.bottom + dfltElementSpacing;
5836     SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5837
5838     /* Magic resize */
5839     AutoSizeDialog(theDialog);
5840     /* Need a horizontal resize anyway so not that useful */
5841
5842     /* Display it */
5843     ShowWindow(theWindow);
5844 /*  BringToFront(theWindow); */
5845     SelectWindow(theWindow);
5846
5847 /*  DrawDialog(theDialog); */
5848 #if 0
5849     GetPort(&oldPort);
5850     SetPortDialogPort(theDialog);
5851 #endif
5852
5853 #ifdef USE_CARBONKEYHANDLER
5854     /* Avoid that we use key events for the main window. */
5855     dialog_busy = TRUE;
5856 #endif
5857
5858     /* Prepare the shortcut-handling filterProc for handing to the dialog */
5859     dialogUPP = NewModalFilterUPP(DialogHotkeyFilterProc);
5860
5861     /* Hang until one of the button is hit */
5862     do
5863     {
5864         ModalDialog(dialogUPP, &itemHit);
5865     } while ((itemHit < 1) || (itemHit > lastButton));
5866
5867 #ifdef USE_CARBONKEYHANDLER
5868     dialog_busy = FALSE;
5869 #endif
5870
5871     /* Copy back the text entered by the user into the param */
5872     if (textfield != NULL)
5873     {
5874         GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5875         GetDialogItemText(itemHandle, (char_u *) &name);
5876 #if IOSIZE < 256
5877         /* Truncate the name to IOSIZE if needed */
5878         if (name[0] > IOSIZE)
5879             name[0] = IOSIZE - 1;
5880 #endif
5881         vim_strncpy(textfield, &name[1], name[0]);
5882     }
5883
5884     /* Restore the original graphical port */
5885     SetPort(oldPort);
5886
5887     /* Free the modal filterProc */
5888     DisposeRoutineDescriptor(dialogUPP);
5889
5890     /* Get ride of th edialog (free memory) */
5891     DisposeDialog(theDialog);
5892
5893     return itemHit;
5894 /*
5895  * Useful thing which could be used
5896  * SetDialogTimeout(): Auto click a button after timeout
5897  * SetDialogTracksCursor() : Get the I-beam cursor over input box
5898  * MoveDialogItem():        Probably better than SetDialogItem
5899  * SizeDialogItem():            (but is it Carbon Only?)
5900  * AutoSizeDialog():        Magic resize of dialog based on text length
5901  */
5902 }
5903 #endif /* FEAT_DIALOG_GUI */
5904
5905 /*
5906  * Display the saved error message(s).
5907  */
5908 #ifdef USE_MCH_ERRMSG
5909     void
5910 display_errors(void)
5911 {
5912     char        *p;
5913     char_u      pError[256];
5914
5915     if (error_ga.ga_data == NULL)
5916         return;
5917
5918     /* avoid putting up a message box with blanks only */
5919     for (p = (char *)error_ga.ga_data; *p; ++p)
5920         if (!isspace(*p))
5921         {
5922             if (STRLEN(p) > 255)
5923                 pError[0] = 255;
5924             else
5925                 pError[0] = STRLEN(p);
5926
5927             STRNCPY(&pError[1], p, pError[0]);
5928             ParamText(pError, nil, nil, nil);
5929             Alert(128, nil);
5930             break;
5931             /* TODO: handled message longer than 256 chars
5932              *   use auto-sizeable alert
5933              *   or dialog with scrollbars (TextEdit zone)
5934              */
5935         }
5936     ga_clear(&error_ga);
5937 }
5938 #endif
5939
5940 /*
5941  * Get current mouse coordinates in text window.
5942  */
5943     void
5944 gui_mch_getmouse(int *x, int *y)
5945 {
5946     Point where;
5947
5948     GetMouse(&where);
5949
5950     *x = where.h;
5951     *y = where.v;
5952 }
5953
5954     void
5955 gui_mch_setmouse(int x, int y)
5956 {
5957     /* TODO */
5958 #if 0
5959     /* From FAQ 3-11 */
5960
5961     CursorDevicePtr myMouse;
5962     Point           where;
5963
5964     if (   NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5965         != NGetTrapAddress(_Unimplemented,   ToolTrap))
5966     {
5967         /* New way */
5968
5969         /*
5970          * Get first devoice with one button.
5971          * This will probably be the standad mouse
5972          * startat head of cursor dev list
5973          *
5974          */
5975
5976         myMouse = nil;
5977
5978         do
5979         {
5980             /* Get the next cursor device */
5981             CursorDeviceNextDevice(&myMouse);
5982         }
5983         while ((myMouse != nil) && (myMouse->cntButtons != 1));
5984
5985         CursorDeviceMoveTo(myMouse, x, y);
5986     }
5987     else
5988     {
5989         /* Old way */
5990         where.h = x;
5991         where.v = y;
5992
5993         *(Point *)RawMouse = where;
5994         *(Point *)MTemp    = where;
5995         *(Ptr)    CrsrNew  = 0xFFFF;
5996     }
5997 #endif
5998 }
5999
6000     void
6001 gui_mch_show_popupmenu(vimmenu_T *menu)
6002 {
6003 /*
6004  *  Clone PopUp to use menu
6005  *  Create a object descriptor for the current selection
6006  *  Call the procedure
6007  */
6008
6009     MenuHandle  CntxMenu;
6010     Point       where;
6011     OSStatus    status;
6012     UInt32      CntxType;
6013     SInt16      CntxMenuID;
6014     UInt16      CntxMenuItem;
6015     Str255      HelpName = "";
6016     GrafPtr     savePort;
6017
6018     /* Save Current Port: On MacOS X we seem to lose the port */
6019     GetPort(&savePort); /*OSX*/
6020
6021     GetMouse(&where);
6022     LocalToGlobal(&where); /*OSX*/
6023     CntxMenu = menu->submenu_handle;
6024
6025     /* TODO: Get the text selection from Vim */
6026
6027     /* Call to Handle Popup */
6028     status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
6029                        HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
6030
6031     if (status == noErr)
6032     {
6033         if (CntxType == kCMMenuItemSelected)
6034         {
6035             /* Handle the menu CntxMenuID, CntxMenuItem */
6036             /* The submenu can be handle directly by gui_mac_handle_menu */
6037             /* But what about the current menu, is the menu changed by
6038              * ContextualMenuSelect */
6039             gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
6040         }
6041         else if (CntxMenuID == kCMShowHelpSelected)
6042         {
6043             /* Should come up with the help */
6044         }
6045     }
6046
6047     /* Restore original Port */
6048     SetPort(savePort); /*OSX*/
6049 }
6050
6051 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
6052 /* TODO: Is it need for MACOS_X? (Dany) */
6053     void
6054 mch_post_buffer_write(buf_T *buf)
6055 {
6056     GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
6057     Send_KAHL_MOD_AE(buf);
6058 }
6059 #endif
6060
6061 #ifdef FEAT_TITLE
6062 /*
6063  * Set the window title and icon.
6064  * (The icon is not taken care of).
6065  */
6066     void
6067 gui_mch_settitle(char_u *title, char_u *icon)
6068 {
6069     /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
6070      *       that 256. Even better get it to fit nicely in the titlebar.
6071      */
6072 #ifdef MACOS_CONVERT
6073     CFStringRef windowTitle;
6074     size_t      windowTitleLen;
6075 #else
6076     char_u   *pascalTitle;
6077 #endif
6078
6079     if (title == NULL)          /* nothing to do */
6080         return;
6081
6082 #ifdef MACOS_CONVERT
6083     windowTitleLen = STRLEN(title);
6084     windowTitle  = (CFStringRef)mac_enc_to_cfstring(title, windowTitleLen);
6085
6086     if (windowTitle)
6087     {
6088         SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
6089         CFRelease(windowTitle);
6090     }
6091 #else
6092     pascalTitle = C2Pascal_save(title);
6093     if (pascalTitle != NULL)
6094     {
6095         SetWTitle(gui.VimWindow, pascalTitle);
6096         vim_free(pascalTitle);
6097     }
6098 #endif
6099 }
6100 #endif
6101
6102 /*
6103  * Transferred from os_mac.c for MacOS X using os_unix.c prep work
6104  */
6105
6106     int
6107 C2PascalString(char_u *CString, Str255 *PascalString)
6108 {
6109     char_u *PascalPtr = (char_u *) PascalString;
6110     int    len;
6111     int    i;
6112
6113     PascalPtr[0] = 0;
6114     if (CString == NULL)
6115         return 0;
6116
6117     len = STRLEN(CString);
6118     if (len > 255)
6119         len = 255;
6120
6121     for (i = 0; i < len; i++)
6122         PascalPtr[i+1] = CString[i];
6123
6124     PascalPtr[0] = len;
6125
6126     return 0;
6127 }
6128
6129     int
6130 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
6131 {
6132     /* From FAQ 8-12 */
6133     Str255      filePascal;
6134     CInfoPBRec  myCPB;
6135     OSErr       err;
6136
6137     (void) C2PascalString(file, &filePascal);
6138
6139     myCPB.dirInfo.ioNamePtr   = filePascal;
6140     myCPB.dirInfo.ioVRefNum   = 0;
6141     myCPB.dirInfo.ioFDirIndex = 0;
6142     myCPB.dirInfo.ioDrDirID   = 0;
6143
6144     err= PBGetCatInfo(&myCPB, false);
6145
6146     /*    vRefNum, dirID, name */
6147     FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
6148
6149     /* TODO: Use an error code mechanism */
6150     return 0;
6151 }
6152
6153 /*
6154  * Convert a FSSpec to a fuill path
6155  */
6156
6157 char_u *FullPathFromFSSpec_save(FSSpec file)
6158 {
6159     /*
6160      * TODO: Add protection for 256 char max.
6161      */
6162
6163     CInfoPBRec  theCPB;
6164     char_u      fname[256];
6165     char_u      *filenamePtr = fname;
6166     OSErr       error;
6167     int         folder = 1;
6168 #ifdef USE_UNIXFILENAME
6169     SInt16      dfltVol_vRefNum;
6170     SInt32      dfltVol_dirID;
6171     FSRef       refFile;
6172     OSStatus    status;
6173     UInt32      pathSize = 256;
6174     char_u      pathname[256];
6175     char_u      *path = pathname;
6176 #else
6177     Str255      directoryName;
6178     char_u      temporary[255];
6179     char_u      *temporaryPtr = temporary;
6180 #endif
6181
6182 #ifdef USE_UNIXFILENAME
6183     /* Get the default volume */
6184     /* TODO: Remove as this only work if Vim is on the Boot Volume*/
6185     error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
6186
6187     if (error)
6188       return NULL;
6189 #endif
6190
6191     /* Start filling fname with file.name  */
6192     vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
6193
6194     /* Get the info about the file specified in FSSpec */
6195     theCPB.dirInfo.ioFDirIndex = 0;
6196     theCPB.dirInfo.ioNamePtr   = file.name;
6197     theCPB.dirInfo.ioVRefNum   = file.vRefNum;
6198     /*theCPB.hFileInfo.ioDirID   = 0;*/
6199     theCPB.dirInfo.ioDrDirID   = file.parID;
6200
6201     /* As ioFDirIndex = 0, get the info of ioNamePtr,
6202        which is relative to ioVrefNum, ioDirID */
6203     error = PBGetCatInfo(&theCPB, false);
6204
6205     /* If we are called for a new file we expect fnfErr */
6206     if ((error) && (error != fnfErr))
6207       return NULL;
6208
6209     /* Check if it's a file or folder       */
6210     /* default to file if file don't exist  */
6211     if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
6212       folder = 0; /* It's not a folder */
6213     else
6214       folder = 1;
6215
6216 #ifdef USE_UNIXFILENAME
6217     /*
6218      * The function used here are available in Carbon, but
6219      * do nothing une MacOS 8 and 9
6220      */
6221     if (error == fnfErr)
6222     {
6223         /* If the file to be saved does not already exist, it isn't possible
6224            to convert its FSSpec into an FSRef.  But we can construct an
6225            FSSpec for the file's parent folder (since we have its volume and
6226            directory IDs), and since that folder does exist, we can convert
6227            that FSSpec into an FSRef, convert the FSRef in turn into a path,
6228            and, finally, append the filename. */
6229         FSSpec dirSpec;
6230         FSRef dirRef;
6231         Str255 emptyFilename = "\p";
6232         error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
6233             theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
6234         if (error)
6235             return NULL;
6236
6237         error = FSpMakeFSRef(&dirSpec, &dirRef);
6238         if (error)
6239             return NULL;
6240
6241         status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
6242         if (status)
6243             return NULL;
6244
6245         STRCAT(path, "/");
6246         STRCAT(path, filenamePtr);
6247     }
6248     else
6249     {
6250         /* If the file to be saved already exists, we can get its full path
6251            by converting its FSSpec into an FSRef. */
6252         error=FSpMakeFSRef(&file, &refFile);
6253         if (error)
6254             return NULL;
6255
6256         status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
6257         if (status)
6258             return NULL;
6259     }
6260
6261     /* Add a slash at the end if needed */
6262     if (folder)
6263         STRCAT(path, "/");
6264
6265     return (vim_strsave(path));
6266 #else
6267     /* TODO: Get rid of all USE_UNIXFILENAME below */
6268     /* Set ioNamePtr, it's the same area which is always reused. */
6269     theCPB.dirInfo.ioNamePtr = directoryName;
6270
6271     /* Trick for first entry, set ioDrParID to the first value
6272      * we want for ioDrDirID*/
6273     theCPB.dirInfo.ioDrParID = file.parID;
6274     theCPB.dirInfo.ioDrDirID = file.parID;
6275
6276     if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
6277     do
6278     {
6279         theCPB.dirInfo.ioFDirIndex = -1;
6280      /* theCPB.dirInfo.ioNamePtr   = directoryName; Already done above. */
6281         theCPB.dirInfo.ioVRefNum   = file.vRefNum;
6282      /* theCPB.dirInfo.ioDirID     = irrelevant when ioFDirIndex = -1 */
6283         theCPB.dirInfo.ioDrDirID   = theCPB.dirInfo.ioDrParID;
6284
6285         /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6286         /*  *ioNamePtr[0 TO 31] will be updated            */
6287         error = PBGetCatInfo(&theCPB,false);
6288
6289         if (error)
6290           return NULL;
6291
6292         /* Put the new directoryName in front of the current fname */
6293         STRCPY(temporaryPtr, filenamePtr);
6294         vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6295         STRCAT(filenamePtr, ":");
6296         STRCAT(filenamePtr, temporaryPtr);
6297     }
6298 #if 1 /* def USE_UNIXFILENAME */
6299     while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
6300          /*  (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
6301 #else
6302     while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
6303 #endif
6304
6305     /* Get the information about the volume on which the file reside */
6306     theCPB.dirInfo.ioFDirIndex = -1;
6307  /* theCPB.dirInfo.ioNamePtr   = directoryName; Already done above. */
6308     theCPB.dirInfo.ioVRefNum   = file.vRefNum;
6309  /* theCPB.dirInfo.ioDirID     = irrelevant when ioFDirIndex = -1 */
6310     theCPB.dirInfo.ioDrDirID   = theCPB.dirInfo.ioDrParID;
6311
6312     /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6313     /*  *ioNamePtr[0 TO 31] will be updated            */
6314     error = PBGetCatInfo(&theCPB,false);
6315
6316     if (error)
6317       return NULL;
6318
6319     /* For MacOS Classic always add the volume name          */
6320     /* For MacOS X add the volume name preceded by "Volumes" */
6321     /*  when we are not referring to the boot volume         */
6322 #ifdef USE_UNIXFILENAME
6323     if (file.vRefNum != dfltVol_vRefNum)
6324 #endif
6325     {
6326         /* Add the volume name */
6327         STRCPY(temporaryPtr, filenamePtr);
6328         vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6329         STRCAT(filenamePtr, ":");
6330         STRCAT(filenamePtr, temporaryPtr);
6331
6332 #ifdef USE_UNIXFILENAME
6333         STRCPY(temporaryPtr, filenamePtr);
6334         filenamePtr[0] = 0; /* NULL terminate the string */
6335         STRCAT(filenamePtr, "Volumes:");
6336         STRCAT(filenamePtr, temporaryPtr);
6337 #endif
6338     }
6339
6340     /* Append final path separator if it's a folder */
6341     if (folder)
6342         STRCAT(fname, ":");
6343
6344     /* As we use Unix File Name for MacOS X convert it */
6345 #ifdef USE_UNIXFILENAME
6346     /* Need to insert leading / */
6347     /* TODO: get the above code to use directly the / */
6348     STRCPY(&temporaryPtr[1], filenamePtr);
6349     temporaryPtr[0] = '/';
6350     STRCPY(filenamePtr, temporaryPtr);
6351     {
6352     char        *p;
6353     for (p = fname; *p; p++)
6354         if (*p == ':')
6355             *p = '/';
6356     }
6357 #endif
6358
6359     return (vim_strsave(fname));
6360 #endif
6361 }
6362
6363 #if (defined(USE_IM_CONTROL) || defined(PROTO)) && defined(USE_CARBONKEYHANDLER)
6364 /*
6365  * Input Method Control functions.
6366  */
6367
6368 /*
6369  * Notify cursor position to IM.
6370  */
6371     void
6372 im_set_position(int row, int col)
6373 {
6374 #if 0
6375     /* TODO: Implement me! */
6376     im_start_row = row;
6377     im_start_col = col;
6378 #endif
6379 }
6380
6381 static ScriptLanguageRecord gTSLWindow;
6382 static ScriptLanguageRecord gTSLInsert;
6383 static ScriptLanguageRecord gTSLDefault = { 0, 0 };
6384
6385 static Component             gTSCWindow;
6386 static Component             gTSCInsert;
6387 static Component             gTSCDefault;
6388
6389 static int                   im_initialized = 0;
6390
6391     static void
6392 im_on_window_switch(int active)
6393 {
6394     ScriptLanguageRecord *slptr = NULL;
6395     OSStatus err;
6396
6397     if (! gui.in_use)
6398         return;
6399
6400     if (im_initialized == 0)
6401     {
6402         im_initialized = 1;
6403
6404         /* save default TSM component (should be U.S.) to default */
6405         GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6406                                      kKeyboardInputMethodClass);
6407     }
6408
6409     if (active == TRUE)
6410     {
6411         im_is_active = TRUE;
6412         ActivateTSMDocument(gTSMDocument);
6413         slptr = &gTSLWindow;
6414
6415         if (slptr)
6416         {
6417             err = SetDefaultInputMethodOfClass(gTSCWindow, slptr,
6418                                                kKeyboardInputMethodClass);
6419             if (err == noErr)
6420                 err = SetTextServiceLanguage(slptr);
6421
6422             if (err == noErr)
6423                 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6424         }
6425     }
6426     else
6427     {
6428         err = GetTextServiceLanguage(&gTSLWindow);
6429         if (err == noErr)
6430             slptr = &gTSLWindow;
6431
6432         if (slptr)
6433             GetDefaultInputMethodOfClass(&gTSCWindow, slptr,
6434                                          kKeyboardInputMethodClass);
6435
6436         im_is_active = FALSE;
6437         DeactivateTSMDocument(gTSMDocument);
6438     }
6439 }
6440
6441 /*
6442  * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6443  */
6444     void
6445 im_set_active(int active)
6446 {
6447     ScriptLanguageRecord *slptr = NULL;
6448     OSStatus err;
6449
6450     if (! gui.in_use)
6451         return;
6452
6453     if (im_initialized == 0)
6454     {
6455         im_initialized = 1;
6456
6457         /* save default TSM component (should be U.S.) to default */
6458         GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6459                                      kKeyboardInputMethodClass);
6460     }
6461
6462     if (active == TRUE)
6463     {
6464         im_is_active = TRUE;
6465         ActivateTSMDocument(gTSMDocument);
6466         slptr = &gTSLInsert;
6467
6468         if (slptr)
6469         {
6470             err = SetDefaultInputMethodOfClass(gTSCInsert, slptr,
6471                                                kKeyboardInputMethodClass);
6472             if (err == noErr)
6473                 err = SetTextServiceLanguage(slptr);
6474
6475             if (err == noErr)
6476                 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6477         }
6478     }
6479     else
6480     {
6481         err = GetTextServiceLanguage(&gTSLInsert);
6482         if (err == noErr)
6483             slptr = &gTSLInsert;
6484
6485         if (slptr)
6486             GetDefaultInputMethodOfClass(&gTSCInsert, slptr,
6487                                          kKeyboardInputMethodClass);
6488
6489         /* restore to default when switch to normal mode, so than we could
6490          * enter commands easier */
6491         SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault,
6492                                      kKeyboardInputMethodClass);
6493         SetTextServiceLanguage(&gTSLDefault);
6494
6495         im_is_active = FALSE;
6496         DeactivateTSMDocument(gTSMDocument);
6497     }
6498 }
6499
6500 /*
6501  * Get IM status.  When IM is on, return not 0.  Else return 0.
6502  */
6503     int
6504 im_get_status(void)
6505 {
6506     if (! gui.in_use)
6507         return 0;
6508
6509     return im_is_active;
6510 }
6511
6512 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6513
6514
6515
6516
6517 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6518 // drawer implementation
6519 static MenuRef contextMenu = NULL;
6520 enum
6521 {
6522     kTabContextMenuId = 42,
6523 };
6524
6525 // the caller has to CFRelease() the returned string
6526     static CFStringRef
6527 getTabLabel(tabpage_T *page)
6528 {
6529     get_tabline_label(page, FALSE);
6530 #ifdef MACOS_CONVERT
6531     return (CFStringRef)mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6532 #else
6533     // TODO: check internal encoding?
6534     return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6535                                                    kCFStringEncodingMacRoman);
6536 #endif
6537 }
6538
6539
6540 #define DRAWER_SIZE 150
6541 #define DRAWER_INSET 16
6542
6543 static ControlRef dataBrowser = NULL;
6544
6545 // when the tabline is hidden, vim doesn't call update_tabline(). When
6546 // the tabline is shown again, show_tabline() is called before update_tabline(),
6547 // and because of this, the tab labels and vims internal tabs are out of sync
6548 // for a very short time. to prevent inconsistent state, we store the labels
6549 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6550 static CFStringRef *tabLabels = NULL;
6551 static int tabLabelsSize = 0;
6552
6553 enum
6554 {
6555     kTabsColumn = 'Tabs'
6556 };
6557
6558     static int
6559 getTabCount(void)
6560 {
6561     tabpage_T   *tp;
6562     int         numTabs = 0;
6563
6564     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6565         ++numTabs;
6566     return numTabs;
6567 }
6568
6569 // data browser item display callback
6570     static OSStatus
6571 dbItemDataCallback(ControlRef browser,
6572         DataBrowserItemID itemID,
6573         DataBrowserPropertyID property /* column id */,
6574         DataBrowserItemDataRef itemData,
6575         Boolean changeValue)
6576 {
6577     OSStatus status = noErr;
6578
6579     // assert(property == kTabsColumn); // why is this violated??
6580
6581     // changeValue is true if we have a modifieable list and data was changed.
6582     // In our case, it's always false.
6583     // (that is: if (changeValue) updateInternalData(); else return
6584     // internalData();
6585     if (!changeValue)
6586     {
6587         CFStringRef str;
6588
6589         assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6590         str = tabLabels[itemID - 1];
6591         status = SetDataBrowserItemDataText(itemData, str);
6592     }
6593     else
6594         status = errDataBrowserPropertyNotSupported;
6595
6596     return status;
6597 }
6598
6599 // data browser action callback
6600     static void
6601 dbItemNotificationCallback(ControlRef browser,
6602         DataBrowserItemID item,
6603         DataBrowserItemNotification message)
6604 {
6605     switch (message)
6606     {
6607         case kDataBrowserItemSelected:
6608             send_tabline_event(item);
6609             break;
6610     }
6611 }
6612
6613 // callbacks needed for contextual menu:
6614     static void
6615 dbGetContextualMenuCallback(ControlRef browser,
6616         MenuRef *menu,
6617         UInt32 *helpType,
6618         CFStringRef *helpItemString,
6619         AEDesc *selection)
6620 {
6621     // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6622     *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6623     *helpItemString = NULL;
6624
6625     *menu = contextMenu;
6626 }
6627
6628     static void
6629 dbSelectContextualMenuCallback(ControlRef browser,
6630         MenuRef menu,
6631         UInt32 selectionType,
6632         SInt16 menuID,
6633         MenuItemIndex menuItem)
6634 {
6635     if (selectionType == kCMMenuItemSelected)
6636     {
6637         MenuCommand command;
6638         GetMenuItemCommandID(menu, menuItem, &command);
6639
6640         // get tab that was selected when the context menu appeared
6641         // (there is always one tab selected). TODO: check if the context menu
6642         // isn't opened on an item but on empty space (has to be possible some
6643         // way, the finder does it too ;-) )
6644         Handle items = NewHandle(0);
6645         if (items != NULL)
6646         {
6647             int numItems;
6648
6649             GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6650                                            kDataBrowserItemIsSelected, items);
6651             numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6652             if (numItems > 0)
6653             {
6654                 int idx;
6655                 DataBrowserItemID *itemsPtr;
6656
6657                 HLock(items);
6658                 itemsPtr = (DataBrowserItemID *)*items;
6659                 idx = itemsPtr[0];
6660                 HUnlock(items);
6661                 send_tabline_menu_event(idx, command);
6662             }
6663             DisposeHandle(items);
6664         }
6665     }
6666 }
6667
6668 // focus callback of the data browser to always leave focus in vim
6669     static OSStatus
6670 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6671 {
6672     assert(GetEventClass(event) == kEventClassControl
6673             && GetEventKind(event) == kEventControlSetFocusPart);
6674
6675     return paramErr;
6676 }
6677
6678
6679 // drawer callback to resize data browser to drawer size
6680     static OSStatus
6681 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6682 {
6683     switch (GetEventKind(event))
6684     {
6685         case kEventWindowBoundsChanged: // move or resize
6686             {
6687                 UInt32 attribs;
6688                 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6689                                        NULL, sizeof(attribs), NULL, &attribs);
6690                 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6691                 {
6692                     Rect r;
6693                     GetWindowBounds(drawer, kWindowContentRgn, &r);
6694                     SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6695                     SetControlBounds(dataBrowser, &r);
6696                     SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6697                                                         kTabsColumn, r.right);
6698                 }
6699             }
6700             break;
6701     }
6702
6703     return eventNotHandledErr;
6704 }
6705
6706 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6707 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6708 // blue highlights in the list view on these systems, though. Oh well.)
6709
6710
6711 #import <mach-o/dyld.h>
6712
6713 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6714
6715     static OSStatus
6716 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6717         OptionBits inAttributesToSet,
6718         OptionBits inAttributesToClear)
6719 {
6720     long osVersion;
6721     char *symbolName;
6722     NSSymbol symbol = NULL;
6723     OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6724               OptionBits   inAttributesToSet, OptionBits inAttributesToClear);
6725
6726     Gestalt(gestaltSystemVersion, &osVersion);
6727     if (osVersion < 0x1040) // only supported for 10.4 (and up)
6728         return noErr;
6729
6730     // C name mangling...
6731     symbolName = "_DataBrowserChangeAttributes";
6732     if (!NSIsSymbolNameDefined(symbolName)
6733             || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6734         return noErr;
6735
6736     dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6737     if (dataBrowserChangeAttributes == NULL)
6738         return noErr; // well...
6739     return dataBrowserChangeAttributes(inDataBrowser,
6740                                       inAttributesToSet, inAttributesToClear);
6741 }
6742
6743     static void
6744 initialise_tabline(void)
6745 {
6746     Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6747     DataBrowserCallbacks dbCallbacks;
6748     EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6749     EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6750     DataBrowserListViewColumnDesc colDesc;
6751
6752     // drawers have to have compositing enabled
6753     CreateNewWindow(kDrawerWindowClass,
6754             kWindowStandardHandlerAttribute
6755                     | kWindowCompositingAttribute
6756                     | kWindowResizableAttribute
6757                     | kWindowLiveResizeAttribute,
6758             &drawerRect, &drawer);
6759
6760     SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6761     SetDrawerParent(drawer, gui.VimWindow);
6762     SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6763
6764
6765     // create list view embedded in drawer
6766     CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6767                                                                 &dataBrowser);
6768
6769     dbCallbacks.version = kDataBrowserLatestCallbacks;
6770     InitDataBrowserCallbacks(&dbCallbacks);
6771     dbCallbacks.u.v1.itemDataCallback =
6772                                 NewDataBrowserItemDataUPP(dbItemDataCallback);
6773     dbCallbacks.u.v1.itemNotificationCallback =
6774                 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6775     dbCallbacks.u.v1.getContextualMenuCallback =
6776               NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6777     dbCallbacks.u.v1.selectContextualMenuCallback =
6778         NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6779
6780     SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6781
6782     SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6783     SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6784     SetDataBrowserSelectionFlags(dataBrowser,
6785               kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6786     SetDataBrowserTableViewHiliteStyle(dataBrowser,
6787                                              kDataBrowserTableViewFillHilite);
6788     Boolean b = false;
6789     SetControlData(dataBrowser, kControlEntireControl,
6790                   kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6791
6792     // enable blue background in data browser (this is only in 10.4 and vim
6793     // has to support older osx versions as well, so we have to load this
6794     // function dynamically)
6795     myDataBrowserChangeAttributes(dataBrowser,
6796                       kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6797
6798     // install callback that keeps focus in vim and away from the data browser
6799     InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6800                                                                   NULL, NULL);
6801
6802     // install callback that keeps data browser at the size of the drawer
6803     InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6804                                                                   NULL, NULL);
6805
6806     // add "tabs" column to data browser
6807     colDesc.propertyDesc.propertyID = kTabsColumn;
6808     colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6809
6810     // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6811     colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6812
6813     colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6814     colDesc.headerBtnDesc.minimumWidth = 100;
6815     colDesc.headerBtnDesc.maximumWidth = 150;
6816     colDesc.headerBtnDesc.titleOffset = 0;
6817     colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6818     colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6819     colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6820     colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6821
6822     AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6823
6824     // create tabline popup menu required by vim docs (see :he tabline-menu)
6825     CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6826     AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6827                                                     TABLINE_MENU_CLOSE, NULL);
6828     AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6829                                                       TABLINE_MENU_NEW, NULL);
6830     AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6831                                                      TABLINE_MENU_OPEN, NULL);
6832 }
6833
6834
6835 /*
6836  * Show or hide the tabline.
6837  */
6838     void
6839 gui_mch_show_tabline(int showit)
6840 {
6841     if (showit == 0)
6842         CloseDrawer(drawer, true);
6843     else
6844         OpenDrawer(drawer, kWindowEdgeRight, true);
6845 }
6846
6847 /*
6848  * Return TRUE when tabline is displayed.
6849  */
6850     int
6851 gui_mch_showing_tabline(void)
6852 {
6853     WindowDrawerState state = GetDrawerState(drawer);
6854
6855     return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6856 }
6857
6858 /*
6859  * Update the labels of the tabline.
6860  */
6861     void
6862 gui_mch_update_tabline(void)
6863 {
6864     tabpage_T   *tp;
6865     int         numTabs = getTabCount();
6866     int         nr = 1;
6867     int         curtabidx = 1;
6868
6869     // adjust data browser
6870     if (tabLabels != NULL)
6871     {
6872         int i;
6873
6874         for (i = 0; i < tabLabelsSize; ++i)
6875             CFRelease(tabLabels[i]);
6876         free(tabLabels);
6877     }
6878     tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6879     tabLabelsSize = numTabs;
6880
6881     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6882     {
6883         if (tp == curtab)
6884             curtabidx = nr;
6885         tabLabels[nr-1] = getTabLabel(tp);
6886     }
6887
6888     RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6889                                                   kDataBrowserItemNoProperty);
6890     // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6891     // can pass NULL for the id array
6892     AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6893                                                   kDataBrowserItemNoProperty);
6894
6895     DataBrowserItemID item = curtabidx;
6896     SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6897 }
6898
6899 /*
6900  * Set the current tab to "nr".  First tab is 1.
6901  */
6902     void
6903 gui_mch_set_curtab(nr)
6904     int         nr;
6905 {
6906     DataBrowserItemID item = nr;
6907     SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6908
6909     // TODO: call something like this?: (or restore scroll position, or...)
6910     RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6911                                                       kDataBrowserRevealOnly);
6912 }
6913
6914 #endif // FEAT_GUI_TABLINE