Deleted junk files.
[MacVim/jjgod.git] / src / gui_mac.c
blob2d3da309490c353cb4a2b2777a51e8c7f3e52eb0
1 /* vi:set ts=8 sts=4 sw=4:
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
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.
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"
22 * TODO: Change still to merge from the macvim's iDisk
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.
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)
37 #include <Devices.h> /* included first to avoid CR problems */
38 #include "vim.h"
40 #define USE_CARBONIZED
41 #define USE_AEVENT /* Enable AEVENT */
42 #undef USE_OFFSETED_WINDOW /* Debugging feature: start Vim window OFFSETed */
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
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
57 static EventHandlerUPP mouseWheelHandlerUPP = NULL;
58 SInt32 gMacSystemVersion;
60 #ifdef MACOS_CONVERT
61 # define USE_CARBONKEYHANDLER
62 static EventHandlerUPP keyEventHandlerUPP = NULL;
63 #endif
66 /* Include some file. TODO: move into os_mac.h */
67 #include <Menus.h>
68 #include <Resources.h>
69 #include <Processes.h>
70 #ifdef USE_AEVENT
71 # include <AppleEvents.h>
72 # include <AERegistry.h>
73 #endif
74 # include <Gestalt.h>
75 #if UNIVERSAL_INTERFACES_VERSION >= 0x0330
76 # include <ControlDefinitions.h>
77 # include <Navigation.h> /* Navigation only part of ?? */
78 #endif
80 /* Help Manager (balloon.h, HM prefixed functions) are not supported
81 * under Carbon (Jussi) */
82 # if 0
83 /* New Help Interface for Mac, not implemented yet.*/
84 # include <MacHelp.h>
85 # endif
88 * These seem to be rectangle options. Why are they not found in
89 * headers? (Jussi)
91 #define kNothing 0
92 #define kCreateEmpty 2 /*1*/
93 #define kCreateRect 2
94 #define kDestroy 3
97 * Dany: Don't like those...
99 #define topLeft(r) (((Point*)&(r))[0])
100 #define botRight(r) (((Point*)&(r))[1])
103 /* Time of last mouse click, to detect double-click */
104 static long lastMouseTick = 0;
106 /* ??? */
107 static RgnHandle cursorRgn;
108 static RgnHandle dragRgn;
109 static Rect dragRect;
110 static short dragRectEnbl;
111 static short dragRectControl;
113 /* This variable is set when waiting for an event, which is the only moment
114 * scrollbar dragging can be done directly. It's not allowed while commands
115 * are executed, because it may move the cursor and that may cause unexpected
116 * problems (e.g., while ":s" is working).
118 static int allow_scrollbar = FALSE;
120 /* Last mouse click caused contextual menu, (to provide proper release) */
121 static short clickIsPopup;
123 /* Feedback Action for Scrollbar */
124 ControlActionUPP gScrollAction;
125 ControlActionUPP gScrollDrag;
127 /* Keeping track of which scrollbar is being dragged */
128 static ControlHandle dragged_sb = NULL;
130 static struct
132 FMFontFamily family;
133 FMFontSize size;
134 FMFontStyle style;
135 Boolean isPanelVisible;
136 } gFontPanelInfo = { 0, 0, 0, false };
138 #ifdef MACOS_CONVERT
139 # define USE_ATSUI_DRAWING
140 ATSUStyle gFontStyle;
141 Boolean gIsFontFallbackSet;
142 #endif
144 /* Colors Macros */
145 #define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b)
146 #define Red(c) ((c & 0x00FF0000) >> 16)
147 #define Green(c) ((c & 0x0000FF00) >> 8)
148 #define Blue(c) ((c & 0x000000FF) >> 0)
150 /* Key mapping */
152 #define vk_Esc 0x35 /* -> 1B */
154 #define vk_F1 0x7A /* -> 10 */
155 #define vk_F2 0x78 /*0x63*/
156 #define vk_F3 0x63 /*0x76*/
157 #define vk_F4 0x76 /*0x60*/
158 #define vk_F5 0x60 /*0x61*/
159 #define vk_F6 0x61 /*0x62*/
160 #define vk_F7 0x62 /*0x63*/ /*?*/
161 #define vk_F8 0x64
162 #define vk_F9 0x65
163 #define vk_F10 0x6D
164 #define vk_F11 0x67
165 #define vk_F12 0x6F
166 #define vk_F13 0x69
167 #define vk_F14 0x6B
168 #define vk_F15 0x71
170 #define vk_Clr 0x47 /* -> 1B (ESC) */
171 #define vk_Enter 0x4C /* -> 03 */
173 #define vk_Space 0x31 /* -> 20 */
174 #define vk_Tab 0x30 /* -> 09 */
175 #define vk_Return 0x24 /* -> 0D */
176 /* This is wrong for OSX, what is it for? */
177 #define vk_Delete 0X08 /* -> 08 BackSpace */
179 #define vk_Help 0x72 /* -> 05 */
180 #define vk_Home 0x73 /* -> 01 */
181 #define vk_PageUp 0x74 /* -> 0D */
182 #define vk_FwdDelete 0x75 /* -> 7F */
183 #define vk_End 0x77 /* -> 04 */
184 #define vk_PageDown 0x79 /* -> 0C */
186 #define vk_Up 0x7E /* -> 1E */
187 #define vk_Down 0x7D /* -> 1F */
188 #define vk_Left 0x7B /* -> 1C */
189 #define vk_Right 0x7C /* -> 1D */
191 #define vk_Undo vk_F1
192 #define vk_Cut vk_F2
193 #define vk_Copy vk_F3
194 #define vk_Paste vk_F4
195 #define vk_PrintScreen vk_F13
196 #define vk_SCrollLock vk_F14
197 #define vk_Pause vk_F15
198 #define vk_NumLock vk_Clr
199 #define vk_Insert vk_Help
201 #define KeySym char
203 static struct
205 KeySym key_sym;
206 char_u vim_code0;
207 char_u vim_code1;
208 } special_keys[] =
210 {vk_Up, 'k', 'u'},
211 {vk_Down, 'k', 'd'},
212 {vk_Left, 'k', 'l'},
213 {vk_Right, 'k', 'r'},
215 {vk_F1, 'k', '1'},
216 {vk_F2, 'k', '2'},
217 {vk_F3, 'k', '3'},
218 {vk_F4, 'k', '4'},
219 {vk_F5, 'k', '5'},
220 {vk_F6, 'k', '6'},
221 {vk_F7, 'k', '7'},
222 {vk_F8, 'k', '8'},
223 {vk_F9, 'k', '9'},
224 {vk_F10, 'k', ';'},
226 {vk_F11, 'F', '1'},
227 {vk_F12, 'F', '2'},
228 {vk_F13, 'F', '3'},
229 {vk_F14, 'F', '4'},
230 {vk_F15, 'F', '5'},
232 /* {XK_Help, '%', '1'}, */
233 /* {XK_Undo, '&', '8'}, */
234 /* {XK_BackSpace, 'k', 'b'}, */
235 #ifndef MACOS_X
236 {vk_Delete, 'k', 'b'},
237 #endif
238 {vk_Insert, 'k', 'I'},
239 {vk_FwdDelete, 'k', 'D'},
240 {vk_Home, 'k', 'h'},
241 {vk_End, '@', '7'},
242 /* {XK_Prior, 'k', 'P'}, */
243 /* {XK_Next, 'k', 'N'}, */
244 /* {XK_Print, '%', '9'}, */
246 {vk_PageUp, 'k', 'P'},
247 {vk_PageDown, 'k', 'N'},
249 /* End of list marker: */
250 {(KeySym)0, 0, 0}
254 * ------------------------------------------------------------
255 * Forward declaration (for those needed)
256 * ------------------------------------------------------------
259 #ifdef USE_AEVENT
260 OSErr HandleUnusedParms(const AppleEvent *theAEvent);
261 #endif
263 #ifdef FEAT_GUI_TABLINE
264 static void initialise_tabline(void);
265 static WindowRef drawer = NULL; // TODO: put into gui.h
266 #endif
269 * ------------------------------------------------------------
270 * Conversion Utility
271 * ------------------------------------------------------------
275 * C2Pascal_save
277 * Allocate memory and convert the C-String passed in
278 * into a pascal string
282 char_u *
283 C2Pascal_save(char_u *Cstring)
285 char_u *PascalString;
286 int len;
288 if (Cstring == NULL)
289 return NULL;
291 len = STRLEN(Cstring);
293 if (len > 255) /* Truncate if necessary */
294 len = 255;
296 PascalString = alloc(len + 1);
297 if (PascalString != NULL)
299 mch_memmove(PascalString + 1, Cstring, len);
300 PascalString[0] = len;
303 return PascalString;
307 * C2Pascal_save_and_remove_backslash
309 * Allocate memory and convert the C-String passed in
310 * into a pascal string. Also remove the backslash at the same time
314 char_u *
315 C2Pascal_save_and_remove_backslash(char_u *Cstring)
317 char_u *PascalString;
318 int len;
319 char_u *p, *c;
321 len = STRLEN(Cstring);
323 if (len > 255) /* Truncate if necessary */
324 len = 255;
326 PascalString = alloc(len + 1);
327 if (PascalString != NULL)
329 for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++)
331 if ((*c == '\\') && (c[1] != 0))
333 c++;
335 *p = *c;
336 p++;
337 len++;
339 PascalString[0] = len;
342 return PascalString;
346 * Convert the modifiers of an Event into vim's modifiers (mouse)
349 int_u
350 EventModifiers2VimMouseModifiers(EventModifiers macModifiers)
352 int_u vimModifiers = 0x00;
354 if (macModifiers & (shiftKey | rightShiftKey))
355 vimModifiers |= MOUSE_SHIFT;
356 if (macModifiers & (controlKey | rightControlKey))
357 vimModifiers |= MOUSE_CTRL;
358 if (macModifiers & (optionKey | rightOptionKey))
359 vimModifiers |= MOUSE_ALT;
360 #if 0
361 /* Not yet supported */
362 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
363 vimModifiers |= MOUSE_CMD;
364 #endif
365 return (vimModifiers);
369 * Convert the modifiers of an Event into vim's modifiers (keys)
372 static int_u
373 EventModifiers2VimModifiers(EventModifiers macModifiers)
375 int_u vimModifiers = 0x00;
377 if (macModifiers & (shiftKey | rightShiftKey))
378 vimModifiers |= MOD_MASK_SHIFT;
379 if (macModifiers & (controlKey | rightControlKey))
380 vimModifiers |= MOD_MASK_CTRL;
381 if (macModifiers & (optionKey | rightOptionKey))
382 vimModifiers |= MOD_MASK_ALT;
383 #ifdef USE_CMD_KEY
384 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
385 vimModifiers |= MOD_MASK_CMD;
386 #endif
387 return (vimModifiers);
390 /* Convert a string representing a point size into pixels. The string should
391 * be a positive decimal number, with an optional decimal point (eg, "12", or
392 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
393 * character is stored in *end. The flag "vertical" says whether this
394 * calculation is for a vertical (height) size or a horizontal (width) one.
396 * From gui_w48.c
398 static int
399 points_to_pixels(char_u *str, char_u **end, int vertical)
401 int pixels;
402 int points = 0;
403 int divisor = 0;
405 while (*str)
407 if (*str == '.' && divisor == 0)
409 /* Start keeping a divisor, for later */
410 divisor = 1;
411 continue;
414 if (!isdigit(*str))
415 break;
417 points *= 10;
418 points += *str - '0';
419 divisor *= 10;
421 ++str;
424 if (divisor == 0)
425 divisor = 1;
427 pixels = points/divisor;
428 *end = str;
429 return pixels;
432 #ifdef MACOS_CONVERT
434 * Deletes all traces of any Windows-style mnemonic text (including any
435 * parentheses) from a menu item and returns the cleaned menu item title.
436 * The caller is responsible for releasing the returned string.
438 static CFStringRef
439 menu_title_removing_mnemonic(vimmenu_T *menu)
441 CFStringRef name;
442 size_t menuTitleLen;
443 CFIndex displayLen;
444 CFRange mnemonicStart;
445 CFRange mnemonicEnd;
446 CFMutableStringRef cleanedName;
448 menuTitleLen = STRLEN(menu->dname);
449 name = mac_enc_to_cfstring(menu->dname, menuTitleLen);
451 if (name)
453 /* Simple mnemonic-removal algorithm, assumes single parenthesized
454 * mnemonic character towards the end of the menu text */
455 mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards);
456 displayLen = CFStringGetLength(name);
458 if (mnemonicStart.location != kCFNotFound
459 && (mnemonicStart.location + 2) < displayLen
460 && CFStringGetCharacterAtIndex(name,
461 mnemonicStart.location + 1) == (UniChar)menu->mnemonic)
463 if (CFStringFindWithOptions(name, CFSTR(")"),
464 CFRangeMake(mnemonicStart.location + 1,
465 displayLen - mnemonicStart.location - 1),
466 kCFCompareBackwards, &mnemonicEnd) &&
467 (mnemonicStart.location + 2) == mnemonicEnd.location)
469 cleanedName = CFStringCreateMutableCopy(NULL, 0, name);
470 if (cleanedName)
472 CFStringDelete(cleanedName,
473 CFRangeMake(mnemonicStart.location,
474 mnemonicEnd.location + 1 -
475 mnemonicStart.location));
477 CFRelease(name);
478 name = cleanedName;
484 return name;
486 #endif
489 * Convert a list of FSSpec aliases into a list of fullpathname
490 * character strings.
493 char_u **
494 new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error)
496 char_u **fnames = NULL;
497 OSErr newError;
498 long fileCount;
499 FSSpec fileToOpen;
500 long actualSize;
501 AEKeyword dummyKeyword;
502 DescType dummyType;
504 /* Get number of files in list */
505 *error = AECountItems(theList, numFiles);
506 if (*error)
507 return fnames;
509 /* Allocate the pointer list */
510 fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
512 /* Empty out the list */
513 for (fileCount = 0; fileCount < *numFiles; fileCount++)
514 fnames[fileCount] = NULL;
516 /* Scan the list of FSSpec */
517 for (fileCount = 1; fileCount <= *numFiles; fileCount++)
519 /* Get the alias for the nth file, convert to an FSSpec */
520 newError = AEGetNthPtr(theList, fileCount, typeFSS,
521 &dummyKeyword, &dummyType,
522 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize);
523 if (newError)
525 /* Caller is able to clean up */
526 /* TODO: Should be clean up or not? For safety. */
527 return fnames;
530 /* Convert the FSSpec to a pathname */
531 fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen);
534 return (fnames);
538 * ------------------------------------------------------------
539 * CodeWarrior External Editor Support
540 * ------------------------------------------------------------
542 #ifdef FEAT_CW_EDITOR
545 * Handle the Window Search event from CodeWarrior
547 * Description
548 * -----------
550 * The IDE sends the Window Search AppleEvent to the editor when it
551 * needs to know whether a particular file is open in the editor.
553 * Event Reply
554 * -----------
556 * None. Put data in the location specified in the structure received.
558 * Remarks
559 * -------
561 * When the editor receives this event, determine whether the specified
562 * file is open. If it is, return the modification date/time for that file
563 * in the appropriate location specified in the structure. If the file is
564 * not opened, put the value fnfErr(file not found) in that location.
568 typedef struct WindowSearch WindowSearch;
569 struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/
571 FSSpec theFile; // identifies the file
572 long *theDate; // where to put the modification date/time
575 pascal OSErr
576 Handle_KAHL_SRCH_AE(
577 const AppleEvent *theAEvent,
578 AppleEvent *theReply,
579 long refCon)
581 OSErr error = noErr;
582 buf_T *buf;
583 int foundFile = false;
584 DescType typeCode;
585 WindowSearch SearchData;
586 Size actualSize;
588 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize);
589 if (error)
590 return error;
592 error = HandleUnusedParms(theAEvent);
593 if (error)
594 return error;
596 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
597 if (buf->b_ml.ml_mfp != NULL
598 && SearchData.theFile.parID == buf->b_FSSpec.parID
599 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0]
600 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0)
602 foundFile = true;
603 break;
606 if (foundFile == false)
607 *SearchData.theDate = fnfErr;
608 else
609 *SearchData.theDate = buf->b_mtime;
611 return error;
615 * Handle the Modified (from IDE to Editor) event from CodeWarrior
617 * Description
618 * -----------
620 * The IDE sends this event to the external editor when it wants to
621 * know which files that are open in the editor have been modified.
623 * Parameters None.
624 * ----------
626 * Event Reply
627 * -----------
628 * The reply for this event is:
630 * keyDirectObject typeAEList required
631 * each element in the list is a structure of typeChar
633 * Remarks
634 * -------
636 * When building the reply event, include one element in the list for
637 * each open file that has been modified.
641 typedef struct ModificationInfo ModificationInfo;
642 struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/
644 FSSpec theFile; // identifies the file
645 long theDate; // the date/time the file was last modified
646 short saved; // set this to zero when replying, unused
649 pascal OSErr
650 Handle_KAHL_MOD_AE(
651 const AppleEvent *theAEvent,
652 AppleEvent *theReply,
653 long refCon)
655 OSErr error = noErr;
656 AEDescList replyList;
657 long numFiles;
658 ModificationInfo theFile;
659 buf_T *buf;
661 theFile.saved = 0;
663 error = HandleUnusedParms(theAEvent);
664 if (error)
665 return error;
667 /* Send the reply */
668 /* replyObject.descriptorType = typeNull;
669 replyObject.dataHandle = nil;*/
671 /* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */
672 error = AECreateList(nil, 0, false, &replyList);
673 if (error)
674 return error;
676 #if 0
677 error = AECountItems(&replyList, &numFiles);
679 /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
680 * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
681 * sizeof(DescType))
684 /* AEPutDesc */
685 #endif
687 numFiles = 0;
688 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
689 if (buf->b_ml.ml_mfp != NULL)
691 /* Add this file to the list */
692 theFile.theFile = buf->b_FSSpec;
693 theFile.theDate = buf->b_mtime;
694 /* theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */
695 error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile));
698 #if 0
699 error = AECountItems(&replyList, &numFiles);
700 #endif
702 /* We can add data only if something to reply */
703 error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
705 if (replyList.dataHandle)
706 AEDisposeDesc(&replyList);
708 return error;
712 * Handle the Get Text event from CodeWarrior
714 * Description
715 * -----------
717 * The IDE sends the Get Text AppleEvent to the editor when it needs
718 * the source code from a file. For example, when the user issues a
719 * Check Syntax or Compile command, the compiler needs access to
720 * the source code contained in the file.
722 * Event Reply
723 * -----------
725 * None. Put data in locations specified in the structure received.
727 * Remarks
728 * -------
730 * When the editor receives this event, it must set the size of the handle
731 * in theText to fit the data in the file. It must then copy the entire
732 * contents of the specified file into the memory location specified in
733 * theText.
737 typedef struct CW_GetText CW_GetText;
738 struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/
740 FSSpec theFile; /* identifies the file */
741 Handle theText; /* the location where you return the text (must be resized properly) */
742 long *unused; /* 0 (not used) */
743 long *theDate; /* where to put the modification date/time */
746 pascal OSErr
747 Handle_KAHL_GTTX_AE(
748 const AppleEvent *theAEvent,
749 AppleEvent *theReply,
750 long refCon)
752 OSErr error = noErr;
753 buf_T *buf;
754 int foundFile = false;
755 DescType typeCode;
756 CW_GetText GetTextData;
757 Size actualSize;
758 char_u *line;
759 char_u *fullbuffer = NULL;
760 long linesize;
761 long lineStart;
762 long BufferSize;
763 long lineno;
765 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize);
767 if (error)
768 return error;
770 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
771 if (buf->b_ml.ml_mfp != NULL)
772 if (GetTextData.theFile.parID == buf->b_FSSpec.parID)
774 foundFile = true;
775 break;
778 if (foundFile)
780 BufferSize = 0; /* GetHandleSize(GetTextData.theText); */
781 for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++)
783 /* Must use the right buffer */
784 line = ml_get_buf(buf, (linenr_T) lineno, FALSE);
785 linesize = STRLEN(line) + 1;
786 lineStart = BufferSize;
787 BufferSize += linesize;
788 /* Resize handle to linesize+1 to include the linefeed */
789 SetHandleSize(GetTextData.theText, BufferSize);
790 if (GetHandleSize(GetTextData.theText) != BufferSize)
792 break; /* Simple handling for now */
794 else
796 HLock(GetTextData.theText);
797 fullbuffer = (char_u *) *GetTextData.theText;
798 STRCPY((char_u *)(fullbuffer + lineStart), line);
799 fullbuffer[BufferSize-1] = '\r';
800 HUnlock(GetTextData.theText);
803 if (fullbuffer != NULL)
805 HLock(GetTextData.theText);
806 fullbuffer[BufferSize-1] = 0;
807 HUnlock(GetTextData.theText);
809 if (foundFile == false)
810 *GetTextData.theDate = fnfErr;
811 else
812 /* *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/
813 *GetTextData.theDate = buf->b_mtime;
816 error = HandleUnusedParms(theAEvent);
818 return error;
825 /* Taken from MoreAppleEvents:ProcessHelpers*/
826 pascal OSErr
827 FindProcessBySignature(
828 const OSType targetType,
829 const OSType targetCreator,
830 ProcessSerialNumberPtr psnPtr)
832 OSErr anErr = noErr;
833 Boolean lookingForProcess = true;
835 ProcessInfoRec infoRec;
837 infoRec.processInfoLength = sizeof(ProcessInfoRec);
838 infoRec.processName = nil;
839 infoRec.processAppSpec = nil;
841 psnPtr->lowLongOfPSN = kNoProcess;
842 psnPtr->highLongOfPSN = kNoProcess;
844 while (lookingForProcess)
846 anErr = GetNextProcess(psnPtr);
847 if (anErr != noErr)
848 lookingForProcess = false;
849 else
851 anErr = GetProcessInformation(psnPtr, &infoRec);
852 if ((anErr == noErr)
853 && (infoRec.processType == targetType)
854 && (infoRec.processSignature == targetCreator))
855 lookingForProcess = false;
859 return anErr;
860 }//end FindProcessBySignature
862 void
863 Send_KAHL_MOD_AE(buf_T *buf)
865 OSErr anErr = noErr;
866 AEDesc targetAppDesc = { typeNull, nil };
867 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
868 AppleEvent theReply = { typeNull, nil };
869 AESendMode sendMode;
870 AppleEvent theEvent = {typeNull, nil };
871 AEIdleUPP idleProcUPP = nil;
872 ModificationInfo ModData;
875 anErr = FindProcessBySignature('APPL', 'CWIE', &psn);
876 if (anErr == noErr)
878 anErr = AECreateDesc(typeProcessSerialNumber, &psn,
879 sizeof(ProcessSerialNumber), &targetAppDesc);
881 if (anErr == noErr)
883 anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc,
884 kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
887 AEDisposeDesc(&targetAppDesc);
889 /* Add the parms */
890 ModData.theFile = buf->b_FSSpec;
891 ModData.theDate = buf->b_mtime;
893 if (anErr == noErr)
894 anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData));
896 if (idleProcUPP == nil)
897 sendMode = kAENoReply;
898 else
899 sendMode = kAEWaitReply;
901 if (anErr == noErr)
902 anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil);
903 if (anErr == noErr && sendMode == kAEWaitReply)
905 /* anErr = AEHGetHandlerError(&theReply);*/
907 (void) AEDisposeDesc(&theReply);
910 #endif /* FEAT_CW_EDITOR */
913 * ------------------------------------------------------------
914 * Apple Event Handling procedure
915 * ------------------------------------------------------------
917 #ifdef USE_AEVENT
920 * Handle the Unused parms of an AppleEvent
923 OSErr
924 HandleUnusedParms(const AppleEvent *theAEvent)
926 OSErr error;
927 long actualSize;
928 DescType dummyType;
929 AEKeyword missedKeyword;
931 /* Get the "missed keyword" attribute from the AppleEvent. */
932 error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr,
933 typeKeyword, &dummyType,
934 (Ptr)&missedKeyword, sizeof(missedKeyword),
935 &actualSize);
937 /* If the descriptor isn't found, then we got the required parameters. */
938 if (error == errAEDescNotFound)
940 error = noErr;
942 else
944 #if 0
945 /* Why is this removed? */
946 error = errAEEventNotHandled;
947 #endif
950 return error;
955 * Handle the ODoc AppleEvent
957 * Deals with all files dragged to the application icon.
961 typedef struct SelectionRange SelectionRange;
962 struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */
964 short unused1; // 0 (not used)
965 short lineNum; // line to select (<0 to specify range)
966 long startRange; // start of selection range (if line < 0)
967 long endRange; // end of selection range (if line < 0)
968 long unused2; // 0 (not used)
969 long theDate; // modification date/time
972 /* The IDE uses the optional keyAEPosition parameter to tell the ed-
973 itor the selection range. If lineNum is zero or greater, scroll the text
974 to the specified line. If lineNum is less than zero, use the values in
975 startRange and endRange to select the specified characters. Scroll
976 the text to display the selection. If lineNum, startRange, and
977 endRange are all negative, there is no selection range specified.
980 pascal OSErr
981 HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
984 * TODO: Clean up the code with convert the AppleEvent into
985 * a ":args"
987 OSErr error = noErr;
988 // OSErr firstError = noErr;
989 // short numErrors = 0;
990 AEDesc theList;
991 DescType typeCode;
992 long numFiles;
993 // long fileCount;
994 char_u **fnames;
995 // char_u fname[256];
996 Size actualSize;
997 SelectionRange thePosition;
998 short gotPosition = false;
999 long lnum;
1001 /* the direct object parameter is the list of aliases to files (one or more) */
1002 error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
1003 if (error)
1004 return error;
1007 error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize);
1008 if (error == noErr)
1009 gotPosition = true;
1010 if (error == errAEDescNotFound)
1011 error = noErr;
1012 if (error)
1013 return error;
1016 error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
1018 if (^error) then
1020 if (thePosition.lineNum >= 0)
1022 // Goto this line
1024 else
1026 // Set the range char wise
1032 #ifdef FEAT_VISUAL
1033 reset_VIsual();
1034 #endif
1036 fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error);
1038 if (error)
1040 /* TODO: empty fnames[] first */
1041 vim_free(fnames);
1042 return (error);
1045 if (starting > 0)
1047 int i;
1048 char_u *p;
1050 /* these are the initial files dropped on the Vim icon */
1051 for (i = 0 ; i < numFiles; i++)
1053 if (ga_grow(&global_alist.al_ga, 1) == FAIL
1054 || (p = vim_strsave(fnames[i])) == NULL)
1055 mch_exit(2);
1056 else
1057 alist_add(&global_alist, p, 2);
1060 /* Change directory to the location of the first file. */
1061 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1062 shorten_fnames(TRUE);
1064 goto finished;
1067 /* Handle the drop, :edit to get to the file */
1068 handle_drop(numFiles, fnames, FALSE);
1070 /* TODO: Handle the goto/select line more cleanly */
1071 if ((numFiles == 1) & (gotPosition))
1073 if (thePosition.lineNum >= 0)
1075 lnum = thePosition.lineNum + 1;
1076 /* oap->motion_type = MLINE;
1077 setpcmark();*/
1078 if (lnum < 1L)
1079 lnum = 1L;
1080 else if (lnum > curbuf->b_ml.ml_line_count)
1081 lnum = curbuf->b_ml.ml_line_count;
1082 curwin->w_cursor.lnum = lnum;
1083 curwin->w_cursor.col = 0;
1084 /* beginline(BL_SOL | BL_FIX);*/
1086 else
1087 goto_byte(thePosition.startRange + 1);
1090 /* Update the screen display */
1091 update_screen(NOT_VALID);
1092 #ifdef FEAT_VISUAL
1093 /* Select the text if possible */
1094 if (gotPosition)
1096 VIsual_active = TRUE;
1097 VIsual_select = FALSE;
1098 VIsual = curwin->w_cursor;
1099 if (thePosition.lineNum < 0)
1101 VIsual_mode = 'v';
1102 goto_byte(thePosition.endRange);
1104 else
1106 VIsual_mode = 'V';
1107 VIsual.col = 0;
1110 #endif
1111 setcursor();
1112 out_flush();
1114 /* Fake mouse event to wake from stall */
1115 PostEvent(mouseUp, 0);
1117 finished:
1118 AEDisposeDesc(&theList); /* dispose what we allocated */
1120 error = HandleUnusedParms(theAEvent);
1121 return error;
1128 pascal OSErr
1129 Handle_aevt_oapp_AE(
1130 const AppleEvent *theAEvent,
1131 AppleEvent *theReply,
1132 long refCon)
1134 OSErr error = noErr;
1136 error = HandleUnusedParms(theAEvent);
1137 return error;
1144 pascal OSErr
1145 Handle_aevt_quit_AE(
1146 const AppleEvent *theAEvent,
1147 AppleEvent *theReply,
1148 long refCon)
1150 OSErr error = noErr;
1152 error = HandleUnusedParms(theAEvent);
1153 if (error)
1154 return error;
1156 /* Need to fake a :confirm qa */
1157 do_cmdline_cmd((char_u *)"confirm qa");
1159 return error;
1166 pascal OSErr
1167 Handle_aevt_pdoc_AE(
1168 const AppleEvent *theAEvent,
1169 AppleEvent *theReply,
1170 long refCon)
1172 OSErr error = noErr;
1174 error = HandleUnusedParms(theAEvent);
1176 return error;
1180 * Handling of unknown AppleEvent
1182 * (Just get rid of all the parms)
1184 pascal OSErr
1185 Handle_unknown_AE(
1186 const AppleEvent *theAEvent,
1187 AppleEvent *theReply,
1188 long refCon)
1190 OSErr error = noErr;
1192 error = HandleUnusedParms(theAEvent);
1194 return error;
1199 * Install the various AppleEvent Handlers
1201 OSErr
1202 InstallAEHandlers(void)
1204 OSErr error;
1206 /* install open application handler */
1207 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1208 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1209 if (error)
1211 return error;
1214 /* install quit application handler */
1215 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1216 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1217 if (error)
1219 return error;
1222 /* install open document handler */
1223 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1224 NewAEEventHandlerUPP(HandleODocAE), 0, false);
1225 if (error)
1227 return error;
1230 /* install print document handler */
1231 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1232 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1234 /* Install Core Suite */
1235 /* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1236 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1238 error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1239 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1241 error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1242 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1244 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1245 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1247 error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1248 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1250 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1251 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1253 error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1254 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1256 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1257 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1259 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1260 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1262 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1263 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1265 error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1266 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1268 error = AEInstallEventHandler(kAECoreSuite, kAESave,
1269 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1271 error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1272 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1275 #ifdef FEAT_CW_EDITOR
1277 * Bind codewarrior support handlers
1279 error = AEInstallEventHandler('KAHL', 'GTTX',
1280 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1281 if (error)
1283 return error;
1285 error = AEInstallEventHandler('KAHL', 'SRCH',
1286 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1287 if (error)
1289 return error;
1291 error = AEInstallEventHandler('KAHL', 'MOD ',
1292 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1293 if (error)
1295 return error;
1297 #endif
1299 return error;
1302 #endif /* USE_AEVENT */
1306 * Callback function, installed by InstallFontPanelHandler(), below,
1307 * to handle Font Panel events.
1309 static OSStatus
1310 FontPanelHandler(
1311 EventHandlerCallRef inHandlerCallRef,
1312 EventRef inEvent,
1313 void *inUserData)
1315 if (GetEventKind(inEvent) == kEventFontPanelClosed)
1317 gFontPanelInfo.isPanelVisible = false;
1318 return noErr;
1321 if (GetEventKind(inEvent) == kEventFontSelection)
1323 OSStatus status;
1324 FMFontFamily newFamily;
1325 FMFontSize newSize;
1326 FMFontStyle newStyle;
1328 /* Retrieve the font family ID number. */
1329 status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1330 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1331 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1332 &newFamily);
1333 if (status == noErr)
1334 gFontPanelInfo.family = newFamily;
1336 /* Retrieve the font size. */
1337 status = GetEventParameter(inEvent, kEventParamFMFontSize,
1338 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1339 if (status == noErr)
1340 gFontPanelInfo.size = newSize;
1342 /* Retrieve the font style (bold, etc.). Currently unused. */
1343 status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1344 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1345 if (status == noErr)
1346 gFontPanelInfo.style = newStyle;
1348 return noErr;
1352 static void
1353 InstallFontPanelHandler(void)
1355 EventTypeSpec eventTypes[2];
1356 EventHandlerUPP handlerUPP;
1357 /* EventHandlerRef handlerRef; */
1359 eventTypes[0].eventClass = kEventClassFont;
1360 eventTypes[0].eventKind = kEventFontSelection;
1361 eventTypes[1].eventClass = kEventClassFont;
1362 eventTypes[1].eventKind = kEventFontPanelClosed;
1364 handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1366 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1367 /*userData=*/NULL, /*handlerRef=*/NULL);
1372 * Fill the buffer pointed to by outName with the name and size
1373 * of the font currently selected in the Font Panel.
1375 #define FONT_STYLE_BUFFER_SIZE 32
1376 static void
1377 GetFontPanelSelection(char_u *outName)
1379 Str255 buf;
1380 ByteCount fontNameLen = 0;
1381 ATSUFontID fid;
1382 char_u styleString[FONT_STYLE_BUFFER_SIZE];
1384 if (!outName)
1385 return;
1387 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1389 /* Canonicalize localized font names */
1390 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1391 gFontPanelInfo.style, &fid, NULL) != noErr)
1392 return;
1394 /* Request font name with Mac encoding (otherwise we could
1395 * get an unwanted utf-16 name) */
1396 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1397 kFontNoScriptCode, kFontNoLanguageCode,
1398 255, (char *)outName, &fontNameLen, NULL) != noErr)
1399 return;
1401 /* Only encode font size, because style (bold, italic, etc) is
1402 * already part of the font full name */
1403 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1404 gFontPanelInfo.size/*,
1405 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1406 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1407 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1409 if ((fontNameLen + STRLEN(styleString)) < 255)
1410 STRCPY(outName + fontNameLen, styleString);
1412 else
1414 *outName = NUL;
1420 * ------------------------------------------------------------
1421 * Unfiled yet
1422 * ------------------------------------------------------------
1426 * gui_mac_get_menu_item_index
1428 * Returns the index inside the menu wher
1430 short /* Shoulde we return MenuItemIndex? */
1431 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1433 short index;
1434 short itemIndex = -1;
1435 vimmenu_T *pBrother;
1437 /* Only menu without parent are the:
1438 * -menu in the menubar
1439 * -popup menu
1440 * -toolbar (guess)
1442 * Which are not items anyway.
1444 if (pMenu->parent)
1446 /* Start from the Oldest Brother */
1447 pBrother = pMenu->parent->children;
1448 index = 1;
1449 while ((pBrother) && (itemIndex == -1))
1451 if (pBrother == pMenu)
1452 itemIndex = index;
1453 index++;
1454 pBrother = pBrother->next;
1457 return itemIndex;
1460 static vimmenu_T *
1461 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1463 short index;
1464 vimmenu_T *pChildMenu;
1465 vimmenu_T *pElder = pMenu->parent;
1468 /* Only menu without parent are the:
1469 * -menu in the menubar
1470 * -popup menu
1471 * -toolbar (guess)
1473 * Which are not items anyway.
1476 if ((pElder) && (pElder->submenu_id == menuID))
1478 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1479 pMenu = pMenu->next;
1481 else
1483 for (; pMenu != NULL; pMenu = pMenu->next)
1485 if (pMenu->children != NULL)
1487 pChildMenu = gui_mac_get_vim_menu
1488 (menuID, itemIndex, pMenu->children);
1489 if (pChildMenu)
1491 pMenu = pChildMenu;
1492 break;
1497 return pMenu;
1501 * ------------------------------------------------------------
1502 * MacOS Feedback procedures
1503 * ------------------------------------------------------------
1505 pascal
1506 void
1507 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1509 scrollbar_T *sb;
1510 int value, dragging;
1511 ControlHandle theControlToUse;
1512 int dont_scroll_save = dont_scroll;
1514 theControlToUse = dragged_sb;
1516 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1518 if (sb == NULL)
1519 return;
1521 /* Need to find value by diff between Old Poss New Pos */
1522 value = GetControl32BitValue(theControlToUse);
1523 dragging = (partCode != 0);
1525 /* When "allow_scrollbar" is FALSE still need to remember the new
1526 * position, but don't actually scroll by setting "dont_scroll". */
1527 dont_scroll = !allow_scrollbar;
1528 gui_drag_scrollbar(sb, value, dragging);
1529 dont_scroll = dont_scroll_save;
1532 pascal
1533 void
1534 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1536 /* TODO: have live support */
1537 scrollbar_T *sb, *sb_info;
1538 long data;
1539 long value;
1540 int page;
1541 int dragging = FALSE;
1542 int dont_scroll_save = dont_scroll;
1544 sb = gui_find_scrollbar((long)GetControlReference(theControl));
1546 if (sb == NULL)
1547 return;
1549 if (sb->wp != NULL) /* Left or right scrollbar */
1552 * Careful: need to get scrollbar info out of first (left) scrollbar
1553 * for window, but keep real scrollbar too because we must pass it to
1554 * gui_drag_scrollbar().
1556 sb_info = &sb->wp->w_scrollbars[0];
1558 if (sb_info->size > 5)
1559 page = sb_info->size - 2; /* use two lines of context */
1560 else
1561 page = sb_info->size;
1563 else /* Bottom scrollbar */
1565 sb_info = sb;
1566 page = W_WIDTH(curwin) - 5;
1569 switch (partCode)
1571 case kControlUpButtonPart: data = -1; break;
1572 case kControlDownButtonPart: data = 1; break;
1573 case kControlPageDownPart: data = page; break;
1574 case kControlPageUpPart: data = -page; break;
1575 default: data = 0; break;
1578 value = sb_info->value + data;
1579 /* if (value > sb_info->max)
1580 value = sb_info->max;
1581 else if (value < 0)
1582 value = 0;*/
1584 /* When "allow_scrollbar" is FALSE still need to remember the new
1585 * position, but don't actually scroll by setting "dont_scroll". */
1586 dont_scroll = !allow_scrollbar;
1587 gui_drag_scrollbar(sb, value, dragging);
1588 dont_scroll = dont_scroll_save;
1590 out_flush();
1591 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1593 /* if (sb_info->wp != NULL)
1595 win_T *wp;
1596 int sb_num;
1598 sb_num = 0;
1599 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1600 sb_num++;
1602 if (wp != NULL)
1604 current_scrollbar = sb_num;
1605 scrollbar_value = value;
1606 gui_do_scroll();
1607 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1613 * ------------------------------------------------------------
1614 * MacOS Click Handling procedures
1615 * ------------------------------------------------------------
1620 * Handle a click inside the window, it may happens in the
1621 * scrollbar or the contents.
1623 * TODO: Add support for potential TOOLBAR
1625 void
1626 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1628 Point thePoint;
1629 int_u vimModifiers;
1630 short thePortion;
1631 ControlHandle theControl;
1632 int vimMouseButton;
1633 short dblClick;
1635 thePoint = theEvent->where;
1636 GlobalToLocal(&thePoint);
1637 SelectWindow(whichWindow);
1639 thePortion = FindControl(thePoint, whichWindow, &theControl);
1641 if (theControl != NUL)
1643 /* We hit a scollbar */
1645 if (thePortion != kControlIndicatorPart)
1647 dragged_sb = theControl;
1648 TrackControl(theControl, thePoint, gScrollAction);
1649 dragged_sb = NULL;
1651 else
1653 dragged_sb = theControl;
1654 #if 1
1655 TrackControl(theControl, thePoint, gScrollDrag);
1656 #else
1657 TrackControl(theControl, thePoint, NULL);
1658 #endif
1659 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1660 * button has been released */
1661 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1662 dragged_sb = NULL;
1665 else
1667 /* We are inside the contents */
1669 /* Convert the CTRL, OPTION, SHIFT and CMD key */
1670 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1672 /* Defaults to MOUSE_LEFT as there's only one mouse button */
1673 vimMouseButton = MOUSE_LEFT;
1675 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1676 /* TODO: NEEDED? */
1677 clickIsPopup = FALSE;
1679 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1681 vimMouseButton = MOUSE_RIGHT;
1682 vimModifiers &= ~MOUSE_CTRL;
1683 clickIsPopup = TRUE;
1686 /* Is it a double click ? */
1687 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1689 /* Send the mouse click to Vim */
1690 gui_send_mouse_event(vimMouseButton, thePoint.h,
1691 thePoint.v, dblClick, vimModifiers);
1693 /* Create the rectangle around the cursor to detect
1694 * the mouse dragging
1696 #if 0
1697 /* TODO: Do we need to this even for the contextual menu?
1698 * It may be require for popup_setpos, but for popup?
1700 if (vimMouseButton == MOUSE_LEFT)
1701 #endif
1703 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1704 FILL_Y(Y_2_ROW(thePoint.v)),
1705 FILL_X(X_2_COL(thePoint.h)+1),
1706 FILL_Y(Y_2_ROW(thePoint.v)+1));
1708 dragRectEnbl = TRUE;
1709 dragRectControl = kCreateRect;
1715 * Handle the click in the titlebar (to move the window)
1717 void
1718 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1720 Rect movingLimits;
1721 Rect *movingLimitsPtr = &movingLimits;
1723 /* TODO: may try to prevent move outside screen? */
1724 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1725 DragWindow(whichWindow, where, movingLimitsPtr);
1729 * Handle the click in the grow box
1731 void
1732 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1735 long newSize;
1736 unsigned short newWidth;
1737 unsigned short newHeight;
1738 Rect resizeLimits;
1739 Rect *resizeLimitsPtr = &resizeLimits;
1740 Rect NewContentRect;
1742 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1744 /* Set the minimum size */
1745 /* TODO: Should this come from Vim? */
1746 resizeLimits.top = 100;
1747 resizeLimits.left = 100;
1749 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1750 newWidth = NewContentRect.right - NewContentRect.left;
1751 newHeight = NewContentRect.bottom - NewContentRect.top;
1752 gui_resize_shell(newWidth, newHeight);
1753 gui_mch_set_bg_color(gui.back_pixel);
1754 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1758 * Handle the click in the zoom box
1760 static void
1761 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1763 Rect r;
1764 Point p;
1765 short thePart;
1767 /* ideal width is current */
1768 p.h = Columns * gui.char_width + 2 * gui.border_offset;
1769 if (gui.which_scrollbars[SBAR_LEFT])
1770 p.h += gui.scrollbar_width;
1771 if (gui.which_scrollbars[SBAR_RIGHT])
1772 p.h += gui.scrollbar_width;
1773 /* ideal height is as heigh as we can get */
1774 p.v = 15 * 1024;
1776 thePart = IsWindowInStandardState(whichWindow, &p, &r)
1777 ? inZoomIn : inZoomOut;
1779 if (!TrackBox(whichWindow, theEvent->where, thePart))
1780 return;
1782 /* use returned width */
1783 p.h = r.right - r.left;
1784 /* adjust returned height */
1785 p.v = r.bottom - r.top - 2 * gui.border_offset;
1786 if (gui.which_scrollbars[SBAR_BOTTOM])
1787 p.v -= gui.scrollbar_height;
1788 p.v -= p.v % gui.char_height;
1789 p.v += 2 * gui.border_width;
1790 if (gui.which_scrollbars[SBAR_BOTTOM]);
1791 p.v += gui.scrollbar_height;
1793 ZoomWindowIdeal(whichWindow, thePart, &p);
1795 GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1796 gui_resize_shell(r.right - r.left, r.bottom - r.top);
1797 gui_mch_set_bg_color(gui.back_pixel);
1798 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1802 * ------------------------------------------------------------
1803 * MacOS Event Handling procedure
1804 * ------------------------------------------------------------
1808 * Handle the Update Event
1811 void
1812 gui_mac_doUpdateEvent(EventRecord *event)
1814 WindowPtr whichWindow;
1815 GrafPtr savePort;
1816 RgnHandle updateRgn;
1817 Rect updateRect;
1818 Rect *updateRectPtr;
1819 Rect rc;
1820 Rect growRect;
1821 RgnHandle saveRgn;
1824 updateRgn = NewRgn();
1825 if (updateRgn == NULL)
1826 return;
1828 /* This could be done by the caller as we
1829 * don't require anything else out of the event
1831 whichWindow = (WindowPtr) event->message;
1833 /* Save Current Port */
1834 GetPort(&savePort);
1836 /* Select the Window's Port */
1837 SetPortWindowPort(whichWindow);
1839 /* Let's update the window */
1840 BeginUpdate(whichWindow);
1841 /* Redraw the biggest rectangle covering the area
1842 * to be updated.
1844 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1845 # if 0
1846 /* Would be more appropriate to use the following but doesn't
1847 * seem to work under MacOS X (Dany)
1849 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1850 # endif
1852 /* Use the HLock useless in Carbon? Is it harmful?*/
1853 HLock((Handle) updateRgn);
1855 updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1856 # if 0
1857 /* Code from original Carbon Port (using GetWindowRegion.
1858 * I believe the UpdateRgn is already in local (Dany)
1860 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1861 GlobalToLocal(&botRight(updateRect));
1862 # endif
1863 /* Update the content (i.e. the text) */
1864 gui_redraw(updateRectPtr->left, updateRectPtr->top,
1865 updateRectPtr->right - updateRectPtr->left,
1866 updateRectPtr->bottom - updateRectPtr->top);
1867 /* Clear the border areas if needed */
1868 gui_mch_set_bg_color(gui.back_pixel);
1869 if (updateRectPtr->left < FILL_X(0))
1871 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1872 EraseRect(&rc);
1874 if (updateRectPtr->top < FILL_Y(0))
1876 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1877 EraseRect(&rc);
1879 if (updateRectPtr->right > FILL_X(Columns))
1881 SetRect(&rc, FILL_X(Columns), 0,
1882 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1883 EraseRect(&rc);
1885 if (updateRectPtr->bottom > FILL_Y(Rows))
1887 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1888 FILL_Y(Rows) + gui.border_offset);
1889 EraseRect(&rc);
1891 HUnlock((Handle) updateRgn);
1892 DisposeRgn(updateRgn);
1894 /* Update scrollbars */
1895 DrawControls(whichWindow);
1897 /* Update the GrowBox */
1898 /* Taken from FAQ 33-27 */
1899 saveRgn = NewRgn();
1900 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1901 GetClip(saveRgn);
1902 ClipRect(&growRect);
1903 DrawGrowIcon(whichWindow);
1904 SetClip(saveRgn);
1905 DisposeRgn(saveRgn);
1906 EndUpdate(whichWindow);
1908 /* Restore original Port */
1909 SetPort(savePort);
1913 * Handle the activate/deactivate event
1914 * (apply to a window)
1916 void
1917 gui_mac_doActivateEvent(EventRecord *event)
1919 WindowPtr whichWindow;
1921 whichWindow = (WindowPtr) event->message;
1922 /* Dim scrollbars */
1923 if (whichWindow == gui.VimWindow)
1925 ControlRef rootControl;
1926 GetRootControl(gui.VimWindow, &rootControl);
1927 if ((event->modifiers) & activeFlag)
1928 ActivateControl(rootControl);
1929 else
1930 DeactivateControl(rootControl);
1933 /* Activate */
1934 gui_focus_change((event->modifiers) & activeFlag);
1939 * Handle the suspend/resume event
1940 * (apply to the application)
1942 void
1943 gui_mac_doSuspendEvent(EventRecord *event)
1945 /* The frontmost application just changed */
1947 /* NOTE: the suspend may happen before the deactivate
1948 * seen on MacOS X
1951 /* May not need to change focus as the window will
1952 * get an activate/deactivate event
1954 if (event->message & 1)
1955 /* Resume */
1956 gui_focus_change(TRUE);
1957 else
1958 /* Suspend */
1959 gui_focus_change(FALSE);
1963 * Handle the key
1965 #ifdef USE_CARBONKEYHANDLER
1967 static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the keys */
1969 # define INLINE_KEY_BUFFER_SIZE 80
1970 static pascal OSStatus
1971 gui_mac_doKeyEventCarbon(
1972 EventHandlerCallRef nextHandler,
1973 EventRef theEvent,
1974 void *data)
1976 /* Multibyte-friendly key event handler */
1977 OSStatus err = -1;
1978 UInt32 actualSize;
1979 UniChar *text;
1980 char_u result[INLINE_KEY_BUFFER_SIZE];
1981 short len = 0;
1982 UInt32 key_sym;
1983 char charcode;
1984 int key_char;
1985 UInt32 modifiers, vimModifiers;
1986 size_t encLen;
1987 char_u *to = NULL;
1988 Boolean isSpecial = FALSE;
1989 int i;
1990 EventRef keyEvent;
1992 /* Mask the mouse (as per user setting) */
1993 if (p_mh)
1994 ObscureCursor();
1996 /* Don't use the keys when the dialog wants them. */
1997 if (dialog_busy)
1998 return eventNotHandledErr;
2000 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2001 typeUnicodeText, NULL, 0, &actualSize, NULL))
2002 return eventNotHandledErr;
2004 text = (UniChar *)alloc(actualSize);
2005 if (!text)
2006 return eventNotHandledErr;
2008 err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2009 typeUnicodeText, NULL, actualSize, NULL, text);
2010 require_noerr(err, done);
2012 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2013 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2014 require_noerr(err, done);
2016 err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2017 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2018 require_noerr(err, done);
2020 err = GetEventParameter(keyEvent, kEventParamKeyCode,
2021 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2022 require_noerr(err, done);
2024 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2025 typeChar, NULL, sizeof(char), NULL, &charcode);
2026 require_noerr(err, done);
2028 #ifndef USE_CMD_KEY
2029 if (modifiers & cmdKey)
2030 goto done; /* Let system handle Cmd+... */
2031 #endif
2033 key_char = charcode;
2034 vimModifiers = EventModifiers2VimModifiers(modifiers);
2036 /* Find the special key (eg., for cursor keys) */
2037 if (actualSize <= sizeof(UniChar) &&
2038 ((text[0] < 0x20) || (text[0] == 0x7f)))
2040 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2041 if (special_keys[i].key_sym == key_sym)
2043 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2044 special_keys[i].vim_code1);
2045 key_char = simplify_key(key_char,
2046 (int *)&vimModifiers);
2047 isSpecial = TRUE;
2048 break;
2052 /* Intercept CMD-. and CTRL-c */
2053 if (((modifiers & controlKey) && key_char == 'c') ||
2054 ((modifiers & cmdKey) && key_char == '.'))
2055 got_int = TRUE;
2057 if (!isSpecial)
2059 /* remove SHIFT for keys that are already shifted, e.g.,
2060 * '(' and '*' */
2061 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2062 vimModifiers &= ~MOD_MASK_SHIFT;
2064 /* remove CTRL from keys that already have it */
2065 if (key_char < 0x20)
2066 vimModifiers &= ~MOD_MASK_CTRL;
2068 /* don't process unicode characters here */
2069 if (!IS_SPECIAL(key_char))
2071 /* Following code to simplify and consolidate vimModifiers
2072 * taken liberally from gui_w48.c */
2073 key_char = simplify_key(key_char, (int *)&vimModifiers);
2075 /* Interpret META, include SHIFT, etc. */
2076 key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2077 if (key_char == CSI)
2078 key_char = K_CSI;
2080 if (IS_SPECIAL(key_char))
2081 isSpecial = TRUE;
2085 if (vimModifiers)
2087 result[len++] = CSI;
2088 result[len++] = KS_MODIFIER;
2089 result[len++] = vimModifiers;
2092 if (isSpecial && IS_SPECIAL(key_char))
2094 result[len++] = CSI;
2095 result[len++] = K_SECOND(key_char);
2096 result[len++] = K_THIRD(key_char);
2098 else
2100 encLen = actualSize;
2101 to = mac_utf16_to_enc(text, actualSize, &encLen);
2102 if (to)
2104 /* This is basically add_to_input_buf_csi() */
2105 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2107 result[len++] = to[i];
2108 if (to[i] == CSI)
2110 result[len++] = KS_EXTRA;
2111 result[len++] = (int)KE_CSI;
2114 vim_free(to);
2118 add_to_input_buf(result, len);
2119 err = noErr;
2121 done:
2122 vim_free(text);
2123 if (err == noErr)
2125 /* Fake event to wake up WNE (required to get
2126 * key repeat working */
2127 PostEvent(keyUp, 0);
2128 return noErr;
2131 return eventNotHandledErr;
2133 #else
2134 void
2135 gui_mac_doKeyEvent(EventRecord *theEvent)
2137 /* TODO: add support for COMMAND KEY */
2138 long menu;
2139 unsigned char string[20];
2140 short num, i;
2141 short len = 0;
2142 KeySym key_sym;
2143 int key_char;
2144 int modifiers;
2145 int simplify = FALSE;
2147 /* Mask the mouse (as per user setting) */
2148 if (p_mh)
2149 ObscureCursor();
2151 /* Get the key code and it's ASCII representation */
2152 key_sym = ((theEvent->message & keyCodeMask) >> 8);
2153 key_char = theEvent->message & charCodeMask;
2154 num = 1;
2156 /* Intercept CTRL-C */
2157 if (theEvent->modifiers & controlKey)
2159 if (key_char == Ctrl_C && ctrl_c_interrupts)
2160 got_int = TRUE;
2161 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2162 && (key_char == '2' || key_char == '6'))
2164 /* CTRL-^ and CTRL-@ don't work in the normal way. */
2165 if (key_char == '2')
2166 key_char = Ctrl_AT;
2167 else
2168 key_char = Ctrl_HAT;
2169 theEvent->modifiers = 0;
2173 /* Intercept CMD-. */
2174 if (theEvent->modifiers & cmdKey)
2175 if (key_char == '.')
2176 got_int = TRUE;
2178 /* Handle command key as per menu */
2179 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2180 if (theEvent->modifiers & cmdKey)
2181 /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2182 * Why the mouse button? */
2183 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2185 menu = MenuKey(key_char);
2186 if (HiWord(menu))
2188 gui_mac_handle_menu(menu);
2189 return;
2193 /* Convert the modifiers */
2194 modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2197 /* Handle special keys. */
2198 #if 0
2199 /* Why has this been removed? */
2200 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2201 #endif
2203 /* Find the special key (for non-printable keyt_char) */
2204 if ((key_char < 0x20) || (key_char == 0x7f))
2205 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2206 if (special_keys[i].key_sym == key_sym)
2208 # if 0
2209 /* We currently don't have not so special key */
2210 if (special_keys[i].vim_code1 == NUL)
2211 key_char = special_keys[i].vim_code0;
2212 else
2213 # endif
2214 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2215 special_keys[i].vim_code1);
2216 simplify = TRUE;
2217 break;
2221 /* For some keys the modifier is included in the char itself. */
2222 if (simplify || key_char == TAB || key_char == ' ')
2223 key_char = simplify_key(key_char, &modifiers);
2225 /* Add the modifier to the input bu if needed */
2226 /* Do not want SHIFT-A or CTRL-A with modifier */
2227 if (!IS_SPECIAL(key_char)
2228 && key_sym != vk_Space
2229 && key_sym != vk_Tab
2230 && key_sym != vk_Return
2231 && key_sym != vk_Enter
2232 && key_sym != vk_Esc)
2234 #if 1
2235 /* Clear modifiers when only one modifier is set */
2236 if ((modifiers == MOD_MASK_SHIFT)
2237 || (modifiers == MOD_MASK_CTRL)
2238 || (modifiers == MOD_MASK_ALT))
2239 modifiers = 0;
2240 #else
2241 if (modifiers & MOD_MASK_CTRL)
2242 modifiers = modifiers & ~MOD_MASK_CTRL;
2243 if (modifiers & MOD_MASK_ALT)
2244 modifiers = modifiers & ~MOD_MASK_ALT;
2245 if (modifiers & MOD_MASK_SHIFT)
2246 modifiers = modifiers & ~MOD_MASK_SHIFT;
2247 #endif
2249 if (modifiers)
2251 string[len++] = CSI;
2252 string[len++] = KS_MODIFIER;
2253 string[len++] = modifiers;
2256 if (IS_SPECIAL(key_char))
2258 string[len++] = CSI;
2259 string[len++] = K_SECOND(key_char);
2260 string[len++] = K_THIRD(key_char);
2262 else
2264 #ifdef FEAT_MBYTE
2265 /* Convert characters when needed (e.g., from MacRoman to latin1).
2266 * This doesn't work for the NUL byte. */
2267 if (input_conv.vc_type != CONV_NONE && key_char > 0)
2269 char_u from[2], *to;
2270 int l;
2272 from[0] = key_char;
2273 from[1] = NUL;
2274 l = 1;
2275 to = string_convert(&input_conv, from, &l);
2276 if (to != NULL)
2278 for (i = 0; i < l && len < 19; i++)
2280 if (to[i] == CSI)
2282 string[len++] = KS_EXTRA;
2283 string[len++] = KE_CSI;
2285 else
2286 string[len++] = to[i];
2288 vim_free(to);
2290 else
2291 string[len++] = key_char;
2293 else
2294 #endif
2295 string[len++] = key_char;
2298 if (len == 1 && string[0] == CSI)
2300 /* Turn CSI into K_CSI. */
2301 string[ len++ ] = KS_EXTRA;
2302 string[ len++ ] = KE_CSI;
2305 add_to_input_buf(string, len);
2307 #endif
2310 * Handle MouseClick
2312 void
2313 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2315 short thePart;
2316 WindowPtr whichWindow;
2318 thePart = FindWindow(theEvent->where, &whichWindow);
2320 #ifdef FEAT_GUI_TABLINE
2321 /* prevent that the vim window size changes if it's activated by a
2322 click into the tab pane */
2323 if (whichWindow == drawer)
2324 return;
2325 #endif
2327 switch (thePart)
2329 case (inDesk):
2330 /* TODO: what to do? */
2331 break;
2333 case (inMenuBar):
2334 gui_mac_handle_menu(MenuSelect(theEvent->where));
2335 break;
2337 case (inContent):
2338 gui_mac_doInContentClick(theEvent, whichWindow);
2339 break;
2341 case (inDrag):
2342 gui_mac_doInDragClick(theEvent->where, whichWindow);
2343 break;
2345 case (inGrow):
2346 gui_mac_doInGrowClick(theEvent->where, whichWindow);
2347 break;
2349 case (inGoAway):
2350 if (TrackGoAway(whichWindow, theEvent->where))
2351 gui_shell_closed();
2352 break;
2354 case (inZoomIn):
2355 case (inZoomOut):
2356 gui_mac_doInZoomClick(theEvent, whichWindow);
2357 break;
2362 * Handle MouseMoved
2363 * [this event is a moving in and out of a region]
2365 void
2366 gui_mac_doMouseMovedEvent(EventRecord *event)
2368 Point thePoint;
2369 int_u vimModifiers;
2371 thePoint = event->where;
2372 GlobalToLocal(&thePoint);
2373 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2375 if (!Button())
2376 gui_mouse_moved(thePoint.h, thePoint.v);
2377 else
2378 if (!clickIsPopup)
2379 gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2380 thePoint.v, FALSE, vimModifiers);
2382 /* Reset the region from which we move in and out */
2383 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2384 FILL_Y(Y_2_ROW(thePoint.v)),
2385 FILL_X(X_2_COL(thePoint.h)+1),
2386 FILL_Y(Y_2_ROW(thePoint.v)+1));
2388 if (dragRectEnbl)
2389 dragRectControl = kCreateRect;
2394 * Handle the mouse release
2396 void
2397 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2399 Point thePoint;
2400 int_u vimModifiers;
2402 /* TODO: Properly convert the Contextual menu mouse-up */
2403 /* Potential source of the double menu */
2404 lastMouseTick = theEvent->when;
2405 dragRectEnbl = FALSE;
2406 dragRectControl = kCreateEmpty;
2407 thePoint = theEvent->where;
2408 GlobalToLocal(&thePoint);
2410 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2411 if (clickIsPopup)
2413 vimModifiers &= ~MOUSE_CTRL;
2414 clickIsPopup = FALSE;
2416 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2419 static pascal OSStatus
2420 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2421 void *data)
2423 EventRef bogusEvent;
2424 Point point;
2425 Rect bounds;
2426 UInt32 mod;
2427 SInt32 delta;
2428 int_u vim_mod;
2429 EventMouseWheelAxis axis;
2431 if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis,
2432 typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis)
2433 && axis != kEventMouseWheelAxisY)
2434 goto bail; /* Vim only does up-down scrolling */
2436 if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta,
2437 typeSInt32, NULL, sizeof(SInt32), NULL, &delta))
2438 goto bail;
2439 if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation,
2440 typeQDPoint, NULL, sizeof(Point), NULL, &point))
2441 goto bail;
2442 if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers,
2443 typeUInt32, NULL, sizeof(UInt32), NULL, &mod))
2444 goto bail;
2446 vim_mod = 0;
2447 if (mod & shiftKey)
2448 vim_mod |= MOUSE_SHIFT;
2449 if (mod & controlKey)
2450 vim_mod |= MOUSE_CTRL;
2451 if (mod & optionKey)
2452 vim_mod |= MOUSE_ALT;
2454 /* post a bogus event to wake up WaitNextEvent */
2455 if (noErr != CreateEvent(NULL, kEventClassMouse, kEventMouseMoved, 0,
2456 kEventAttributeNone, &bogusEvent))
2457 goto bail;
2458 if (noErr != PostEventToQueue(GetMainEventQueue(), bogusEvent,
2459 kEventPriorityLow))
2460 goto bail;
2462 ReleaseEvent(bogusEvent);
2464 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2466 point.h -= bounds.left;
2467 point.v -= bounds.top;
2470 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2471 point.h, point.v, FALSE, vim_mod);
2473 return noErr;
2475 bail:
2477 * when we fail give any additional callback handler a chance to perform
2478 * it's actions
2480 return CallNextEventHandler(nextHandler, theEvent);
2483 #if 0
2486 * This would be the normal way of invoking the contextual menu
2487 * but the Vim API doesn't seem to a support a request to get
2488 * the menu that we should display
2490 void
2491 gui_mac_handle_contextual_menu(event)
2492 EventRecord *event;
2495 * Clone PopUp to use menu
2496 * Create a object descriptor for the current selection
2497 * Call the procedure
2500 // Call to Handle Popup
2501 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2503 if (status != noErr)
2504 return;
2506 if (CntxType == kCMMenuItemSelected)
2508 /* Handle the menu CntxMenuID, CntxMenuItem */
2509 /* The submenu can be handle directly by gui_mac_handle_menu */
2510 /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2511 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2513 else if (CntxMenuID == kCMShowHelpSelected)
2515 /* Should come up with the help */
2519 #endif
2522 * Handle menubar selection
2524 void
2525 gui_mac_handle_menu(long menuChoice)
2527 short menu = HiWord(menuChoice);
2528 short item = LoWord(menuChoice);
2529 vimmenu_T *theVimMenu = root_menu;
2531 if (menu == 256) /* TODO: use constant or gui.xyz */
2533 if (item == 1)
2534 gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2536 else if (item != 0)
2538 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2540 if (theVimMenu)
2541 gui_menu_cb(theVimMenu);
2543 HiliteMenu(0);
2547 * Dispatch the event to proper handler
2550 void
2551 gui_mac_handle_event(EventRecord *event)
2553 OSErr error;
2555 /* Handle contextual menu right now (if needed) */
2556 if (IsShowContextualMenuClick(event))
2558 # if 0
2559 gui_mac_handle_contextual_menu(event);
2560 # else
2561 gui_mac_doMouseDownEvent(event);
2562 # endif
2563 return;
2566 /* Handle normal event */
2567 switch (event->what)
2569 #ifndef USE_CARBONKEYHANDLER
2570 case (keyDown):
2571 case (autoKey):
2572 gui_mac_doKeyEvent(event);
2573 break;
2574 #endif
2575 case (keyUp):
2576 /* We don't care about when the key is released */
2577 break;
2579 case (mouseDown):
2580 gui_mac_doMouseDownEvent(event);
2581 break;
2583 case (mouseUp):
2584 gui_mac_doMouseUpEvent(event);
2585 break;
2587 case (updateEvt):
2588 gui_mac_doUpdateEvent(event);
2589 break;
2591 case (diskEvt):
2592 /* We don't need special handling for disk insertion */
2593 break;
2595 case (activateEvt):
2596 gui_mac_doActivateEvent(event);
2597 break;
2599 case (osEvt):
2600 switch ((event->message >> 24) & 0xFF)
2602 case (0xFA): /* mouseMovedMessage */
2603 gui_mac_doMouseMovedEvent(event);
2604 break;
2605 case (0x01): /* suspendResumeMessage */
2606 gui_mac_doSuspendEvent(event);
2607 break;
2609 break;
2611 #ifdef USE_AEVENT
2612 case (kHighLevelEvent):
2613 /* Someone's talking to us, through AppleEvents */
2614 error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2615 break;
2616 #endif
2621 * ------------------------------------------------------------
2622 * Unknown Stuff
2623 * ------------------------------------------------------------
2627 GuiFont
2628 gui_mac_find_font(char_u *font_name)
2630 char_u c;
2631 char_u *p;
2632 char_u pFontName[256];
2633 Str255 systemFontname;
2634 short font_id;
2635 short size=9;
2636 GuiFont font;
2637 #if 0
2638 char_u *fontNamePtr;
2639 #endif
2641 for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2644 c = *p;
2645 *p = 0;
2647 #if 1
2648 STRCPY(&pFontName[1], font_name);
2649 pFontName[0] = STRLEN(font_name);
2650 *p = c;
2652 /* Get the font name, minus the style suffix (:h, etc) */
2653 char_u fontName[256];
2654 char_u *styleStart = vim_strchr(font_name, ':');
2655 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2656 vim_strncpy(fontName, font_name, fontNameLen);
2658 ATSUFontID fontRef;
2659 FMFontStyle fontStyle;
2660 font_id = 0;
2662 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2663 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2664 &fontRef) == noErr)
2666 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2667 font_id = 0;
2670 if (font_id == 0)
2673 * Try again, this time replacing underscores in the font name
2674 * with spaces (:set guifont allows the two to be used
2675 * interchangeably; the Font Manager doesn't).
2677 int i, changed = FALSE;
2679 for (i = pFontName[0]; i > 0; --i)
2681 if (pFontName[i] == '_')
2683 pFontName[i] = ' ';
2684 changed = TRUE;
2687 if (changed)
2688 if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2689 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2690 kFontNoLanguageCode, &fontRef) == noErr)
2692 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2693 font_id = 0;
2697 #else
2698 /* name = C2Pascal_save(menu->dname); */
2699 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2701 GetFNum(fontNamePtr, &font_id);
2702 #endif
2705 if (font_id == 0)
2707 /* Oups, the system font was it the one the user want */
2709 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2710 return NOFONT;
2711 if (!EqualString(pFontName, systemFontname, false, false))
2712 return NOFONT;
2714 if (*p == ':')
2716 p++;
2717 /* Set the values found after ':' */
2718 while (*p)
2720 switch (*p++)
2722 case 'h':
2723 size = points_to_pixels(p, &p, TRUE);
2724 break;
2726 * TODO: Maybe accept width and styles
2729 while (*p == ':')
2730 p++;
2734 if (size < 1)
2735 size = 1; /* Avoid having a size of 0 with system font */
2737 font = (size << 16) + ((long) font_id & 0xFFFF);
2739 return font;
2743 * ------------------------------------------------------------
2744 * GUI_MCH functionality
2745 * ------------------------------------------------------------
2749 * Parse the GUI related command-line arguments. Any arguments used are
2750 * deleted from argv, and *argc is decremented accordingly. This is called
2751 * when vim is started, whether or not the GUI has been started.
2753 void
2754 gui_mch_prepare(int *argc, char **argv)
2756 /* TODO: Move most of this stuff toward gui_mch_init */
2757 #ifdef USE_EXE_NAME
2758 FSSpec applDir;
2759 # ifndef USE_FIND_BUNDLE_PATH
2760 short applVRefNum;
2761 long applDirID;
2762 Str255 volName;
2763 # else
2764 ProcessSerialNumber psn;
2765 FSRef applFSRef;
2766 # endif
2767 #endif
2769 #if 0
2770 InitCursor();
2772 RegisterAppearanceClient();
2774 #ifdef USE_AEVENT
2775 (void) InstallAEHandlers();
2776 #endif
2778 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2780 AppendMenu(pomme, "\pAbout VIM");
2782 InsertMenu(pomme, 0);
2784 DrawMenuBar();
2787 #ifndef USE_OFFSETED_WINDOW
2788 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2789 #else
2790 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2791 #endif
2794 CreateNewWindow(kDocumentWindowClass,
2795 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2796 &windRect, &gui.VimWindow);
2797 SetPortWindowPort(gui.VimWindow);
2799 gui.char_width = 7;
2800 gui.char_height = 11;
2801 gui.char_ascent = 6;
2802 gui.num_rows = 24;
2803 gui.num_cols = 80;
2804 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2806 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2807 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2809 dragRectEnbl = FALSE;
2810 dragRgn = NULL;
2811 dragRectControl = kCreateEmpty;
2812 cursorRgn = NewRgn();
2813 #endif
2814 #ifdef USE_EXE_NAME
2815 # ifndef USE_FIND_BUNDLE_PATH
2816 HGetVol(volName, &applVRefNum, &applDirID);
2817 /* TN2015: mention a possible bad VRefNum */
2818 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2819 # else
2820 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2821 * of TN2015
2822 * This technic remove the ../Contents/MacOS/etc part
2824 (void)GetCurrentProcess(&psn);
2825 /* if (err != noErr) return err; */
2827 (void)GetProcessBundleLocation(&psn, &applFSRef);
2828 /* if (err != noErr) return err; */
2830 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2832 /* This technic return NIL when we disallow_gui */
2833 # endif
2834 exe_name = FullPathFromFSSpec_save(applDir);
2835 #endif
2838 #ifndef ALWAYS_USE_GUI
2840 * Check if the GUI can be started. Called before gvimrc is sourced.
2841 * Return OK or FAIL.
2844 gui_mch_init_check(void)
2846 /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2847 * using the >console
2849 if (disallow_gui) /* see main.c for reason to disallow */
2850 return FAIL;
2851 return OK;
2853 #endif
2855 static OSErr
2856 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2858 int x, y;
2859 int_u modifiers;
2860 char_u **fnames = NULL;
2861 int count;
2862 int i, j;
2864 /* Get drop position, modifiers and count of items */
2866 Point point;
2867 SInt16 mouseUpModifiers;
2868 UInt16 countItem;
2870 GetDragMouse(theDrag, &point, NULL);
2871 GlobalToLocal(&point);
2872 x = point.h;
2873 y = point.v;
2874 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2875 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2876 CountDragItems(theDrag, &countItem);
2877 count = countItem;
2880 fnames = (char_u **)alloc(count * sizeof(char_u *));
2881 if (fnames == NULL)
2882 return dragNotAcceptedErr;
2884 /* Get file names dropped */
2885 for (i = j = 0; i < count; ++i)
2887 DragItemRef item;
2888 OSErr err;
2889 Size size;
2890 FlavorType type = flavorTypeHFS;
2891 HFSFlavor hfsFlavor;
2893 fnames[i] = NULL;
2894 GetDragItemReferenceNumber(theDrag, i + 1, &item);
2895 err = GetFlavorDataSize(theDrag, item, type, &size);
2896 if (err != noErr || size > sizeof(hfsFlavor))
2897 continue;
2898 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
2899 if (err != noErr)
2900 continue;
2901 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
2903 count = j;
2905 gui_handle_drop(x, y, modifiers, fnames, count);
2907 /* Fake mouse event to wake from stall */
2908 PostEvent(mouseUp, 0);
2910 return noErr;
2914 * Initialise the GUI. Create all the windows, set up all the call-backs
2915 * etc.
2918 gui_mch_init(void)
2920 /* TODO: Move most of this stuff toward gui_mch_init */
2921 Rect windRect;
2922 MenuHandle pomme;
2923 long gestalt_rc;
2924 EventTypeSpec eventTypeSpec;
2925 EventHandlerRef mouseWheelHandlerRef;
2926 #ifdef USE_CARBONKEYHANDLER
2927 EventHandlerRef keyEventHandlerRef;
2928 #endif
2929 ControlRef rootControl;
2931 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
2932 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
2934 #if 1
2935 InitCursor();
2937 RegisterAppearanceClient();
2939 #ifdef USE_AEVENT
2940 (void) InstallAEHandlers();
2941 #endif
2943 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2945 AppendMenu(pomme, "\pAbout VIM");
2947 InsertMenu(pomme, 0);
2949 DrawMenuBar();
2952 #ifndef USE_OFFSETED_WINDOW
2953 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2954 #else
2955 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2956 #endif
2958 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
2959 zoomDocProc,
2960 (WindowPtr)-1L, true, 0);
2961 CreateRootControl(gui.VimWindow, &rootControl);
2962 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
2963 gui.VimWindow, NULL);
2964 SetPortWindowPort(gui.VimWindow);
2966 gui.char_width = 7;
2967 gui.char_height = 11;
2968 gui.char_ascent = 6;
2969 gui.num_rows = 24;
2970 gui.num_cols = 80;
2971 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2973 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2974 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2976 /* Install Carbon event callbacks. */
2977 (void)InstallFontPanelHandler();
2979 dragRectEnbl = FALSE;
2980 dragRgn = NULL;
2981 dragRectControl = kCreateEmpty;
2982 cursorRgn = NewRgn();
2983 #endif
2984 /* Display any pending error messages */
2985 display_errors();
2987 /* Get background/foreground colors from system */
2988 /* TODO: do the appropriate call to get real defaults */
2989 gui.norm_pixel = 0x00000000;
2990 gui.back_pixel = 0x00FFFFFF;
2992 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
2993 * file). */
2994 set_normal_colors();
2997 * Check that none of the colors are the same as the background color.
2998 * Then store the current values as the defaults.
3000 gui_check_colors();
3001 gui.def_norm_pixel = gui.norm_pixel;
3002 gui.def_back_pixel = gui.back_pixel;
3004 /* Get the colors for the highlight groups (gui_check_colors() might have
3005 * changed them) */
3006 highlight_gui_started();
3009 * Setting the gui constants
3011 #ifdef FEAT_MENU
3012 gui.menu_height = 0;
3013 #endif
3014 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3015 gui.border_offset = gui.border_width = 2;
3017 /* If Quartz-style text anti aliasing is available (see
3018 gui_mch_draw_string() below), enable it for all font sizes. */
3019 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3021 eventTypeSpec.eventClass = kEventClassMouse;
3022 eventTypeSpec.eventKind = kEventMouseWheelMoved;
3023 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3024 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3025 &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3027 mouseWheelHandlerRef = NULL;
3028 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3029 mouseWheelHandlerUPP = NULL;
3032 #ifdef USE_CARBONKEYHANDLER
3033 eventTypeSpec.eventClass = kEventClassTextInput;
3034 eventTypeSpec.eventKind = kEventUnicodeForKeyEvent;
3035 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_doKeyEventCarbon);
3036 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP, 1,
3037 &eventTypeSpec, NULL, &keyEventHandlerRef))
3039 keyEventHandlerRef = NULL;
3040 DisposeEventHandlerUPP(keyEventHandlerUPP);
3041 keyEventHandlerUPP = NULL;
3043 #endif
3046 #ifdef FEAT_MBYTE
3047 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3048 #endif
3051 #ifdef FEAT_GUI_TABLINE
3053 * Create the tabline
3055 initialise_tabline();
3056 #endif
3058 /* TODO: Load bitmap if using TOOLBAR */
3059 return OK;
3063 * Called when the foreground or background color has been changed.
3065 void
3066 gui_mch_new_colors(void)
3068 /* TODO:
3069 * This proc is called when Normal is set to a value
3070 * so what msut be done? I don't know
3075 * Open the GUI window which was created by a call to gui_mch_init().
3078 gui_mch_open(void)
3080 ShowWindow(gui.VimWindow);
3082 if (gui_win_x != -1 && gui_win_y != -1)
3083 gui_mch_set_winpos(gui_win_x, gui_win_y);
3086 * Make the GUI the foreground process (in case it was launched
3087 * from the Terminal or via :gui).
3090 ProcessSerialNumber psn;
3091 if (GetCurrentProcess(&psn) == noErr)
3092 SetFrontProcess(&psn);
3095 return OK;
3098 void
3099 gui_mch_exit(int rc)
3101 /* TODO: find out all what is missing here? */
3102 DisposeRgn(cursorRgn);
3104 #ifdef USE_CARBONKEYHANDLER
3105 if (keyEventHandlerUPP)
3106 DisposeEventHandlerUPP(keyEventHandlerUPP);
3107 #endif
3109 if (mouseWheelHandlerUPP != NULL)
3110 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3112 #ifdef USE_ATSUI_DRAWING
3113 if (p_macatsui && gFontStyle)
3114 ATSUDisposeStyle(gFontStyle);
3115 #endif
3117 /* Exit to shell? */
3118 exit(rc);
3122 * Get the position of the top left corner of the window.
3125 gui_mch_get_winpos(int *x, int *y)
3127 /* TODO */
3128 Rect bounds;
3129 OSStatus status;
3131 /* Carbon >= 1.0.2, MacOS >= 8.5 */
3132 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3134 if (status != noErr)
3135 return FAIL;
3136 *x = bounds.left;
3137 *y = bounds.top;
3138 return OK;
3139 return FAIL;
3143 * Set the position of the top left corner of the window to the given
3144 * coordinates.
3146 void
3147 gui_mch_set_winpos(int x, int y)
3149 /* TODO: Should make sure the window is move within range
3150 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3152 MoveWindow(gui.VimWindow, x, y, TRUE);
3155 void
3156 gui_mch_set_shellsize(
3157 int width,
3158 int height,
3159 int min_width,
3160 int min_height,
3161 int base_width,
3162 int base_height,
3163 int direction)
3165 CGrafPtr VimPort;
3166 Rect VimBound;
3168 if (gui.which_scrollbars[SBAR_LEFT])
3170 VimPort = GetWindowPort(gui.VimWindow);
3171 GetPortBounds(VimPort, &VimBound);
3172 VimBound.left = -gui.scrollbar_width; /* + 1;*/
3173 SetPortBounds(VimPort, &VimBound);
3174 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3176 else
3178 VimPort = GetWindowPort(gui.VimWindow);
3179 GetPortBounds(VimPort, &VimBound);
3180 VimBound.left = 0;
3181 SetPortBounds(VimPort, &VimBound);
3184 SizeWindow(gui.VimWindow, width, height, TRUE);
3186 gui_resize_shell(width, height);
3190 * Get the screen dimensions.
3191 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3192 * Is there no way to find out how wide the borders really are?
3193 * TODO: Add live update of those value on suspend/resume.
3195 void
3196 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3198 GDHandle dominantDevice = GetMainDevice();
3199 Rect screenRect = (**dominantDevice).gdRect;
3201 *screen_w = screenRect.right - 10;
3202 *screen_h = screenRect.bottom - 40;
3207 * Open the Font Panel and wait for the user to select a font and
3208 * close the panel. Then fill the buffer pointed to by font_name with
3209 * the name and size of the selected font and return the font's handle,
3210 * or NOFONT in case of an error.
3212 static GuiFont
3213 gui_mac_select_font(char_u *font_name)
3215 GuiFont selected_font = NOFONT;
3216 OSStatus status;
3217 FontSelectionQDStyle curr_font;
3219 /* Initialize the Font Panel with the current font. */
3220 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3221 curr_font.size = (gui.norm_font >> 16);
3222 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3223 curr_font.instance.fontStyle = 0;
3224 curr_font.hasColor = false;
3225 curr_font.version = 0; /* version number of the style structure */
3226 status = SetFontInfoForSelection(kFontSelectionQDType,
3227 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3229 gFontPanelInfo.family = curr_font.instance.fontFamily;
3230 gFontPanelInfo.style = curr_font.instance.fontStyle;
3231 gFontPanelInfo.size = curr_font.size;
3233 /* Pop up the Font Panel. */
3234 status = FPShowHideFontPanel();
3235 if (status == noErr)
3238 * The Font Panel is modeless. We really need it to be modal,
3239 * so we spin in an event loop until the panel is closed.
3241 gFontPanelInfo.isPanelVisible = true;
3242 while (gFontPanelInfo.isPanelVisible)
3244 EventRecord e;
3245 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3248 GetFontPanelSelection(font_name);
3249 selected_font = gui_mac_find_font(font_name);
3251 return selected_font;
3256 * Initialise vim to use the font with the given name. Return FAIL if the font
3257 * could not be loaded, OK otherwise.
3260 gui_mch_init_font(char_u *font_name, int fontset)
3262 /* TODO: Add support for bold italic underline proportional etc... */
3263 Str255 suggestedFont = "\pMonaco";
3264 int suggestedSize = 10;
3265 FontInfo font_info;
3266 short font_id;
3267 GuiFont font;
3268 char_u used_font_name[512];
3270 #ifdef USE_ATSUI_DRAWING
3271 if (p_macatsui && gFontStyle == NULL)
3273 if (ATSUCreateStyle(&gFontStyle) != noErr)
3274 gFontStyle = NULL;
3276 #endif
3278 if (font_name == NULL)
3280 /* First try to get the suggested font */
3281 GetFNum(suggestedFont, &font_id);
3283 if (font_id == 0)
3285 /* Then pickup the standard application font */
3286 font_id = GetAppFont();
3287 STRCPY(used_font_name, "default");
3289 else
3290 STRCPY(used_font_name, "Monaco");
3291 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3293 else if (STRCMP(font_name, "*") == 0)
3295 char_u *new_p_guifont;
3297 font = gui_mac_select_font(used_font_name);
3298 if (font == NOFONT)
3299 return FAIL;
3301 /* Set guifont to the name of the selected font. */
3302 new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3303 if (new_p_guifont != NULL)
3305 STRCPY(new_p_guifont, used_font_name);
3306 vim_free(p_guifont);
3307 p_guifont = new_p_guifont;
3308 /* Replace spaces in the font name with underscores. */
3309 for ( ; *new_p_guifont; ++new_p_guifont)
3311 if (*new_p_guifont == ' ')
3312 *new_p_guifont = '_';
3316 else
3318 font = gui_mac_find_font(font_name);
3319 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3321 if (font == NOFONT)
3322 return FAIL;
3325 gui.norm_font = font;
3327 hl_set_font_name(used_font_name);
3329 TextSize(font >> 16);
3330 TextFont(font & 0xFFFF);
3332 GetFontInfo(&font_info);
3334 gui.char_ascent = font_info.ascent;
3335 gui.char_width = CharWidth('_');
3336 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3338 #ifdef USE_ATSUI_DRAWING
3339 ATSUFontID fontID;
3340 Fixed fontSize;
3341 ATSStyleRenderingOptions fontOptions;
3343 if (p_macatsui && gFontStyle)
3345 fontID = font & 0xFFFF;
3346 fontSize = Long2Fix(font >> 16);
3348 /* No antialiasing by default (do not attempt to touch antialising
3349 * options on pre-Jaguar) */
3350 fontOptions =
3351 (gMacSystemVersion >= 0x1020) ?
3352 kATSStyleNoAntiAliasing :
3353 kATSStyleNoOptions;
3355 ATSUAttributeTag attribTags[] =
3357 kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
3358 kATSUMaxATSUITagValue+1
3360 ByteCount attribSizes[] =
3362 sizeof(ATSUFontID), sizeof(Fixed),
3363 sizeof(ATSStyleRenderingOptions), sizeof font
3365 ATSUAttributeValuePtr attribValues[] =
3367 &fontID, &fontSize, &fontOptions, &font
3370 /* Convert font id to ATSUFontID */
3371 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3373 if (ATSUSetAttributes(gFontStyle,
3374 (sizeof attribTags)/sizeof(ATSUAttributeTag),
3375 attribTags, attribSizes, attribValues) != noErr)
3377 ATSUDisposeStyle(gFontStyle);
3378 gFontStyle = NULL;
3382 #endif
3384 return OK;
3388 * Adjust gui.char_height (after 'linespace' was changed).
3391 gui_mch_adjust_charheight(void)
3393 FontInfo font_info;
3395 GetFontInfo(&font_info);
3396 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3397 gui.char_ascent = font_info.ascent + p_linespace / 2;
3398 return OK;
3402 * Get a font structure for highlighting.
3404 GuiFont
3405 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3407 GuiFont font;
3409 font = gui_mac_find_font(name);
3411 if (font == NOFONT)
3413 if (giveErrorIfMissing)
3414 EMSG2(_(e_font), name);
3415 return NOFONT;
3418 * TODO : Accept only monospace
3421 return font;
3424 #if defined(FEAT_EVAL) || defined(PROTO)
3426 * Return the name of font "font" in allocated memory.
3427 * Don't know how to get the actual name, thus use the provided name.
3429 char_u *
3430 gui_mch_get_fontname(GuiFont font, char_u *name)
3432 if (name == NULL)
3433 return NULL;
3434 return vim_strsave(name);
3436 #endif
3439 * Set the current text font.
3441 void
3442 gui_mch_set_font(GuiFont font)
3444 #ifdef USE_ATSUI_DRAWING
3445 GuiFont currFont;
3446 ByteCount actualFontByteCount;
3447 ATSUFontID fontID;
3448 Fixed fontSize;
3449 ATSStyleRenderingOptions fontOptions;
3451 if (p_macatsui && gFontStyle)
3453 /* Avoid setting same font again */
3454 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue+1, sizeof font,
3455 &currFont, &actualFontByteCount) == noErr &&
3456 actualFontByteCount == (sizeof font))
3458 if (currFont == font)
3459 return;
3462 fontID = font & 0xFFFF;
3463 fontSize = Long2Fix(font >> 16);
3464 /* Respect p_antialias setting only for wide font.
3465 * The reason for doing this at the moment is a bit complicated,
3466 * but it's mainly because a) latin (non-wide) aliased fonts
3467 * look bad in OS X 10.3.x and below (due to a bug in ATS), and
3468 * b) wide multibyte input does not suffer from that problem. */
3469 /*fontOptions =
3470 (p_antialias && (font == gui.wide_font)) ?
3471 kATSStyleNoOptions : kATSStyleNoAntiAliasing;
3473 /*fontOptions = kATSStyleAntiAliasing;*/
3475 ATSUAttributeTag attribTags[] =
3477 kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
3478 kATSUMaxATSUITagValue+1
3480 ByteCount attribSizes[] =
3482 sizeof(ATSUFontID), sizeof(Fixed),
3483 sizeof(ATSStyleRenderingOptions), sizeof font
3485 ATSUAttributeValuePtr attribValues[] =
3487 &fontID, &fontSize, &fontOptions, &font
3490 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3492 if (ATSUSetAttributes(gFontStyle,
3493 (sizeof attribTags)/sizeof(ATSUAttributeTag),
3494 attribTags, attribSizes, attribValues) != noErr)
3496 # ifndef NDEBUG
3497 fprintf(stderr, "couldn't set font style\n");
3498 # endif
3499 ATSUDisposeStyle(gFontStyle);
3500 gFontStyle = NULL;
3506 if (p_macatsui && !gIsFontFallbackSet)
3508 /* Setup automatic font substitution. The user's guifontwide
3509 * is tried first, then the system tries other fonts. */
3511 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3512 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3513 ATSUCreateFontFallbacks(&gFontFallbacks);
3514 ATSUSetObjFontFallbacks(gFontFallbacks, );
3516 if (gui.wide_font)
3518 ATSUFontID fallbackFonts;
3519 gIsFontFallbackSet = TRUE;
3521 if (FMGetFontFromFontFamilyInstance(
3522 (gui.wide_font & 0xFFFF),
3524 &fallbackFonts,
3525 NULL) == noErr)
3527 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID), &fallbackFonts, kATSUSequentialFallbacksPreferred);
3530 ATSUAttributeValuePtr fallbackValues[] = { };
3534 #endif
3535 TextSize(font >> 16);
3536 TextFont(font & 0xFFFF);
3540 * If a font is not going to be used, free its structure.
3542 void
3543 gui_mch_free_font(font)
3544 GuiFont font;
3547 * Free font when "font" is not 0.
3548 * Nothing to do in the current implementation, since
3549 * nothing is allocated for each font used.
3553 static int
3554 hex_digit(int c)
3556 if (isdigit(c))
3557 return c - '0';
3558 c = TOLOWER_ASC(c);
3559 if (c >= 'a' && c <= 'f')
3560 return c - 'a' + 10;
3561 return -1000;
3565 * Return the Pixel value (color) for the given color name. This routine was
3566 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3567 * Programmer's Guide.
3568 * Return INVALCOLOR when failed.
3570 guicolor_T
3571 gui_mch_get_color(char_u *name)
3573 /* TODO: Add support for the new named color of MacOS 8
3575 RGBColor MacColor;
3576 // guicolor_T color = 0;
3578 typedef struct guicolor_tTable
3580 char *name;
3581 guicolor_T color;
3582 } guicolor_tTable;
3585 * The comment at the end of each line is the source
3586 * (Mac, Window, Unix) and the number is the unix rgb.txt value
3588 static guicolor_tTable table[] =
3590 {"Black", RGB(0x00, 0x00, 0x00)},
3591 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
3592 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
3593 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3594 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3595 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3596 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3597 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3598 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3599 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/
3600 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/
3601 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3602 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3603 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/
3604 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/
3605 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3606 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3607 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/
3608 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/
3609 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3610 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3611 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3612 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3613 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3614 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3615 {"white", RGB(0xFF, 0xFF, 0xFF)},
3616 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
3617 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
3618 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3619 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
3620 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
3621 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3622 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
3623 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
3624 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3625 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3626 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
3627 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3628 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3629 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
3630 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3631 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
3632 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
3633 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3634 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
3635 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3636 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3637 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3638 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3639 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
3642 int r, g, b;
3643 int i;
3645 if (name[0] == '#' && strlen((char *) name) == 7)
3647 /* Name is in "#rrggbb" format */
3648 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3649 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3650 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3651 if (r < 0 || g < 0 || b < 0)
3652 return INVALCOLOR;
3653 return RGB(r, g, b);
3655 else
3657 if (STRICMP(name, "hilite") == 0)
3659 LMGetHiliteRGB(&MacColor);
3660 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3662 /* Check if the name is one of the colors we know */
3663 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3664 if (STRICMP(name, table[i].name) == 0)
3665 return table[i].color;
3669 * Last attempt. Look in the file "$VIM/rgb.txt".
3672 #define LINE_LEN 100
3673 FILE *fd;
3674 char line[LINE_LEN];
3675 char_u *fname;
3677 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3678 if (fname == NULL)
3679 return INVALCOLOR;
3681 fd = fopen((char *)fname, "rt");
3682 vim_free(fname);
3683 if (fd == NULL)
3684 return INVALCOLOR;
3686 while (!feof(fd))
3688 int len;
3689 int pos;
3690 char *color;
3692 fgets(line, LINE_LEN, fd);
3693 len = strlen(line);
3695 if (len <= 1 || line[len-1] != '\n')
3696 continue;
3698 line[len-1] = '\0';
3700 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3701 if (i != 3)
3702 continue;
3704 color = line + pos;
3706 if (STRICMP(color, name) == 0)
3708 fclose(fd);
3709 return (guicolor_T) RGB(r, g, b);
3712 fclose(fd);
3715 return INVALCOLOR;
3719 * Set the current text foreground color.
3721 void
3722 gui_mch_set_fg_color(guicolor_T color)
3724 RGBColor TheColor;
3726 TheColor.red = Red(color) * 0x0101;
3727 TheColor.green = Green(color) * 0x0101;
3728 TheColor.blue = Blue(color) * 0x0101;
3730 RGBForeColor(&TheColor);
3734 * Set the current text background color.
3736 void
3737 gui_mch_set_bg_color(guicolor_T color)
3739 RGBColor TheColor;
3741 TheColor.red = Red(color) * 0x0101;
3742 TheColor.green = Green(color) * 0x0101;
3743 TheColor.blue = Blue(color) * 0x0101;
3745 RGBBackColor(&TheColor);
3748 RGBColor specialColor;
3751 * Set the current text special color.
3753 void
3754 gui_mch_set_sp_color(guicolor_T color)
3756 specialColor.red = Red(color) * 0x0101;
3757 specialColor.green = Green(color) * 0x0101;
3758 specialColor.blue = Blue(color) * 0x0101;
3762 * Draw undercurl at the bottom of the character cell.
3764 static void
3765 draw_undercurl(int flags, int row, int col, int cells)
3767 int x;
3768 int offset;
3769 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3770 int y = FILL_Y(row + 1) - 1;
3772 RGBForeColor(&specialColor);
3774 offset = val[FILL_X(col) % 8];
3775 MoveTo(FILL_X(col), y - offset);
3777 for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3779 offset = val[x % 8];
3780 LineTo(x, y - offset);
3785 static void
3786 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3788 #ifdef FEAT_MBYTE
3789 char_u *tofree = NULL;
3791 if (output_conv.vc_type != CONV_NONE)
3793 tofree = string_convert(&output_conv, s, &len);
3794 if (tofree != NULL)
3795 s = tofree;
3797 #endif
3800 * On OS X, try using Quartz-style text antialiasing.
3802 if (gMacSystemVersion >= 0x1020)
3804 /* Quartz antialiasing is available only in OS 10.2 and later. */
3805 UInt32 qd_flags = (p_antialias ?
3806 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3807 QDSwapTextFlags(qd_flags);
3811 * When antialiasing we're using srcOr mode, we have to clear the block
3812 * before drawing the text.
3813 * Also needed when 'linespace' is non-zero to remove the cursor and
3814 * underlining.
3815 * But not when drawing transparently.
3816 * The following is like calling gui_mch_clear_block(row, col, row, col +
3817 * len - 1), but without setting the bg color to gui.back_pixel.
3819 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3820 && !(flags & DRAW_TRANSP))
3822 Rect rc;
3824 rc.left = FILL_X(col);
3825 rc.top = FILL_Y(row);
3826 #ifdef FEAT_MBYTE
3827 /* Multibyte computation taken from gui_w32.c */
3828 if (has_mbyte)
3830 int cell_len = 0;
3831 int n;
3833 /* Compute the length in display cells. */
3834 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3835 cell_len += (*mb_ptr2cells)(s + n);
3836 rc.right = FILL_X(col + cell_len);
3838 else
3839 #endif
3840 rc.right = FILL_X(col + len) + (col + len == Columns);
3841 rc.bottom = FILL_Y(row + 1);
3842 EraseRect(&rc);
3845 if (gMacSystemVersion >= 0x1020 && p_antialias)
3847 StyleParameter face;
3849 face = normal;
3850 if (flags & DRAW_BOLD)
3851 face |= bold;
3852 if (flags & DRAW_UNDERL)
3853 face |= underline;
3854 TextFace(face);
3856 /* Quartz antialiasing works only in srcOr transfer mode. */
3857 TextMode(srcOr);
3859 MoveTo(TEXT_X(col), TEXT_Y(row));
3860 DrawText((char*)s, 0, len);
3862 else
3864 /* Use old-style, non-antialiased QuickDraw text rendering. */
3865 TextMode(srcCopy);
3866 TextFace(normal);
3868 /* SelectFont(hdc, gui.currFont); */
3870 if (flags & DRAW_TRANSP)
3872 TextMode(srcOr);
3875 MoveTo(TEXT_X(col), TEXT_Y(row));
3876 DrawText((char *)s, 0, len);
3878 if (flags & DRAW_BOLD)
3880 TextMode(srcOr);
3881 MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
3882 DrawText((char *)s, 0, len);
3885 if (flags & DRAW_UNDERL)
3887 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
3888 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
3892 if (flags & DRAW_UNDERC)
3893 draw_undercurl(flags, row, col, len);
3895 #ifdef FEAT_MBYTE
3896 vim_free(tofree);
3897 #endif
3900 #ifdef USE_ATSUI_DRAWING
3902 static void
3903 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
3905 /* ATSUI requires utf-16 strings */
3906 UniCharCount utf16_len;
3907 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
3908 utf16_len /= sizeof(UniChar);
3910 /* - ATSUI automatically antialiases text (Someone)
3911 * - for some reason it does not work... (Jussi) */
3914 * When antialiasing we're using srcOr mode, we have to clear the block
3915 * before drawing the text.
3916 * Also needed when 'linespace' is non-zero to remove the cursor and
3917 * underlining.
3918 * But not when drawing transparently.
3919 * The following is like calling gui_mch_clear_block(row, col, row, col +
3920 * len - 1), but without setting the bg color to gui.back_pixel.
3922 if ((flags & DRAW_TRANSP) == 0)
3924 Rect rc;
3926 rc.left = FILL_X(col);
3927 rc.top = FILL_Y(row);
3928 /* Multibyte computation taken from gui_w32.c */
3929 if (has_mbyte)
3931 int cell_len = 0;
3932 int n;
3934 /* Compute the length in display cells. */
3935 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3936 cell_len += (*mb_ptr2cells)(s + n);
3937 rc.right = FILL_X(col + cell_len);
3939 else
3940 rc.right = FILL_X(col + len) + (col + len == Columns);
3942 rc.bottom = FILL_Y(row + 1);
3943 EraseRect(&rc);
3947 /* Use old-style, non-antialiased QuickDraw text rendering. */
3948 TextMode(srcCopy);
3949 TextFace(normal);
3951 /* SelectFont(hdc, gui.currFont); */
3953 if (flags & DRAW_TRANSP)
3955 TextMode(srcOr);
3958 MoveTo(TEXT_X(col), TEXT_Y(row));
3959 ATSUTextLayout textLayout;
3961 if (ATSUCreateTextLayoutWithTextPtr(tofree,
3962 kATSUFromTextBeginning, kATSUToTextEnd,
3963 utf16_len,
3964 (gFontStyle ? 1 : 0), &utf16_len,
3965 (gFontStyle ? &gFontStyle : NULL),
3966 &textLayout) == noErr)
3968 ATSUSetTransientFontMatching(textLayout, TRUE);
3970 ATSUDrawText(textLayout,
3971 kATSUFromTextBeginning, kATSUToTextEnd,
3972 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
3974 ATSUDisposeTextLayout(textLayout);
3978 if (flags & DRAW_UNDERC)
3979 draw_undercurl(flags, row, col, len);
3981 vim_free(tofree);
3983 #endif
3985 void
3986 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
3988 #if defined(USE_ATSUI_DRAWING)
3989 if (p_macatsui)
3990 draw_string_ATSUI(row, col, s, len, flags);
3991 else
3992 #endif
3993 draw_string_QD(row, col, s, len, flags);
3997 * Return OK if the key with the termcap name "name" is supported.
4000 gui_mch_haskey(char_u *name)
4002 int i;
4004 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4005 if (name[0] == special_keys[i].vim_code0 &&
4006 name[1] == special_keys[i].vim_code1)
4007 return OK;
4008 return FAIL;
4011 void
4012 gui_mch_beep(void)
4014 SysBeep(1); /* Should this be 0? (????) */
4017 void
4018 gui_mch_flash(int msec)
4020 /* Do a visual beep by reversing the foreground and background colors */
4021 Rect rc;
4024 * Note: InvertRect() excludes right and bottom of rectangle.
4026 rc.left = 0;
4027 rc.top = 0;
4028 rc.right = gui.num_cols * gui.char_width;
4029 rc.bottom = gui.num_rows * gui.char_height;
4030 InvertRect(&rc);
4032 ui_delay((long)msec, TRUE); /* wait for some msec */
4034 InvertRect(&rc);
4038 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4040 void
4041 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4043 Rect rc;
4046 * Note: InvertRect() excludes right and bottom of rectangle.
4048 rc.left = FILL_X(c);
4049 rc.top = FILL_Y(r);
4050 rc.right = rc.left + nc * gui.char_width;
4051 rc.bottom = rc.top + nr * gui.char_height;
4052 InvertRect(&rc);
4056 * Iconify the GUI window.
4058 void
4059 gui_mch_iconify(void)
4061 /* TODO: find out what could replace iconify
4062 * -window shade?
4063 * -hide application?
4067 #if defined(FEAT_EVAL) || defined(PROTO)
4069 * Bring the Vim window to the foreground.
4071 void
4072 gui_mch_set_foreground(void)
4074 /* TODO */
4076 #endif
4079 * Draw a cursor without focus.
4081 void
4082 gui_mch_draw_hollow_cursor(guicolor_T color)
4084 Rect rc;
4087 * Note: FrameRect() excludes right and bottom of rectangle.
4089 rc.left = FILL_X(gui.col);
4090 rc.top = FILL_Y(gui.row);
4091 rc.right = rc.left + gui.char_width;
4092 #ifdef FEAT_MBYTE
4093 if (mb_lefthalve(gui.row, gui.col))
4094 rc.right += gui.char_width;
4095 #endif
4096 rc.bottom = rc.top + gui.char_height;
4098 gui_mch_set_fg_color(color);
4100 FrameRect(&rc);
4104 * Draw part of a cursor, only w pixels wide, and h pixels high.
4106 void
4107 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4109 Rect rc;
4111 #ifdef FEAT_RIGHTLEFT
4112 /* vertical line should be on the right of current point */
4113 if (CURSOR_BAR_RIGHT)
4114 rc.left = FILL_X(gui.col + 1) - w;
4115 else
4116 #endif
4117 rc.left = FILL_X(gui.col);
4118 rc.top = FILL_Y(gui.row) + gui.char_height - h;
4119 rc.right = rc.left + w;
4120 rc.bottom = rc.top + h;
4122 gui_mch_set_fg_color(color);
4124 FrameRect(&rc);
4125 // PaintRect(&rc);
4131 * Catch up with any queued X events. This may put keyboard input into the
4132 * input buffer, call resize call-backs, trigger timers etc. If there is
4133 * nothing in the X event queue (& no timers pending), then we return
4134 * immediately.
4136 void
4137 gui_mch_update(void)
4139 /* TODO: find what to do
4140 * maybe call gui_mch_wait_for_chars (0)
4141 * more like look at EventQueue then
4142 * call heart of gui_mch_wait_for_chars;
4144 * if (eventther)
4145 * gui_mac_handle_event(&event);
4147 EventRecord theEvent;
4149 if (EventAvail(everyEvent, &theEvent))
4150 if (theEvent.what != nullEvent)
4151 gui_mch_wait_for_chars(0);
4155 * Simple wrapper to neglect more easily the time
4156 * spent inside WaitNextEvent while profiling.
4159 pascal
4160 Boolean
4161 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4163 if (((long) sleep) < -1)
4164 sleep = 32767;
4165 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4169 * GUI input routine called by gui_wait_for_chars(). Waits for a character
4170 * from the keyboard.
4171 * wtime == -1 Wait forever.
4172 * wtime == 0 This should never happen.
4173 * wtime > 0 Wait wtime milliseconds for a character.
4174 * Returns OK if a character was found to be available within the given time,
4175 * or FAIL otherwise.
4178 gui_mch_wait_for_chars(int wtime)
4180 EventMask mask = (everyEvent);
4181 EventRecord event;
4182 long entryTick;
4183 long currentTick;
4184 long sleeppyTick;
4186 /* If we are providing life feedback with the scrollbar,
4187 * we don't want to try to wait for an event, or else
4188 * there won't be any life feedback.
4190 if (dragged_sb != NULL)
4191 return FAIL;
4192 /* TODO: Check if FAIL is the proper return code */
4194 entryTick = TickCount();
4196 allow_scrollbar = TRUE;
4200 /* if (dragRectControl == kCreateEmpty)
4202 dragRgn = NULL;
4203 dragRectControl = kNothing;
4205 else*/ if (dragRectControl == kCreateRect)
4207 dragRgn = cursorRgn;
4208 RectRgn(dragRgn, &dragRect);
4209 dragRectControl = kNothing;
4212 * Don't use gui_mch_update() because then we will spin-lock until a
4213 * char arrives, instead we use WaitNextEventWrp() to hang until an
4214 * event arrives. No need to check for input_buf_full because we are
4215 * returning as soon as it contains a single char.
4217 /* TODO: reduce wtime accordinly??? */
4218 if (wtime > -1)
4219 sleeppyTick = 60*wtime/1000;
4220 else
4221 sleeppyTick = 32767;
4222 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4224 gui_mac_handle_event(&event);
4225 if (input_available())
4227 allow_scrollbar = FALSE;
4228 return OK;
4231 currentTick = TickCount();
4233 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4235 allow_scrollbar = FALSE;
4236 return FAIL;
4240 * Output routines.
4243 /* Flush any output to the screen */
4244 void
4245 gui_mch_flush(void)
4247 /* TODO: Is anything needed here? */
4251 * Clear a rectangular region of the screen from text pos (row1, col1) to
4252 * (row2, col2) inclusive.
4254 void
4255 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4257 Rect rc;
4260 * Clear one extra pixel at the far right, for when bold characters have
4261 * spilled over to the next column.
4263 rc.left = FILL_X(col1);
4264 rc.top = FILL_Y(row1);
4265 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4266 rc.bottom = FILL_Y(row2 + 1);
4268 gui_mch_set_bg_color(gui.back_pixel);
4269 EraseRect(&rc);
4273 * Clear the whole text window.
4275 void
4276 gui_mch_clear_all(void)
4278 Rect rc;
4280 rc.left = 0;
4281 rc.top = 0;
4282 rc.right = Columns * gui.char_width + 2 * gui.border_width;
4283 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4285 gui_mch_set_bg_color(gui.back_pixel);
4286 EraseRect(&rc);
4287 /* gui_mch_set_fg_color(gui.norm_pixel);
4288 FrameRect(&rc);
4293 * Delete the given number of lines from the given row, scrolling up any
4294 * text further down within the scroll region.
4296 void
4297 gui_mch_delete_lines(int row, int num_lines)
4299 Rect rc;
4301 /* changed without checking! */
4302 rc.left = FILL_X(gui.scroll_region_left);
4303 rc.right = FILL_X(gui.scroll_region_right + 1);
4304 rc.top = FILL_Y(row);
4305 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4307 gui_mch_set_bg_color(gui.back_pixel);
4308 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4310 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4311 gui.scroll_region_left,
4312 gui.scroll_region_bot, gui.scroll_region_right);
4316 * Insert the given number of lines before the given row, scrolling down any
4317 * following text within the scroll region.
4319 void
4320 gui_mch_insert_lines(int row, int num_lines)
4322 Rect rc;
4324 rc.left = FILL_X(gui.scroll_region_left);
4325 rc.right = FILL_X(gui.scroll_region_right + 1);
4326 rc.top = FILL_Y(row);
4327 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4329 gui_mch_set_bg_color(gui.back_pixel);
4331 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4333 /* Update gui.cursor_row if the cursor scrolled or copied over */
4334 if (gui.cursor_row >= gui.row
4335 && gui.cursor_col >= gui.scroll_region_left
4336 && gui.cursor_col <= gui.scroll_region_right)
4338 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4339 gui.cursor_row += num_lines;
4340 else if (gui.cursor_row <= gui.scroll_region_bot)
4341 gui.cursor_is_valid = FALSE;
4344 gui_clear_block(row, gui.scroll_region_left,
4345 row + num_lines - 1, gui.scroll_region_right);
4349 * TODO: add a vim format to the clipboard which remember
4350 * LINEWISE, CHARWISE, BLOCKWISE
4353 void
4354 clip_mch_request_selection(VimClipboard *cbd)
4357 Handle textOfClip;
4358 int flavor = 0;
4359 Size scrapSize;
4360 ScrapFlavorFlags scrapFlags;
4361 ScrapRef scrap = nil;
4362 OSStatus error;
4363 int type;
4364 char *searchCR;
4365 char_u *tempclip;
4368 error = GetCurrentScrap(&scrap);
4369 if (error != noErr)
4370 return;
4372 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4373 if (error == noErr)
4375 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4376 if (error == noErr && scrapSize > 1)
4377 flavor = 1;
4380 if (flavor == 0)
4382 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4383 if (error != noErr)
4384 return;
4386 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4387 if (error != noErr)
4388 return;
4391 ReserveMem(scrapSize);
4393 /* In CARBON we don't need a Handle, a pointer is good */
4394 textOfClip = NewHandle(scrapSize);
4396 /* tempclip = lalloc(scrapSize+1, TRUE); */
4397 HLock(textOfClip);
4398 error = GetScrapFlavorData(scrap,
4399 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4400 &scrapSize, *textOfClip);
4401 scrapSize -= flavor;
4403 if (flavor)
4404 type = **textOfClip;
4405 else
4406 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4408 tempclip = lalloc(scrapSize + 1, TRUE);
4409 mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4410 tempclip[scrapSize] = 0;
4412 #ifdef MACOS_CONVERT
4414 /* Convert from utf-16 (clipboard) */
4415 size_t encLen = 0;
4416 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4418 if (to != NULL)
4420 scrapSize = encLen;
4421 vim_free(tempclip);
4422 tempclip = to;
4425 #endif
4427 searchCR = (char *)tempclip;
4428 while (searchCR != NULL)
4430 searchCR = strchr(searchCR, '\r');
4431 if (searchCR != NULL)
4432 *searchCR = '\n';
4435 clip_yank_selection(type, tempclip, scrapSize, cbd);
4437 vim_free(tempclip);
4438 HUnlock(textOfClip);
4440 DisposeHandle(textOfClip);
4443 void
4444 clip_mch_lose_selection(VimClipboard *cbd)
4447 * TODO: Really nothing to do?
4452 clip_mch_own_selection(VimClipboard *cbd)
4454 return OK;
4458 * Send the current selection to the clipboard.
4460 void
4461 clip_mch_set_selection(VimClipboard *cbd)
4463 Handle textOfClip;
4464 long scrapSize;
4465 int type;
4466 ScrapRef scrap;
4468 char_u *str = NULL;
4470 if (!cbd->owned)
4471 return;
4473 clip_get_selection(cbd);
4476 * Once we set the clipboard, lose ownership. If another application sets
4477 * the clipboard, we don't want to think that we still own it.
4479 cbd->owned = FALSE;
4481 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4483 #ifdef MACOS_CONVERT
4484 size_t utf16_len = 0;
4485 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4486 if (to)
4488 scrapSize = utf16_len;
4489 vim_free(str);
4490 str = (char_u *)to;
4492 #endif
4494 if (type >= 0)
4496 ClearCurrentScrap();
4498 textOfClip = NewHandle(scrapSize + 1);
4499 HLock(textOfClip);
4501 **textOfClip = type;
4502 mch_memmove(*textOfClip + 1, str, scrapSize);
4503 GetCurrentScrap(&scrap);
4504 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4505 scrapSize, *textOfClip + 1);
4506 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4507 scrapSize + 1, *textOfClip);
4508 HUnlock(textOfClip);
4509 DisposeHandle(textOfClip);
4512 vim_free(str);
4515 void
4516 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4518 Rect VimBound;
4520 /* HideWindow(gui.VimWindow); */
4521 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4523 if (gui.which_scrollbars[SBAR_LEFT])
4525 VimBound.left = -gui.scrollbar_width + 1;
4527 else
4529 VimBound.left = 0;
4532 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4534 ShowWindow(gui.VimWindow);
4538 * Menu stuff.
4541 void
4542 gui_mch_enable_menu(int flag)
4545 * Menu is always active.
4549 void
4550 gui_mch_set_menu_pos(int x, int y, int w, int h)
4553 * The menu is always at the top of the screen.
4558 * Add a sub menu to the menu bar.
4560 void
4561 gui_mch_add_menu(vimmenu_T *menu, int idx)
4564 * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4565 * TODO: use menu->mnemonic and menu->actext
4566 * TODO: Try to reuse menu id
4567 * Carbon Help suggest to use only id between 1 and 235
4569 static long next_avail_id = 128;
4570 long menu_after_me = 0; /* Default to the end */
4571 #if defined(FEAT_MBYTE)
4572 CFStringRef name;
4573 #else
4574 char_u *name;
4575 #endif
4576 short index;
4577 vimmenu_T *parent = menu->parent;
4578 vimmenu_T *brother = menu->next;
4580 /* Cannot add a menu if ... */
4581 if ((parent != NULL && parent->submenu_id == 0))
4582 return;
4584 /* menu ID greater than 1024 are reserved for ??? */
4585 if (next_avail_id == 1024)
4586 return;
4588 /* My brother could be the PopUp, find my real brother */
4589 while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4590 brother = brother->next;
4592 /* Find where to insert the menu (for MenuBar) */
4593 if ((parent == NULL) && (brother != NULL))
4594 menu_after_me = brother->submenu_id;
4596 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4597 if (!menu_is_menubar(menu->name))
4598 menu_after_me = hierMenu;
4600 /* Convert the name */
4601 #ifdef MACOS_CONVERT
4602 name = menu_title_removing_mnemonic(menu);
4603 #else
4604 name = C2Pascal_save(menu->dname);
4605 #endif
4606 if (name == NULL)
4607 return;
4609 /* Create the menu unless it's the help menu */
4611 /* Carbon suggest use of
4612 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4613 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4615 menu->submenu_id = next_avail_id;
4616 #if defined(FEAT_MBYTE)
4617 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4618 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4619 #else
4620 menu->submenu_handle = NewMenu(menu->submenu_id, name);
4621 #endif
4622 next_avail_id++;
4625 if (parent == NULL)
4627 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4629 /* TODO: Verify if we could only Insert Menu if really part of the
4630 * menubar The Inserted menu are scanned or the Command-key combos
4633 /* Insert the menu */
4634 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4635 #if 1
4636 /* Vim should normally update it. TODO: verify */
4637 DrawMenuBar();
4638 #endif
4640 else
4642 /* Adding as a submenu */
4644 index = gui_mac_get_menu_item_index(menu);
4646 /* Call InsertMenuItem followed by SetMenuItemText
4647 * to avoid special character recognition by InsertMenuItem
4649 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4650 #if defined(FEAT_MBYTE)
4651 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4652 #else
4653 SetMenuItemText(parent->submenu_handle, idx+1, name);
4654 #endif
4655 SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4656 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4657 InsertMenu(menu->submenu_handle, hierMenu);
4660 #if defined(FEAT_MBYTE)
4661 CFRelease(name);
4662 #else
4663 vim_free(name);
4664 #endif
4666 #if 0
4667 /* Done by Vim later on */
4668 DrawMenuBar();
4669 #endif
4673 * Add a menu item to a menu
4675 void
4676 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4678 #if defined(FEAT_MBYTE)
4679 CFStringRef name;
4680 #else
4681 char_u *name;
4682 #endif
4683 vimmenu_T *parent = menu->parent;
4684 int menu_inserted;
4686 /* Cannot add item, if the menu have not been created */
4687 if (parent->submenu_id == 0)
4688 return;
4690 /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4691 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4693 /* Convert the name */
4694 #ifdef MACOS_CONVERT
4695 name = menu_title_removing_mnemonic(menu);
4696 #else
4697 name = C2Pascal_save(menu->dname);
4698 #endif
4700 /* Where are just a menu item, so no handle, no id */
4701 menu->submenu_id = 0;
4702 menu->submenu_handle = NULL;
4704 menu_inserted = 0;
4705 if (menu->actext)
4707 /* If the accelerator text for the menu item looks like it describes
4708 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4709 * item's command equivalent.
4711 int key = 0;
4712 int modifiers = 0;
4713 char_u *p_actext;
4715 p_actext = menu->actext;
4716 key = find_special_key(&p_actext, &modifiers, /*keycode=*/0);
4717 if (*p_actext != 0)
4718 key = 0; /* error: trailing text */
4719 /* find_special_key() returns a keycode with as many of the
4720 * specified modifiers as appropriate already applied (e.g., for
4721 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4722 * as the only modifier). Since we want to display all of the
4723 * modifiers, we need to convert the keycode back to a printable
4724 * character plus modifiers.
4725 * TODO: Write an alternative find_special_key() that doesn't
4726 * apply modifiers.
4728 if (key > 0 && key < 32)
4730 /* Convert a control key to an uppercase letter. Note that
4731 * by this point it is no longer possible to distinguish
4732 * between, e.g., Ctrl-S and Ctrl-Shift-S.
4734 modifiers |= MOD_MASK_CTRL;
4735 key += '@';
4737 /* If the keycode is an uppercase letter, set the Shift modifier.
4738 * If it is a lowercase letter, don't set the modifier, but convert
4739 * the letter to uppercase for display in the menu.
4741 else if (key >= 'A' && key <= 'Z')
4742 modifiers |= MOD_MASK_SHIFT;
4743 else if (key >= 'a' && key <= 'z')
4744 key += 'A' - 'a';
4745 /* Note: keycodes below 0x22 are reserved by Apple. */
4746 if (key >= 0x22 && vim_isprintc_strict(key))
4748 int valid = 1;
4749 char_u mac_mods = kMenuNoModifiers;
4750 /* Convert Vim modifier codes to Menu Manager equivalents. */
4751 if (modifiers & MOD_MASK_SHIFT)
4752 mac_mods |= kMenuShiftModifier;
4753 if (modifiers & MOD_MASK_CTRL)
4754 mac_mods |= kMenuControlModifier;
4755 if (!(modifiers & MOD_MASK_CMD))
4756 mac_mods |= kMenuNoCommandModifier;
4757 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
4758 valid = 0; /* TODO: will Alt someday map to Option? */
4759 if (valid)
4761 char_u item_txt[10];
4762 /* Insert the menu item after idx, with its command key. */
4763 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
4764 item_txt[3] = key;
4765 InsertMenuItem(parent->submenu_handle, item_txt, idx);
4766 /* Set the modifier keys. */
4767 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
4768 menu_inserted = 1;
4772 /* Call InsertMenuItem followed by SetMenuItemText
4773 * to avoid special character recognition by InsertMenuItem
4775 if (!menu_inserted)
4776 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4777 /* Set the menu item name. */
4778 #if defined(FEAT_MBYTE)
4779 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4780 #else
4781 SetMenuItemText(parent->submenu_handle, idx+1, name);
4782 #endif
4784 #if 0
4785 /* Called by Vim */
4786 DrawMenuBar();
4787 #endif
4789 #if defined(FEAT_MBYTE)
4790 CFRelease(name);
4791 #else
4792 /* TODO: Can name be freed? */
4793 vim_free(name);
4794 #endif
4797 void
4798 gui_mch_toggle_tearoffs(int enable)
4800 /* no tearoff menus */
4804 * Destroy the machine specific menu widget.
4806 void
4807 gui_mch_destroy_menu(vimmenu_T *menu)
4809 short index = gui_mac_get_menu_item_index(menu);
4811 if (index > 0)
4813 if (menu->parent)
4816 /* For now just don't delete help menu items. (Huh? Dany) */
4817 DeleteMenuItem(menu->parent->submenu_handle, index);
4819 /* Delete the Menu if it was a hierarchical Menu */
4820 if (menu->submenu_id != 0)
4822 DeleteMenu(menu->submenu_id);
4823 DisposeMenu(menu->submenu_handle);
4827 #ifdef DEBUG_MAC_MENU
4828 else
4830 printf("gmdm 2\n");
4832 #endif
4834 else
4837 DeleteMenu(menu->submenu_id);
4838 DisposeMenu(menu->submenu_handle);
4841 /* Shouldn't this be already done by Vim. TODO: Check */
4842 DrawMenuBar();
4846 * Make a menu either grey or not grey.
4848 void
4849 gui_mch_menu_grey(vimmenu_T *menu, int grey)
4851 /* TODO: Check if menu really exists */
4852 short index = gui_mac_get_menu_item_index(menu);
4854 index = menu->index;
4856 if (grey)
4858 if (menu->children)
4859 DisableMenuItem(menu->submenu_handle, index);
4860 if (menu->parent)
4861 if (menu->parent->submenu_handle)
4862 DisableMenuItem(menu->parent->submenu_handle, index);
4864 else
4866 if (menu->children)
4867 EnableMenuItem(menu->submenu_handle, index);
4868 if (menu->parent)
4869 if (menu->parent->submenu_handle)
4870 EnableMenuItem(menu->parent->submenu_handle, index);
4875 * Make menu item hidden or not hidden
4877 void
4878 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
4880 /* There's no hidden mode on MacOS */
4881 gui_mch_menu_grey(menu, hidden);
4886 * This is called after setting all the menus to grey/hidden or not.
4888 void
4889 gui_mch_draw_menubar(void)
4891 DrawMenuBar();
4896 * Scrollbar stuff.
4899 void
4900 gui_mch_enable_scrollbar(
4901 scrollbar_T *sb,
4902 int flag)
4904 if (flag)
4905 ShowControl(sb->id);
4906 else
4907 HideControl(sb->id);
4909 #ifdef DEBUG_MAC_SB
4910 printf("enb_sb (%x) %x\n",sb->id, flag);
4911 #endif
4914 void
4915 gui_mch_set_scrollbar_thumb(
4916 scrollbar_T *sb,
4917 long val,
4918 long size,
4919 long max)
4921 SetControl32BitMaximum (sb->id, max);
4922 SetControl32BitMinimum (sb->id, 0);
4923 SetControl32BitValue (sb->id, val);
4924 SetControlViewSize (sb->id, size);
4925 #ifdef DEBUG_MAC_SB
4926 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
4927 #endif
4930 void
4931 gui_mch_set_scrollbar_pos(
4932 scrollbar_T *sb,
4933 int x,
4934 int y,
4935 int w,
4936 int h)
4938 gui_mch_set_bg_color(gui.back_pixel);
4939 /* if (gui.which_scrollbars[SBAR_LEFT])
4941 MoveControl(sb->id, x-16, y);
4942 SizeControl(sb->id, w + 1, h);
4944 else
4946 MoveControl(sb->id, x, y);
4947 SizeControl(sb->id, w + 1, h);
4949 if (sb == &gui.bottom_sbar)
4950 h += 1;
4951 else
4952 w += 1;
4954 if (gui.which_scrollbars[SBAR_LEFT])
4955 x -= 15;
4957 MoveControl(sb->id, x, y);
4958 SizeControl(sb->id, w, h);
4959 #ifdef DEBUG_MAC_SB
4960 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
4961 #endif
4964 void
4965 gui_mch_create_scrollbar(
4966 scrollbar_T *sb,
4967 int orient) /* SBAR_VERT or SBAR_HORIZ */
4969 Rect bounds;
4971 bounds.top = -16;
4972 bounds.bottom = -10;
4973 bounds.right = -10;
4974 bounds.left = -16;
4976 sb->id = NewControl(gui.VimWindow,
4977 &bounds,
4978 "\pScrollBar",
4979 TRUE,
4980 0, /* current*/
4981 0, /* top */
4982 0, /* bottom */
4983 kControlScrollBarLiveProc,
4984 (long) sb->ident);
4985 #ifdef DEBUG_MAC_SB
4986 printf("create_sb (%x) %x\n",sb->id, orient);
4987 #endif
4990 void
4991 gui_mch_destroy_scrollbar(scrollbar_T *sb)
4993 gui_mch_set_bg_color(gui.back_pixel);
4994 DisposeControl(sb->id);
4995 #ifdef DEBUG_MAC_SB
4996 printf("dest_sb (%x) \n",sb->id);
4997 #endif
5002 * Cursor blink functions.
5004 * This is a simple state machine:
5005 * BLINK_NONE not blinking at all
5006 * BLINK_OFF blinking, cursor is not shown
5007 * BLINK_ON blinking, cursor is shown
5009 void
5010 gui_mch_set_blinking(long wait, long on, long off)
5012 /* TODO: TODO: TODO: TODO: */
5013 /* blink_waittime = wait;
5014 blink_ontime = on;
5015 blink_offtime = off;*/
5019 * Stop the cursor blinking. Show the cursor if it wasn't shown.
5021 void
5022 gui_mch_stop_blink(void)
5024 gui_update_cursor(TRUE, FALSE);
5025 /* TODO: TODO: TODO: TODO: */
5026 /* gui_w32_rm_blink_timer();
5027 if (blink_state == BLINK_OFF)
5028 gui_update_cursor(TRUE, FALSE);
5029 blink_state = BLINK_NONE;*/
5033 * Start the cursor blinking. If it was already blinking, this restarts the
5034 * waiting time and shows the cursor.
5036 void
5037 gui_mch_start_blink(void)
5039 gui_update_cursor(TRUE, FALSE);
5040 /* TODO: TODO: TODO: TODO: */
5041 /* gui_w32_rm_blink_timer(); */
5043 /* Only switch blinking on if none of the times is zero */
5044 /* if (blink_waittime && blink_ontime && blink_offtime)
5046 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5047 (TIMERPROC)_OnBlinkTimer);
5048 blink_state = BLINK_ON;
5049 gui_update_cursor(TRUE, FALSE);
5054 * Return the RGB value of a pixel as long.
5056 long_u
5057 gui_mch_get_rgb(guicolor_T pixel)
5059 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5064 #ifdef FEAT_BROWSE
5066 * Pop open a file browser and return the file selected, in allocated memory,
5067 * or NULL if Cancel is hit.
5068 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
5069 * title - Title message for the file browser dialog.
5070 * dflt - Default name of file.
5071 * ext - Default extension to be added to files without extensions.
5072 * initdir - directory in which to open the browser (NULL = current dir)
5073 * filter - Filter for matched files to choose from.
5074 * Has a format like this:
5075 * "C Files (*.c)\0*.c\0"
5076 * "All Files\0*.*\0\0"
5077 * If these two strings were concatenated, then a choice of two file
5078 * filters will be selectable to the user. Then only matching files will
5079 * be shown in the browser. If NULL, the default allows all files.
5081 * *NOTE* - the filter string must be terminated with TWO nulls.
5083 char_u *
5084 gui_mch_browse(
5085 int saving,
5086 char_u *title,
5087 char_u *dflt,
5088 char_u *ext,
5089 char_u *initdir,
5090 char_u *filter)
5092 /* TODO: Add Ammon's safety checl (Dany) */
5093 NavReplyRecord reply;
5094 char_u *fname = NULL;
5095 char_u **fnames = NULL;
5096 long numFiles;
5097 NavDialogOptions navOptions;
5098 OSErr error;
5100 /* Get Navigation Service Defaults value */
5101 NavGetDefaultDialogOptions(&navOptions);
5104 /* TODO: If we get a :browse args, set the Multiple bit. */
5105 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles
5106 | kNavDontAutoTranslate
5107 | kNavDontAddTranslateItems
5108 /* | kNavAllowMultipleFiles */
5109 | kNavAllowStationery;
5111 (void) C2PascalString(title, &navOptions.message);
5112 (void) C2PascalString(dflt, &navOptions.savedFileName);
5113 /* Could set clientName?
5114 * windowTitle? (there's no title bar?)
5117 if (saving)
5119 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5120 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5121 if (!reply.validRecord)
5122 return NULL;
5124 else
5126 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5127 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5128 if (!reply.validRecord)
5129 return NULL;
5132 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5134 NavDisposeReply(&reply);
5136 if (fnames)
5138 fname = fnames[0];
5139 vim_free(fnames);
5142 /* TODO: Shorten the file name if possible */
5143 return fname;
5145 #endif /* FEAT_BROWSE */
5147 #ifdef FEAT_GUI_DIALOG
5149 * Stuff for dialogues
5153 * Create a dialogue dynamically from the parameter strings.
5154 * type = type of dialogue (question, alert, etc.)
5155 * title = dialogue title. may be NULL for default title.
5156 * message = text to display. Dialogue sizes to accommodate it.
5157 * buttons = '\n' separated list of button captions, default first.
5158 * dfltbutton = number of default button.
5160 * This routine returns 1 if the first button is pressed,
5161 * 2 for the second, etc.
5163 * 0 indicates Esc was pressed.
5164 * -1 for unexpected error
5166 * If stubbing out this fn, return 1.
5169 typedef struct
5171 short idx;
5172 short width; /* Size of the text in pixel */
5173 Rect box;
5174 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5176 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5178 static void
5179 macMoveDialogItem(
5180 DialogRef theDialog,
5181 short itemNumber,
5182 short X,
5183 short Y,
5184 Rect *inBox)
5186 #if 0 /* USE_CARBONIZED */
5187 /* Untested */
5188 MoveDialogItem(theDialog, itemNumber, X, Y);
5189 if (inBox != nil)
5190 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5191 #else
5192 short itemType;
5193 Handle itemHandle;
5194 Rect localBox;
5195 Rect *itemBox = &localBox;
5197 if (inBox != nil)
5198 itemBox = inBox;
5200 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5201 OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5202 OffsetRect(itemBox, X, Y);
5203 /* To move a control (like a button) we need to call both
5204 * MoveControl and SetDialogItem. FAQ 6-18 */
5205 if (1) /*(itemType & kControlDialogItem) */
5206 MoveControl((ControlRef) itemHandle, X, Y);
5207 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5208 #endif
5211 static void
5212 macSizeDialogItem(
5213 DialogRef theDialog,
5214 short itemNumber,
5215 short width,
5216 short height)
5218 short itemType;
5219 Handle itemHandle;
5220 Rect itemBox;
5222 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5224 /* When width or height is zero do not change it */
5225 if (width == 0)
5226 width = itemBox.right - itemBox.left;
5227 if (height == 0)
5228 height = itemBox.bottom - itemBox.top;
5230 #if 0 /* USE_CARBONIZED */
5231 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5232 #else
5233 /* Resize the bounding box */
5234 itemBox.right = itemBox.left + width;
5235 itemBox.bottom = itemBox.top + height;
5237 /* To resize a control (like a button) we need to call both
5238 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5239 if (itemType & kControlDialogItem)
5240 SizeControl((ControlRef) itemHandle, width, height);
5242 /* Configure back the item */
5243 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5244 #endif
5247 static void
5248 macSetDialogItemText(
5249 DialogRef theDialog,
5250 short itemNumber,
5251 Str255 itemName)
5253 short itemType;
5254 Handle itemHandle;
5255 Rect itemBox;
5257 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5259 if (itemType & kControlDialogItem)
5260 SetControlTitle((ControlRef) itemHandle, itemName);
5261 else
5262 SetDialogItemText(itemHandle, itemName);
5265 /* TODO: There have been some crashes with dialogs, check your inbox
5266 * (Jussi)
5269 gui_mch_dialog(
5270 int type,
5271 char_u *title,
5272 char_u *message,
5273 char_u *buttons,
5274 int dfltbutton,
5275 char_u *textfield)
5277 Handle buttonDITL;
5278 Handle iconDITL;
5279 Handle inputDITL;
5280 Handle messageDITL;
5281 Handle itemHandle;
5282 Handle iconHandle;
5283 DialogPtr theDialog;
5284 char_u len;
5285 char_u PascalTitle[256]; /* place holder for the title */
5286 char_u name[256];
5287 GrafPtr oldPort;
5288 short itemHit;
5289 char_u *buttonChar;
5290 Rect box;
5291 short button;
5292 short lastButton;
5293 short itemType;
5294 short useIcon;
5295 short width;
5296 short totalButtonWidth = 0; /* the width of all button together
5297 including spacing */
5298 short widestButton = 0;
5299 short dfltButtonEdge = 20; /* gut feeling */
5300 short dfltElementSpacing = 13; /* from IM:V.2-29 */
5301 short dfltIconSideSpace = 23; /* from IM:V.2-29 */
5302 short maximumWidth = 400; /* gut feeling */
5303 short maxButtonWidth = 175; /* gut feeling */
5305 short vertical;
5306 short dialogHeight;
5307 short messageLines = 3;
5308 FontInfo textFontInfo;
5310 vgmDlgItm iconItm;
5311 vgmDlgItm messageItm;
5312 vgmDlgItm inputItm;
5313 vgmDlgItm buttonItm;
5315 WindowRef theWindow;
5317 /* Check 'v' flag in 'guioptions': vertical button placement. */
5318 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5320 /* Create a new Dialog Box from template. */
5321 theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5323 /* Get the WindowRef */
5324 theWindow = GetDialogWindow(theDialog);
5326 /* Hide the window.
5327 * 1. to avoid seeing slow drawing
5328 * 2. to prevent a problem seen while moving dialog item
5329 * within a visible window. (non-Carbon MacOS 9)
5330 * Could be avoided by changing the resource.
5332 HideWindow(theWindow);
5334 /* Change the graphical port to the dialog,
5335 * so we can measure the text with the proper font */
5336 GetPort(&oldPort);
5337 SetPortDialogPort(theDialog);
5339 /* Get the info about the default text,
5340 * used to calculate the height of the message
5341 * and of the text field */
5342 GetFontInfo(&textFontInfo);
5344 /* Set the dialog title */
5345 if (title != NULL)
5347 (void) C2PascalString(title, &PascalTitle);
5348 SetWTitle(theWindow, PascalTitle);
5351 /* Creates the buttons and add them to the Dialog Box. */
5352 buttonDITL = GetResource('DITL', 130);
5353 buttonChar = buttons;
5354 button = 0;
5356 for (;*buttonChar != 0;)
5358 /* Get the name of the button */
5359 button++;
5360 len = 0;
5361 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5363 if (*buttonChar != DLG_HOTKEY_CHAR)
5364 name[++len] = *buttonChar;
5366 if (*buttonChar != 0)
5367 buttonChar++;
5368 name[0] = len;
5370 /* Add the button */
5371 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5373 /* Change the button's name */
5374 macSetDialogItemText(theDialog, button, name);
5376 /* Resize the button to fit its name */
5377 width = StringWidth(name) + 2 * dfltButtonEdge;
5378 /* Limite the size of any button to an acceptable value. */
5379 /* TODO: Should be based on the message width */
5380 if (width > maxButtonWidth)
5381 width = maxButtonWidth;
5382 macSizeDialogItem(theDialog, button, width, 0);
5384 totalButtonWidth += width;
5386 if (width > widestButton)
5387 widestButton = width;
5389 ReleaseResource(buttonDITL);
5390 lastButton = button;
5392 /* Add the icon to the Dialog Box. */
5393 iconItm.idx = lastButton + 1;
5394 iconDITL = GetResource('DITL', 131);
5395 switch (type)
5397 case VIM_GENERIC: useIcon = kNoteIcon;
5398 case VIM_ERROR: useIcon = kStopIcon;
5399 case VIM_WARNING: useIcon = kCautionIcon;
5400 case VIM_INFO: useIcon = kNoteIcon;
5401 case VIM_QUESTION: useIcon = kNoteIcon;
5402 default: useIcon = kStopIcon;
5404 AppendDITL(theDialog, iconDITL, overlayDITL);
5405 ReleaseResource(iconDITL);
5406 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5407 /* TODO: Should the item be freed? */
5408 iconHandle = GetIcon(useIcon);
5409 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5411 /* Add the message to the Dialog box. */
5412 messageItm.idx = lastButton + 2;
5413 messageDITL = GetResource('DITL', 132);
5414 AppendDITL(theDialog, messageDITL, overlayDITL);
5415 ReleaseResource(messageDITL);
5416 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5417 (void) C2PascalString(message, &name);
5418 SetDialogItemText(itemHandle, name);
5419 messageItm.width = StringWidth(name);
5421 /* Add the input box if needed */
5422 if (textfield != NULL)
5424 /* Cheat for now reuse the message and convert to text edit */
5425 inputItm.idx = lastButton + 3;
5426 inputDITL = GetResource('DITL', 132);
5427 AppendDITL(theDialog, inputDITL, overlayDITL);
5428 ReleaseResource(inputDITL);
5429 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5430 /* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5431 (void) C2PascalString(textfield, &name);
5432 SetDialogItemText(itemHandle, name);
5433 inputItm.width = StringWidth(name);
5436 /* Set the <ENTER> and <ESC> button. */
5437 SetDialogDefaultItem(theDialog, dfltbutton);
5438 SetDialogCancelItem(theDialog, 0);
5440 /* Reposition element */
5442 /* Check if we need to force vertical */
5443 if (totalButtonWidth > maximumWidth)
5444 vertical = TRUE;
5446 /* Place icon */
5447 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5448 iconItm.box.right = box.right;
5449 iconItm.box.bottom = box.bottom;
5451 /* Place Message */
5452 messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5453 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent));
5454 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5456 /* Place Input */
5457 if (textfield != NULL)
5459 inputItm.box.left = messageItm.box.left;
5460 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5461 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5462 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5463 /* Convert the static text into a text edit.
5464 * For some reason this change need to be done last (Dany) */
5465 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5466 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5467 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5470 /* Place Button */
5471 if (textfield != NULL)
5473 buttonItm.box.left = inputItm.box.left;
5474 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing;
5476 else
5478 buttonItm.box.left = messageItm.box.left;
5479 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5482 for (button=1; button <= lastButton; button++)
5485 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5486 /* With vertical, it's better to have all button the same lenght */
5487 if (vertical)
5489 macSizeDialogItem(theDialog, button, widestButton, 0);
5490 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5492 /* Calculate position of next button */
5493 if (vertical)
5494 buttonItm.box.top = box.bottom + dfltElementSpacing;
5495 else
5496 buttonItm.box.left = box.right + dfltElementSpacing;
5499 /* Resize the dialog box */
5500 dialogHeight = box.bottom + dfltElementSpacing;
5501 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5503 /* Magic resize */
5504 AutoSizeDialog(theDialog);
5505 /* Need a horizontal resize anyway so not that useful */
5507 /* Display it */
5508 ShowWindow(theWindow);
5509 /* BringToFront(theWindow); */
5510 SelectWindow(theWindow);
5512 /* DrawDialog(theDialog); */
5513 #if 0
5514 GetPort(&oldPort);
5515 SetPortDialogPort(theDialog);
5516 #endif
5518 #ifdef USE_CARBONKEYHANDLER
5519 /* Avoid that we use key events for the main window. */
5520 dialog_busy = TRUE;
5521 #endif
5523 /* Hang until one of the button is hit */
5526 ModalDialog(nil, &itemHit);
5527 } while ((itemHit < 1) || (itemHit > lastButton));
5529 #ifdef USE_CARBONKEYHANDLER
5530 dialog_busy = FALSE;
5531 #endif
5533 /* Copy back the text entered by the user into the param */
5534 if (textfield != NULL)
5536 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5537 GetDialogItemText(itemHandle, (char_u *) &name);
5538 #if IOSIZE < 256
5539 /* Truncate the name to IOSIZE if needed */
5540 if (name[0] > IOSIZE)
5541 name[0] = IOSIZE - 1;
5542 #endif
5543 vim_strncpy(textfield, &name[1], name[0]);
5546 /* Restore the original graphical port */
5547 SetPort(oldPort);
5549 /* Get ride of th edialog (free memory) */
5550 DisposeDialog(theDialog);
5552 return itemHit;
5554 * Usefull thing which could be used
5555 * SetDialogTimeout(): Auto click a button after timeout
5556 * SetDialogTracksCursor() : Get the I-beam cursor over input box
5557 * MoveDialogItem(): Probably better than SetDialogItem
5558 * SizeDialogItem(): (but is it Carbon Only?)
5559 * AutoSizeDialog(): Magic resize of dialog based on text lenght
5562 #endif /* FEAT_DIALOG_GUI */
5565 * Display the saved error message(s).
5567 #ifdef USE_MCH_ERRMSG
5568 void
5569 display_errors(void)
5571 char *p;
5572 char_u pError[256];
5574 if (error_ga.ga_data == NULL)
5575 return;
5577 /* avoid putting up a message box with blanks only */
5578 for (p = (char *)error_ga.ga_data; *p; ++p)
5579 if (!isspace(*p))
5581 if (STRLEN(p) > 255)
5582 pError[0] = 255;
5583 else
5584 pError[0] = STRLEN(p);
5586 STRNCPY(&pError[1], p, pError[0]);
5587 ParamText(pError, nil, nil, nil);
5588 Alert(128, nil);
5589 break;
5590 /* TODO: handled message longer than 256 chars
5591 * use auto-sizeable alert
5592 * or dialog with scrollbars (TextEdit zone)
5595 ga_clear(&error_ga);
5597 #endif
5600 * Get current mouse coordinates in text window.
5602 void
5603 gui_mch_getmouse(int *x, int *y)
5605 Point where;
5607 GetMouse(&where);
5609 *x = where.h;
5610 *y = where.v;
5613 void
5614 gui_mch_setmouse(int x, int y)
5616 /* TODO */
5617 #if 0
5618 /* From FAQ 3-11 */
5620 CursorDevicePtr myMouse;
5621 Point where;
5623 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5624 != NGetTrapAddress(_Unimplemented, ToolTrap))
5626 /* New way */
5629 * Get first devoice with one button.
5630 * This will probably be the standad mouse
5631 * startat head of cursor dev list
5635 myMouse = nil;
5639 /* Get the next cursor device */
5640 CursorDeviceNextDevice(&myMouse);
5642 while ((myMouse != nil) && (myMouse->cntButtons != 1));
5644 CursorDeviceMoveTo(myMouse, x, y);
5646 else
5648 /* Old way */
5649 where.h = x;
5650 where.v = y;
5652 *(Point *)RawMouse = where;
5653 *(Point *)MTemp = where;
5654 *(Ptr) CrsrNew = 0xFFFF;
5656 #endif
5659 void
5660 gui_mch_show_popupmenu(vimmenu_T *menu)
5663 * Clone PopUp to use menu
5664 * Create a object descriptor for the current selection
5665 * Call the procedure
5668 MenuHandle CntxMenu;
5669 Point where;
5670 OSStatus status;
5671 UInt32 CntxType;
5672 SInt16 CntxMenuID;
5673 UInt16 CntxMenuItem;
5674 Str255 HelpName = "";
5675 GrafPtr savePort;
5677 /* Save Current Port: On MacOS X we seem to lose the port */
5678 GetPort(&savePort); /*OSX*/
5680 GetMouse(&where);
5681 LocalToGlobal(&where); /*OSX*/
5682 CntxMenu = menu->submenu_handle;
5684 /* TODO: Get the text selection from Vim */
5686 /* Call to Handle Popup */
5687 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
5688 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
5690 if (status == noErr)
5692 if (CntxType == kCMMenuItemSelected)
5694 /* Handle the menu CntxMenuID, CntxMenuItem */
5695 /* The submenu can be handle directly by gui_mac_handle_menu */
5696 /* But what about the current menu, is the menu changed by
5697 * ContextualMenuSelect */
5698 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
5700 else if (CntxMenuID == kCMShowHelpSelected)
5702 /* Should come up with the help */
5706 /* Restore original Port */
5707 SetPort(savePort); /*OSX*/
5710 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
5711 /* TODO: Is it need for MACOS_X? (Dany) */
5712 void
5713 mch_post_buffer_write(buf_T *buf)
5715 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
5716 Send_KAHL_MOD_AE(buf);
5718 #endif
5720 #ifdef FEAT_TITLE
5722 * Set the window title and icon.
5723 * (The icon is not taken care of).
5725 void
5726 gui_mch_settitle(char_u *title, char_u *icon)
5728 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
5729 * that 256. Even better get it to fit nicely in the titlebar.
5731 #ifdef MACOS_CONVERT
5732 CFStringRef windowTitle;
5733 size_t windowTitleLen;
5734 #else
5735 char_u *pascalTitle;
5736 #endif
5738 if (title == NULL) /* nothing to do */
5739 return;
5741 #ifdef MACOS_CONVERT
5742 windowTitleLen = STRLEN(title);
5743 windowTitle = mac_enc_to_cfstring(title, windowTitleLen);
5745 if (windowTitle)
5747 SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
5748 CFRelease(windowTitle);
5750 #else
5751 pascalTitle = C2Pascal_save(title);
5752 if (pascalTitle != NULL)
5754 SetWTitle(gui.VimWindow, pascalTitle);
5755 vim_free(pascalTitle);
5757 #endif
5759 #endif
5762 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
5766 C2PascalString(char_u *CString, Str255 *PascalString)
5768 char_u *PascalPtr = (char_u *) PascalString;
5769 int len;
5770 int i;
5772 PascalPtr[0] = 0;
5773 if (CString == NULL)
5774 return 0;
5776 len = STRLEN(CString);
5777 if (len > 255)
5778 len = 255;
5780 for (i = 0; i < len; i++)
5781 PascalPtr[i+1] = CString[i];
5783 PascalPtr[0] = len;
5785 return 0;
5789 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
5791 /* From FAQ 8-12 */
5792 Str255 filePascal;
5793 CInfoPBRec myCPB;
5794 OSErr err;
5796 (void) C2PascalString(file, &filePascal);
5798 myCPB.dirInfo.ioNamePtr = filePascal;
5799 myCPB.dirInfo.ioVRefNum = 0;
5800 myCPB.dirInfo.ioFDirIndex = 0;
5801 myCPB.dirInfo.ioDrDirID = 0;
5803 err= PBGetCatInfo(&myCPB, false);
5805 /* vRefNum, dirID, name */
5806 FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
5808 /* TODO: Use an error code mechanism */
5809 return 0;
5813 * Convert a FSSpec to a fuill path
5816 char_u *FullPathFromFSSpec_save(FSSpec file)
5819 * TODO: Add protection for 256 char max.
5822 CInfoPBRec theCPB;
5823 char_u fname[256];
5824 char_u *filenamePtr = fname;
5825 OSErr error;
5826 int folder = 1;
5827 #ifdef USE_UNIXFILENAME
5828 SInt16 dfltVol_vRefNum;
5829 SInt32 dfltVol_dirID;
5830 FSRef refFile;
5831 OSStatus status;
5832 UInt32 pathSize = 256;
5833 char_u pathname[256];
5834 char_u *path = pathname;
5835 #else
5836 Str255 directoryName;
5837 char_u temporary[255];
5838 char_u *temporaryPtr = temporary;
5839 #endif
5841 #ifdef USE_UNIXFILENAME
5842 /* Get the default volume */
5843 /* TODO: Remove as this only work if Vim is on the Boot Volume*/
5844 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
5846 if (error)
5847 return NULL;
5848 #endif
5850 /* Start filling fname with file.name */
5851 vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
5853 /* Get the info about the file specified in FSSpec */
5854 theCPB.dirInfo.ioFDirIndex = 0;
5855 theCPB.dirInfo.ioNamePtr = file.name;
5856 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5857 /*theCPB.hFileInfo.ioDirID = 0;*/
5858 theCPB.dirInfo.ioDrDirID = file.parID;
5860 /* As ioFDirIndex = 0, get the info of ioNamePtr,
5861 which is relative to ioVrefNum, ioDirID */
5862 error = PBGetCatInfo(&theCPB, false);
5864 /* If we are called for a new file we expect fnfErr */
5865 if ((error) && (error != fnfErr))
5866 return NULL;
5868 /* Check if it's a file or folder */
5869 /* default to file if file don't exist */
5870 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
5871 folder = 0; /* It's not a folder */
5872 else
5873 folder = 1;
5875 #ifdef USE_UNIXFILENAME
5877 * The function used here are available in Carbon, but
5878 * do nothing une MacOS 8 and 9
5880 if (error == fnfErr)
5882 /* If the file to be saved does not already exist, it isn't possible
5883 to convert its FSSpec into an FSRef. But we can construct an
5884 FSSpec for the file's parent folder (since we have its volume and
5885 directory IDs), and since that folder does exist, we can convert
5886 that FSSpec into an FSRef, convert the FSRef in turn into a path,
5887 and, finally, append the filename. */
5888 FSSpec dirSpec;
5889 FSRef dirRef;
5890 Str255 emptyFilename = "\p";
5891 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
5892 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
5893 if (error)
5894 return NULL;
5896 error = FSpMakeFSRef(&dirSpec, &dirRef);
5897 if (error)
5898 return NULL;
5900 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
5901 if (status)
5902 return NULL;
5904 STRCAT(path, "/");
5905 STRCAT(path, filenamePtr);
5907 else
5909 /* If the file to be saved already exists, we can get its full path
5910 by converting its FSSpec into an FSRef. */
5911 error=FSpMakeFSRef(&file, &refFile);
5912 if (error)
5913 return NULL;
5915 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
5916 if (status)
5917 return NULL;
5920 /* Add a slash at the end if needed */
5921 if (folder)
5922 STRCAT(path, "/");
5924 return (vim_strsave(path));
5925 #else
5926 /* TODO: Get rid of all USE_UNIXFILENAME below */
5927 /* Set ioNamePtr, it's the same area which is always reused. */
5928 theCPB.dirInfo.ioNamePtr = directoryName;
5930 /* Trick for first entry, set ioDrParID to the first value
5931 * we want for ioDrDirID*/
5932 theCPB.dirInfo.ioDrParID = file.parID;
5933 theCPB.dirInfo.ioDrDirID = file.parID;
5935 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
5938 theCPB.dirInfo.ioFDirIndex = -1;
5939 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
5940 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5941 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
5942 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
5944 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
5945 /* *ioNamePtr[0 TO 31] will be updated */
5946 error = PBGetCatInfo(&theCPB,false);
5948 if (error)
5949 return NULL;
5951 /* Put the new directoryName in front of the current fname */
5952 STRCPY(temporaryPtr, filenamePtr);
5953 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
5954 STRCAT(filenamePtr, ":");
5955 STRCAT(filenamePtr, temporaryPtr);
5957 #if 1 /* def USE_UNIXFILENAME */
5958 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
5959 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
5960 #else
5961 while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
5962 #endif
5964 /* Get the information about the volume on which the file reside */
5965 theCPB.dirInfo.ioFDirIndex = -1;
5966 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
5967 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5968 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
5969 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
5971 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
5972 /* *ioNamePtr[0 TO 31] will be updated */
5973 error = PBGetCatInfo(&theCPB,false);
5975 if (error)
5976 return NULL;
5978 /* For MacOS Classic always add the volume name */
5979 /* For MacOS X add the volume name preceded by "Volumes" */
5980 /* when we are not referring to the boot volume */
5981 #ifdef USE_UNIXFILENAME
5982 if (file.vRefNum != dfltVol_vRefNum)
5983 #endif
5985 /* Add the volume name */
5986 STRCPY(temporaryPtr, filenamePtr);
5987 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
5988 STRCAT(filenamePtr, ":");
5989 STRCAT(filenamePtr, temporaryPtr);
5991 #ifdef USE_UNIXFILENAME
5992 STRCPY(temporaryPtr, filenamePtr);
5993 filenamePtr[0] = 0; /* NULL terminate the string */
5994 STRCAT(filenamePtr, "Volumes:");
5995 STRCAT(filenamePtr, temporaryPtr);
5996 #endif
5999 /* Append final path separator if it's a folder */
6000 if (folder)
6001 STRCAT(fname, ":");
6003 /* As we use Unix File Name for MacOS X convert it */
6004 #ifdef USE_UNIXFILENAME
6005 /* Need to insert leading / */
6006 /* TODO: get the above code to use directly the / */
6007 STRCPY(&temporaryPtr[1], filenamePtr);
6008 temporaryPtr[0] = '/';
6009 STRCPY(filenamePtr, temporaryPtr);
6011 char *p;
6012 for (p = fname; *p; p++)
6013 if (*p == ':')
6014 *p = '/';
6016 #endif
6018 return (vim_strsave(fname));
6019 #endif
6022 #if defined(USE_IM_CONTROL) || defined(PROTO)
6024 * Input Method Control functions.
6028 * Notify cursor position to IM.
6030 void
6031 im_set_position(int row, int col)
6033 /* TODO: Implement me! */
6037 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6039 void
6040 im_set_active(int active)
6042 KeyScript(active ? smKeySysScript : smKeyRoman);
6046 * Get IM status. When IM is on, return not 0. Else return 0.
6049 im_get_status(void)
6051 SInt32 script = GetScriptManagerVariable(smKeyScript);
6052 return (script != smRoman
6053 && script == GetScriptManagerVariable(smSysScript)) ? 1 : 0;
6056 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6061 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6062 // drawer implementation
6063 static MenuRef contextMenu = NULL;
6064 enum
6066 kTabContextMenuId = 42,
6069 // the caller has to CFRelease() the returned string
6070 static CFStringRef
6071 getTabLabel(tabpage_T *page)
6073 get_tabline_label(page, FALSE);
6074 #ifdef MACOS_CONVERT
6075 return mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6076 #else
6077 // TODO: check internal encoding?
6078 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6079 kCFStringEncodingMacRoman);
6080 #endif
6084 #define DRAWER_SIZE 150
6085 #define DRAWER_INSET 16
6087 static ControlRef dataBrowser = NULL;
6089 // when the tabline is hidden, vim doesn't call update_tabline(). When
6090 // the tabline is shown again, show_tabline() is called before upate_tabline(),
6091 // and because of this, the tab labels and vims internal tabs are out of sync
6092 // for a very short time. to prevent inconsistent state, we store the labels
6093 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6094 static CFStringRef *tabLabels = NULL;
6095 static int tabLabelsSize = 0;
6097 enum
6099 kTabsColumn = 'Tabs'
6102 static int
6103 getTabCount(void)
6105 tabpage_T *tp;
6106 int numTabs = 0;
6108 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6109 ++numTabs;
6110 return numTabs;
6113 // data browser item display callback
6114 static OSStatus
6115 dbItemDataCallback(ControlRef browser,
6116 DataBrowserItemID itemID,
6117 DataBrowserPropertyID property /* column id */,
6118 DataBrowserItemDataRef itemData,
6119 Boolean changeValue)
6121 OSStatus status = noErr;
6123 // assert(property == kTabsColumn); // why is this violated??
6125 // changeValue is true if we have a modifieable list and data was changed.
6126 // In our case, it's always false.
6127 // (that is: if (changeValue) updateInternalData(); else return
6128 // internalData();
6129 if (!changeValue)
6131 CFStringRef str;
6133 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6134 str = tabLabels[itemID - 1];
6135 status = SetDataBrowserItemDataText(itemData, str);
6137 else
6138 status = errDataBrowserPropertyNotSupported;
6140 return status;
6143 // data browser action callback
6144 static void
6145 dbItemNotificationCallback(ControlRef browser,
6146 DataBrowserItemID item,
6147 DataBrowserItemNotification message)
6149 switch (message)
6151 case kDataBrowserItemSelected:
6152 send_tabline_event(item);
6153 break;
6157 // callbacks needed for contextual menu:
6158 static void
6159 dbGetContextualMenuCallback(ControlRef browser,
6160 MenuRef *menu,
6161 UInt32 *helpType,
6162 CFStringRef *helpItemString,
6163 AEDesc *selection)
6165 // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6166 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6167 *helpItemString = NULL;
6169 *menu = contextMenu;
6172 static void
6173 dbSelectContextualMenuCallback(ControlRef browser,
6174 MenuRef menu,
6175 UInt32 selectionType,
6176 SInt16 menuID,
6177 MenuItemIndex menuItem)
6179 if (selectionType == kCMMenuItemSelected)
6181 MenuCommand command;
6182 GetMenuItemCommandID(menu, menuItem, &command);
6184 // get tab that was selected when the context menu appeared
6185 // (there is always one tab selected). TODO: check if the context menu
6186 // isn't opened on an item but on empty space (has to be possible some
6187 // way, the finder does it too ;-) )
6188 Handle items = NewHandle(0);
6189 if (items != NULL)
6191 int numItems;
6193 GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6194 kDataBrowserItemIsSelected, items);
6195 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6196 if (numItems > 0)
6198 int idx;
6199 DataBrowserItemID *itemsPtr;
6201 HLock(items);
6202 itemsPtr = (DataBrowserItemID *)*items;
6203 idx = itemsPtr[0];
6204 HUnlock(items);
6205 send_tabline_menu_event(idx, command);
6207 DisposeHandle(items);
6212 // focus callback of the data browser to always leave focus in vim
6213 static OSStatus
6214 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6216 assert(GetEventClass(event) == kEventClassControl
6217 && GetEventKind(event) == kEventControlSetFocusPart);
6219 return paramErr;
6223 // drawer callback to resize data browser to drawer size
6224 static OSStatus
6225 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6227 switch (GetEventKind(event))
6229 case kEventWindowBoundsChanged: // move or resize
6231 UInt32 attribs;
6232 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6233 NULL, sizeof(attribs), NULL, &attribs);
6234 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6236 Rect r;
6237 GetWindowBounds(drawer, kWindowContentRgn, &r);
6238 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6239 SetControlBounds(dataBrowser, &r);
6240 SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6241 kTabsColumn, r.right);
6244 break;
6247 return eventNotHandledErr;
6250 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6251 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6252 // blue highlights in the list view on these systems, though. Oh well.)
6255 #import <mach-o/dyld.h>
6257 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6259 static OSStatus
6260 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6261 OptionBits inAttributesToSet,
6262 OptionBits inAttributesToClear)
6264 long osVersion;
6265 char *symbolName;
6266 NSSymbol symbol = NULL;
6267 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6268 OptionBits inAttributesToSet, OptionBits inAttributesToClear);
6270 Gestalt(gestaltSystemVersion, &osVersion);
6271 if (osVersion < 0x1040) // only supported for 10.4 (and up)
6272 return noErr;
6274 // C name mangling...
6275 symbolName = "_DataBrowserChangeAttributes";
6276 if (!NSIsSymbolNameDefined(symbolName)
6277 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6278 return noErr;
6280 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6281 if (dataBrowserChangeAttributes == NULL)
6282 return noErr; // well...
6283 return dataBrowserChangeAttributes(inDataBrowser,
6284 inAttributesToSet, inAttributesToClear);
6287 static void
6288 initialise_tabline(void)
6290 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6291 DataBrowserCallbacks dbCallbacks;
6292 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6293 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6294 DataBrowserListViewColumnDesc colDesc;
6296 // drawers have to have compositing enabled
6297 CreateNewWindow(kDrawerWindowClass,
6298 kWindowStandardHandlerAttribute
6299 | kWindowCompositingAttribute
6300 | kWindowResizableAttribute
6301 | kWindowLiveResizeAttribute,
6302 &drawerRect, &drawer);
6304 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6305 SetDrawerParent(drawer, gui.VimWindow);
6306 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6309 // create list view embedded in drawer
6310 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6311 &dataBrowser);
6313 dbCallbacks.version = kDataBrowserLatestCallbacks;
6314 InitDataBrowserCallbacks(&dbCallbacks);
6315 dbCallbacks.u.v1.itemDataCallback =
6316 NewDataBrowserItemDataUPP(dbItemDataCallback);
6317 dbCallbacks.u.v1.itemNotificationCallback =
6318 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6319 dbCallbacks.u.v1.getContextualMenuCallback =
6320 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6321 dbCallbacks.u.v1.selectContextualMenuCallback =
6322 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6324 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6326 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6327 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6328 SetDataBrowserSelectionFlags(dataBrowser,
6329 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6330 SetDataBrowserTableViewHiliteStyle(dataBrowser,
6331 kDataBrowserTableViewFillHilite);
6332 Boolean b = false;
6333 SetControlData(dataBrowser, kControlEntireControl,
6334 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6336 // enable blue background in data browser (this is only in 10.4 and vim
6337 // has to support older osx versions as well, so we have to load this
6338 // function dynamically)
6339 myDataBrowserChangeAttributes(dataBrowser,
6340 kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6342 // install callback that keeps focus in vim and away from the data browser
6343 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6344 NULL, NULL);
6346 // install callback that keeps data browser at the size of the drawer
6347 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6348 NULL, NULL);
6350 // add "tabs" column to data browser
6351 colDesc.propertyDesc.propertyID = kTabsColumn;
6352 colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6354 // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6355 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6357 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6358 colDesc.headerBtnDesc.minimumWidth = 100;
6359 colDesc.headerBtnDesc.maximumWidth = 150;
6360 colDesc.headerBtnDesc.titleOffset = 0;
6361 colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6362 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6363 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6364 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6366 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6368 // create tabline popup menu required by vim docs (see :he tabline-menu)
6369 CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6370 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6371 TABLINE_MENU_CLOSE, NULL);
6372 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6373 TABLINE_MENU_NEW, NULL);
6374 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6375 TABLINE_MENU_OPEN, NULL);
6380 * Show or hide the tabline.
6382 void
6383 gui_mch_show_tabline(int showit)
6385 if (showit == 0)
6386 CloseDrawer(drawer, true);
6387 else
6388 OpenDrawer(drawer, kWindowEdgeRight, true);
6392 * Return TRUE when tabline is displayed.
6395 gui_mch_showing_tabline(void)
6397 WindowDrawerState state = GetDrawerState(drawer);
6399 return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6403 * Update the labels of the tabline.
6405 void
6406 gui_mch_update_tabline(void)
6408 tabpage_T *tp;
6409 int numTabs = getTabCount();
6410 int nr = 1;
6411 int curtabidx = 1;
6413 // adjust data browser
6414 if (tabLabels != NULL)
6416 int i;
6418 for (i = 0; i < tabLabelsSize; ++i)
6419 CFRelease(tabLabels[i]);
6420 free(tabLabels);
6422 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6423 tabLabelsSize = numTabs;
6425 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6427 if (tp == curtab)
6428 curtabidx = nr;
6429 tabLabels[nr-1] = getTabLabel(tp);
6432 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6433 kDataBrowserItemNoProperty);
6434 // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6435 // can pass NULL for the id array
6436 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6437 kDataBrowserItemNoProperty);
6439 DataBrowserItemID item = curtabidx;
6440 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6444 * Set the current tab to "nr". First tab is 1.
6446 void
6447 gui_mch_set_curtab(nr)
6448 int nr;
6450 DataBrowserItemID item = nr;
6451 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6453 // TODO: call something like this?: (or restore scroll position, or...)
6454 RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6455 kDataBrowserRevealOnly);
6458 #endif // FEAT_GUI_TABLINE