Merge branch 'vim-with-runtime' into feat/persistent-undo
[vim_extended.git] / src / gui_mac.c
blob9112c56b71c21271c7444dc6136c13e313e127bf
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
63 static int im_is_active = FALSE;
64 #if 0
65 /* TODO: Implement me! */
66 static int im_start_row = 0;
67 static int im_start_col = 0;
68 #endif
70 #define NR_ELEMS(x) (sizeof(x) / sizeof(x[0]))
72 static TSMDocumentID gTSMDocument;
74 static void im_on_window_switch(int active);
75 static EventHandlerUPP keyEventHandlerUPP = NULL;
76 static EventHandlerUPP winEventHandlerUPP = NULL;
78 static pascal OSStatus gui_mac_handle_window_activate(
79 EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
81 static pascal OSStatus gui_mac_handle_text_input(
82 EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
84 static pascal OSStatus gui_mac_update_input_area(
85 EventHandlerCallRef nextHandler, EventRef theEvent);
87 static pascal OSStatus gui_mac_unicode_key_event(
88 EventHandlerCallRef nextHandler, EventRef theEvent);
90 #endif
93 /* Include some file. TODO: move into os_mac.h */
94 #include <Menus.h>
95 #include <Resources.h>
96 #include <Processes.h>
97 #ifdef USE_AEVENT
98 # include <AppleEvents.h>
99 # include <AERegistry.h>
100 #endif
101 # include <Gestalt.h>
102 #if UNIVERSAL_INTERFACES_VERSION >= 0x0330
103 # include <ControlDefinitions.h>
104 # include <Navigation.h> /* Navigation only part of ?? */
105 #endif
107 /* Help Manager (balloon.h, HM prefixed functions) are not supported
108 * under Carbon (Jussi) */
109 # if 0
110 /* New Help Interface for Mac, not implemented yet.*/
111 # include <MacHelp.h>
112 # endif
115 * These seem to be rectangle options. Why are they not found in
116 * headers? (Jussi)
118 #define kNothing 0
119 #define kCreateEmpty 2 /*1*/
120 #define kCreateRect 2
121 #define kDestroy 3
124 * Dany: Don't like those...
126 #define topLeft(r) (((Point*)&(r))[0])
127 #define botRight(r) (((Point*)&(r))[1])
130 /* Time of last mouse click, to detect double-click */
131 static long lastMouseTick = 0;
133 /* ??? */
134 static RgnHandle cursorRgn;
135 static RgnHandle dragRgn;
136 static Rect dragRect;
137 static short dragRectEnbl;
138 static short dragRectControl;
140 /* This variable is set when waiting for an event, which is the only moment
141 * scrollbar dragging can be done directly. It's not allowed while commands
142 * are executed, because it may move the cursor and that may cause unexpected
143 * problems (e.g., while ":s" is working).
145 static int allow_scrollbar = FALSE;
147 /* Last mouse click caused contextual menu, (to provide proper release) */
148 static short clickIsPopup;
150 /* Feedback Action for Scrollbar */
151 ControlActionUPP gScrollAction;
152 ControlActionUPP gScrollDrag;
154 /* Keeping track of which scrollbar is being dragged */
155 static ControlHandle dragged_sb = NULL;
157 /* Vector of char_u --> control index for hotkeys in dialogs */
158 static short *gDialogHotKeys;
160 static struct
162 FMFontFamily family;
163 FMFontSize size;
164 FMFontStyle style;
165 Boolean isPanelVisible;
166 } gFontPanelInfo = { 0, 0, 0, false };
168 #ifdef MACOS_CONVERT
169 # define USE_ATSUI_DRAWING
170 int p_macatsui_last;
171 ATSUStyle gFontStyle;
172 # ifdef FEAT_MBYTE
173 ATSUStyle gWideFontStyle;
174 # endif
175 Boolean gIsFontFallbackSet;
176 #endif
178 /* Colors Macros */
179 #define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b)
180 #define Red(c) ((c & 0x00FF0000) >> 16)
181 #define Green(c) ((c & 0x0000FF00) >> 8)
182 #define Blue(c) ((c & 0x000000FF) >> 0)
184 /* Key mapping */
186 #define vk_Esc 0x35 /* -> 1B */
188 #define vk_F1 0x7A /* -> 10 */
189 #define vk_F2 0x78 /*0x63*/
190 #define vk_F3 0x63 /*0x76*/
191 #define vk_F4 0x76 /*0x60*/
192 #define vk_F5 0x60 /*0x61*/
193 #define vk_F6 0x61 /*0x62*/
194 #define vk_F7 0x62 /*0x63*/ /*?*/
195 #define vk_F8 0x64
196 #define vk_F9 0x65
197 #define vk_F10 0x6D
198 #define vk_F11 0x67
199 #define vk_F12 0x6F
200 #define vk_F13 0x69
201 #define vk_F14 0x6B
202 #define vk_F15 0x71
204 #define vk_Clr 0x47 /* -> 1B (ESC) */
205 #define vk_Enter 0x4C /* -> 03 */
207 #define vk_Space 0x31 /* -> 20 */
208 #define vk_Tab 0x30 /* -> 09 */
209 #define vk_Return 0x24 /* -> 0D */
210 /* This is wrong for OSX, what is it for? */
211 #define vk_Delete 0X08 /* -> 08 BackSpace */
213 #define vk_Help 0x72 /* -> 05 */
214 #define vk_Home 0x73 /* -> 01 */
215 #define vk_PageUp 0x74 /* -> 0D */
216 #define vk_FwdDelete 0x75 /* -> 7F */
217 #define vk_End 0x77 /* -> 04 */
218 #define vk_PageDown 0x79 /* -> 0C */
220 #define vk_Up 0x7E /* -> 1E */
221 #define vk_Down 0x7D /* -> 1F */
222 #define vk_Left 0x7B /* -> 1C */
223 #define vk_Right 0x7C /* -> 1D */
225 #define vk_Undo vk_F1
226 #define vk_Cut vk_F2
227 #define vk_Copy vk_F3
228 #define vk_Paste vk_F4
229 #define vk_PrintScreen vk_F13
230 #define vk_SCrollLock vk_F14
231 #define vk_Pause vk_F15
232 #define vk_NumLock vk_Clr
233 #define vk_Insert vk_Help
235 #define KeySym char
237 static struct
239 KeySym key_sym;
240 char_u vim_code0;
241 char_u vim_code1;
242 } special_keys[] =
244 {vk_Up, 'k', 'u'},
245 {vk_Down, 'k', 'd'},
246 {vk_Left, 'k', 'l'},
247 {vk_Right, 'k', 'r'},
249 {vk_F1, 'k', '1'},
250 {vk_F2, 'k', '2'},
251 {vk_F3, 'k', '3'},
252 {vk_F4, 'k', '4'},
253 {vk_F5, 'k', '5'},
254 {vk_F6, 'k', '6'},
255 {vk_F7, 'k', '7'},
256 {vk_F8, 'k', '8'},
257 {vk_F9, 'k', '9'},
258 {vk_F10, 'k', ';'},
260 {vk_F11, 'F', '1'},
261 {vk_F12, 'F', '2'},
262 {vk_F13, 'F', '3'},
263 {vk_F14, 'F', '4'},
264 {vk_F15, 'F', '5'},
266 /* {XK_Help, '%', '1'}, */
267 /* {XK_Undo, '&', '8'}, */
268 /* {XK_BackSpace, 'k', 'b'}, */
269 #ifndef MACOS_X
270 {vk_Delete, 'k', 'b'},
271 #endif
272 {vk_Insert, 'k', 'I'},
273 {vk_FwdDelete, 'k', 'D'},
274 {vk_Home, 'k', 'h'},
275 {vk_End, '@', '7'},
276 /* {XK_Prior, 'k', 'P'}, */
277 /* {XK_Next, 'k', 'N'}, */
278 /* {XK_Print, '%', '9'}, */
280 {vk_PageUp, 'k', 'P'},
281 {vk_PageDown, 'k', 'N'},
283 /* End of list marker: */
284 {(KeySym)0, 0, 0}
288 * ------------------------------------------------------------
289 * Forward declaration (for those needed)
290 * ------------------------------------------------------------
293 #ifdef USE_AEVENT
294 OSErr HandleUnusedParms(const AppleEvent *theAEvent);
295 #endif
297 #ifdef FEAT_GUI_TABLINE
298 static void initialise_tabline(void);
299 static WindowRef drawer = NULL; // TODO: put into gui.h
300 #endif
302 #ifdef USE_ATSUI_DRAWING
303 static void gui_mac_set_font_attributes(GuiFont font);
304 static void gui_mac_dispose_atsui_style(void);
305 #endif
308 * ------------------------------------------------------------
309 * Conversion Utility
310 * ------------------------------------------------------------
314 * C2Pascal_save
316 * Allocate memory and convert the C-String passed in
317 * into a pascal string
321 char_u *
322 C2Pascal_save(char_u *Cstring)
324 char_u *PascalString;
325 int len;
327 if (Cstring == NULL)
328 return NULL;
330 len = STRLEN(Cstring);
332 if (len > 255) /* Truncate if necessary */
333 len = 255;
335 PascalString = alloc(len + 1);
336 if (PascalString != NULL)
338 mch_memmove(PascalString + 1, Cstring, len);
339 PascalString[0] = len;
342 return PascalString;
346 * C2Pascal_save_and_remove_backslash
348 * Allocate memory and convert the C-String passed in
349 * into a pascal string. Also remove the backslash at the same time
353 char_u *
354 C2Pascal_save_and_remove_backslash(char_u *Cstring)
356 char_u *PascalString;
357 int len;
358 char_u *p, *c;
360 len = STRLEN(Cstring);
362 if (len > 255) /* Truncate if necessary */
363 len = 255;
365 PascalString = alloc(len + 1);
366 if (PascalString != NULL)
368 for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++)
370 if ((*c == '\\') && (c[1] != 0))
372 c++;
374 *p = *c;
375 p++;
376 len++;
378 PascalString[0] = len;
381 return PascalString;
385 * Convert the modifiers of an Event into vim's modifiers (mouse)
388 int_u
389 EventModifiers2VimMouseModifiers(EventModifiers macModifiers)
391 int_u vimModifiers = 0x00;
393 if (macModifiers & (shiftKey | rightShiftKey))
394 vimModifiers |= MOUSE_SHIFT;
395 if (macModifiers & (controlKey | rightControlKey))
396 vimModifiers |= MOUSE_CTRL;
397 if (macModifiers & (optionKey | rightOptionKey))
398 vimModifiers |= MOUSE_ALT;
399 #if 0
400 /* Not yet supported */
401 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
402 vimModifiers |= MOUSE_CMD;
403 #endif
404 return (vimModifiers);
408 * Convert the modifiers of an Event into vim's modifiers (keys)
411 static int_u
412 EventModifiers2VimModifiers(EventModifiers macModifiers)
414 int_u vimModifiers = 0x00;
416 if (macModifiers & (shiftKey | rightShiftKey))
417 vimModifiers |= MOD_MASK_SHIFT;
418 if (macModifiers & (controlKey | rightControlKey))
419 vimModifiers |= MOD_MASK_CTRL;
420 if (macModifiers & (optionKey | rightOptionKey))
421 vimModifiers |= MOD_MASK_ALT;
422 #ifdef USE_CMD_KEY
423 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
424 vimModifiers |= MOD_MASK_CMD;
425 #endif
426 return (vimModifiers);
429 /* Convert a string representing a point size into pixels. The string should
430 * be a positive decimal number, with an optional decimal point (eg, "12", or
431 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
432 * character is stored in *end. The flag "vertical" says whether this
433 * calculation is for a vertical (height) size or a horizontal (width) one.
435 * From gui_w48.c
437 static int
438 points_to_pixels(char_u *str, char_u **end, int vertical)
440 int pixels;
441 int points = 0;
442 int divisor = 0;
444 while (*str)
446 if (*str == '.' && divisor == 0)
448 /* Start keeping a divisor, for later */
449 divisor = 1;
450 continue;
453 if (!isdigit(*str))
454 break;
456 points *= 10;
457 points += *str - '0';
458 divisor *= 10;
460 ++str;
463 if (divisor == 0)
464 divisor = 1;
466 pixels = points/divisor;
467 *end = str;
468 return pixels;
471 #ifdef MACOS_CONVERT
473 * Deletes all traces of any Windows-style mnemonic text (including any
474 * parentheses) from a menu item and returns the cleaned menu item title.
475 * The caller is responsible for releasing the returned string.
477 static CFStringRef
478 menu_title_removing_mnemonic(vimmenu_T *menu)
480 CFStringRef name;
481 size_t menuTitleLen;
482 CFIndex displayLen;
483 CFRange mnemonicStart;
484 CFRange mnemonicEnd;
485 CFMutableStringRef cleanedName;
487 menuTitleLen = STRLEN(menu->dname);
488 name = (CFStringRef) mac_enc_to_cfstring(menu->dname, menuTitleLen);
490 if (name)
492 /* Simple mnemonic-removal algorithm, assumes single parenthesized
493 * mnemonic character towards the end of the menu text */
494 mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards);
495 displayLen = CFStringGetLength(name);
497 if (mnemonicStart.location != kCFNotFound
498 && (mnemonicStart.location + 2) < displayLen
499 && CFStringGetCharacterAtIndex(name,
500 mnemonicStart.location + 1) == (UniChar)menu->mnemonic)
502 if (CFStringFindWithOptions(name, CFSTR(")"),
503 CFRangeMake(mnemonicStart.location + 1,
504 displayLen - mnemonicStart.location - 1),
505 kCFCompareBackwards, &mnemonicEnd) &&
506 (mnemonicStart.location + 2) == mnemonicEnd.location)
508 cleanedName = CFStringCreateMutableCopy(NULL, 0, name);
509 if (cleanedName)
511 CFStringDelete(cleanedName,
512 CFRangeMake(mnemonicStart.location,
513 mnemonicEnd.location + 1 -
514 mnemonicStart.location));
516 CFRelease(name);
517 name = cleanedName;
523 return name;
525 #endif
528 * Convert a list of FSSpec aliases into a list of fullpathname
529 * character strings.
532 char_u **
533 new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error)
535 char_u **fnames = NULL;
536 OSErr newError;
537 long fileCount;
538 FSSpec fileToOpen;
539 long actualSize;
540 AEKeyword dummyKeyword;
541 DescType dummyType;
543 /* Get number of files in list */
544 *error = AECountItems(theList, numFiles);
545 if (*error)
546 return fnames;
548 /* Allocate the pointer list */
549 fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
551 /* Empty out the list */
552 for (fileCount = 0; fileCount < *numFiles; fileCount++)
553 fnames[fileCount] = NULL;
555 /* Scan the list of FSSpec */
556 for (fileCount = 1; fileCount <= *numFiles; fileCount++)
558 /* Get the alias for the nth file, convert to an FSSpec */
559 newError = AEGetNthPtr(theList, fileCount, typeFSS,
560 &dummyKeyword, &dummyType,
561 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize);
562 if (newError)
564 /* Caller is able to clean up */
565 /* TODO: Should be clean up or not? For safety. */
566 return fnames;
569 /* Convert the FSSpec to a pathname */
570 fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen);
573 return (fnames);
577 * ------------------------------------------------------------
578 * CodeWarrior External Editor Support
579 * ------------------------------------------------------------
581 #ifdef FEAT_CW_EDITOR
584 * Handle the Window Search event from CodeWarrior
586 * Description
587 * -----------
589 * The IDE sends the Window Search AppleEvent to the editor when it
590 * needs to know whether a particular file is open in the editor.
592 * Event Reply
593 * -----------
595 * None. Put data in the location specified in the structure received.
597 * Remarks
598 * -------
600 * When the editor receives this event, determine whether the specified
601 * file is open. If it is, return the modification date/time for that file
602 * in the appropriate location specified in the structure. If the file is
603 * not opened, put the value fnfErr(file not found) in that location.
607 typedef struct WindowSearch WindowSearch;
608 struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/
610 FSSpec theFile; // identifies the file
611 long *theDate; // where to put the modification date/time
614 pascal OSErr
615 Handle_KAHL_SRCH_AE(
616 const AppleEvent *theAEvent,
617 AppleEvent *theReply,
618 long refCon)
620 OSErr error = noErr;
621 buf_T *buf;
622 int foundFile = false;
623 DescType typeCode;
624 WindowSearch SearchData;
625 Size actualSize;
627 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize);
628 if (error)
629 return error;
631 error = HandleUnusedParms(theAEvent);
632 if (error)
633 return error;
635 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
636 if (buf->b_ml.ml_mfp != NULL
637 && SearchData.theFile.parID == buf->b_FSSpec.parID
638 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0]
639 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0)
641 foundFile = true;
642 break;
645 if (foundFile == false)
646 *SearchData.theDate = fnfErr;
647 else
648 *SearchData.theDate = buf->b_mtime;
650 return error;
654 * Handle the Modified (from IDE to Editor) event from CodeWarrior
656 * Description
657 * -----------
659 * The IDE sends this event to the external editor when it wants to
660 * know which files that are open in the editor have been modified.
662 * Parameters None.
663 * ----------
665 * Event Reply
666 * -----------
667 * The reply for this event is:
669 * keyDirectObject typeAEList required
670 * each element in the list is a structure of typeChar
672 * Remarks
673 * -------
675 * When building the reply event, include one element in the list for
676 * each open file that has been modified.
680 typedef struct ModificationInfo ModificationInfo;
681 struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/
683 FSSpec theFile; // identifies the file
684 long theDate; // the date/time the file was last modified
685 short saved; // set this to zero when replying, unused
688 pascal OSErr
689 Handle_KAHL_MOD_AE(
690 const AppleEvent *theAEvent,
691 AppleEvent *theReply,
692 long refCon)
694 OSErr error = noErr;
695 AEDescList replyList;
696 long numFiles;
697 ModificationInfo theFile;
698 buf_T *buf;
700 theFile.saved = 0;
702 error = HandleUnusedParms(theAEvent);
703 if (error)
704 return error;
706 /* Send the reply */
707 /* replyObject.descriptorType = typeNull;
708 replyObject.dataHandle = nil;*/
710 /* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */
711 error = AECreateList(nil, 0, false, &replyList);
712 if (error)
713 return error;
715 #if 0
716 error = AECountItems(&replyList, &numFiles);
718 /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
719 * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
720 * sizeof(DescType))
723 /* AEPutDesc */
724 #endif
726 numFiles = 0;
727 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
728 if (buf->b_ml.ml_mfp != NULL)
730 /* Add this file to the list */
731 theFile.theFile = buf->b_FSSpec;
732 theFile.theDate = buf->b_mtime;
733 /* theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */
734 error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile));
737 #if 0
738 error = AECountItems(&replyList, &numFiles);
739 #endif
741 /* We can add data only if something to reply */
742 error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
744 if (replyList.dataHandle)
745 AEDisposeDesc(&replyList);
747 return error;
751 * Handle the Get Text event from CodeWarrior
753 * Description
754 * -----------
756 * The IDE sends the Get Text AppleEvent to the editor when it needs
757 * the source code from a file. For example, when the user issues a
758 * Check Syntax or Compile command, the compiler needs access to
759 * the source code contained in the file.
761 * Event Reply
762 * -----------
764 * None. Put data in locations specified in the structure received.
766 * Remarks
767 * -------
769 * When the editor receives this event, it must set the size of the handle
770 * in theText to fit the data in the file. It must then copy the entire
771 * contents of the specified file into the memory location specified in
772 * theText.
776 typedef struct CW_GetText CW_GetText;
777 struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/
779 FSSpec theFile; /* identifies the file */
780 Handle theText; /* the location where you return the text (must be resized properly) */
781 long *unused; /* 0 (not used) */
782 long *theDate; /* where to put the modification date/time */
785 pascal OSErr
786 Handle_KAHL_GTTX_AE(
787 const AppleEvent *theAEvent,
788 AppleEvent *theReply,
789 long refCon)
791 OSErr error = noErr;
792 buf_T *buf;
793 int foundFile = false;
794 DescType typeCode;
795 CW_GetText GetTextData;
796 Size actualSize;
797 char_u *line;
798 char_u *fullbuffer = NULL;
799 long linesize;
800 long lineStart;
801 long BufferSize;
802 long lineno;
804 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize);
806 if (error)
807 return error;
809 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
810 if (buf->b_ml.ml_mfp != NULL)
811 if (GetTextData.theFile.parID == buf->b_FSSpec.parID)
813 foundFile = true;
814 break;
817 if (foundFile)
819 BufferSize = 0; /* GetHandleSize(GetTextData.theText); */
820 for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++)
822 /* Must use the right buffer */
823 line = ml_get_buf(buf, (linenr_T) lineno, FALSE);
824 linesize = STRLEN(line) + 1;
825 lineStart = BufferSize;
826 BufferSize += linesize;
827 /* Resize handle to linesize+1 to include the linefeed */
828 SetHandleSize(GetTextData.theText, BufferSize);
829 if (GetHandleSize(GetTextData.theText) != BufferSize)
831 break; /* Simple handling for now */
833 else
835 HLock(GetTextData.theText);
836 fullbuffer = (char_u *) *GetTextData.theText;
837 STRCPY((char_u *)(fullbuffer + lineStart), line);
838 fullbuffer[BufferSize-1] = '\r';
839 HUnlock(GetTextData.theText);
842 if (fullbuffer != NULL)
844 HLock(GetTextData.theText);
845 fullbuffer[BufferSize-1] = 0;
846 HUnlock(GetTextData.theText);
848 if (foundFile == false)
849 *GetTextData.theDate = fnfErr;
850 else
851 /* *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/
852 *GetTextData.theDate = buf->b_mtime;
855 error = HandleUnusedParms(theAEvent);
857 return error;
864 /* Taken from MoreAppleEvents:ProcessHelpers*/
865 pascal OSErr
866 FindProcessBySignature(
867 const OSType targetType,
868 const OSType targetCreator,
869 ProcessSerialNumberPtr psnPtr)
871 OSErr anErr = noErr;
872 Boolean lookingForProcess = true;
874 ProcessInfoRec infoRec;
876 infoRec.processInfoLength = sizeof(ProcessInfoRec);
877 infoRec.processName = nil;
878 infoRec.processAppSpec = nil;
880 psnPtr->lowLongOfPSN = kNoProcess;
881 psnPtr->highLongOfPSN = kNoProcess;
883 while (lookingForProcess)
885 anErr = GetNextProcess(psnPtr);
886 if (anErr != noErr)
887 lookingForProcess = false;
888 else
890 anErr = GetProcessInformation(psnPtr, &infoRec);
891 if ((anErr == noErr)
892 && (infoRec.processType == targetType)
893 && (infoRec.processSignature == targetCreator))
894 lookingForProcess = false;
898 return anErr;
899 }//end FindProcessBySignature
901 void
902 Send_KAHL_MOD_AE(buf_T *buf)
904 OSErr anErr = noErr;
905 AEDesc targetAppDesc = { typeNull, nil };
906 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
907 AppleEvent theReply = { typeNull, nil };
908 AESendMode sendMode;
909 AppleEvent theEvent = {typeNull, nil };
910 AEIdleUPP idleProcUPP = nil;
911 ModificationInfo ModData;
914 anErr = FindProcessBySignature('APPL', 'CWIE', &psn);
915 if (anErr == noErr)
917 anErr = AECreateDesc(typeProcessSerialNumber, &psn,
918 sizeof(ProcessSerialNumber), &targetAppDesc);
920 if (anErr == noErr)
922 anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc,
923 kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
926 AEDisposeDesc(&targetAppDesc);
928 /* Add the parms */
929 ModData.theFile = buf->b_FSSpec;
930 ModData.theDate = buf->b_mtime;
932 if (anErr == noErr)
933 anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData));
935 if (idleProcUPP == nil)
936 sendMode = kAENoReply;
937 else
938 sendMode = kAEWaitReply;
940 if (anErr == noErr)
941 anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil);
942 if (anErr == noErr && sendMode == kAEWaitReply)
944 /* anErr = AEHGetHandlerError(&theReply);*/
946 (void) AEDisposeDesc(&theReply);
949 #endif /* FEAT_CW_EDITOR */
952 * ------------------------------------------------------------
953 * Apple Event Handling procedure
954 * ------------------------------------------------------------
956 #ifdef USE_AEVENT
959 * Handle the Unused parms of an AppleEvent
962 OSErr
963 HandleUnusedParms(const AppleEvent *theAEvent)
965 OSErr error;
966 long actualSize;
967 DescType dummyType;
968 AEKeyword missedKeyword;
970 /* Get the "missed keyword" attribute from the AppleEvent. */
971 error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr,
972 typeKeyword, &dummyType,
973 (Ptr)&missedKeyword, sizeof(missedKeyword),
974 &actualSize);
976 /* If the descriptor isn't found, then we got the required parameters. */
977 if (error == errAEDescNotFound)
979 error = noErr;
981 else
983 #if 0
984 /* Why is this removed? */
985 error = errAEEventNotHandled;
986 #endif
989 return error;
994 * Handle the ODoc AppleEvent
996 * Deals with all files dragged to the application icon.
1000 typedef struct SelectionRange SelectionRange;
1001 struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */
1003 short unused1; // 0 (not used)
1004 short lineNum; // line to select (<0 to specify range)
1005 long startRange; // start of selection range (if line < 0)
1006 long endRange; // end of selection range (if line < 0)
1007 long unused2; // 0 (not used)
1008 long theDate; // modification date/time
1011 /* The IDE uses the optional keyAEPosition parameter to tell the ed-
1012 itor the selection range. If lineNum is zero or greater, scroll the text
1013 to the specified line. If lineNum is less than zero, use the values in
1014 startRange and endRange to select the specified characters. Scroll
1015 the text to display the selection. If lineNum, startRange, and
1016 endRange are all negative, there is no selection range specified.
1019 pascal OSErr
1020 HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
1023 * TODO: Clean up the code with convert the AppleEvent into
1024 * a ":args"
1026 OSErr error = noErr;
1027 // OSErr firstError = noErr;
1028 // short numErrors = 0;
1029 AEDesc theList;
1030 DescType typeCode;
1031 long numFiles;
1032 // long fileCount;
1033 char_u **fnames;
1034 // char_u fname[256];
1035 Size actualSize;
1036 SelectionRange thePosition;
1037 short gotPosition = false;
1038 long lnum;
1040 /* the direct object parameter is the list of aliases to files (one or more) */
1041 error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
1042 if (error)
1043 return error;
1046 error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize);
1047 if (error == noErr)
1048 gotPosition = true;
1049 if (error == errAEDescNotFound)
1050 error = noErr;
1051 if (error)
1052 return error;
1055 error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
1057 if (^error) then
1059 if (thePosition.lineNum >= 0)
1061 // Goto this line
1063 else
1065 // Set the range char wise
1071 #ifdef FEAT_VISUAL
1072 reset_VIsual();
1073 #endif
1075 fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error);
1077 if (error)
1079 /* TODO: empty fnames[] first */
1080 vim_free(fnames);
1081 return (error);
1084 if (starting > 0)
1086 int i;
1087 char_u *p;
1088 int fnum = -1;
1090 /* these are the initial files dropped on the Vim icon */
1091 for (i = 0 ; i < numFiles; i++)
1093 if (ga_grow(&global_alist.al_ga, 1) == FAIL
1094 || (p = vim_strsave(fnames[i])) == NULL)
1095 mch_exit(2);
1096 else
1097 alist_add(&global_alist, p, 2);
1098 if (fnum == -1)
1099 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum;
1102 /* If the file name was already in the buffer list we need to switch
1103 * to it. */
1104 if (curbuf->b_fnum != fnum)
1106 char_u cmd[30];
1108 vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum);
1109 do_cmdline_cmd(cmd);
1112 /* Change directory to the location of the first file. */
1113 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1114 shorten_fnames(TRUE);
1116 goto finished;
1119 /* Handle the drop, :edit to get to the file */
1120 handle_drop(numFiles, fnames, FALSE);
1122 /* TODO: Handle the goto/select line more cleanly */
1123 if ((numFiles == 1) & (gotPosition))
1125 if (thePosition.lineNum >= 0)
1127 lnum = thePosition.lineNum + 1;
1128 /* oap->motion_type = MLINE;
1129 setpcmark();*/
1130 if (lnum < 1L)
1131 lnum = 1L;
1132 else if (lnum > curbuf->b_ml.ml_line_count)
1133 lnum = curbuf->b_ml.ml_line_count;
1134 curwin->w_cursor.lnum = lnum;
1135 curwin->w_cursor.col = 0;
1136 /* beginline(BL_SOL | BL_FIX);*/
1138 else
1139 goto_byte(thePosition.startRange + 1);
1142 /* Update the screen display */
1143 update_screen(NOT_VALID);
1144 #ifdef FEAT_VISUAL
1145 /* Select the text if possible */
1146 if (gotPosition)
1148 VIsual_active = TRUE;
1149 VIsual_select = FALSE;
1150 VIsual = curwin->w_cursor;
1151 if (thePosition.lineNum < 0)
1153 VIsual_mode = 'v';
1154 goto_byte(thePosition.endRange);
1156 else
1158 VIsual_mode = 'V';
1159 VIsual.col = 0;
1162 #endif
1163 setcursor();
1164 out_flush();
1166 /* Fake mouse event to wake from stall */
1167 PostEvent(mouseUp, 0);
1169 finished:
1170 AEDisposeDesc(&theList); /* dispose what we allocated */
1172 error = HandleUnusedParms(theAEvent);
1173 return error;
1180 pascal OSErr
1181 Handle_aevt_oapp_AE(
1182 const AppleEvent *theAEvent,
1183 AppleEvent *theReply,
1184 long refCon)
1186 OSErr error = noErr;
1188 error = HandleUnusedParms(theAEvent);
1189 return error;
1196 pascal OSErr
1197 Handle_aevt_quit_AE(
1198 const AppleEvent *theAEvent,
1199 AppleEvent *theReply,
1200 long refCon)
1202 OSErr error = noErr;
1204 error = HandleUnusedParms(theAEvent);
1205 if (error)
1206 return error;
1208 /* Need to fake a :confirm qa */
1209 do_cmdline_cmd((char_u *)"confirm qa");
1211 return error;
1218 pascal OSErr
1219 Handle_aevt_pdoc_AE(
1220 const AppleEvent *theAEvent,
1221 AppleEvent *theReply,
1222 long refCon)
1224 OSErr error = noErr;
1226 error = HandleUnusedParms(theAEvent);
1228 return error;
1232 * Handling of unknown AppleEvent
1234 * (Just get rid of all the parms)
1236 pascal OSErr
1237 Handle_unknown_AE(
1238 const AppleEvent *theAEvent,
1239 AppleEvent *theReply,
1240 long refCon)
1242 OSErr error = noErr;
1244 error = HandleUnusedParms(theAEvent);
1246 return error;
1251 * Install the various AppleEvent Handlers
1253 OSErr
1254 InstallAEHandlers(void)
1256 OSErr error;
1258 /* install open application handler */
1259 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1260 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1261 if (error)
1263 return error;
1266 /* install quit application handler */
1267 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1268 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1269 if (error)
1271 return error;
1274 /* install open document handler */
1275 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1276 NewAEEventHandlerUPP(HandleODocAE), 0, false);
1277 if (error)
1279 return error;
1282 /* install print document handler */
1283 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1284 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1286 /* Install Core Suite */
1287 /* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1288 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1290 error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1291 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1293 error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1294 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1296 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1297 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1299 error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1300 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1302 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1303 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1305 error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1306 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1308 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1309 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1311 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1312 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1314 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1315 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1317 error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1318 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1320 error = AEInstallEventHandler(kAECoreSuite, kAESave,
1321 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1323 error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1324 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1327 #ifdef FEAT_CW_EDITOR
1329 * Bind codewarrior support handlers
1331 error = AEInstallEventHandler('KAHL', 'GTTX',
1332 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1333 if (error)
1335 return error;
1337 error = AEInstallEventHandler('KAHL', 'SRCH',
1338 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1339 if (error)
1341 return error;
1343 error = AEInstallEventHandler('KAHL', 'MOD ',
1344 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1345 if (error)
1347 return error;
1349 #endif
1351 return error;
1354 #endif /* USE_AEVENT */
1358 * Callback function, installed by InstallFontPanelHandler(), below,
1359 * to handle Font Panel events.
1361 static OSStatus
1362 FontPanelHandler(
1363 EventHandlerCallRef inHandlerCallRef,
1364 EventRef inEvent,
1365 void *inUserData)
1367 if (GetEventKind(inEvent) == kEventFontPanelClosed)
1369 gFontPanelInfo.isPanelVisible = false;
1370 return noErr;
1373 if (GetEventKind(inEvent) == kEventFontSelection)
1375 OSStatus status;
1376 FMFontFamily newFamily;
1377 FMFontSize newSize;
1378 FMFontStyle newStyle;
1380 /* Retrieve the font family ID number. */
1381 status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1382 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1383 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1384 &newFamily);
1385 if (status == noErr)
1386 gFontPanelInfo.family = newFamily;
1388 /* Retrieve the font size. */
1389 status = GetEventParameter(inEvent, kEventParamFMFontSize,
1390 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1391 if (status == noErr)
1392 gFontPanelInfo.size = newSize;
1394 /* Retrieve the font style (bold, etc.). Currently unused. */
1395 status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1396 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1397 if (status == noErr)
1398 gFontPanelInfo.style = newStyle;
1400 return noErr;
1404 static void
1405 InstallFontPanelHandler(void)
1407 EventTypeSpec eventTypes[2];
1408 EventHandlerUPP handlerUPP;
1409 /* EventHandlerRef handlerRef; */
1411 eventTypes[0].eventClass = kEventClassFont;
1412 eventTypes[0].eventKind = kEventFontSelection;
1413 eventTypes[1].eventClass = kEventClassFont;
1414 eventTypes[1].eventKind = kEventFontPanelClosed;
1416 handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1418 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1419 /*userData=*/NULL, /*handlerRef=*/NULL);
1424 * Fill the buffer pointed to by outName with the name and size
1425 * of the font currently selected in the Font Panel.
1427 #define FONT_STYLE_BUFFER_SIZE 32
1428 static void
1429 GetFontPanelSelection(char_u *outName)
1431 Str255 buf;
1432 ByteCount fontNameLen = 0;
1433 ATSUFontID fid;
1434 char_u styleString[FONT_STYLE_BUFFER_SIZE];
1436 if (!outName)
1437 return;
1439 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1441 /* Canonicalize localized font names */
1442 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1443 gFontPanelInfo.style, &fid, NULL) != noErr)
1444 return;
1446 /* Request font name with Mac encoding (otherwise we could
1447 * get an unwanted utf-16 name) */
1448 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1449 kFontNoScriptCode, kFontNoLanguageCode,
1450 255, (char *)outName, &fontNameLen, NULL) != noErr)
1451 return;
1453 /* Only encode font size, because style (bold, italic, etc) is
1454 * already part of the font full name */
1455 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1456 gFontPanelInfo.size/*,
1457 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1458 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1459 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1461 if ((fontNameLen + STRLEN(styleString)) < 255)
1462 STRCPY(outName + fontNameLen, styleString);
1464 else
1466 *outName = NUL;
1472 * ------------------------------------------------------------
1473 * Unfiled yet
1474 * ------------------------------------------------------------
1478 * gui_mac_get_menu_item_index
1480 * Returns the index inside the menu wher
1482 short /* Shoulde we return MenuItemIndex? */
1483 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1485 short index;
1486 short itemIndex = -1;
1487 vimmenu_T *pBrother;
1489 /* Only menu without parent are the:
1490 * -menu in the menubar
1491 * -popup menu
1492 * -toolbar (guess)
1494 * Which are not items anyway.
1496 if (pMenu->parent)
1498 /* Start from the Oldest Brother */
1499 pBrother = pMenu->parent->children;
1500 index = 1;
1501 while ((pBrother) && (itemIndex == -1))
1503 if (pBrother == pMenu)
1504 itemIndex = index;
1505 index++;
1506 pBrother = pBrother->next;
1509 return itemIndex;
1512 static vimmenu_T *
1513 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1515 short index;
1516 vimmenu_T *pChildMenu;
1517 vimmenu_T *pElder = pMenu->parent;
1520 /* Only menu without parent are the:
1521 * -menu in the menubar
1522 * -popup menu
1523 * -toolbar (guess)
1525 * Which are not items anyway.
1528 if ((pElder) && (pElder->submenu_id == menuID))
1530 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1531 pMenu = pMenu->next;
1533 else
1535 for (; pMenu != NULL; pMenu = pMenu->next)
1537 if (pMenu->children != NULL)
1539 pChildMenu = gui_mac_get_vim_menu
1540 (menuID, itemIndex, pMenu->children);
1541 if (pChildMenu)
1543 pMenu = pChildMenu;
1544 break;
1549 return pMenu;
1553 * ------------------------------------------------------------
1554 * MacOS Feedback procedures
1555 * ------------------------------------------------------------
1557 pascal
1558 void
1559 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1561 scrollbar_T *sb;
1562 int value, dragging;
1563 ControlHandle theControlToUse;
1564 int dont_scroll_save = dont_scroll;
1566 theControlToUse = dragged_sb;
1568 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1570 if (sb == NULL)
1571 return;
1573 /* Need to find value by diff between Old Poss New Pos */
1574 value = GetControl32BitValue(theControlToUse);
1575 dragging = (partCode != 0);
1577 /* When "allow_scrollbar" is FALSE still need to remember the new
1578 * position, but don't actually scroll by setting "dont_scroll". */
1579 dont_scroll = !allow_scrollbar;
1580 gui_drag_scrollbar(sb, value, dragging);
1581 dont_scroll = dont_scroll_save;
1584 pascal
1585 void
1586 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1588 /* TODO: have live support */
1589 scrollbar_T *sb, *sb_info;
1590 long data;
1591 long value;
1592 int page;
1593 int dragging = FALSE;
1594 int dont_scroll_save = dont_scroll;
1596 sb = gui_find_scrollbar((long)GetControlReference(theControl));
1598 if (sb == NULL)
1599 return;
1601 if (sb->wp != NULL) /* Left or right scrollbar */
1604 * Careful: need to get scrollbar info out of first (left) scrollbar
1605 * for window, but keep real scrollbar too because we must pass it to
1606 * gui_drag_scrollbar().
1608 sb_info = &sb->wp->w_scrollbars[0];
1610 if (sb_info->size > 5)
1611 page = sb_info->size - 2; /* use two lines of context */
1612 else
1613 page = sb_info->size;
1615 else /* Bottom scrollbar */
1617 sb_info = sb;
1618 page = W_WIDTH(curwin) - 5;
1621 switch (partCode)
1623 case kControlUpButtonPart: data = -1; break;
1624 case kControlDownButtonPart: data = 1; break;
1625 case kControlPageDownPart: data = page; break;
1626 case kControlPageUpPart: data = -page; break;
1627 default: data = 0; break;
1630 value = sb_info->value + data;
1631 /* if (value > sb_info->max)
1632 value = sb_info->max;
1633 else if (value < 0)
1634 value = 0;*/
1636 /* When "allow_scrollbar" is FALSE still need to remember the new
1637 * position, but don't actually scroll by setting "dont_scroll". */
1638 dont_scroll = !allow_scrollbar;
1639 gui_drag_scrollbar(sb, value, dragging);
1640 dont_scroll = dont_scroll_save;
1642 out_flush();
1643 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1645 /* if (sb_info->wp != NULL)
1647 win_T *wp;
1648 int sb_num;
1650 sb_num = 0;
1651 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1652 sb_num++;
1654 if (wp != NULL)
1656 current_scrollbar = sb_num;
1657 scrollbar_value = value;
1658 gui_do_scroll();
1659 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1665 * ------------------------------------------------------------
1666 * MacOS Click Handling procedures
1667 * ------------------------------------------------------------
1672 * Handle a click inside the window, it may happens in the
1673 * scrollbar or the contents.
1675 * TODO: Add support for potential TOOLBAR
1677 void
1678 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1680 Point thePoint;
1681 int_u vimModifiers;
1682 short thePortion;
1683 ControlHandle theControl;
1684 int vimMouseButton;
1685 short dblClick;
1687 thePoint = theEvent->where;
1688 GlobalToLocal(&thePoint);
1689 SelectWindow(whichWindow);
1691 thePortion = FindControl(thePoint, whichWindow, &theControl);
1693 if (theControl != NUL)
1695 /* We hit a scollbar */
1697 if (thePortion != kControlIndicatorPart)
1699 dragged_sb = theControl;
1700 TrackControl(theControl, thePoint, gScrollAction);
1701 dragged_sb = NULL;
1703 else
1705 dragged_sb = theControl;
1706 #if 1
1707 TrackControl(theControl, thePoint, gScrollDrag);
1708 #else
1709 TrackControl(theControl, thePoint, NULL);
1710 #endif
1711 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1712 * button has been released */
1713 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1714 dragged_sb = NULL;
1717 else
1719 /* We are inside the contents */
1721 /* Convert the CTRL, OPTION, SHIFT and CMD key */
1722 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1724 /* Defaults to MOUSE_LEFT as there's only one mouse button */
1725 vimMouseButton = MOUSE_LEFT;
1727 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1728 /* TODO: NEEDED? */
1729 clickIsPopup = FALSE;
1731 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1733 vimMouseButton = MOUSE_RIGHT;
1734 vimModifiers &= ~MOUSE_CTRL;
1735 clickIsPopup = TRUE;
1738 /* Is it a double click ? */
1739 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1741 /* Send the mouse click to Vim */
1742 gui_send_mouse_event(vimMouseButton, thePoint.h,
1743 thePoint.v, dblClick, vimModifiers);
1745 /* Create the rectangle around the cursor to detect
1746 * the mouse dragging
1748 #if 0
1749 /* TODO: Do we need to this even for the contextual menu?
1750 * It may be require for popup_setpos, but for popup?
1752 if (vimMouseButton == MOUSE_LEFT)
1753 #endif
1755 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1756 FILL_Y(Y_2_ROW(thePoint.v)),
1757 FILL_X(X_2_COL(thePoint.h)+1),
1758 FILL_Y(Y_2_ROW(thePoint.v)+1));
1760 dragRectEnbl = TRUE;
1761 dragRectControl = kCreateRect;
1767 * Handle the click in the titlebar (to move the window)
1769 void
1770 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1772 Rect movingLimits;
1773 Rect *movingLimitsPtr = &movingLimits;
1775 /* TODO: may try to prevent move outside screen? */
1776 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1777 DragWindow(whichWindow, where, movingLimitsPtr);
1781 * Handle the click in the grow box
1783 void
1784 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1787 long newSize;
1788 unsigned short newWidth;
1789 unsigned short newHeight;
1790 Rect resizeLimits;
1791 Rect *resizeLimitsPtr = &resizeLimits;
1792 Rect NewContentRect;
1794 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1796 /* Set the minimum size */
1797 /* TODO: Should this come from Vim? */
1798 resizeLimits.top = 100;
1799 resizeLimits.left = 100;
1801 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1802 newWidth = NewContentRect.right - NewContentRect.left;
1803 newHeight = NewContentRect.bottom - NewContentRect.top;
1804 gui_resize_shell(newWidth, newHeight);
1805 gui_mch_set_bg_color(gui.back_pixel);
1806 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1810 * Handle the click in the zoom box
1812 static void
1813 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1815 Rect r;
1816 Point p;
1817 short thePart;
1819 /* ideal width is current */
1820 p.h = Columns * gui.char_width + 2 * gui.border_offset;
1821 if (gui.which_scrollbars[SBAR_LEFT])
1822 p.h += gui.scrollbar_width;
1823 if (gui.which_scrollbars[SBAR_RIGHT])
1824 p.h += gui.scrollbar_width;
1825 /* ideal height is as heigh as we can get */
1826 p.v = 15 * 1024;
1828 thePart = IsWindowInStandardState(whichWindow, &p, &r)
1829 ? inZoomIn : inZoomOut;
1831 if (!TrackBox(whichWindow, theEvent->where, thePart))
1832 return;
1834 /* use returned width */
1835 p.h = r.right - r.left;
1836 /* adjust returned height */
1837 p.v = r.bottom - r.top - 2 * gui.border_offset;
1838 if (gui.which_scrollbars[SBAR_BOTTOM])
1839 p.v -= gui.scrollbar_height;
1840 p.v -= p.v % gui.char_height;
1841 p.v += 2 * gui.border_width;
1842 if (gui.which_scrollbars[SBAR_BOTTOM]);
1843 p.v += gui.scrollbar_height;
1845 ZoomWindowIdeal(whichWindow, thePart, &p);
1847 GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1848 gui_resize_shell(r.right - r.left, r.bottom - r.top);
1849 gui_mch_set_bg_color(gui.back_pixel);
1850 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1854 * ------------------------------------------------------------
1855 * MacOS Event Handling procedure
1856 * ------------------------------------------------------------
1860 * Handle the Update Event
1863 void
1864 gui_mac_doUpdateEvent(EventRecord *event)
1866 WindowPtr whichWindow;
1867 GrafPtr savePort;
1868 RgnHandle updateRgn;
1869 Rect updateRect;
1870 Rect *updateRectPtr;
1871 Rect rc;
1872 Rect growRect;
1873 RgnHandle saveRgn;
1876 updateRgn = NewRgn();
1877 if (updateRgn == NULL)
1878 return;
1880 /* This could be done by the caller as we
1881 * don't require anything else out of the event
1883 whichWindow = (WindowPtr) event->message;
1885 /* Save Current Port */
1886 GetPort(&savePort);
1888 /* Select the Window's Port */
1889 SetPortWindowPort(whichWindow);
1891 /* Let's update the window */
1892 BeginUpdate(whichWindow);
1893 /* Redraw the biggest rectangle covering the area
1894 * to be updated.
1896 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1897 # if 0
1898 /* Would be more appropriate to use the following but doesn't
1899 * seem to work under MacOS X (Dany)
1901 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1902 # endif
1904 /* Use the HLock useless in Carbon? Is it harmful?*/
1905 HLock((Handle) updateRgn);
1907 updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1908 # if 0
1909 /* Code from original Carbon Port (using GetWindowRegion.
1910 * I believe the UpdateRgn is already in local (Dany)
1912 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1913 GlobalToLocal(&botRight(updateRect));
1914 # endif
1915 /* Update the content (i.e. the text) */
1916 gui_redraw(updateRectPtr->left, updateRectPtr->top,
1917 updateRectPtr->right - updateRectPtr->left,
1918 updateRectPtr->bottom - updateRectPtr->top);
1919 /* Clear the border areas if needed */
1920 gui_mch_set_bg_color(gui.back_pixel);
1921 if (updateRectPtr->left < FILL_X(0))
1923 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1924 EraseRect(&rc);
1926 if (updateRectPtr->top < FILL_Y(0))
1928 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1929 EraseRect(&rc);
1931 if (updateRectPtr->right > FILL_X(Columns))
1933 SetRect(&rc, FILL_X(Columns), 0,
1934 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1935 EraseRect(&rc);
1937 if (updateRectPtr->bottom > FILL_Y(Rows))
1939 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1940 FILL_Y(Rows) + gui.border_offset);
1941 EraseRect(&rc);
1943 HUnlock((Handle) updateRgn);
1944 DisposeRgn(updateRgn);
1946 /* Update scrollbars */
1947 DrawControls(whichWindow);
1949 /* Update the GrowBox */
1950 /* Taken from FAQ 33-27 */
1951 saveRgn = NewRgn();
1952 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1953 GetClip(saveRgn);
1954 ClipRect(&growRect);
1955 DrawGrowIcon(whichWindow);
1956 SetClip(saveRgn);
1957 DisposeRgn(saveRgn);
1958 EndUpdate(whichWindow);
1960 /* Restore original Port */
1961 SetPort(savePort);
1965 * Handle the activate/deactivate event
1966 * (apply to a window)
1968 void
1969 gui_mac_doActivateEvent(EventRecord *event)
1971 WindowPtr whichWindow;
1973 whichWindow = (WindowPtr) event->message;
1974 /* Dim scrollbars */
1975 if (whichWindow == gui.VimWindow)
1977 ControlRef rootControl;
1978 GetRootControl(gui.VimWindow, &rootControl);
1979 if ((event->modifiers) & activeFlag)
1980 ActivateControl(rootControl);
1981 else
1982 DeactivateControl(rootControl);
1985 /* Activate */
1986 gui_focus_change((event->modifiers) & activeFlag);
1991 * Handle the suspend/resume event
1992 * (apply to the application)
1994 void
1995 gui_mac_doSuspendEvent(EventRecord *event)
1997 /* The frontmost application just changed */
1999 /* NOTE: the suspend may happen before the deactivate
2000 * seen on MacOS X
2003 /* May not need to change focus as the window will
2004 * get an activate/deactivate event
2006 if (event->message & 1)
2007 /* Resume */
2008 gui_focus_change(TRUE);
2009 else
2010 /* Suspend */
2011 gui_focus_change(FALSE);
2015 * Handle the key
2017 #ifdef USE_CARBONKEYHANDLER
2018 static pascal OSStatus
2019 gui_mac_handle_window_activate(
2020 EventHandlerCallRef nextHandler,
2021 EventRef theEvent,
2022 void *data)
2024 UInt32 eventClass = GetEventClass(theEvent);
2025 UInt32 eventKind = GetEventKind(theEvent);
2027 if (eventClass == kEventClassWindow)
2029 switch (eventKind)
2031 case kEventWindowActivated:
2032 #if defined(USE_IM_CONTROL)
2033 im_on_window_switch(TRUE);
2034 #endif
2035 return noErr;
2037 case kEventWindowDeactivated:
2038 #if defined(USE_IM_CONTROL)
2039 im_on_window_switch(FALSE);
2040 #endif
2041 return noErr;
2045 return eventNotHandledErr;
2048 static pascal OSStatus
2049 gui_mac_handle_text_input(
2050 EventHandlerCallRef nextHandler,
2051 EventRef theEvent,
2052 void *data)
2054 UInt32 eventClass = GetEventClass(theEvent);
2055 UInt32 eventKind = GetEventKind(theEvent);
2057 if (eventClass != kEventClassTextInput)
2058 return eventNotHandledErr;
2060 if ((kEventTextInputUpdateActiveInputArea != eventKind) &&
2061 (kEventTextInputUnicodeForKeyEvent != eventKind) &&
2062 (kEventTextInputOffsetToPos != eventKind) &&
2063 (kEventTextInputPosToOffset != eventKind) &&
2064 (kEventTextInputGetSelectedText != eventKind))
2065 return eventNotHandledErr;
2067 switch (eventKind)
2069 case kEventTextInputUpdateActiveInputArea:
2070 return gui_mac_update_input_area(nextHandler, theEvent);
2071 case kEventTextInputUnicodeForKeyEvent:
2072 return gui_mac_unicode_key_event(nextHandler, theEvent);
2074 case kEventTextInputOffsetToPos:
2075 case kEventTextInputPosToOffset:
2076 case kEventTextInputGetSelectedText:
2077 break;
2080 return eventNotHandledErr;
2083 static pascal
2084 OSStatus gui_mac_update_input_area(
2085 EventHandlerCallRef nextHandler,
2086 EventRef theEvent)
2088 return eventNotHandledErr;
2091 static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the
2092 keys */
2094 # define INLINE_KEY_BUFFER_SIZE 80
2095 static pascal OSStatus
2096 gui_mac_unicode_key_event(
2097 EventHandlerCallRef nextHandler,
2098 EventRef theEvent)
2100 /* Multibyte-friendly key event handler */
2101 OSStatus err = -1;
2102 UInt32 actualSize;
2103 UniChar *text;
2104 char_u result[INLINE_KEY_BUFFER_SIZE];
2105 short len = 0;
2106 UInt32 key_sym;
2107 char charcode;
2108 int key_char;
2109 UInt32 modifiers, vimModifiers;
2110 size_t encLen;
2111 char_u *to = NULL;
2112 Boolean isSpecial = FALSE;
2113 int i;
2114 EventRef keyEvent;
2116 /* Mask the mouse (as per user setting) */
2117 if (p_mh)
2118 ObscureCursor();
2120 /* Don't use the keys when the dialog wants them. */
2121 if (dialog_busy)
2122 return eventNotHandledErr;
2124 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2125 typeUnicodeText, NULL, 0, &actualSize, NULL))
2126 return eventNotHandledErr;
2128 text = (UniChar *)alloc(actualSize);
2129 if (!text)
2130 return eventNotHandledErr;
2132 err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2133 typeUnicodeText, NULL, actualSize, NULL, text);
2134 require_noerr(err, done);
2136 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2137 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2138 require_noerr(err, done);
2140 err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2141 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2142 require_noerr(err, done);
2144 err = GetEventParameter(keyEvent, kEventParamKeyCode,
2145 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2146 require_noerr(err, done);
2148 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2149 typeChar, NULL, sizeof(char), NULL, &charcode);
2150 require_noerr(err, done);
2152 #ifndef USE_CMD_KEY
2153 if (modifiers & cmdKey)
2154 goto done; /* Let system handle Cmd+... */
2155 #endif
2157 key_char = charcode;
2158 vimModifiers = EventModifiers2VimModifiers(modifiers);
2160 /* Find the special key (eg., for cursor keys) */
2161 if (actualSize <= sizeof(UniChar) &&
2162 ((text[0] < 0x20) || (text[0] == 0x7f)))
2164 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2165 if (special_keys[i].key_sym == key_sym)
2167 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2168 special_keys[i].vim_code1);
2169 key_char = simplify_key(key_char,
2170 (int *)&vimModifiers);
2171 isSpecial = TRUE;
2172 break;
2176 /* Intercept CMD-. and CTRL-c */
2177 if (((modifiers & controlKey) && key_char == 'c') ||
2178 ((modifiers & cmdKey) && key_char == '.'))
2179 got_int = TRUE;
2181 if (!isSpecial)
2183 /* remove SHIFT for keys that are already shifted, e.g.,
2184 * '(' and '*' */
2185 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2186 vimModifiers &= ~MOD_MASK_SHIFT;
2188 /* remove CTRL from keys that already have it */
2189 if (key_char < 0x20)
2190 vimModifiers &= ~MOD_MASK_CTRL;
2192 /* don't process unicode characters here */
2193 if (!IS_SPECIAL(key_char))
2195 /* Following code to simplify and consolidate vimModifiers
2196 * taken liberally from gui_w48.c */
2197 key_char = simplify_key(key_char, (int *)&vimModifiers);
2199 /* Interpret META, include SHIFT, etc. */
2200 key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2201 if (key_char == CSI)
2202 key_char = K_CSI;
2204 if (IS_SPECIAL(key_char))
2205 isSpecial = TRUE;
2209 if (vimModifiers)
2211 result[len++] = CSI;
2212 result[len++] = KS_MODIFIER;
2213 result[len++] = vimModifiers;
2216 if (isSpecial && IS_SPECIAL(key_char))
2218 result[len++] = CSI;
2219 result[len++] = K_SECOND(key_char);
2220 result[len++] = K_THIRD(key_char);
2222 else
2224 encLen = actualSize;
2225 to = mac_utf16_to_enc(text, actualSize, &encLen);
2226 if (to)
2228 /* This is basically add_to_input_buf_csi() */
2229 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2231 result[len++] = to[i];
2232 if (to[i] == CSI)
2234 result[len++] = KS_EXTRA;
2235 result[len++] = (int)KE_CSI;
2238 vim_free(to);
2242 add_to_input_buf(result, len);
2243 err = noErr;
2245 done:
2246 vim_free(text);
2247 if (err == noErr)
2249 /* Fake event to wake up WNE (required to get
2250 * key repeat working */
2251 PostEvent(keyUp, 0);
2252 return noErr;
2255 return eventNotHandledErr;
2257 #else
2258 void
2259 gui_mac_doKeyEvent(EventRecord *theEvent)
2261 /* TODO: add support for COMMAND KEY */
2262 long menu;
2263 unsigned char string[20];
2264 short num, i;
2265 short len = 0;
2266 KeySym key_sym;
2267 int key_char;
2268 int modifiers;
2269 int simplify = FALSE;
2271 /* Mask the mouse (as per user setting) */
2272 if (p_mh)
2273 ObscureCursor();
2275 /* Get the key code and it's ASCII representation */
2276 key_sym = ((theEvent->message & keyCodeMask) >> 8);
2277 key_char = theEvent->message & charCodeMask;
2278 num = 1;
2280 /* Intercept CTRL-C */
2281 if (theEvent->modifiers & controlKey)
2283 if (key_char == Ctrl_C && ctrl_c_interrupts)
2284 got_int = TRUE;
2285 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2286 && (key_char == '2' || key_char == '6'))
2288 /* CTRL-^ and CTRL-@ don't work in the normal way. */
2289 if (key_char == '2')
2290 key_char = Ctrl_AT;
2291 else
2292 key_char = Ctrl_HAT;
2293 theEvent->modifiers = 0;
2297 /* Intercept CMD-. */
2298 if (theEvent->modifiers & cmdKey)
2299 if (key_char == '.')
2300 got_int = TRUE;
2302 /* Handle command key as per menu */
2303 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2304 if (theEvent->modifiers & cmdKey)
2305 /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2306 * Why the mouse button? */
2307 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2309 menu = MenuKey(key_char);
2310 if (HiWord(menu))
2312 gui_mac_handle_menu(menu);
2313 return;
2317 /* Convert the modifiers */
2318 modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2321 /* Handle special keys. */
2322 #if 0
2323 /* Why has this been removed? */
2324 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2325 #endif
2327 /* Find the special key (for non-printable keyt_char) */
2328 if ((key_char < 0x20) || (key_char == 0x7f))
2329 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2330 if (special_keys[i].key_sym == key_sym)
2332 # if 0
2333 /* We currently don't have not so special key */
2334 if (special_keys[i].vim_code1 == NUL)
2335 key_char = special_keys[i].vim_code0;
2336 else
2337 # endif
2338 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2339 special_keys[i].vim_code1);
2340 simplify = TRUE;
2341 break;
2345 /* For some keys the modifier is included in the char itself. */
2346 if (simplify || key_char == TAB || key_char == ' ')
2347 key_char = simplify_key(key_char, &modifiers);
2349 /* Add the modifier to the input bu if needed */
2350 /* Do not want SHIFT-A or CTRL-A with modifier */
2351 if (!IS_SPECIAL(key_char)
2352 && key_sym != vk_Space
2353 && key_sym != vk_Tab
2354 && key_sym != vk_Return
2355 && key_sym != vk_Enter
2356 && key_sym != vk_Esc)
2358 #if 1
2359 /* Clear modifiers when only one modifier is set */
2360 if ((modifiers == MOD_MASK_SHIFT)
2361 || (modifiers == MOD_MASK_CTRL)
2362 || (modifiers == MOD_MASK_ALT))
2363 modifiers = 0;
2364 #else
2365 if (modifiers & MOD_MASK_CTRL)
2366 modifiers = modifiers & ~MOD_MASK_CTRL;
2367 if (modifiers & MOD_MASK_ALT)
2368 modifiers = modifiers & ~MOD_MASK_ALT;
2369 if (modifiers & MOD_MASK_SHIFT)
2370 modifiers = modifiers & ~MOD_MASK_SHIFT;
2371 #endif
2373 if (modifiers)
2375 string[len++] = CSI;
2376 string[len++] = KS_MODIFIER;
2377 string[len++] = modifiers;
2380 if (IS_SPECIAL(key_char))
2382 string[len++] = CSI;
2383 string[len++] = K_SECOND(key_char);
2384 string[len++] = K_THIRD(key_char);
2386 else
2388 #ifdef FEAT_MBYTE
2389 /* Convert characters when needed (e.g., from MacRoman to latin1).
2390 * This doesn't work for the NUL byte. */
2391 if (input_conv.vc_type != CONV_NONE && key_char > 0)
2393 char_u from[2], *to;
2394 int l;
2396 from[0] = key_char;
2397 from[1] = NUL;
2398 l = 1;
2399 to = string_convert(&input_conv, from, &l);
2400 if (to != NULL)
2402 for (i = 0; i < l && len < 19; i++)
2404 if (to[i] == CSI)
2406 string[len++] = KS_EXTRA;
2407 string[len++] = KE_CSI;
2409 else
2410 string[len++] = to[i];
2412 vim_free(to);
2414 else
2415 string[len++] = key_char;
2417 else
2418 #endif
2419 string[len++] = key_char;
2422 if (len == 1 && string[0] == CSI)
2424 /* Turn CSI into K_CSI. */
2425 string[ len++ ] = KS_EXTRA;
2426 string[ len++ ] = KE_CSI;
2429 add_to_input_buf(string, len);
2431 #endif
2434 * Handle MouseClick
2436 void
2437 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2439 short thePart;
2440 WindowPtr whichWindow;
2442 thePart = FindWindow(theEvent->where, &whichWindow);
2444 #ifdef FEAT_GUI_TABLINE
2445 /* prevent that the vim window size changes if it's activated by a
2446 click into the tab pane */
2447 if (whichWindow == drawer)
2448 return;
2449 #endif
2451 switch (thePart)
2453 case (inDesk):
2454 /* TODO: what to do? */
2455 break;
2457 case (inMenuBar):
2458 gui_mac_handle_menu(MenuSelect(theEvent->where));
2459 break;
2461 case (inContent):
2462 gui_mac_doInContentClick(theEvent, whichWindow);
2463 break;
2465 case (inDrag):
2466 gui_mac_doInDragClick(theEvent->where, whichWindow);
2467 break;
2469 case (inGrow):
2470 gui_mac_doInGrowClick(theEvent->where, whichWindow);
2471 break;
2473 case (inGoAway):
2474 if (TrackGoAway(whichWindow, theEvent->where))
2475 gui_shell_closed();
2476 break;
2478 case (inZoomIn):
2479 case (inZoomOut):
2480 gui_mac_doInZoomClick(theEvent, whichWindow);
2481 break;
2486 * Handle MouseMoved
2487 * [this event is a moving in and out of a region]
2489 void
2490 gui_mac_doMouseMovedEvent(EventRecord *event)
2492 Point thePoint;
2493 int_u vimModifiers;
2495 thePoint = event->where;
2496 GlobalToLocal(&thePoint);
2497 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2499 if (!Button())
2500 gui_mouse_moved(thePoint.h, thePoint.v);
2501 else
2502 if (!clickIsPopup)
2503 gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2504 thePoint.v, FALSE, vimModifiers);
2506 /* Reset the region from which we move in and out */
2507 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2508 FILL_Y(Y_2_ROW(thePoint.v)),
2509 FILL_X(X_2_COL(thePoint.h)+1),
2510 FILL_Y(Y_2_ROW(thePoint.v)+1));
2512 if (dragRectEnbl)
2513 dragRectControl = kCreateRect;
2518 * Handle the mouse release
2520 void
2521 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2523 Point thePoint;
2524 int_u vimModifiers;
2526 /* TODO: Properly convert the Contextual menu mouse-up */
2527 /* Potential source of the double menu */
2528 lastMouseTick = theEvent->when;
2529 dragRectEnbl = FALSE;
2530 dragRectControl = kCreateEmpty;
2531 thePoint = theEvent->where;
2532 GlobalToLocal(&thePoint);
2534 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2535 if (clickIsPopup)
2537 vimModifiers &= ~MOUSE_CTRL;
2538 clickIsPopup = FALSE;
2540 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2543 static pascal OSStatus
2544 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2545 void *data)
2547 Point point;
2548 Rect bounds;
2549 UInt32 mod;
2550 SInt32 delta;
2551 int_u vim_mod;
2552 EventMouseWheelAxis axis;
2554 if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis,
2555 typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis)
2556 && axis != kEventMouseWheelAxisY)
2557 goto bail; /* Vim only does up-down scrolling */
2559 if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta,
2560 typeSInt32, NULL, sizeof(SInt32), NULL, &delta))
2561 goto bail;
2562 if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation,
2563 typeQDPoint, NULL, sizeof(Point), NULL, &point))
2564 goto bail;
2565 if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers,
2566 typeUInt32, NULL, sizeof(UInt32), NULL, &mod))
2567 goto bail;
2569 vim_mod = 0;
2570 if (mod & shiftKey)
2571 vim_mod |= MOUSE_SHIFT;
2572 if (mod & controlKey)
2573 vim_mod |= MOUSE_CTRL;
2574 if (mod & optionKey)
2575 vim_mod |= MOUSE_ALT;
2577 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2579 point.h -= bounds.left;
2580 point.v -= bounds.top;
2583 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2584 point.h, point.v, FALSE, vim_mod);
2586 /* post a bogus event to wake up WaitNextEvent */
2587 PostEvent(keyUp, 0);
2589 return noErr;
2591 bail:
2593 * when we fail give any additional callback handler a chance to perform
2594 * it's actions
2596 return CallNextEventHandler(nextHandler, theEvent);
2599 #if 0
2602 * This would be the normal way of invoking the contextual menu
2603 * but the Vim API doesn't seem to a support a request to get
2604 * the menu that we should display
2606 void
2607 gui_mac_handle_contextual_menu(event)
2608 EventRecord *event;
2611 * Clone PopUp to use menu
2612 * Create a object descriptor for the current selection
2613 * Call the procedure
2616 // Call to Handle Popup
2617 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2619 if (status != noErr)
2620 return;
2622 if (CntxType == kCMMenuItemSelected)
2624 /* Handle the menu CntxMenuID, CntxMenuItem */
2625 /* The submenu can be handle directly by gui_mac_handle_menu */
2626 /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2627 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2629 else if (CntxMenuID == kCMShowHelpSelected)
2631 /* Should come up with the help */
2635 #endif
2638 * Handle menubar selection
2640 void
2641 gui_mac_handle_menu(long menuChoice)
2643 short menu = HiWord(menuChoice);
2644 short item = LoWord(menuChoice);
2645 vimmenu_T *theVimMenu = root_menu;
2647 if (menu == 256) /* TODO: use constant or gui.xyz */
2649 if (item == 1)
2650 gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2652 else if (item != 0)
2654 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2656 if (theVimMenu)
2657 gui_menu_cb(theVimMenu);
2659 HiliteMenu(0);
2663 * Dispatch the event to proper handler
2666 void
2667 gui_mac_handle_event(EventRecord *event)
2669 OSErr error;
2671 /* Handle contextual menu right now (if needed) */
2672 if (IsShowContextualMenuClick(event))
2674 # if 0
2675 gui_mac_handle_contextual_menu(event);
2676 # else
2677 gui_mac_doMouseDownEvent(event);
2678 # endif
2679 return;
2682 /* Handle normal event */
2683 switch (event->what)
2685 #ifndef USE_CARBONKEYHANDLER
2686 case (keyDown):
2687 case (autoKey):
2688 gui_mac_doKeyEvent(event);
2689 break;
2690 #endif
2691 case (keyUp):
2692 /* We don't care about when the key is released */
2693 break;
2695 case (mouseDown):
2696 gui_mac_doMouseDownEvent(event);
2697 break;
2699 case (mouseUp):
2700 gui_mac_doMouseUpEvent(event);
2701 break;
2703 case (updateEvt):
2704 gui_mac_doUpdateEvent(event);
2705 break;
2707 case (diskEvt):
2708 /* We don't need special handling for disk insertion */
2709 break;
2711 case (activateEvt):
2712 gui_mac_doActivateEvent(event);
2713 break;
2715 case (osEvt):
2716 switch ((event->message >> 24) & 0xFF)
2718 case (0xFA): /* mouseMovedMessage */
2719 gui_mac_doMouseMovedEvent(event);
2720 break;
2721 case (0x01): /* suspendResumeMessage */
2722 gui_mac_doSuspendEvent(event);
2723 break;
2725 break;
2727 #ifdef USE_AEVENT
2728 case (kHighLevelEvent):
2729 /* Someone's talking to us, through AppleEvents */
2730 error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2731 break;
2732 #endif
2737 * ------------------------------------------------------------
2738 * Unknown Stuff
2739 * ------------------------------------------------------------
2743 GuiFont
2744 gui_mac_find_font(char_u *font_name)
2746 char_u c;
2747 char_u *p;
2748 char_u pFontName[256];
2749 Str255 systemFontname;
2750 short font_id;
2751 short size=9;
2752 GuiFont font;
2753 #if 0
2754 char_u *fontNamePtr;
2755 #endif
2757 for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2760 c = *p;
2761 *p = 0;
2763 #if 1
2764 STRCPY(&pFontName[1], font_name);
2765 pFontName[0] = STRLEN(font_name);
2766 *p = c;
2768 /* Get the font name, minus the style suffix (:h, etc) */
2769 char_u fontName[256];
2770 char_u *styleStart = vim_strchr(font_name, ':');
2771 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2772 vim_strncpy(fontName, font_name, fontNameLen);
2774 ATSUFontID fontRef;
2775 FMFontStyle fontStyle;
2776 font_id = 0;
2778 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2779 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2780 &fontRef) == noErr)
2782 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2783 font_id = 0;
2786 if (font_id == 0)
2789 * Try again, this time replacing underscores in the font name
2790 * with spaces (:set guifont allows the two to be used
2791 * interchangeably; the Font Manager doesn't).
2793 int i, changed = FALSE;
2795 for (i = pFontName[0]; i > 0; --i)
2797 if (pFontName[i] == '_')
2799 pFontName[i] = ' ';
2800 changed = TRUE;
2803 if (changed)
2804 if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2805 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2806 kFontNoLanguageCode, &fontRef) == noErr)
2808 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2809 font_id = 0;
2813 #else
2814 /* name = C2Pascal_save(menu->dname); */
2815 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2817 GetFNum(fontNamePtr, &font_id);
2818 #endif
2821 if (font_id == 0)
2823 /* Oups, the system font was it the one the user want */
2825 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2826 return NOFONT;
2827 if (!EqualString(pFontName, systemFontname, false, false))
2828 return NOFONT;
2830 if (*p == ':')
2832 p++;
2833 /* Set the values found after ':' */
2834 while (*p)
2836 switch (*p++)
2838 case 'h':
2839 size = points_to_pixels(p, &p, TRUE);
2840 break;
2842 * TODO: Maybe accept width and styles
2845 while (*p == ':')
2846 p++;
2850 if (size < 1)
2851 size = 1; /* Avoid having a size of 0 with system font */
2853 font = (size << 16) + ((long) font_id & 0xFFFF);
2855 return font;
2859 * ------------------------------------------------------------
2860 * GUI_MCH functionality
2861 * ------------------------------------------------------------
2865 * Parse the GUI related command-line arguments. Any arguments used are
2866 * deleted from argv, and *argc is decremented accordingly. This is called
2867 * when vim is started, whether or not the GUI has been started.
2869 void
2870 gui_mch_prepare(int *argc, char **argv)
2872 /* TODO: Move most of this stuff toward gui_mch_init */
2873 #ifdef USE_EXE_NAME
2874 FSSpec applDir;
2875 # ifndef USE_FIND_BUNDLE_PATH
2876 short applVRefNum;
2877 long applDirID;
2878 Str255 volName;
2879 # else
2880 ProcessSerialNumber psn;
2881 FSRef applFSRef;
2882 # endif
2883 #endif
2885 #if 0
2886 InitCursor();
2888 RegisterAppearanceClient();
2890 #ifdef USE_AEVENT
2891 (void) InstallAEHandlers();
2892 #endif
2894 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2896 AppendMenu(pomme, "\pAbout VIM");
2898 InsertMenu(pomme, 0);
2900 DrawMenuBar();
2903 #ifndef USE_OFFSETED_WINDOW
2904 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2905 #else
2906 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2907 #endif
2910 CreateNewWindow(kDocumentWindowClass,
2911 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2912 &windRect, &gui.VimWindow);
2913 SetPortWindowPort(gui.VimWindow);
2915 gui.char_width = 7;
2916 gui.char_height = 11;
2917 gui.char_ascent = 6;
2918 gui.num_rows = 24;
2919 gui.num_cols = 80;
2920 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2922 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2923 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2925 dragRectEnbl = FALSE;
2926 dragRgn = NULL;
2927 dragRectControl = kCreateEmpty;
2928 cursorRgn = NewRgn();
2929 #endif
2930 #ifdef USE_EXE_NAME
2931 # ifndef USE_FIND_BUNDLE_PATH
2932 HGetVol(volName, &applVRefNum, &applDirID);
2933 /* TN2015: mention a possible bad VRefNum */
2934 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2935 # else
2936 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2937 * of TN2015
2939 (void)GetCurrentProcess(&psn);
2940 /* if (err != noErr) return err; */
2942 (void)GetProcessBundleLocation(&psn, &applFSRef);
2943 /* if (err != noErr) return err; */
2945 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2947 /* This technic return NIL when we disallow_gui */
2948 # endif
2949 exe_name = FullPathFromFSSpec_save(applDir);
2950 #endif
2953 #ifndef ALWAYS_USE_GUI
2955 * Check if the GUI can be started. Called before gvimrc is sourced.
2956 * Return OK or FAIL.
2959 gui_mch_init_check(void)
2961 /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2962 * using the >console
2964 if (disallow_gui) /* see main.c for reason to disallow */
2965 return FAIL;
2966 return OK;
2968 #endif
2970 static OSErr
2971 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2973 int x, y;
2974 int_u modifiers;
2975 char_u **fnames = NULL;
2976 int count;
2977 int i, j;
2979 /* Get drop position, modifiers and count of items */
2981 Point point;
2982 SInt16 mouseUpModifiers;
2983 UInt16 countItem;
2985 GetDragMouse(theDrag, &point, NULL);
2986 GlobalToLocal(&point);
2987 x = point.h;
2988 y = point.v;
2989 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2990 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2991 CountDragItems(theDrag, &countItem);
2992 count = countItem;
2995 fnames = (char_u **)alloc(count * sizeof(char_u *));
2996 if (fnames == NULL)
2997 return dragNotAcceptedErr;
2999 /* Get file names dropped */
3000 for (i = j = 0; i < count; ++i)
3002 DragItemRef item;
3003 OSErr err;
3004 Size size;
3005 FlavorType type = flavorTypeHFS;
3006 HFSFlavor hfsFlavor;
3008 fnames[i] = NULL;
3009 GetDragItemReferenceNumber(theDrag, i + 1, &item);
3010 err = GetFlavorDataSize(theDrag, item, type, &size);
3011 if (err != noErr || size > sizeof(hfsFlavor))
3012 continue;
3013 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
3014 if (err != noErr)
3015 continue;
3016 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
3018 count = j;
3020 gui_handle_drop(x, y, modifiers, fnames, count);
3022 /* Fake mouse event to wake from stall */
3023 PostEvent(mouseUp, 0);
3025 return noErr;
3029 * Initialise the GUI. Create all the windows, set up all the call-backs
3030 * etc.
3033 gui_mch_init(void)
3035 /* TODO: Move most of this stuff toward gui_mch_init */
3036 Rect windRect;
3037 MenuHandle pomme;
3038 EventHandlerRef mouseWheelHandlerRef;
3039 EventTypeSpec eventTypeSpec;
3040 ControlRef rootControl;
3042 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
3043 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
3045 #if 1
3046 InitCursor();
3048 RegisterAppearanceClient();
3050 #ifdef USE_AEVENT
3051 (void) InstallAEHandlers();
3052 #endif
3054 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
3056 AppendMenu(pomme, "\pAbout VIM");
3058 InsertMenu(pomme, 0);
3060 DrawMenuBar();
3063 #ifndef USE_OFFSETED_WINDOW
3064 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
3065 #else
3066 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
3067 #endif
3069 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
3070 zoomDocProc,
3071 (WindowPtr)-1L, true, 0);
3072 CreateRootControl(gui.VimWindow, &rootControl);
3073 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
3074 gui.VimWindow, NULL);
3075 SetPortWindowPort(gui.VimWindow);
3077 gui.char_width = 7;
3078 gui.char_height = 11;
3079 gui.char_ascent = 6;
3080 gui.num_rows = 24;
3081 gui.num_cols = 80;
3082 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
3084 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
3085 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
3087 /* Install Carbon event callbacks. */
3088 (void)InstallFontPanelHandler();
3090 dragRectEnbl = FALSE;
3091 dragRgn = NULL;
3092 dragRectControl = kCreateEmpty;
3093 cursorRgn = NewRgn();
3094 #endif
3095 /* Display any pending error messages */
3096 display_errors();
3098 /* Get background/foreground colors from system */
3099 /* TODO: do the appropriate call to get real defaults */
3100 gui.norm_pixel = 0x00000000;
3101 gui.back_pixel = 0x00FFFFFF;
3103 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3104 * file). */
3105 set_normal_colors();
3108 * Check that none of the colors are the same as the background color.
3109 * Then store the current values as the defaults.
3111 gui_check_colors();
3112 gui.def_norm_pixel = gui.norm_pixel;
3113 gui.def_back_pixel = gui.back_pixel;
3115 /* Get the colors for the highlight groups (gui_check_colors() might have
3116 * changed them) */
3117 highlight_gui_started();
3120 * Setting the gui constants
3122 #ifdef FEAT_MENU
3123 gui.menu_height = 0;
3124 #endif
3125 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3126 gui.border_offset = gui.border_width = 2;
3128 /* If Quartz-style text anti aliasing is available (see
3129 gui_mch_draw_string() below), enable it for all font sizes. */
3130 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3132 eventTypeSpec.eventClass = kEventClassMouse;
3133 eventTypeSpec.eventKind = kEventMouseWheelMoved;
3134 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3135 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3136 &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3138 mouseWheelHandlerRef = NULL;
3139 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3140 mouseWheelHandlerUPP = NULL;
3143 #ifdef USE_CARBONKEYHANDLER
3144 InterfaceTypeList supportedServices = { kUnicodeDocument };
3145 NewTSMDocument(1, supportedServices, &gTSMDocument, 0);
3147 /* We don't support inline input yet, use input window by default */
3148 UseInputWindow(gTSMDocument, TRUE);
3150 /* Should we activate the document by default? */
3151 // ActivateTSMDocument(gTSMDocument);
3153 EventTypeSpec textEventTypes[] = {
3154 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
3155 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
3156 { kEventClassTextInput, kEventTextInputPosToOffset },
3157 { kEventClassTextInput, kEventTextInputOffsetToPos },
3160 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input);
3161 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP,
3162 NR_ELEMS(textEventTypes),
3163 textEventTypes, NULL, NULL))
3165 DisposeEventHandlerUPP(keyEventHandlerUPP);
3166 keyEventHandlerUPP = NULL;
3169 EventTypeSpec windowEventTypes[] = {
3170 { kEventClassWindow, kEventWindowActivated },
3171 { kEventClassWindow, kEventWindowDeactivated },
3174 /* Install window event handler to support TSMDocument activate and
3175 * deactivate */
3176 winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate);
3177 if (noErr != InstallWindowEventHandler(gui.VimWindow,
3178 winEventHandlerUPP,
3179 NR_ELEMS(windowEventTypes),
3180 windowEventTypes, NULL, NULL))
3182 DisposeEventHandlerUPP(winEventHandlerUPP);
3183 winEventHandlerUPP = NULL;
3185 #endif
3188 #ifdef FEAT_MBYTE
3189 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3190 #endif
3193 #ifdef FEAT_GUI_TABLINE
3195 * Create the tabline
3197 initialise_tabline();
3198 #endif
3200 /* TODO: Load bitmap if using TOOLBAR */
3201 return OK;
3205 * Called when the foreground or background color has been changed.
3207 void
3208 gui_mch_new_colors(void)
3210 /* TODO:
3211 * This proc is called when Normal is set to a value
3212 * so what msut be done? I don't know
3217 * Open the GUI window which was created by a call to gui_mch_init().
3220 gui_mch_open(void)
3222 ShowWindow(gui.VimWindow);
3224 if (gui_win_x != -1 && gui_win_y != -1)
3225 gui_mch_set_winpos(gui_win_x, gui_win_y);
3228 * Make the GUI the foreground process (in case it was launched
3229 * from the Terminal or via :gui).
3232 ProcessSerialNumber psn;
3233 if (GetCurrentProcess(&psn) == noErr)
3234 SetFrontProcess(&psn);
3237 return OK;
3240 #ifdef USE_ATSUI_DRAWING
3241 static void
3242 gui_mac_dispose_atsui_style(void)
3244 if (p_macatsui && gFontStyle)
3245 ATSUDisposeStyle(gFontStyle);
3246 #ifdef FEAT_MBYTE
3247 if (p_macatsui && gWideFontStyle)
3248 ATSUDisposeStyle(gWideFontStyle);
3249 #endif
3251 #endif
3253 void
3254 gui_mch_exit(int rc)
3256 /* TODO: find out all what is missing here? */
3257 DisposeRgn(cursorRgn);
3259 #ifdef USE_CARBONKEYHANDLER
3260 if (keyEventHandlerUPP)
3261 DisposeEventHandlerUPP(keyEventHandlerUPP);
3262 #endif
3264 if (mouseWheelHandlerUPP != NULL)
3265 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3267 #ifdef USE_ATSUI_DRAWING
3268 gui_mac_dispose_atsui_style();
3269 #endif
3271 #ifdef USE_CARBONKEYHANDLER
3272 FixTSMDocument(gTSMDocument);
3273 DeactivateTSMDocument(gTSMDocument);
3274 DeleteTSMDocument(gTSMDocument);
3275 #endif
3277 /* Exit to shell? */
3278 exit(rc);
3282 * Get the position of the top left corner of the window.
3285 gui_mch_get_winpos(int *x, int *y)
3287 /* TODO */
3288 Rect bounds;
3289 OSStatus status;
3291 /* Carbon >= 1.0.2, MacOS >= 8.5 */
3292 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3294 if (status != noErr)
3295 return FAIL;
3296 *x = bounds.left;
3297 *y = bounds.top;
3298 return OK;
3299 return FAIL;
3303 * Set the position of the top left corner of the window to the given
3304 * coordinates.
3306 void
3307 gui_mch_set_winpos(int x, int y)
3309 /* TODO: Should make sure the window is move within range
3310 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3312 MoveWindowStructure(gui.VimWindow, x, y);
3315 void
3316 gui_mch_set_shellsize(
3317 int width,
3318 int height,
3319 int min_width,
3320 int min_height,
3321 int base_width,
3322 int base_height,
3323 int direction)
3325 CGrafPtr VimPort;
3326 Rect VimBound;
3328 if (gui.which_scrollbars[SBAR_LEFT])
3330 VimPort = GetWindowPort(gui.VimWindow);
3331 GetPortBounds(VimPort, &VimBound);
3332 VimBound.left = -gui.scrollbar_width; /* + 1;*/
3333 SetPortBounds(VimPort, &VimBound);
3334 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3336 else
3338 VimPort = GetWindowPort(gui.VimWindow);
3339 GetPortBounds(VimPort, &VimBound);
3340 VimBound.left = 0;
3341 SetPortBounds(VimPort, &VimBound);
3344 SizeWindow(gui.VimWindow, width, height, TRUE);
3346 gui_resize_shell(width, height);
3350 * Get the screen dimensions.
3351 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3352 * Is there no way to find out how wide the borders really are?
3353 * TODO: Add live update of those value on suspend/resume.
3355 void
3356 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3358 GDHandle dominantDevice = GetMainDevice();
3359 Rect screenRect = (**dominantDevice).gdRect;
3361 *screen_w = screenRect.right - 10;
3362 *screen_h = screenRect.bottom - 40;
3367 * Open the Font Panel and wait for the user to select a font and
3368 * close the panel. Then fill the buffer pointed to by font_name with
3369 * the name and size of the selected font and return the font's handle,
3370 * or NOFONT in case of an error.
3372 static GuiFont
3373 gui_mac_select_font(char_u *font_name)
3375 GuiFont selected_font = NOFONT;
3376 OSStatus status;
3377 FontSelectionQDStyle curr_font;
3379 /* Initialize the Font Panel with the current font. */
3380 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3381 curr_font.size = (gui.norm_font >> 16);
3382 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3383 curr_font.instance.fontStyle = 0;
3384 curr_font.hasColor = false;
3385 curr_font.version = 0; /* version number of the style structure */
3386 status = SetFontInfoForSelection(kFontSelectionQDType,
3387 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3389 gFontPanelInfo.family = curr_font.instance.fontFamily;
3390 gFontPanelInfo.style = curr_font.instance.fontStyle;
3391 gFontPanelInfo.size = curr_font.size;
3393 /* Pop up the Font Panel. */
3394 status = FPShowHideFontPanel();
3395 if (status == noErr)
3398 * The Font Panel is modeless. We really need it to be modal,
3399 * so we spin in an event loop until the panel is closed.
3401 gFontPanelInfo.isPanelVisible = true;
3402 while (gFontPanelInfo.isPanelVisible)
3404 EventRecord e;
3405 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3408 GetFontPanelSelection(font_name);
3409 selected_font = gui_mac_find_font(font_name);
3411 return selected_font;
3414 #ifdef USE_ATSUI_DRAWING
3415 static void
3416 gui_mac_create_atsui_style(void)
3418 if (p_macatsui && gFontStyle == NULL)
3420 if (ATSUCreateStyle(&gFontStyle) != noErr)
3421 gFontStyle = NULL;
3423 #ifdef FEAT_MBYTE
3424 if (p_macatsui && gWideFontStyle == NULL)
3426 if (ATSUCreateStyle(&gWideFontStyle) != noErr)
3427 gWideFontStyle = NULL;
3429 #endif
3431 p_macatsui_last = p_macatsui;
3433 #endif
3436 * Initialise vim to use the font with the given name. Return FAIL if the font
3437 * could not be loaded, OK otherwise.
3440 gui_mch_init_font(char_u *font_name, int fontset)
3442 /* TODO: Add support for bold italic underline proportional etc... */
3443 Str255 suggestedFont = "\pMonaco";
3444 int suggestedSize = 10;
3445 FontInfo font_info;
3446 short font_id;
3447 GuiFont font;
3448 char_u used_font_name[512];
3450 #ifdef USE_ATSUI_DRAWING
3451 gui_mac_create_atsui_style();
3452 #endif
3454 if (font_name == NULL)
3456 /* First try to get the suggested font */
3457 GetFNum(suggestedFont, &font_id);
3459 if (font_id == 0)
3461 /* Then pickup the standard application font */
3462 font_id = GetAppFont();
3463 STRCPY(used_font_name, "default");
3465 else
3466 STRCPY(used_font_name, "Monaco");
3467 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3469 else if (STRCMP(font_name, "*") == 0)
3471 char_u *new_p_guifont;
3473 font = gui_mac_select_font(used_font_name);
3474 if (font == NOFONT)
3475 return FAIL;
3477 /* Set guifont to the name of the selected font. */
3478 new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3479 if (new_p_guifont != NULL)
3481 STRCPY(new_p_guifont, used_font_name);
3482 vim_free(p_guifont);
3483 p_guifont = new_p_guifont;
3484 /* Replace spaces in the font name with underscores. */
3485 for ( ; *new_p_guifont; ++new_p_guifont)
3487 if (*new_p_guifont == ' ')
3488 *new_p_guifont = '_';
3492 else
3494 font = gui_mac_find_font(font_name);
3495 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3497 if (font == NOFONT)
3498 return FAIL;
3501 gui.norm_font = font;
3503 hl_set_font_name(used_font_name);
3505 TextSize(font >> 16);
3506 TextFont(font & 0xFFFF);
3508 GetFontInfo(&font_info);
3510 gui.char_ascent = font_info.ascent;
3511 gui.char_width = CharWidth('_');
3512 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3514 #ifdef USE_ATSUI_DRAWING
3515 if (p_macatsui && gFontStyle)
3516 gui_mac_set_font_attributes(font);
3517 #endif
3519 return OK;
3523 * Adjust gui.char_height (after 'linespace' was changed).
3526 gui_mch_adjust_charheight(void)
3528 FontInfo font_info;
3530 GetFontInfo(&font_info);
3531 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3532 gui.char_ascent = font_info.ascent + p_linespace / 2;
3533 return OK;
3537 * Get a font structure for highlighting.
3539 GuiFont
3540 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3542 GuiFont font;
3544 font = gui_mac_find_font(name);
3546 if (font == NOFONT)
3548 if (giveErrorIfMissing)
3549 EMSG2(_(e_font), name);
3550 return NOFONT;
3553 * TODO : Accept only monospace
3556 return font;
3559 #if defined(FEAT_EVAL) || defined(PROTO)
3561 * Return the name of font "font" in allocated memory.
3562 * Don't know how to get the actual name, thus use the provided name.
3564 char_u *
3565 gui_mch_get_fontname(GuiFont font, char_u *name)
3567 if (name == NULL)
3568 return NULL;
3569 return vim_strsave(name);
3571 #endif
3573 #ifdef USE_ATSUI_DRAWING
3574 static void
3575 gui_mac_set_font_attributes(GuiFont font)
3577 ATSUFontID fontID;
3578 Fixed fontSize;
3579 Fixed fontWidth;
3581 fontID = font & 0xFFFF;
3582 fontSize = Long2Fix(font >> 16);
3583 fontWidth = Long2Fix(gui.char_width);
3585 ATSUAttributeTag attribTags[] =
3587 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
3588 kATSUMaxATSUITagValue + 1
3591 ByteCount attribSizes[] =
3593 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
3594 sizeof(font)
3597 ATSUAttributeValuePtr attribValues[] =
3599 &fontID, &fontSize, &fontWidth, &font
3602 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3604 if (ATSUSetAttributes(gFontStyle,
3605 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3606 attribTags, attribSizes, attribValues) != noErr)
3608 # ifndef NDEBUG
3609 fprintf(stderr, "couldn't set font style\n");
3610 # endif
3611 ATSUDisposeStyle(gFontStyle);
3612 gFontStyle = NULL;
3615 #ifdef FEAT_MBYTE
3616 if (has_mbyte)
3618 /* FIXME: we should use a more mbyte sensitive way to support
3619 * wide font drawing */
3620 fontWidth = Long2Fix(gui.char_width * 2);
3622 if (ATSUSetAttributes(gWideFontStyle,
3623 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3624 attribTags, attribSizes, attribValues) != noErr)
3626 ATSUDisposeStyle(gWideFontStyle);
3627 gWideFontStyle = NULL;
3630 #endif
3633 #endif
3636 * Set the current text font.
3638 void
3639 gui_mch_set_font(GuiFont font)
3641 #ifdef USE_ATSUI_DRAWING
3642 GuiFont currFont;
3643 ByteCount actualFontByteCount;
3645 if (p_macatsui && gFontStyle)
3647 /* Avoid setting same font again */
3648 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1,
3649 sizeof(font), &currFont, &actualFontByteCount) == noErr
3650 && actualFontByteCount == (sizeof font))
3652 if (currFont == font)
3653 return;
3656 gui_mac_set_font_attributes(font);
3659 if (p_macatsui && !gIsFontFallbackSet)
3661 /* Setup automatic font substitution. The user's guifontwide
3662 * is tried first, then the system tries other fonts. */
3664 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3665 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3666 ATSUCreateFontFallbacks(&gFontFallbacks);
3667 ATSUSetObjFontFallbacks(gFontFallbacks, );
3669 if (gui.wide_font)
3671 ATSUFontID fallbackFonts;
3672 gIsFontFallbackSet = TRUE;
3674 if (FMGetFontFromFontFamilyInstance(
3675 (gui.wide_font & 0xFFFF),
3677 &fallbackFonts,
3678 NULL) == noErr)
3680 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID),
3681 &fallbackFonts,
3682 kATSUSequentialFallbacksPreferred);
3685 ATSUAttributeValuePtr fallbackValues[] = { };
3689 #endif
3690 TextSize(font >> 16);
3691 TextFont(font & 0xFFFF);
3695 * If a font is not going to be used, free its structure.
3697 void
3698 gui_mch_free_font(font)
3699 GuiFont font;
3702 * Free font when "font" is not 0.
3703 * Nothing to do in the current implementation, since
3704 * nothing is allocated for each font used.
3708 static int
3709 hex_digit(int c)
3711 if (isdigit(c))
3712 return c - '0';
3713 c = TOLOWER_ASC(c);
3714 if (c >= 'a' && c <= 'f')
3715 return c - 'a' + 10;
3716 return -1000;
3720 * Return the Pixel value (color) for the given color name. This routine was
3721 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3722 * Programmer's Guide.
3723 * Return INVALCOLOR when failed.
3725 guicolor_T
3726 gui_mch_get_color(char_u *name)
3728 /* TODO: Add support for the new named color of MacOS 8
3730 RGBColor MacColor;
3731 // guicolor_T color = 0;
3733 typedef struct guicolor_tTable
3735 char *name;
3736 guicolor_T color;
3737 } guicolor_tTable;
3740 * The comment at the end of each line is the source
3741 * (Mac, Window, Unix) and the number is the unix rgb.txt value
3743 static guicolor_tTable table[] =
3745 {"Black", RGB(0x00, 0x00, 0x00)},
3746 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
3747 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
3748 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3749 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3750 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3751 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3752 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3753 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3754 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/
3755 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/
3756 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3757 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3758 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/
3759 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/
3760 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3761 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3762 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/
3763 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/
3764 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3765 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3766 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3767 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3768 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3769 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3770 {"white", RGB(0xFF, 0xFF, 0xFF)},
3771 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
3772 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
3773 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3774 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
3775 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
3776 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3777 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
3778 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
3779 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3780 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3781 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
3782 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3783 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3784 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
3785 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3786 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
3787 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
3788 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3789 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
3790 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3791 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3792 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3793 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3794 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
3797 int r, g, b;
3798 int i;
3800 if (name[0] == '#' && strlen((char *) name) == 7)
3802 /* Name is in "#rrggbb" format */
3803 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3804 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3805 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3806 if (r < 0 || g < 0 || b < 0)
3807 return INVALCOLOR;
3808 return RGB(r, g, b);
3810 else
3812 if (STRICMP(name, "hilite") == 0)
3814 LMGetHiliteRGB(&MacColor);
3815 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3817 /* Check if the name is one of the colors we know */
3818 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3819 if (STRICMP(name, table[i].name) == 0)
3820 return table[i].color;
3824 * Last attempt. Look in the file "$VIM/rgb.txt".
3827 #define LINE_LEN 100
3828 FILE *fd;
3829 char line[LINE_LEN];
3830 char_u *fname;
3832 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3833 if (fname == NULL)
3834 return INVALCOLOR;
3836 fd = fopen((char *)fname, "rt");
3837 vim_free(fname);
3838 if (fd == NULL)
3839 return INVALCOLOR;
3841 while (!feof(fd))
3843 int len;
3844 int pos;
3845 char *color;
3847 fgets(line, LINE_LEN, fd);
3848 len = strlen(line);
3850 if (len <= 1 || line[len-1] != '\n')
3851 continue;
3853 line[len-1] = '\0';
3855 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3856 if (i != 3)
3857 continue;
3859 color = line + pos;
3861 if (STRICMP(color, name) == 0)
3863 fclose(fd);
3864 return (guicolor_T) RGB(r, g, b);
3867 fclose(fd);
3870 return INVALCOLOR;
3874 * Set the current text foreground color.
3876 void
3877 gui_mch_set_fg_color(guicolor_T color)
3879 RGBColor TheColor;
3881 TheColor.red = Red(color) * 0x0101;
3882 TheColor.green = Green(color) * 0x0101;
3883 TheColor.blue = Blue(color) * 0x0101;
3885 RGBForeColor(&TheColor);
3889 * Set the current text background color.
3891 void
3892 gui_mch_set_bg_color(guicolor_T color)
3894 RGBColor TheColor;
3896 TheColor.red = Red(color) * 0x0101;
3897 TheColor.green = Green(color) * 0x0101;
3898 TheColor.blue = Blue(color) * 0x0101;
3900 RGBBackColor(&TheColor);
3903 RGBColor specialColor;
3906 * Set the current text special color.
3908 void
3909 gui_mch_set_sp_color(guicolor_T color)
3911 specialColor.red = Red(color) * 0x0101;
3912 specialColor.green = Green(color) * 0x0101;
3913 specialColor.blue = Blue(color) * 0x0101;
3917 * Draw undercurl at the bottom of the character cell.
3919 static void
3920 draw_undercurl(int flags, int row, int col, int cells)
3922 int x;
3923 int offset;
3924 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3925 int y = FILL_Y(row + 1) - 1;
3927 RGBForeColor(&specialColor);
3929 offset = val[FILL_X(col) % 8];
3930 MoveTo(FILL_X(col), y - offset);
3932 for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3934 offset = val[x % 8];
3935 LineTo(x, y - offset);
3940 static void
3941 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3943 #ifdef FEAT_MBYTE
3944 char_u *tofree = NULL;
3946 if (output_conv.vc_type != CONV_NONE)
3948 tofree = string_convert(&output_conv, s, &len);
3949 if (tofree != NULL)
3950 s = tofree;
3952 #endif
3955 * On OS X, try using Quartz-style text antialiasing.
3957 if (gMacSystemVersion >= 0x1020)
3959 /* Quartz antialiasing is available only in OS 10.2 and later. */
3960 UInt32 qd_flags = (p_antialias ?
3961 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3962 QDSwapTextFlags(qd_flags);
3966 * When antialiasing we're using srcOr mode, we have to clear the block
3967 * before drawing the text.
3968 * Also needed when 'linespace' is non-zero to remove the cursor and
3969 * underlining.
3970 * But not when drawing transparently.
3971 * The following is like calling gui_mch_clear_block(row, col, row, col +
3972 * len - 1), but without setting the bg color to gui.back_pixel.
3974 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3975 && !(flags & DRAW_TRANSP))
3977 Rect rc;
3979 rc.left = FILL_X(col);
3980 rc.top = FILL_Y(row);
3981 #ifdef FEAT_MBYTE
3982 /* Multibyte computation taken from gui_w32.c */
3983 if (has_mbyte)
3985 int cell_len = 0;
3986 int n;
3988 /* Compute the length in display cells. */
3989 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3990 cell_len += (*mb_ptr2cells)(s + n);
3991 rc.right = FILL_X(col + cell_len);
3993 else
3994 #endif
3995 rc.right = FILL_X(col + len) + (col + len == Columns);
3996 rc.bottom = FILL_Y(row + 1);
3997 EraseRect(&rc);
4000 if (gMacSystemVersion >= 0x1020 && p_antialias)
4002 StyleParameter face;
4004 face = normal;
4005 if (flags & DRAW_BOLD)
4006 face |= bold;
4007 if (flags & DRAW_UNDERL)
4008 face |= underline;
4009 TextFace(face);
4011 /* Quartz antialiasing works only in srcOr transfer mode. */
4012 TextMode(srcOr);
4014 MoveTo(TEXT_X(col), TEXT_Y(row));
4015 DrawText((char*)s, 0, len);
4017 else
4019 /* Use old-style, non-antialiased QuickDraw text rendering. */
4020 TextMode(srcCopy);
4021 TextFace(normal);
4023 /* SelectFont(hdc, gui.currFont); */
4025 if (flags & DRAW_TRANSP)
4027 TextMode(srcOr);
4030 MoveTo(TEXT_X(col), TEXT_Y(row));
4031 DrawText((char *)s, 0, len);
4033 if (flags & DRAW_BOLD)
4035 TextMode(srcOr);
4036 MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
4037 DrawText((char *)s, 0, len);
4040 if (flags & DRAW_UNDERL)
4042 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
4043 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
4047 if (flags & DRAW_UNDERC)
4048 draw_undercurl(flags, row, col, len);
4050 #ifdef FEAT_MBYTE
4051 vim_free(tofree);
4052 #endif
4055 #ifdef USE_ATSUI_DRAWING
4057 static void
4058 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
4060 /* ATSUI requires utf-16 strings */
4061 UniCharCount utf16_len;
4062 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
4063 utf16_len /= sizeof(UniChar);
4065 /* - ATSUI automatically antialiases text (Someone)
4066 * - for some reason it does not work... (Jussi) */
4067 #ifdef MAC_ATSUI_DEBUG
4068 fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n",
4069 row, col, len, len == 1 ? s[0] : ' ');
4070 #endif
4072 * When antialiasing we're using srcOr mode, we have to clear the block
4073 * before drawing the text.
4074 * Also needed when 'linespace' is non-zero to remove the cursor and
4075 * underlining.
4076 * But not when drawing transparently.
4077 * The following is like calling gui_mch_clear_block(row, col, row, col +
4078 * len - 1), but without setting the bg color to gui.back_pixel.
4080 if ((flags & DRAW_TRANSP) == 0)
4082 Rect rc;
4084 rc.left = FILL_X(col);
4085 rc.top = FILL_Y(row);
4086 /* Multibyte computation taken from gui_w32.c */
4087 if (has_mbyte)
4089 int cell_len = 0;
4090 int n;
4092 /* Compute the length in display cells. */
4093 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4094 cell_len += (*mb_ptr2cells)(s + n);
4095 rc.right = FILL_X(col + cell_len);
4097 else
4098 rc.right = FILL_X(col + len) + (col + len == Columns);
4100 rc.bottom = FILL_Y(row + 1);
4101 EraseRect(&rc);
4105 TextMode(srcCopy);
4106 TextFace(normal);
4108 /* SelectFont(hdc, gui.currFont); */
4109 if (flags & DRAW_TRANSP)
4111 TextMode(srcOr);
4114 MoveTo(TEXT_X(col), TEXT_Y(row));
4116 if (gFontStyle && flags & DRAW_BOLD)
4118 Boolean attValue = true;
4119 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4120 ByteCount attribSizes[] = { sizeof(Boolean) };
4121 ATSUAttributeValuePtr attribValues[] = { &attValue };
4123 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues);
4126 #ifdef FEAT_MBYTE
4127 if (has_mbyte)
4129 int n, width_in_cell, last_width_in_cell;
4130 UniCharArrayOffset offset = 0;
4131 UniCharCount yet_to_draw = 0;
4132 ATSUTextLayout textLayout;
4133 ATSUStyle textStyle;
4135 last_width_in_cell = 1;
4136 ATSUCreateTextLayout(&textLayout);
4137 ATSUSetTextPointerLocation(textLayout, tofree,
4138 kATSUFromTextBeginning,
4139 kATSUToTextEnd, utf16_len);
4141 ATSUSetRunStyle(textLayout, gFontStyle,
4142 kATSUFromTextBeginning, kATSUToTextEnd); */
4144 /* Compute the length in display cells. */
4145 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4147 width_in_cell = (*mb_ptr2cells)(s + n);
4149 /* probably we are switching from single byte character
4150 * to multibyte characters (which requires more than one
4151 * cell to draw) */
4152 if (width_in_cell != last_width_in_cell)
4154 #ifdef MAC_ATSUI_DEBUG
4155 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4156 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4157 #endif
4158 textStyle = last_width_in_cell > 1 ? gWideFontStyle
4159 : gFontStyle;
4161 ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw);
4162 offset += yet_to_draw;
4163 yet_to_draw = 0;
4164 last_width_in_cell = width_in_cell;
4167 yet_to_draw++;
4170 if (yet_to_draw)
4172 #ifdef MAC_ATSUI_DEBUG
4173 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4174 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4175 #endif
4176 /* finish the rest style */
4177 textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle;
4178 ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd);
4181 ATSUSetTransientFontMatching(textLayout, TRUE);
4182 ATSUDrawText(textLayout,
4183 kATSUFromTextBeginning, kATSUToTextEnd,
4184 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4185 ATSUDisposeTextLayout(textLayout);
4187 else
4188 #endif
4190 ATSUTextLayout textLayout;
4192 if (ATSUCreateTextLayoutWithTextPtr(tofree,
4193 kATSUFromTextBeginning, kATSUToTextEnd,
4194 utf16_len,
4195 (gFontStyle ? 1 : 0), &utf16_len,
4196 (gFontStyle ? &gFontStyle : NULL),
4197 &textLayout) == noErr)
4199 ATSUSetTransientFontMatching(textLayout, TRUE);
4201 ATSUDrawText(textLayout,
4202 kATSUFromTextBeginning, kATSUToTextEnd,
4203 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4205 ATSUDisposeTextLayout(textLayout);
4209 /* drawing is done, now reset bold to normal */
4210 if (gFontStyle && flags & DRAW_BOLD)
4212 Boolean attValue = false;
4214 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4215 ByteCount attribSizes[] = { sizeof(Boolean) };
4216 ATSUAttributeValuePtr attribValues[] = { &attValue };
4218 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes,
4219 attribValues);
4223 if (flags & DRAW_UNDERC)
4224 draw_undercurl(flags, row, col, len);
4226 vim_free(tofree);
4228 #endif
4230 void
4231 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4233 #if defined(USE_ATSUI_DRAWING)
4234 if (p_macatsui == 0 && p_macatsui_last != 0)
4235 /* switch from macatsui to nomacatsui */
4236 gui_mac_dispose_atsui_style();
4237 else if (p_macatsui != 0 && p_macatsui_last == 0)
4238 /* switch from nomacatsui to macatsui */
4239 gui_mac_create_atsui_style();
4241 if (p_macatsui)
4242 draw_string_ATSUI(row, col, s, len, flags);
4243 else
4244 #endif
4245 draw_string_QD(row, col, s, len, flags);
4249 * Return OK if the key with the termcap name "name" is supported.
4252 gui_mch_haskey(char_u *name)
4254 int i;
4256 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4257 if (name[0] == special_keys[i].vim_code0 &&
4258 name[1] == special_keys[i].vim_code1)
4259 return OK;
4260 return FAIL;
4263 void
4264 gui_mch_beep(void)
4266 SysBeep(1); /* Should this be 0? (????) */
4269 void
4270 gui_mch_flash(int msec)
4272 /* Do a visual beep by reversing the foreground and background colors */
4273 Rect rc;
4276 * Note: InvertRect() excludes right and bottom of rectangle.
4278 rc.left = 0;
4279 rc.top = 0;
4280 rc.right = gui.num_cols * gui.char_width;
4281 rc.bottom = gui.num_rows * gui.char_height;
4282 InvertRect(&rc);
4284 ui_delay((long)msec, TRUE); /* wait for some msec */
4286 InvertRect(&rc);
4290 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4292 void
4293 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4295 Rect rc;
4298 * Note: InvertRect() excludes right and bottom of rectangle.
4300 rc.left = FILL_X(c);
4301 rc.top = FILL_Y(r);
4302 rc.right = rc.left + nc * gui.char_width;
4303 rc.bottom = rc.top + nr * gui.char_height;
4304 InvertRect(&rc);
4308 * Iconify the GUI window.
4310 void
4311 gui_mch_iconify(void)
4313 /* TODO: find out what could replace iconify
4314 * -window shade?
4315 * -hide application?
4319 #if defined(FEAT_EVAL) || defined(PROTO)
4321 * Bring the Vim window to the foreground.
4323 void
4324 gui_mch_set_foreground(void)
4326 /* TODO */
4328 #endif
4331 * Draw a cursor without focus.
4333 void
4334 gui_mch_draw_hollow_cursor(guicolor_T color)
4336 Rect rc;
4339 * Note: FrameRect() excludes right and bottom of rectangle.
4341 rc.left = FILL_X(gui.col);
4342 rc.top = FILL_Y(gui.row);
4343 rc.right = rc.left + gui.char_width;
4344 #ifdef FEAT_MBYTE
4345 if (mb_lefthalve(gui.row, gui.col))
4346 rc.right += gui.char_width;
4347 #endif
4348 rc.bottom = rc.top + gui.char_height;
4350 gui_mch_set_fg_color(color);
4352 FrameRect(&rc);
4356 * Draw part of a cursor, only w pixels wide, and h pixels high.
4358 void
4359 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4361 Rect rc;
4363 #ifdef FEAT_RIGHTLEFT
4364 /* vertical line should be on the right of current point */
4365 if (CURSOR_BAR_RIGHT)
4366 rc.left = FILL_X(gui.col + 1) - w;
4367 else
4368 #endif
4369 rc.left = FILL_X(gui.col);
4370 rc.top = FILL_Y(gui.row) + gui.char_height - h;
4371 rc.right = rc.left + w;
4372 rc.bottom = rc.top + h;
4374 gui_mch_set_fg_color(color);
4376 FrameRect(&rc);
4377 // PaintRect(&rc);
4383 * Catch up with any queued X events. This may put keyboard input into the
4384 * input buffer, call resize call-backs, trigger timers etc. If there is
4385 * nothing in the X event queue (& no timers pending), then we return
4386 * immediately.
4388 void
4389 gui_mch_update(void)
4391 /* TODO: find what to do
4392 * maybe call gui_mch_wait_for_chars (0)
4393 * more like look at EventQueue then
4394 * call heart of gui_mch_wait_for_chars;
4396 * if (eventther)
4397 * gui_mac_handle_event(&event);
4399 EventRecord theEvent;
4401 if (EventAvail(everyEvent, &theEvent))
4402 if (theEvent.what != nullEvent)
4403 gui_mch_wait_for_chars(0);
4407 * Simple wrapper to neglect more easily the time
4408 * spent inside WaitNextEvent while profiling.
4411 pascal
4412 Boolean
4413 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4415 if (((long) sleep) < -1)
4416 sleep = 32767;
4417 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4421 * GUI input routine called by gui_wait_for_chars(). Waits for a character
4422 * from the keyboard.
4423 * wtime == -1 Wait forever.
4424 * wtime == 0 This should never happen.
4425 * wtime > 0 Wait wtime milliseconds for a character.
4426 * Returns OK if a character was found to be available within the given time,
4427 * or FAIL otherwise.
4430 gui_mch_wait_for_chars(int wtime)
4432 EventMask mask = (everyEvent);
4433 EventRecord event;
4434 long entryTick;
4435 long currentTick;
4436 long sleeppyTick;
4438 /* If we are providing life feedback with the scrollbar,
4439 * we don't want to try to wait for an event, or else
4440 * there won't be any life feedback.
4442 if (dragged_sb != NULL)
4443 return FAIL;
4444 /* TODO: Check if FAIL is the proper return code */
4446 entryTick = TickCount();
4448 allow_scrollbar = TRUE;
4452 /* if (dragRectControl == kCreateEmpty)
4454 dragRgn = NULL;
4455 dragRectControl = kNothing;
4457 else*/ if (dragRectControl == kCreateRect)
4459 dragRgn = cursorRgn;
4460 RectRgn(dragRgn, &dragRect);
4461 dragRectControl = kNothing;
4464 * Don't use gui_mch_update() because then we will spin-lock until a
4465 * char arrives, instead we use WaitNextEventWrp() to hang until an
4466 * event arrives. No need to check for input_buf_full because we are
4467 * returning as soon as it contains a single char.
4469 /* TODO: reduce wtime accordinly??? */
4470 if (wtime > -1)
4471 sleeppyTick = 60 * wtime / 1000;
4472 else
4473 sleeppyTick = 32767;
4475 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4477 gui_mac_handle_event(&event);
4478 if (input_available())
4480 allow_scrollbar = FALSE;
4481 return OK;
4484 currentTick = TickCount();
4486 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4488 allow_scrollbar = FALSE;
4489 return FAIL;
4493 * Output routines.
4496 /* Flush any output to the screen */
4497 void
4498 gui_mch_flush(void)
4500 /* TODO: Is anything needed here? */
4504 * Clear a rectangular region of the screen from text pos (row1, col1) to
4505 * (row2, col2) inclusive.
4507 void
4508 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4510 Rect rc;
4513 * Clear one extra pixel at the far right, for when bold characters have
4514 * spilled over to the next column.
4516 rc.left = FILL_X(col1);
4517 rc.top = FILL_Y(row1);
4518 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4519 rc.bottom = FILL_Y(row2 + 1);
4521 gui_mch_set_bg_color(gui.back_pixel);
4522 EraseRect(&rc);
4526 * Clear the whole text window.
4528 void
4529 gui_mch_clear_all(void)
4531 Rect rc;
4533 rc.left = 0;
4534 rc.top = 0;
4535 rc.right = Columns * gui.char_width + 2 * gui.border_width;
4536 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4538 gui_mch_set_bg_color(gui.back_pixel);
4539 EraseRect(&rc);
4540 /* gui_mch_set_fg_color(gui.norm_pixel);
4541 FrameRect(&rc);
4546 * Delete the given number of lines from the given row, scrolling up any
4547 * text further down within the scroll region.
4549 void
4550 gui_mch_delete_lines(int row, int num_lines)
4552 Rect rc;
4554 /* changed without checking! */
4555 rc.left = FILL_X(gui.scroll_region_left);
4556 rc.right = FILL_X(gui.scroll_region_right + 1);
4557 rc.top = FILL_Y(row);
4558 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4560 gui_mch_set_bg_color(gui.back_pixel);
4561 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4563 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4564 gui.scroll_region_left,
4565 gui.scroll_region_bot, gui.scroll_region_right);
4569 * Insert the given number of lines before the given row, scrolling down any
4570 * following text within the scroll region.
4572 void
4573 gui_mch_insert_lines(int row, int num_lines)
4575 Rect rc;
4577 rc.left = FILL_X(gui.scroll_region_left);
4578 rc.right = FILL_X(gui.scroll_region_right + 1);
4579 rc.top = FILL_Y(row);
4580 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4582 gui_mch_set_bg_color(gui.back_pixel);
4584 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4586 /* Update gui.cursor_row if the cursor scrolled or copied over */
4587 if (gui.cursor_row >= gui.row
4588 && gui.cursor_col >= gui.scroll_region_left
4589 && gui.cursor_col <= gui.scroll_region_right)
4591 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4592 gui.cursor_row += num_lines;
4593 else if (gui.cursor_row <= gui.scroll_region_bot)
4594 gui.cursor_is_valid = FALSE;
4597 gui_clear_block(row, gui.scroll_region_left,
4598 row + num_lines - 1, gui.scroll_region_right);
4602 * TODO: add a vim format to the clipboard which remember
4603 * LINEWISE, CHARWISE, BLOCKWISE
4606 void
4607 clip_mch_request_selection(VimClipboard *cbd)
4610 Handle textOfClip;
4611 int flavor = 0;
4612 Size scrapSize;
4613 ScrapFlavorFlags scrapFlags;
4614 ScrapRef scrap = nil;
4615 OSStatus error;
4616 int type;
4617 char *searchCR;
4618 char_u *tempclip;
4621 error = GetCurrentScrap(&scrap);
4622 if (error != noErr)
4623 return;
4625 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4626 if (error == noErr)
4628 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4629 if (error == noErr && scrapSize > 1)
4630 flavor = 1;
4633 if (flavor == 0)
4635 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4636 if (error != noErr)
4637 return;
4639 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4640 if (error != noErr)
4641 return;
4644 ReserveMem(scrapSize);
4646 /* In CARBON we don't need a Handle, a pointer is good */
4647 textOfClip = NewHandle(scrapSize);
4649 /* tempclip = lalloc(scrapSize+1, TRUE); */
4650 HLock(textOfClip);
4651 error = GetScrapFlavorData(scrap,
4652 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4653 &scrapSize, *textOfClip);
4654 scrapSize -= flavor;
4656 if (flavor)
4657 type = **textOfClip;
4658 else
4659 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4661 tempclip = lalloc(scrapSize + 1, TRUE);
4662 mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4663 tempclip[scrapSize] = 0;
4665 #ifdef MACOS_CONVERT
4667 /* Convert from utf-16 (clipboard) */
4668 size_t encLen = 0;
4669 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4671 if (to != NULL)
4673 scrapSize = encLen;
4674 vim_free(tempclip);
4675 tempclip = to;
4678 #endif
4680 searchCR = (char *)tempclip;
4681 while (searchCR != NULL)
4683 searchCR = strchr(searchCR, '\r');
4684 if (searchCR != NULL)
4685 *searchCR = '\n';
4688 clip_yank_selection(type, tempclip, scrapSize, cbd);
4690 vim_free(tempclip);
4691 HUnlock(textOfClip);
4693 DisposeHandle(textOfClip);
4696 void
4697 clip_mch_lose_selection(VimClipboard *cbd)
4700 * TODO: Really nothing to do?
4705 clip_mch_own_selection(VimClipboard *cbd)
4707 return OK;
4711 * Send the current selection to the clipboard.
4713 void
4714 clip_mch_set_selection(VimClipboard *cbd)
4716 Handle textOfClip;
4717 long scrapSize;
4718 int type;
4719 ScrapRef scrap;
4721 char_u *str = NULL;
4723 if (!cbd->owned)
4724 return;
4726 clip_get_selection(cbd);
4729 * Once we set the clipboard, lose ownership. If another application sets
4730 * the clipboard, we don't want to think that we still own it.
4732 cbd->owned = FALSE;
4734 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4736 #ifdef MACOS_CONVERT
4737 size_t utf16_len = 0;
4738 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4739 if (to)
4741 scrapSize = utf16_len;
4742 vim_free(str);
4743 str = (char_u *)to;
4745 #endif
4747 if (type >= 0)
4749 ClearCurrentScrap();
4751 textOfClip = NewHandle(scrapSize + 1);
4752 HLock(textOfClip);
4754 **textOfClip = type;
4755 mch_memmove(*textOfClip + 1, str, scrapSize);
4756 GetCurrentScrap(&scrap);
4757 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4758 scrapSize, *textOfClip + 1);
4759 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4760 scrapSize + 1, *textOfClip);
4761 HUnlock(textOfClip);
4762 DisposeHandle(textOfClip);
4765 vim_free(str);
4768 void
4769 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4771 Rect VimBound;
4773 /* HideWindow(gui.VimWindow); */
4774 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4776 if (gui.which_scrollbars[SBAR_LEFT])
4778 VimBound.left = -gui.scrollbar_width + 1;
4780 else
4782 VimBound.left = 0;
4785 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4787 ShowWindow(gui.VimWindow);
4791 * Menu stuff.
4794 void
4795 gui_mch_enable_menu(int flag)
4798 * Menu is always active.
4802 void
4803 gui_mch_set_menu_pos(int x, int y, int w, int h)
4806 * The menu is always at the top of the screen.
4811 * Add a sub menu to the menu bar.
4813 void
4814 gui_mch_add_menu(vimmenu_T *menu, int idx)
4817 * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4818 * TODO: use menu->mnemonic and menu->actext
4819 * TODO: Try to reuse menu id
4820 * Carbon Help suggest to use only id between 1 and 235
4822 static long next_avail_id = 128;
4823 long menu_after_me = 0; /* Default to the end */
4824 #if defined(FEAT_MBYTE)
4825 CFStringRef name;
4826 #else
4827 char_u *name;
4828 #endif
4829 short index;
4830 vimmenu_T *parent = menu->parent;
4831 vimmenu_T *brother = menu->next;
4833 /* Cannot add a menu if ... */
4834 if ((parent != NULL && parent->submenu_id == 0))
4835 return;
4837 /* menu ID greater than 1024 are reserved for ??? */
4838 if (next_avail_id == 1024)
4839 return;
4841 /* My brother could be the PopUp, find my real brother */
4842 while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4843 brother = brother->next;
4845 /* Find where to insert the menu (for MenuBar) */
4846 if ((parent == NULL) && (brother != NULL))
4847 menu_after_me = brother->submenu_id;
4849 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4850 if (!menu_is_menubar(menu->name))
4851 menu_after_me = hierMenu;
4853 /* Convert the name */
4854 #ifdef MACOS_CONVERT
4855 name = menu_title_removing_mnemonic(menu);
4856 #else
4857 name = C2Pascal_save(menu->dname);
4858 #endif
4859 if (name == NULL)
4860 return;
4862 /* Create the menu unless it's the help menu */
4864 /* Carbon suggest use of
4865 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4866 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4868 menu->submenu_id = next_avail_id;
4869 #if defined(FEAT_MBYTE)
4870 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4871 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4872 #else
4873 menu->submenu_handle = NewMenu(menu->submenu_id, name);
4874 #endif
4875 next_avail_id++;
4878 if (parent == NULL)
4880 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4882 /* TODO: Verify if we could only Insert Menu if really part of the
4883 * menubar The Inserted menu are scanned or the Command-key combos
4886 /* Insert the menu */
4887 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4888 #if 1
4889 /* Vim should normally update it. TODO: verify */
4890 DrawMenuBar();
4891 #endif
4893 else
4895 /* Adding as a submenu */
4897 index = gui_mac_get_menu_item_index(menu);
4899 /* Call InsertMenuItem followed by SetMenuItemText
4900 * to avoid special character recognition by InsertMenuItem
4902 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4903 #if defined(FEAT_MBYTE)
4904 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4905 #else
4906 SetMenuItemText(parent->submenu_handle, idx+1, name);
4907 #endif
4908 SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4909 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4910 InsertMenu(menu->submenu_handle, hierMenu);
4913 #if defined(FEAT_MBYTE)
4914 CFRelease(name);
4915 #else
4916 vim_free(name);
4917 #endif
4919 #if 0
4920 /* Done by Vim later on */
4921 DrawMenuBar();
4922 #endif
4926 * Add a menu item to a menu
4928 void
4929 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4931 #if defined(FEAT_MBYTE)
4932 CFStringRef name;
4933 #else
4934 char_u *name;
4935 #endif
4936 vimmenu_T *parent = menu->parent;
4937 int menu_inserted;
4939 /* Cannot add item, if the menu have not been created */
4940 if (parent->submenu_id == 0)
4941 return;
4943 /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4944 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4946 /* Convert the name */
4947 #ifdef MACOS_CONVERT
4948 name = menu_title_removing_mnemonic(menu);
4949 #else
4950 name = C2Pascal_save(menu->dname);
4951 #endif
4953 /* Where are just a menu item, so no handle, no id */
4954 menu->submenu_id = 0;
4955 menu->submenu_handle = NULL;
4957 menu_inserted = 0;
4958 if (menu->actext)
4960 /* If the accelerator text for the menu item looks like it describes
4961 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4962 * item's command equivalent.
4964 int key = 0;
4965 int modifiers = 0;
4966 char_u *p_actext;
4968 p_actext = menu->actext;
4969 key = find_special_key(&p_actext, &modifiers, FALSE, FALSE);
4970 if (*p_actext != 0)
4971 key = 0; /* error: trailing text */
4972 /* find_special_key() returns a keycode with as many of the
4973 * specified modifiers as appropriate already applied (e.g., for
4974 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4975 * as the only modifier). Since we want to display all of the
4976 * modifiers, we need to convert the keycode back to a printable
4977 * character plus modifiers.
4978 * TODO: Write an alternative find_special_key() that doesn't
4979 * apply modifiers.
4981 if (key > 0 && key < 32)
4983 /* Convert a control key to an uppercase letter. Note that
4984 * by this point it is no longer possible to distinguish
4985 * between, e.g., Ctrl-S and Ctrl-Shift-S.
4987 modifiers |= MOD_MASK_CTRL;
4988 key += '@';
4990 /* If the keycode is an uppercase letter, set the Shift modifier.
4991 * If it is a lowercase letter, don't set the modifier, but convert
4992 * the letter to uppercase for display in the menu.
4994 else if (key >= 'A' && key <= 'Z')
4995 modifiers |= MOD_MASK_SHIFT;
4996 else if (key >= 'a' && key <= 'z')
4997 key += 'A' - 'a';
4998 /* Note: keycodes below 0x22 are reserved by Apple. */
4999 if (key >= 0x22 && vim_isprintc_strict(key))
5001 int valid = 1;
5002 char_u mac_mods = kMenuNoModifiers;
5003 /* Convert Vim modifier codes to Menu Manager equivalents. */
5004 if (modifiers & MOD_MASK_SHIFT)
5005 mac_mods |= kMenuShiftModifier;
5006 if (modifiers & MOD_MASK_CTRL)
5007 mac_mods |= kMenuControlModifier;
5008 if (!(modifiers & MOD_MASK_CMD))
5009 mac_mods |= kMenuNoCommandModifier;
5010 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
5011 valid = 0; /* TODO: will Alt someday map to Option? */
5012 if (valid)
5014 char_u item_txt[10];
5015 /* Insert the menu item after idx, with its command key. */
5016 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
5017 item_txt[3] = key;
5018 InsertMenuItem(parent->submenu_handle, item_txt, idx);
5019 /* Set the modifier keys. */
5020 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
5021 menu_inserted = 1;
5025 /* Call InsertMenuItem followed by SetMenuItemText
5026 * to avoid special character recognition by InsertMenuItem
5028 if (!menu_inserted)
5029 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
5030 /* Set the menu item name. */
5031 #if defined(FEAT_MBYTE)
5032 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
5033 #else
5034 SetMenuItemText(parent->submenu_handle, idx+1, name);
5035 #endif
5037 #if 0
5038 /* Called by Vim */
5039 DrawMenuBar();
5040 #endif
5042 #if defined(FEAT_MBYTE)
5043 CFRelease(name);
5044 #else
5045 /* TODO: Can name be freed? */
5046 vim_free(name);
5047 #endif
5050 void
5051 gui_mch_toggle_tearoffs(int enable)
5053 /* no tearoff menus */
5057 * Destroy the machine specific menu widget.
5059 void
5060 gui_mch_destroy_menu(vimmenu_T *menu)
5062 short index = gui_mac_get_menu_item_index(menu);
5064 if (index > 0)
5066 if (menu->parent)
5069 /* For now just don't delete help menu items. (Huh? Dany) */
5070 DeleteMenuItem(menu->parent->submenu_handle, index);
5072 /* Delete the Menu if it was a hierarchical Menu */
5073 if (menu->submenu_id != 0)
5075 DeleteMenu(menu->submenu_id);
5076 DisposeMenu(menu->submenu_handle);
5080 #ifdef DEBUG_MAC_MENU
5081 else
5083 printf("gmdm 2\n");
5085 #endif
5087 else
5090 DeleteMenu(menu->submenu_id);
5091 DisposeMenu(menu->submenu_handle);
5094 /* Shouldn't this be already done by Vim. TODO: Check */
5095 DrawMenuBar();
5099 * Make a menu either grey or not grey.
5101 void
5102 gui_mch_menu_grey(vimmenu_T *menu, int grey)
5104 /* TODO: Check if menu really exists */
5105 short index = gui_mac_get_menu_item_index(menu);
5107 index = menu->index;
5109 if (grey)
5111 if (menu->children)
5112 DisableMenuItem(menu->submenu_handle, index);
5113 if (menu->parent)
5114 if (menu->parent->submenu_handle)
5115 DisableMenuItem(menu->parent->submenu_handle, index);
5117 else
5119 if (menu->children)
5120 EnableMenuItem(menu->submenu_handle, index);
5121 if (menu->parent)
5122 if (menu->parent->submenu_handle)
5123 EnableMenuItem(menu->parent->submenu_handle, index);
5128 * Make menu item hidden or not hidden
5130 void
5131 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5133 /* There's no hidden mode on MacOS */
5134 gui_mch_menu_grey(menu, hidden);
5139 * This is called after setting all the menus to grey/hidden or not.
5141 void
5142 gui_mch_draw_menubar(void)
5144 DrawMenuBar();
5149 * Scrollbar stuff.
5152 void
5153 gui_mch_enable_scrollbar(
5154 scrollbar_T *sb,
5155 int flag)
5157 if (flag)
5158 ShowControl(sb->id);
5159 else
5160 HideControl(sb->id);
5162 #ifdef DEBUG_MAC_SB
5163 printf("enb_sb (%x) %x\n",sb->id, flag);
5164 #endif
5167 void
5168 gui_mch_set_scrollbar_thumb(
5169 scrollbar_T *sb,
5170 long val,
5171 long size,
5172 long max)
5174 SetControl32BitMaximum (sb->id, max);
5175 SetControl32BitMinimum (sb->id, 0);
5176 SetControl32BitValue (sb->id, val);
5177 SetControlViewSize (sb->id, size);
5178 #ifdef DEBUG_MAC_SB
5179 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
5180 #endif
5183 void
5184 gui_mch_set_scrollbar_pos(
5185 scrollbar_T *sb,
5186 int x,
5187 int y,
5188 int w,
5189 int h)
5191 gui_mch_set_bg_color(gui.back_pixel);
5192 /* if (gui.which_scrollbars[SBAR_LEFT])
5194 MoveControl(sb->id, x-16, y);
5195 SizeControl(sb->id, w + 1, h);
5197 else
5199 MoveControl(sb->id, x, y);
5200 SizeControl(sb->id, w + 1, h);
5202 if (sb == &gui.bottom_sbar)
5203 h += 1;
5204 else
5205 w += 1;
5207 if (gui.which_scrollbars[SBAR_LEFT])
5208 x -= 15;
5210 MoveControl(sb->id, x, y);
5211 SizeControl(sb->id, w, h);
5212 #ifdef DEBUG_MAC_SB
5213 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
5214 #endif
5217 void
5218 gui_mch_create_scrollbar(
5219 scrollbar_T *sb,
5220 int orient) /* SBAR_VERT or SBAR_HORIZ */
5222 Rect bounds;
5224 bounds.top = -16;
5225 bounds.bottom = -10;
5226 bounds.right = -10;
5227 bounds.left = -16;
5229 sb->id = NewControl(gui.VimWindow,
5230 &bounds,
5231 "\pScrollBar",
5232 TRUE,
5233 0, /* current*/
5234 0, /* top */
5235 0, /* bottom */
5236 kControlScrollBarLiveProc,
5237 (long) sb->ident);
5238 #ifdef DEBUG_MAC_SB
5239 printf("create_sb (%x) %x\n",sb->id, orient);
5240 #endif
5243 void
5244 gui_mch_destroy_scrollbar(scrollbar_T *sb)
5246 gui_mch_set_bg_color(gui.back_pixel);
5247 DisposeControl(sb->id);
5248 #ifdef DEBUG_MAC_SB
5249 printf("dest_sb (%x) \n",sb->id);
5250 #endif
5255 * Cursor blink functions.
5257 * This is a simple state machine:
5258 * BLINK_NONE not blinking at all
5259 * BLINK_OFF blinking, cursor is not shown
5260 * BLINK_ON blinking, cursor is shown
5262 void
5263 gui_mch_set_blinking(long wait, long on, long off)
5265 /* TODO: TODO: TODO: TODO: */
5266 /* blink_waittime = wait;
5267 blink_ontime = on;
5268 blink_offtime = off;*/
5272 * Stop the cursor blinking. Show the cursor if it wasn't shown.
5274 void
5275 gui_mch_stop_blink(void)
5277 gui_update_cursor(TRUE, FALSE);
5278 /* TODO: TODO: TODO: TODO: */
5279 /* gui_w32_rm_blink_timer();
5280 if (blink_state == BLINK_OFF)
5281 gui_update_cursor(TRUE, FALSE);
5282 blink_state = BLINK_NONE;*/
5286 * Start the cursor blinking. If it was already blinking, this restarts the
5287 * waiting time and shows the cursor.
5289 void
5290 gui_mch_start_blink(void)
5292 gui_update_cursor(TRUE, FALSE);
5293 /* TODO: TODO: TODO: TODO: */
5294 /* gui_w32_rm_blink_timer(); */
5296 /* Only switch blinking on if none of the times is zero */
5297 /* if (blink_waittime && blink_ontime && blink_offtime)
5299 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5300 (TIMERPROC)_OnBlinkTimer);
5301 blink_state = BLINK_ON;
5302 gui_update_cursor(TRUE, FALSE);
5307 * Return the RGB value of a pixel as long.
5309 long_u
5310 gui_mch_get_rgb(guicolor_T pixel)
5312 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5317 #ifdef FEAT_BROWSE
5319 * Pop open a file browser and return the file selected, in allocated memory,
5320 * or NULL if Cancel is hit.
5321 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
5322 * title - Title message for the file browser dialog.
5323 * dflt - Default name of file.
5324 * ext - Default extension to be added to files without extensions.
5325 * initdir - directory in which to open the browser (NULL = current dir)
5326 * filter - Filter for matched files to choose from.
5327 * Has a format like this:
5328 * "C Files (*.c)\0*.c\0"
5329 * "All Files\0*.*\0\0"
5330 * If these two strings were concatenated, then a choice of two file
5331 * filters will be selectable to the user. Then only matching files will
5332 * be shown in the browser. If NULL, the default allows all files.
5334 * *NOTE* - the filter string must be terminated with TWO nulls.
5336 char_u *
5337 gui_mch_browse(
5338 int saving,
5339 char_u *title,
5340 char_u *dflt,
5341 char_u *ext,
5342 char_u *initdir,
5343 char_u *filter)
5345 /* TODO: Add Ammon's safety checl (Dany) */
5346 NavReplyRecord reply;
5347 char_u *fname = NULL;
5348 char_u **fnames = NULL;
5349 long numFiles;
5350 NavDialogOptions navOptions;
5351 OSErr error;
5353 /* Get Navigation Service Defaults value */
5354 NavGetDefaultDialogOptions(&navOptions);
5357 /* TODO: If we get a :browse args, set the Multiple bit. */
5358 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles
5359 | kNavDontAutoTranslate
5360 | kNavDontAddTranslateItems
5361 /* | kNavAllowMultipleFiles */
5362 | kNavAllowStationery;
5364 (void) C2PascalString(title, &navOptions.message);
5365 (void) C2PascalString(dflt, &navOptions.savedFileName);
5366 /* Could set clientName?
5367 * windowTitle? (there's no title bar?)
5370 if (saving)
5372 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5373 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5374 if (!reply.validRecord)
5375 return NULL;
5377 else
5379 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5380 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5381 if (!reply.validRecord)
5382 return NULL;
5385 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5387 NavDisposeReply(&reply);
5389 if (fnames)
5391 fname = fnames[0];
5392 vim_free(fnames);
5395 /* TODO: Shorten the file name if possible */
5396 return fname;
5398 #endif /* FEAT_BROWSE */
5400 #ifdef FEAT_GUI_DIALOG
5402 * Stuff for dialogues
5406 * Create a dialogue dynamically from the parameter strings.
5407 * type = type of dialogue (question, alert, etc.)
5408 * title = dialogue title. may be NULL for default title.
5409 * message = text to display. Dialogue sizes to accommodate it.
5410 * buttons = '\n' separated list of button captions, default first.
5411 * dfltbutton = number of default button.
5413 * This routine returns 1 if the first button is pressed,
5414 * 2 for the second, etc.
5416 * 0 indicates Esc was pressed.
5417 * -1 for unexpected error
5419 * If stubbing out this fn, return 1.
5422 typedef struct
5424 short idx;
5425 short width; /* Size of the text in pixel */
5426 Rect box;
5427 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5429 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5431 static void
5432 macMoveDialogItem(
5433 DialogRef theDialog,
5434 short itemNumber,
5435 short X,
5436 short Y,
5437 Rect *inBox)
5439 #if 0 /* USE_CARBONIZED */
5440 /* Untested */
5441 MoveDialogItem(theDialog, itemNumber, X, Y);
5442 if (inBox != nil)
5443 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5444 #else
5445 short itemType;
5446 Handle itemHandle;
5447 Rect localBox;
5448 Rect *itemBox = &localBox;
5450 if (inBox != nil)
5451 itemBox = inBox;
5453 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5454 OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5455 OffsetRect(itemBox, X, Y);
5456 /* To move a control (like a button) we need to call both
5457 * MoveControl and SetDialogItem. FAQ 6-18 */
5458 if (1) /*(itemType & kControlDialogItem) */
5459 MoveControl((ControlRef) itemHandle, X, Y);
5460 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5461 #endif
5464 static void
5465 macSizeDialogItem(
5466 DialogRef theDialog,
5467 short itemNumber,
5468 short width,
5469 short height)
5471 short itemType;
5472 Handle itemHandle;
5473 Rect itemBox;
5475 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5477 /* When width or height is zero do not change it */
5478 if (width == 0)
5479 width = itemBox.right - itemBox.left;
5480 if (height == 0)
5481 height = itemBox.bottom - itemBox.top;
5483 #if 0 /* USE_CARBONIZED */
5484 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5485 #else
5486 /* Resize the bounding box */
5487 itemBox.right = itemBox.left + width;
5488 itemBox.bottom = itemBox.top + height;
5490 /* To resize a control (like a button) we need to call both
5491 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5492 if (itemType & kControlDialogItem)
5493 SizeControl((ControlRef) itemHandle, width, height);
5495 /* Configure back the item */
5496 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5497 #endif
5500 static void
5501 macSetDialogItemText(
5502 DialogRef theDialog,
5503 short itemNumber,
5504 Str255 itemName)
5506 short itemType;
5507 Handle itemHandle;
5508 Rect itemBox;
5510 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5512 if (itemType & kControlDialogItem)
5513 SetControlTitle((ControlRef) itemHandle, itemName);
5514 else
5515 SetDialogItemText(itemHandle, itemName);
5519 /* ModalDialog() handler for message dialogs that have hotkey accelerators.
5520 * Expects a mapping of hotkey char to control index in gDialogHotKeys;
5521 * setting gDialogHotKeys to NULL disables any hotkey handling.
5523 static pascal Boolean
5524 DialogHotkeyFilterProc (
5525 DialogRef theDialog,
5526 EventRecord *event,
5527 DialogItemIndex *itemHit)
5529 char_u keyHit;
5531 if (event->what == keyDown || event->what == autoKey)
5533 keyHit = (event->message & charCodeMask);
5535 if (gDialogHotKeys && gDialogHotKeys[keyHit])
5537 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5538 printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]);
5539 #endif
5540 *itemHit = gDialogHotKeys[keyHit];
5542 /* When handing off to StdFilterProc, pretend that the user
5543 * clicked the control manually. Note that this is also supposed
5544 * to cause the button to hilite briefly (to give some user
5545 * feedback), but this seems not to actually work (or it's too
5546 * fast to be seen).
5548 event->what = kEventControlSimulateHit;
5550 return true; /* we took care of it */
5553 /* Defer to the OS's standard behavior for this event.
5554 * This ensures that Enter will still activate the default button. */
5555 return StdFilterProc(theDialog, event, itemHit);
5557 return false; /* Let ModalDialog deal with it */
5561 /* TODO: There have been some crashes with dialogs, check your inbox
5562 * (Jussi)
5565 gui_mch_dialog(
5566 int type,
5567 char_u *title,
5568 char_u *message,
5569 char_u *buttons,
5570 int dfltbutton,
5571 char_u *textfield)
5573 Handle buttonDITL;
5574 Handle iconDITL;
5575 Handle inputDITL;
5576 Handle messageDITL;
5577 Handle itemHandle;
5578 Handle iconHandle;
5579 DialogPtr theDialog;
5580 char_u len;
5581 char_u PascalTitle[256]; /* place holder for the title */
5582 char_u name[256];
5583 GrafPtr oldPort;
5584 short itemHit;
5585 char_u *buttonChar;
5586 short hotKeys[256]; /* map of hotkey -> control ID */
5587 char_u aHotKey;
5588 Rect box;
5589 short button;
5590 short lastButton;
5591 short itemType;
5592 short useIcon;
5593 short width;
5594 short totalButtonWidth = 0; /* the width of all buttons together
5595 including spacing */
5596 short widestButton = 0;
5597 short dfltButtonEdge = 20; /* gut feeling */
5598 short dfltElementSpacing = 13; /* from IM:V.2-29 */
5599 short dfltIconSideSpace = 23; /* from IM:V.2-29 */
5600 short maximumWidth = 400; /* gut feeling */
5601 short maxButtonWidth = 175; /* gut feeling */
5603 short vertical;
5604 short dialogHeight;
5605 short messageLines = 3;
5606 FontInfo textFontInfo;
5608 vgmDlgItm iconItm;
5609 vgmDlgItm messageItm;
5610 vgmDlgItm inputItm;
5611 vgmDlgItm buttonItm;
5613 WindowRef theWindow;
5615 ModalFilterUPP dialogUPP;
5617 /* Check 'v' flag in 'guioptions': vertical button placement. */
5618 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5620 /* Create a new Dialog Box from template. */
5621 theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5623 /* Get the WindowRef */
5624 theWindow = GetDialogWindow(theDialog);
5626 /* Hide the window.
5627 * 1. to avoid seeing slow drawing
5628 * 2. to prevent a problem seen while moving dialog item
5629 * within a visible window. (non-Carbon MacOS 9)
5630 * Could be avoided by changing the resource.
5632 HideWindow(theWindow);
5634 /* Change the graphical port to the dialog,
5635 * so we can measure the text with the proper font */
5636 GetPort(&oldPort);
5637 SetPortDialogPort(theDialog);
5639 /* Get the info about the default text,
5640 * used to calculate the height of the message
5641 * and of the text field */
5642 GetFontInfo(&textFontInfo);
5644 /* Set the dialog title */
5645 if (title != NULL)
5647 (void) C2PascalString(title, &PascalTitle);
5648 SetWTitle(theWindow, PascalTitle);
5651 /* Creates the buttons and add them to the Dialog Box. */
5652 buttonDITL = GetResource('DITL', 130);
5653 buttonChar = buttons;
5654 button = 0;
5656 /* initialize the hotkey mapping */
5657 memset(hotKeys, 0, sizeof(hotKeys));
5659 for (;*buttonChar != 0;)
5661 /* Get the name of the button */
5662 button++;
5663 len = 0;
5664 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5666 if (*buttonChar != DLG_HOTKEY_CHAR)
5667 name[++len] = *buttonChar;
5668 else
5670 aHotKey = (char_u)*(buttonChar+1);
5671 if (aHotKey >= 'A' && aHotKey <= 'Z')
5672 aHotKey = (char_u)((int)aHotKey + (int)'a' - (int)'A');
5673 hotKeys[aHotKey] = button;
5674 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5675 printf("### hotKey for button %d is '%c'\n", button, aHotKey);
5676 #endif
5680 if (*buttonChar != 0)
5681 buttonChar++;
5682 name[0] = len;
5684 /* Add the button */
5685 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5687 /* Change the button's name */
5688 macSetDialogItemText(theDialog, button, name);
5690 /* Resize the button to fit its name */
5691 width = StringWidth(name) + 2 * dfltButtonEdge;
5692 /* Limite the size of any button to an acceptable value. */
5693 /* TODO: Should be based on the message width */
5694 if (width > maxButtonWidth)
5695 width = maxButtonWidth;
5696 macSizeDialogItem(theDialog, button, width, 0);
5698 totalButtonWidth += width;
5700 if (width > widestButton)
5701 widestButton = width;
5703 ReleaseResource(buttonDITL);
5704 lastButton = button;
5706 /* Add the icon to the Dialog Box. */
5707 iconItm.idx = lastButton + 1;
5708 iconDITL = GetResource('DITL', 131);
5709 switch (type)
5711 case VIM_GENERIC: useIcon = kNoteIcon;
5712 case VIM_ERROR: useIcon = kStopIcon;
5713 case VIM_WARNING: useIcon = kCautionIcon;
5714 case VIM_INFO: useIcon = kNoteIcon;
5715 case VIM_QUESTION: useIcon = kNoteIcon;
5716 default: useIcon = kStopIcon;
5718 AppendDITL(theDialog, iconDITL, overlayDITL);
5719 ReleaseResource(iconDITL);
5720 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5721 /* TODO: Should the item be freed? */
5722 iconHandle = GetIcon(useIcon);
5723 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5725 /* Add the message to the Dialog box. */
5726 messageItm.idx = lastButton + 2;
5727 messageDITL = GetResource('DITL', 132);
5728 AppendDITL(theDialog, messageDITL, overlayDITL);
5729 ReleaseResource(messageDITL);
5730 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5731 (void) C2PascalString(message, &name);
5732 SetDialogItemText(itemHandle, name);
5733 messageItm.width = StringWidth(name);
5735 /* Add the input box if needed */
5736 if (textfield != NULL)
5738 /* Cheat for now reuse the message and convert to text edit */
5739 inputItm.idx = lastButton + 3;
5740 inputDITL = GetResource('DITL', 132);
5741 AppendDITL(theDialog, inputDITL, overlayDITL);
5742 ReleaseResource(inputDITL);
5743 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5744 /* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5745 (void) C2PascalString(textfield, &name);
5746 SetDialogItemText(itemHandle, name);
5747 inputItm.width = StringWidth(name);
5749 /* Hotkeys don't make sense if there's a text field */
5750 gDialogHotKeys = NULL;
5752 else
5753 /* Install hotkey table */
5754 gDialogHotKeys = (short *)&hotKeys;
5756 /* Set the <ENTER> and <ESC> button. */
5757 SetDialogDefaultItem(theDialog, dfltbutton);
5758 SetDialogCancelItem(theDialog, 0);
5760 /* Reposition element */
5762 /* Check if we need to force vertical */
5763 if (totalButtonWidth > maximumWidth)
5764 vertical = TRUE;
5766 /* Place icon */
5767 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5768 iconItm.box.right = box.right;
5769 iconItm.box.bottom = box.bottom;
5771 /* Place Message */
5772 messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5773 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent));
5774 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5776 /* Place Input */
5777 if (textfield != NULL)
5779 inputItm.box.left = messageItm.box.left;
5780 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5781 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5782 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5783 /* Convert the static text into a text edit.
5784 * For some reason this change need to be done last (Dany) */
5785 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5786 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5787 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5790 /* Place Button */
5791 if (textfield != NULL)
5793 buttonItm.box.left = inputItm.box.left;
5794 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing;
5796 else
5798 buttonItm.box.left = messageItm.box.left;
5799 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5802 for (button=1; button <= lastButton; button++)
5805 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5806 /* With vertical, it's better to have all buttons the same length */
5807 if (vertical)
5809 macSizeDialogItem(theDialog, button, widestButton, 0);
5810 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5812 /* Calculate position of next button */
5813 if (vertical)
5814 buttonItm.box.top = box.bottom + dfltElementSpacing;
5815 else
5816 buttonItm.box.left = box.right + dfltElementSpacing;
5819 /* Resize the dialog box */
5820 dialogHeight = box.bottom + dfltElementSpacing;
5821 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5823 /* Magic resize */
5824 AutoSizeDialog(theDialog);
5825 /* Need a horizontal resize anyway so not that useful */
5827 /* Display it */
5828 ShowWindow(theWindow);
5829 /* BringToFront(theWindow); */
5830 SelectWindow(theWindow);
5832 /* DrawDialog(theDialog); */
5833 #if 0
5834 GetPort(&oldPort);
5835 SetPortDialogPort(theDialog);
5836 #endif
5838 #ifdef USE_CARBONKEYHANDLER
5839 /* Avoid that we use key events for the main window. */
5840 dialog_busy = TRUE;
5841 #endif
5843 /* Prepare the shortcut-handling filterProc for handing to the dialog */
5844 dialogUPP = NewModalFilterUPP(DialogHotkeyFilterProc);
5846 /* Hang until one of the button is hit */
5849 ModalDialog(dialogUPP, &itemHit);
5850 } while ((itemHit < 1) || (itemHit > lastButton));
5852 #ifdef USE_CARBONKEYHANDLER
5853 dialog_busy = FALSE;
5854 #endif
5856 /* Copy back the text entered by the user into the param */
5857 if (textfield != NULL)
5859 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5860 GetDialogItemText(itemHandle, (char_u *) &name);
5861 #if IOSIZE < 256
5862 /* Truncate the name to IOSIZE if needed */
5863 if (name[0] > IOSIZE)
5864 name[0] = IOSIZE - 1;
5865 #endif
5866 vim_strncpy(textfield, &name[1], name[0]);
5869 /* Restore the original graphical port */
5870 SetPort(oldPort);
5872 /* Free the modal filterProc */
5873 DisposeRoutineDescriptor(dialogUPP);
5875 /* Get ride of th edialog (free memory) */
5876 DisposeDialog(theDialog);
5878 return itemHit;
5880 * Usefull thing which could be used
5881 * SetDialogTimeout(): Auto click a button after timeout
5882 * SetDialogTracksCursor() : Get the I-beam cursor over input box
5883 * MoveDialogItem(): Probably better than SetDialogItem
5884 * SizeDialogItem(): (but is it Carbon Only?)
5885 * AutoSizeDialog(): Magic resize of dialog based on text length
5888 #endif /* FEAT_DIALOG_GUI */
5891 * Display the saved error message(s).
5893 #ifdef USE_MCH_ERRMSG
5894 void
5895 display_errors(void)
5897 char *p;
5898 char_u pError[256];
5900 if (error_ga.ga_data == NULL)
5901 return;
5903 /* avoid putting up a message box with blanks only */
5904 for (p = (char *)error_ga.ga_data; *p; ++p)
5905 if (!isspace(*p))
5907 if (STRLEN(p) > 255)
5908 pError[0] = 255;
5909 else
5910 pError[0] = STRLEN(p);
5912 STRNCPY(&pError[1], p, pError[0]);
5913 ParamText(pError, nil, nil, nil);
5914 Alert(128, nil);
5915 break;
5916 /* TODO: handled message longer than 256 chars
5917 * use auto-sizeable alert
5918 * or dialog with scrollbars (TextEdit zone)
5921 ga_clear(&error_ga);
5923 #endif
5926 * Get current mouse coordinates in text window.
5928 void
5929 gui_mch_getmouse(int *x, int *y)
5931 Point where;
5933 GetMouse(&where);
5935 *x = where.h;
5936 *y = where.v;
5939 void
5940 gui_mch_setmouse(int x, int y)
5942 /* TODO */
5943 #if 0
5944 /* From FAQ 3-11 */
5946 CursorDevicePtr myMouse;
5947 Point where;
5949 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5950 != NGetTrapAddress(_Unimplemented, ToolTrap))
5952 /* New way */
5955 * Get first devoice with one button.
5956 * This will probably be the standad mouse
5957 * startat head of cursor dev list
5961 myMouse = nil;
5965 /* Get the next cursor device */
5966 CursorDeviceNextDevice(&myMouse);
5968 while ((myMouse != nil) && (myMouse->cntButtons != 1));
5970 CursorDeviceMoveTo(myMouse, x, y);
5972 else
5974 /* Old way */
5975 where.h = x;
5976 where.v = y;
5978 *(Point *)RawMouse = where;
5979 *(Point *)MTemp = where;
5980 *(Ptr) CrsrNew = 0xFFFF;
5982 #endif
5985 void
5986 gui_mch_show_popupmenu(vimmenu_T *menu)
5989 * Clone PopUp to use menu
5990 * Create a object descriptor for the current selection
5991 * Call the procedure
5994 MenuHandle CntxMenu;
5995 Point where;
5996 OSStatus status;
5997 UInt32 CntxType;
5998 SInt16 CntxMenuID;
5999 UInt16 CntxMenuItem;
6000 Str255 HelpName = "";
6001 GrafPtr savePort;
6003 /* Save Current Port: On MacOS X we seem to lose the port */
6004 GetPort(&savePort); /*OSX*/
6006 GetMouse(&where);
6007 LocalToGlobal(&where); /*OSX*/
6008 CntxMenu = menu->submenu_handle;
6010 /* TODO: Get the text selection from Vim */
6012 /* Call to Handle Popup */
6013 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
6014 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
6016 if (status == noErr)
6018 if (CntxType == kCMMenuItemSelected)
6020 /* Handle the menu CntxMenuID, CntxMenuItem */
6021 /* The submenu can be handle directly by gui_mac_handle_menu */
6022 /* But what about the current menu, is the menu changed by
6023 * ContextualMenuSelect */
6024 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
6026 else if (CntxMenuID == kCMShowHelpSelected)
6028 /* Should come up with the help */
6032 /* Restore original Port */
6033 SetPort(savePort); /*OSX*/
6036 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
6037 /* TODO: Is it need for MACOS_X? (Dany) */
6038 void
6039 mch_post_buffer_write(buf_T *buf)
6041 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
6042 Send_KAHL_MOD_AE(buf);
6044 #endif
6046 #ifdef FEAT_TITLE
6048 * Set the window title and icon.
6049 * (The icon is not taken care of).
6051 void
6052 gui_mch_settitle(char_u *title, char_u *icon)
6054 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
6055 * that 256. Even better get it to fit nicely in the titlebar.
6057 #ifdef MACOS_CONVERT
6058 CFStringRef windowTitle;
6059 size_t windowTitleLen;
6060 #else
6061 char_u *pascalTitle;
6062 #endif
6064 if (title == NULL) /* nothing to do */
6065 return;
6067 #ifdef MACOS_CONVERT
6068 windowTitleLen = STRLEN(title);
6069 windowTitle = (CFStringRef)mac_enc_to_cfstring(title, windowTitleLen);
6071 if (windowTitle)
6073 SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
6074 CFRelease(windowTitle);
6076 #else
6077 pascalTitle = C2Pascal_save(title);
6078 if (pascalTitle != NULL)
6080 SetWTitle(gui.VimWindow, pascalTitle);
6081 vim_free(pascalTitle);
6083 #endif
6085 #endif
6088 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
6092 C2PascalString(char_u *CString, Str255 *PascalString)
6094 char_u *PascalPtr = (char_u *) PascalString;
6095 int len;
6096 int i;
6098 PascalPtr[0] = 0;
6099 if (CString == NULL)
6100 return 0;
6102 len = STRLEN(CString);
6103 if (len > 255)
6104 len = 255;
6106 for (i = 0; i < len; i++)
6107 PascalPtr[i+1] = CString[i];
6109 PascalPtr[0] = len;
6111 return 0;
6115 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
6117 /* From FAQ 8-12 */
6118 Str255 filePascal;
6119 CInfoPBRec myCPB;
6120 OSErr err;
6122 (void) C2PascalString(file, &filePascal);
6124 myCPB.dirInfo.ioNamePtr = filePascal;
6125 myCPB.dirInfo.ioVRefNum = 0;
6126 myCPB.dirInfo.ioFDirIndex = 0;
6127 myCPB.dirInfo.ioDrDirID = 0;
6129 err= PBGetCatInfo(&myCPB, false);
6131 /* vRefNum, dirID, name */
6132 FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
6134 /* TODO: Use an error code mechanism */
6135 return 0;
6139 * Convert a FSSpec to a fuill path
6142 char_u *FullPathFromFSSpec_save(FSSpec file)
6145 * TODO: Add protection for 256 char max.
6148 CInfoPBRec theCPB;
6149 char_u fname[256];
6150 char_u *filenamePtr = fname;
6151 OSErr error;
6152 int folder = 1;
6153 #ifdef USE_UNIXFILENAME
6154 SInt16 dfltVol_vRefNum;
6155 SInt32 dfltVol_dirID;
6156 FSRef refFile;
6157 OSStatus status;
6158 UInt32 pathSize = 256;
6159 char_u pathname[256];
6160 char_u *path = pathname;
6161 #else
6162 Str255 directoryName;
6163 char_u temporary[255];
6164 char_u *temporaryPtr = temporary;
6165 #endif
6167 #ifdef USE_UNIXFILENAME
6168 /* Get the default volume */
6169 /* TODO: Remove as this only work if Vim is on the Boot Volume*/
6170 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
6172 if (error)
6173 return NULL;
6174 #endif
6176 /* Start filling fname with file.name */
6177 vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
6179 /* Get the info about the file specified in FSSpec */
6180 theCPB.dirInfo.ioFDirIndex = 0;
6181 theCPB.dirInfo.ioNamePtr = file.name;
6182 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6183 /*theCPB.hFileInfo.ioDirID = 0;*/
6184 theCPB.dirInfo.ioDrDirID = file.parID;
6186 /* As ioFDirIndex = 0, get the info of ioNamePtr,
6187 which is relative to ioVrefNum, ioDirID */
6188 error = PBGetCatInfo(&theCPB, false);
6190 /* If we are called for a new file we expect fnfErr */
6191 if ((error) && (error != fnfErr))
6192 return NULL;
6194 /* Check if it's a file or folder */
6195 /* default to file if file don't exist */
6196 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
6197 folder = 0; /* It's not a folder */
6198 else
6199 folder = 1;
6201 #ifdef USE_UNIXFILENAME
6203 * The function used here are available in Carbon, but
6204 * do nothing une MacOS 8 and 9
6206 if (error == fnfErr)
6208 /* If the file to be saved does not already exist, it isn't possible
6209 to convert its FSSpec into an FSRef. But we can construct an
6210 FSSpec for the file's parent folder (since we have its volume and
6211 directory IDs), and since that folder does exist, we can convert
6212 that FSSpec into an FSRef, convert the FSRef in turn into a path,
6213 and, finally, append the filename. */
6214 FSSpec dirSpec;
6215 FSRef dirRef;
6216 Str255 emptyFilename = "\p";
6217 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
6218 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
6219 if (error)
6220 return NULL;
6222 error = FSpMakeFSRef(&dirSpec, &dirRef);
6223 if (error)
6224 return NULL;
6226 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
6227 if (status)
6228 return NULL;
6230 STRCAT(path, "/");
6231 STRCAT(path, filenamePtr);
6233 else
6235 /* If the file to be saved already exists, we can get its full path
6236 by converting its FSSpec into an FSRef. */
6237 error=FSpMakeFSRef(&file, &refFile);
6238 if (error)
6239 return NULL;
6241 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
6242 if (status)
6243 return NULL;
6246 /* Add a slash at the end if needed */
6247 if (folder)
6248 STRCAT(path, "/");
6250 return (vim_strsave(path));
6251 #else
6252 /* TODO: Get rid of all USE_UNIXFILENAME below */
6253 /* Set ioNamePtr, it's the same area which is always reused. */
6254 theCPB.dirInfo.ioNamePtr = directoryName;
6256 /* Trick for first entry, set ioDrParID to the first value
6257 * we want for ioDrDirID*/
6258 theCPB.dirInfo.ioDrParID = file.parID;
6259 theCPB.dirInfo.ioDrDirID = file.parID;
6261 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
6264 theCPB.dirInfo.ioFDirIndex = -1;
6265 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6266 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6267 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6268 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6270 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6271 /* *ioNamePtr[0 TO 31] will be updated */
6272 error = PBGetCatInfo(&theCPB,false);
6274 if (error)
6275 return NULL;
6277 /* Put the new directoryName in front of the current fname */
6278 STRCPY(temporaryPtr, filenamePtr);
6279 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6280 STRCAT(filenamePtr, ":");
6281 STRCAT(filenamePtr, temporaryPtr);
6283 #if 1 /* def USE_UNIXFILENAME */
6284 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
6285 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
6286 #else
6287 while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
6288 #endif
6290 /* Get the information about the volume on which the file reside */
6291 theCPB.dirInfo.ioFDirIndex = -1;
6292 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6293 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6294 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6295 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6297 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6298 /* *ioNamePtr[0 TO 31] will be updated */
6299 error = PBGetCatInfo(&theCPB,false);
6301 if (error)
6302 return NULL;
6304 /* For MacOS Classic always add the volume name */
6305 /* For MacOS X add the volume name preceded by "Volumes" */
6306 /* when we are not referring to the boot volume */
6307 #ifdef USE_UNIXFILENAME
6308 if (file.vRefNum != dfltVol_vRefNum)
6309 #endif
6311 /* Add the volume name */
6312 STRCPY(temporaryPtr, filenamePtr);
6313 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6314 STRCAT(filenamePtr, ":");
6315 STRCAT(filenamePtr, temporaryPtr);
6317 #ifdef USE_UNIXFILENAME
6318 STRCPY(temporaryPtr, filenamePtr);
6319 filenamePtr[0] = 0; /* NULL terminate the string */
6320 STRCAT(filenamePtr, "Volumes:");
6321 STRCAT(filenamePtr, temporaryPtr);
6322 #endif
6325 /* Append final path separator if it's a folder */
6326 if (folder)
6327 STRCAT(fname, ":");
6329 /* As we use Unix File Name for MacOS X convert it */
6330 #ifdef USE_UNIXFILENAME
6331 /* Need to insert leading / */
6332 /* TODO: get the above code to use directly the / */
6333 STRCPY(&temporaryPtr[1], filenamePtr);
6334 temporaryPtr[0] = '/';
6335 STRCPY(filenamePtr, temporaryPtr);
6337 char *p;
6338 for (p = fname; *p; p++)
6339 if (*p == ':')
6340 *p = '/';
6342 #endif
6344 return (vim_strsave(fname));
6345 #endif
6348 #if (defined(USE_IM_CONTROL) || defined(PROTO)) && defined(USE_CARBONKEYHANDLER)
6350 * Input Method Control functions.
6354 * Notify cursor position to IM.
6356 void
6357 im_set_position(int row, int col)
6359 #if 0
6360 /* TODO: Implement me! */
6361 im_start_row = row;
6362 im_start_col = col;
6363 #endif
6366 static ScriptLanguageRecord gTSLWindow;
6367 static ScriptLanguageRecord gTSLInsert;
6368 static ScriptLanguageRecord gTSLDefault = { 0, 0 };
6370 static Component gTSCWindow;
6371 static Component gTSCInsert;
6372 static Component gTSCDefault;
6374 static int im_initialized = 0;
6376 static void
6377 im_on_window_switch(int active)
6379 ScriptLanguageRecord *slptr = NULL;
6380 OSStatus err;
6382 if (! gui.in_use)
6383 return;
6385 if (im_initialized == 0)
6387 im_initialized = 1;
6389 /* save default TSM component (should be U.S.) to default */
6390 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6391 kKeyboardInputMethodClass);
6394 if (active == TRUE)
6396 im_is_active = TRUE;
6397 ActivateTSMDocument(gTSMDocument);
6398 slptr = &gTSLWindow;
6400 if (slptr)
6402 err = SetDefaultInputMethodOfClass(gTSCWindow, slptr,
6403 kKeyboardInputMethodClass);
6404 if (err == noErr)
6405 err = SetTextServiceLanguage(slptr);
6407 if (err == noErr)
6408 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6411 else
6413 err = GetTextServiceLanguage(&gTSLWindow);
6414 if (err == noErr)
6415 slptr = &gTSLWindow;
6417 if (slptr)
6418 GetDefaultInputMethodOfClass(&gTSCWindow, slptr,
6419 kKeyboardInputMethodClass);
6421 im_is_active = FALSE;
6422 DeactivateTSMDocument(gTSMDocument);
6427 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6429 void
6430 im_set_active(int active)
6432 ScriptLanguageRecord *slptr = NULL;
6433 OSStatus err;
6435 if (! gui.in_use)
6436 return;
6438 if (im_initialized == 0)
6440 im_initialized = 1;
6442 /* save default TSM component (should be U.S.) to default */
6443 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6444 kKeyboardInputMethodClass);
6447 if (active == TRUE)
6449 im_is_active = TRUE;
6450 ActivateTSMDocument(gTSMDocument);
6451 slptr = &gTSLInsert;
6453 if (slptr)
6455 err = SetDefaultInputMethodOfClass(gTSCInsert, slptr,
6456 kKeyboardInputMethodClass);
6457 if (err == noErr)
6458 err = SetTextServiceLanguage(slptr);
6460 if (err == noErr)
6461 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6464 else
6466 err = GetTextServiceLanguage(&gTSLInsert);
6467 if (err == noErr)
6468 slptr = &gTSLInsert;
6470 if (slptr)
6471 GetDefaultInputMethodOfClass(&gTSCInsert, slptr,
6472 kKeyboardInputMethodClass);
6474 /* restore to default when switch to normal mode, so than we could
6475 * enter commands easier */
6476 SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault,
6477 kKeyboardInputMethodClass);
6478 SetTextServiceLanguage(&gTSLDefault);
6480 im_is_active = FALSE;
6481 DeactivateTSMDocument(gTSMDocument);
6486 * Get IM status. When IM is on, return not 0. Else return 0.
6489 im_get_status(void)
6491 if (! gui.in_use)
6492 return 0;
6494 return im_is_active;
6497 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6502 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6503 // drawer implementation
6504 static MenuRef contextMenu = NULL;
6505 enum
6507 kTabContextMenuId = 42,
6510 // the caller has to CFRelease() the returned string
6511 static CFStringRef
6512 getTabLabel(tabpage_T *page)
6514 get_tabline_label(page, FALSE);
6515 #ifdef MACOS_CONVERT
6516 return (CFStringRef)mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6517 #else
6518 // TODO: check internal encoding?
6519 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6520 kCFStringEncodingMacRoman);
6521 #endif
6525 #define DRAWER_SIZE 150
6526 #define DRAWER_INSET 16
6528 static ControlRef dataBrowser = NULL;
6530 // when the tabline is hidden, vim doesn't call update_tabline(). When
6531 // the tabline is shown again, show_tabline() is called before upate_tabline(),
6532 // and because of this, the tab labels and vims internal tabs are out of sync
6533 // for a very short time. to prevent inconsistent state, we store the labels
6534 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6535 static CFStringRef *tabLabels = NULL;
6536 static int tabLabelsSize = 0;
6538 enum
6540 kTabsColumn = 'Tabs'
6543 static int
6544 getTabCount(void)
6546 tabpage_T *tp;
6547 int numTabs = 0;
6549 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6550 ++numTabs;
6551 return numTabs;
6554 // data browser item display callback
6555 static OSStatus
6556 dbItemDataCallback(ControlRef browser,
6557 DataBrowserItemID itemID,
6558 DataBrowserPropertyID property /* column id */,
6559 DataBrowserItemDataRef itemData,
6560 Boolean changeValue)
6562 OSStatus status = noErr;
6564 // assert(property == kTabsColumn); // why is this violated??
6566 // changeValue is true if we have a modifieable list and data was changed.
6567 // In our case, it's always false.
6568 // (that is: if (changeValue) updateInternalData(); else return
6569 // internalData();
6570 if (!changeValue)
6572 CFStringRef str;
6574 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6575 str = tabLabels[itemID - 1];
6576 status = SetDataBrowserItemDataText(itemData, str);
6578 else
6579 status = errDataBrowserPropertyNotSupported;
6581 return status;
6584 // data browser action callback
6585 static void
6586 dbItemNotificationCallback(ControlRef browser,
6587 DataBrowserItemID item,
6588 DataBrowserItemNotification message)
6590 switch (message)
6592 case kDataBrowserItemSelected:
6593 send_tabline_event(item);
6594 break;
6598 // callbacks needed for contextual menu:
6599 static void
6600 dbGetContextualMenuCallback(ControlRef browser,
6601 MenuRef *menu,
6602 UInt32 *helpType,
6603 CFStringRef *helpItemString,
6604 AEDesc *selection)
6606 // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6607 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6608 *helpItemString = NULL;
6610 *menu = contextMenu;
6613 static void
6614 dbSelectContextualMenuCallback(ControlRef browser,
6615 MenuRef menu,
6616 UInt32 selectionType,
6617 SInt16 menuID,
6618 MenuItemIndex menuItem)
6620 if (selectionType == kCMMenuItemSelected)
6622 MenuCommand command;
6623 GetMenuItemCommandID(menu, menuItem, &command);
6625 // get tab that was selected when the context menu appeared
6626 // (there is always one tab selected). TODO: check if the context menu
6627 // isn't opened on an item but on empty space (has to be possible some
6628 // way, the finder does it too ;-) )
6629 Handle items = NewHandle(0);
6630 if (items != NULL)
6632 int numItems;
6634 GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6635 kDataBrowserItemIsSelected, items);
6636 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6637 if (numItems > 0)
6639 int idx;
6640 DataBrowserItemID *itemsPtr;
6642 HLock(items);
6643 itemsPtr = (DataBrowserItemID *)*items;
6644 idx = itemsPtr[0];
6645 HUnlock(items);
6646 send_tabline_menu_event(idx, command);
6648 DisposeHandle(items);
6653 // focus callback of the data browser to always leave focus in vim
6654 static OSStatus
6655 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6657 assert(GetEventClass(event) == kEventClassControl
6658 && GetEventKind(event) == kEventControlSetFocusPart);
6660 return paramErr;
6664 // drawer callback to resize data browser to drawer size
6665 static OSStatus
6666 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6668 switch (GetEventKind(event))
6670 case kEventWindowBoundsChanged: // move or resize
6672 UInt32 attribs;
6673 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6674 NULL, sizeof(attribs), NULL, &attribs);
6675 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6677 Rect r;
6678 GetWindowBounds(drawer, kWindowContentRgn, &r);
6679 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6680 SetControlBounds(dataBrowser, &r);
6681 SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6682 kTabsColumn, r.right);
6685 break;
6688 return eventNotHandledErr;
6691 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6692 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6693 // blue highlights in the list view on these systems, though. Oh well.)
6696 #import <mach-o/dyld.h>
6698 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6700 static OSStatus
6701 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6702 OptionBits inAttributesToSet,
6703 OptionBits inAttributesToClear)
6705 long osVersion;
6706 char *symbolName;
6707 NSSymbol symbol = NULL;
6708 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6709 OptionBits inAttributesToSet, OptionBits inAttributesToClear);
6711 Gestalt(gestaltSystemVersion, &osVersion);
6712 if (osVersion < 0x1040) // only supported for 10.4 (and up)
6713 return noErr;
6715 // C name mangling...
6716 symbolName = "_DataBrowserChangeAttributes";
6717 if (!NSIsSymbolNameDefined(symbolName)
6718 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6719 return noErr;
6721 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6722 if (dataBrowserChangeAttributes == NULL)
6723 return noErr; // well...
6724 return dataBrowserChangeAttributes(inDataBrowser,
6725 inAttributesToSet, inAttributesToClear);
6728 static void
6729 initialise_tabline(void)
6731 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6732 DataBrowserCallbacks dbCallbacks;
6733 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6734 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6735 DataBrowserListViewColumnDesc colDesc;
6737 // drawers have to have compositing enabled
6738 CreateNewWindow(kDrawerWindowClass,
6739 kWindowStandardHandlerAttribute
6740 | kWindowCompositingAttribute
6741 | kWindowResizableAttribute
6742 | kWindowLiveResizeAttribute,
6743 &drawerRect, &drawer);
6745 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6746 SetDrawerParent(drawer, gui.VimWindow);
6747 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6750 // create list view embedded in drawer
6751 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6752 &dataBrowser);
6754 dbCallbacks.version = kDataBrowserLatestCallbacks;
6755 InitDataBrowserCallbacks(&dbCallbacks);
6756 dbCallbacks.u.v1.itemDataCallback =
6757 NewDataBrowserItemDataUPP(dbItemDataCallback);
6758 dbCallbacks.u.v1.itemNotificationCallback =
6759 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6760 dbCallbacks.u.v1.getContextualMenuCallback =
6761 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6762 dbCallbacks.u.v1.selectContextualMenuCallback =
6763 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6765 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6767 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6768 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6769 SetDataBrowserSelectionFlags(dataBrowser,
6770 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6771 SetDataBrowserTableViewHiliteStyle(dataBrowser,
6772 kDataBrowserTableViewFillHilite);
6773 Boolean b = false;
6774 SetControlData(dataBrowser, kControlEntireControl,
6775 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6777 // enable blue background in data browser (this is only in 10.4 and vim
6778 // has to support older osx versions as well, so we have to load this
6779 // function dynamically)
6780 myDataBrowserChangeAttributes(dataBrowser,
6781 kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6783 // install callback that keeps focus in vim and away from the data browser
6784 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6785 NULL, NULL);
6787 // install callback that keeps data browser at the size of the drawer
6788 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6789 NULL, NULL);
6791 // add "tabs" column to data browser
6792 colDesc.propertyDesc.propertyID = kTabsColumn;
6793 colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6795 // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6796 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6798 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6799 colDesc.headerBtnDesc.minimumWidth = 100;
6800 colDesc.headerBtnDesc.maximumWidth = 150;
6801 colDesc.headerBtnDesc.titleOffset = 0;
6802 colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6803 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6804 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6805 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6807 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6809 // create tabline popup menu required by vim docs (see :he tabline-menu)
6810 CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6811 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6812 TABLINE_MENU_CLOSE, NULL);
6813 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6814 TABLINE_MENU_NEW, NULL);
6815 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6816 TABLINE_MENU_OPEN, NULL);
6821 * Show or hide the tabline.
6823 void
6824 gui_mch_show_tabline(int showit)
6826 if (showit == 0)
6827 CloseDrawer(drawer, true);
6828 else
6829 OpenDrawer(drawer, kWindowEdgeRight, true);
6833 * Return TRUE when tabline is displayed.
6836 gui_mch_showing_tabline(void)
6838 WindowDrawerState state = GetDrawerState(drawer);
6840 return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6844 * Update the labels of the tabline.
6846 void
6847 gui_mch_update_tabline(void)
6849 tabpage_T *tp;
6850 int numTabs = getTabCount();
6851 int nr = 1;
6852 int curtabidx = 1;
6854 // adjust data browser
6855 if (tabLabels != NULL)
6857 int i;
6859 for (i = 0; i < tabLabelsSize; ++i)
6860 CFRelease(tabLabels[i]);
6861 free(tabLabels);
6863 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6864 tabLabelsSize = numTabs;
6866 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6868 if (tp == curtab)
6869 curtabidx = nr;
6870 tabLabels[nr-1] = getTabLabel(tp);
6873 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6874 kDataBrowserItemNoProperty);
6875 // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6876 // can pass NULL for the id array
6877 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6878 kDataBrowserItemNoProperty);
6880 DataBrowserItemID item = curtabidx;
6881 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6885 * Set the current tab to "nr". First tab is 1.
6887 void
6888 gui_mch_set_curtab(nr)
6889 int nr;
6891 DataBrowserItemID item = nr;
6892 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6894 // TODO: call something like this?: (or restore scroll position, or...)
6895 RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6896 kDataBrowserRevealOnly);
6899 #endif // FEAT_GUI_TABLINE