Fix ":set go+=c" and menu autoenabling bugs.
[MacVim/jjgod.git] / src / gui_mac.c
blobadc8ddc1f19884958b875d7c345b5670f07fe185
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;
1049 int fnum = -1;
1051 /* these are the initial files dropped on the Vim icon */
1052 for (i = 0 ; i < numFiles; i++)
1054 if (ga_grow(&global_alist.al_ga, 1) == FAIL
1055 || (p = vim_strsave(fnames[i])) == NULL)
1056 mch_exit(2);
1057 else
1058 alist_add(&global_alist, p, 2);
1059 if (fnum == -1)
1060 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum;
1063 /* If the file name was already in the buffer list we need to switch
1064 * to it. */
1065 if (curbuf->b_fnum != fnum)
1067 char_u cmd[30];
1069 vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum);
1070 do_cmdline_cmd(cmd);
1073 /* Change directory to the location of the first file. */
1074 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1075 shorten_fnames(TRUE);
1077 goto finished;
1080 /* Handle the drop, :edit to get to the file */
1081 handle_drop(numFiles, fnames, FALSE);
1083 /* TODO: Handle the goto/select line more cleanly */
1084 if ((numFiles == 1) & (gotPosition))
1086 if (thePosition.lineNum >= 0)
1088 lnum = thePosition.lineNum + 1;
1089 /* oap->motion_type = MLINE;
1090 setpcmark();*/
1091 if (lnum < 1L)
1092 lnum = 1L;
1093 else if (lnum > curbuf->b_ml.ml_line_count)
1094 lnum = curbuf->b_ml.ml_line_count;
1095 curwin->w_cursor.lnum = lnum;
1096 curwin->w_cursor.col = 0;
1097 /* beginline(BL_SOL | BL_FIX);*/
1099 else
1100 goto_byte(thePosition.startRange + 1);
1103 /* Update the screen display */
1104 update_screen(NOT_VALID);
1105 #ifdef FEAT_VISUAL
1106 /* Select the text if possible */
1107 if (gotPosition)
1109 VIsual_active = TRUE;
1110 VIsual_select = FALSE;
1111 VIsual = curwin->w_cursor;
1112 if (thePosition.lineNum < 0)
1114 VIsual_mode = 'v';
1115 goto_byte(thePosition.endRange);
1117 else
1119 VIsual_mode = 'V';
1120 VIsual.col = 0;
1123 #endif
1124 setcursor();
1125 out_flush();
1127 /* Fake mouse event to wake from stall */
1128 PostEvent(mouseUp, 0);
1130 finished:
1131 AEDisposeDesc(&theList); /* dispose what we allocated */
1133 error = HandleUnusedParms(theAEvent);
1134 return error;
1141 pascal OSErr
1142 Handle_aevt_oapp_AE(
1143 const AppleEvent *theAEvent,
1144 AppleEvent *theReply,
1145 long refCon)
1147 OSErr error = noErr;
1149 error = HandleUnusedParms(theAEvent);
1150 return error;
1157 pascal OSErr
1158 Handle_aevt_quit_AE(
1159 const AppleEvent *theAEvent,
1160 AppleEvent *theReply,
1161 long refCon)
1163 OSErr error = noErr;
1165 error = HandleUnusedParms(theAEvent);
1166 if (error)
1167 return error;
1169 /* Need to fake a :confirm qa */
1170 do_cmdline_cmd((char_u *)"confirm qa");
1172 return error;
1179 pascal OSErr
1180 Handle_aevt_pdoc_AE(
1181 const AppleEvent *theAEvent,
1182 AppleEvent *theReply,
1183 long refCon)
1185 OSErr error = noErr;
1187 error = HandleUnusedParms(theAEvent);
1189 return error;
1193 * Handling of unknown AppleEvent
1195 * (Just get rid of all the parms)
1197 pascal OSErr
1198 Handle_unknown_AE(
1199 const AppleEvent *theAEvent,
1200 AppleEvent *theReply,
1201 long refCon)
1203 OSErr error = noErr;
1205 error = HandleUnusedParms(theAEvent);
1207 return error;
1212 * Install the various AppleEvent Handlers
1214 OSErr
1215 InstallAEHandlers(void)
1217 OSErr error;
1219 /* install open application handler */
1220 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1221 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1222 if (error)
1224 return error;
1227 /* install quit application handler */
1228 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1229 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1230 if (error)
1232 return error;
1235 /* install open document handler */
1236 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1237 NewAEEventHandlerUPP(HandleODocAE), 0, false);
1238 if (error)
1240 return error;
1243 /* install print document handler */
1244 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1245 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1247 /* Install Core Suite */
1248 /* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1249 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1251 error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1252 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1254 error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1255 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1257 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1258 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1260 error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1261 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1263 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1264 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1266 error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1267 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1269 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1270 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1272 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1273 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1275 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1276 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1278 error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1279 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1281 error = AEInstallEventHandler(kAECoreSuite, kAESave,
1282 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1284 error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1285 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1288 #ifdef FEAT_CW_EDITOR
1290 * Bind codewarrior support handlers
1292 error = AEInstallEventHandler('KAHL', 'GTTX',
1293 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1294 if (error)
1296 return error;
1298 error = AEInstallEventHandler('KAHL', 'SRCH',
1299 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1300 if (error)
1302 return error;
1304 error = AEInstallEventHandler('KAHL', 'MOD ',
1305 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1306 if (error)
1308 return error;
1310 #endif
1312 return error;
1315 #endif /* USE_AEVENT */
1319 * Callback function, installed by InstallFontPanelHandler(), below,
1320 * to handle Font Panel events.
1322 static OSStatus
1323 FontPanelHandler(
1324 EventHandlerCallRef inHandlerCallRef,
1325 EventRef inEvent,
1326 void *inUserData)
1328 if (GetEventKind(inEvent) == kEventFontPanelClosed)
1330 gFontPanelInfo.isPanelVisible = false;
1331 return noErr;
1334 if (GetEventKind(inEvent) == kEventFontSelection)
1336 OSStatus status;
1337 FMFontFamily newFamily;
1338 FMFontSize newSize;
1339 FMFontStyle newStyle;
1341 /* Retrieve the font family ID number. */
1342 status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1343 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1344 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1345 &newFamily);
1346 if (status == noErr)
1347 gFontPanelInfo.family = newFamily;
1349 /* Retrieve the font size. */
1350 status = GetEventParameter(inEvent, kEventParamFMFontSize,
1351 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1352 if (status == noErr)
1353 gFontPanelInfo.size = newSize;
1355 /* Retrieve the font style (bold, etc.). Currently unused. */
1356 status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1357 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1358 if (status == noErr)
1359 gFontPanelInfo.style = newStyle;
1361 return noErr;
1365 static void
1366 InstallFontPanelHandler(void)
1368 EventTypeSpec eventTypes[2];
1369 EventHandlerUPP handlerUPP;
1370 /* EventHandlerRef handlerRef; */
1372 eventTypes[0].eventClass = kEventClassFont;
1373 eventTypes[0].eventKind = kEventFontSelection;
1374 eventTypes[1].eventClass = kEventClassFont;
1375 eventTypes[1].eventKind = kEventFontPanelClosed;
1377 handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1379 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1380 /*userData=*/NULL, /*handlerRef=*/NULL);
1385 * Fill the buffer pointed to by outName with the name and size
1386 * of the font currently selected in the Font Panel.
1388 #define FONT_STYLE_BUFFER_SIZE 32
1389 static void
1390 GetFontPanelSelection(char_u *outName)
1392 Str255 buf;
1393 ByteCount fontNameLen = 0;
1394 ATSUFontID fid;
1395 char_u styleString[FONT_STYLE_BUFFER_SIZE];
1397 if (!outName)
1398 return;
1400 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1402 /* Canonicalize localized font names */
1403 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1404 gFontPanelInfo.style, &fid, NULL) != noErr)
1405 return;
1407 /* Request font name with Mac encoding (otherwise we could
1408 * get an unwanted utf-16 name) */
1409 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1410 kFontNoScriptCode, kFontNoLanguageCode,
1411 255, (char *)outName, &fontNameLen, NULL) != noErr)
1412 return;
1414 /* Only encode font size, because style (bold, italic, etc) is
1415 * already part of the font full name */
1416 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1417 gFontPanelInfo.size/*,
1418 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1419 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1420 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1422 if ((fontNameLen + STRLEN(styleString)) < 255)
1423 STRCPY(outName + fontNameLen, styleString);
1425 else
1427 *outName = NUL;
1433 * ------------------------------------------------------------
1434 * Unfiled yet
1435 * ------------------------------------------------------------
1439 * gui_mac_get_menu_item_index
1441 * Returns the index inside the menu wher
1443 short /* Shoulde we return MenuItemIndex? */
1444 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1446 short index;
1447 short itemIndex = -1;
1448 vimmenu_T *pBrother;
1450 /* Only menu without parent are the:
1451 * -menu in the menubar
1452 * -popup menu
1453 * -toolbar (guess)
1455 * Which are not items anyway.
1457 if (pMenu->parent)
1459 /* Start from the Oldest Brother */
1460 pBrother = pMenu->parent->children;
1461 index = 1;
1462 while ((pBrother) && (itemIndex == -1))
1464 if (pBrother == pMenu)
1465 itemIndex = index;
1466 index++;
1467 pBrother = pBrother->next;
1470 return itemIndex;
1473 static vimmenu_T *
1474 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1476 short index;
1477 vimmenu_T *pChildMenu;
1478 vimmenu_T *pElder = pMenu->parent;
1481 /* Only menu without parent are the:
1482 * -menu in the menubar
1483 * -popup menu
1484 * -toolbar (guess)
1486 * Which are not items anyway.
1489 if ((pElder) && (pElder->submenu_id == menuID))
1491 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1492 pMenu = pMenu->next;
1494 else
1496 for (; pMenu != NULL; pMenu = pMenu->next)
1498 if (pMenu->children != NULL)
1500 pChildMenu = gui_mac_get_vim_menu
1501 (menuID, itemIndex, pMenu->children);
1502 if (pChildMenu)
1504 pMenu = pChildMenu;
1505 break;
1510 return pMenu;
1514 * ------------------------------------------------------------
1515 * MacOS Feedback procedures
1516 * ------------------------------------------------------------
1518 pascal
1519 void
1520 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1522 scrollbar_T *sb;
1523 int value, dragging;
1524 ControlHandle theControlToUse;
1525 int dont_scroll_save = dont_scroll;
1527 theControlToUse = dragged_sb;
1529 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1531 if (sb == NULL)
1532 return;
1534 /* Need to find value by diff between Old Poss New Pos */
1535 value = GetControl32BitValue(theControlToUse);
1536 dragging = (partCode != 0);
1538 /* When "allow_scrollbar" is FALSE still need to remember the new
1539 * position, but don't actually scroll by setting "dont_scroll". */
1540 dont_scroll = !allow_scrollbar;
1541 gui_drag_scrollbar(sb, value, dragging);
1542 dont_scroll = dont_scroll_save;
1545 pascal
1546 void
1547 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1549 /* TODO: have live support */
1550 scrollbar_T *sb, *sb_info;
1551 long data;
1552 long value;
1553 int page;
1554 int dragging = FALSE;
1555 int dont_scroll_save = dont_scroll;
1557 sb = gui_find_scrollbar((long)GetControlReference(theControl));
1559 if (sb == NULL)
1560 return;
1562 if (sb->wp != NULL) /* Left or right scrollbar */
1565 * Careful: need to get scrollbar info out of first (left) scrollbar
1566 * for window, but keep real scrollbar too because we must pass it to
1567 * gui_drag_scrollbar().
1569 sb_info = &sb->wp->w_scrollbars[0];
1571 if (sb_info->size > 5)
1572 page = sb_info->size - 2; /* use two lines of context */
1573 else
1574 page = sb_info->size;
1576 else /* Bottom scrollbar */
1578 sb_info = sb;
1579 page = W_WIDTH(curwin) - 5;
1582 switch (partCode)
1584 case kControlUpButtonPart: data = -1; break;
1585 case kControlDownButtonPart: data = 1; break;
1586 case kControlPageDownPart: data = page; break;
1587 case kControlPageUpPart: data = -page; break;
1588 default: data = 0; break;
1591 value = sb_info->value + data;
1592 /* if (value > sb_info->max)
1593 value = sb_info->max;
1594 else if (value < 0)
1595 value = 0;*/
1597 /* When "allow_scrollbar" is FALSE still need to remember the new
1598 * position, but don't actually scroll by setting "dont_scroll". */
1599 dont_scroll = !allow_scrollbar;
1600 gui_drag_scrollbar(sb, value, dragging);
1601 dont_scroll = dont_scroll_save;
1603 out_flush();
1604 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1606 /* if (sb_info->wp != NULL)
1608 win_T *wp;
1609 int sb_num;
1611 sb_num = 0;
1612 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1613 sb_num++;
1615 if (wp != NULL)
1617 current_scrollbar = sb_num;
1618 scrollbar_value = value;
1619 gui_do_scroll();
1620 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1626 * ------------------------------------------------------------
1627 * MacOS Click Handling procedures
1628 * ------------------------------------------------------------
1633 * Handle a click inside the window, it may happens in the
1634 * scrollbar or the contents.
1636 * TODO: Add support for potential TOOLBAR
1638 void
1639 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1641 Point thePoint;
1642 int_u vimModifiers;
1643 short thePortion;
1644 ControlHandle theControl;
1645 int vimMouseButton;
1646 short dblClick;
1648 thePoint = theEvent->where;
1649 GlobalToLocal(&thePoint);
1650 SelectWindow(whichWindow);
1652 thePortion = FindControl(thePoint, whichWindow, &theControl);
1654 if (theControl != NUL)
1656 /* We hit a scollbar */
1658 if (thePortion != kControlIndicatorPart)
1660 dragged_sb = theControl;
1661 TrackControl(theControl, thePoint, gScrollAction);
1662 dragged_sb = NULL;
1664 else
1666 dragged_sb = theControl;
1667 #if 1
1668 TrackControl(theControl, thePoint, gScrollDrag);
1669 #else
1670 TrackControl(theControl, thePoint, NULL);
1671 #endif
1672 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1673 * button has been released */
1674 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1675 dragged_sb = NULL;
1678 else
1680 /* We are inside the contents */
1682 /* Convert the CTRL, OPTION, SHIFT and CMD key */
1683 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1685 /* Defaults to MOUSE_LEFT as there's only one mouse button */
1686 vimMouseButton = MOUSE_LEFT;
1688 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1689 /* TODO: NEEDED? */
1690 clickIsPopup = FALSE;
1692 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1694 vimMouseButton = MOUSE_RIGHT;
1695 vimModifiers &= ~MOUSE_CTRL;
1696 clickIsPopup = TRUE;
1699 /* Is it a double click ? */
1700 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1702 /* Send the mouse click to Vim */
1703 gui_send_mouse_event(vimMouseButton, thePoint.h,
1704 thePoint.v, dblClick, vimModifiers);
1706 /* Create the rectangle around the cursor to detect
1707 * the mouse dragging
1709 #if 0
1710 /* TODO: Do we need to this even for the contextual menu?
1711 * It may be require for popup_setpos, but for popup?
1713 if (vimMouseButton == MOUSE_LEFT)
1714 #endif
1716 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1717 FILL_Y(Y_2_ROW(thePoint.v)),
1718 FILL_X(X_2_COL(thePoint.h)+1),
1719 FILL_Y(Y_2_ROW(thePoint.v)+1));
1721 dragRectEnbl = TRUE;
1722 dragRectControl = kCreateRect;
1728 * Handle the click in the titlebar (to move the window)
1730 void
1731 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1733 Rect movingLimits;
1734 Rect *movingLimitsPtr = &movingLimits;
1736 /* TODO: may try to prevent move outside screen? */
1737 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1738 DragWindow(whichWindow, where, movingLimitsPtr);
1742 * Handle the click in the grow box
1744 void
1745 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1748 long newSize;
1749 unsigned short newWidth;
1750 unsigned short newHeight;
1751 Rect resizeLimits;
1752 Rect *resizeLimitsPtr = &resizeLimits;
1753 Rect NewContentRect;
1755 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1757 /* Set the minimum size */
1758 /* TODO: Should this come from Vim? */
1759 resizeLimits.top = 100;
1760 resizeLimits.left = 100;
1762 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1763 newWidth = NewContentRect.right - NewContentRect.left;
1764 newHeight = NewContentRect.bottom - NewContentRect.top;
1765 gui_resize_shell(newWidth, newHeight);
1766 gui_mch_set_bg_color(gui.back_pixel);
1767 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1771 * Handle the click in the zoom box
1773 static void
1774 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1776 Rect r;
1777 Point p;
1778 short thePart;
1780 /* ideal width is current */
1781 p.h = Columns * gui.char_width + 2 * gui.border_offset;
1782 if (gui.which_scrollbars[SBAR_LEFT])
1783 p.h += gui.scrollbar_width;
1784 if (gui.which_scrollbars[SBAR_RIGHT])
1785 p.h += gui.scrollbar_width;
1786 /* ideal height is as heigh as we can get */
1787 p.v = 15 * 1024;
1789 thePart = IsWindowInStandardState(whichWindow, &p, &r)
1790 ? inZoomIn : inZoomOut;
1792 if (!TrackBox(whichWindow, theEvent->where, thePart))
1793 return;
1795 /* use returned width */
1796 p.h = r.right - r.left;
1797 /* adjust returned height */
1798 p.v = r.bottom - r.top - 2 * gui.border_offset;
1799 if (gui.which_scrollbars[SBAR_BOTTOM])
1800 p.v -= gui.scrollbar_height;
1801 p.v -= p.v % gui.char_height;
1802 p.v += 2 * gui.border_width;
1803 if (gui.which_scrollbars[SBAR_BOTTOM]);
1804 p.v += gui.scrollbar_height;
1806 ZoomWindowIdeal(whichWindow, thePart, &p);
1808 GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1809 gui_resize_shell(r.right - r.left, r.bottom - r.top);
1810 gui_mch_set_bg_color(gui.back_pixel);
1811 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1815 * ------------------------------------------------------------
1816 * MacOS Event Handling procedure
1817 * ------------------------------------------------------------
1821 * Handle the Update Event
1824 void
1825 gui_mac_doUpdateEvent(EventRecord *event)
1827 WindowPtr whichWindow;
1828 GrafPtr savePort;
1829 RgnHandle updateRgn;
1830 Rect updateRect;
1831 Rect *updateRectPtr;
1832 Rect rc;
1833 Rect growRect;
1834 RgnHandle saveRgn;
1837 updateRgn = NewRgn();
1838 if (updateRgn == NULL)
1839 return;
1841 /* This could be done by the caller as we
1842 * don't require anything else out of the event
1844 whichWindow = (WindowPtr) event->message;
1846 /* Save Current Port */
1847 GetPort(&savePort);
1849 /* Select the Window's Port */
1850 SetPortWindowPort(whichWindow);
1852 /* Let's update the window */
1853 BeginUpdate(whichWindow);
1854 /* Redraw the biggest rectangle covering the area
1855 * to be updated.
1857 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1858 # if 0
1859 /* Would be more appropriate to use the following but doesn't
1860 * seem to work under MacOS X (Dany)
1862 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1863 # endif
1865 /* Use the HLock useless in Carbon? Is it harmful?*/
1866 HLock((Handle) updateRgn);
1868 updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1869 # if 0
1870 /* Code from original Carbon Port (using GetWindowRegion.
1871 * I believe the UpdateRgn is already in local (Dany)
1873 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1874 GlobalToLocal(&botRight(updateRect));
1875 # endif
1876 /* Update the content (i.e. the text) */
1877 gui_redraw(updateRectPtr->left, updateRectPtr->top,
1878 updateRectPtr->right - updateRectPtr->left,
1879 updateRectPtr->bottom - updateRectPtr->top);
1880 /* Clear the border areas if needed */
1881 gui_mch_set_bg_color(gui.back_pixel);
1882 if (updateRectPtr->left < FILL_X(0))
1884 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1885 EraseRect(&rc);
1887 if (updateRectPtr->top < FILL_Y(0))
1889 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1890 EraseRect(&rc);
1892 if (updateRectPtr->right > FILL_X(Columns))
1894 SetRect(&rc, FILL_X(Columns), 0,
1895 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1896 EraseRect(&rc);
1898 if (updateRectPtr->bottom > FILL_Y(Rows))
1900 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1901 FILL_Y(Rows) + gui.border_offset);
1902 EraseRect(&rc);
1904 HUnlock((Handle) updateRgn);
1905 DisposeRgn(updateRgn);
1907 /* Update scrollbars */
1908 DrawControls(whichWindow);
1910 /* Update the GrowBox */
1911 /* Taken from FAQ 33-27 */
1912 saveRgn = NewRgn();
1913 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1914 GetClip(saveRgn);
1915 ClipRect(&growRect);
1916 DrawGrowIcon(whichWindow);
1917 SetClip(saveRgn);
1918 DisposeRgn(saveRgn);
1919 EndUpdate(whichWindow);
1921 /* Restore original Port */
1922 SetPort(savePort);
1926 * Handle the activate/deactivate event
1927 * (apply to a window)
1929 void
1930 gui_mac_doActivateEvent(EventRecord *event)
1932 WindowPtr whichWindow;
1934 whichWindow = (WindowPtr) event->message;
1935 /* Dim scrollbars */
1936 if (whichWindow == gui.VimWindow)
1938 ControlRef rootControl;
1939 GetRootControl(gui.VimWindow, &rootControl);
1940 if ((event->modifiers) & activeFlag)
1941 ActivateControl(rootControl);
1942 else
1943 DeactivateControl(rootControl);
1946 /* Activate */
1947 gui_focus_change((event->modifiers) & activeFlag);
1952 * Handle the suspend/resume event
1953 * (apply to the application)
1955 void
1956 gui_mac_doSuspendEvent(EventRecord *event)
1958 /* The frontmost application just changed */
1960 /* NOTE: the suspend may happen before the deactivate
1961 * seen on MacOS X
1964 /* May not need to change focus as the window will
1965 * get an activate/deactivate event
1967 if (event->message & 1)
1968 /* Resume */
1969 gui_focus_change(TRUE);
1970 else
1971 /* Suspend */
1972 gui_focus_change(FALSE);
1976 * Handle the key
1978 #ifdef USE_CARBONKEYHANDLER
1980 static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the keys */
1982 # define INLINE_KEY_BUFFER_SIZE 80
1983 static pascal OSStatus
1984 gui_mac_doKeyEventCarbon(
1985 EventHandlerCallRef nextHandler,
1986 EventRef theEvent,
1987 void *data)
1989 /* Multibyte-friendly key event handler */
1990 OSStatus err = -1;
1991 UInt32 actualSize;
1992 UniChar *text;
1993 char_u result[INLINE_KEY_BUFFER_SIZE];
1994 short len = 0;
1995 UInt32 key_sym;
1996 char charcode;
1997 int key_char;
1998 UInt32 modifiers, vimModifiers;
1999 size_t encLen;
2000 char_u *to = NULL;
2001 Boolean isSpecial = FALSE;
2002 int i;
2003 EventRef keyEvent;
2005 /* Mask the mouse (as per user setting) */
2006 if (p_mh)
2007 ObscureCursor();
2009 /* Don't use the keys when the dialog wants them. */
2010 if (dialog_busy)
2011 return eventNotHandledErr;
2013 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2014 typeUnicodeText, NULL, 0, &actualSize, NULL))
2015 return eventNotHandledErr;
2017 text = (UniChar *)alloc(actualSize);
2018 if (!text)
2019 return eventNotHandledErr;
2021 err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2022 typeUnicodeText, NULL, actualSize, NULL, text);
2023 require_noerr(err, done);
2025 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2026 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2027 require_noerr(err, done);
2029 err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2030 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2031 require_noerr(err, done);
2033 err = GetEventParameter(keyEvent, kEventParamKeyCode,
2034 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2035 require_noerr(err, done);
2037 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2038 typeChar, NULL, sizeof(char), NULL, &charcode);
2039 require_noerr(err, done);
2041 #ifndef USE_CMD_KEY
2042 if (modifiers & cmdKey)
2043 goto done; /* Let system handle Cmd+... */
2044 #endif
2046 key_char = charcode;
2047 vimModifiers = EventModifiers2VimModifiers(modifiers);
2049 /* Find the special key (eg., for cursor keys) */
2050 if (actualSize <= sizeof(UniChar) &&
2051 ((text[0] < 0x20) || (text[0] == 0x7f)))
2053 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2054 if (special_keys[i].key_sym == key_sym)
2056 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2057 special_keys[i].vim_code1);
2058 key_char = simplify_key(key_char,
2059 (int *)&vimModifiers);
2060 isSpecial = TRUE;
2061 break;
2065 /* Intercept CMD-. and CTRL-c */
2066 if (((modifiers & controlKey) && key_char == 'c') ||
2067 ((modifiers & cmdKey) && key_char == '.'))
2068 got_int = TRUE;
2070 if (!isSpecial)
2072 /* remove SHIFT for keys that are already shifted, e.g.,
2073 * '(' and '*' */
2074 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2075 vimModifiers &= ~MOD_MASK_SHIFT;
2077 /* remove CTRL from keys that already have it */
2078 if (key_char < 0x20)
2079 vimModifiers &= ~MOD_MASK_CTRL;
2081 /* don't process unicode characters here */
2082 if (!IS_SPECIAL(key_char))
2084 /* Following code to simplify and consolidate vimModifiers
2085 * taken liberally from gui_w48.c */
2086 key_char = simplify_key(key_char, (int *)&vimModifiers);
2088 /* Interpret META, include SHIFT, etc. */
2089 key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2090 if (key_char == CSI)
2091 key_char = K_CSI;
2093 if (IS_SPECIAL(key_char))
2094 isSpecial = TRUE;
2098 if (vimModifiers)
2100 result[len++] = CSI;
2101 result[len++] = KS_MODIFIER;
2102 result[len++] = vimModifiers;
2105 if (isSpecial && IS_SPECIAL(key_char))
2107 result[len++] = CSI;
2108 result[len++] = K_SECOND(key_char);
2109 result[len++] = K_THIRD(key_char);
2111 else
2113 encLen = actualSize;
2114 to = mac_utf16_to_enc(text, actualSize, &encLen);
2115 if (to)
2117 /* This is basically add_to_input_buf_csi() */
2118 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2120 result[len++] = to[i];
2121 if (to[i] == CSI)
2123 result[len++] = KS_EXTRA;
2124 result[len++] = (int)KE_CSI;
2127 vim_free(to);
2131 add_to_input_buf(result, len);
2132 err = noErr;
2134 done:
2135 vim_free(text);
2136 if (err == noErr)
2138 /* Fake event to wake up WNE (required to get
2139 * key repeat working */
2140 PostEvent(keyUp, 0);
2141 return noErr;
2144 return eventNotHandledErr;
2146 #else
2147 void
2148 gui_mac_doKeyEvent(EventRecord *theEvent)
2150 /* TODO: add support for COMMAND KEY */
2151 long menu;
2152 unsigned char string[20];
2153 short num, i;
2154 short len = 0;
2155 KeySym key_sym;
2156 int key_char;
2157 int modifiers;
2158 int simplify = FALSE;
2160 /* Mask the mouse (as per user setting) */
2161 if (p_mh)
2162 ObscureCursor();
2164 /* Get the key code and it's ASCII representation */
2165 key_sym = ((theEvent->message & keyCodeMask) >> 8);
2166 key_char = theEvent->message & charCodeMask;
2167 num = 1;
2169 /* Intercept CTRL-C */
2170 if (theEvent->modifiers & controlKey)
2172 if (key_char == Ctrl_C && ctrl_c_interrupts)
2173 got_int = TRUE;
2174 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2175 && (key_char == '2' || key_char == '6'))
2177 /* CTRL-^ and CTRL-@ don't work in the normal way. */
2178 if (key_char == '2')
2179 key_char = Ctrl_AT;
2180 else
2181 key_char = Ctrl_HAT;
2182 theEvent->modifiers = 0;
2186 /* Intercept CMD-. */
2187 if (theEvent->modifiers & cmdKey)
2188 if (key_char == '.')
2189 got_int = TRUE;
2191 /* Handle command key as per menu */
2192 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2193 if (theEvent->modifiers & cmdKey)
2194 /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2195 * Why the mouse button? */
2196 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2198 menu = MenuKey(key_char);
2199 if (HiWord(menu))
2201 gui_mac_handle_menu(menu);
2202 return;
2206 /* Convert the modifiers */
2207 modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2210 /* Handle special keys. */
2211 #if 0
2212 /* Why has this been removed? */
2213 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2214 #endif
2216 /* Find the special key (for non-printable keyt_char) */
2217 if ((key_char < 0x20) || (key_char == 0x7f))
2218 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2219 if (special_keys[i].key_sym == key_sym)
2221 # if 0
2222 /* We currently don't have not so special key */
2223 if (special_keys[i].vim_code1 == NUL)
2224 key_char = special_keys[i].vim_code0;
2225 else
2226 # endif
2227 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2228 special_keys[i].vim_code1);
2229 simplify = TRUE;
2230 break;
2234 /* For some keys the modifier is included in the char itself. */
2235 if (simplify || key_char == TAB || key_char == ' ')
2236 key_char = simplify_key(key_char, &modifiers);
2238 /* Add the modifier to the input bu if needed */
2239 /* Do not want SHIFT-A or CTRL-A with modifier */
2240 if (!IS_SPECIAL(key_char)
2241 && key_sym != vk_Space
2242 && key_sym != vk_Tab
2243 && key_sym != vk_Return
2244 && key_sym != vk_Enter
2245 && key_sym != vk_Esc)
2247 #if 1
2248 /* Clear modifiers when only one modifier is set */
2249 if ((modifiers == MOD_MASK_SHIFT)
2250 || (modifiers == MOD_MASK_CTRL)
2251 || (modifiers == MOD_MASK_ALT))
2252 modifiers = 0;
2253 #else
2254 if (modifiers & MOD_MASK_CTRL)
2255 modifiers = modifiers & ~MOD_MASK_CTRL;
2256 if (modifiers & MOD_MASK_ALT)
2257 modifiers = modifiers & ~MOD_MASK_ALT;
2258 if (modifiers & MOD_MASK_SHIFT)
2259 modifiers = modifiers & ~MOD_MASK_SHIFT;
2260 #endif
2262 if (modifiers)
2264 string[len++] = CSI;
2265 string[len++] = KS_MODIFIER;
2266 string[len++] = modifiers;
2269 if (IS_SPECIAL(key_char))
2271 string[len++] = CSI;
2272 string[len++] = K_SECOND(key_char);
2273 string[len++] = K_THIRD(key_char);
2275 else
2277 #ifdef FEAT_MBYTE
2278 /* Convert characters when needed (e.g., from MacRoman to latin1).
2279 * This doesn't work for the NUL byte. */
2280 if (input_conv.vc_type != CONV_NONE && key_char > 0)
2282 char_u from[2], *to;
2283 int l;
2285 from[0] = key_char;
2286 from[1] = NUL;
2287 l = 1;
2288 to = string_convert(&input_conv, from, &l);
2289 if (to != NULL)
2291 for (i = 0; i < l && len < 19; i++)
2293 if (to[i] == CSI)
2295 string[len++] = KS_EXTRA;
2296 string[len++] = KE_CSI;
2298 else
2299 string[len++] = to[i];
2301 vim_free(to);
2303 else
2304 string[len++] = key_char;
2306 else
2307 #endif
2308 string[len++] = key_char;
2311 if (len == 1 && string[0] == CSI)
2313 /* Turn CSI into K_CSI. */
2314 string[ len++ ] = KS_EXTRA;
2315 string[ len++ ] = KE_CSI;
2318 add_to_input_buf(string, len);
2320 #endif
2323 * Handle MouseClick
2325 void
2326 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2328 short thePart;
2329 WindowPtr whichWindow;
2331 thePart = FindWindow(theEvent->where, &whichWindow);
2333 #ifdef FEAT_GUI_TABLINE
2334 /* prevent that the vim window size changes if it's activated by a
2335 click into the tab pane */
2336 if (whichWindow == drawer)
2337 return;
2338 #endif
2340 switch (thePart)
2342 case (inDesk):
2343 /* TODO: what to do? */
2344 break;
2346 case (inMenuBar):
2347 gui_mac_handle_menu(MenuSelect(theEvent->where));
2348 break;
2350 case (inContent):
2351 gui_mac_doInContentClick(theEvent, whichWindow);
2352 break;
2354 case (inDrag):
2355 gui_mac_doInDragClick(theEvent->where, whichWindow);
2356 break;
2358 case (inGrow):
2359 gui_mac_doInGrowClick(theEvent->where, whichWindow);
2360 break;
2362 case (inGoAway):
2363 if (TrackGoAway(whichWindow, theEvent->where))
2364 gui_shell_closed();
2365 break;
2367 case (inZoomIn):
2368 case (inZoomOut):
2369 gui_mac_doInZoomClick(theEvent, whichWindow);
2370 break;
2375 * Handle MouseMoved
2376 * [this event is a moving in and out of a region]
2378 void
2379 gui_mac_doMouseMovedEvent(EventRecord *event)
2381 Point thePoint;
2382 int_u vimModifiers;
2384 thePoint = event->where;
2385 GlobalToLocal(&thePoint);
2386 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2388 if (!Button())
2389 gui_mouse_moved(thePoint.h, thePoint.v);
2390 else
2391 if (!clickIsPopup)
2392 gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2393 thePoint.v, FALSE, vimModifiers);
2395 /* Reset the region from which we move in and out */
2396 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2397 FILL_Y(Y_2_ROW(thePoint.v)),
2398 FILL_X(X_2_COL(thePoint.h)+1),
2399 FILL_Y(Y_2_ROW(thePoint.v)+1));
2401 if (dragRectEnbl)
2402 dragRectControl = kCreateRect;
2407 * Handle the mouse release
2409 void
2410 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2412 Point thePoint;
2413 int_u vimModifiers;
2415 /* TODO: Properly convert the Contextual menu mouse-up */
2416 /* Potential source of the double menu */
2417 lastMouseTick = theEvent->when;
2418 dragRectEnbl = FALSE;
2419 dragRectControl = kCreateEmpty;
2420 thePoint = theEvent->where;
2421 GlobalToLocal(&thePoint);
2423 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2424 if (clickIsPopup)
2426 vimModifiers &= ~MOUSE_CTRL;
2427 clickIsPopup = FALSE;
2429 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2432 static pascal OSStatus
2433 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2434 void *data)
2436 EventRef bogusEvent;
2437 Point point;
2438 Rect bounds;
2439 UInt32 mod;
2440 SInt32 delta;
2441 int_u vim_mod;
2442 EventMouseWheelAxis axis;
2444 if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis,
2445 typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis)
2446 && axis != kEventMouseWheelAxisY)
2447 goto bail; /* Vim only does up-down scrolling */
2449 if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta,
2450 typeSInt32, NULL, sizeof(SInt32), NULL, &delta))
2451 goto bail;
2452 if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation,
2453 typeQDPoint, NULL, sizeof(Point), NULL, &point))
2454 goto bail;
2455 if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers,
2456 typeUInt32, NULL, sizeof(UInt32), NULL, &mod))
2457 goto bail;
2459 vim_mod = 0;
2460 if (mod & shiftKey)
2461 vim_mod |= MOUSE_SHIFT;
2462 if (mod & controlKey)
2463 vim_mod |= MOUSE_CTRL;
2464 if (mod & optionKey)
2465 vim_mod |= MOUSE_ALT;
2467 /* post a bogus event to wake up WaitNextEvent */
2468 if (noErr != CreateEvent(NULL, kEventClassMouse, kEventMouseMoved, 0,
2469 kEventAttributeNone, &bogusEvent))
2470 goto bail;
2471 if (noErr != PostEventToQueue(GetMainEventQueue(), bogusEvent,
2472 kEventPriorityLow))
2473 goto bail;
2475 ReleaseEvent(bogusEvent);
2477 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2479 point.h -= bounds.left;
2480 point.v -= bounds.top;
2483 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2484 point.h, point.v, FALSE, vim_mod);
2486 return noErr;
2488 bail:
2490 * when we fail give any additional callback handler a chance to perform
2491 * it's actions
2493 return CallNextEventHandler(nextHandler, theEvent);
2496 #if 0
2499 * This would be the normal way of invoking the contextual menu
2500 * but the Vim API doesn't seem to a support a request to get
2501 * the menu that we should display
2503 void
2504 gui_mac_handle_contextual_menu(event)
2505 EventRecord *event;
2508 * Clone PopUp to use menu
2509 * Create a object descriptor for the current selection
2510 * Call the procedure
2513 // Call to Handle Popup
2514 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2516 if (status != noErr)
2517 return;
2519 if (CntxType == kCMMenuItemSelected)
2521 /* Handle the menu CntxMenuID, CntxMenuItem */
2522 /* The submenu can be handle directly by gui_mac_handle_menu */
2523 /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2524 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2526 else if (CntxMenuID == kCMShowHelpSelected)
2528 /* Should come up with the help */
2532 #endif
2535 * Handle menubar selection
2537 void
2538 gui_mac_handle_menu(long menuChoice)
2540 short menu = HiWord(menuChoice);
2541 short item = LoWord(menuChoice);
2542 vimmenu_T *theVimMenu = root_menu;
2544 if (menu == 256) /* TODO: use constant or gui.xyz */
2546 if (item == 1)
2547 gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2549 else if (item != 0)
2551 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2553 if (theVimMenu)
2554 gui_menu_cb(theVimMenu);
2556 HiliteMenu(0);
2560 * Dispatch the event to proper handler
2563 void
2564 gui_mac_handle_event(EventRecord *event)
2566 OSErr error;
2568 /* Handle contextual menu right now (if needed) */
2569 if (IsShowContextualMenuClick(event))
2571 # if 0
2572 gui_mac_handle_contextual_menu(event);
2573 # else
2574 gui_mac_doMouseDownEvent(event);
2575 # endif
2576 return;
2579 /* Handle normal event */
2580 switch (event->what)
2582 #ifndef USE_CARBONKEYHANDLER
2583 case (keyDown):
2584 case (autoKey):
2585 gui_mac_doKeyEvent(event);
2586 break;
2587 #endif
2588 case (keyUp):
2589 /* We don't care about when the key is released */
2590 break;
2592 case (mouseDown):
2593 gui_mac_doMouseDownEvent(event);
2594 break;
2596 case (mouseUp):
2597 gui_mac_doMouseUpEvent(event);
2598 break;
2600 case (updateEvt):
2601 gui_mac_doUpdateEvent(event);
2602 break;
2604 case (diskEvt):
2605 /* We don't need special handling for disk insertion */
2606 break;
2608 case (activateEvt):
2609 gui_mac_doActivateEvent(event);
2610 break;
2612 case (osEvt):
2613 switch ((event->message >> 24) & 0xFF)
2615 case (0xFA): /* mouseMovedMessage */
2616 gui_mac_doMouseMovedEvent(event);
2617 break;
2618 case (0x01): /* suspendResumeMessage */
2619 gui_mac_doSuspendEvent(event);
2620 break;
2622 break;
2624 #ifdef USE_AEVENT
2625 case (kHighLevelEvent):
2626 /* Someone's talking to us, through AppleEvents */
2627 error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2628 break;
2629 #endif
2634 * ------------------------------------------------------------
2635 * Unknown Stuff
2636 * ------------------------------------------------------------
2640 GuiFont
2641 gui_mac_find_font(char_u *font_name)
2643 char_u c;
2644 char_u *p;
2645 char_u pFontName[256];
2646 Str255 systemFontname;
2647 short font_id;
2648 short size=9;
2649 GuiFont font;
2650 #if 0
2651 char_u *fontNamePtr;
2652 #endif
2654 for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2657 c = *p;
2658 *p = 0;
2660 #if 1
2661 STRCPY(&pFontName[1], font_name);
2662 pFontName[0] = STRLEN(font_name);
2663 *p = c;
2665 /* Get the font name, minus the style suffix (:h, etc) */
2666 char_u fontName[256];
2667 char_u *styleStart = vim_strchr(font_name, ':');
2668 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2669 vim_strncpy(fontName, font_name, fontNameLen);
2671 ATSUFontID fontRef;
2672 FMFontStyle fontStyle;
2673 font_id = 0;
2675 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2676 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2677 &fontRef) == noErr)
2679 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2680 font_id = 0;
2683 if (font_id == 0)
2686 * Try again, this time replacing underscores in the font name
2687 * with spaces (:set guifont allows the two to be used
2688 * interchangeably; the Font Manager doesn't).
2690 int i, changed = FALSE;
2692 for (i = pFontName[0]; i > 0; --i)
2694 if (pFontName[i] == '_')
2696 pFontName[i] = ' ';
2697 changed = TRUE;
2700 if (changed)
2701 if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2702 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2703 kFontNoLanguageCode, &fontRef) == noErr)
2705 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2706 font_id = 0;
2710 #else
2711 /* name = C2Pascal_save(menu->dname); */
2712 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2714 GetFNum(fontNamePtr, &font_id);
2715 #endif
2718 if (font_id == 0)
2720 /* Oups, the system font was it the one the user want */
2722 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2723 return NOFONT;
2724 if (!EqualString(pFontName, systemFontname, false, false))
2725 return NOFONT;
2727 if (*p == ':')
2729 p++;
2730 /* Set the values found after ':' */
2731 while (*p)
2733 switch (*p++)
2735 case 'h':
2736 size = points_to_pixels(p, &p, TRUE);
2737 break;
2739 * TODO: Maybe accept width and styles
2742 while (*p == ':')
2743 p++;
2747 if (size < 1)
2748 size = 1; /* Avoid having a size of 0 with system font */
2750 font = (size << 16) + ((long) font_id & 0xFFFF);
2752 return font;
2756 * ------------------------------------------------------------
2757 * GUI_MCH functionality
2758 * ------------------------------------------------------------
2762 * Parse the GUI related command-line arguments. Any arguments used are
2763 * deleted from argv, and *argc is decremented accordingly. This is called
2764 * when vim is started, whether or not the GUI has been started.
2766 void
2767 gui_mch_prepare(int *argc, char **argv)
2769 /* TODO: Move most of this stuff toward gui_mch_init */
2770 #ifdef USE_EXE_NAME
2771 FSSpec applDir;
2772 # ifndef USE_FIND_BUNDLE_PATH
2773 short applVRefNum;
2774 long applDirID;
2775 Str255 volName;
2776 # else
2777 ProcessSerialNumber psn;
2778 FSRef applFSRef;
2779 # endif
2780 #endif
2782 #if 0
2783 InitCursor();
2785 RegisterAppearanceClient();
2787 #ifdef USE_AEVENT
2788 (void) InstallAEHandlers();
2789 #endif
2791 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2793 AppendMenu(pomme, "\pAbout VIM");
2795 InsertMenu(pomme, 0);
2797 DrawMenuBar();
2800 #ifndef USE_OFFSETED_WINDOW
2801 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2802 #else
2803 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2804 #endif
2807 CreateNewWindow(kDocumentWindowClass,
2808 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2809 &windRect, &gui.VimWindow);
2810 SetPortWindowPort(gui.VimWindow);
2812 gui.char_width = 7;
2813 gui.char_height = 11;
2814 gui.char_ascent = 6;
2815 gui.num_rows = 24;
2816 gui.num_cols = 80;
2817 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2819 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2820 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2822 dragRectEnbl = FALSE;
2823 dragRgn = NULL;
2824 dragRectControl = kCreateEmpty;
2825 cursorRgn = NewRgn();
2826 #endif
2827 #ifdef USE_EXE_NAME
2828 # ifndef USE_FIND_BUNDLE_PATH
2829 HGetVol(volName, &applVRefNum, &applDirID);
2830 /* TN2015: mention a possible bad VRefNum */
2831 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2832 # else
2833 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2834 * of TN2015
2835 * This technic remove the ../Contents/MacOS/etc part
2837 (void)GetCurrentProcess(&psn);
2838 /* if (err != noErr) return err; */
2840 (void)GetProcessBundleLocation(&psn, &applFSRef);
2841 /* if (err != noErr) return err; */
2843 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2845 /* This technic return NIL when we disallow_gui */
2846 # endif
2847 exe_name = FullPathFromFSSpec_save(applDir);
2848 #endif
2851 #ifndef ALWAYS_USE_GUI
2853 * Check if the GUI can be started. Called before gvimrc is sourced.
2854 * Return OK or FAIL.
2857 gui_mch_init_check(void)
2859 /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2860 * using the >console
2862 if (disallow_gui) /* see main.c for reason to disallow */
2863 return FAIL;
2864 return OK;
2866 #endif
2868 static OSErr
2869 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2871 int x, y;
2872 int_u modifiers;
2873 char_u **fnames = NULL;
2874 int count;
2875 int i, j;
2877 /* Get drop position, modifiers and count of items */
2879 Point point;
2880 SInt16 mouseUpModifiers;
2881 UInt16 countItem;
2883 GetDragMouse(theDrag, &point, NULL);
2884 GlobalToLocal(&point);
2885 x = point.h;
2886 y = point.v;
2887 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2888 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2889 CountDragItems(theDrag, &countItem);
2890 count = countItem;
2893 fnames = (char_u **)alloc(count * sizeof(char_u *));
2894 if (fnames == NULL)
2895 return dragNotAcceptedErr;
2897 /* Get file names dropped */
2898 for (i = j = 0; i < count; ++i)
2900 DragItemRef item;
2901 OSErr err;
2902 Size size;
2903 FlavorType type = flavorTypeHFS;
2904 HFSFlavor hfsFlavor;
2906 fnames[i] = NULL;
2907 GetDragItemReferenceNumber(theDrag, i + 1, &item);
2908 err = GetFlavorDataSize(theDrag, item, type, &size);
2909 if (err != noErr || size > sizeof(hfsFlavor))
2910 continue;
2911 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
2912 if (err != noErr)
2913 continue;
2914 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
2916 count = j;
2918 gui_handle_drop(x, y, modifiers, fnames, count);
2920 /* Fake mouse event to wake from stall */
2921 PostEvent(mouseUp, 0);
2923 return noErr;
2927 * Initialise the GUI. Create all the windows, set up all the call-backs
2928 * etc.
2931 gui_mch_init(void)
2933 /* TODO: Move most of this stuff toward gui_mch_init */
2934 Rect windRect;
2935 MenuHandle pomme;
2936 EventTypeSpec eventTypeSpec;
2937 EventHandlerRef mouseWheelHandlerRef;
2938 #ifdef USE_CARBONKEYHANDLER
2939 EventHandlerRef keyEventHandlerRef;
2940 #endif
2941 ControlRef rootControl;
2943 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
2944 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
2946 #if 1
2947 InitCursor();
2949 RegisterAppearanceClient();
2951 #ifdef USE_AEVENT
2952 (void) InstallAEHandlers();
2953 #endif
2955 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2957 AppendMenu(pomme, "\pAbout VIM");
2959 InsertMenu(pomme, 0);
2961 DrawMenuBar();
2964 #ifndef USE_OFFSETED_WINDOW
2965 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2966 #else
2967 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2968 #endif
2970 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
2971 zoomDocProc,
2972 (WindowPtr)-1L, true, 0);
2973 CreateRootControl(gui.VimWindow, &rootControl);
2974 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
2975 gui.VimWindow, NULL);
2976 SetPortWindowPort(gui.VimWindow);
2978 gui.char_width = 7;
2979 gui.char_height = 11;
2980 gui.char_ascent = 6;
2981 gui.num_rows = 24;
2982 gui.num_cols = 80;
2983 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2985 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2986 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2988 /* Install Carbon event callbacks. */
2989 (void)InstallFontPanelHandler();
2991 dragRectEnbl = FALSE;
2992 dragRgn = NULL;
2993 dragRectControl = kCreateEmpty;
2994 cursorRgn = NewRgn();
2995 #endif
2996 /* Display any pending error messages */
2997 display_errors();
2999 /* Get background/foreground colors from system */
3000 /* TODO: do the appropriate call to get real defaults */
3001 gui.norm_pixel = 0x00000000;
3002 gui.back_pixel = 0x00FFFFFF;
3004 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3005 * file). */
3006 set_normal_colors();
3009 * Check that none of the colors are the same as the background color.
3010 * Then store the current values as the defaults.
3012 gui_check_colors();
3013 gui.def_norm_pixel = gui.norm_pixel;
3014 gui.def_back_pixel = gui.back_pixel;
3016 /* Get the colors for the highlight groups (gui_check_colors() might have
3017 * changed them) */
3018 highlight_gui_started();
3021 * Setting the gui constants
3023 #ifdef FEAT_MENU
3024 gui.menu_height = 0;
3025 #endif
3026 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3027 gui.border_offset = gui.border_width = 2;
3029 /* If Quartz-style text anti aliasing is available (see
3030 gui_mch_draw_string() below), enable it for all font sizes. */
3031 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3033 eventTypeSpec.eventClass = kEventClassMouse;
3034 eventTypeSpec.eventKind = kEventMouseWheelMoved;
3035 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3036 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3037 &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3039 mouseWheelHandlerRef = NULL;
3040 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3041 mouseWheelHandlerUPP = NULL;
3044 #ifdef USE_CARBONKEYHANDLER
3045 eventTypeSpec.eventClass = kEventClassTextInput;
3046 eventTypeSpec.eventKind = kEventUnicodeForKeyEvent;
3047 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_doKeyEventCarbon);
3048 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP, 1,
3049 &eventTypeSpec, NULL, &keyEventHandlerRef))
3051 keyEventHandlerRef = NULL;
3052 DisposeEventHandlerUPP(keyEventHandlerUPP);
3053 keyEventHandlerUPP = NULL;
3055 #endif
3058 #ifdef FEAT_MBYTE
3059 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3060 #endif
3063 #ifdef FEAT_GUI_TABLINE
3065 * Create the tabline
3067 initialise_tabline();
3068 #endif
3070 /* TODO: Load bitmap if using TOOLBAR */
3071 return OK;
3075 * Called when the foreground or background color has been changed.
3077 void
3078 gui_mch_new_colors(void)
3080 /* TODO:
3081 * This proc is called when Normal is set to a value
3082 * so what msut be done? I don't know
3087 * Open the GUI window which was created by a call to gui_mch_init().
3090 gui_mch_open(void)
3092 ShowWindow(gui.VimWindow);
3094 if (gui_win_x != -1 && gui_win_y != -1)
3095 gui_mch_set_winpos(gui_win_x, gui_win_y);
3098 * Make the GUI the foreground process (in case it was launched
3099 * from the Terminal or via :gui).
3102 ProcessSerialNumber psn;
3103 if (GetCurrentProcess(&psn) == noErr)
3104 SetFrontProcess(&psn);
3107 return OK;
3110 void
3111 gui_mch_exit(int rc)
3113 /* TODO: find out all what is missing here? */
3114 DisposeRgn(cursorRgn);
3116 #ifdef USE_CARBONKEYHANDLER
3117 if (keyEventHandlerUPP)
3118 DisposeEventHandlerUPP(keyEventHandlerUPP);
3119 #endif
3121 if (mouseWheelHandlerUPP != NULL)
3122 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3124 #ifdef USE_ATSUI_DRAWING
3125 if (p_macatsui && gFontStyle)
3126 ATSUDisposeStyle(gFontStyle);
3127 #endif
3129 /* Exit to shell? */
3130 exit(rc);
3134 * Get the position of the top left corner of the window.
3137 gui_mch_get_winpos(int *x, int *y)
3139 /* TODO */
3140 Rect bounds;
3141 OSStatus status;
3143 /* Carbon >= 1.0.2, MacOS >= 8.5 */
3144 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3146 if (status != noErr)
3147 return FAIL;
3148 *x = bounds.left;
3149 *y = bounds.top;
3150 return OK;
3151 return FAIL;
3155 * Set the position of the top left corner of the window to the given
3156 * coordinates.
3158 void
3159 gui_mch_set_winpos(int x, int y)
3161 /* TODO: Should make sure the window is move within range
3162 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3164 MoveWindowStructure(gui.VimWindow, x, y);
3167 void
3168 gui_mch_set_shellsize(
3169 int width,
3170 int height,
3171 int min_width,
3172 int min_height,
3173 int base_width,
3174 int base_height,
3175 int direction)
3177 CGrafPtr VimPort;
3178 Rect VimBound;
3180 if (gui.which_scrollbars[SBAR_LEFT])
3182 VimPort = GetWindowPort(gui.VimWindow);
3183 GetPortBounds(VimPort, &VimBound);
3184 VimBound.left = -gui.scrollbar_width; /* + 1;*/
3185 SetPortBounds(VimPort, &VimBound);
3186 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3188 else
3190 VimPort = GetWindowPort(gui.VimWindow);
3191 GetPortBounds(VimPort, &VimBound);
3192 VimBound.left = 0;
3193 SetPortBounds(VimPort, &VimBound);
3196 SizeWindow(gui.VimWindow, width, height, TRUE);
3198 gui_resize_shell(width, height);
3202 * Get the screen dimensions.
3203 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3204 * Is there no way to find out how wide the borders really are?
3205 * TODO: Add live update of those value on suspend/resume.
3207 void
3208 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3210 GDHandle dominantDevice = GetMainDevice();
3211 Rect screenRect = (**dominantDevice).gdRect;
3213 *screen_w = screenRect.right - 10;
3214 *screen_h = screenRect.bottom - 40;
3219 * Open the Font Panel and wait for the user to select a font and
3220 * close the panel. Then fill the buffer pointed to by font_name with
3221 * the name and size of the selected font and return the font's handle,
3222 * or NOFONT in case of an error.
3224 static GuiFont
3225 gui_mac_select_font(char_u *font_name)
3227 GuiFont selected_font = NOFONT;
3228 OSStatus status;
3229 FontSelectionQDStyle curr_font;
3231 /* Initialize the Font Panel with the current font. */
3232 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3233 curr_font.size = (gui.norm_font >> 16);
3234 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3235 curr_font.instance.fontStyle = 0;
3236 curr_font.hasColor = false;
3237 curr_font.version = 0; /* version number of the style structure */
3238 status = SetFontInfoForSelection(kFontSelectionQDType,
3239 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3241 gFontPanelInfo.family = curr_font.instance.fontFamily;
3242 gFontPanelInfo.style = curr_font.instance.fontStyle;
3243 gFontPanelInfo.size = curr_font.size;
3245 /* Pop up the Font Panel. */
3246 status = FPShowHideFontPanel();
3247 if (status == noErr)
3250 * The Font Panel is modeless. We really need it to be modal,
3251 * so we spin in an event loop until the panel is closed.
3253 gFontPanelInfo.isPanelVisible = true;
3254 while (gFontPanelInfo.isPanelVisible)
3256 EventRecord e;
3257 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3260 GetFontPanelSelection(font_name);
3261 selected_font = gui_mac_find_font(font_name);
3263 return selected_font;
3268 * Initialise vim to use the font with the given name. Return FAIL if the font
3269 * could not be loaded, OK otherwise.
3272 gui_mch_init_font(char_u *font_name, int fontset)
3274 /* TODO: Add support for bold italic underline proportional etc... */
3275 Str255 suggestedFont = "\pMonaco";
3276 int suggestedSize = 10;
3277 FontInfo font_info;
3278 short font_id;
3279 GuiFont font;
3280 char_u used_font_name[512];
3282 #ifdef USE_ATSUI_DRAWING
3283 if (p_macatsui && gFontStyle == NULL)
3285 if (ATSUCreateStyle(&gFontStyle) != noErr)
3286 gFontStyle = NULL;
3288 #endif
3290 if (font_name == NULL)
3292 /* First try to get the suggested font */
3293 GetFNum(suggestedFont, &font_id);
3295 if (font_id == 0)
3297 /* Then pickup the standard application font */
3298 font_id = GetAppFont();
3299 STRCPY(used_font_name, "default");
3301 else
3302 STRCPY(used_font_name, "Monaco");
3303 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3305 else if (STRCMP(font_name, "*") == 0)
3307 char_u *new_p_guifont;
3309 font = gui_mac_select_font(used_font_name);
3310 if (font == NOFONT)
3311 return FAIL;
3313 /* Set guifont to the name of the selected font. */
3314 new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3315 if (new_p_guifont != NULL)
3317 STRCPY(new_p_guifont, used_font_name);
3318 vim_free(p_guifont);
3319 p_guifont = new_p_guifont;
3320 /* Replace spaces in the font name with underscores. */
3321 for ( ; *new_p_guifont; ++new_p_guifont)
3323 if (*new_p_guifont == ' ')
3324 *new_p_guifont = '_';
3328 else
3330 font = gui_mac_find_font(font_name);
3331 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3333 if (font == NOFONT)
3334 return FAIL;
3337 gui.norm_font = font;
3339 hl_set_font_name(used_font_name);
3341 TextSize(font >> 16);
3342 TextFont(font & 0xFFFF);
3344 GetFontInfo(&font_info);
3346 gui.char_ascent = font_info.ascent;
3347 gui.char_width = CharWidth('_');
3348 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3350 #ifdef USE_ATSUI_DRAWING
3351 ATSUFontID fontID;
3352 Fixed fontSize;
3353 ATSStyleRenderingOptions fontOptions;
3355 if (p_macatsui && gFontStyle)
3357 fontID = font & 0xFFFF;
3358 fontSize = Long2Fix(font >> 16);
3360 /* No antialiasing by default (do not attempt to touch antialising
3361 * options on pre-Jaguar) */
3362 fontOptions =
3363 (gMacSystemVersion >= 0x1020) ?
3364 kATSStyleNoAntiAliasing :
3365 kATSStyleNoOptions;
3367 ATSUAttributeTag attribTags[] =
3369 kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
3370 kATSUMaxATSUITagValue+1
3372 ByteCount attribSizes[] =
3374 sizeof(ATSUFontID), sizeof(Fixed),
3375 sizeof(ATSStyleRenderingOptions), sizeof font
3377 ATSUAttributeValuePtr attribValues[] =
3379 &fontID, &fontSize, &fontOptions, &font
3382 /* Convert font id to ATSUFontID */
3383 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3385 if (ATSUSetAttributes(gFontStyle,
3386 (sizeof attribTags)/sizeof(ATSUAttributeTag),
3387 attribTags, attribSizes, attribValues) != noErr)
3389 ATSUDisposeStyle(gFontStyle);
3390 gFontStyle = NULL;
3394 #endif
3396 return OK;
3400 * Adjust gui.char_height (after 'linespace' was changed).
3403 gui_mch_adjust_charheight(void)
3405 FontInfo font_info;
3407 GetFontInfo(&font_info);
3408 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3409 gui.char_ascent = font_info.ascent + p_linespace / 2;
3410 return OK;
3414 * Get a font structure for highlighting.
3416 GuiFont
3417 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3419 GuiFont font;
3421 font = gui_mac_find_font(name);
3423 if (font == NOFONT)
3425 if (giveErrorIfMissing)
3426 EMSG2(_(e_font), name);
3427 return NOFONT;
3430 * TODO : Accept only monospace
3433 return font;
3436 #if defined(FEAT_EVAL) || defined(PROTO)
3438 * Return the name of font "font" in allocated memory.
3439 * Don't know how to get the actual name, thus use the provided name.
3441 char_u *
3442 gui_mch_get_fontname(GuiFont font, char_u *name)
3444 if (name == NULL)
3445 return NULL;
3446 return vim_strsave(name);
3448 #endif
3451 * Set the current text font.
3453 void
3454 gui_mch_set_font(GuiFont font)
3456 #ifdef USE_ATSUI_DRAWING
3457 GuiFont currFont;
3458 ByteCount actualFontByteCount;
3459 ATSUFontID fontID;
3460 Fixed fontSize;
3461 ATSStyleRenderingOptions fontOptions;
3463 if (p_macatsui && gFontStyle)
3465 /* Avoid setting same font again */
3466 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue+1, sizeof font,
3467 &currFont, &actualFontByteCount) == noErr &&
3468 actualFontByteCount == (sizeof font))
3470 if (currFont == font)
3471 return;
3474 fontID = font & 0xFFFF;
3475 fontSize = Long2Fix(font >> 16);
3476 /* Respect p_antialias setting only for wide font.
3477 * The reason for doing this at the moment is a bit complicated,
3478 * but it's mainly because a) latin (non-wide) aliased fonts
3479 * look bad in OS X 10.3.x and below (due to a bug in ATS), and
3480 * b) wide multibyte input does not suffer from that problem. */
3481 /*fontOptions =
3482 (p_antialias && (font == gui.wide_font)) ?
3483 kATSStyleNoOptions : kATSStyleNoAntiAliasing;
3485 /*fontOptions = kATSStyleAntiAliasing;*/
3487 ATSUAttributeTag attribTags[] =
3489 kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
3490 kATSUMaxATSUITagValue+1
3492 ByteCount attribSizes[] =
3494 sizeof(ATSUFontID), sizeof(Fixed),
3495 sizeof(ATSStyleRenderingOptions), sizeof font
3497 ATSUAttributeValuePtr attribValues[] =
3499 &fontID, &fontSize, &fontOptions, &font
3502 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3504 if (ATSUSetAttributes(gFontStyle,
3505 (sizeof attribTags)/sizeof(ATSUAttributeTag),
3506 attribTags, attribSizes, attribValues) != noErr)
3508 # ifndef NDEBUG
3509 fprintf(stderr, "couldn't set font style\n");
3510 # endif
3511 ATSUDisposeStyle(gFontStyle);
3512 gFontStyle = NULL;
3518 if (p_macatsui && !gIsFontFallbackSet)
3520 /* Setup automatic font substitution. The user's guifontwide
3521 * is tried first, then the system tries other fonts. */
3523 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3524 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3525 ATSUCreateFontFallbacks(&gFontFallbacks);
3526 ATSUSetObjFontFallbacks(gFontFallbacks, );
3528 if (gui.wide_font)
3530 ATSUFontID fallbackFonts;
3531 gIsFontFallbackSet = TRUE;
3533 if (FMGetFontFromFontFamilyInstance(
3534 (gui.wide_font & 0xFFFF),
3536 &fallbackFonts,
3537 NULL) == noErr)
3539 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID), &fallbackFonts, kATSUSequentialFallbacksPreferred);
3542 ATSUAttributeValuePtr fallbackValues[] = { };
3546 #endif
3547 TextSize(font >> 16);
3548 TextFont(font & 0xFFFF);
3552 * If a font is not going to be used, free its structure.
3554 void
3555 gui_mch_free_font(font)
3556 GuiFont font;
3559 * Free font when "font" is not 0.
3560 * Nothing to do in the current implementation, since
3561 * nothing is allocated for each font used.
3565 static int
3566 hex_digit(int c)
3568 if (isdigit(c))
3569 return c - '0';
3570 c = TOLOWER_ASC(c);
3571 if (c >= 'a' && c <= 'f')
3572 return c - 'a' + 10;
3573 return -1000;
3577 * Return the Pixel value (color) for the given color name. This routine was
3578 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3579 * Programmer's Guide.
3580 * Return INVALCOLOR when failed.
3582 guicolor_T
3583 gui_mch_get_color(char_u *name)
3585 /* TODO: Add support for the new named color of MacOS 8
3587 RGBColor MacColor;
3588 // guicolor_T color = 0;
3590 typedef struct guicolor_tTable
3592 char *name;
3593 guicolor_T color;
3594 } guicolor_tTable;
3597 * The comment at the end of each line is the source
3598 * (Mac, Window, Unix) and the number is the unix rgb.txt value
3600 static guicolor_tTable table[] =
3602 {"Black", RGB(0x00, 0x00, 0x00)},
3603 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
3604 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
3605 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3606 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3607 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3608 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3609 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3610 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3611 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/
3612 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/
3613 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3614 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3615 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/
3616 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/
3617 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3618 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3619 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/
3620 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/
3621 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3622 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3623 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3624 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3625 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3626 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3627 {"white", RGB(0xFF, 0xFF, 0xFF)},
3628 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
3629 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
3630 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3631 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
3632 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
3633 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3634 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
3635 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
3636 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3637 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3638 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
3639 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3640 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3641 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
3642 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3643 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
3644 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
3645 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3646 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
3647 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3648 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3649 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3650 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3651 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
3654 int r, g, b;
3655 int i;
3657 if (name[0] == '#' && strlen((char *) name) == 7)
3659 /* Name is in "#rrggbb" format */
3660 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3661 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3662 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3663 if (r < 0 || g < 0 || b < 0)
3664 return INVALCOLOR;
3665 return RGB(r, g, b);
3667 else
3669 if (STRICMP(name, "hilite") == 0)
3671 LMGetHiliteRGB(&MacColor);
3672 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3674 /* Check if the name is one of the colors we know */
3675 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3676 if (STRICMP(name, table[i].name) == 0)
3677 return table[i].color;
3681 * Last attempt. Look in the file "$VIM/rgb.txt".
3684 #define LINE_LEN 100
3685 FILE *fd;
3686 char line[LINE_LEN];
3687 char_u *fname;
3689 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3690 if (fname == NULL)
3691 return INVALCOLOR;
3693 fd = fopen((char *)fname, "rt");
3694 vim_free(fname);
3695 if (fd == NULL)
3696 return INVALCOLOR;
3698 while (!feof(fd))
3700 int len;
3701 int pos;
3702 char *color;
3704 fgets(line, LINE_LEN, fd);
3705 len = strlen(line);
3707 if (len <= 1 || line[len-1] != '\n')
3708 continue;
3710 line[len-1] = '\0';
3712 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3713 if (i != 3)
3714 continue;
3716 color = line + pos;
3718 if (STRICMP(color, name) == 0)
3720 fclose(fd);
3721 return (guicolor_T) RGB(r, g, b);
3724 fclose(fd);
3727 return INVALCOLOR;
3731 * Set the current text foreground color.
3733 void
3734 gui_mch_set_fg_color(guicolor_T color)
3736 RGBColor TheColor;
3738 TheColor.red = Red(color) * 0x0101;
3739 TheColor.green = Green(color) * 0x0101;
3740 TheColor.blue = Blue(color) * 0x0101;
3742 RGBForeColor(&TheColor);
3746 * Set the current text background color.
3748 void
3749 gui_mch_set_bg_color(guicolor_T color)
3751 RGBColor TheColor;
3753 TheColor.red = Red(color) * 0x0101;
3754 TheColor.green = Green(color) * 0x0101;
3755 TheColor.blue = Blue(color) * 0x0101;
3757 RGBBackColor(&TheColor);
3760 RGBColor specialColor;
3763 * Set the current text special color.
3765 void
3766 gui_mch_set_sp_color(guicolor_T color)
3768 specialColor.red = Red(color) * 0x0101;
3769 specialColor.green = Green(color) * 0x0101;
3770 specialColor.blue = Blue(color) * 0x0101;
3774 * Draw undercurl at the bottom of the character cell.
3776 static void
3777 draw_undercurl(int flags, int row, int col, int cells)
3779 int x;
3780 int offset;
3781 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3782 int y = FILL_Y(row + 1) - 1;
3784 RGBForeColor(&specialColor);
3786 offset = val[FILL_X(col) % 8];
3787 MoveTo(FILL_X(col), y - offset);
3789 for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3791 offset = val[x % 8];
3792 LineTo(x, y - offset);
3797 static void
3798 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3800 #ifdef FEAT_MBYTE
3801 char_u *tofree = NULL;
3803 if (output_conv.vc_type != CONV_NONE)
3805 tofree = string_convert(&output_conv, s, &len);
3806 if (tofree != NULL)
3807 s = tofree;
3809 #endif
3812 * On OS X, try using Quartz-style text antialiasing.
3814 if (gMacSystemVersion >= 0x1020)
3816 /* Quartz antialiasing is available only in OS 10.2 and later. */
3817 UInt32 qd_flags = (p_antialias ?
3818 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3819 QDSwapTextFlags(qd_flags);
3823 * When antialiasing we're using srcOr mode, we have to clear the block
3824 * before drawing the text.
3825 * Also needed when 'linespace' is non-zero to remove the cursor and
3826 * underlining.
3827 * But not when drawing transparently.
3828 * The following is like calling gui_mch_clear_block(row, col, row, col +
3829 * len - 1), but without setting the bg color to gui.back_pixel.
3831 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3832 && !(flags & DRAW_TRANSP))
3834 Rect rc;
3836 rc.left = FILL_X(col);
3837 rc.top = FILL_Y(row);
3838 #ifdef FEAT_MBYTE
3839 /* Multibyte computation taken from gui_w32.c */
3840 if (has_mbyte)
3842 int cell_len = 0;
3843 int n;
3845 /* Compute the length in display cells. */
3846 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3847 cell_len += (*mb_ptr2cells)(s + n);
3848 rc.right = FILL_X(col + cell_len);
3850 else
3851 #endif
3852 rc.right = FILL_X(col + len) + (col + len == Columns);
3853 rc.bottom = FILL_Y(row + 1);
3854 EraseRect(&rc);
3857 if (gMacSystemVersion >= 0x1020 && p_antialias)
3859 StyleParameter face;
3861 face = normal;
3862 if (flags & DRAW_BOLD)
3863 face |= bold;
3864 if (flags & DRAW_UNDERL)
3865 face |= underline;
3866 TextFace(face);
3868 /* Quartz antialiasing works only in srcOr transfer mode. */
3869 TextMode(srcOr);
3871 MoveTo(TEXT_X(col), TEXT_Y(row));
3872 DrawText((char*)s, 0, len);
3874 else
3876 /* Use old-style, non-antialiased QuickDraw text rendering. */
3877 TextMode(srcCopy);
3878 TextFace(normal);
3880 /* SelectFont(hdc, gui.currFont); */
3882 if (flags & DRAW_TRANSP)
3884 TextMode(srcOr);
3887 MoveTo(TEXT_X(col), TEXT_Y(row));
3888 DrawText((char *)s, 0, len);
3890 if (flags & DRAW_BOLD)
3892 TextMode(srcOr);
3893 MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
3894 DrawText((char *)s, 0, len);
3897 if (flags & DRAW_UNDERL)
3899 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
3900 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
3904 if (flags & DRAW_UNDERC)
3905 draw_undercurl(flags, row, col, len);
3907 #ifdef FEAT_MBYTE
3908 vim_free(tofree);
3909 #endif
3912 #ifdef USE_ATSUI_DRAWING
3914 static void
3915 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
3917 /* ATSUI requires utf-16 strings */
3918 UniCharCount utf16_len;
3919 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
3920 utf16_len /= sizeof(UniChar);
3922 /* - ATSUI automatically antialiases text (Someone)
3923 * - for some reason it does not work... (Jussi) */
3926 * When antialiasing we're using srcOr mode, we have to clear the block
3927 * before drawing the text.
3928 * Also needed when 'linespace' is non-zero to remove the cursor and
3929 * underlining.
3930 * But not when drawing transparently.
3931 * The following is like calling gui_mch_clear_block(row, col, row, col +
3932 * len - 1), but without setting the bg color to gui.back_pixel.
3934 if ((flags & DRAW_TRANSP) == 0)
3936 Rect rc;
3938 rc.left = FILL_X(col);
3939 rc.top = FILL_Y(row);
3940 /* Multibyte computation taken from gui_w32.c */
3941 if (has_mbyte)
3943 int cell_len = 0;
3944 int n;
3946 /* Compute the length in display cells. */
3947 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3948 cell_len += (*mb_ptr2cells)(s + n);
3949 rc.right = FILL_X(col + cell_len);
3951 else
3952 rc.right = FILL_X(col + len) + (col + len == Columns);
3954 rc.bottom = FILL_Y(row + 1);
3955 EraseRect(&rc);
3959 /* Use old-style, non-antialiased QuickDraw text rendering. */
3960 TextMode(srcCopy);
3961 TextFace(normal);
3963 /* SelectFont(hdc, gui.currFont); */
3965 if (flags & DRAW_TRANSP)
3967 TextMode(srcOr);
3970 MoveTo(TEXT_X(col), TEXT_Y(row));
3971 ATSUTextLayout textLayout;
3973 if (ATSUCreateTextLayoutWithTextPtr(tofree,
3974 kATSUFromTextBeginning, kATSUToTextEnd,
3975 utf16_len,
3976 (gFontStyle ? 1 : 0), &utf16_len,
3977 (gFontStyle ? &gFontStyle : NULL),
3978 &textLayout) == noErr)
3980 ATSUSetTransientFontMatching(textLayout, TRUE);
3982 ATSUDrawText(textLayout,
3983 kATSUFromTextBeginning, kATSUToTextEnd,
3984 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
3986 ATSUDisposeTextLayout(textLayout);
3990 if (flags & DRAW_UNDERC)
3991 draw_undercurl(flags, row, col, len);
3993 vim_free(tofree);
3995 #endif
3997 void
3998 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4000 #if defined(USE_ATSUI_DRAWING)
4001 if (p_macatsui)
4002 draw_string_ATSUI(row, col, s, len, flags);
4003 else
4004 #endif
4005 draw_string_QD(row, col, s, len, flags);
4009 * Return OK if the key with the termcap name "name" is supported.
4012 gui_mch_haskey(char_u *name)
4014 int i;
4016 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4017 if (name[0] == special_keys[i].vim_code0 &&
4018 name[1] == special_keys[i].vim_code1)
4019 return OK;
4020 return FAIL;
4023 void
4024 gui_mch_beep(void)
4026 SysBeep(1); /* Should this be 0? (????) */
4029 void
4030 gui_mch_flash(int msec)
4032 /* Do a visual beep by reversing the foreground and background colors */
4033 Rect rc;
4036 * Note: InvertRect() excludes right and bottom of rectangle.
4038 rc.left = 0;
4039 rc.top = 0;
4040 rc.right = gui.num_cols * gui.char_width;
4041 rc.bottom = gui.num_rows * gui.char_height;
4042 InvertRect(&rc);
4044 ui_delay((long)msec, TRUE); /* wait for some msec */
4046 InvertRect(&rc);
4050 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4052 void
4053 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4055 Rect rc;
4058 * Note: InvertRect() excludes right and bottom of rectangle.
4060 rc.left = FILL_X(c);
4061 rc.top = FILL_Y(r);
4062 rc.right = rc.left + nc * gui.char_width;
4063 rc.bottom = rc.top + nr * gui.char_height;
4064 InvertRect(&rc);
4068 * Iconify the GUI window.
4070 void
4071 gui_mch_iconify(void)
4073 /* TODO: find out what could replace iconify
4074 * -window shade?
4075 * -hide application?
4079 #if defined(FEAT_EVAL) || defined(PROTO)
4081 * Bring the Vim window to the foreground.
4083 void
4084 gui_mch_set_foreground(void)
4086 /* TODO */
4088 #endif
4091 * Draw a cursor without focus.
4093 void
4094 gui_mch_draw_hollow_cursor(guicolor_T color)
4096 Rect rc;
4099 * Note: FrameRect() excludes right and bottom of rectangle.
4101 rc.left = FILL_X(gui.col);
4102 rc.top = FILL_Y(gui.row);
4103 rc.right = rc.left + gui.char_width;
4104 #ifdef FEAT_MBYTE
4105 if (mb_lefthalve(gui.row, gui.col))
4106 rc.right += gui.char_width;
4107 #endif
4108 rc.bottom = rc.top + gui.char_height;
4110 gui_mch_set_fg_color(color);
4112 FrameRect(&rc);
4116 * Draw part of a cursor, only w pixels wide, and h pixels high.
4118 void
4119 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4121 Rect rc;
4123 #ifdef FEAT_RIGHTLEFT
4124 /* vertical line should be on the right of current point */
4125 if (CURSOR_BAR_RIGHT)
4126 rc.left = FILL_X(gui.col + 1) - w;
4127 else
4128 #endif
4129 rc.left = FILL_X(gui.col);
4130 rc.top = FILL_Y(gui.row) + gui.char_height - h;
4131 rc.right = rc.left + w;
4132 rc.bottom = rc.top + h;
4134 gui_mch_set_fg_color(color);
4136 FrameRect(&rc);
4137 // PaintRect(&rc);
4143 * Catch up with any queued X events. This may put keyboard input into the
4144 * input buffer, call resize call-backs, trigger timers etc. If there is
4145 * nothing in the X event queue (& no timers pending), then we return
4146 * immediately.
4148 void
4149 gui_mch_update(void)
4151 /* TODO: find what to do
4152 * maybe call gui_mch_wait_for_chars (0)
4153 * more like look at EventQueue then
4154 * call heart of gui_mch_wait_for_chars;
4156 * if (eventther)
4157 * gui_mac_handle_event(&event);
4159 EventRecord theEvent;
4161 if (EventAvail(everyEvent, &theEvent))
4162 if (theEvent.what != nullEvent)
4163 gui_mch_wait_for_chars(0);
4167 * Simple wrapper to neglect more easily the time
4168 * spent inside WaitNextEvent while profiling.
4171 pascal
4172 Boolean
4173 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4175 if (((long) sleep) < -1)
4176 sleep = 32767;
4177 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4181 * GUI input routine called by gui_wait_for_chars(). Waits for a character
4182 * from the keyboard.
4183 * wtime == -1 Wait forever.
4184 * wtime == 0 This should never happen.
4185 * wtime > 0 Wait wtime milliseconds for a character.
4186 * Returns OK if a character was found to be available within the given time,
4187 * or FAIL otherwise.
4190 gui_mch_wait_for_chars(int wtime)
4192 EventMask mask = (everyEvent);
4193 EventRecord event;
4194 long entryTick;
4195 long currentTick;
4196 long sleeppyTick;
4198 /* If we are providing life feedback with the scrollbar,
4199 * we don't want to try to wait for an event, or else
4200 * there won't be any life feedback.
4202 if (dragged_sb != NULL)
4203 return FAIL;
4204 /* TODO: Check if FAIL is the proper return code */
4206 entryTick = TickCount();
4208 allow_scrollbar = TRUE;
4212 /* if (dragRectControl == kCreateEmpty)
4214 dragRgn = NULL;
4215 dragRectControl = kNothing;
4217 else*/ if (dragRectControl == kCreateRect)
4219 dragRgn = cursorRgn;
4220 RectRgn(dragRgn, &dragRect);
4221 dragRectControl = kNothing;
4224 * Don't use gui_mch_update() because then we will spin-lock until a
4225 * char arrives, instead we use WaitNextEventWrp() to hang until an
4226 * event arrives. No need to check for input_buf_full because we are
4227 * returning as soon as it contains a single char.
4229 /* TODO: reduce wtime accordinly??? */
4230 if (wtime > -1)
4231 sleeppyTick = 60*wtime/1000;
4232 else
4233 sleeppyTick = 32767;
4234 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4236 gui_mac_handle_event(&event);
4237 if (input_available())
4239 allow_scrollbar = FALSE;
4240 return OK;
4243 currentTick = TickCount();
4245 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4247 allow_scrollbar = FALSE;
4248 return FAIL;
4252 * Output routines.
4255 /* Flush any output to the screen */
4256 void
4257 gui_mch_flush(void)
4259 /* TODO: Is anything needed here? */
4263 * Clear a rectangular region of the screen from text pos (row1, col1) to
4264 * (row2, col2) inclusive.
4266 void
4267 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4269 Rect rc;
4272 * Clear one extra pixel at the far right, for when bold characters have
4273 * spilled over to the next column.
4275 rc.left = FILL_X(col1);
4276 rc.top = FILL_Y(row1);
4277 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4278 rc.bottom = FILL_Y(row2 + 1);
4280 gui_mch_set_bg_color(gui.back_pixel);
4281 EraseRect(&rc);
4285 * Clear the whole text window.
4287 void
4288 gui_mch_clear_all(void)
4290 Rect rc;
4292 rc.left = 0;
4293 rc.top = 0;
4294 rc.right = Columns * gui.char_width + 2 * gui.border_width;
4295 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4297 gui_mch_set_bg_color(gui.back_pixel);
4298 EraseRect(&rc);
4299 /* gui_mch_set_fg_color(gui.norm_pixel);
4300 FrameRect(&rc);
4305 * Delete the given number of lines from the given row, scrolling up any
4306 * text further down within the scroll region.
4308 void
4309 gui_mch_delete_lines(int row, int num_lines)
4311 Rect rc;
4313 /* changed without checking! */
4314 rc.left = FILL_X(gui.scroll_region_left);
4315 rc.right = FILL_X(gui.scroll_region_right + 1);
4316 rc.top = FILL_Y(row);
4317 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4319 gui_mch_set_bg_color(gui.back_pixel);
4320 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4322 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4323 gui.scroll_region_left,
4324 gui.scroll_region_bot, gui.scroll_region_right);
4328 * Insert the given number of lines before the given row, scrolling down any
4329 * following text within the scroll region.
4331 void
4332 gui_mch_insert_lines(int row, int num_lines)
4334 Rect rc;
4336 rc.left = FILL_X(gui.scroll_region_left);
4337 rc.right = FILL_X(gui.scroll_region_right + 1);
4338 rc.top = FILL_Y(row);
4339 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4341 gui_mch_set_bg_color(gui.back_pixel);
4343 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4345 /* Update gui.cursor_row if the cursor scrolled or copied over */
4346 if (gui.cursor_row >= gui.row
4347 && gui.cursor_col >= gui.scroll_region_left
4348 && gui.cursor_col <= gui.scroll_region_right)
4350 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4351 gui.cursor_row += num_lines;
4352 else if (gui.cursor_row <= gui.scroll_region_bot)
4353 gui.cursor_is_valid = FALSE;
4356 gui_clear_block(row, gui.scroll_region_left,
4357 row + num_lines - 1, gui.scroll_region_right);
4361 * TODO: add a vim format to the clipboard which remember
4362 * LINEWISE, CHARWISE, BLOCKWISE
4365 void
4366 clip_mch_request_selection(VimClipboard *cbd)
4369 Handle textOfClip;
4370 int flavor = 0;
4371 Size scrapSize;
4372 ScrapFlavorFlags scrapFlags;
4373 ScrapRef scrap = nil;
4374 OSStatus error;
4375 int type;
4376 char *searchCR;
4377 char_u *tempclip;
4380 error = GetCurrentScrap(&scrap);
4381 if (error != noErr)
4382 return;
4384 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4385 if (error == noErr)
4387 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4388 if (error == noErr && scrapSize > 1)
4389 flavor = 1;
4392 if (flavor == 0)
4394 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4395 if (error != noErr)
4396 return;
4398 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4399 if (error != noErr)
4400 return;
4403 ReserveMem(scrapSize);
4405 /* In CARBON we don't need a Handle, a pointer is good */
4406 textOfClip = NewHandle(scrapSize);
4408 /* tempclip = lalloc(scrapSize+1, TRUE); */
4409 HLock(textOfClip);
4410 error = GetScrapFlavorData(scrap,
4411 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4412 &scrapSize, *textOfClip);
4413 scrapSize -= flavor;
4415 if (flavor)
4416 type = **textOfClip;
4417 else
4418 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4420 tempclip = lalloc(scrapSize + 1, TRUE);
4421 mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4422 tempclip[scrapSize] = 0;
4424 #ifdef MACOS_CONVERT
4426 /* Convert from utf-16 (clipboard) */
4427 size_t encLen = 0;
4428 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4430 if (to != NULL)
4432 scrapSize = encLen;
4433 vim_free(tempclip);
4434 tempclip = to;
4437 #endif
4439 searchCR = (char *)tempclip;
4440 while (searchCR != NULL)
4442 searchCR = strchr(searchCR, '\r');
4443 if (searchCR != NULL)
4444 *searchCR = '\n';
4447 clip_yank_selection(type, tempclip, scrapSize, cbd);
4449 vim_free(tempclip);
4450 HUnlock(textOfClip);
4452 DisposeHandle(textOfClip);
4455 void
4456 clip_mch_lose_selection(VimClipboard *cbd)
4459 * TODO: Really nothing to do?
4464 clip_mch_own_selection(VimClipboard *cbd)
4466 return OK;
4470 * Send the current selection to the clipboard.
4472 void
4473 clip_mch_set_selection(VimClipboard *cbd)
4475 Handle textOfClip;
4476 long scrapSize;
4477 int type;
4478 ScrapRef scrap;
4480 char_u *str = NULL;
4482 if (!cbd->owned)
4483 return;
4485 clip_get_selection(cbd);
4488 * Once we set the clipboard, lose ownership. If another application sets
4489 * the clipboard, we don't want to think that we still own it.
4491 cbd->owned = FALSE;
4493 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4495 #ifdef MACOS_CONVERT
4496 size_t utf16_len = 0;
4497 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4498 if (to)
4500 scrapSize = utf16_len;
4501 vim_free(str);
4502 str = (char_u *)to;
4504 #endif
4506 if (type >= 0)
4508 ClearCurrentScrap();
4510 textOfClip = NewHandle(scrapSize + 1);
4511 HLock(textOfClip);
4513 **textOfClip = type;
4514 mch_memmove(*textOfClip + 1, str, scrapSize);
4515 GetCurrentScrap(&scrap);
4516 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4517 scrapSize, *textOfClip + 1);
4518 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4519 scrapSize + 1, *textOfClip);
4520 HUnlock(textOfClip);
4521 DisposeHandle(textOfClip);
4524 vim_free(str);
4527 void
4528 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4530 Rect VimBound;
4532 /* HideWindow(gui.VimWindow); */
4533 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4535 if (gui.which_scrollbars[SBAR_LEFT])
4537 VimBound.left = -gui.scrollbar_width + 1;
4539 else
4541 VimBound.left = 0;
4544 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4546 ShowWindow(gui.VimWindow);
4550 * Menu stuff.
4553 void
4554 gui_mch_enable_menu(int flag)
4557 * Menu is always active.
4561 void
4562 gui_mch_set_menu_pos(int x, int y, int w, int h)
4565 * The menu is always at the top of the screen.
4570 * Add a sub menu to the menu bar.
4572 void
4573 gui_mch_add_menu(vimmenu_T *menu, int idx)
4576 * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4577 * TODO: use menu->mnemonic and menu->actext
4578 * TODO: Try to reuse menu id
4579 * Carbon Help suggest to use only id between 1 and 235
4581 static long next_avail_id = 128;
4582 long menu_after_me = 0; /* Default to the end */
4583 #if defined(FEAT_MBYTE)
4584 CFStringRef name;
4585 #else
4586 char_u *name;
4587 #endif
4588 short index;
4589 vimmenu_T *parent = menu->parent;
4590 vimmenu_T *brother = menu->next;
4592 /* Cannot add a menu if ... */
4593 if ((parent != NULL && parent->submenu_id == 0))
4594 return;
4596 /* menu ID greater than 1024 are reserved for ??? */
4597 if (next_avail_id == 1024)
4598 return;
4600 /* My brother could be the PopUp, find my real brother */
4601 while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4602 brother = brother->next;
4604 /* Find where to insert the menu (for MenuBar) */
4605 if ((parent == NULL) && (brother != NULL))
4606 menu_after_me = brother->submenu_id;
4608 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4609 if (!menu_is_menubar(menu->name))
4610 menu_after_me = hierMenu;
4612 /* Convert the name */
4613 #ifdef MACOS_CONVERT
4614 name = menu_title_removing_mnemonic(menu);
4615 #else
4616 name = C2Pascal_save(menu->dname);
4617 #endif
4618 if (name == NULL)
4619 return;
4621 /* Create the menu unless it's the help menu */
4623 /* Carbon suggest use of
4624 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4625 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4627 menu->submenu_id = next_avail_id;
4628 #if defined(FEAT_MBYTE)
4629 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4630 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4631 #else
4632 menu->submenu_handle = NewMenu(menu->submenu_id, name);
4633 #endif
4634 next_avail_id++;
4637 if (parent == NULL)
4639 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4641 /* TODO: Verify if we could only Insert Menu if really part of the
4642 * menubar The Inserted menu are scanned or the Command-key combos
4645 /* Insert the menu */
4646 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4647 #if 1
4648 /* Vim should normally update it. TODO: verify */
4649 DrawMenuBar();
4650 #endif
4652 else
4654 /* Adding as a submenu */
4656 index = gui_mac_get_menu_item_index(menu);
4658 /* Call InsertMenuItem followed by SetMenuItemText
4659 * to avoid special character recognition by InsertMenuItem
4661 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4662 #if defined(FEAT_MBYTE)
4663 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4664 #else
4665 SetMenuItemText(parent->submenu_handle, idx+1, name);
4666 #endif
4667 SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4668 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4669 InsertMenu(menu->submenu_handle, hierMenu);
4672 #if defined(FEAT_MBYTE)
4673 CFRelease(name);
4674 #else
4675 vim_free(name);
4676 #endif
4678 #if 0
4679 /* Done by Vim later on */
4680 DrawMenuBar();
4681 #endif
4685 * Add a menu item to a menu
4687 void
4688 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4690 #if defined(FEAT_MBYTE)
4691 CFStringRef name;
4692 #else
4693 char_u *name;
4694 #endif
4695 vimmenu_T *parent = menu->parent;
4696 int menu_inserted;
4698 /* Cannot add item, if the menu have not been created */
4699 if (parent->submenu_id == 0)
4700 return;
4702 /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4703 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4705 /* Convert the name */
4706 #ifdef MACOS_CONVERT
4707 name = menu_title_removing_mnemonic(menu);
4708 #else
4709 name = C2Pascal_save(menu->dname);
4710 #endif
4712 /* Where are just a menu item, so no handle, no id */
4713 menu->submenu_id = 0;
4714 menu->submenu_handle = NULL;
4716 menu_inserted = 0;
4717 if (menu->actext)
4719 /* If the accelerator text for the menu item looks like it describes
4720 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4721 * item's command equivalent.
4723 int key = 0;
4724 int modifiers = 0;
4725 char_u *p_actext;
4727 p_actext = menu->actext;
4728 key = find_special_key(&p_actext, &modifiers, /*keycode=*/0);
4729 if (*p_actext != 0)
4730 key = 0; /* error: trailing text */
4731 /* find_special_key() returns a keycode with as many of the
4732 * specified modifiers as appropriate already applied (e.g., for
4733 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4734 * as the only modifier). Since we want to display all of the
4735 * modifiers, we need to convert the keycode back to a printable
4736 * character plus modifiers.
4737 * TODO: Write an alternative find_special_key() that doesn't
4738 * apply modifiers.
4740 if (key > 0 && key < 32)
4742 /* Convert a control key to an uppercase letter. Note that
4743 * by this point it is no longer possible to distinguish
4744 * between, e.g., Ctrl-S and Ctrl-Shift-S.
4746 modifiers |= MOD_MASK_CTRL;
4747 key += '@';
4749 /* If the keycode is an uppercase letter, set the Shift modifier.
4750 * If it is a lowercase letter, don't set the modifier, but convert
4751 * the letter to uppercase for display in the menu.
4753 else if (key >= 'A' && key <= 'Z')
4754 modifiers |= MOD_MASK_SHIFT;
4755 else if (key >= 'a' && key <= 'z')
4756 key += 'A' - 'a';
4757 /* Note: keycodes below 0x22 are reserved by Apple. */
4758 if (key >= 0x22 && vim_isprintc_strict(key))
4760 int valid = 1;
4761 char_u mac_mods = kMenuNoModifiers;
4762 /* Convert Vim modifier codes to Menu Manager equivalents. */
4763 if (modifiers & MOD_MASK_SHIFT)
4764 mac_mods |= kMenuShiftModifier;
4765 if (modifiers & MOD_MASK_CTRL)
4766 mac_mods |= kMenuControlModifier;
4767 if (!(modifiers & MOD_MASK_CMD))
4768 mac_mods |= kMenuNoCommandModifier;
4769 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
4770 valid = 0; /* TODO: will Alt someday map to Option? */
4771 if (valid)
4773 char_u item_txt[10];
4774 /* Insert the menu item after idx, with its command key. */
4775 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
4776 item_txt[3] = key;
4777 InsertMenuItem(parent->submenu_handle, item_txt, idx);
4778 /* Set the modifier keys. */
4779 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
4780 menu_inserted = 1;
4784 /* Call InsertMenuItem followed by SetMenuItemText
4785 * to avoid special character recognition by InsertMenuItem
4787 if (!menu_inserted)
4788 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4789 /* Set the menu item name. */
4790 #if defined(FEAT_MBYTE)
4791 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4792 #else
4793 SetMenuItemText(parent->submenu_handle, idx+1, name);
4794 #endif
4796 #if 0
4797 /* Called by Vim */
4798 DrawMenuBar();
4799 #endif
4801 #if defined(FEAT_MBYTE)
4802 CFRelease(name);
4803 #else
4804 /* TODO: Can name be freed? */
4805 vim_free(name);
4806 #endif
4809 void
4810 gui_mch_toggle_tearoffs(int enable)
4812 /* no tearoff menus */
4816 * Destroy the machine specific menu widget.
4818 void
4819 gui_mch_destroy_menu(vimmenu_T *menu)
4821 short index = gui_mac_get_menu_item_index(menu);
4823 if (index > 0)
4825 if (menu->parent)
4828 /* For now just don't delete help menu items. (Huh? Dany) */
4829 DeleteMenuItem(menu->parent->submenu_handle, index);
4831 /* Delete the Menu if it was a hierarchical Menu */
4832 if (menu->submenu_id != 0)
4834 DeleteMenu(menu->submenu_id);
4835 DisposeMenu(menu->submenu_handle);
4839 #ifdef DEBUG_MAC_MENU
4840 else
4842 printf("gmdm 2\n");
4844 #endif
4846 else
4849 DeleteMenu(menu->submenu_id);
4850 DisposeMenu(menu->submenu_handle);
4853 /* Shouldn't this be already done by Vim. TODO: Check */
4854 DrawMenuBar();
4858 * Make a menu either grey or not grey.
4860 void
4861 gui_mch_menu_grey(vimmenu_T *menu, int grey)
4863 /* TODO: Check if menu really exists */
4864 short index = gui_mac_get_menu_item_index(menu);
4866 index = menu->index;
4868 if (grey)
4870 if (menu->children)
4871 DisableMenuItem(menu->submenu_handle, index);
4872 if (menu->parent)
4873 if (menu->parent->submenu_handle)
4874 DisableMenuItem(menu->parent->submenu_handle, index);
4876 else
4878 if (menu->children)
4879 EnableMenuItem(menu->submenu_handle, index);
4880 if (menu->parent)
4881 if (menu->parent->submenu_handle)
4882 EnableMenuItem(menu->parent->submenu_handle, index);
4887 * Make menu item hidden or not hidden
4889 void
4890 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
4892 /* There's no hidden mode on MacOS */
4893 gui_mch_menu_grey(menu, hidden);
4898 * This is called after setting all the menus to grey/hidden or not.
4900 void
4901 gui_mch_draw_menubar(void)
4903 DrawMenuBar();
4908 * Scrollbar stuff.
4911 void
4912 gui_mch_enable_scrollbar(
4913 scrollbar_T *sb,
4914 int flag)
4916 if (flag)
4917 ShowControl(sb->id);
4918 else
4919 HideControl(sb->id);
4921 #ifdef DEBUG_MAC_SB
4922 printf("enb_sb (%x) %x\n",sb->id, flag);
4923 #endif
4926 void
4927 gui_mch_set_scrollbar_thumb(
4928 scrollbar_T *sb,
4929 long val,
4930 long size,
4931 long max)
4933 SetControl32BitMaximum (sb->id, max);
4934 SetControl32BitMinimum (sb->id, 0);
4935 SetControl32BitValue (sb->id, val);
4936 SetControlViewSize (sb->id, size);
4937 #ifdef DEBUG_MAC_SB
4938 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
4939 #endif
4942 void
4943 gui_mch_set_scrollbar_pos(
4944 scrollbar_T *sb,
4945 int x,
4946 int y,
4947 int w,
4948 int h)
4950 gui_mch_set_bg_color(gui.back_pixel);
4951 /* if (gui.which_scrollbars[SBAR_LEFT])
4953 MoveControl(sb->id, x-16, y);
4954 SizeControl(sb->id, w + 1, h);
4956 else
4958 MoveControl(sb->id, x, y);
4959 SizeControl(sb->id, w + 1, h);
4961 if (sb == &gui.bottom_sbar)
4962 h += 1;
4963 else
4964 w += 1;
4966 if (gui.which_scrollbars[SBAR_LEFT])
4967 x -= 15;
4969 MoveControl(sb->id, x, y);
4970 SizeControl(sb->id, w, h);
4971 #ifdef DEBUG_MAC_SB
4972 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
4973 #endif
4976 void
4977 gui_mch_create_scrollbar(
4978 scrollbar_T *sb,
4979 int orient) /* SBAR_VERT or SBAR_HORIZ */
4981 Rect bounds;
4983 bounds.top = -16;
4984 bounds.bottom = -10;
4985 bounds.right = -10;
4986 bounds.left = -16;
4988 sb->id = NewControl(gui.VimWindow,
4989 &bounds,
4990 "\pScrollBar",
4991 TRUE,
4992 0, /* current*/
4993 0, /* top */
4994 0, /* bottom */
4995 kControlScrollBarLiveProc,
4996 (long) sb->ident);
4997 #ifdef DEBUG_MAC_SB
4998 printf("create_sb (%x) %x\n",sb->id, orient);
4999 #endif
5002 void
5003 gui_mch_destroy_scrollbar(scrollbar_T *sb)
5005 gui_mch_set_bg_color(gui.back_pixel);
5006 DisposeControl(sb->id);
5007 #ifdef DEBUG_MAC_SB
5008 printf("dest_sb (%x) \n",sb->id);
5009 #endif
5014 * Cursor blink functions.
5016 * This is a simple state machine:
5017 * BLINK_NONE not blinking at all
5018 * BLINK_OFF blinking, cursor is not shown
5019 * BLINK_ON blinking, cursor is shown
5021 void
5022 gui_mch_set_blinking(long wait, long on, long off)
5024 /* TODO: TODO: TODO: TODO: */
5025 /* blink_waittime = wait;
5026 blink_ontime = on;
5027 blink_offtime = off;*/
5031 * Stop the cursor blinking. Show the cursor if it wasn't shown.
5033 void
5034 gui_mch_stop_blink(void)
5036 gui_update_cursor(TRUE, FALSE);
5037 /* TODO: TODO: TODO: TODO: */
5038 /* gui_w32_rm_blink_timer();
5039 if (blink_state == BLINK_OFF)
5040 gui_update_cursor(TRUE, FALSE);
5041 blink_state = BLINK_NONE;*/
5045 * Start the cursor blinking. If it was already blinking, this restarts the
5046 * waiting time and shows the cursor.
5048 void
5049 gui_mch_start_blink(void)
5051 gui_update_cursor(TRUE, FALSE);
5052 /* TODO: TODO: TODO: TODO: */
5053 /* gui_w32_rm_blink_timer(); */
5055 /* Only switch blinking on if none of the times is zero */
5056 /* if (blink_waittime && blink_ontime && blink_offtime)
5058 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5059 (TIMERPROC)_OnBlinkTimer);
5060 blink_state = BLINK_ON;
5061 gui_update_cursor(TRUE, FALSE);
5066 * Return the RGB value of a pixel as long.
5068 long_u
5069 gui_mch_get_rgb(guicolor_T pixel)
5071 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5076 #ifdef FEAT_BROWSE
5078 * Pop open a file browser and return the file selected, in allocated memory,
5079 * or NULL if Cancel is hit.
5080 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
5081 * title - Title message for the file browser dialog.
5082 * dflt - Default name of file.
5083 * ext - Default extension to be added to files without extensions.
5084 * initdir - directory in which to open the browser (NULL = current dir)
5085 * filter - Filter for matched files to choose from.
5086 * Has a format like this:
5087 * "C Files (*.c)\0*.c\0"
5088 * "All Files\0*.*\0\0"
5089 * If these two strings were concatenated, then a choice of two file
5090 * filters will be selectable to the user. Then only matching files will
5091 * be shown in the browser. If NULL, the default allows all files.
5093 * *NOTE* - the filter string must be terminated with TWO nulls.
5095 char_u *
5096 gui_mch_browse(
5097 int saving,
5098 char_u *title,
5099 char_u *dflt,
5100 char_u *ext,
5101 char_u *initdir,
5102 char_u *filter)
5104 /* TODO: Add Ammon's safety checl (Dany) */
5105 NavReplyRecord reply;
5106 char_u *fname = NULL;
5107 char_u **fnames = NULL;
5108 long numFiles;
5109 NavDialogOptions navOptions;
5110 OSErr error;
5112 /* Get Navigation Service Defaults value */
5113 NavGetDefaultDialogOptions(&navOptions);
5116 /* TODO: If we get a :browse args, set the Multiple bit. */
5117 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles
5118 | kNavDontAutoTranslate
5119 | kNavDontAddTranslateItems
5120 /* | kNavAllowMultipleFiles */
5121 | kNavAllowStationery;
5123 (void) C2PascalString(title, &navOptions.message);
5124 (void) C2PascalString(dflt, &navOptions.savedFileName);
5125 /* Could set clientName?
5126 * windowTitle? (there's no title bar?)
5129 if (saving)
5131 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5132 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5133 if (!reply.validRecord)
5134 return NULL;
5136 else
5138 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5139 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5140 if (!reply.validRecord)
5141 return NULL;
5144 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5146 NavDisposeReply(&reply);
5148 if (fnames)
5150 fname = fnames[0];
5151 vim_free(fnames);
5154 /* TODO: Shorten the file name if possible */
5155 return fname;
5157 #endif /* FEAT_BROWSE */
5159 #ifdef FEAT_GUI_DIALOG
5161 * Stuff for dialogues
5165 * Create a dialogue dynamically from the parameter strings.
5166 * type = type of dialogue (question, alert, etc.)
5167 * title = dialogue title. may be NULL for default title.
5168 * message = text to display. Dialogue sizes to accommodate it.
5169 * buttons = '\n' separated list of button captions, default first.
5170 * dfltbutton = number of default button.
5172 * This routine returns 1 if the first button is pressed,
5173 * 2 for the second, etc.
5175 * 0 indicates Esc was pressed.
5176 * -1 for unexpected error
5178 * If stubbing out this fn, return 1.
5181 typedef struct
5183 short idx;
5184 short width; /* Size of the text in pixel */
5185 Rect box;
5186 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5188 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5190 static void
5191 macMoveDialogItem(
5192 DialogRef theDialog,
5193 short itemNumber,
5194 short X,
5195 short Y,
5196 Rect *inBox)
5198 #if 0 /* USE_CARBONIZED */
5199 /* Untested */
5200 MoveDialogItem(theDialog, itemNumber, X, Y);
5201 if (inBox != nil)
5202 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5203 #else
5204 short itemType;
5205 Handle itemHandle;
5206 Rect localBox;
5207 Rect *itemBox = &localBox;
5209 if (inBox != nil)
5210 itemBox = inBox;
5212 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5213 OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5214 OffsetRect(itemBox, X, Y);
5215 /* To move a control (like a button) we need to call both
5216 * MoveControl and SetDialogItem. FAQ 6-18 */
5217 if (1) /*(itemType & kControlDialogItem) */
5218 MoveControl((ControlRef) itemHandle, X, Y);
5219 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5220 #endif
5223 static void
5224 macSizeDialogItem(
5225 DialogRef theDialog,
5226 short itemNumber,
5227 short width,
5228 short height)
5230 short itemType;
5231 Handle itemHandle;
5232 Rect itemBox;
5234 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5236 /* When width or height is zero do not change it */
5237 if (width == 0)
5238 width = itemBox.right - itemBox.left;
5239 if (height == 0)
5240 height = itemBox.bottom - itemBox.top;
5242 #if 0 /* USE_CARBONIZED */
5243 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5244 #else
5245 /* Resize the bounding box */
5246 itemBox.right = itemBox.left + width;
5247 itemBox.bottom = itemBox.top + height;
5249 /* To resize a control (like a button) we need to call both
5250 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5251 if (itemType & kControlDialogItem)
5252 SizeControl((ControlRef) itemHandle, width, height);
5254 /* Configure back the item */
5255 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5256 #endif
5259 static void
5260 macSetDialogItemText(
5261 DialogRef theDialog,
5262 short itemNumber,
5263 Str255 itemName)
5265 short itemType;
5266 Handle itemHandle;
5267 Rect itemBox;
5269 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5271 if (itemType & kControlDialogItem)
5272 SetControlTitle((ControlRef) itemHandle, itemName);
5273 else
5274 SetDialogItemText(itemHandle, itemName);
5277 /* TODO: There have been some crashes with dialogs, check your inbox
5278 * (Jussi)
5281 gui_mch_dialog(
5282 int type,
5283 char_u *title,
5284 char_u *message,
5285 char_u *buttons,
5286 int dfltbutton,
5287 char_u *textfield)
5289 Handle buttonDITL;
5290 Handle iconDITL;
5291 Handle inputDITL;
5292 Handle messageDITL;
5293 Handle itemHandle;
5294 Handle iconHandle;
5295 DialogPtr theDialog;
5296 char_u len;
5297 char_u PascalTitle[256]; /* place holder for the title */
5298 char_u name[256];
5299 GrafPtr oldPort;
5300 short itemHit;
5301 char_u *buttonChar;
5302 Rect box;
5303 short button;
5304 short lastButton;
5305 short itemType;
5306 short useIcon;
5307 short width;
5308 short totalButtonWidth = 0; /* the width of all buttons together
5309 including spacing */
5310 short widestButton = 0;
5311 short dfltButtonEdge = 20; /* gut feeling */
5312 short dfltElementSpacing = 13; /* from IM:V.2-29 */
5313 short dfltIconSideSpace = 23; /* from IM:V.2-29 */
5314 short maximumWidth = 400; /* gut feeling */
5315 short maxButtonWidth = 175; /* gut feeling */
5317 short vertical;
5318 short dialogHeight;
5319 short messageLines = 3;
5320 FontInfo textFontInfo;
5322 vgmDlgItm iconItm;
5323 vgmDlgItm messageItm;
5324 vgmDlgItm inputItm;
5325 vgmDlgItm buttonItm;
5327 WindowRef theWindow;
5329 /* Check 'v' flag in 'guioptions': vertical button placement. */
5330 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5332 /* Create a new Dialog Box from template. */
5333 theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5335 /* Get the WindowRef */
5336 theWindow = GetDialogWindow(theDialog);
5338 /* Hide the window.
5339 * 1. to avoid seeing slow drawing
5340 * 2. to prevent a problem seen while moving dialog item
5341 * within a visible window. (non-Carbon MacOS 9)
5342 * Could be avoided by changing the resource.
5344 HideWindow(theWindow);
5346 /* Change the graphical port to the dialog,
5347 * so we can measure the text with the proper font */
5348 GetPort(&oldPort);
5349 SetPortDialogPort(theDialog);
5351 /* Get the info about the default text,
5352 * used to calculate the height of the message
5353 * and of the text field */
5354 GetFontInfo(&textFontInfo);
5356 /* Set the dialog title */
5357 if (title != NULL)
5359 (void) C2PascalString(title, &PascalTitle);
5360 SetWTitle(theWindow, PascalTitle);
5363 /* Creates the buttons and add them to the Dialog Box. */
5364 buttonDITL = GetResource('DITL', 130);
5365 buttonChar = buttons;
5366 button = 0;
5368 for (;*buttonChar != 0;)
5370 /* Get the name of the button */
5371 button++;
5372 len = 0;
5373 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5375 if (*buttonChar != DLG_HOTKEY_CHAR)
5376 name[++len] = *buttonChar;
5378 if (*buttonChar != 0)
5379 buttonChar++;
5380 name[0] = len;
5382 /* Add the button */
5383 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5385 /* Change the button's name */
5386 macSetDialogItemText(theDialog, button, name);
5388 /* Resize the button to fit its name */
5389 width = StringWidth(name) + 2 * dfltButtonEdge;
5390 /* Limite the size of any button to an acceptable value. */
5391 /* TODO: Should be based on the message width */
5392 if (width > maxButtonWidth)
5393 width = maxButtonWidth;
5394 macSizeDialogItem(theDialog, button, width, 0);
5396 totalButtonWidth += width;
5398 if (width > widestButton)
5399 widestButton = width;
5401 ReleaseResource(buttonDITL);
5402 lastButton = button;
5404 /* Add the icon to the Dialog Box. */
5405 iconItm.idx = lastButton + 1;
5406 iconDITL = GetResource('DITL', 131);
5407 switch (type)
5409 case VIM_GENERIC: useIcon = kNoteIcon;
5410 case VIM_ERROR: useIcon = kStopIcon;
5411 case VIM_WARNING: useIcon = kCautionIcon;
5412 case VIM_INFO: useIcon = kNoteIcon;
5413 case VIM_QUESTION: useIcon = kNoteIcon;
5414 default: useIcon = kStopIcon;
5416 AppendDITL(theDialog, iconDITL, overlayDITL);
5417 ReleaseResource(iconDITL);
5418 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5419 /* TODO: Should the item be freed? */
5420 iconHandle = GetIcon(useIcon);
5421 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5423 /* Add the message to the Dialog box. */
5424 messageItm.idx = lastButton + 2;
5425 messageDITL = GetResource('DITL', 132);
5426 AppendDITL(theDialog, messageDITL, overlayDITL);
5427 ReleaseResource(messageDITL);
5428 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5429 (void) C2PascalString(message, &name);
5430 SetDialogItemText(itemHandle, name);
5431 messageItm.width = StringWidth(name);
5433 /* Add the input box if needed */
5434 if (textfield != NULL)
5436 /* Cheat for now reuse the message and convert to text edit */
5437 inputItm.idx = lastButton + 3;
5438 inputDITL = GetResource('DITL', 132);
5439 AppendDITL(theDialog, inputDITL, overlayDITL);
5440 ReleaseResource(inputDITL);
5441 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5442 /* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5443 (void) C2PascalString(textfield, &name);
5444 SetDialogItemText(itemHandle, name);
5445 inputItm.width = StringWidth(name);
5448 /* Set the <ENTER> and <ESC> button. */
5449 SetDialogDefaultItem(theDialog, dfltbutton);
5450 SetDialogCancelItem(theDialog, 0);
5452 /* Reposition element */
5454 /* Check if we need to force vertical */
5455 if (totalButtonWidth > maximumWidth)
5456 vertical = TRUE;
5458 /* Place icon */
5459 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5460 iconItm.box.right = box.right;
5461 iconItm.box.bottom = box.bottom;
5463 /* Place Message */
5464 messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5465 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent));
5466 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5468 /* Place Input */
5469 if (textfield != NULL)
5471 inputItm.box.left = messageItm.box.left;
5472 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5473 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5474 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5475 /* Convert the static text into a text edit.
5476 * For some reason this change need to be done last (Dany) */
5477 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5478 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5479 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5482 /* Place Button */
5483 if (textfield != NULL)
5485 buttonItm.box.left = inputItm.box.left;
5486 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing;
5488 else
5490 buttonItm.box.left = messageItm.box.left;
5491 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5494 for (button=1; button <= lastButton; button++)
5497 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5498 /* With vertical, it's better to have all buttons the same length */
5499 if (vertical)
5501 macSizeDialogItem(theDialog, button, widestButton, 0);
5502 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5504 /* Calculate position of next button */
5505 if (vertical)
5506 buttonItm.box.top = box.bottom + dfltElementSpacing;
5507 else
5508 buttonItm.box.left = box.right + dfltElementSpacing;
5511 /* Resize the dialog box */
5512 dialogHeight = box.bottom + dfltElementSpacing;
5513 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5515 /* Magic resize */
5516 AutoSizeDialog(theDialog);
5517 /* Need a horizontal resize anyway so not that useful */
5519 /* Display it */
5520 ShowWindow(theWindow);
5521 /* BringToFront(theWindow); */
5522 SelectWindow(theWindow);
5524 /* DrawDialog(theDialog); */
5525 #if 0
5526 GetPort(&oldPort);
5527 SetPortDialogPort(theDialog);
5528 #endif
5530 #ifdef USE_CARBONKEYHANDLER
5531 /* Avoid that we use key events for the main window. */
5532 dialog_busy = TRUE;
5533 #endif
5535 /* Hang until one of the button is hit */
5538 ModalDialog(nil, &itemHit);
5539 } while ((itemHit < 1) || (itemHit > lastButton));
5541 #ifdef USE_CARBONKEYHANDLER
5542 dialog_busy = FALSE;
5543 #endif
5545 /* Copy back the text entered by the user into the param */
5546 if (textfield != NULL)
5548 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5549 GetDialogItemText(itemHandle, (char_u *) &name);
5550 #if IOSIZE < 256
5551 /* Truncate the name to IOSIZE if needed */
5552 if (name[0] > IOSIZE)
5553 name[0] = IOSIZE - 1;
5554 #endif
5555 vim_strncpy(textfield, &name[1], name[0]);
5558 /* Restore the original graphical port */
5559 SetPort(oldPort);
5561 /* Get ride of th edialog (free memory) */
5562 DisposeDialog(theDialog);
5564 return itemHit;
5566 * Usefull thing which could be used
5567 * SetDialogTimeout(): Auto click a button after timeout
5568 * SetDialogTracksCursor() : Get the I-beam cursor over input box
5569 * MoveDialogItem(): Probably better than SetDialogItem
5570 * SizeDialogItem(): (but is it Carbon Only?)
5571 * AutoSizeDialog(): Magic resize of dialog based on text length
5574 #endif /* FEAT_DIALOG_GUI */
5577 * Display the saved error message(s).
5579 #ifdef USE_MCH_ERRMSG
5580 void
5581 display_errors(void)
5583 char *p;
5584 char_u pError[256];
5586 if (error_ga.ga_data == NULL)
5587 return;
5589 /* avoid putting up a message box with blanks only */
5590 for (p = (char *)error_ga.ga_data; *p; ++p)
5591 if (!isspace(*p))
5593 if (STRLEN(p) > 255)
5594 pError[0] = 255;
5595 else
5596 pError[0] = STRLEN(p);
5598 STRNCPY(&pError[1], p, pError[0]);
5599 ParamText(pError, nil, nil, nil);
5600 Alert(128, nil);
5601 break;
5602 /* TODO: handled message longer than 256 chars
5603 * use auto-sizeable alert
5604 * or dialog with scrollbars (TextEdit zone)
5607 ga_clear(&error_ga);
5609 #endif
5612 * Get current mouse coordinates in text window.
5614 void
5615 gui_mch_getmouse(int *x, int *y)
5617 Point where;
5619 GetMouse(&where);
5621 *x = where.h;
5622 *y = where.v;
5625 void
5626 gui_mch_setmouse(int x, int y)
5628 /* TODO */
5629 #if 0
5630 /* From FAQ 3-11 */
5632 CursorDevicePtr myMouse;
5633 Point where;
5635 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5636 != NGetTrapAddress(_Unimplemented, ToolTrap))
5638 /* New way */
5641 * Get first devoice with one button.
5642 * This will probably be the standad mouse
5643 * startat head of cursor dev list
5647 myMouse = nil;
5651 /* Get the next cursor device */
5652 CursorDeviceNextDevice(&myMouse);
5654 while ((myMouse != nil) && (myMouse->cntButtons != 1));
5656 CursorDeviceMoveTo(myMouse, x, y);
5658 else
5660 /* Old way */
5661 where.h = x;
5662 where.v = y;
5664 *(Point *)RawMouse = where;
5665 *(Point *)MTemp = where;
5666 *(Ptr) CrsrNew = 0xFFFF;
5668 #endif
5671 void
5672 gui_mch_show_popupmenu(vimmenu_T *menu)
5675 * Clone PopUp to use menu
5676 * Create a object descriptor for the current selection
5677 * Call the procedure
5680 MenuHandle CntxMenu;
5681 Point where;
5682 OSStatus status;
5683 UInt32 CntxType;
5684 SInt16 CntxMenuID;
5685 UInt16 CntxMenuItem;
5686 Str255 HelpName = "";
5687 GrafPtr savePort;
5689 /* Save Current Port: On MacOS X we seem to lose the port */
5690 GetPort(&savePort); /*OSX*/
5692 GetMouse(&where);
5693 LocalToGlobal(&where); /*OSX*/
5694 CntxMenu = menu->submenu_handle;
5696 /* TODO: Get the text selection from Vim */
5698 /* Call to Handle Popup */
5699 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
5700 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
5702 if (status == noErr)
5704 if (CntxType == kCMMenuItemSelected)
5706 /* Handle the menu CntxMenuID, CntxMenuItem */
5707 /* The submenu can be handle directly by gui_mac_handle_menu */
5708 /* But what about the current menu, is the menu changed by
5709 * ContextualMenuSelect */
5710 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
5712 else if (CntxMenuID == kCMShowHelpSelected)
5714 /* Should come up with the help */
5718 /* Restore original Port */
5719 SetPort(savePort); /*OSX*/
5722 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
5723 /* TODO: Is it need for MACOS_X? (Dany) */
5724 void
5725 mch_post_buffer_write(buf_T *buf)
5727 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
5728 Send_KAHL_MOD_AE(buf);
5730 #endif
5732 #ifdef FEAT_TITLE
5734 * Set the window title and icon.
5735 * (The icon is not taken care of).
5737 void
5738 gui_mch_settitle(char_u *title, char_u *icon)
5740 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
5741 * that 256. Even better get it to fit nicely in the titlebar.
5743 #ifdef MACOS_CONVERT
5744 CFStringRef windowTitle;
5745 size_t windowTitleLen;
5746 #else
5747 char_u *pascalTitle;
5748 #endif
5750 if (title == NULL) /* nothing to do */
5751 return;
5753 #ifdef MACOS_CONVERT
5754 windowTitleLen = STRLEN(title);
5755 windowTitle = mac_enc_to_cfstring(title, windowTitleLen);
5757 if (windowTitle)
5759 SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
5760 CFRelease(windowTitle);
5762 #else
5763 pascalTitle = C2Pascal_save(title);
5764 if (pascalTitle != NULL)
5766 SetWTitle(gui.VimWindow, pascalTitle);
5767 vim_free(pascalTitle);
5769 #endif
5771 #endif
5774 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
5778 C2PascalString(char_u *CString, Str255 *PascalString)
5780 char_u *PascalPtr = (char_u *) PascalString;
5781 int len;
5782 int i;
5784 PascalPtr[0] = 0;
5785 if (CString == NULL)
5786 return 0;
5788 len = STRLEN(CString);
5789 if (len > 255)
5790 len = 255;
5792 for (i = 0; i < len; i++)
5793 PascalPtr[i+1] = CString[i];
5795 PascalPtr[0] = len;
5797 return 0;
5801 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
5803 /* From FAQ 8-12 */
5804 Str255 filePascal;
5805 CInfoPBRec myCPB;
5806 OSErr err;
5808 (void) C2PascalString(file, &filePascal);
5810 myCPB.dirInfo.ioNamePtr = filePascal;
5811 myCPB.dirInfo.ioVRefNum = 0;
5812 myCPB.dirInfo.ioFDirIndex = 0;
5813 myCPB.dirInfo.ioDrDirID = 0;
5815 err= PBGetCatInfo(&myCPB, false);
5817 /* vRefNum, dirID, name */
5818 FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
5820 /* TODO: Use an error code mechanism */
5821 return 0;
5825 * Convert a FSSpec to a fuill path
5828 char_u *FullPathFromFSSpec_save(FSSpec file)
5831 * TODO: Add protection for 256 char max.
5834 CInfoPBRec theCPB;
5835 char_u fname[256];
5836 char_u *filenamePtr = fname;
5837 OSErr error;
5838 int folder = 1;
5839 #ifdef USE_UNIXFILENAME
5840 SInt16 dfltVol_vRefNum;
5841 SInt32 dfltVol_dirID;
5842 FSRef refFile;
5843 OSStatus status;
5844 UInt32 pathSize = 256;
5845 char_u pathname[256];
5846 char_u *path = pathname;
5847 #else
5848 Str255 directoryName;
5849 char_u temporary[255];
5850 char_u *temporaryPtr = temporary;
5851 #endif
5853 #ifdef USE_UNIXFILENAME
5854 /* Get the default volume */
5855 /* TODO: Remove as this only work if Vim is on the Boot Volume*/
5856 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
5858 if (error)
5859 return NULL;
5860 #endif
5862 /* Start filling fname with file.name */
5863 vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
5865 /* Get the info about the file specified in FSSpec */
5866 theCPB.dirInfo.ioFDirIndex = 0;
5867 theCPB.dirInfo.ioNamePtr = file.name;
5868 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5869 /*theCPB.hFileInfo.ioDirID = 0;*/
5870 theCPB.dirInfo.ioDrDirID = file.parID;
5872 /* As ioFDirIndex = 0, get the info of ioNamePtr,
5873 which is relative to ioVrefNum, ioDirID */
5874 error = PBGetCatInfo(&theCPB, false);
5876 /* If we are called for a new file we expect fnfErr */
5877 if ((error) && (error != fnfErr))
5878 return NULL;
5880 /* Check if it's a file or folder */
5881 /* default to file if file don't exist */
5882 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
5883 folder = 0; /* It's not a folder */
5884 else
5885 folder = 1;
5887 #ifdef USE_UNIXFILENAME
5889 * The function used here are available in Carbon, but
5890 * do nothing une MacOS 8 and 9
5892 if (error == fnfErr)
5894 /* If the file to be saved does not already exist, it isn't possible
5895 to convert its FSSpec into an FSRef. But we can construct an
5896 FSSpec for the file's parent folder (since we have its volume and
5897 directory IDs), and since that folder does exist, we can convert
5898 that FSSpec into an FSRef, convert the FSRef in turn into a path,
5899 and, finally, append the filename. */
5900 FSSpec dirSpec;
5901 FSRef dirRef;
5902 Str255 emptyFilename = "\p";
5903 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
5904 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
5905 if (error)
5906 return NULL;
5908 error = FSpMakeFSRef(&dirSpec, &dirRef);
5909 if (error)
5910 return NULL;
5912 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
5913 if (status)
5914 return NULL;
5916 STRCAT(path, "/");
5917 STRCAT(path, filenamePtr);
5919 else
5921 /* If the file to be saved already exists, we can get its full path
5922 by converting its FSSpec into an FSRef. */
5923 error=FSpMakeFSRef(&file, &refFile);
5924 if (error)
5925 return NULL;
5927 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
5928 if (status)
5929 return NULL;
5932 /* Add a slash at the end if needed */
5933 if (folder)
5934 STRCAT(path, "/");
5936 return (vim_strsave(path));
5937 #else
5938 /* TODO: Get rid of all USE_UNIXFILENAME below */
5939 /* Set ioNamePtr, it's the same area which is always reused. */
5940 theCPB.dirInfo.ioNamePtr = directoryName;
5942 /* Trick for first entry, set ioDrParID to the first value
5943 * we want for ioDrDirID*/
5944 theCPB.dirInfo.ioDrParID = file.parID;
5945 theCPB.dirInfo.ioDrDirID = file.parID;
5947 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
5950 theCPB.dirInfo.ioFDirIndex = -1;
5951 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
5952 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5953 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
5954 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
5956 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
5957 /* *ioNamePtr[0 TO 31] will be updated */
5958 error = PBGetCatInfo(&theCPB,false);
5960 if (error)
5961 return NULL;
5963 /* Put the new directoryName in front of the current fname */
5964 STRCPY(temporaryPtr, filenamePtr);
5965 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
5966 STRCAT(filenamePtr, ":");
5967 STRCAT(filenamePtr, temporaryPtr);
5969 #if 1 /* def USE_UNIXFILENAME */
5970 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
5971 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
5972 #else
5973 while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
5974 #endif
5976 /* Get the information about the volume on which the file reside */
5977 theCPB.dirInfo.ioFDirIndex = -1;
5978 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
5979 theCPB.dirInfo.ioVRefNum = file.vRefNum;
5980 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
5981 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
5983 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
5984 /* *ioNamePtr[0 TO 31] will be updated */
5985 error = PBGetCatInfo(&theCPB,false);
5987 if (error)
5988 return NULL;
5990 /* For MacOS Classic always add the volume name */
5991 /* For MacOS X add the volume name preceded by "Volumes" */
5992 /* when we are not referring to the boot volume */
5993 #ifdef USE_UNIXFILENAME
5994 if (file.vRefNum != dfltVol_vRefNum)
5995 #endif
5997 /* Add the volume name */
5998 STRCPY(temporaryPtr, filenamePtr);
5999 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6000 STRCAT(filenamePtr, ":");
6001 STRCAT(filenamePtr, temporaryPtr);
6003 #ifdef USE_UNIXFILENAME
6004 STRCPY(temporaryPtr, filenamePtr);
6005 filenamePtr[0] = 0; /* NULL terminate the string */
6006 STRCAT(filenamePtr, "Volumes:");
6007 STRCAT(filenamePtr, temporaryPtr);
6008 #endif
6011 /* Append final path separator if it's a folder */
6012 if (folder)
6013 STRCAT(fname, ":");
6015 /* As we use Unix File Name for MacOS X convert it */
6016 #ifdef USE_UNIXFILENAME
6017 /* Need to insert leading / */
6018 /* TODO: get the above code to use directly the / */
6019 STRCPY(&temporaryPtr[1], filenamePtr);
6020 temporaryPtr[0] = '/';
6021 STRCPY(filenamePtr, temporaryPtr);
6023 char *p;
6024 for (p = fname; *p; p++)
6025 if (*p == ':')
6026 *p = '/';
6028 #endif
6030 return (vim_strsave(fname));
6031 #endif
6034 #if defined(USE_IM_CONTROL) || defined(PROTO)
6036 * Input Method Control functions.
6040 * Notify cursor position to IM.
6042 void
6043 im_set_position(int row, int col)
6045 /* TODO: Implement me! */
6049 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6051 void
6052 im_set_active(int active)
6054 KeyScript(active ? smKeySysScript : smKeyRoman);
6058 * Get IM status. When IM is on, return not 0. Else return 0.
6061 im_get_status(void)
6063 SInt32 script = GetScriptManagerVariable(smKeyScript);
6064 return (script != smRoman
6065 && script == GetScriptManagerVariable(smSysScript)) ? 1 : 0;
6068 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6073 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6074 // drawer implementation
6075 static MenuRef contextMenu = NULL;
6076 enum
6078 kTabContextMenuId = 42,
6081 // the caller has to CFRelease() the returned string
6082 static CFStringRef
6083 getTabLabel(tabpage_T *page)
6085 get_tabline_label(page, FALSE);
6086 #ifdef MACOS_CONVERT
6087 return mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6088 #else
6089 // TODO: check internal encoding?
6090 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6091 kCFStringEncodingMacRoman);
6092 #endif
6096 #define DRAWER_SIZE 150
6097 #define DRAWER_INSET 16
6099 static ControlRef dataBrowser = NULL;
6101 // when the tabline is hidden, vim doesn't call update_tabline(). When
6102 // the tabline is shown again, show_tabline() is called before upate_tabline(),
6103 // and because of this, the tab labels and vims internal tabs are out of sync
6104 // for a very short time. to prevent inconsistent state, we store the labels
6105 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6106 static CFStringRef *tabLabels = NULL;
6107 static int tabLabelsSize = 0;
6109 enum
6111 kTabsColumn = 'Tabs'
6114 static int
6115 getTabCount(void)
6117 tabpage_T *tp;
6118 int numTabs = 0;
6120 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6121 ++numTabs;
6122 return numTabs;
6125 // data browser item display callback
6126 static OSStatus
6127 dbItemDataCallback(ControlRef browser,
6128 DataBrowserItemID itemID,
6129 DataBrowserPropertyID property /* column id */,
6130 DataBrowserItemDataRef itemData,
6131 Boolean changeValue)
6133 OSStatus status = noErr;
6135 // assert(property == kTabsColumn); // why is this violated??
6137 // changeValue is true if we have a modifieable list and data was changed.
6138 // In our case, it's always false.
6139 // (that is: if (changeValue) updateInternalData(); else return
6140 // internalData();
6141 if (!changeValue)
6143 CFStringRef str;
6145 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6146 str = tabLabels[itemID - 1];
6147 status = SetDataBrowserItemDataText(itemData, str);
6149 else
6150 status = errDataBrowserPropertyNotSupported;
6152 return status;
6155 // data browser action callback
6156 static void
6157 dbItemNotificationCallback(ControlRef browser,
6158 DataBrowserItemID item,
6159 DataBrowserItemNotification message)
6161 switch (message)
6163 case kDataBrowserItemSelected:
6164 send_tabline_event(item);
6165 break;
6169 // callbacks needed for contextual menu:
6170 static void
6171 dbGetContextualMenuCallback(ControlRef browser,
6172 MenuRef *menu,
6173 UInt32 *helpType,
6174 CFStringRef *helpItemString,
6175 AEDesc *selection)
6177 // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6178 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6179 *helpItemString = NULL;
6181 *menu = contextMenu;
6184 static void
6185 dbSelectContextualMenuCallback(ControlRef browser,
6186 MenuRef menu,
6187 UInt32 selectionType,
6188 SInt16 menuID,
6189 MenuItemIndex menuItem)
6191 if (selectionType == kCMMenuItemSelected)
6193 MenuCommand command;
6194 GetMenuItemCommandID(menu, menuItem, &command);
6196 // get tab that was selected when the context menu appeared
6197 // (there is always one tab selected). TODO: check if the context menu
6198 // isn't opened on an item but on empty space (has to be possible some
6199 // way, the finder does it too ;-) )
6200 Handle items = NewHandle(0);
6201 if (items != NULL)
6203 int numItems;
6205 GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6206 kDataBrowserItemIsSelected, items);
6207 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6208 if (numItems > 0)
6210 int idx;
6211 DataBrowserItemID *itemsPtr;
6213 HLock(items);
6214 itemsPtr = (DataBrowserItemID *)*items;
6215 idx = itemsPtr[0];
6216 HUnlock(items);
6217 send_tabline_menu_event(idx, command);
6219 DisposeHandle(items);
6224 // focus callback of the data browser to always leave focus in vim
6225 static OSStatus
6226 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6228 assert(GetEventClass(event) == kEventClassControl
6229 && GetEventKind(event) == kEventControlSetFocusPart);
6231 return paramErr;
6235 // drawer callback to resize data browser to drawer size
6236 static OSStatus
6237 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6239 switch (GetEventKind(event))
6241 case kEventWindowBoundsChanged: // move or resize
6243 UInt32 attribs;
6244 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6245 NULL, sizeof(attribs), NULL, &attribs);
6246 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6248 Rect r;
6249 GetWindowBounds(drawer, kWindowContentRgn, &r);
6250 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6251 SetControlBounds(dataBrowser, &r);
6252 SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6253 kTabsColumn, r.right);
6256 break;
6259 return eventNotHandledErr;
6262 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6263 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6264 // blue highlights in the list view on these systems, though. Oh well.)
6267 #import <mach-o/dyld.h>
6269 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6271 static OSStatus
6272 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6273 OptionBits inAttributesToSet,
6274 OptionBits inAttributesToClear)
6276 long osVersion;
6277 char *symbolName;
6278 NSSymbol symbol = NULL;
6279 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6280 OptionBits inAttributesToSet, OptionBits inAttributesToClear);
6282 Gestalt(gestaltSystemVersion, &osVersion);
6283 if (osVersion < 0x1040) // only supported for 10.4 (and up)
6284 return noErr;
6286 // C name mangling...
6287 symbolName = "_DataBrowserChangeAttributes";
6288 if (!NSIsSymbolNameDefined(symbolName)
6289 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6290 return noErr;
6292 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6293 if (dataBrowserChangeAttributes == NULL)
6294 return noErr; // well...
6295 return dataBrowserChangeAttributes(inDataBrowser,
6296 inAttributesToSet, inAttributesToClear);
6299 static void
6300 initialise_tabline(void)
6302 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6303 DataBrowserCallbacks dbCallbacks;
6304 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6305 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6306 DataBrowserListViewColumnDesc colDesc;
6308 // drawers have to have compositing enabled
6309 CreateNewWindow(kDrawerWindowClass,
6310 kWindowStandardHandlerAttribute
6311 | kWindowCompositingAttribute
6312 | kWindowResizableAttribute
6313 | kWindowLiveResizeAttribute,
6314 &drawerRect, &drawer);
6316 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6317 SetDrawerParent(drawer, gui.VimWindow);
6318 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6321 // create list view embedded in drawer
6322 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6323 &dataBrowser);
6325 dbCallbacks.version = kDataBrowserLatestCallbacks;
6326 InitDataBrowserCallbacks(&dbCallbacks);
6327 dbCallbacks.u.v1.itemDataCallback =
6328 NewDataBrowserItemDataUPP(dbItemDataCallback);
6329 dbCallbacks.u.v1.itemNotificationCallback =
6330 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6331 dbCallbacks.u.v1.getContextualMenuCallback =
6332 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6333 dbCallbacks.u.v1.selectContextualMenuCallback =
6334 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6336 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6338 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6339 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6340 SetDataBrowserSelectionFlags(dataBrowser,
6341 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6342 SetDataBrowserTableViewHiliteStyle(dataBrowser,
6343 kDataBrowserTableViewFillHilite);
6344 Boolean b = false;
6345 SetControlData(dataBrowser, kControlEntireControl,
6346 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6348 // enable blue background in data browser (this is only in 10.4 and vim
6349 // has to support older osx versions as well, so we have to load this
6350 // function dynamically)
6351 myDataBrowserChangeAttributes(dataBrowser,
6352 kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6354 // install callback that keeps focus in vim and away from the data browser
6355 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6356 NULL, NULL);
6358 // install callback that keeps data browser at the size of the drawer
6359 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6360 NULL, NULL);
6362 // add "tabs" column to data browser
6363 colDesc.propertyDesc.propertyID = kTabsColumn;
6364 colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6366 // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6367 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6369 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6370 colDesc.headerBtnDesc.minimumWidth = 100;
6371 colDesc.headerBtnDesc.maximumWidth = 150;
6372 colDesc.headerBtnDesc.titleOffset = 0;
6373 colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6374 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6375 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6376 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6378 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6380 // create tabline popup menu required by vim docs (see :he tabline-menu)
6381 CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6382 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6383 TABLINE_MENU_CLOSE, NULL);
6384 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6385 TABLINE_MENU_NEW, NULL);
6386 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6387 TABLINE_MENU_OPEN, NULL);
6392 * Show or hide the tabline.
6394 void
6395 gui_mch_show_tabline(int showit)
6397 if (showit == 0)
6398 CloseDrawer(drawer, true);
6399 else
6400 OpenDrawer(drawer, kWindowEdgeRight, true);
6404 * Return TRUE when tabline is displayed.
6407 gui_mch_showing_tabline(void)
6409 WindowDrawerState state = GetDrawerState(drawer);
6411 return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6415 * Update the labels of the tabline.
6417 void
6418 gui_mch_update_tabline(void)
6420 tabpage_T *tp;
6421 int numTabs = getTabCount();
6422 int nr = 1;
6423 int curtabidx = 1;
6425 // adjust data browser
6426 if (tabLabels != NULL)
6428 int i;
6430 for (i = 0; i < tabLabelsSize; ++i)
6431 CFRelease(tabLabels[i]);
6432 free(tabLabels);
6434 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6435 tabLabelsSize = numTabs;
6437 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6439 if (tp == curtab)
6440 curtabidx = nr;
6441 tabLabels[nr-1] = getTabLabel(tp);
6444 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6445 kDataBrowserItemNoProperty);
6446 // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6447 // can pass NULL for the id array
6448 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6449 kDataBrowserItemNoProperty);
6451 DataBrowserItemID item = curtabidx;
6452 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6456 * Set the current tab to "nr". First tab is 1.
6458 void
6459 gui_mch_set_curtab(nr)
6460 int nr;
6462 DataBrowserItemID item = nr;
6463 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6465 // TODO: call something like this?: (or restore scroll position, or...)
6466 RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6467 kDataBrowserRevealOnly);
6470 #endif // FEAT_GUI_TABLINE