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
57static EventHandlerUPP mouseWheelHandlerUPP = NULL;
58SInt32 gMacSystemVersion;
59
60#ifdef MACOS_CONVERT
61# define USE_CARBONKEYHANDLER
62
63static int im_is_active = FALSE;
64#if 0
65    /* TODO: Implement me! */
66static int im_start_row = 0;
67static int im_start_col = 0;
68#endif
69
70#define NR_ELEMS(x)	(sizeof(x) / sizeof(x[0]))
71
72static TSMDocumentID gTSMDocument;
73
74static void im_on_window_switch(int active);
75static EventHandlerUPP keyEventHandlerUPP = NULL;
76static EventHandlerUPP winEventHandlerUPP = NULL;
77
78static pascal OSStatus gui_mac_handle_window_activate(
79	EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
80
81static pascal OSStatus gui_mac_handle_text_input(
82	EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
83
84static pascal OSStatus gui_mac_update_input_area(
85	EventHandlerCallRef nextHandler, EventRef theEvent);
86
87static 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 */
131static long lastMouseTick = 0;
132
133/* ??? */
134static RgnHandle cursorRgn;
135static RgnHandle dragRgn;
136static Rect dragRect;
137static short dragRectEnbl;
138static 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 */
145static int allow_scrollbar = FALSE;
146
147/* Last mouse click caused contextual menu, (to provide proper release) */
148static short clickIsPopup;
149
150/* Feedback Action for Scrollbar */
151ControlActionUPP gScrollAction;
152ControlActionUPP gScrollDrag;
153
154/* Keeping track of which scrollbar is being dragged */
155static ControlHandle dragged_sb = NULL;
156
157/* Vector of char_u --> control index for hotkeys in dialogs */
158static short *gDialogHotKeys;
159
160static 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
170int	    p_macatsui_last;
171ATSUStyle   gFontStyle;
172# ifdef FEAT_MBYTE
173ATSUStyle   gWideFontStyle;
174# endif
175Boolean	    gIsFontFallbackSet;
176UInt32      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
238static 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
295OSErr HandleUnusedParms(const AppleEvent *theAEvent);
296#endif
297
298#ifdef FEAT_GUI_TABLINE
299static void initialise_tabline(void);
300static WindowRef drawer = NULL; // TODO: put into gui.h
301#endif
302
303#ifdef USE_ATSUI_DRAWING
304static void gui_mac_set_font_attributes(GuiFont font);
305static 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 *
323C2Pascal_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 *
355C2Pascal_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
390EventModifiers2VimMouseModifiers(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
413EventModifiers2VimModifiers(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
439points_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
479menu_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 **
534new_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
608typedef struct WindowSearch WindowSearch;
609struct 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
616Handle_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
681typedef struct ModificationInfo ModificationInfo;
682struct 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
690Handle_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
777typedef struct CW_GetText CW_GetText;
778struct 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
787Handle_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
867FindProcessBySignature(
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
903Send_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
964HandleUnusedParms(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
1001typedef struct SelectionRange SelectionRange;
1002struct 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
1021HandleODocAE(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
1170finished:
1171    AEDisposeDesc(&theList); /* dispose what we allocated */
1172
1173    error = HandleUnusedParms(theAEvent);
1174    return error;
1175}
1176
1177/*
1178 *
1179 */
1180
1181    pascal OSErr
1182Handle_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
1198Handle_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
1220Handle_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
1238Handle_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
1255InstallAEHandlers(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
1363FontPanelHandler(
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
1406InstallFontPanelHandler(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
1430GetFontPanelSelection(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 /* Shoulde we return MenuItemIndex? */
1484gui_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 *
1514gui_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
1560gui_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
1587gui_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
1679gui_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
1771gui_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
1785gui_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
1814gui_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 heigh 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
1865gui_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
1970gui_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
1996gui_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
2020gui_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
2050gui_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
2085OSStatus gui_mac_update_input_area(
2086	EventHandlerCallRef nextHandler,
2087	EventRef	    theEvent)
2088{
2089    return eventNotHandledErr;
2090}
2091
2092static 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
2097gui_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
2246done:
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
2260gui_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
2438gui_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
2491gui_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
2522gui_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
2545gui_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
2592bail:
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
2601gui_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
2614gui_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
2648gui_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
2674gui_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
2751gui_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
2877gui_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
2966gui_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
2978receiveHandler(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
3040gui_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
3215gui_mch_new_colors(void)
3216{
3217    /* TODO:
3218     * This proc is called when Normal is set to a value
3219     * so what msut 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
3227gui_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
3249gui_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
3261gui_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
3292gui_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    return FAIL;
3307}
3308
3309/*
3310 * Set the position of the top left corner of the window to the given
3311 * coordinates.
3312 */
3313    void
3314gui_mch_set_winpos(int x, int y)
3315{
3316    /* TODO:  Should make sure the window is move within range
3317     *	      e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3318     */
3319    MoveWindowStructure(gui.VimWindow, x, y);
3320}
3321
3322    void
3323gui_mch_set_shellsize(
3324    int		width,
3325    int		height,
3326    int		min_width,
3327    int		min_height,
3328    int		base_width,
3329    int		base_height,
3330    int		direction)
3331{
3332    CGrafPtr	VimPort;
3333    Rect	VimBound;
3334
3335    if (gui.which_scrollbars[SBAR_LEFT])
3336    {
3337	VimPort = GetWindowPort(gui.VimWindow);
3338	GetPortBounds(VimPort, &VimBound);
3339	VimBound.left = -gui.scrollbar_width; /* + 1;*/
3340	SetPortBounds(VimPort, &VimBound);
3341    /*	GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3342    }
3343    else
3344    {
3345	VimPort = GetWindowPort(gui.VimWindow);
3346	GetPortBounds(VimPort, &VimBound);
3347	VimBound.left = 0;
3348	SetPortBounds(VimPort, &VimBound);
3349    }
3350
3351    SizeWindow(gui.VimWindow, width, height, TRUE);
3352
3353    gui_resize_shell(width, height);
3354}
3355
3356/*
3357 * Get the screen dimensions.
3358 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3359 * Is there no way to find out how wide the borders really are?
3360 * TODO: Add live update of those value on suspend/resume.
3361 */
3362    void
3363gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3364{
3365    GDHandle	dominantDevice = GetMainDevice();
3366    Rect	screenRect = (**dominantDevice).gdRect;
3367
3368    *screen_w = screenRect.right - 10;
3369    *screen_h = screenRect.bottom - 40;
3370}
3371
3372
3373/*
3374 * Open the Font Panel and wait for the user to select a font and
3375 * close the panel.  Then fill the buffer pointed to by font_name with
3376 * the name and size of the selected font and return the font's handle,
3377 * or NOFONT in case of an error.
3378 */
3379    static GuiFont
3380gui_mac_select_font(char_u *font_name)
3381{
3382    GuiFont		    selected_font = NOFONT;
3383    OSStatus		    status;
3384    FontSelectionQDStyle    curr_font;
3385
3386    /* Initialize the Font Panel with the current font. */
3387    curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3388    curr_font.size = (gui.norm_font >> 16);
3389    /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3390    curr_font.instance.fontStyle = 0;
3391    curr_font.hasColor = false;
3392    curr_font.version = 0; /* version number of the style structure */
3393    status = SetFontInfoForSelection(kFontSelectionQDType,
3394	    /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3395
3396    gFontPanelInfo.family = curr_font.instance.fontFamily;
3397    gFontPanelInfo.style = curr_font.instance.fontStyle;
3398    gFontPanelInfo.size = curr_font.size;
3399
3400    /* Pop up the Font Panel. */
3401    status = FPShowHideFontPanel();
3402    if (status == noErr)
3403    {
3404	/*
3405	 * The Font Panel is modeless.  We really need it to be modal,
3406	 * so we spin in an event loop until the panel is closed.
3407	 */
3408	gFontPanelInfo.isPanelVisible = true;
3409	while (gFontPanelInfo.isPanelVisible)
3410	{
3411	    EventRecord e;
3412	    WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3413	}
3414
3415	GetFontPanelSelection(font_name);
3416	selected_font = gui_mac_find_font(font_name);
3417    }
3418    return selected_font;
3419}
3420
3421#ifdef USE_ATSUI_DRAWING
3422    static void
3423gui_mac_create_atsui_style(void)
3424{
3425    if (p_macatsui && gFontStyle == NULL)
3426    {
3427	if (ATSUCreateStyle(&gFontStyle) != noErr)
3428	    gFontStyle = NULL;
3429    }
3430#ifdef FEAT_MBYTE
3431    if (p_macatsui && gWideFontStyle == NULL)
3432    {
3433	if (ATSUCreateStyle(&gWideFontStyle) != noErr)
3434	    gWideFontStyle = NULL;
3435    }
3436#endif
3437
3438    p_macatsui_last = p_macatsui;
3439}
3440#endif
3441
3442/*
3443 * Initialise vim to use the font with the given name.	Return FAIL if the font
3444 * could not be loaded, OK otherwise.
3445 */
3446    int
3447gui_mch_init_font(char_u *font_name, int fontset)
3448{
3449    /* TODO: Add support for bold italic underline proportional etc... */
3450    Str255	suggestedFont = "\pMonaco";
3451    int		suggestedSize = 10;
3452    FontInfo	font_info;
3453    short	font_id;
3454    GuiFont	font;
3455    char_u	used_font_name[512];
3456
3457#ifdef USE_ATSUI_DRAWING
3458    gui_mac_create_atsui_style();
3459#endif
3460
3461    if (font_name == NULL)
3462    {
3463	/* First try to get the suggested font */
3464	GetFNum(suggestedFont, &font_id);
3465
3466	if (font_id == 0)
3467	{
3468	    /* Then pickup the standard application font */
3469	    font_id = GetAppFont();
3470	    STRCPY(used_font_name, "default");
3471	}
3472	else
3473	    STRCPY(used_font_name, "Monaco");
3474	font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3475    }
3476    else if (STRCMP(font_name, "*") == 0)
3477    {
3478	char_u *new_p_guifont;
3479
3480	font = gui_mac_select_font(used_font_name);
3481	if (font == NOFONT)
3482	    return FAIL;
3483
3484	/* Set guifont to the name of the selected font. */
3485	new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3486	if (new_p_guifont != NULL)
3487	{
3488	    STRCPY(new_p_guifont, used_font_name);
3489	    vim_free(p_guifont);
3490	    p_guifont = new_p_guifont;
3491	    /* Replace spaces in the font name with underscores. */
3492	    for ( ; *new_p_guifont; ++new_p_guifont)
3493	    {
3494		if (*new_p_guifont == ' ')
3495		    *new_p_guifont = '_';
3496	    }
3497	}
3498    }
3499    else
3500    {
3501	font = gui_mac_find_font(font_name);
3502	vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3503
3504	if (font == NOFONT)
3505	    return FAIL;
3506    }
3507
3508    gui.norm_font = font;
3509
3510    hl_set_font_name(used_font_name);
3511
3512    TextSize(font >> 16);
3513    TextFont(font & 0xFFFF);
3514
3515    GetFontInfo(&font_info);
3516
3517    gui.char_ascent = font_info.ascent;
3518    gui.char_width  = CharWidth('_');
3519    gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3520
3521#ifdef USE_ATSUI_DRAWING
3522    if (p_macatsui && gFontStyle)
3523	gui_mac_set_font_attributes(font);
3524#endif
3525
3526    return OK;
3527}
3528
3529/*
3530 * Adjust gui.char_height (after 'linespace' was changed).
3531 */
3532    int
3533gui_mch_adjust_charheight(void)
3534{
3535    FontInfo    font_info;
3536
3537    GetFontInfo(&font_info);
3538    gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3539    gui.char_ascent = font_info.ascent + p_linespace / 2;
3540    return OK;
3541}
3542
3543/*
3544 * Get a font structure for highlighting.
3545 */
3546    GuiFont
3547gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3548{
3549    GuiFont font;
3550
3551    font = gui_mac_find_font(name);
3552
3553    if (font == NOFONT)
3554    {
3555	if (giveErrorIfMissing)
3556	    EMSG2(_(e_font), name);
3557	return NOFONT;
3558    }
3559    /*
3560     * TODO : Accept only monospace
3561     */
3562
3563    return font;
3564}
3565
3566#if defined(FEAT_EVAL) || defined(PROTO)
3567/*
3568 * Return the name of font "font" in allocated memory.
3569 * Don't know how to get the actual name, thus use the provided name.
3570 */
3571    char_u *
3572gui_mch_get_fontname(GuiFont font, char_u *name)
3573{
3574    if (name == NULL)
3575	return NULL;
3576    return vim_strsave(name);
3577}
3578#endif
3579
3580#ifdef USE_ATSUI_DRAWING
3581    static void
3582gui_mac_set_font_attributes(GuiFont font)
3583{
3584    ATSUFontID	fontID;
3585    Fixed	fontSize;
3586    Fixed	fontWidth;
3587
3588    fontID    = font & 0xFFFF;
3589    fontSize  = Long2Fix(font >> 16);
3590    fontWidth = Long2Fix(gui.char_width);
3591
3592    ATSUAttributeTag attribTags[] =
3593    {
3594	kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
3595	kATSUMaxATSUITagValue + 1
3596    };
3597
3598    ByteCount attribSizes[] =
3599    {
3600	sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
3601	sizeof(font)
3602    };
3603
3604    ATSUAttributeValuePtr attribValues[] =
3605    {
3606	&fontID, &fontSize, &fontWidth, &font
3607    };
3608
3609    if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3610    {
3611	if (ATSUSetAttributes(gFontStyle,
3612		    (sizeof attribTags) / sizeof(ATSUAttributeTag),
3613		    attribTags, attribSizes, attribValues) != noErr)
3614	{
3615# ifndef NDEBUG
3616	    fprintf(stderr, "couldn't set font style\n");
3617# endif
3618	    ATSUDisposeStyle(gFontStyle);
3619	    gFontStyle = NULL;
3620	}
3621
3622#ifdef FEAT_MBYTE
3623	if (has_mbyte)
3624	{
3625	    /* FIXME: we should use a more mbyte sensitive way to support
3626	     * wide font drawing */
3627	    fontWidth = Long2Fix(gui.char_width * 2);
3628
3629	    if (ATSUSetAttributes(gWideFontStyle,
3630			(sizeof attribTags) / sizeof(ATSUAttributeTag),
3631			attribTags, attribSizes, attribValues) != noErr)
3632	    {
3633		ATSUDisposeStyle(gWideFontStyle);
3634		gWideFontStyle = NULL;
3635	    }
3636	}
3637#endif
3638    }
3639}
3640#endif
3641
3642/*
3643 * Set the current text font.
3644 */
3645    void
3646gui_mch_set_font(GuiFont font)
3647{
3648#ifdef USE_ATSUI_DRAWING
3649    GuiFont			currFont;
3650    ByteCount			actualFontByteCount;
3651
3652    if (p_macatsui && gFontStyle)
3653    {
3654	/* Avoid setting same font again */
3655	if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1,
3656		    sizeof(font), &currFont, &actualFontByteCount) == noErr
3657		&& actualFontByteCount == (sizeof font))
3658	{
3659	    if (currFont == font)
3660		return;
3661	}
3662
3663	gui_mac_set_font_attributes(font);
3664    }
3665
3666    if (p_macatsui && !gIsFontFallbackSet)
3667    {
3668	/* Setup automatic font substitution. The user's guifontwide
3669	 * is tried first, then the system tries other fonts. */
3670/*
3671	ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3672	ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3673	ATSUCreateFontFallbacks(&gFontFallbacks);
3674	ATSUSetObjFontFallbacks(gFontFallbacks, );
3675*/
3676	if (gui.wide_font)
3677	{
3678	    ATSUFontID fallbackFonts;
3679	    gIsFontFallbackSet = TRUE;
3680
3681	    if (FMGetFontFromFontFamilyInstance(
3682			(gui.wide_font & 0xFFFF),
3683			0,
3684			&fallbackFonts,
3685			NULL) == noErr)
3686	    {
3687		ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID),
3688				     &fallbackFonts,
3689				     kATSUSequentialFallbacksPreferred);
3690	    }
3691/*
3692	ATSUAttributeValuePtr fallbackValues[] = { };
3693*/
3694	}
3695    }
3696#endif
3697    TextSize(font >> 16);
3698    TextFont(font & 0xFFFF);
3699}
3700
3701/*
3702 * If a font is not going to be used, free its structure.
3703 */
3704    void
3705gui_mch_free_font(font)
3706    GuiFont	font;
3707{
3708    /*
3709     * Free font when "font" is not 0.
3710     * Nothing to do in the current implementation, since
3711     * nothing is allocated for each font used.
3712     */
3713}
3714
3715    static int
3716hex_digit(int c)
3717{
3718    if (isdigit(c))
3719	return c - '0';
3720    c = TOLOWER_ASC(c);
3721    if (c >= 'a' && c <= 'f')
3722	return c - 'a' + 10;
3723    return -1000;
3724}
3725
3726/*
3727 * Return the Pixel value (color) for the given color name.  This routine was
3728 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3729 * Programmer's Guide.
3730 * Return INVALCOLOR when failed.
3731 */
3732    guicolor_T
3733gui_mch_get_color(char_u *name)
3734{
3735    /* TODO: Add support for the new named color of MacOS 8
3736     */
3737    RGBColor	MacColor;
3738//    guicolor_T	color = 0;
3739
3740    typedef struct guicolor_tTable
3741    {
3742	char	    *name;
3743	guicolor_T  color;
3744    } guicolor_tTable;
3745
3746    /*
3747     * The comment at the end of each line is the source
3748     * (Mac, Window, Unix) and the number is the unix rgb.txt value
3749     */
3750    static guicolor_tTable table[] =
3751    {
3752	{"Black",	RGB(0x00, 0x00, 0x00)},
3753	{"darkgray",	RGB(0x80, 0x80, 0x80)}, /*W*/
3754	{"darkgrey",	RGB(0x80, 0x80, 0x80)}, /*W*/
3755	{"Gray",	RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3756	{"Grey",	RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3757	{"lightgray",	RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3758	{"lightgrey",	RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3759	{"gray10",	RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3760	{"grey10",	RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3761	{"gray20",	RGB(0x33, 0x33, 0x33)}, /*W*/
3762	{"grey20",	RGB(0x33, 0x33, 0x33)}, /*W*/
3763	{"gray30",	RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3764	{"grey30",	RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3765	{"gray40",	RGB(0x66, 0x66, 0x66)}, /*W*/
3766	{"grey40",	RGB(0x66, 0x66, 0x66)}, /*W*/
3767	{"gray50",	RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3768	{"grey50",	RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3769	{"gray60",	RGB(0x99, 0x99, 0x99)}, /*W*/
3770	{"grey60",	RGB(0x99, 0x99, 0x99)}, /*W*/
3771	{"gray70",	RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3772	{"grey70",	RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3773	{"gray80",	RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3774	{"grey80",	RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3775	{"gray90",	RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3776	{"grey90",	RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3777	{"white",	RGB(0xFF, 0xFF, 0xFF)},
3778	{"darkred",	RGB(0x80, 0x00, 0x00)}, /*W*/
3779	{"red",		RGB(0xDD, 0x08, 0x06)}, /*M*/
3780	{"lightred",	RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3781	{"DarkBlue",	RGB(0x00, 0x00, 0x80)}, /*W*/
3782	{"Blue",	RGB(0x00, 0x00, 0xD4)}, /*M*/
3783	{"lightblue",	RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3784	{"DarkGreen",	RGB(0x00, 0x80, 0x00)}, /*W*/
3785	{"Green",	RGB(0x00, 0x64, 0x11)}, /*M*/
3786	{"lightgreen",	RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3787	{"DarkCyan",	RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3788	{"cyan",	RGB(0x02, 0xAB, 0xEA)}, /*M*/
3789	{"lightcyan",	RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3790	{"darkmagenta",	RGB(0x80, 0x00, 0x80)}, /*W*/
3791	{"magenta",	RGB(0xF2, 0x08, 0x84)}, /*M*/
3792	{"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3793	{"brown",	RGB(0x80, 0x40, 0x40)}, /*W*/
3794	{"yellow",	RGB(0xFC, 0xF3, 0x05)}, /*M*/
3795	{"lightyellow",	RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3796	{"darkyellow",	RGB(0xBB, 0xBB, 0x00)}, /*U*/
3797	{"SeaGreen",	RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3798	{"orange",	RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3799	{"Purple",	RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3800	{"SlateBlue",	RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3801	{"Violet",	RGB(0x8D, 0x38, 0xC9)}, /*U*/
3802    };
3803
3804    int		r, g, b;
3805    int		i;
3806
3807    if (name[0] == '#' && strlen((char *) name) == 7)
3808    {
3809	/* Name is in "#rrggbb" format */
3810	r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3811	g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3812	b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3813	if (r < 0 || g < 0 || b < 0)
3814	    return INVALCOLOR;
3815	return RGB(r, g, b);
3816    }
3817    else
3818    {
3819	if (STRICMP(name, "hilite") == 0)
3820	{
3821	    LMGetHiliteRGB(&MacColor);
3822	    return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3823	}
3824	/* Check if the name is one of the colors we know */
3825	for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3826	    if (STRICMP(name, table[i].name) == 0)
3827		return table[i].color;
3828    }
3829
3830    /*
3831     * Last attempt. Look in the file "$VIM/rgb.txt".
3832     */
3833    {
3834#define LINE_LEN 100
3835	FILE	*fd;
3836	char	line[LINE_LEN];
3837	char_u	*fname;
3838
3839	fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3840	if (fname == NULL)
3841	    return INVALCOLOR;
3842
3843	fd = fopen((char *)fname, "rt");
3844	vim_free(fname);
3845	if (fd == NULL)
3846	    return INVALCOLOR;
3847
3848	while (!feof(fd))
3849	{
3850	    int		len;
3851	    int		pos;
3852	    char	*color;
3853
3854	    fgets(line, LINE_LEN, fd);
3855	    len = strlen(line);
3856
3857	    if (len <= 1 || line[len-1] != '\n')
3858		continue;
3859
3860	    line[len-1] = '\0';
3861
3862	    i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3863	    if (i != 3)
3864		continue;
3865
3866	    color = line + pos;
3867
3868	    if (STRICMP(color, name) == 0)
3869	    {
3870		fclose(fd);
3871		return (guicolor_T) RGB(r, g, b);
3872	    }
3873	}
3874	fclose(fd);
3875    }
3876
3877    return INVALCOLOR;
3878}
3879
3880/*
3881 * Set the current text foreground color.
3882 */
3883    void
3884gui_mch_set_fg_color(guicolor_T color)
3885{
3886    RGBColor TheColor;
3887
3888    TheColor.red = Red(color) * 0x0101;
3889    TheColor.green = Green(color) * 0x0101;
3890    TheColor.blue = Blue(color) * 0x0101;
3891
3892    RGBForeColor(&TheColor);
3893}
3894
3895/*
3896 * Set the current text background color.
3897 */
3898    void
3899gui_mch_set_bg_color(guicolor_T color)
3900{
3901    RGBColor TheColor;
3902
3903    TheColor.red = Red(color) * 0x0101;
3904    TheColor.green = Green(color) * 0x0101;
3905    TheColor.blue = Blue(color) * 0x0101;
3906
3907    RGBBackColor(&TheColor);
3908}
3909
3910RGBColor specialColor;
3911
3912/*
3913 * Set the current text special color.
3914 */
3915    void
3916gui_mch_set_sp_color(guicolor_T color)
3917{
3918    specialColor.red = Red(color) * 0x0101;
3919    specialColor.green = Green(color) * 0x0101;
3920    specialColor.blue = Blue(color) * 0x0101;
3921}
3922
3923/*
3924 * Draw undercurl at the bottom of the character cell.
3925 */
3926    static void
3927draw_undercurl(int flags, int row, int col, int cells)
3928{
3929    int			x;
3930    int			offset;
3931    const static int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3932    int			y = FILL_Y(row + 1) - 1;
3933
3934    RGBForeColor(&specialColor);
3935
3936    offset = val[FILL_X(col) % 8];
3937    MoveTo(FILL_X(col), y - offset);
3938
3939    for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3940    {
3941	offset = val[x % 8];
3942	LineTo(x, y - offset);
3943    }
3944}
3945
3946
3947    static void
3948draw_string_QD(int row, int col, char_u *s, int len, int flags)
3949{
3950#ifdef FEAT_MBYTE
3951    char_u	*tofree = NULL;
3952
3953    if (output_conv.vc_type != CONV_NONE)
3954    {
3955	tofree = string_convert(&output_conv, s, &len);
3956	if (tofree != NULL)
3957	    s = tofree;
3958    }
3959#endif
3960
3961    /*
3962     * On OS X, try using Quartz-style text antialiasing.
3963     */
3964    if (gMacSystemVersion >= 0x1020)
3965    {
3966	/* Quartz antialiasing is available only in OS 10.2 and later. */
3967	UInt32 qd_flags = (p_antialias ?
3968			     kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3969	QDSwapTextFlags(qd_flags);
3970    }
3971
3972    /*
3973     * When antialiasing we're using srcOr mode, we have to clear the block
3974     * before drawing the text.
3975     * Also needed when 'linespace' is non-zero to remove the cursor and
3976     * underlining.
3977     * But not when drawing transparently.
3978     * The following is like calling gui_mch_clear_block(row, col, row, col +
3979     * len - 1), but without setting the bg color to gui.back_pixel.
3980     */
3981    if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3982	    && !(flags & DRAW_TRANSP))
3983    {
3984	Rect rc;
3985
3986	rc.left = FILL_X(col);
3987	rc.top = FILL_Y(row);
3988#ifdef FEAT_MBYTE
3989	/* Multibyte computation taken from gui_w32.c */
3990	if (has_mbyte)
3991	{
3992	    /* Compute the length in display cells. */
3993	    rc.right = FILL_X(col + mb_string2cells(s, len));
3994	}
3995	else
3996#endif
3997	rc.right = FILL_X(col + len) + (col + len == Columns);
3998	rc.bottom = FILL_Y(row + 1);
3999	EraseRect(&rc);
4000    }
4001
4002    if (gMacSystemVersion >= 0x1020 && p_antialias)
4003    {
4004	StyleParameter face;
4005
4006	face = normal;
4007	if (flags & DRAW_BOLD)
4008	    face |= bold;
4009	if (flags & DRAW_UNDERL)
4010	    face |= underline;
4011	TextFace(face);
4012
4013	/* Quartz antialiasing works only in srcOr transfer mode. */
4014	TextMode(srcOr);
4015
4016	MoveTo(TEXT_X(col), TEXT_Y(row));
4017	DrawText((char*)s, 0, len);
4018    }
4019    else
4020    {
4021	/* Use old-style, non-antialiased QuickDraw text rendering. */
4022	TextMode(srcCopy);
4023	TextFace(normal);
4024
4025    /*  SelectFont(hdc, gui.currFont); */
4026
4027	if (flags & DRAW_TRANSP)
4028	{
4029	    TextMode(srcOr);
4030	}
4031
4032	MoveTo(TEXT_X(col), TEXT_Y(row));
4033	DrawText((char *)s, 0, len);
4034
4035	if (flags & DRAW_BOLD)
4036	{
4037	    TextMode(srcOr);
4038	    MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
4039	    DrawText((char *)s, 0, len);
4040	}
4041
4042	if (flags & DRAW_UNDERL)
4043	{
4044	    MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
4045	    LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
4046	}
4047    }
4048
4049    if (flags & DRAW_UNDERC)
4050	draw_undercurl(flags, row, col, len);
4051
4052#ifdef FEAT_MBYTE
4053    vim_free(tofree);
4054#endif
4055}
4056
4057#ifdef USE_ATSUI_DRAWING
4058
4059    static void
4060draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
4061{
4062    /* ATSUI requires utf-16 strings */
4063    UniCharCount utf16_len;
4064    UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
4065    utf16_len /= sizeof(UniChar);
4066
4067    /* - ATSUI automatically antialiases text (Someone)
4068     * - for some reason it does not work... (Jussi) */
4069#ifdef MAC_ATSUI_DEBUG
4070    fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n",
4071	    row, col, len, len == 1 ? s[0] : ' ');
4072#endif
4073    /*
4074     * When antialiasing we're using srcOr mode, we have to clear the block
4075     * before drawing the text.
4076     * Also needed when 'linespace' is non-zero to remove the cursor and
4077     * underlining.
4078     * But not when drawing transparently.
4079     * The following is like calling gui_mch_clear_block(row, col, row, col +
4080     * len - 1), but without setting the bg color to gui.back_pixel.
4081     */
4082    if ((flags & DRAW_TRANSP) == 0)
4083    {
4084	Rect rc;
4085
4086	rc.left = FILL_X(col);
4087	rc.top = FILL_Y(row);
4088	/* Multibyte computation taken from gui_w32.c */
4089	if (has_mbyte)
4090	{
4091	    /* Compute the length in display cells. */
4092	    rc.right = FILL_X(col + mb_string2cells(s, len));
4093	}
4094	else
4095	    rc.right = FILL_X(col + len) + (col + len == Columns);
4096
4097	rc.bottom = FILL_Y(row + 1);
4098	EraseRect(&rc);
4099    }
4100
4101    {
4102	TextMode(srcCopy);
4103	TextFace(normal);
4104
4105	/*  SelectFont(hdc, gui.currFont); */
4106	if (flags & DRAW_TRANSP)
4107	{
4108	    TextMode(srcOr);
4109	}
4110
4111	MoveTo(TEXT_X(col), TEXT_Y(row));
4112
4113	if (gFontStyle && flags & DRAW_BOLD)
4114	{
4115	    Boolean attValue = true;
4116	    ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4117	    ByteCount attribSizes[] = { sizeof(Boolean) };
4118	    ATSUAttributeValuePtr attribValues[] = { &attValue };
4119
4120	    ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues);
4121	}
4122
4123	UInt32 useAntialias = p_antialias ? kATSStyleApplyAntiAliasing
4124					  : kATSStyleNoAntiAliasing;
4125	if (useAntialias != useAntialias_cached)
4126	{
4127	    ATSUAttributeTag attribTags[] = { kATSUStyleRenderingOptionsTag };
4128	    ByteCount attribSizes[] = { sizeof(UInt32) };
4129	    ATSUAttributeValuePtr attribValues[] = { &useAntialias };
4130
4131	    if (gFontStyle)
4132		ATSUSetAttributes(gFontStyle, 1, attribTags,
4133						   attribSizes, attribValues);
4134	    if (gWideFontStyle)
4135		ATSUSetAttributes(gWideFontStyle, 1, attribTags,
4136						   attribSizes, attribValues);
4137
4138	    useAntialias_cached = useAntialias;
4139	}
4140
4141#ifdef FEAT_MBYTE
4142	if (has_mbyte)
4143	{
4144	    int n, width_in_cell, last_width_in_cell;
4145	    UniCharArrayOffset offset = 0;
4146	    UniCharCount yet_to_draw = 0;
4147	    ATSUTextLayout textLayout;
4148	    ATSUStyle      textStyle;
4149
4150	    last_width_in_cell = 1;
4151	    ATSUCreateTextLayout(&textLayout);
4152	    ATSUSetTextPointerLocation(textLayout, tofree,
4153				       kATSUFromTextBeginning,
4154				       kATSUToTextEnd, utf16_len);
4155	    /*
4156	       ATSUSetRunStyle(textLayout, gFontStyle,
4157	       kATSUFromTextBeginning, kATSUToTextEnd); */
4158
4159	    /* Compute the length in display cells. */
4160	    for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4161	    {
4162		width_in_cell = (*mb_ptr2cells)(s + n);
4163
4164		/* probably we are switching from single byte character
4165		 * to multibyte characters (which requires more than one
4166		 * cell to draw) */
4167		if (width_in_cell != last_width_in_cell)
4168		{
4169#ifdef MAC_ATSUI_DEBUG
4170		    fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4171			    n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4172#endif
4173		    textStyle = last_width_in_cell > 1 ? gWideFontStyle
4174								 : gFontStyle;
4175
4176		    ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw);
4177		    offset += yet_to_draw;
4178		    yet_to_draw = 0;
4179		    last_width_in_cell = width_in_cell;
4180		}
4181
4182		yet_to_draw++;
4183	    }
4184
4185	    if (yet_to_draw)
4186	    {
4187#ifdef MAC_ATSUI_DEBUG
4188		fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4189			n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4190#endif
4191		/* finish the rest style */
4192		textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle;
4193		ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd);
4194	    }
4195
4196	    ATSUSetTransientFontMatching(textLayout, TRUE);
4197	    ATSUDrawText(textLayout,
4198			 kATSUFromTextBeginning, kATSUToTextEnd,
4199			 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4200	    ATSUDisposeTextLayout(textLayout);
4201	}
4202	else
4203#endif
4204	{
4205	    ATSUTextLayout textLayout;
4206
4207	    if (ATSUCreateTextLayoutWithTextPtr(tofree,
4208			kATSUFromTextBeginning, kATSUToTextEnd,
4209			utf16_len,
4210			(gFontStyle ? 1 : 0), &utf16_len,
4211			(gFontStyle ? &gFontStyle : NULL),
4212			&textLayout) == noErr)
4213	    {
4214		ATSUSetTransientFontMatching(textLayout, TRUE);
4215
4216		ATSUDrawText(textLayout,
4217			kATSUFromTextBeginning, kATSUToTextEnd,
4218			kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4219
4220		ATSUDisposeTextLayout(textLayout);
4221	    }
4222	}
4223
4224	/* drawing is done, now reset bold to normal */
4225	if (gFontStyle && flags & DRAW_BOLD)
4226	{
4227	    Boolean attValue = false;
4228
4229	    ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4230	    ByteCount attribSizes[] = { sizeof(Boolean) };
4231	    ATSUAttributeValuePtr attribValues[] = { &attValue };
4232
4233	    ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes,
4234								attribValues);
4235	}
4236    }
4237
4238    if (flags & DRAW_UNDERC)
4239	draw_undercurl(flags, row, col, len);
4240
4241    vim_free(tofree);
4242}
4243#endif
4244
4245    void
4246gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4247{
4248#if defined(USE_ATSUI_DRAWING)
4249    if (p_macatsui == 0 && p_macatsui_last != 0)
4250	/* switch from macatsui to nomacatsui */
4251	gui_mac_dispose_atsui_style();
4252    else if (p_macatsui != 0 && p_macatsui_last == 0)
4253	/* switch from nomacatsui to macatsui */
4254	gui_mac_create_atsui_style();
4255
4256    if (p_macatsui)
4257	draw_string_ATSUI(row, col, s, len, flags);
4258    else
4259#endif
4260	draw_string_QD(row, col, s, len, flags);
4261}
4262
4263/*
4264 * Return OK if the key with the termcap name "name" is supported.
4265 */
4266    int
4267gui_mch_haskey(char_u *name)
4268{
4269    int i;
4270
4271    for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4272	if (name[0] == special_keys[i].vim_code0 &&
4273					 name[1] == special_keys[i].vim_code1)
4274	    return OK;
4275    return FAIL;
4276}
4277
4278    void
4279gui_mch_beep(void)
4280{
4281    SysBeep(1); /* Should this be 0? (????) */
4282}
4283
4284    void
4285gui_mch_flash(int msec)
4286{
4287    /* Do a visual beep by reversing the foreground and background colors */
4288    Rect    rc;
4289
4290    /*
4291     * Note: InvertRect() excludes right and bottom of rectangle.
4292     */
4293    rc.left = 0;
4294    rc.top = 0;
4295    rc.right = gui.num_cols * gui.char_width;
4296    rc.bottom = gui.num_rows * gui.char_height;
4297    InvertRect(&rc);
4298
4299    ui_delay((long)msec, TRUE);		/* wait for some msec */
4300
4301    InvertRect(&rc);
4302}
4303
4304/*
4305 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4306 */
4307    void
4308gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4309{
4310    Rect	rc;
4311
4312    /*
4313     * Note: InvertRect() excludes right and bottom of rectangle.
4314     */
4315    rc.left = FILL_X(c);
4316    rc.top = FILL_Y(r);
4317    rc.right = rc.left + nc * gui.char_width;
4318    rc.bottom = rc.top + nr * gui.char_height;
4319    InvertRect(&rc);
4320}
4321
4322/*
4323 * Iconify the GUI window.
4324 */
4325    void
4326gui_mch_iconify(void)
4327{
4328    /* TODO: find out what could replace iconify
4329     *	     -window shade?
4330     *	     -hide application?
4331     */
4332}
4333
4334#if defined(FEAT_EVAL) || defined(PROTO)
4335/*
4336 * Bring the Vim window to the foreground.
4337 */
4338    void
4339gui_mch_set_foreground(void)
4340{
4341    /* TODO */
4342}
4343#endif
4344
4345/*
4346 * Draw a cursor without focus.
4347 */
4348    void
4349gui_mch_draw_hollow_cursor(guicolor_T color)
4350{
4351    Rect rc;
4352
4353    /*
4354     * Note: FrameRect() excludes right and bottom of rectangle.
4355     */
4356    rc.left = FILL_X(gui.col);
4357    rc.top = FILL_Y(gui.row);
4358    rc.right = rc.left + gui.char_width;
4359#ifdef FEAT_MBYTE
4360    if (mb_lefthalve(gui.row, gui.col))
4361	rc.right += gui.char_width;
4362#endif
4363    rc.bottom = rc.top + gui.char_height;
4364
4365    gui_mch_set_fg_color(color);
4366
4367    FrameRect(&rc);
4368}
4369
4370/*
4371 * Draw part of a cursor, only w pixels wide, and h pixels high.
4372 */
4373    void
4374gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4375{
4376    Rect rc;
4377
4378#ifdef FEAT_RIGHTLEFT
4379    /* vertical line should be on the right of current point */
4380    if (CURSOR_BAR_RIGHT)
4381	rc.left = FILL_X(gui.col + 1) - w;
4382    else
4383#endif
4384	rc.left = FILL_X(gui.col);
4385    rc.top = FILL_Y(gui.row) + gui.char_height - h;
4386    rc.right = rc.left + w;
4387    rc.bottom = rc.top + h;
4388
4389    gui_mch_set_fg_color(color);
4390
4391    FrameRect(&rc);
4392//    PaintRect(&rc);
4393}
4394
4395
4396
4397/*
4398 * Catch up with any queued X events.  This may put keyboard input into the
4399 * input buffer, call resize call-backs, trigger timers etc.  If there is
4400 * nothing in the X event queue (& no timers pending), then we return
4401 * immediately.
4402 */
4403    void
4404gui_mch_update(void)
4405{
4406    /* TODO: find what to do
4407     *	     maybe call gui_mch_wait_for_chars (0)
4408     *	     more like look at EventQueue then
4409     *	     call heart of gui_mch_wait_for_chars;
4410     *
4411     *	if (eventther)
4412     *	    gui_mac_handle_event(&event);
4413     */
4414    EventRecord theEvent;
4415
4416    if (EventAvail(everyEvent, &theEvent))
4417	if (theEvent.what != nullEvent)
4418	    gui_mch_wait_for_chars(0);
4419}
4420
4421/*
4422 * Simple wrapper to neglect more easily the time
4423 * spent inside WaitNextEvent while profiling.
4424 */
4425
4426    pascal
4427    Boolean
4428WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4429{
4430    if (((long) sleep) < -1)
4431	sleep = 32767;
4432    return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4433}
4434
4435/*
4436 * GUI input routine called by gui_wait_for_chars().  Waits for a character
4437 * from the keyboard.
4438 *  wtime == -1	    Wait forever.
4439 *  wtime == 0	    This should never happen.
4440 *  wtime > 0	    Wait wtime milliseconds for a character.
4441 * Returns OK if a character was found to be available within the given time,
4442 * or FAIL otherwise.
4443 */
4444    int
4445gui_mch_wait_for_chars(int wtime)
4446{
4447    EventMask	mask  = (everyEvent);
4448    EventRecord event;
4449    long	entryTick;
4450    long	currentTick;
4451    long	sleeppyTick;
4452
4453    /* If we are providing life feedback with the scrollbar,
4454     * we don't want to try to wait for an event, or else
4455     * there won't be any life feedback.
4456     */
4457    if (dragged_sb != NULL)
4458	return FAIL;
4459	/* TODO: Check if FAIL is the proper return code */
4460
4461    entryTick = TickCount();
4462
4463    allow_scrollbar = TRUE;
4464
4465    do
4466    {
4467/*	if (dragRectControl == kCreateEmpty)
4468	{
4469	    dragRgn = NULL;
4470	    dragRectControl = kNothing;
4471	}
4472	else*/ if (dragRectControl == kCreateRect)
4473	{
4474	    dragRgn = cursorRgn;
4475	    RectRgn(dragRgn, &dragRect);
4476	    dragRectControl = kNothing;
4477	}
4478	/*
4479	 * Don't use gui_mch_update() because then we will spin-lock until a
4480	 * char arrives, instead we use WaitNextEventWrp() to hang until an
4481	 * event arrives.  No need to check for input_buf_full because we are
4482	 * returning as soon as it contains a single char.
4483	 */
4484	/* TODO: reduce wtime accordinly???  */
4485	if (wtime > -1)
4486	    sleeppyTick = 60 * wtime / 1000;
4487	else
4488	    sleeppyTick = 32767;
4489
4490	if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4491	{
4492	    gui_mac_handle_event(&event);
4493	    if (input_available())
4494	    {
4495		allow_scrollbar = FALSE;
4496		return OK;
4497	    }
4498	}
4499	currentTick = TickCount();
4500    }
4501    while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4502
4503    allow_scrollbar = FALSE;
4504    return FAIL;
4505}
4506
4507/*
4508 * Output routines.
4509 */
4510
4511/* Flush any output to the screen */
4512    void
4513gui_mch_flush(void)
4514{
4515    /* TODO: Is anything needed here? */
4516}
4517
4518/*
4519 * Clear a rectangular region of the screen from text pos (row1, col1) to
4520 * (row2, col2) inclusive.
4521 */
4522    void
4523gui_mch_clear_block(int row1, int col1, int row2, int col2)
4524{
4525    Rect rc;
4526
4527    /*
4528     * Clear one extra pixel at the far right, for when bold characters have
4529     * spilled over to the next column.
4530     */
4531    rc.left = FILL_X(col1);
4532    rc.top = FILL_Y(row1);
4533    rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4534    rc.bottom = FILL_Y(row2 + 1);
4535
4536    gui_mch_set_bg_color(gui.back_pixel);
4537    EraseRect(&rc);
4538}
4539
4540/*
4541 * Clear the whole text window.
4542 */
4543    void
4544gui_mch_clear_all(void)
4545{
4546    Rect	rc;
4547
4548    rc.left = 0;
4549    rc.top = 0;
4550    rc.right = Columns * gui.char_width + 2 * gui.border_width;
4551    rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4552
4553    gui_mch_set_bg_color(gui.back_pixel);
4554    EraseRect(&rc);
4555/*  gui_mch_set_fg_color(gui.norm_pixel);
4556    FrameRect(&rc);
4557*/
4558}
4559
4560/*
4561 * Delete the given number of lines from the given row, scrolling up any
4562 * text further down within the scroll region.
4563 */
4564    void
4565gui_mch_delete_lines(int row, int num_lines)
4566{
4567    Rect	rc;
4568
4569    /* changed without checking! */
4570    rc.left = FILL_X(gui.scroll_region_left);
4571    rc.right = FILL_X(gui.scroll_region_right + 1);
4572    rc.top = FILL_Y(row);
4573    rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4574
4575    gui_mch_set_bg_color(gui.back_pixel);
4576    ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4577
4578    gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4579						       gui.scroll_region_left,
4580	gui.scroll_region_bot, gui.scroll_region_right);
4581}
4582
4583/*
4584 * Insert the given number of lines before the given row, scrolling down any
4585 * following text within the scroll region.
4586 */
4587    void
4588gui_mch_insert_lines(int row, int num_lines)
4589{
4590    Rect rc;
4591
4592    rc.left = FILL_X(gui.scroll_region_left);
4593    rc.right = FILL_X(gui.scroll_region_right + 1);
4594    rc.top = FILL_Y(row);
4595    rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4596
4597    gui_mch_set_bg_color(gui.back_pixel);
4598
4599    ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4600
4601    /* Update gui.cursor_row if the cursor scrolled or copied over */
4602    if (gui.cursor_row >= gui.row
4603	    && gui.cursor_col >= gui.scroll_region_left
4604	    && gui.cursor_col <= gui.scroll_region_right)
4605    {
4606	if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4607	    gui.cursor_row += num_lines;
4608	else if (gui.cursor_row <= gui.scroll_region_bot)
4609	    gui.cursor_is_valid = FALSE;
4610    }
4611
4612    gui_clear_block(row, gui.scroll_region_left,
4613				row + num_lines - 1, gui.scroll_region_right);
4614}
4615
4616    /*
4617     * TODO: add a vim format to the clipboard which remember
4618     *	     LINEWISE, CHARWISE, BLOCKWISE
4619     */
4620
4621    void
4622clip_mch_request_selection(VimClipboard *cbd)
4623{
4624
4625    Handle	textOfClip;
4626    int		flavor = 0;
4627    Size	scrapSize;
4628    ScrapFlavorFlags	scrapFlags;
4629    ScrapRef    scrap = nil;
4630    OSStatus	error;
4631    int		type;
4632    char	*searchCR;
4633    char_u	*tempclip;
4634
4635
4636    error = GetCurrentScrap(&scrap);
4637    if (error != noErr)
4638	return;
4639
4640    error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4641    if (error == noErr)
4642    {
4643	error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4644	if (error == noErr && scrapSize > 1)
4645	    flavor = 1;
4646    }
4647
4648    if (flavor == 0)
4649    {
4650	error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4651	if (error != noErr)
4652	    return;
4653
4654	error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4655	if (error != noErr)
4656	    return;
4657    }
4658
4659    ReserveMem(scrapSize);
4660
4661    /* In CARBON we don't need a Handle, a pointer is good */
4662    textOfClip = NewHandle(scrapSize);
4663
4664    /* tempclip = lalloc(scrapSize+1, TRUE); */
4665    HLock(textOfClip);
4666    error = GetScrapFlavorData(scrap,
4667	    flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4668	    &scrapSize, *textOfClip);
4669    scrapSize -= flavor;
4670
4671    if (flavor)
4672	type = **textOfClip;
4673    else
4674	type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4675
4676    tempclip = lalloc(scrapSize + 1, TRUE);
4677    mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4678    tempclip[scrapSize] = 0;
4679
4680#ifdef MACOS_CONVERT
4681    {
4682	/* Convert from utf-16 (clipboard) */
4683	size_t encLen = 0;
4684	char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4685
4686	if (to != NULL)
4687	{
4688	    scrapSize = encLen;
4689	    vim_free(tempclip);
4690	    tempclip = to;
4691	}
4692    }
4693#endif
4694
4695    searchCR = (char *)tempclip;
4696    while (searchCR != NULL)
4697    {
4698	searchCR = strchr(searchCR, '\r');
4699	if (searchCR != NULL)
4700	    *searchCR = '\n';
4701    }
4702
4703    clip_yank_selection(type, tempclip, scrapSize, cbd);
4704
4705    vim_free(tempclip);
4706    HUnlock(textOfClip);
4707
4708    DisposeHandle(textOfClip);
4709}
4710
4711    void
4712clip_mch_lose_selection(VimClipboard *cbd)
4713{
4714    /*
4715     * TODO: Really nothing to do?
4716     */
4717}
4718
4719    int
4720clip_mch_own_selection(VimClipboard *cbd)
4721{
4722    return OK;
4723}
4724
4725/*
4726 * Send the current selection to the clipboard.
4727 */
4728    void
4729clip_mch_set_selection(VimClipboard *cbd)
4730{
4731    Handle	textOfClip;
4732    long	scrapSize;
4733    int		type;
4734    ScrapRef    scrap;
4735
4736    char_u	*str = NULL;
4737
4738    if (!cbd->owned)
4739	return;
4740
4741    clip_get_selection(cbd);
4742
4743    /*
4744     * Once we set the clipboard, lose ownership.  If another application sets
4745     * the clipboard, we don't want to think that we still own it.
4746     */
4747    cbd->owned = FALSE;
4748
4749    type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4750
4751#ifdef MACOS_CONVERT
4752    size_t utf16_len = 0;
4753    UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4754    if (to)
4755    {
4756	scrapSize = utf16_len;
4757	vim_free(str);
4758	str = (char_u *)to;
4759    }
4760#endif
4761
4762    if (type >= 0)
4763    {
4764	ClearCurrentScrap();
4765
4766	textOfClip = NewHandle(scrapSize + 1);
4767	HLock(textOfClip);
4768
4769	**textOfClip = type;
4770	mch_memmove(*textOfClip + 1, str, scrapSize);
4771	GetCurrentScrap(&scrap);
4772	PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4773		scrapSize, *textOfClip + 1);
4774	PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4775		scrapSize + 1, *textOfClip);
4776	HUnlock(textOfClip);
4777	DisposeHandle(textOfClip);
4778    }
4779
4780    vim_free(str);
4781}
4782
4783    void
4784gui_mch_set_text_area_pos(int x, int y, int w, int h)
4785{
4786    Rect	VimBound;
4787
4788/*  HideWindow(gui.VimWindow); */
4789    GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4790
4791    if (gui.which_scrollbars[SBAR_LEFT])
4792    {
4793	VimBound.left = -gui.scrollbar_width + 1;
4794    }
4795    else
4796    {
4797	VimBound.left = 0;
4798    }
4799
4800    SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4801
4802    ShowWindow(gui.VimWindow);
4803}
4804
4805/*
4806 * Menu stuff.
4807 */
4808
4809    void
4810gui_mch_enable_menu(int flag)
4811{
4812    /*
4813     * Menu is always active.
4814     */
4815}
4816
4817    void
4818gui_mch_set_menu_pos(int x, int y, int w, int h)
4819{
4820    /*
4821     * The menu is always at the top of the screen.
4822     */
4823}
4824
4825/*
4826 * Add a sub menu to the menu bar.
4827 */
4828    void
4829gui_mch_add_menu(vimmenu_T *menu, int idx)
4830{
4831    /*
4832     * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4833     * TODO: use menu->mnemonic and menu->actext
4834     * TODO: Try to reuse menu id
4835     *       Carbon Help suggest to use only id between 1 and 235
4836     */
4837    static long	 next_avail_id = 128;
4838    long	 menu_after_me = 0; /* Default to the end */
4839#if defined(FEAT_MBYTE)
4840    CFStringRef name;
4841#else
4842    char_u	*name;
4843#endif
4844    short	 index;
4845    vimmenu_T	*parent = menu->parent;
4846    vimmenu_T	*brother = menu->next;
4847
4848    /* Cannot add a menu if ... */
4849    if ((parent != NULL && parent->submenu_id == 0))
4850	return;
4851
4852    /* menu ID greater than 1024 are reserved for ??? */
4853    if (next_avail_id == 1024)
4854	return;
4855
4856    /* My brother could be the PopUp, find my real brother */
4857    while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4858	brother = brother->next;
4859
4860    /*  Find where to insert the menu (for MenuBar) */
4861    if ((parent == NULL) && (brother != NULL))
4862	menu_after_me = brother->submenu_id;
4863
4864    /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4865    if (!menu_is_menubar(menu->name))
4866	menu_after_me = hierMenu;
4867
4868    /* Convert the name */
4869#ifdef MACOS_CONVERT
4870    name = menu_title_removing_mnemonic(menu);
4871#else
4872    name = C2Pascal_save(menu->dname);
4873#endif
4874    if (name == NULL)
4875	return;
4876
4877    /* Create the menu unless it's the help menu */
4878    {
4879	/* Carbon suggest use of
4880	 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4881	 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4882	 */
4883	menu->submenu_id = next_avail_id;
4884#if defined(FEAT_MBYTE)
4885	if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4886	    SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4887#else
4888	menu->submenu_handle = NewMenu(menu->submenu_id, name);
4889#endif
4890	next_avail_id++;
4891    }
4892
4893    if (parent == NULL)
4894    {
4895	/* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4896
4897	/* TODO: Verify if we could only Insert Menu if really part of the
4898	 * menubar The Inserted menu are scanned or the Command-key combos
4899	 */
4900
4901	/* Insert the menu */
4902	InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4903#if 1
4904	/* Vim should normally update it. TODO: verify */
4905	DrawMenuBar();
4906#endif
4907    }
4908    else
4909    {
4910	/* Adding as a submenu */
4911
4912	index = gui_mac_get_menu_item_index(menu);
4913
4914	/* Call InsertMenuItem followed by SetMenuItemText
4915	 * to avoid special character recognition by InsertMenuItem
4916	 */
4917	InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4918#if defined(FEAT_MBYTE)
4919	SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4920#else
4921	SetMenuItemText(parent->submenu_handle, idx+1, name);
4922#endif
4923	SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4924	SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4925	InsertMenu(menu->submenu_handle, hierMenu);
4926    }
4927
4928#if defined(FEAT_MBYTE)
4929    CFRelease(name);
4930#else
4931    vim_free(name);
4932#endif
4933
4934#if 0
4935    /* Done by Vim later on */
4936    DrawMenuBar();
4937#endif
4938}
4939
4940/*
4941 * Add a menu item to a menu
4942 */
4943    void
4944gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4945{
4946#if defined(FEAT_MBYTE)
4947    CFStringRef name;
4948#else
4949    char_u	*name;
4950#endif
4951    vimmenu_T	*parent = menu->parent;
4952    int		menu_inserted;
4953
4954    /* Cannot add item, if the menu have not been created */
4955    if (parent->submenu_id == 0)
4956	return;
4957
4958    /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4959       for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4960
4961    /* Convert the name */
4962#ifdef MACOS_CONVERT
4963    name = menu_title_removing_mnemonic(menu);
4964#else
4965    name = C2Pascal_save(menu->dname);
4966#endif
4967
4968    /* Where are just a menu item, so no handle, no id */
4969    menu->submenu_id = 0;
4970    menu->submenu_handle = NULL;
4971
4972    menu_inserted = 0;
4973    if (menu->actext)
4974    {
4975	/* If the accelerator text for the menu item looks like it describes
4976	 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4977	 * item's command equivalent.
4978	 */
4979	int	    key = 0;
4980	int	    modifiers = 0;
4981	char_u	    *p_actext;
4982
4983	p_actext = menu->actext;
4984	key = find_special_key(&p_actext, &modifiers, FALSE, FALSE);
4985	if (*p_actext != 0)
4986	    key = 0; /* error: trailing text */
4987	/* find_special_key() returns a keycode with as many of the
4988	 * specified modifiers as appropriate already applied (e.g., for
4989	 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4990	 * as the only modifier).  Since we want to display all of the
4991	 * modifiers, we need to convert the keycode back to a printable
4992	 * character plus modifiers.
4993	 * TODO: Write an alternative find_special_key() that doesn't
4994	 * apply modifiers.
4995	 */
4996	if (key > 0 && key < 32)
4997	{
4998	    /* Convert a control key to an uppercase letter.  Note that
4999	     * by this point it is no longer possible to distinguish
5000	     * between, e.g., Ctrl-S and Ctrl-Shift-S.
5001	     */
5002	    modifiers |= MOD_MASK_CTRL;
5003	    key += '@';
5004	}
5005	/* If the keycode is an uppercase letter, set the Shift modifier.
5006	 * If it is a lowercase letter, don't set the modifier, but convert
5007	 * the letter to uppercase for display in the menu.
5008	 */
5009	else if (key >= 'A' && key <= 'Z')
5010	    modifiers |= MOD_MASK_SHIFT;
5011	else if (key >= 'a' && key <= 'z')
5012	    key += 'A' - 'a';
5013	/* Note: keycodes below 0x22 are reserved by Apple. */
5014	if (key >= 0x22 && vim_isprintc_strict(key))
5015	{
5016	    int		valid = 1;
5017	    char_u      mac_mods = kMenuNoModifiers;
5018	    /* Convert Vim modifier codes to Menu Manager equivalents. */
5019	    if (modifiers & MOD_MASK_SHIFT)
5020		mac_mods |= kMenuShiftModifier;
5021	    if (modifiers & MOD_MASK_CTRL)
5022		mac_mods |= kMenuControlModifier;
5023	    if (!(modifiers & MOD_MASK_CMD))
5024		mac_mods |= kMenuNoCommandModifier;
5025	    if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
5026		valid = 0; /* TODO: will Alt someday map to Option? */
5027	    if (valid)
5028	    {
5029		char_u	    item_txt[10];
5030		/* Insert the menu item after idx, with its command key. */
5031		item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
5032		item_txt[3] = key;
5033		InsertMenuItem(parent->submenu_handle, item_txt, idx);
5034		/* Set the modifier keys. */
5035		SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
5036		menu_inserted = 1;
5037	    }
5038	}
5039    }
5040    /* Call InsertMenuItem followed by SetMenuItemText
5041     * to avoid special character recognition by InsertMenuItem
5042     */
5043    if (!menu_inserted)
5044	InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
5045    /* Set the menu item name. */
5046#if defined(FEAT_MBYTE)
5047    SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
5048#else
5049    SetMenuItemText(parent->submenu_handle, idx+1, name);
5050#endif
5051
5052#if 0
5053    /* Called by Vim */
5054    DrawMenuBar();
5055#endif
5056
5057#if defined(FEAT_MBYTE)
5058    CFRelease(name);
5059#else
5060    /* TODO: Can name be freed? */
5061    vim_free(name);
5062#endif
5063}
5064
5065    void
5066gui_mch_toggle_tearoffs(int enable)
5067{
5068    /* no tearoff menus */
5069}
5070
5071/*
5072 * Destroy the machine specific menu widget.
5073 */
5074    void
5075gui_mch_destroy_menu(vimmenu_T *menu)
5076{
5077    short	index = gui_mac_get_menu_item_index(menu);
5078
5079    if (index > 0)
5080    {
5081      if (menu->parent)
5082      {
5083	{
5084	    /* For now just don't delete help menu items. (Huh? Dany) */
5085	    DeleteMenuItem(menu->parent->submenu_handle, index);
5086
5087	    /* Delete the Menu if it was a hierarchical Menu */
5088	    if (menu->submenu_id != 0)
5089	    {
5090		DeleteMenu(menu->submenu_id);
5091		DisposeMenu(menu->submenu_handle);
5092	    }
5093	}
5094      }
5095#ifdef DEBUG_MAC_MENU
5096      else
5097      {
5098	printf("gmdm 2\n");
5099      }
5100#endif
5101    }
5102    else
5103    {
5104	{
5105	    DeleteMenu(menu->submenu_id);
5106	    DisposeMenu(menu->submenu_handle);
5107	}
5108    }
5109    /* Shouldn't this be already done by Vim. TODO: Check */
5110    DrawMenuBar();
5111}
5112
5113/*
5114 * Make a menu either grey or not grey.
5115 */
5116    void
5117gui_mch_menu_grey(vimmenu_T *menu, int grey)
5118{
5119    /* TODO: Check if menu really exists */
5120    short index = gui_mac_get_menu_item_index(menu);
5121/*
5122    index = menu->index;
5123*/
5124    if (grey)
5125    {
5126	if (menu->children)
5127	    DisableMenuItem(menu->submenu_handle, index);
5128	if (menu->parent)
5129	  if (menu->parent->submenu_handle)
5130	    DisableMenuItem(menu->parent->submenu_handle, index);
5131    }
5132    else
5133    {
5134	if (menu->children)
5135	    EnableMenuItem(menu->submenu_handle, index);
5136	if (menu->parent)
5137	  if (menu->parent->submenu_handle)
5138	    EnableMenuItem(menu->parent->submenu_handle, index);
5139    }
5140}
5141
5142/*
5143 * Make menu item hidden or not hidden
5144 */
5145    void
5146gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5147{
5148    /* There's no hidden mode on MacOS */
5149    gui_mch_menu_grey(menu, hidden);
5150}
5151
5152
5153/*
5154 * This is called after setting all the menus to grey/hidden or not.
5155 */
5156    void
5157gui_mch_draw_menubar(void)
5158{
5159    DrawMenuBar();
5160}
5161
5162
5163/*
5164 * Scrollbar stuff.
5165 */
5166
5167    void
5168gui_mch_enable_scrollbar(
5169	scrollbar_T	*sb,
5170	int		flag)
5171{
5172    if (flag)
5173	ShowControl(sb->id);
5174    else
5175	HideControl(sb->id);
5176
5177#ifdef DEBUG_MAC_SB
5178    printf("enb_sb (%x) %x\n",sb->id, flag);
5179#endif
5180}
5181
5182    void
5183gui_mch_set_scrollbar_thumb(
5184	scrollbar_T *sb,
5185	long val,
5186	long size,
5187	long max)
5188{
5189    SetControl32BitMaximum (sb->id, max);
5190    SetControl32BitMinimum (sb->id, 0);
5191    SetControl32BitValue   (sb->id, val);
5192    SetControlViewSize     (sb->id, size);
5193#ifdef DEBUG_MAC_SB
5194    printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
5195#endif
5196}
5197
5198    void
5199gui_mch_set_scrollbar_pos(
5200	scrollbar_T *sb,
5201	int x,
5202	int y,
5203	int w,
5204	int h)
5205{
5206    gui_mch_set_bg_color(gui.back_pixel);
5207/*  if (gui.which_scrollbars[SBAR_LEFT])
5208    {
5209	MoveControl(sb->id, x-16, y);
5210	SizeControl(sb->id, w + 1, h);
5211    }
5212    else
5213    {
5214	MoveControl(sb->id, x, y);
5215	SizeControl(sb->id, w + 1, h);
5216    }*/
5217    if (sb == &gui.bottom_sbar)
5218	h += 1;
5219    else
5220	w += 1;
5221
5222    if (gui.which_scrollbars[SBAR_LEFT])
5223	x -= 15;
5224
5225    MoveControl(sb->id, x, y);
5226    SizeControl(sb->id, w, h);
5227#ifdef DEBUG_MAC_SB
5228    printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
5229#endif
5230}
5231
5232    void
5233gui_mch_create_scrollbar(
5234	scrollbar_T *sb,
5235	int orient)	/* SBAR_VERT or SBAR_HORIZ */
5236{
5237    Rect bounds;
5238
5239    bounds.top = -16;
5240    bounds.bottom = -10;
5241    bounds.right = -10;
5242    bounds.left = -16;
5243
5244    sb->id = NewControl(gui.VimWindow,
5245			 &bounds,
5246			 "\pScrollBar",
5247			 TRUE,
5248			 0, /* current*/
5249			 0, /* top */
5250			 0, /* bottom */
5251			 kControlScrollBarLiveProc,
5252			 (long) sb->ident);
5253#ifdef DEBUG_MAC_SB
5254    printf("create_sb (%x) %x\n",sb->id, orient);
5255#endif
5256}
5257
5258    void
5259gui_mch_destroy_scrollbar(scrollbar_T *sb)
5260{
5261    gui_mch_set_bg_color(gui.back_pixel);
5262    DisposeControl(sb->id);
5263#ifdef DEBUG_MAC_SB
5264    printf("dest_sb (%x) \n",sb->id);
5265#endif
5266}
5267
5268
5269/*
5270 * Cursor blink functions.
5271 *
5272 * This is a simple state machine:
5273 * BLINK_NONE	not blinking at all
5274 * BLINK_OFF	blinking, cursor is not shown
5275 * BLINK_ON blinking, cursor is shown
5276 */
5277    void
5278gui_mch_set_blinking(long wait, long on, long off)
5279{
5280    /* TODO: TODO: TODO: TODO: */
5281/*    blink_waittime = wait;
5282    blink_ontime = on;
5283    blink_offtime = off;*/
5284}
5285
5286/*
5287 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
5288 */
5289    void
5290gui_mch_stop_blink(void)
5291{
5292    gui_update_cursor(TRUE, FALSE);
5293    /* TODO: TODO: TODO: TODO: */
5294/*    gui_w32_rm_blink_timer();
5295    if (blink_state == BLINK_OFF)
5296    gui_update_cursor(TRUE, FALSE);
5297    blink_state = BLINK_NONE;*/
5298}
5299
5300/*
5301 * Start the cursor blinking.  If it was already blinking, this restarts the
5302 * waiting time and shows the cursor.
5303 */
5304    void
5305gui_mch_start_blink(void)
5306{
5307    gui_update_cursor(TRUE, FALSE);
5308    /* TODO: TODO: TODO: TODO: */
5309/*    gui_w32_rm_blink_timer(); */
5310
5311    /* Only switch blinking on if none of the times is zero */
5312/*    if (blink_waittime && blink_ontime && blink_offtime)
5313    {
5314    blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5315			    (TIMERPROC)_OnBlinkTimer);
5316    blink_state = BLINK_ON;
5317    gui_update_cursor(TRUE, FALSE);
5318    }*/
5319}
5320
5321/*
5322 * Return the RGB value of a pixel as long.
5323 */
5324    long_u
5325gui_mch_get_rgb(guicolor_T pixel)
5326{
5327    return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5328}
5329
5330
5331
5332#ifdef FEAT_BROWSE
5333/*
5334 * Pop open a file browser and return the file selected, in allocated memory,
5335 * or NULL if Cancel is hit.
5336 *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
5337 *  title   - Title message for the file browser dialog.
5338 *  dflt    - Default name of file.
5339 *  ext     - Default extension to be added to files without extensions.
5340 *  initdir - directory in which to open the browser (NULL = current dir)
5341 *  filter  - Filter for matched files to choose from.
5342 *  Has a format like this:
5343 *  "C Files (*.c)\0*.c\0"
5344 *  "All Files\0*.*\0\0"
5345 *  If these two strings were concatenated, then a choice of two file
5346 *  filters will be selectable to the user.  Then only matching files will
5347 *  be shown in the browser.  If NULL, the default allows all files.
5348 *
5349 *  *NOTE* - the filter string must be terminated with TWO nulls.
5350 */
5351    char_u *
5352gui_mch_browse(
5353    int saving,
5354    char_u *title,
5355    char_u *dflt,
5356    char_u *ext,
5357    char_u *initdir,
5358    char_u *filter)
5359{
5360    /* TODO: Add Ammon's safety checl (Dany) */
5361    NavReplyRecord	reply;
5362    char_u		*fname = NULL;
5363    char_u		**fnames = NULL;
5364    long		numFiles;
5365    NavDialogOptions	navOptions;
5366    OSErr		error;
5367
5368    /* Get Navigation Service Defaults value */
5369    NavGetDefaultDialogOptions(&navOptions);
5370
5371
5372    /* TODO: If we get a :browse args, set the Multiple bit. */
5373    navOptions.dialogOptionFlags =  kNavAllowInvisibleFiles
5374				 |  kNavDontAutoTranslate
5375				 |  kNavDontAddTranslateItems
5376			    /*	 |  kNavAllowMultipleFiles */
5377				 |  kNavAllowStationery;
5378
5379    (void) C2PascalString(title,   &navOptions.message);
5380    (void) C2PascalString(dflt,    &navOptions.savedFileName);
5381    /* Could set clientName?
5382     *		 windowTitle? (there's no title bar?)
5383     */
5384
5385    if (saving)
5386    {
5387	/* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5388	NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5389	if (!reply.validRecord)
5390	    return NULL;
5391    }
5392    else
5393    {
5394	/* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5395	NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5396	if (!reply.validRecord)
5397	    return NULL;
5398    }
5399
5400    fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5401
5402    NavDisposeReply(&reply);
5403
5404    if (fnames)
5405    {
5406	fname = fnames[0];
5407	vim_free(fnames);
5408    }
5409
5410    /* TODO: Shorten the file name if possible */
5411    return fname;
5412}
5413#endif /* FEAT_BROWSE */
5414
5415#ifdef FEAT_GUI_DIALOG
5416/*
5417 * Stuff for dialogues
5418 */
5419
5420/*
5421 * Create a dialogue dynamically from the parameter strings.
5422 * type       = type of dialogue (question, alert, etc.)
5423 * title      = dialogue title. may be NULL for default title.
5424 * message    = text to display. Dialogue sizes to accommodate it.
5425 * buttons    = '\n' separated list of button captions, default first.
5426 * dfltbutton = number of default button.
5427 *
5428 * This routine returns 1 if the first button is pressed,
5429 *	    2 for the second, etc.
5430 *
5431 *	    0 indicates Esc was pressed.
5432 *	    -1 for unexpected error
5433 *
5434 * If stubbing out this fn, return 1.
5435 */
5436
5437typedef struct
5438{
5439    short   idx;
5440    short   width;	/* Size of the text in pixel */
5441    Rect    box;
5442} vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5443
5444#define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5445
5446    static void
5447macMoveDialogItem(
5448    DialogRef	theDialog,
5449    short	itemNumber,
5450    short	X,
5451    short	Y,
5452    Rect	*inBox)
5453{
5454#if 0 /* USE_CARBONIZED */
5455    /* Untested */
5456    MoveDialogItem(theDialog, itemNumber, X, Y);
5457    if (inBox != nil)
5458	GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5459#else
5460    short	itemType;
5461    Handle	itemHandle;
5462    Rect	localBox;
5463    Rect	*itemBox = &localBox;
5464
5465    if (inBox != nil)
5466	itemBox = inBox;
5467
5468    GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5469    OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5470    OffsetRect(itemBox, X, Y);
5471    /* To move a control (like a button) we need to call both
5472     * MoveControl and SetDialogItem. FAQ 6-18 */
5473    if (1) /*(itemType & kControlDialogItem) */
5474	MoveControl((ControlRef) itemHandle, X, Y);
5475    SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5476#endif
5477}
5478
5479    static void
5480macSizeDialogItem(
5481    DialogRef	theDialog,
5482    short	itemNumber,
5483    short	width,
5484    short	height)
5485{
5486    short	itemType;
5487    Handle	itemHandle;
5488    Rect	itemBox;
5489
5490    GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5491
5492    /* When width or height is zero do not change it */
5493    if (width  == 0)
5494	width  = itemBox.right  - itemBox.left;
5495    if (height == 0)
5496	height = itemBox.bottom - itemBox.top;
5497
5498#if 0 /* USE_CARBONIZED */
5499    SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5500#else
5501    /* Resize the bounding box */
5502    itemBox.right  = itemBox.left + width;
5503    itemBox.bottom = itemBox.top  + height;
5504
5505    /* To resize a control (like a button) we need to call both
5506     * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5507    if (itemType & kControlDialogItem)
5508	SizeControl((ControlRef) itemHandle, width, height);
5509
5510    /* Configure back the item */
5511    SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5512#endif
5513}
5514
5515    static void
5516macSetDialogItemText(
5517    DialogRef	theDialog,
5518    short	itemNumber,
5519    Str255	itemName)
5520{
5521    short	itemType;
5522    Handle	itemHandle;
5523    Rect	itemBox;
5524
5525    GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5526
5527    if (itemType & kControlDialogItem)
5528	SetControlTitle((ControlRef) itemHandle, itemName);
5529    else
5530	SetDialogItemText(itemHandle, itemName);
5531}
5532
5533
5534/* ModalDialog() handler for message dialogs that have hotkey accelerators.
5535 * Expects a mapping of hotkey char to control index in gDialogHotKeys;
5536 * setting gDialogHotKeys to NULL disables any hotkey handling.
5537 */
5538    static pascal Boolean
5539DialogHotkeyFilterProc (
5540    DialogRef	    theDialog,
5541    EventRecord	    *event,
5542    DialogItemIndex *itemHit)
5543{
5544    char_u keyHit;
5545
5546    if (event->what == keyDown || event->what == autoKey)
5547    {
5548	keyHit = (event->message & charCodeMask);
5549
5550	if (gDialogHotKeys && gDialogHotKeys[keyHit])
5551	{
5552#ifdef DEBUG_MAC_DIALOG_HOTKEYS
5553	    printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]);
5554#endif
5555	    *itemHit = gDialogHotKeys[keyHit];
5556
5557	    /* When handing off to StdFilterProc, pretend that the user
5558	     * clicked the control manually. Note that this is also supposed
5559	     * to cause the button to hilite briefly (to give some user
5560	     * feedback), but this seems not to actually work (or it's too
5561	     * fast to be seen).
5562	     */
5563	    event->what = kEventControlSimulateHit;
5564
5565	    return true; /* we took care of it */
5566	}
5567
5568	/* Defer to the OS's standard behavior for this event.
5569	 * This ensures that Enter will still activate the default button. */
5570	return StdFilterProc(theDialog, event, itemHit);
5571    }
5572    return false;      /* Let ModalDialog deal with it */
5573}
5574
5575
5576/* TODO: There have been some crashes with dialogs, check your inbox
5577 * (Jussi)
5578 */
5579    int
5580gui_mch_dialog(
5581    int		type,
5582    char_u	*title,
5583    char_u	*message,
5584    char_u	*buttons,
5585    int		dfltbutton,
5586    char_u	*textfield)
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:  useIcon = kNoteIcon;
5727	case VIM_ERROR:    useIcon = kStopIcon;
5728	case VIM_WARNING:  useIcon = kCautionIcon;
5729	case VIM_INFO:     useIcon = kNoteIcon;
5730	case VIM_QUESTION: useIcon = kNoteIcon;
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 * Usefull 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
5910display_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
5944gui_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
5955gui_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
6001gui_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
6054mch_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
6067gui_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 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
6104 */
6105
6106    int
6107C2PascalString(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
6130GetFSSpecFromPath(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
6157char_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
6372im_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
6381static ScriptLanguageRecord gTSLWindow;
6382static ScriptLanguageRecord gTSLInsert;
6383static ScriptLanguageRecord gTSLDefault = { 0, 0 };
6384
6385static Component	     gTSCWindow;
6386static Component	     gTSCInsert;
6387static Component	     gTSCDefault;
6388
6389static int		     im_initialized = 0;
6390
6391    static void
6392im_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
6445im_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
6504im_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
6519static MenuRef contextMenu = NULL;
6520enum
6521{
6522    kTabContextMenuId = 42,
6523};
6524
6525// the caller has to CFRelease() the returned string
6526    static CFStringRef
6527getTabLabel(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
6543static 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 upate_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).
6550static CFStringRef *tabLabels = NULL;
6551static int tabLabelsSize = 0;
6552
6553enum
6554{
6555    kTabsColumn = 'Tabs'
6556};
6557
6558    static int
6559getTabCount(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
6571dbItemDataCallback(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
6601dbItemNotificationCallback(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
6615dbGetContextualMenuCallback(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
6629dbSelectContextualMenuCallback(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
6670dbFocusCallback(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
6681drawerCallback(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
6713enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6714
6715    static OSStatus
6716myDataBrowserChangeAttributes(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
6744initialise_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
6839gui_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
6851gui_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
6862gui_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
6903gui_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
6915