Merged from the latest developing branch.
[MacVim.git] / src / gui_mac.c
blob1ef5820539309e857e3814cc06827abef0f7d0fd
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 static int im_start_row = 0;
66 static int im_start_col = 0;
67 #endif
69 #define NR_ELEMS(x) (sizeof(x) / sizeof(x[0]))
71 static TSMDocumentID gTSMDocument;
73 static void im_on_window_switch(int active);
74 static EventHandlerUPP keyEventHandlerUPP = NULL;
75 static EventHandlerUPP winEventHandlerUPP = NULL;
77 static pascal OSStatus gui_mac_handle_window_activate(
78 EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
80 static pascal OSStatus gui_mac_handle_text_input(
81 EventHandlerCallRef nextHandler, EventRef theEvent, void *data);
83 static pascal OSStatus gui_mac_update_input_area(
84 EventHandlerCallRef nextHandler, EventRef theEvent);
86 static pascal OSStatus gui_mac_unicode_key_event(
87 EventHandlerCallRef nextHandler, EventRef theEvent);
89 #endif
92 /* Include some file. TODO: move into os_mac.h */
93 #include <Menus.h>
94 #include <Resources.h>
95 #include <Processes.h>
96 #ifdef USE_AEVENT
97 # include <AppleEvents.h>
98 # include <AERegistry.h>
99 #endif
100 # include <Gestalt.h>
101 #if UNIVERSAL_INTERFACES_VERSION >= 0x0330
102 # include <ControlDefinitions.h>
103 # include <Navigation.h> /* Navigation only part of ?? */
104 #endif
106 /* Help Manager (balloon.h, HM prefixed functions) are not supported
107 * under Carbon (Jussi) */
108 # if 0
109 /* New Help Interface for Mac, not implemented yet.*/
110 # include <MacHelp.h>
111 # endif
114 * These seem to be rectangle options. Why are they not found in
115 * headers? (Jussi)
117 #define kNothing 0
118 #define kCreateEmpty 2 /*1*/
119 #define kCreateRect 2
120 #define kDestroy 3
123 * Dany: Don't like those...
125 #define topLeft(r) (((Point*)&(r))[0])
126 #define botRight(r) (((Point*)&(r))[1])
129 /* Time of last mouse click, to detect double-click */
130 static long lastMouseTick = 0;
132 /* ??? */
133 static RgnHandle cursorRgn;
134 static RgnHandle dragRgn;
135 static Rect dragRect;
136 static short dragRectEnbl;
137 static short dragRectControl;
139 /* This variable is set when waiting for an event, which is the only moment
140 * scrollbar dragging can be done directly. It's not allowed while commands
141 * are executed, because it may move the cursor and that may cause unexpected
142 * problems (e.g., while ":s" is working).
144 static int allow_scrollbar = FALSE;
146 /* Last mouse click caused contextual menu, (to provide proper release) */
147 static short clickIsPopup;
149 /* Feedback Action for Scrollbar */
150 ControlActionUPP gScrollAction;
151 ControlActionUPP gScrollDrag;
153 /* Keeping track of which scrollbar is being dragged */
154 static ControlHandle dragged_sb = NULL;
156 /* Vector of char_u --> control index for hotkeys in dialogs */
157 static short *gDialogHotKeys;
159 static struct
161 FMFontFamily family;
162 FMFontSize size;
163 FMFontStyle style;
164 Boolean isPanelVisible;
165 } gFontPanelInfo = { 0, 0, 0, false };
167 #ifdef MACOS_CONVERT
168 # define USE_ATSUI_DRAWING
169 int p_macatsui_last;
170 ATSUStyle gFontStyle;
171 # ifdef FEAT_MBYTE
172 ATSUStyle gWideFontStyle;
173 # endif
174 Boolean gIsFontFallbackSet;
175 #endif
177 /* Colors Macros */
178 #define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b)
179 #define Red(c) ((c & 0x00FF0000) >> 16)
180 #define Green(c) ((c & 0x0000FF00) >> 8)
181 #define Blue(c) ((c & 0x000000FF) >> 0)
183 /* Key mapping */
185 #define vk_Esc 0x35 /* -> 1B */
187 #define vk_F1 0x7A /* -> 10 */
188 #define vk_F2 0x78 /*0x63*/
189 #define vk_F3 0x63 /*0x76*/
190 #define vk_F4 0x76 /*0x60*/
191 #define vk_F5 0x60 /*0x61*/
192 #define vk_F6 0x61 /*0x62*/
193 #define vk_F7 0x62 /*0x63*/ /*?*/
194 #define vk_F8 0x64
195 #define vk_F9 0x65
196 #define vk_F10 0x6D
197 #define vk_F11 0x67
198 #define vk_F12 0x6F
199 #define vk_F13 0x69
200 #define vk_F14 0x6B
201 #define vk_F15 0x71
203 #define vk_Clr 0x47 /* -> 1B (ESC) */
204 #define vk_Enter 0x4C /* -> 03 */
206 #define vk_Space 0x31 /* -> 20 */
207 #define vk_Tab 0x30 /* -> 09 */
208 #define vk_Return 0x24 /* -> 0D */
209 /* This is wrong for OSX, what is it for? */
210 #define vk_Delete 0X08 /* -> 08 BackSpace */
212 #define vk_Help 0x72 /* -> 05 */
213 #define vk_Home 0x73 /* -> 01 */
214 #define vk_PageUp 0x74 /* -> 0D */
215 #define vk_FwdDelete 0x75 /* -> 7F */
216 #define vk_End 0x77 /* -> 04 */
217 #define vk_PageDown 0x79 /* -> 0C */
219 #define vk_Up 0x7E /* -> 1E */
220 #define vk_Down 0x7D /* -> 1F */
221 #define vk_Left 0x7B /* -> 1C */
222 #define vk_Right 0x7C /* -> 1D */
224 #define vk_Undo vk_F1
225 #define vk_Cut vk_F2
226 #define vk_Copy vk_F3
227 #define vk_Paste vk_F4
228 #define vk_PrintScreen vk_F13
229 #define vk_SCrollLock vk_F14
230 #define vk_Pause vk_F15
231 #define vk_NumLock vk_Clr
232 #define vk_Insert vk_Help
234 #define KeySym char
236 static struct
238 KeySym key_sym;
239 char_u vim_code0;
240 char_u vim_code1;
241 } special_keys[] =
243 {vk_Up, 'k', 'u'},
244 {vk_Down, 'k', 'd'},
245 {vk_Left, 'k', 'l'},
246 {vk_Right, 'k', 'r'},
248 {vk_F1, 'k', '1'},
249 {vk_F2, 'k', '2'},
250 {vk_F3, 'k', '3'},
251 {vk_F4, 'k', '4'},
252 {vk_F5, 'k', '5'},
253 {vk_F6, 'k', '6'},
254 {vk_F7, 'k', '7'},
255 {vk_F8, 'k', '8'},
256 {vk_F9, 'k', '9'},
257 {vk_F10, 'k', ';'},
259 {vk_F11, 'F', '1'},
260 {vk_F12, 'F', '2'},
261 {vk_F13, 'F', '3'},
262 {vk_F14, 'F', '4'},
263 {vk_F15, 'F', '5'},
265 /* {XK_Help, '%', '1'}, */
266 /* {XK_Undo, '&', '8'}, */
267 /* {XK_BackSpace, 'k', 'b'}, */
268 #ifndef MACOS_X
269 {vk_Delete, 'k', 'b'},
270 #endif
271 {vk_Insert, 'k', 'I'},
272 {vk_FwdDelete, 'k', 'D'},
273 {vk_Home, 'k', 'h'},
274 {vk_End, '@', '7'},
275 /* {XK_Prior, 'k', 'P'}, */
276 /* {XK_Next, 'k', 'N'}, */
277 /* {XK_Print, '%', '9'}, */
279 {vk_PageUp, 'k', 'P'},
280 {vk_PageDown, 'k', 'N'},
282 /* End of list marker: */
283 {(KeySym)0, 0, 0}
287 * ------------------------------------------------------------
288 * Forward declaration (for those needed)
289 * ------------------------------------------------------------
292 #ifdef USE_AEVENT
293 OSErr HandleUnusedParms(const AppleEvent *theAEvent);
294 #endif
296 #ifdef FEAT_GUI_TABLINE
297 static void initialise_tabline(void);
298 static WindowRef drawer = NULL; // TODO: put into gui.h
299 #endif
301 #ifdef USE_ATSUI_DRAWING
302 static void gui_mac_set_font_attributes(GuiFont font);
303 static void gui_mac_dispose_atsui_style(void);
304 #endif
307 * ------------------------------------------------------------
308 * Conversion Utility
309 * ------------------------------------------------------------
313 * C2Pascal_save
315 * Allocate memory and convert the C-String passed in
316 * into a pascal string
320 char_u *
321 C2Pascal_save(char_u *Cstring)
323 char_u *PascalString;
324 int len;
326 if (Cstring == NULL)
327 return NULL;
329 len = STRLEN(Cstring);
331 if (len > 255) /* Truncate if necessary */
332 len = 255;
334 PascalString = alloc(len + 1);
335 if (PascalString != NULL)
337 mch_memmove(PascalString + 1, Cstring, len);
338 PascalString[0] = len;
341 return PascalString;
345 * C2Pascal_save_and_remove_backslash
347 * Allocate memory and convert the C-String passed in
348 * into a pascal string. Also remove the backslash at the same time
352 char_u *
353 C2Pascal_save_and_remove_backslash(char_u *Cstring)
355 char_u *PascalString;
356 int len;
357 char_u *p, *c;
359 len = STRLEN(Cstring);
361 if (len > 255) /* Truncate if necessary */
362 len = 255;
364 PascalString = alloc(len + 1);
365 if (PascalString != NULL)
367 for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++)
369 if ((*c == '\\') && (c[1] != 0))
371 c++;
373 *p = *c;
374 p++;
375 len++;
377 PascalString[0] = len;
380 return PascalString;
384 * Convert the modifiers of an Event into vim's modifiers (mouse)
387 int_u
388 EventModifiers2VimMouseModifiers(EventModifiers macModifiers)
390 int_u vimModifiers = 0x00;
392 if (macModifiers & (shiftKey | rightShiftKey))
393 vimModifiers |= MOUSE_SHIFT;
394 if (macModifiers & (controlKey | rightControlKey))
395 vimModifiers |= MOUSE_CTRL;
396 if (macModifiers & (optionKey | rightOptionKey))
397 vimModifiers |= MOUSE_ALT;
398 #if 0
399 /* Not yet supported */
400 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
401 vimModifiers |= MOUSE_CMD;
402 #endif
403 return (vimModifiers);
407 * Convert the modifiers of an Event into vim's modifiers (keys)
410 static int_u
411 EventModifiers2VimModifiers(EventModifiers macModifiers)
413 int_u vimModifiers = 0x00;
415 if (macModifiers & (shiftKey | rightShiftKey))
416 vimModifiers |= MOD_MASK_SHIFT;
417 if (macModifiers & (controlKey | rightControlKey))
418 vimModifiers |= MOD_MASK_CTRL;
419 if (macModifiers & (optionKey | rightOptionKey))
420 vimModifiers |= MOD_MASK_ALT;
421 #ifdef USE_CMD_KEY
422 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
423 vimModifiers |= MOD_MASK_CMD;
424 #endif
425 return (vimModifiers);
428 /* Convert a string representing a point size into pixels. The string should
429 * be a positive decimal number, with an optional decimal point (eg, "12", or
430 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
431 * character is stored in *end. The flag "vertical" says whether this
432 * calculation is for a vertical (height) size or a horizontal (width) one.
434 * From gui_w48.c
436 static int
437 points_to_pixels(char_u *str, char_u **end, int vertical)
439 int pixels;
440 int points = 0;
441 int divisor = 0;
443 while (*str)
445 if (*str == '.' && divisor == 0)
447 /* Start keeping a divisor, for later */
448 divisor = 1;
449 continue;
452 if (!isdigit(*str))
453 break;
455 points *= 10;
456 points += *str - '0';
457 divisor *= 10;
459 ++str;
462 if (divisor == 0)
463 divisor = 1;
465 pixels = points/divisor;
466 *end = str;
467 return pixels;
470 #ifdef MACOS_CONVERT
472 * Deletes all traces of any Windows-style mnemonic text (including any
473 * parentheses) from a menu item and returns the cleaned menu item title.
474 * The caller is responsible for releasing the returned string.
476 static CFStringRef
477 menu_title_removing_mnemonic(vimmenu_T *menu)
479 CFStringRef name;
480 size_t menuTitleLen;
481 CFIndex displayLen;
482 CFRange mnemonicStart;
483 CFRange mnemonicEnd;
484 CFMutableStringRef cleanedName;
486 menuTitleLen = STRLEN(menu->dname);
487 name = (CFStringRef) mac_enc_to_cfstring(menu->dname, menuTitleLen);
489 if (name)
491 /* Simple mnemonic-removal algorithm, assumes single parenthesized
492 * mnemonic character towards the end of the menu text */
493 mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards);
494 displayLen = CFStringGetLength(name);
496 if (mnemonicStart.location != kCFNotFound
497 && (mnemonicStart.location + 2) < displayLen
498 && CFStringGetCharacterAtIndex(name,
499 mnemonicStart.location + 1) == (UniChar)menu->mnemonic)
501 if (CFStringFindWithOptions(name, CFSTR(")"),
502 CFRangeMake(mnemonicStart.location + 1,
503 displayLen - mnemonicStart.location - 1),
504 kCFCompareBackwards, &mnemonicEnd) &&
505 (mnemonicStart.location + 2) == mnemonicEnd.location)
507 cleanedName = CFStringCreateMutableCopy(NULL, 0, name);
508 if (cleanedName)
510 CFStringDelete(cleanedName,
511 CFRangeMake(mnemonicStart.location,
512 mnemonicEnd.location + 1 -
513 mnemonicStart.location));
515 CFRelease(name);
516 name = cleanedName;
522 return name;
524 #endif
527 * Convert a list of FSSpec aliases into a list of fullpathname
528 * character strings.
531 char_u **
532 new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error)
534 char_u **fnames = NULL;
535 OSErr newError;
536 long fileCount;
537 FSSpec fileToOpen;
538 long actualSize;
539 AEKeyword dummyKeyword;
540 DescType dummyType;
542 /* Get number of files in list */
543 *error = AECountItems(theList, numFiles);
544 if (*error)
545 return fnames;
547 /* Allocate the pointer list */
548 fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
550 /* Empty out the list */
551 for (fileCount = 0; fileCount < *numFiles; fileCount++)
552 fnames[fileCount] = NULL;
554 /* Scan the list of FSSpec */
555 for (fileCount = 1; fileCount <= *numFiles; fileCount++)
557 /* Get the alias for the nth file, convert to an FSSpec */
558 newError = AEGetNthPtr(theList, fileCount, typeFSS,
559 &dummyKeyword, &dummyType,
560 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize);
561 if (newError)
563 /* Caller is able to clean up */
564 /* TODO: Should be clean up or not? For safety. */
565 return fnames;
568 /* Convert the FSSpec to a pathname */
569 fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen);
572 return (fnames);
576 * ------------------------------------------------------------
577 * CodeWarrior External Editor Support
578 * ------------------------------------------------------------
580 #ifdef FEAT_CW_EDITOR
583 * Handle the Window Search event from CodeWarrior
585 * Description
586 * -----------
588 * The IDE sends the Window Search AppleEvent to the editor when it
589 * needs to know whether a particular file is open in the editor.
591 * Event Reply
592 * -----------
594 * None. Put data in the location specified in the structure received.
596 * Remarks
597 * -------
599 * When the editor receives this event, determine whether the specified
600 * file is open. If it is, return the modification date/time for that file
601 * in the appropriate location specified in the structure. If the file is
602 * not opened, put the value fnfErr(file not found) in that location.
606 typedef struct WindowSearch WindowSearch;
607 struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/
609 FSSpec theFile; // identifies the file
610 long *theDate; // where to put the modification date/time
613 pascal OSErr
614 Handle_KAHL_SRCH_AE(
615 const AppleEvent *theAEvent,
616 AppleEvent *theReply,
617 long refCon)
619 OSErr error = noErr;
620 buf_T *buf;
621 int foundFile = false;
622 DescType typeCode;
623 WindowSearch SearchData;
624 Size actualSize;
626 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize);
627 if (error)
628 return error;
630 error = HandleUnusedParms(theAEvent);
631 if (error)
632 return error;
634 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
635 if (buf->b_ml.ml_mfp != NULL
636 && SearchData.theFile.parID == buf->b_FSSpec.parID
637 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0]
638 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0)
640 foundFile = true;
641 break;
644 if (foundFile == false)
645 *SearchData.theDate = fnfErr;
646 else
647 *SearchData.theDate = buf->b_mtime;
649 return error;
653 * Handle the Modified (from IDE to Editor) event from CodeWarrior
655 * Description
656 * -----------
658 * The IDE sends this event to the external editor when it wants to
659 * know which files that are open in the editor have been modified.
661 * Parameters None.
662 * ----------
664 * Event Reply
665 * -----------
666 * The reply for this event is:
668 * keyDirectObject typeAEList required
669 * each element in the list is a structure of typeChar
671 * Remarks
672 * -------
674 * When building the reply event, include one element in the list for
675 * each open file that has been modified.
679 typedef struct ModificationInfo ModificationInfo;
680 struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/
682 FSSpec theFile; // identifies the file
683 long theDate; // the date/time the file was last modified
684 short saved; // set this to zero when replying, unused
687 pascal OSErr
688 Handle_KAHL_MOD_AE(
689 const AppleEvent *theAEvent,
690 AppleEvent *theReply,
691 long refCon)
693 OSErr error = noErr;
694 AEDescList replyList;
695 long numFiles;
696 ModificationInfo theFile;
697 buf_T *buf;
699 theFile.saved = 0;
701 error = HandleUnusedParms(theAEvent);
702 if (error)
703 return error;
705 /* Send the reply */
706 /* replyObject.descriptorType = typeNull;
707 replyObject.dataHandle = nil;*/
709 /* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */
710 error = AECreateList(nil, 0, false, &replyList);
711 if (error)
712 return error;
714 #if 0
715 error = AECountItems(&replyList, &numFiles);
717 /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
718 * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
719 * sizeof(DescType))
722 /* AEPutDesc */
723 #endif
725 numFiles = 0;
726 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
727 if (buf->b_ml.ml_mfp != NULL)
729 /* Add this file to the list */
730 theFile.theFile = buf->b_FSSpec;
731 theFile.theDate = buf->b_mtime;
732 /* theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */
733 error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile));
736 #if 0
737 error = AECountItems(&replyList, &numFiles);
738 #endif
740 /* We can add data only if something to reply */
741 error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
743 if (replyList.dataHandle)
744 AEDisposeDesc(&replyList);
746 return error;
750 * Handle the Get Text event from CodeWarrior
752 * Description
753 * -----------
755 * The IDE sends the Get Text AppleEvent to the editor when it needs
756 * the source code from a file. For example, when the user issues a
757 * Check Syntax or Compile command, the compiler needs access to
758 * the source code contained in the file.
760 * Event Reply
761 * -----------
763 * None. Put data in locations specified in the structure received.
765 * Remarks
766 * -------
768 * When the editor receives this event, it must set the size of the handle
769 * in theText to fit the data in the file. It must then copy the entire
770 * contents of the specified file into the memory location specified in
771 * theText.
775 typedef struct CW_GetText CW_GetText;
776 struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/
778 FSSpec theFile; /* identifies the file */
779 Handle theText; /* the location where you return the text (must be resized properly) */
780 long *unused; /* 0 (not used) */
781 long *theDate; /* where to put the modification date/time */
784 pascal OSErr
785 Handle_KAHL_GTTX_AE(
786 const AppleEvent *theAEvent,
787 AppleEvent *theReply,
788 long refCon)
790 OSErr error = noErr;
791 buf_T *buf;
792 int foundFile = false;
793 DescType typeCode;
794 CW_GetText GetTextData;
795 Size actualSize;
796 char_u *line;
797 char_u *fullbuffer = NULL;
798 long linesize;
799 long lineStart;
800 long BufferSize;
801 long lineno;
803 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize);
805 if (error)
806 return error;
808 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
809 if (buf->b_ml.ml_mfp != NULL)
810 if (GetTextData.theFile.parID == buf->b_FSSpec.parID)
812 foundFile = true;
813 break;
816 if (foundFile)
818 BufferSize = 0; /* GetHandleSize(GetTextData.theText); */
819 for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++)
821 /* Must use the right buffer */
822 line = ml_get_buf(buf, (linenr_T) lineno, FALSE);
823 linesize = STRLEN(line) + 1;
824 lineStart = BufferSize;
825 BufferSize += linesize;
826 /* Resize handle to linesize+1 to include the linefeed */
827 SetHandleSize(GetTextData.theText, BufferSize);
828 if (GetHandleSize(GetTextData.theText) != BufferSize)
830 break; /* Simple handling for now */
832 else
834 HLock(GetTextData.theText);
835 fullbuffer = (char_u *) *GetTextData.theText;
836 STRCPY((char_u *)(fullbuffer + lineStart), line);
837 fullbuffer[BufferSize-1] = '\r';
838 HUnlock(GetTextData.theText);
841 if (fullbuffer != NULL)
843 HLock(GetTextData.theText);
844 fullbuffer[BufferSize-1] = 0;
845 HUnlock(GetTextData.theText);
847 if (foundFile == false)
848 *GetTextData.theDate = fnfErr;
849 else
850 /* *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/
851 *GetTextData.theDate = buf->b_mtime;
854 error = HandleUnusedParms(theAEvent);
856 return error;
863 /* Taken from MoreAppleEvents:ProcessHelpers*/
864 pascal OSErr
865 FindProcessBySignature(
866 const OSType targetType,
867 const OSType targetCreator,
868 ProcessSerialNumberPtr psnPtr)
870 OSErr anErr = noErr;
871 Boolean lookingForProcess = true;
873 ProcessInfoRec infoRec;
875 infoRec.processInfoLength = sizeof(ProcessInfoRec);
876 infoRec.processName = nil;
877 infoRec.processAppSpec = nil;
879 psnPtr->lowLongOfPSN = kNoProcess;
880 psnPtr->highLongOfPSN = kNoProcess;
882 while (lookingForProcess)
884 anErr = GetNextProcess(psnPtr);
885 if (anErr != noErr)
886 lookingForProcess = false;
887 else
889 anErr = GetProcessInformation(psnPtr, &infoRec);
890 if ((anErr == noErr)
891 && (infoRec.processType == targetType)
892 && (infoRec.processSignature == targetCreator))
893 lookingForProcess = false;
897 return anErr;
898 }//end FindProcessBySignature
900 void
901 Send_KAHL_MOD_AE(buf_T *buf)
903 OSErr anErr = noErr;
904 AEDesc targetAppDesc = { typeNull, nil };
905 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
906 AppleEvent theReply = { typeNull, nil };
907 AESendMode sendMode;
908 AppleEvent theEvent = {typeNull, nil };
909 AEIdleUPP idleProcUPP = nil;
910 ModificationInfo ModData;
913 anErr = FindProcessBySignature('APPL', 'CWIE', &psn);
914 if (anErr == noErr)
916 anErr = AECreateDesc(typeProcessSerialNumber, &psn,
917 sizeof(ProcessSerialNumber), &targetAppDesc);
919 if (anErr == noErr)
921 anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc,
922 kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
925 AEDisposeDesc(&targetAppDesc);
927 /* Add the parms */
928 ModData.theFile = buf->b_FSSpec;
929 ModData.theDate = buf->b_mtime;
931 if (anErr == noErr)
932 anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData));
934 if (idleProcUPP == nil)
935 sendMode = kAENoReply;
936 else
937 sendMode = kAEWaitReply;
939 if (anErr == noErr)
940 anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil);
941 if (anErr == noErr && sendMode == kAEWaitReply)
943 /* anErr = AEHGetHandlerError(&theReply);*/
945 (void) AEDisposeDesc(&theReply);
948 #endif /* FEAT_CW_EDITOR */
951 * ------------------------------------------------------------
952 * Apple Event Handling procedure
953 * ------------------------------------------------------------
955 #ifdef USE_AEVENT
958 * Handle the Unused parms of an AppleEvent
961 OSErr
962 HandleUnusedParms(const AppleEvent *theAEvent)
964 OSErr error;
965 long actualSize;
966 DescType dummyType;
967 AEKeyword missedKeyword;
969 /* Get the "missed keyword" attribute from the AppleEvent. */
970 error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr,
971 typeKeyword, &dummyType,
972 (Ptr)&missedKeyword, sizeof(missedKeyword),
973 &actualSize);
975 /* If the descriptor isn't found, then we got the required parameters. */
976 if (error == errAEDescNotFound)
978 error = noErr;
980 else
982 #if 0
983 /* Why is this removed? */
984 error = errAEEventNotHandled;
985 #endif
988 return error;
993 * Handle the ODoc AppleEvent
995 * Deals with all files dragged to the application icon.
999 typedef struct SelectionRange SelectionRange;
1000 struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */
1002 short unused1; // 0 (not used)
1003 short lineNum; // line to select (<0 to specify range)
1004 long startRange; // start of selection range (if line < 0)
1005 long endRange; // end of selection range (if line < 0)
1006 long unused2; // 0 (not used)
1007 long theDate; // modification date/time
1010 /* The IDE uses the optional keyAEPosition parameter to tell the ed-
1011 itor the selection range. If lineNum is zero or greater, scroll the text
1012 to the specified line. If lineNum is less than zero, use the values in
1013 startRange and endRange to select the specified characters. Scroll
1014 the text to display the selection. If lineNum, startRange, and
1015 endRange are all negative, there is no selection range specified.
1018 pascal OSErr
1019 HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
1022 * TODO: Clean up the code with convert the AppleEvent into
1023 * a ":args"
1025 OSErr error = noErr;
1026 // OSErr firstError = noErr;
1027 // short numErrors = 0;
1028 AEDesc theList;
1029 DescType typeCode;
1030 long numFiles;
1031 // long fileCount;
1032 char_u **fnames;
1033 // char_u fname[256];
1034 Size actualSize;
1035 SelectionRange thePosition;
1036 short gotPosition = false;
1037 long lnum;
1039 /* the direct object parameter is the list of aliases to files (one or more) */
1040 error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
1041 if (error)
1042 return error;
1045 error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize);
1046 if (error == noErr)
1047 gotPosition = true;
1048 if (error == errAEDescNotFound)
1049 error = noErr;
1050 if (error)
1051 return error;
1054 error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
1056 if (^error) then
1058 if (thePosition.lineNum >= 0)
1060 // Goto this line
1062 else
1064 // Set the range char wise
1070 #ifdef FEAT_VISUAL
1071 reset_VIsual();
1072 #endif
1074 fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error);
1076 if (error)
1078 /* TODO: empty fnames[] first */
1079 vim_free(fnames);
1080 return (error);
1083 if (starting > 0)
1085 int i;
1086 char_u *p;
1087 int fnum = -1;
1089 /* these are the initial files dropped on the Vim icon */
1090 for (i = 0 ; i < numFiles; i++)
1092 if (ga_grow(&global_alist.al_ga, 1) == FAIL
1093 || (p = vim_strsave(fnames[i])) == NULL)
1094 mch_exit(2);
1095 else
1096 alist_add(&global_alist, p, 2);
1097 if (fnum == -1)
1098 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum;
1101 /* If the file name was already in the buffer list we need to switch
1102 * to it. */
1103 if (curbuf->b_fnum != fnum)
1105 char_u cmd[30];
1107 vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum);
1108 do_cmdline_cmd(cmd);
1111 /* Change directory to the location of the first file. */
1112 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1113 shorten_fnames(TRUE);
1115 goto finished;
1118 /* Handle the drop, :edit to get to the file */
1119 handle_drop(numFiles, fnames, FALSE);
1121 /* TODO: Handle the goto/select line more cleanly */
1122 if ((numFiles == 1) & (gotPosition))
1124 if (thePosition.lineNum >= 0)
1126 lnum = thePosition.lineNum + 1;
1127 /* oap->motion_type = MLINE;
1128 setpcmark();*/
1129 if (lnum < 1L)
1130 lnum = 1L;
1131 else if (lnum > curbuf->b_ml.ml_line_count)
1132 lnum = curbuf->b_ml.ml_line_count;
1133 curwin->w_cursor.lnum = lnum;
1134 curwin->w_cursor.col = 0;
1135 /* beginline(BL_SOL | BL_FIX);*/
1137 else
1138 goto_byte(thePosition.startRange + 1);
1141 /* Update the screen display */
1142 update_screen(NOT_VALID);
1143 #ifdef FEAT_VISUAL
1144 /* Select the text if possible */
1145 if (gotPosition)
1147 VIsual_active = TRUE;
1148 VIsual_select = FALSE;
1149 VIsual = curwin->w_cursor;
1150 if (thePosition.lineNum < 0)
1152 VIsual_mode = 'v';
1153 goto_byte(thePosition.endRange);
1155 else
1157 VIsual_mode = 'V';
1158 VIsual.col = 0;
1161 #endif
1162 setcursor();
1163 out_flush();
1165 /* Fake mouse event to wake from stall */
1166 PostEvent(mouseUp, 0);
1168 finished:
1169 AEDisposeDesc(&theList); /* dispose what we allocated */
1171 error = HandleUnusedParms(theAEvent);
1172 return error;
1179 pascal OSErr
1180 Handle_aevt_oapp_AE(
1181 const AppleEvent *theAEvent,
1182 AppleEvent *theReply,
1183 long refCon)
1185 OSErr error = noErr;
1187 error = HandleUnusedParms(theAEvent);
1188 return error;
1195 pascal OSErr
1196 Handle_aevt_quit_AE(
1197 const AppleEvent *theAEvent,
1198 AppleEvent *theReply,
1199 long refCon)
1201 OSErr error = noErr;
1203 error = HandleUnusedParms(theAEvent);
1204 if (error)
1205 return error;
1207 /* Need to fake a :confirm qa */
1208 do_cmdline_cmd((char_u *)"confirm qa");
1210 return error;
1217 pascal OSErr
1218 Handle_aevt_pdoc_AE(
1219 const AppleEvent *theAEvent,
1220 AppleEvent *theReply,
1221 long refCon)
1223 OSErr error = noErr;
1225 error = HandleUnusedParms(theAEvent);
1227 return error;
1231 * Handling of unknown AppleEvent
1233 * (Just get rid of all the parms)
1235 pascal OSErr
1236 Handle_unknown_AE(
1237 const AppleEvent *theAEvent,
1238 AppleEvent *theReply,
1239 long refCon)
1241 OSErr error = noErr;
1243 error = HandleUnusedParms(theAEvent);
1245 return error;
1250 * Install the various AppleEvent Handlers
1252 OSErr
1253 InstallAEHandlers(void)
1255 OSErr error;
1257 /* install open application handler */
1258 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1259 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1260 if (error)
1262 return error;
1265 /* install quit application handler */
1266 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1267 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1268 if (error)
1270 return error;
1273 /* install open document handler */
1274 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1275 NewAEEventHandlerUPP(HandleODocAE), 0, false);
1276 if (error)
1278 return error;
1281 /* install print document handler */
1282 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1283 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1285 /* Install Core Suite */
1286 /* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1287 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1289 error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1290 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1292 error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1293 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1295 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1296 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1298 error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1299 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1301 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1302 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1304 error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1305 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1307 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1308 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1310 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1311 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1313 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1314 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1316 error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1317 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1319 error = AEInstallEventHandler(kAECoreSuite, kAESave,
1320 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1322 error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1323 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1326 #ifdef FEAT_CW_EDITOR
1328 * Bind codewarrior support handlers
1330 error = AEInstallEventHandler('KAHL', 'GTTX',
1331 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1332 if (error)
1334 return error;
1336 error = AEInstallEventHandler('KAHL', 'SRCH',
1337 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1338 if (error)
1340 return error;
1342 error = AEInstallEventHandler('KAHL', 'MOD ',
1343 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1344 if (error)
1346 return error;
1348 #endif
1350 return error;
1353 #endif /* USE_AEVENT */
1357 * Callback function, installed by InstallFontPanelHandler(), below,
1358 * to handle Font Panel events.
1360 static OSStatus
1361 FontPanelHandler(
1362 EventHandlerCallRef inHandlerCallRef,
1363 EventRef inEvent,
1364 void *inUserData)
1366 if (GetEventKind(inEvent) == kEventFontPanelClosed)
1368 gFontPanelInfo.isPanelVisible = false;
1369 return noErr;
1372 if (GetEventKind(inEvent) == kEventFontSelection)
1374 OSStatus status;
1375 FMFontFamily newFamily;
1376 FMFontSize newSize;
1377 FMFontStyle newStyle;
1379 /* Retrieve the font family ID number. */
1380 status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1381 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1382 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1383 &newFamily);
1384 if (status == noErr)
1385 gFontPanelInfo.family = newFamily;
1387 /* Retrieve the font size. */
1388 status = GetEventParameter(inEvent, kEventParamFMFontSize,
1389 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1390 if (status == noErr)
1391 gFontPanelInfo.size = newSize;
1393 /* Retrieve the font style (bold, etc.). Currently unused. */
1394 status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1395 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1396 if (status == noErr)
1397 gFontPanelInfo.style = newStyle;
1399 return noErr;
1403 static void
1404 InstallFontPanelHandler(void)
1406 EventTypeSpec eventTypes[2];
1407 EventHandlerUPP handlerUPP;
1408 /* EventHandlerRef handlerRef; */
1410 eventTypes[0].eventClass = kEventClassFont;
1411 eventTypes[0].eventKind = kEventFontSelection;
1412 eventTypes[1].eventClass = kEventClassFont;
1413 eventTypes[1].eventKind = kEventFontPanelClosed;
1415 handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1417 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1418 /*userData=*/NULL, /*handlerRef=*/NULL);
1423 * Fill the buffer pointed to by outName with the name and size
1424 * of the font currently selected in the Font Panel.
1426 #define FONT_STYLE_BUFFER_SIZE 32
1427 static void
1428 GetFontPanelSelection(char_u *outName)
1430 Str255 buf;
1431 ByteCount fontNameLen = 0;
1432 ATSUFontID fid;
1433 char_u styleString[FONT_STYLE_BUFFER_SIZE];
1435 if (!outName)
1436 return;
1438 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1440 /* Canonicalize localized font names */
1441 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1442 gFontPanelInfo.style, &fid, NULL) != noErr)
1443 return;
1445 /* Request font name with Mac encoding (otherwise we could
1446 * get an unwanted utf-16 name) */
1447 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1448 kFontNoScriptCode, kFontNoLanguageCode,
1449 255, (char *)outName, &fontNameLen, NULL) != noErr)
1450 return;
1452 /* Only encode font size, because style (bold, italic, etc) is
1453 * already part of the font full name */
1454 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1455 gFontPanelInfo.size/*,
1456 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1457 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1458 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1460 if ((fontNameLen + STRLEN(styleString)) < 255)
1461 STRCPY(outName + fontNameLen, styleString);
1463 else
1465 *outName = NUL;
1471 * ------------------------------------------------------------
1472 * Unfiled yet
1473 * ------------------------------------------------------------
1477 * gui_mac_get_menu_item_index
1479 * Returns the index inside the menu wher
1481 short /* Shoulde we return MenuItemIndex? */
1482 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1484 short index;
1485 short itemIndex = -1;
1486 vimmenu_T *pBrother;
1488 /* Only menu without parent are the:
1489 * -menu in the menubar
1490 * -popup menu
1491 * -toolbar (guess)
1493 * Which are not items anyway.
1495 if (pMenu->parent)
1497 /* Start from the Oldest Brother */
1498 pBrother = pMenu->parent->children;
1499 index = 1;
1500 while ((pBrother) && (itemIndex == -1))
1502 if (pBrother == pMenu)
1503 itemIndex = index;
1504 index++;
1505 pBrother = pBrother->next;
1508 return itemIndex;
1511 static vimmenu_T *
1512 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1514 short index;
1515 vimmenu_T *pChildMenu;
1516 vimmenu_T *pElder = pMenu->parent;
1519 /* Only menu without parent are the:
1520 * -menu in the menubar
1521 * -popup menu
1522 * -toolbar (guess)
1524 * Which are not items anyway.
1527 if ((pElder) && (pElder->submenu_id == menuID))
1529 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1530 pMenu = pMenu->next;
1532 else
1534 for (; pMenu != NULL; pMenu = pMenu->next)
1536 if (pMenu->children != NULL)
1538 pChildMenu = gui_mac_get_vim_menu
1539 (menuID, itemIndex, pMenu->children);
1540 if (pChildMenu)
1542 pMenu = pChildMenu;
1543 break;
1548 return pMenu;
1552 * ------------------------------------------------------------
1553 * MacOS Feedback procedures
1554 * ------------------------------------------------------------
1556 pascal
1557 void
1558 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1560 scrollbar_T *sb;
1561 int value, dragging;
1562 ControlHandle theControlToUse;
1563 int dont_scroll_save = dont_scroll;
1565 theControlToUse = dragged_sb;
1567 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1569 if (sb == NULL)
1570 return;
1572 /* Need to find value by diff between Old Poss New Pos */
1573 value = GetControl32BitValue(theControlToUse);
1574 dragging = (partCode != 0);
1576 /* When "allow_scrollbar" is FALSE still need to remember the new
1577 * position, but don't actually scroll by setting "dont_scroll". */
1578 dont_scroll = !allow_scrollbar;
1579 gui_drag_scrollbar(sb, value, dragging);
1580 dont_scroll = dont_scroll_save;
1583 pascal
1584 void
1585 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1587 /* TODO: have live support */
1588 scrollbar_T *sb, *sb_info;
1589 long data;
1590 long value;
1591 int page;
1592 int dragging = FALSE;
1593 int dont_scroll_save = dont_scroll;
1595 sb = gui_find_scrollbar((long)GetControlReference(theControl));
1597 if (sb == NULL)
1598 return;
1600 if (sb->wp != NULL) /* Left or right scrollbar */
1603 * Careful: need to get scrollbar info out of first (left) scrollbar
1604 * for window, but keep real scrollbar too because we must pass it to
1605 * gui_drag_scrollbar().
1607 sb_info = &sb->wp->w_scrollbars[0];
1609 if (sb_info->size > 5)
1610 page = sb_info->size - 2; /* use two lines of context */
1611 else
1612 page = sb_info->size;
1614 else /* Bottom scrollbar */
1616 sb_info = sb;
1617 page = W_WIDTH(curwin) - 5;
1620 switch (partCode)
1622 case kControlUpButtonPart: data = -1; break;
1623 case kControlDownButtonPart: data = 1; break;
1624 case kControlPageDownPart: data = page; break;
1625 case kControlPageUpPart: data = -page; break;
1626 default: data = 0; break;
1629 value = sb_info->value + data;
1630 /* if (value > sb_info->max)
1631 value = sb_info->max;
1632 else if (value < 0)
1633 value = 0;*/
1635 /* When "allow_scrollbar" is FALSE still need to remember the new
1636 * position, but don't actually scroll by setting "dont_scroll". */
1637 dont_scroll = !allow_scrollbar;
1638 gui_drag_scrollbar(sb, value, dragging);
1639 dont_scroll = dont_scroll_save;
1641 out_flush();
1642 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1644 /* if (sb_info->wp != NULL)
1646 win_T *wp;
1647 int sb_num;
1649 sb_num = 0;
1650 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1651 sb_num++;
1653 if (wp != NULL)
1655 current_scrollbar = sb_num;
1656 scrollbar_value = value;
1657 gui_do_scroll();
1658 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1664 * ------------------------------------------------------------
1665 * MacOS Click Handling procedures
1666 * ------------------------------------------------------------
1671 * Handle a click inside the window, it may happens in the
1672 * scrollbar or the contents.
1674 * TODO: Add support for potential TOOLBAR
1676 void
1677 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1679 Point thePoint;
1680 int_u vimModifiers;
1681 short thePortion;
1682 ControlHandle theControl;
1683 int vimMouseButton;
1684 short dblClick;
1686 thePoint = theEvent->where;
1687 GlobalToLocal(&thePoint);
1688 SelectWindow(whichWindow);
1690 thePortion = FindControl(thePoint, whichWindow, &theControl);
1692 if (theControl != NUL)
1694 /* We hit a scollbar */
1696 if (thePortion != kControlIndicatorPart)
1698 dragged_sb = theControl;
1699 TrackControl(theControl, thePoint, gScrollAction);
1700 dragged_sb = NULL;
1702 else
1704 dragged_sb = theControl;
1705 #if 1
1706 TrackControl(theControl, thePoint, gScrollDrag);
1707 #else
1708 TrackControl(theControl, thePoint, NULL);
1709 #endif
1710 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1711 * button has been released */
1712 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1713 dragged_sb = NULL;
1716 else
1718 /* We are inside the contents */
1720 /* Convert the CTRL, OPTION, SHIFT and CMD key */
1721 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1723 /* Defaults to MOUSE_LEFT as there's only one mouse button */
1724 vimMouseButton = MOUSE_LEFT;
1726 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1727 /* TODO: NEEDED? */
1728 clickIsPopup = FALSE;
1730 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1732 vimMouseButton = MOUSE_RIGHT;
1733 vimModifiers &= ~MOUSE_CTRL;
1734 clickIsPopup = TRUE;
1737 /* Is it a double click ? */
1738 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1740 /* Send the mouse click to Vim */
1741 gui_send_mouse_event(vimMouseButton, thePoint.h,
1742 thePoint.v, dblClick, vimModifiers);
1744 /* Create the rectangle around the cursor to detect
1745 * the mouse dragging
1747 #if 0
1748 /* TODO: Do we need to this even for the contextual menu?
1749 * It may be require for popup_setpos, but for popup?
1751 if (vimMouseButton == MOUSE_LEFT)
1752 #endif
1754 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1755 FILL_Y(Y_2_ROW(thePoint.v)),
1756 FILL_X(X_2_COL(thePoint.h)+1),
1757 FILL_Y(Y_2_ROW(thePoint.v)+1));
1759 dragRectEnbl = TRUE;
1760 dragRectControl = kCreateRect;
1766 * Handle the click in the titlebar (to move the window)
1768 void
1769 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1771 Rect movingLimits;
1772 Rect *movingLimitsPtr = &movingLimits;
1774 /* TODO: may try to prevent move outside screen? */
1775 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1776 DragWindow(whichWindow, where, movingLimitsPtr);
1780 * Handle the click in the grow box
1782 void
1783 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1786 long newSize;
1787 unsigned short newWidth;
1788 unsigned short newHeight;
1789 Rect resizeLimits;
1790 Rect *resizeLimitsPtr = &resizeLimits;
1791 Rect NewContentRect;
1793 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1795 /* Set the minimum size */
1796 /* TODO: Should this come from Vim? */
1797 resizeLimits.top = 100;
1798 resizeLimits.left = 100;
1800 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1801 newWidth = NewContentRect.right - NewContentRect.left;
1802 newHeight = NewContentRect.bottom - NewContentRect.top;
1803 gui_resize_shell(newWidth, newHeight);
1804 gui_mch_set_bg_color(gui.back_pixel);
1805 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1809 * Handle the click in the zoom box
1811 static void
1812 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1814 Rect r;
1815 Point p;
1816 short thePart;
1818 /* ideal width is current */
1819 p.h = Columns * gui.char_width + 2 * gui.border_offset;
1820 if (gui.which_scrollbars[SBAR_LEFT])
1821 p.h += gui.scrollbar_width;
1822 if (gui.which_scrollbars[SBAR_RIGHT])
1823 p.h += gui.scrollbar_width;
1824 /* ideal height is as heigh as we can get */
1825 p.v = 15 * 1024;
1827 thePart = IsWindowInStandardState(whichWindow, &p, &r)
1828 ? inZoomIn : inZoomOut;
1830 if (!TrackBox(whichWindow, theEvent->where, thePart))
1831 return;
1833 /* use returned width */
1834 p.h = r.right - r.left;
1835 /* adjust returned height */
1836 p.v = r.bottom - r.top - 2 * gui.border_offset;
1837 if (gui.which_scrollbars[SBAR_BOTTOM])
1838 p.v -= gui.scrollbar_height;
1839 p.v -= p.v % gui.char_height;
1840 p.v += 2 * gui.border_width;
1841 if (gui.which_scrollbars[SBAR_BOTTOM]);
1842 p.v += gui.scrollbar_height;
1844 ZoomWindowIdeal(whichWindow, thePart, &p);
1846 GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1847 gui_resize_shell(r.right - r.left, r.bottom - r.top);
1848 gui_mch_set_bg_color(gui.back_pixel);
1849 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1853 * ------------------------------------------------------------
1854 * MacOS Event Handling procedure
1855 * ------------------------------------------------------------
1859 * Handle the Update Event
1862 void
1863 gui_mac_doUpdateEvent(EventRecord *event)
1865 WindowPtr whichWindow;
1866 GrafPtr savePort;
1867 RgnHandle updateRgn;
1868 Rect updateRect;
1869 Rect *updateRectPtr;
1870 Rect rc;
1871 Rect growRect;
1872 RgnHandle saveRgn;
1875 updateRgn = NewRgn();
1876 if (updateRgn == NULL)
1877 return;
1879 /* This could be done by the caller as we
1880 * don't require anything else out of the event
1882 whichWindow = (WindowPtr) event->message;
1884 /* Save Current Port */
1885 GetPort(&savePort);
1887 /* Select the Window's Port */
1888 SetPortWindowPort(whichWindow);
1890 /* Let's update the window */
1891 BeginUpdate(whichWindow);
1892 /* Redraw the biggest rectangle covering the area
1893 * to be updated.
1895 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1896 # if 0
1897 /* Would be more appropriate to use the following but doesn't
1898 * seem to work under MacOS X (Dany)
1900 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1901 # endif
1903 /* Use the HLock useless in Carbon? Is it harmful?*/
1904 HLock((Handle) updateRgn);
1906 updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1907 # if 0
1908 /* Code from original Carbon Port (using GetWindowRegion.
1909 * I believe the UpdateRgn is already in local (Dany)
1911 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1912 GlobalToLocal(&botRight(updateRect));
1913 # endif
1914 /* Update the content (i.e. the text) */
1915 gui_redraw(updateRectPtr->left, updateRectPtr->top,
1916 updateRectPtr->right - updateRectPtr->left,
1917 updateRectPtr->bottom - updateRectPtr->top);
1918 /* Clear the border areas if needed */
1919 gui_mch_set_bg_color(gui.back_pixel);
1920 if (updateRectPtr->left < FILL_X(0))
1922 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1923 EraseRect(&rc);
1925 if (updateRectPtr->top < FILL_Y(0))
1927 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1928 EraseRect(&rc);
1930 if (updateRectPtr->right > FILL_X(Columns))
1932 SetRect(&rc, FILL_X(Columns), 0,
1933 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1934 EraseRect(&rc);
1936 if (updateRectPtr->bottom > FILL_Y(Rows))
1938 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1939 FILL_Y(Rows) + gui.border_offset);
1940 EraseRect(&rc);
1942 HUnlock((Handle) updateRgn);
1943 DisposeRgn(updateRgn);
1945 /* Update scrollbars */
1946 DrawControls(whichWindow);
1948 /* Update the GrowBox */
1949 /* Taken from FAQ 33-27 */
1950 saveRgn = NewRgn();
1951 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1952 GetClip(saveRgn);
1953 ClipRect(&growRect);
1954 DrawGrowIcon(whichWindow);
1955 SetClip(saveRgn);
1956 DisposeRgn(saveRgn);
1957 EndUpdate(whichWindow);
1959 /* Restore original Port */
1960 SetPort(savePort);
1964 * Handle the activate/deactivate event
1965 * (apply to a window)
1967 void
1968 gui_mac_doActivateEvent(EventRecord *event)
1970 WindowPtr whichWindow;
1972 whichWindow = (WindowPtr) event->message;
1973 /* Dim scrollbars */
1974 if (whichWindow == gui.VimWindow)
1976 ControlRef rootControl;
1977 GetRootControl(gui.VimWindow, &rootControl);
1978 if ((event->modifiers) & activeFlag)
1979 ActivateControl(rootControl);
1980 else
1981 DeactivateControl(rootControl);
1984 /* Activate */
1985 gui_focus_change((event->modifiers) & activeFlag);
1990 * Handle the suspend/resume event
1991 * (apply to the application)
1993 void
1994 gui_mac_doSuspendEvent(EventRecord *event)
1996 /* The frontmost application just changed */
1998 /* NOTE: the suspend may happen before the deactivate
1999 * seen on MacOS X
2002 /* May not need to change focus as the window will
2003 * get an activate/deactivate event
2005 if (event->message & 1)
2006 /* Resume */
2007 gui_focus_change(TRUE);
2008 else
2009 /* Suspend */
2010 gui_focus_change(FALSE);
2014 * Handle the key
2016 #ifdef USE_CARBONKEYHANDLER
2017 static pascal OSStatus
2018 gui_mac_handle_window_activate(
2019 EventHandlerCallRef nextHandler,
2020 EventRef theEvent,
2021 void *data)
2023 UInt32 eventClass = GetEventClass(theEvent);
2024 UInt32 eventKind = GetEventKind(theEvent);
2026 if (eventClass == kEventClassWindow)
2028 switch (eventKind)
2030 case kEventWindowActivated:
2031 #if defined(USE_IM_CONTROL)
2032 im_on_window_switch(TRUE);
2033 #endif
2034 return noErr;
2036 case kEventWindowDeactivated:
2037 #if defined(USE_IM_CONTROL)
2038 im_on_window_switch(FALSE);
2039 #endif
2040 return noErr;
2044 return eventNotHandledErr;
2047 static pascal OSStatus
2048 gui_mac_handle_text_input(
2049 EventHandlerCallRef nextHandler,
2050 EventRef theEvent,
2051 void *data)
2053 UInt32 eventClass = GetEventClass(theEvent);
2054 UInt32 eventKind = GetEventKind(theEvent);
2056 if (eventClass != kEventClassTextInput)
2057 return eventNotHandledErr;
2059 if ((kEventTextInputUpdateActiveInputArea != eventKind) &&
2060 (kEventTextInputUnicodeForKeyEvent != eventKind) &&
2061 (kEventTextInputOffsetToPos != eventKind) &&
2062 (kEventTextInputPosToOffset != eventKind) &&
2063 (kEventTextInputGetSelectedText != eventKind))
2064 return eventNotHandledErr;
2066 switch (eventKind)
2068 case kEventTextInputUpdateActiveInputArea:
2069 return gui_mac_update_input_area(nextHandler, theEvent);
2070 case kEventTextInputUnicodeForKeyEvent:
2071 return gui_mac_unicode_key_event(nextHandler, theEvent);
2073 case kEventTextInputOffsetToPos:
2074 case kEventTextInputPosToOffset:
2075 case kEventTextInputGetSelectedText:
2076 break;
2079 return eventNotHandledErr;
2082 static pascal
2083 OSStatus gui_mac_update_input_area(
2084 EventHandlerCallRef nextHandler,
2085 EventRef theEvent)
2087 return eventNotHandledErr;
2090 static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the
2091 keys */
2093 # define INLINE_KEY_BUFFER_SIZE 80
2094 static pascal OSStatus
2095 gui_mac_unicode_key_event(
2096 EventHandlerCallRef nextHandler,
2097 EventRef theEvent)
2099 /* Multibyte-friendly key event handler */
2100 OSStatus err = -1;
2101 UInt32 actualSize;
2102 UniChar *text;
2103 char_u result[INLINE_KEY_BUFFER_SIZE];
2104 short len = 0;
2105 UInt32 key_sym;
2106 char charcode;
2107 int key_char;
2108 UInt32 modifiers, vimModifiers;
2109 size_t encLen;
2110 char_u *to = NULL;
2111 Boolean isSpecial = FALSE;
2112 int i;
2113 EventRef keyEvent;
2115 /* Mask the mouse (as per user setting) */
2116 if (p_mh)
2117 ObscureCursor();
2119 /* Don't use the keys when the dialog wants them. */
2120 if (dialog_busy)
2121 return eventNotHandledErr;
2123 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2124 typeUnicodeText, NULL, 0, &actualSize, NULL))
2125 return eventNotHandledErr;
2127 text = (UniChar *)alloc(actualSize);
2128 if (!text)
2129 return eventNotHandledErr;
2131 err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2132 typeUnicodeText, NULL, actualSize, NULL, text);
2133 require_noerr(err, done);
2135 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2136 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2137 require_noerr(err, done);
2139 err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2140 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2141 require_noerr(err, done);
2143 err = GetEventParameter(keyEvent, kEventParamKeyCode,
2144 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2145 require_noerr(err, done);
2147 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2148 typeChar, NULL, sizeof(char), NULL, &charcode);
2149 require_noerr(err, done);
2151 #ifndef USE_CMD_KEY
2152 if (modifiers & cmdKey)
2153 goto done; /* Let system handle Cmd+... */
2154 #endif
2156 key_char = charcode;
2157 vimModifiers = EventModifiers2VimModifiers(modifiers);
2159 /* Find the special key (eg., for cursor keys) */
2160 if (actualSize <= sizeof(UniChar) &&
2161 ((text[0] < 0x20) || (text[0] == 0x7f)))
2163 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2164 if (special_keys[i].key_sym == key_sym)
2166 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2167 special_keys[i].vim_code1);
2168 key_char = simplify_key(key_char,
2169 (int *)&vimModifiers);
2170 isSpecial = TRUE;
2171 break;
2175 /* Intercept CMD-. and CTRL-c */
2176 if (((modifiers & controlKey) && key_char == 'c') ||
2177 ((modifiers & cmdKey) && key_char == '.'))
2178 got_int = TRUE;
2180 if (!isSpecial)
2182 /* remove SHIFT for keys that are already shifted, e.g.,
2183 * '(' and '*' */
2184 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2185 vimModifiers &= ~MOD_MASK_SHIFT;
2187 /* remove CTRL from keys that already have it */
2188 if (key_char < 0x20)
2189 vimModifiers &= ~MOD_MASK_CTRL;
2191 /* don't process unicode characters here */
2192 if (!IS_SPECIAL(key_char))
2194 /* Following code to simplify and consolidate vimModifiers
2195 * taken liberally from gui_w48.c */
2196 key_char = simplify_key(key_char, (int *)&vimModifiers);
2198 /* Interpret META, include SHIFT, etc. */
2199 key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2200 if (key_char == CSI)
2201 key_char = K_CSI;
2203 if (IS_SPECIAL(key_char))
2204 isSpecial = TRUE;
2208 if (vimModifiers)
2210 result[len++] = CSI;
2211 result[len++] = KS_MODIFIER;
2212 result[len++] = vimModifiers;
2215 if (isSpecial && IS_SPECIAL(key_char))
2217 result[len++] = CSI;
2218 result[len++] = K_SECOND(key_char);
2219 result[len++] = K_THIRD(key_char);
2221 else
2223 encLen = actualSize;
2224 to = mac_utf16_to_enc(text, actualSize, &encLen);
2225 if (to)
2227 /* This is basically add_to_input_buf_csi() */
2228 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2230 result[len++] = to[i];
2231 if (to[i] == CSI)
2233 result[len++] = KS_EXTRA;
2234 result[len++] = (int)KE_CSI;
2237 vim_free(to);
2241 add_to_input_buf(result, len);
2242 err = noErr;
2244 done:
2245 vim_free(text);
2246 if (err == noErr)
2248 /* Fake event to wake up WNE (required to get
2249 * key repeat working */
2250 PostEvent(keyUp, 0);
2251 return noErr;
2254 return eventNotHandledErr;
2256 #else
2257 void
2258 gui_mac_doKeyEvent(EventRecord *theEvent)
2260 /* TODO: add support for COMMAND KEY */
2261 long menu;
2262 unsigned char string[20];
2263 short num, i;
2264 short len = 0;
2265 KeySym key_sym;
2266 int key_char;
2267 int modifiers;
2268 int simplify = FALSE;
2270 /* Mask the mouse (as per user setting) */
2271 if (p_mh)
2272 ObscureCursor();
2274 /* Get the key code and it's ASCII representation */
2275 key_sym = ((theEvent->message & keyCodeMask) >> 8);
2276 key_char = theEvent->message & charCodeMask;
2277 num = 1;
2279 /* Intercept CTRL-C */
2280 if (theEvent->modifiers & controlKey)
2282 if (key_char == Ctrl_C && ctrl_c_interrupts)
2283 got_int = TRUE;
2284 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2285 && (key_char == '2' || key_char == '6'))
2287 /* CTRL-^ and CTRL-@ don't work in the normal way. */
2288 if (key_char == '2')
2289 key_char = Ctrl_AT;
2290 else
2291 key_char = Ctrl_HAT;
2292 theEvent->modifiers = 0;
2296 /* Intercept CMD-. */
2297 if (theEvent->modifiers & cmdKey)
2298 if (key_char == '.')
2299 got_int = TRUE;
2301 /* Handle command key as per menu */
2302 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2303 if (theEvent->modifiers & cmdKey)
2304 /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2305 * Why the mouse button? */
2306 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2308 menu = MenuKey(key_char);
2309 if (HiWord(menu))
2311 gui_mac_handle_menu(menu);
2312 return;
2316 /* Convert the modifiers */
2317 modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2320 /* Handle special keys. */
2321 #if 0
2322 /* Why has this been removed? */
2323 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2324 #endif
2326 /* Find the special key (for non-printable keyt_char) */
2327 if ((key_char < 0x20) || (key_char == 0x7f))
2328 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2329 if (special_keys[i].key_sym == key_sym)
2331 # if 0
2332 /* We currently don't have not so special key */
2333 if (special_keys[i].vim_code1 == NUL)
2334 key_char = special_keys[i].vim_code0;
2335 else
2336 # endif
2337 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2338 special_keys[i].vim_code1);
2339 simplify = TRUE;
2340 break;
2344 /* For some keys the modifier is included in the char itself. */
2345 if (simplify || key_char == TAB || key_char == ' ')
2346 key_char = simplify_key(key_char, &modifiers);
2348 /* Add the modifier to the input bu if needed */
2349 /* Do not want SHIFT-A or CTRL-A with modifier */
2350 if (!IS_SPECIAL(key_char)
2351 && key_sym != vk_Space
2352 && key_sym != vk_Tab
2353 && key_sym != vk_Return
2354 && key_sym != vk_Enter
2355 && key_sym != vk_Esc)
2357 #if 1
2358 /* Clear modifiers when only one modifier is set */
2359 if ((modifiers == MOD_MASK_SHIFT)
2360 || (modifiers == MOD_MASK_CTRL)
2361 || (modifiers == MOD_MASK_ALT))
2362 modifiers = 0;
2363 #else
2364 if (modifiers & MOD_MASK_CTRL)
2365 modifiers = modifiers & ~MOD_MASK_CTRL;
2366 if (modifiers & MOD_MASK_ALT)
2367 modifiers = modifiers & ~MOD_MASK_ALT;
2368 if (modifiers & MOD_MASK_SHIFT)
2369 modifiers = modifiers & ~MOD_MASK_SHIFT;
2370 #endif
2372 if (modifiers)
2374 string[len++] = CSI;
2375 string[len++] = KS_MODIFIER;
2376 string[len++] = modifiers;
2379 if (IS_SPECIAL(key_char))
2381 string[len++] = CSI;
2382 string[len++] = K_SECOND(key_char);
2383 string[len++] = K_THIRD(key_char);
2385 else
2387 #ifdef FEAT_MBYTE
2388 /* Convert characters when needed (e.g., from MacRoman to latin1).
2389 * This doesn't work for the NUL byte. */
2390 if (input_conv.vc_type != CONV_NONE && key_char > 0)
2392 char_u from[2], *to;
2393 int l;
2395 from[0] = key_char;
2396 from[1] = NUL;
2397 l = 1;
2398 to = string_convert(&input_conv, from, &l);
2399 if (to != NULL)
2401 for (i = 0; i < l && len < 19; i++)
2403 if (to[i] == CSI)
2405 string[len++] = KS_EXTRA;
2406 string[len++] = KE_CSI;
2408 else
2409 string[len++] = to[i];
2411 vim_free(to);
2413 else
2414 string[len++] = key_char;
2416 else
2417 #endif
2418 string[len++] = key_char;
2421 if (len == 1 && string[0] == CSI)
2423 /* Turn CSI into K_CSI. */
2424 string[ len++ ] = KS_EXTRA;
2425 string[ len++ ] = KE_CSI;
2428 add_to_input_buf(string, len);
2430 #endif
2433 * Handle MouseClick
2435 void
2436 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2438 short thePart;
2439 WindowPtr whichWindow;
2441 thePart = FindWindow(theEvent->where, &whichWindow);
2443 #ifdef FEAT_GUI_TABLINE
2444 /* prevent that the vim window size changes if it's activated by a
2445 click into the tab pane */
2446 if (whichWindow == drawer)
2447 return;
2448 #endif
2450 switch (thePart)
2452 case (inDesk):
2453 /* TODO: what to do? */
2454 break;
2456 case (inMenuBar):
2457 gui_mac_handle_menu(MenuSelect(theEvent->where));
2458 break;
2460 case (inContent):
2461 gui_mac_doInContentClick(theEvent, whichWindow);
2462 break;
2464 case (inDrag):
2465 gui_mac_doInDragClick(theEvent->where, whichWindow);
2466 break;
2468 case (inGrow):
2469 gui_mac_doInGrowClick(theEvent->where, whichWindow);
2470 break;
2472 case (inGoAway):
2473 if (TrackGoAway(whichWindow, theEvent->where))
2474 gui_shell_closed();
2475 break;
2477 case (inZoomIn):
2478 case (inZoomOut):
2479 gui_mac_doInZoomClick(theEvent, whichWindow);
2480 break;
2485 * Handle MouseMoved
2486 * [this event is a moving in and out of a region]
2488 void
2489 gui_mac_doMouseMovedEvent(EventRecord *event)
2491 Point thePoint;
2492 int_u vimModifiers;
2494 thePoint = event->where;
2495 GlobalToLocal(&thePoint);
2496 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2498 if (!Button())
2499 gui_mouse_moved(thePoint.h, thePoint.v);
2500 else
2501 if (!clickIsPopup)
2502 gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2503 thePoint.v, FALSE, vimModifiers);
2505 /* Reset the region from which we move in and out */
2506 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2507 FILL_Y(Y_2_ROW(thePoint.v)),
2508 FILL_X(X_2_COL(thePoint.h)+1),
2509 FILL_Y(Y_2_ROW(thePoint.v)+1));
2511 if (dragRectEnbl)
2512 dragRectControl = kCreateRect;
2517 * Handle the mouse release
2519 void
2520 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2522 Point thePoint;
2523 int_u vimModifiers;
2525 /* TODO: Properly convert the Contextual menu mouse-up */
2526 /* Potential source of the double menu */
2527 lastMouseTick = theEvent->when;
2528 dragRectEnbl = FALSE;
2529 dragRectControl = kCreateEmpty;
2530 thePoint = theEvent->where;
2531 GlobalToLocal(&thePoint);
2533 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2534 if (clickIsPopup)
2536 vimModifiers &= ~MOUSE_CTRL;
2537 clickIsPopup = FALSE;
2539 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2542 static pascal OSStatus
2543 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2544 void *data)
2546 EventRef bogusEvent;
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 /* post a bogus event to wake up WaitNextEvent */
2578 if (noErr != CreateEvent(NULL, kEventClassMouse, kEventMouseMoved, 0,
2579 kEventAttributeNone, &bogusEvent))
2580 goto bail;
2581 if (noErr != PostEventToQueue(GetMainEventQueue(), bogusEvent,
2582 kEventPriorityLow))
2583 goto bail;
2585 ReleaseEvent(bogusEvent);
2587 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2589 point.h -= bounds.left;
2590 point.v -= bounds.top;
2593 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2594 point.h, point.v, FALSE, vim_mod);
2596 return noErr;
2598 bail:
2600 * when we fail give any additional callback handler a chance to perform
2601 * it's actions
2603 return CallNextEventHandler(nextHandler, theEvent);
2606 #if 0
2609 * This would be the normal way of invoking the contextual menu
2610 * but the Vim API doesn't seem to a support a request to get
2611 * the menu that we should display
2613 void
2614 gui_mac_handle_contextual_menu(event)
2615 EventRecord *event;
2618 * Clone PopUp to use menu
2619 * Create a object descriptor for the current selection
2620 * Call the procedure
2623 // Call to Handle Popup
2624 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2626 if (status != noErr)
2627 return;
2629 if (CntxType == kCMMenuItemSelected)
2631 /* Handle the menu CntxMenuID, CntxMenuItem */
2632 /* The submenu can be handle directly by gui_mac_handle_menu */
2633 /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2634 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2636 else if (CntxMenuID == kCMShowHelpSelected)
2638 /* Should come up with the help */
2642 #endif
2645 * Handle menubar selection
2647 void
2648 gui_mac_handle_menu(long menuChoice)
2650 short menu = HiWord(menuChoice);
2651 short item = LoWord(menuChoice);
2652 vimmenu_T *theVimMenu = root_menu;
2654 if (menu == 256) /* TODO: use constant or gui.xyz */
2656 if (item == 1)
2657 gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2659 else if (item != 0)
2661 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2663 if (theVimMenu)
2664 gui_menu_cb(theVimMenu);
2666 HiliteMenu(0);
2670 * Dispatch the event to proper handler
2673 void
2674 gui_mac_handle_event(EventRecord *event)
2676 OSErr error;
2678 /* Handle contextual menu right now (if needed) */
2679 if (IsShowContextualMenuClick(event))
2681 # if 0
2682 gui_mac_handle_contextual_menu(event);
2683 # else
2684 gui_mac_doMouseDownEvent(event);
2685 # endif
2686 return;
2689 /* Handle normal event */
2690 switch (event->what)
2692 #ifndef USE_CARBONKEYHANDLER
2693 case (keyDown):
2694 case (autoKey):
2695 gui_mac_doKeyEvent(event);
2696 break;
2697 #endif
2698 case (keyUp):
2699 /* We don't care about when the key is released */
2700 break;
2702 case (mouseDown):
2703 gui_mac_doMouseDownEvent(event);
2704 break;
2706 case (mouseUp):
2707 gui_mac_doMouseUpEvent(event);
2708 break;
2710 case (updateEvt):
2711 gui_mac_doUpdateEvent(event);
2712 break;
2714 case (diskEvt):
2715 /* We don't need special handling for disk insertion */
2716 break;
2718 case (activateEvt):
2719 gui_mac_doActivateEvent(event);
2720 break;
2722 case (osEvt):
2723 switch ((event->message >> 24) & 0xFF)
2725 case (0xFA): /* mouseMovedMessage */
2726 gui_mac_doMouseMovedEvent(event);
2727 break;
2728 case (0x01): /* suspendResumeMessage */
2729 gui_mac_doSuspendEvent(event);
2730 break;
2732 break;
2734 #ifdef USE_AEVENT
2735 case (kHighLevelEvent):
2736 /* Someone's talking to us, through AppleEvents */
2737 error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2738 break;
2739 #endif
2744 * ------------------------------------------------------------
2745 * Unknown Stuff
2746 * ------------------------------------------------------------
2750 GuiFont
2751 gui_mac_find_font(char_u *font_name)
2753 char_u c;
2754 char_u *p;
2755 char_u pFontName[256];
2756 Str255 systemFontname;
2757 short font_id;
2758 short size=9;
2759 GuiFont font;
2760 #if 0
2761 char_u *fontNamePtr;
2762 #endif
2764 for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2767 c = *p;
2768 *p = 0;
2770 #if 1
2771 STRCPY(&pFontName[1], font_name);
2772 pFontName[0] = STRLEN(font_name);
2773 *p = c;
2775 /* Get the font name, minus the style suffix (:h, etc) */
2776 char_u fontName[256];
2777 char_u *styleStart = vim_strchr(font_name, ':');
2778 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2779 vim_strncpy(fontName, font_name, fontNameLen);
2781 ATSUFontID fontRef;
2782 FMFontStyle fontStyle;
2783 font_id = 0;
2785 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2786 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2787 &fontRef) == noErr)
2789 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2790 font_id = 0;
2793 if (font_id == 0)
2796 * Try again, this time replacing underscores in the font name
2797 * with spaces (:set guifont allows the two to be used
2798 * interchangeably; the Font Manager doesn't).
2800 int i, changed = FALSE;
2802 for (i = pFontName[0]; i > 0; --i)
2804 if (pFontName[i] == '_')
2806 pFontName[i] = ' ';
2807 changed = TRUE;
2810 if (changed)
2811 if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2812 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2813 kFontNoLanguageCode, &fontRef) == noErr)
2815 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2816 font_id = 0;
2820 #else
2821 /* name = C2Pascal_save(menu->dname); */
2822 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2824 GetFNum(fontNamePtr, &font_id);
2825 #endif
2828 if (font_id == 0)
2830 /* Oups, the system font was it the one the user want */
2832 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2833 return NOFONT;
2834 if (!EqualString(pFontName, systemFontname, false, false))
2835 return NOFONT;
2837 if (*p == ':')
2839 p++;
2840 /* Set the values found after ':' */
2841 while (*p)
2843 switch (*p++)
2845 case 'h':
2846 size = points_to_pixels(p, &p, TRUE);
2847 break;
2849 * TODO: Maybe accept width and styles
2852 while (*p == ':')
2853 p++;
2857 if (size < 1)
2858 size = 1; /* Avoid having a size of 0 with system font */
2860 font = (size << 16) + ((long) font_id & 0xFFFF);
2862 return font;
2866 * ------------------------------------------------------------
2867 * GUI_MCH functionality
2868 * ------------------------------------------------------------
2872 * Parse the GUI related command-line arguments. Any arguments used are
2873 * deleted from argv, and *argc is decremented accordingly. This is called
2874 * when vim is started, whether or not the GUI has been started.
2876 void
2877 gui_mch_prepare(int *argc, char **argv)
2879 /* TODO: Move most of this stuff toward gui_mch_init */
2880 #ifdef USE_EXE_NAME
2881 FSSpec applDir;
2882 # ifndef USE_FIND_BUNDLE_PATH
2883 short applVRefNum;
2884 long applDirID;
2885 Str255 volName;
2886 # else
2887 ProcessSerialNumber psn;
2888 FSRef applFSRef;
2889 # endif
2890 #endif
2892 #if 0
2893 InitCursor();
2895 RegisterAppearanceClient();
2897 #ifdef USE_AEVENT
2898 (void) InstallAEHandlers();
2899 #endif
2901 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2903 AppendMenu(pomme, "\pAbout VIM");
2905 InsertMenu(pomme, 0);
2907 DrawMenuBar();
2910 #ifndef USE_OFFSETED_WINDOW
2911 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2912 #else
2913 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2914 #endif
2917 CreateNewWindow(kDocumentWindowClass,
2918 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2919 &windRect, &gui.VimWindow);
2920 SetPortWindowPort(gui.VimWindow);
2922 gui.char_width = 7;
2923 gui.char_height = 11;
2924 gui.char_ascent = 6;
2925 gui.num_rows = 24;
2926 gui.num_cols = 80;
2927 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2929 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2930 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2932 dragRectEnbl = FALSE;
2933 dragRgn = NULL;
2934 dragRectControl = kCreateEmpty;
2935 cursorRgn = NewRgn();
2936 #endif
2937 #ifdef USE_EXE_NAME
2938 # ifndef USE_FIND_BUNDLE_PATH
2939 HGetVol(volName, &applVRefNum, &applDirID);
2940 /* TN2015: mention a possible bad VRefNum */
2941 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2942 # else
2943 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2944 * of TN2015
2946 (void)GetCurrentProcess(&psn);
2947 /* if (err != noErr) return err; */
2949 (void)GetProcessBundleLocation(&psn, &applFSRef);
2950 /* if (err != noErr) return err; */
2952 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2954 /* This technic return NIL when we disallow_gui */
2955 # endif
2956 exe_name = FullPathFromFSSpec_save(applDir);
2957 #endif
2960 #ifndef ALWAYS_USE_GUI
2962 * Check if the GUI can be started. Called before gvimrc is sourced.
2963 * Return OK or FAIL.
2966 gui_mch_init_check(void)
2968 /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2969 * using the >console
2971 if (disallow_gui) /* see main.c for reason to disallow */
2972 return FAIL;
2973 return OK;
2975 #endif
2977 static OSErr
2978 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2980 int x, y;
2981 int_u modifiers;
2982 char_u **fnames = NULL;
2983 int count;
2984 int i, j;
2986 /* Get drop position, modifiers and count of items */
2988 Point point;
2989 SInt16 mouseUpModifiers;
2990 UInt16 countItem;
2992 GetDragMouse(theDrag, &point, NULL);
2993 GlobalToLocal(&point);
2994 x = point.h;
2995 y = point.v;
2996 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2997 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2998 CountDragItems(theDrag, &countItem);
2999 count = countItem;
3002 fnames = (char_u **)alloc(count * sizeof(char_u *));
3003 if (fnames == NULL)
3004 return dragNotAcceptedErr;
3006 /* Get file names dropped */
3007 for (i = j = 0; i < count; ++i)
3009 DragItemRef item;
3010 OSErr err;
3011 Size size;
3012 FlavorType type = flavorTypeHFS;
3013 HFSFlavor hfsFlavor;
3015 fnames[i] = NULL;
3016 GetDragItemReferenceNumber(theDrag, i + 1, &item);
3017 err = GetFlavorDataSize(theDrag, item, type, &size);
3018 if (err != noErr || size > sizeof(hfsFlavor))
3019 continue;
3020 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
3021 if (err != noErr)
3022 continue;
3023 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
3025 count = j;
3027 gui_handle_drop(x, y, modifiers, fnames, count);
3029 /* Fake mouse event to wake from stall */
3030 PostEvent(mouseUp, 0);
3032 return noErr;
3036 * Initialise the GUI. Create all the windows, set up all the call-backs
3037 * etc.
3040 gui_mch_init(void)
3042 /* TODO: Move most of this stuff toward gui_mch_init */
3043 Rect windRect;
3044 MenuHandle pomme;
3045 EventHandlerRef mouseWheelHandlerRef;
3046 EventTypeSpec eventTypeSpec;
3047 ControlRef rootControl;
3049 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
3050 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
3052 #if 1
3053 InitCursor();
3055 RegisterAppearanceClient();
3057 #ifdef USE_AEVENT
3058 (void) InstallAEHandlers();
3059 #endif
3061 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
3063 AppendMenu(pomme, "\pAbout VIM");
3065 InsertMenu(pomme, 0);
3067 DrawMenuBar();
3070 #ifndef USE_OFFSETED_WINDOW
3071 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
3072 #else
3073 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
3074 #endif
3076 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
3077 zoomDocProc,
3078 (WindowPtr)-1L, true, 0);
3079 CreateRootControl(gui.VimWindow, &rootControl);
3080 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
3081 gui.VimWindow, NULL);
3082 SetPortWindowPort(gui.VimWindow);
3084 gui.char_width = 7;
3085 gui.char_height = 11;
3086 gui.char_ascent = 6;
3087 gui.num_rows = 24;
3088 gui.num_cols = 80;
3089 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
3091 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
3092 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
3094 /* Install Carbon event callbacks. */
3095 (void)InstallFontPanelHandler();
3097 dragRectEnbl = FALSE;
3098 dragRgn = NULL;
3099 dragRectControl = kCreateEmpty;
3100 cursorRgn = NewRgn();
3101 #endif
3102 /* Display any pending error messages */
3103 display_errors();
3105 /* Get background/foreground colors from system */
3106 /* TODO: do the appropriate call to get real defaults */
3107 gui.norm_pixel = 0x00000000;
3108 gui.back_pixel = 0x00FFFFFF;
3110 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3111 * file). */
3112 set_normal_colors();
3115 * Check that none of the colors are the same as the background color.
3116 * Then store the current values as the defaults.
3118 gui_check_colors();
3119 gui.def_norm_pixel = gui.norm_pixel;
3120 gui.def_back_pixel = gui.back_pixel;
3122 /* Get the colors for the highlight groups (gui_check_colors() might have
3123 * changed them) */
3124 highlight_gui_started();
3127 * Setting the gui constants
3129 #ifdef FEAT_MENU
3130 gui.menu_height = 0;
3131 #endif
3132 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3133 gui.border_offset = gui.border_width = 2;
3135 /* If Quartz-style text anti aliasing is available (see
3136 gui_mch_draw_string() below), enable it for all font sizes. */
3137 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3139 eventTypeSpec.eventClass = kEventClassMouse;
3140 eventTypeSpec.eventKind = kEventMouseWheelMoved;
3141 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3142 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3143 &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3145 mouseWheelHandlerRef = NULL;
3146 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3147 mouseWheelHandlerUPP = NULL;
3150 #ifdef USE_CARBONKEYHANDLER
3151 InterfaceTypeList supportedServices = { kUnicodeDocument };
3152 NewTSMDocument(1, supportedServices, &gTSMDocument, 0);
3154 /* We don't support inline input yet, use input window by default */
3155 UseInputWindow(gTSMDocument, TRUE);
3157 /* Should we activate the document by default? */
3158 // ActivateTSMDocument(gTSMDocument);
3160 EventTypeSpec textEventTypes[] = {
3161 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
3162 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
3163 { kEventClassTextInput, kEventTextInputPosToOffset },
3164 { kEventClassTextInput, kEventTextInputOffsetToPos },
3167 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input);
3168 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP,
3169 NR_ELEMS(textEventTypes),
3170 textEventTypes, NULL, NULL))
3172 DisposeEventHandlerUPP(keyEventHandlerUPP);
3173 keyEventHandlerUPP = NULL;
3176 EventTypeSpec windowEventTypes[] = {
3177 { kEventClassWindow, kEventWindowActivated },
3178 { kEventClassWindow, kEventWindowDeactivated },
3181 /* Install window event handler to support TSMDocument activate and
3182 * deactivate */
3183 winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate);
3184 if (noErr != InstallWindowEventHandler(gui.VimWindow,
3185 winEventHandlerUPP,
3186 NR_ELEMS(windowEventTypes),
3187 windowEventTypes, NULL, NULL))
3189 DisposeEventHandlerUPP(winEventHandlerUPP);
3190 winEventHandlerUPP = NULL;
3192 #endif
3195 #ifdef FEAT_MBYTE
3196 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3197 #endif
3200 #ifdef FEAT_GUI_TABLINE
3202 * Create the tabline
3204 initialise_tabline();
3205 #endif
3207 /* TODO: Load bitmap if using TOOLBAR */
3208 return OK;
3212 * Called when the foreground or background color has been changed.
3214 void
3215 gui_mch_new_colors(void)
3217 /* TODO:
3218 * This proc is called when Normal is set to a value
3219 * so what msut be done? I don't know
3224 * Open the GUI window which was created by a call to gui_mch_init().
3227 gui_mch_open(void)
3229 ShowWindow(gui.VimWindow);
3231 if (gui_win_x != -1 && gui_win_y != -1)
3232 gui_mch_set_winpos(gui_win_x, gui_win_y);
3235 * Make the GUI the foreground process (in case it was launched
3236 * from the Terminal or via :gui).
3239 ProcessSerialNumber psn;
3240 if (GetCurrentProcess(&psn) == noErr)
3241 SetFrontProcess(&psn);
3244 return OK;
3247 #ifdef USE_ATSUI_DRAWING
3248 static void
3249 gui_mac_dispose_atsui_style(void)
3251 if (p_macatsui && gFontStyle)
3252 ATSUDisposeStyle(gFontStyle);
3253 #ifdef FEAT_MBYTE
3254 if (p_macatsui && gWideFontStyle)
3255 ATSUDisposeStyle(gWideFontStyle);
3256 #endif
3258 #endif
3260 void
3261 gui_mch_exit(int rc)
3263 /* TODO: find out all what is missing here? */
3264 DisposeRgn(cursorRgn);
3266 #ifdef USE_CARBONKEYHANDLER
3267 if (keyEventHandlerUPP)
3268 DisposeEventHandlerUPP(keyEventHandlerUPP);
3269 #endif
3271 if (mouseWheelHandlerUPP != NULL)
3272 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3274 #ifdef USE_ATSUI_DRAWING
3275 gui_mac_dispose_atsui_style();
3276 #endif
3278 #ifdef USE_CARBONKEYHANDLER
3279 FixTSMDocument(gTSMDocument);
3280 DeactivateTSMDocument(gTSMDocument);
3281 DeleteTSMDocument(gTSMDocument);
3282 #endif
3284 /* Exit to shell? */
3285 exit(rc);
3289 * Get the position of the top left corner of the window.
3292 gui_mch_get_winpos(int *x, int *y)
3294 /* TODO */
3295 Rect bounds;
3296 OSStatus status;
3298 /* Carbon >= 1.0.2, MacOS >= 8.5 */
3299 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3301 if (status != noErr)
3302 return FAIL;
3303 *x = bounds.left;
3304 *y = bounds.top;
3305 return OK;
3306 return FAIL;
3310 * Set the position of the top left corner of the window to the given
3311 * coordinates.
3313 void
3314 gui_mch_set_winpos(int x, int y)
3316 /* TODO: Should make sure the window is move within range
3317 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3319 MoveWindowStructure(gui.VimWindow, x, y);
3322 void
3323 gui_mch_set_shellsize(
3324 int width,
3325 int height,
3326 int min_width,
3327 int min_height,
3328 int base_width,
3329 int base_height,
3330 int direction)
3332 CGrafPtr VimPort;
3333 Rect VimBound;
3335 if (gui.which_scrollbars[SBAR_LEFT])
3337 VimPort = GetWindowPort(gui.VimWindow);
3338 GetPortBounds(VimPort, &VimBound);
3339 VimBound.left = -gui.scrollbar_width; /* + 1;*/
3340 SetPortBounds(VimPort, &VimBound);
3341 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3343 else
3345 VimPort = GetWindowPort(gui.VimWindow);
3346 GetPortBounds(VimPort, &VimBound);
3347 VimBound.left = 0;
3348 SetPortBounds(VimPort, &VimBound);
3351 SizeWindow(gui.VimWindow, width, height, TRUE);
3353 gui_resize_shell(width, height);
3357 * Get the screen dimensions.
3358 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3359 * Is there no way to find out how wide the borders really are?
3360 * TODO: Add live update of those value on suspend/resume.
3362 void
3363 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3365 GDHandle dominantDevice = GetMainDevice();
3366 Rect screenRect = (**dominantDevice).gdRect;
3368 *screen_w = screenRect.right - 10;
3369 *screen_h = screenRect.bottom - 40;
3374 * Open the Font Panel and wait for the user to select a font and
3375 * close the panel. Then fill the buffer pointed to by font_name with
3376 * the name and size of the selected font and return the font's handle,
3377 * or NOFONT in case of an error.
3379 static GuiFont
3380 gui_mac_select_font(char_u *font_name)
3382 GuiFont selected_font = NOFONT;
3383 OSStatus status;
3384 FontSelectionQDStyle curr_font;
3386 /* Initialize the Font Panel with the current font. */
3387 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3388 curr_font.size = (gui.norm_font >> 16);
3389 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3390 curr_font.instance.fontStyle = 0;
3391 curr_font.hasColor = false;
3392 curr_font.version = 0; /* version number of the style structure */
3393 status = SetFontInfoForSelection(kFontSelectionQDType,
3394 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3396 gFontPanelInfo.family = curr_font.instance.fontFamily;
3397 gFontPanelInfo.style = curr_font.instance.fontStyle;
3398 gFontPanelInfo.size = curr_font.size;
3400 /* Pop up the Font Panel. */
3401 status = FPShowHideFontPanel();
3402 if (status == noErr)
3405 * The Font Panel is modeless. We really need it to be modal,
3406 * so we spin in an event loop until the panel is closed.
3408 gFontPanelInfo.isPanelVisible = true;
3409 while (gFontPanelInfo.isPanelVisible)
3411 EventRecord e;
3412 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3415 GetFontPanelSelection(font_name);
3416 selected_font = gui_mac_find_font(font_name);
3418 return selected_font;
3421 #ifdef USE_ATSUI_DRAWING
3422 static void
3423 gui_mac_create_atsui_style(void)
3425 if (p_macatsui && gFontStyle == NULL)
3427 if (ATSUCreateStyle(&gFontStyle) != noErr)
3428 gFontStyle = NULL;
3430 #ifdef FEAT_MBYTE
3431 if (p_macatsui && gWideFontStyle == NULL)
3433 if (ATSUCreateStyle(&gWideFontStyle) != noErr)
3434 gWideFontStyle = NULL;
3436 #endif
3438 p_macatsui_last = p_macatsui;
3440 #endif
3443 * Initialise vim to use the font with the given name. Return FAIL if the font
3444 * could not be loaded, OK otherwise.
3447 gui_mch_init_font(char_u *font_name, int fontset)
3449 /* TODO: Add support for bold italic underline proportional etc... */
3450 Str255 suggestedFont = "\pMonaco";
3451 int suggestedSize = 10;
3452 FontInfo font_info;
3453 short font_id;
3454 GuiFont font;
3455 char_u used_font_name[512];
3457 #ifdef USE_ATSUI_DRAWING
3458 gui_mac_create_atsui_style();
3459 #endif
3461 if (font_name == NULL)
3463 /* First try to get the suggested font */
3464 GetFNum(suggestedFont, &font_id);
3466 if (font_id == 0)
3468 /* Then pickup the standard application font */
3469 font_id = GetAppFont();
3470 STRCPY(used_font_name, "default");
3472 else
3473 STRCPY(used_font_name, "Monaco");
3474 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3476 else if (STRCMP(font_name, "*") == 0)
3478 char_u *new_p_guifont;
3480 font = gui_mac_select_font(used_font_name);
3481 if (font == NOFONT)
3482 return FAIL;
3484 /* Set guifont to the name of the selected font. */
3485 new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3486 if (new_p_guifont != NULL)
3488 STRCPY(new_p_guifont, used_font_name);
3489 vim_free(p_guifont);
3490 p_guifont = new_p_guifont;
3491 /* Replace spaces in the font name with underscores. */
3492 for ( ; *new_p_guifont; ++new_p_guifont)
3494 if (*new_p_guifont == ' ')
3495 *new_p_guifont = '_';
3499 else
3501 font = gui_mac_find_font(font_name);
3502 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3504 if (font == NOFONT)
3505 return FAIL;
3508 gui.norm_font = font;
3510 hl_set_font_name(used_font_name);
3512 TextSize(font >> 16);
3513 TextFont(font & 0xFFFF);
3515 GetFontInfo(&font_info);
3517 gui.char_ascent = font_info.ascent;
3518 gui.char_width = CharWidth('_');
3519 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3521 #ifdef USE_ATSUI_DRAWING
3522 if (p_macatsui && gFontStyle)
3523 gui_mac_set_font_attributes(font);
3524 #endif
3526 return OK;
3530 * Adjust gui.char_height (after 'linespace' was changed).
3533 gui_mch_adjust_charheight(void)
3535 FontInfo font_info;
3537 GetFontInfo(&font_info);
3538 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3539 gui.char_ascent = font_info.ascent + p_linespace / 2;
3540 return OK;
3544 * Get a font structure for highlighting.
3546 GuiFont
3547 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3549 GuiFont font;
3551 font = gui_mac_find_font(name);
3553 if (font == NOFONT)
3555 if (giveErrorIfMissing)
3556 EMSG2(_(e_font), name);
3557 return NOFONT;
3560 * TODO : Accept only monospace
3563 return font;
3566 #if defined(FEAT_EVAL) || defined(PROTO)
3568 * Return the name of font "font" in allocated memory.
3569 * Don't know how to get the actual name, thus use the provided name.
3571 char_u *
3572 gui_mch_get_fontname(GuiFont font, char_u *name)
3574 if (name == NULL)
3575 return NULL;
3576 return vim_strsave(name);
3578 #endif
3580 #ifdef USE_ATSUI_DRAWING
3581 static void
3582 gui_mac_set_font_attributes(GuiFont font)
3584 ATSUFontID fontID;
3585 Fixed fontSize;
3586 Fixed fontWidth;
3588 fontID = font & 0xFFFF;
3589 fontSize = Long2Fix(font >> 16);
3590 fontWidth = Long2Fix(gui.char_width);
3592 ATSUAttributeTag attribTags[] =
3594 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
3595 kATSUMaxATSUITagValue + 1
3598 ByteCount attribSizes[] =
3600 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
3601 sizeof(font)
3604 ATSUAttributeValuePtr attribValues[] =
3606 &fontID, &fontSize, &fontWidth, &font
3609 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3611 if (ATSUSetAttributes(gFontStyle,
3612 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3613 attribTags, attribSizes, attribValues) != noErr)
3615 # ifndef NDEBUG
3616 fprintf(stderr, "couldn't set font style\n");
3617 # endif
3618 ATSUDisposeStyle(gFontStyle);
3619 gFontStyle = NULL;
3622 #ifdef FEAT_MBYTE
3623 if (has_mbyte)
3625 /* FIXME: we should use a more mbyte sensitive way to support
3626 * wide font drawing */
3627 fontWidth = Long2Fix(gui.char_width * 2);
3629 if (ATSUSetAttributes(gWideFontStyle,
3630 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3631 attribTags, attribSizes, attribValues) != noErr)
3633 ATSUDisposeStyle(gWideFontStyle);
3634 gWideFontStyle = NULL;
3637 #endif
3640 #endif
3643 * Set the current text font.
3645 void
3646 gui_mch_set_font(GuiFont font)
3648 #ifdef USE_ATSUI_DRAWING
3649 GuiFont currFont;
3650 ByteCount actualFontByteCount;
3652 if (p_macatsui && gFontStyle)
3654 /* Avoid setting same font again */
3655 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1,
3656 sizeof(font), &currFont, &actualFontByteCount) == noErr
3657 && actualFontByteCount == (sizeof font))
3659 if (currFont == font)
3660 return;
3663 gui_mac_set_font_attributes(font);
3666 if (p_macatsui && !gIsFontFallbackSet)
3668 /* Setup automatic font substitution. The user's guifontwide
3669 * is tried first, then the system tries other fonts. */
3671 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3672 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3673 ATSUCreateFontFallbacks(&gFontFallbacks);
3674 ATSUSetObjFontFallbacks(gFontFallbacks, );
3676 if (gui.wide_font)
3678 ATSUFontID fallbackFonts;
3679 gIsFontFallbackSet = TRUE;
3681 if (FMGetFontFromFontFamilyInstance(
3682 (gui.wide_font & 0xFFFF),
3684 &fallbackFonts,
3685 NULL) == noErr)
3687 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID),
3688 &fallbackFonts,
3689 kATSUSequentialFallbacksPreferred);
3692 ATSUAttributeValuePtr fallbackValues[] = { };
3696 #endif
3697 TextSize(font >> 16);
3698 TextFont(font & 0xFFFF);
3702 * If a font is not going to be used, free its structure.
3704 void
3705 gui_mch_free_font(font)
3706 GuiFont font;
3709 * Free font when "font" is not 0.
3710 * Nothing to do in the current implementation, since
3711 * nothing is allocated for each font used.
3715 static int
3716 hex_digit(int c)
3718 if (isdigit(c))
3719 return c - '0';
3720 c = TOLOWER_ASC(c);
3721 if (c >= 'a' && c <= 'f')
3722 return c - 'a' + 10;
3723 return -1000;
3727 * Return the Pixel value (color) for the given color name. This routine was
3728 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3729 * Programmer's Guide.
3730 * Return INVALCOLOR when failed.
3732 guicolor_T
3733 gui_mch_get_color(char_u *name)
3735 /* TODO: Add support for the new named color of MacOS 8
3737 RGBColor MacColor;
3738 // guicolor_T color = 0;
3740 typedef struct guicolor_tTable
3742 char *name;
3743 guicolor_T color;
3744 } guicolor_tTable;
3747 * The comment at the end of each line is the source
3748 * (Mac, Window, Unix) and the number is the unix rgb.txt value
3750 static guicolor_tTable table[] =
3752 {"Black", RGB(0x00, 0x00, 0x00)},
3753 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
3754 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
3755 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3756 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3757 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3758 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3759 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3760 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3761 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/
3762 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/
3763 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3764 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3765 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/
3766 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/
3767 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3768 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3769 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/
3770 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/
3771 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3772 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3773 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3774 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3775 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3776 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3777 {"white", RGB(0xFF, 0xFF, 0xFF)},
3778 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
3779 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
3780 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3781 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
3782 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
3783 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3784 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
3785 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
3786 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3787 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3788 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
3789 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3790 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3791 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
3792 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3793 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
3794 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
3795 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3796 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
3797 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3798 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3799 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3800 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3801 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
3804 int r, g, b;
3805 int i;
3807 if (name[0] == '#' && strlen((char *) name) == 7)
3809 /* Name is in "#rrggbb" format */
3810 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3811 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3812 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3813 if (r < 0 || g < 0 || b < 0)
3814 return INVALCOLOR;
3815 return RGB(r, g, b);
3817 else
3819 if (STRICMP(name, "hilite") == 0)
3821 LMGetHiliteRGB(&MacColor);
3822 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3824 /* Check if the name is one of the colors we know */
3825 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3826 if (STRICMP(name, table[i].name) == 0)
3827 return table[i].color;
3831 * Last attempt. Look in the file "$VIM/rgb.txt".
3834 #define LINE_LEN 100
3835 FILE *fd;
3836 char line[LINE_LEN];
3837 char_u *fname;
3839 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3840 if (fname == NULL)
3841 return INVALCOLOR;
3843 fd = fopen((char *)fname, "rt");
3844 vim_free(fname);
3845 if (fd == NULL)
3846 return INVALCOLOR;
3848 while (!feof(fd))
3850 int len;
3851 int pos;
3852 char *color;
3854 fgets(line, LINE_LEN, fd);
3855 len = strlen(line);
3857 if (len <= 1 || line[len-1] != '\n')
3858 continue;
3860 line[len-1] = '\0';
3862 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3863 if (i != 3)
3864 continue;
3866 color = line + pos;
3868 if (STRICMP(color, name) == 0)
3870 fclose(fd);
3871 return (guicolor_T) RGB(r, g, b);
3874 fclose(fd);
3877 return INVALCOLOR;
3881 * Set the current text foreground color.
3883 void
3884 gui_mch_set_fg_color(guicolor_T color)
3886 RGBColor TheColor;
3888 TheColor.red = Red(color) * 0x0101;
3889 TheColor.green = Green(color) * 0x0101;
3890 TheColor.blue = Blue(color) * 0x0101;
3892 RGBForeColor(&TheColor);
3896 * Set the current text background color.
3898 void
3899 gui_mch_set_bg_color(guicolor_T color)
3901 RGBColor TheColor;
3903 TheColor.red = Red(color) * 0x0101;
3904 TheColor.green = Green(color) * 0x0101;
3905 TheColor.blue = Blue(color) * 0x0101;
3907 RGBBackColor(&TheColor);
3910 RGBColor specialColor;
3913 * Set the current text special color.
3915 void
3916 gui_mch_set_sp_color(guicolor_T color)
3918 specialColor.red = Red(color) * 0x0101;
3919 specialColor.green = Green(color) * 0x0101;
3920 specialColor.blue = Blue(color) * 0x0101;
3924 * Draw undercurl at the bottom of the character cell.
3926 static void
3927 draw_undercurl(int flags, int row, int col, int cells)
3929 int x;
3930 int offset;
3931 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3932 int y = FILL_Y(row + 1) - 1;
3934 RGBForeColor(&specialColor);
3936 offset = val[FILL_X(col) % 8];
3937 MoveTo(FILL_X(col), y - offset);
3939 for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3941 offset = val[x % 8];
3942 LineTo(x, y - offset);
3947 static void
3948 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3950 #ifdef FEAT_MBYTE
3951 char_u *tofree = NULL;
3953 if (output_conv.vc_type != CONV_NONE)
3955 tofree = string_convert(&output_conv, s, &len);
3956 if (tofree != NULL)
3957 s = tofree;
3959 #endif
3962 * On OS X, try using Quartz-style text antialiasing.
3964 if (gMacSystemVersion >= 0x1020)
3966 /* Quartz antialiasing is available only in OS 10.2 and later. */
3967 UInt32 qd_flags = (p_antialias ?
3968 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3969 QDSwapTextFlags(qd_flags);
3973 * When antialiasing we're using srcOr mode, we have to clear the block
3974 * before drawing the text.
3975 * Also needed when 'linespace' is non-zero to remove the cursor and
3976 * underlining.
3977 * But not when drawing transparently.
3978 * The following is like calling gui_mch_clear_block(row, col, row, col +
3979 * len - 1), but without setting the bg color to gui.back_pixel.
3981 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3982 && !(flags & DRAW_TRANSP))
3984 Rect rc;
3986 rc.left = FILL_X(col);
3987 rc.top = FILL_Y(row);
3988 #ifdef FEAT_MBYTE
3989 /* Multibyte computation taken from gui_w32.c */
3990 if (has_mbyte)
3992 int cell_len = 0;
3993 int n;
3995 /* Compute the length in display cells. */
3996 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3997 cell_len += (*mb_ptr2cells)(s + n);
3998 rc.right = FILL_X(col + cell_len);
4000 else
4001 #endif
4002 rc.right = FILL_X(col + len) + (col + len == Columns);
4003 rc.bottom = FILL_Y(row + 1);
4004 EraseRect(&rc);
4007 if (gMacSystemVersion >= 0x1020 && p_antialias)
4009 StyleParameter face;
4011 face = normal;
4012 if (flags & DRAW_BOLD)
4013 face |= bold;
4014 if (flags & DRAW_UNDERL)
4015 face |= underline;
4016 TextFace(face);
4018 /* Quartz antialiasing works only in srcOr transfer mode. */
4019 TextMode(srcOr);
4021 MoveTo(TEXT_X(col), TEXT_Y(row));
4022 DrawText((char*)s, 0, len);
4024 else
4026 /* Use old-style, non-antialiased QuickDraw text rendering. */
4027 TextMode(srcCopy);
4028 TextFace(normal);
4030 /* SelectFont(hdc, gui.currFont); */
4032 if (flags & DRAW_TRANSP)
4034 TextMode(srcOr);
4037 MoveTo(TEXT_X(col), TEXT_Y(row));
4038 DrawText((char *)s, 0, len);
4040 if (flags & DRAW_BOLD)
4042 TextMode(srcOr);
4043 MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
4044 DrawText((char *)s, 0, len);
4047 if (flags & DRAW_UNDERL)
4049 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
4050 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
4054 if (flags & DRAW_UNDERC)
4055 draw_undercurl(flags, row, col, len);
4057 #ifdef FEAT_MBYTE
4058 vim_free(tofree);
4059 #endif
4062 #ifdef USE_ATSUI_DRAWING
4064 static void
4065 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
4067 /* ATSUI requires utf-16 strings */
4068 UniCharCount utf16_len;
4069 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
4070 utf16_len /= sizeof(UniChar);
4072 /* - ATSUI automatically antialiases text (Someone)
4073 * - for some reason it does not work... (Jussi) */
4074 #ifdef MAC_ATSUI_DEBUG
4075 fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n",
4076 row, col, len, len == 1 ? s[0] : ' ');
4077 #endif
4079 * When antialiasing we're using srcOr mode, we have to clear the block
4080 * before drawing the text.
4081 * Also needed when 'linespace' is non-zero to remove the cursor and
4082 * underlining.
4083 * But not when drawing transparently.
4084 * The following is like calling gui_mch_clear_block(row, col, row, col +
4085 * len - 1), but without setting the bg color to gui.back_pixel.
4087 if ((flags & DRAW_TRANSP) == 0)
4089 Rect rc;
4091 rc.left = FILL_X(col);
4092 rc.top = FILL_Y(row);
4093 /* Multibyte computation taken from gui_w32.c */
4094 if (has_mbyte)
4096 int cell_len = 0;
4097 int n;
4099 /* Compute the length in display cells. */
4100 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4101 cell_len += (*mb_ptr2cells)(s + n);
4102 rc.right = FILL_X(col + cell_len);
4104 else
4105 rc.right = FILL_X(col + len) + (col + len == Columns);
4107 rc.bottom = FILL_Y(row + 1);
4108 EraseRect(&rc);
4112 TextMode(srcCopy);
4113 TextFace(normal);
4115 /* SelectFont(hdc, gui.currFont); */
4116 if (flags & DRAW_TRANSP)
4118 TextMode(srcOr);
4121 MoveTo(TEXT_X(col), TEXT_Y(row));
4123 if (gFontStyle && flags & DRAW_BOLD)
4125 Boolean attValue = true;
4126 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4127 ByteCount attribSizes[] = { sizeof(Boolean) };
4128 ATSUAttributeValuePtr attribValues[] = { &attValue };
4130 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues);
4133 #ifdef FEAT_MBYTE
4134 if (has_mbyte)
4136 int n, width_in_cell, last_width_in_cell;
4137 UniCharArrayOffset offset = 0;
4138 UniCharCount yet_to_draw = 0;
4139 ATSUTextLayout textLayout;
4140 ATSUStyle textStyle;
4142 last_width_in_cell = 1;
4143 ATSUCreateTextLayout(&textLayout);
4144 ATSUSetTextPointerLocation(textLayout, tofree,
4145 kATSUFromTextBeginning,
4146 kATSUToTextEnd, utf16_len);
4148 ATSUSetRunStyle(textLayout, gFontStyle,
4149 kATSUFromTextBeginning, kATSUToTextEnd); */
4151 /* Compute the length in display cells. */
4152 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4154 width_in_cell = (*mb_ptr2cells)(s + n);
4156 /* probably we are switching from single byte character
4157 * to multibyte characters (which requires more than one
4158 * cell to draw) */
4159 if (width_in_cell != last_width_in_cell)
4161 #ifdef MAC_ATSUI_DEBUG
4162 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4163 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4164 #endif
4165 textStyle = last_width_in_cell > 1 ? gWideFontStyle
4166 : gFontStyle;
4168 ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw);
4169 offset += yet_to_draw;
4170 yet_to_draw = 0;
4171 last_width_in_cell = width_in_cell;
4174 yet_to_draw++;
4177 if (yet_to_draw)
4179 #ifdef MAC_ATSUI_DEBUG
4180 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4181 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4182 #endif
4183 /* finish the rest style */
4184 textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle;
4185 ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd);
4188 ATSUSetTransientFontMatching(textLayout, TRUE);
4189 ATSUDrawText(textLayout,
4190 kATSUFromTextBeginning, kATSUToTextEnd,
4191 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4192 ATSUDisposeTextLayout(textLayout);
4194 else
4195 #endif
4197 ATSUTextLayout textLayout;
4199 if (ATSUCreateTextLayoutWithTextPtr(tofree,
4200 kATSUFromTextBeginning, kATSUToTextEnd,
4201 utf16_len,
4202 (gFontStyle ? 1 : 0), &utf16_len,
4203 (gFontStyle ? &gFontStyle : NULL),
4204 &textLayout) == noErr)
4206 ATSUSetTransientFontMatching(textLayout, TRUE);
4208 ATSUDrawText(textLayout,
4209 kATSUFromTextBeginning, kATSUToTextEnd,
4210 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4212 ATSUDisposeTextLayout(textLayout);
4216 /* drawing is done, now reset bold to normal */
4217 if (gFontStyle && flags & DRAW_BOLD)
4219 Boolean attValue = false;
4221 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4222 ByteCount attribSizes[] = { sizeof(Boolean) };
4223 ATSUAttributeValuePtr attribValues[] = { &attValue };
4225 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes,
4226 attribValues);
4230 if (flags & DRAW_UNDERC)
4231 draw_undercurl(flags, row, col, len);
4233 vim_free(tofree);
4235 #endif
4237 void
4238 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4240 #if defined(USE_ATSUI_DRAWING)
4241 if (p_macatsui == 0 && p_macatsui_last != 0)
4242 /* switch from macatsui to nomacatsui */
4243 gui_mac_dispose_atsui_style();
4244 else if (p_macatsui != 0 && p_macatsui_last == 0)
4245 /* switch from nomacatsui to macatsui */
4246 gui_mac_create_atsui_style();
4248 if (p_macatsui)
4249 draw_string_ATSUI(row, col, s, len, flags);
4250 else
4251 #endif
4252 draw_string_QD(row, col, s, len, flags);
4256 * Return OK if the key with the termcap name "name" is supported.
4259 gui_mch_haskey(char_u *name)
4261 int i;
4263 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4264 if (name[0] == special_keys[i].vim_code0 &&
4265 name[1] == special_keys[i].vim_code1)
4266 return OK;
4267 return FAIL;
4270 void
4271 gui_mch_beep(void)
4273 SysBeep(1); /* Should this be 0? (????) */
4276 void
4277 gui_mch_flash(int msec)
4279 /* Do a visual beep by reversing the foreground and background colors */
4280 Rect rc;
4283 * Note: InvertRect() excludes right and bottom of rectangle.
4285 rc.left = 0;
4286 rc.top = 0;
4287 rc.right = gui.num_cols * gui.char_width;
4288 rc.bottom = gui.num_rows * gui.char_height;
4289 InvertRect(&rc);
4291 ui_delay((long)msec, TRUE); /* wait for some msec */
4293 InvertRect(&rc);
4297 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4299 void
4300 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4302 Rect rc;
4305 * Note: InvertRect() excludes right and bottom of rectangle.
4307 rc.left = FILL_X(c);
4308 rc.top = FILL_Y(r);
4309 rc.right = rc.left + nc * gui.char_width;
4310 rc.bottom = rc.top + nr * gui.char_height;
4311 InvertRect(&rc);
4315 * Iconify the GUI window.
4317 void
4318 gui_mch_iconify(void)
4320 /* TODO: find out what could replace iconify
4321 * -window shade?
4322 * -hide application?
4326 #if defined(FEAT_EVAL) || defined(PROTO)
4328 * Bring the Vim window to the foreground.
4330 void
4331 gui_mch_set_foreground(void)
4333 /* TODO */
4335 #endif
4338 * Draw a cursor without focus.
4340 void
4341 gui_mch_draw_hollow_cursor(guicolor_T color)
4343 Rect rc;
4346 * Note: FrameRect() excludes right and bottom of rectangle.
4348 rc.left = FILL_X(gui.col);
4349 rc.top = FILL_Y(gui.row);
4350 rc.right = rc.left + gui.char_width;
4351 #ifdef FEAT_MBYTE
4352 if (mb_lefthalve(gui.row, gui.col))
4353 rc.right += gui.char_width;
4354 #endif
4355 rc.bottom = rc.top + gui.char_height;
4357 gui_mch_set_fg_color(color);
4359 FrameRect(&rc);
4363 * Draw part of a cursor, only w pixels wide, and h pixels high.
4365 void
4366 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4368 Rect rc;
4370 #ifdef FEAT_RIGHTLEFT
4371 /* vertical line should be on the right of current point */
4372 if (CURSOR_BAR_RIGHT)
4373 rc.left = FILL_X(gui.col + 1) - w;
4374 else
4375 #endif
4376 rc.left = FILL_X(gui.col);
4377 rc.top = FILL_Y(gui.row) + gui.char_height - h;
4378 rc.right = rc.left + w;
4379 rc.bottom = rc.top + h;
4381 gui_mch_set_fg_color(color);
4383 FrameRect(&rc);
4384 // PaintRect(&rc);
4390 * Catch up with any queued X events. This may put keyboard input into the
4391 * input buffer, call resize call-backs, trigger timers etc. If there is
4392 * nothing in the X event queue (& no timers pending), then we return
4393 * immediately.
4395 void
4396 gui_mch_update(void)
4398 /* TODO: find what to do
4399 * maybe call gui_mch_wait_for_chars (0)
4400 * more like look at EventQueue then
4401 * call heart of gui_mch_wait_for_chars;
4403 * if (eventther)
4404 * gui_mac_handle_event(&event);
4406 EventRecord theEvent;
4408 if (EventAvail(everyEvent, &theEvent))
4409 if (theEvent.what != nullEvent)
4410 gui_mch_wait_for_chars(0);
4414 * Simple wrapper to neglect more easily the time
4415 * spent inside WaitNextEvent while profiling.
4418 pascal
4419 Boolean
4420 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4422 if (((long) sleep) < -1)
4423 sleep = 32767;
4424 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4428 * GUI input routine called by gui_wait_for_chars(). Waits for a character
4429 * from the keyboard.
4430 * wtime == -1 Wait forever.
4431 * wtime == 0 This should never happen.
4432 * wtime > 0 Wait wtime milliseconds for a character.
4433 * Returns OK if a character was found to be available within the given time,
4434 * or FAIL otherwise.
4437 gui_mch_wait_for_chars(int wtime)
4439 EventMask mask = (everyEvent);
4440 EventRecord event;
4441 long entryTick;
4442 long currentTick;
4443 long sleeppyTick;
4445 /* If we are providing life feedback with the scrollbar,
4446 * we don't want to try to wait for an event, or else
4447 * there won't be any life feedback.
4449 if (dragged_sb != NULL)
4450 return FAIL;
4451 /* TODO: Check if FAIL is the proper return code */
4453 entryTick = TickCount();
4455 allow_scrollbar = TRUE;
4459 /* if (dragRectControl == kCreateEmpty)
4461 dragRgn = NULL;
4462 dragRectControl = kNothing;
4464 else*/ if (dragRectControl == kCreateRect)
4466 dragRgn = cursorRgn;
4467 RectRgn(dragRgn, &dragRect);
4468 dragRectControl = kNothing;
4471 * Don't use gui_mch_update() because then we will spin-lock until a
4472 * char arrives, instead we use WaitNextEventWrp() to hang until an
4473 * event arrives. No need to check for input_buf_full because we are
4474 * returning as soon as it contains a single char.
4476 /* TODO: reduce wtime accordinly??? */
4477 if (wtime > -1)
4478 sleeppyTick = 60 * wtime / 1000;
4479 else
4480 sleeppyTick = 32767;
4482 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4484 gui_mac_handle_event(&event);
4485 if (input_available())
4487 allow_scrollbar = FALSE;
4488 return OK;
4491 currentTick = TickCount();
4493 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4495 allow_scrollbar = FALSE;
4496 return FAIL;
4500 * Output routines.
4503 /* Flush any output to the screen */
4504 void
4505 gui_mch_flush(void)
4507 /* TODO: Is anything needed here? */
4511 * Clear a rectangular region of the screen from text pos (row1, col1) to
4512 * (row2, col2) inclusive.
4514 void
4515 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4517 Rect rc;
4520 * Clear one extra pixel at the far right, for when bold characters have
4521 * spilled over to the next column.
4523 rc.left = FILL_X(col1);
4524 rc.top = FILL_Y(row1);
4525 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4526 rc.bottom = FILL_Y(row2 + 1);
4528 gui_mch_set_bg_color(gui.back_pixel);
4529 EraseRect(&rc);
4533 * Clear the whole text window.
4535 void
4536 gui_mch_clear_all(void)
4538 Rect rc;
4540 rc.left = 0;
4541 rc.top = 0;
4542 rc.right = Columns * gui.char_width + 2 * gui.border_width;
4543 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4545 gui_mch_set_bg_color(gui.back_pixel);
4546 EraseRect(&rc);
4547 /* gui_mch_set_fg_color(gui.norm_pixel);
4548 FrameRect(&rc);
4553 * Delete the given number of lines from the given row, scrolling up any
4554 * text further down within the scroll region.
4556 void
4557 gui_mch_delete_lines(int row, int num_lines)
4559 Rect rc;
4561 /* changed without checking! */
4562 rc.left = FILL_X(gui.scroll_region_left);
4563 rc.right = FILL_X(gui.scroll_region_right + 1);
4564 rc.top = FILL_Y(row);
4565 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4567 gui_mch_set_bg_color(gui.back_pixel);
4568 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4570 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4571 gui.scroll_region_left,
4572 gui.scroll_region_bot, gui.scroll_region_right);
4576 * Insert the given number of lines before the given row, scrolling down any
4577 * following text within the scroll region.
4579 void
4580 gui_mch_insert_lines(int row, int num_lines)
4582 Rect rc;
4584 rc.left = FILL_X(gui.scroll_region_left);
4585 rc.right = FILL_X(gui.scroll_region_right + 1);
4586 rc.top = FILL_Y(row);
4587 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4589 gui_mch_set_bg_color(gui.back_pixel);
4591 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4593 /* Update gui.cursor_row if the cursor scrolled or copied over */
4594 if (gui.cursor_row >= gui.row
4595 && gui.cursor_col >= gui.scroll_region_left
4596 && gui.cursor_col <= gui.scroll_region_right)
4598 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4599 gui.cursor_row += num_lines;
4600 else if (gui.cursor_row <= gui.scroll_region_bot)
4601 gui.cursor_is_valid = FALSE;
4604 gui_clear_block(row, gui.scroll_region_left,
4605 row + num_lines - 1, gui.scroll_region_right);
4609 * TODO: add a vim format to the clipboard which remember
4610 * LINEWISE, CHARWISE, BLOCKWISE
4613 void
4614 clip_mch_request_selection(VimClipboard *cbd)
4617 Handle textOfClip;
4618 int flavor = 0;
4619 Size scrapSize;
4620 ScrapFlavorFlags scrapFlags;
4621 ScrapRef scrap = nil;
4622 OSStatus error;
4623 int type;
4624 char *searchCR;
4625 char_u *tempclip;
4628 error = GetCurrentScrap(&scrap);
4629 if (error != noErr)
4630 return;
4632 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4633 if (error == noErr)
4635 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4636 if (error == noErr && scrapSize > 1)
4637 flavor = 1;
4640 if (flavor == 0)
4642 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4643 if (error != noErr)
4644 return;
4646 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4647 if (error != noErr)
4648 return;
4651 ReserveMem(scrapSize);
4653 /* In CARBON we don't need a Handle, a pointer is good */
4654 textOfClip = NewHandle(scrapSize);
4656 /* tempclip = lalloc(scrapSize+1, TRUE); */
4657 HLock(textOfClip);
4658 error = GetScrapFlavorData(scrap,
4659 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4660 &scrapSize, *textOfClip);
4661 scrapSize -= flavor;
4663 if (flavor)
4664 type = **textOfClip;
4665 else
4666 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4668 tempclip = lalloc(scrapSize + 1, TRUE);
4669 mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4670 tempclip[scrapSize] = 0;
4672 #ifdef MACOS_CONVERT
4674 /* Convert from utf-16 (clipboard) */
4675 size_t encLen = 0;
4676 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4678 if (to != NULL)
4680 scrapSize = encLen;
4681 vim_free(tempclip);
4682 tempclip = to;
4685 #endif
4687 searchCR = (char *)tempclip;
4688 while (searchCR != NULL)
4690 searchCR = strchr(searchCR, '\r');
4691 if (searchCR != NULL)
4692 *searchCR = '\n';
4695 clip_yank_selection(type, tempclip, scrapSize, cbd);
4697 vim_free(tempclip);
4698 HUnlock(textOfClip);
4700 DisposeHandle(textOfClip);
4703 void
4704 clip_mch_lose_selection(VimClipboard *cbd)
4707 * TODO: Really nothing to do?
4712 clip_mch_own_selection(VimClipboard *cbd)
4714 return OK;
4718 * Send the current selection to the clipboard.
4720 void
4721 clip_mch_set_selection(VimClipboard *cbd)
4723 Handle textOfClip;
4724 long scrapSize;
4725 int type;
4726 ScrapRef scrap;
4728 char_u *str = NULL;
4730 if (!cbd->owned)
4731 return;
4733 clip_get_selection(cbd);
4736 * Once we set the clipboard, lose ownership. If another application sets
4737 * the clipboard, we don't want to think that we still own it.
4739 cbd->owned = FALSE;
4741 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4743 #ifdef MACOS_CONVERT
4744 size_t utf16_len = 0;
4745 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4746 if (to)
4748 scrapSize = utf16_len;
4749 vim_free(str);
4750 str = (char_u *)to;
4752 #endif
4754 if (type >= 0)
4756 ClearCurrentScrap();
4758 textOfClip = NewHandle(scrapSize + 1);
4759 HLock(textOfClip);
4761 **textOfClip = type;
4762 mch_memmove(*textOfClip + 1, str, scrapSize);
4763 GetCurrentScrap(&scrap);
4764 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4765 scrapSize, *textOfClip + 1);
4766 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4767 scrapSize + 1, *textOfClip);
4768 HUnlock(textOfClip);
4769 DisposeHandle(textOfClip);
4772 vim_free(str);
4775 void
4776 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4778 Rect VimBound;
4780 /* HideWindow(gui.VimWindow); */
4781 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4783 if (gui.which_scrollbars[SBAR_LEFT])
4785 VimBound.left = -gui.scrollbar_width + 1;
4787 else
4789 VimBound.left = 0;
4792 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4794 ShowWindow(gui.VimWindow);
4798 * Menu stuff.
4801 void
4802 gui_mch_enable_menu(int flag)
4805 * Menu is always active.
4809 void
4810 gui_mch_set_menu_pos(int x, int y, int w, int h)
4813 * The menu is always at the top of the screen.
4818 * Add a sub menu to the menu bar.
4820 void
4821 gui_mch_add_menu(vimmenu_T *menu, int idx)
4824 * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4825 * TODO: use menu->mnemonic and menu->actext
4826 * TODO: Try to reuse menu id
4827 * Carbon Help suggest to use only id between 1 and 235
4829 static long next_avail_id = 128;
4830 long menu_after_me = 0; /* Default to the end */
4831 #if defined(FEAT_MBYTE)
4832 CFStringRef name;
4833 #else
4834 char_u *name;
4835 #endif
4836 short index;
4837 vimmenu_T *parent = menu->parent;
4838 vimmenu_T *brother = menu->next;
4840 /* Cannot add a menu if ... */
4841 if ((parent != NULL && parent->submenu_id == 0))
4842 return;
4844 /* menu ID greater than 1024 are reserved for ??? */
4845 if (next_avail_id == 1024)
4846 return;
4848 /* My brother could be the PopUp, find my real brother */
4849 while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4850 brother = brother->next;
4852 /* Find where to insert the menu (for MenuBar) */
4853 if ((parent == NULL) && (brother != NULL))
4854 menu_after_me = brother->submenu_id;
4856 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4857 if (!menu_is_menubar(menu->name))
4858 menu_after_me = hierMenu;
4860 /* Convert the name */
4861 #ifdef MACOS_CONVERT
4862 name = menu_title_removing_mnemonic(menu);
4863 #else
4864 name = C2Pascal_save(menu->dname);
4865 #endif
4866 if (name == NULL)
4867 return;
4869 /* Create the menu unless it's the help menu */
4871 /* Carbon suggest use of
4872 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4873 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4875 menu->submenu_id = next_avail_id;
4876 #if defined(FEAT_MBYTE)
4877 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4878 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4879 #else
4880 menu->submenu_handle = NewMenu(menu->submenu_id, name);
4881 #endif
4882 next_avail_id++;
4885 if (parent == NULL)
4887 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4889 /* TODO: Verify if we could only Insert Menu if really part of the
4890 * menubar The Inserted menu are scanned or the Command-key combos
4893 /* Insert the menu */
4894 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4895 #if 1
4896 /* Vim should normally update it. TODO: verify */
4897 DrawMenuBar();
4898 #endif
4900 else
4902 /* Adding as a submenu */
4904 index = gui_mac_get_menu_item_index(menu);
4906 /* Call InsertMenuItem followed by SetMenuItemText
4907 * to avoid special character recognition by InsertMenuItem
4909 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4910 #if defined(FEAT_MBYTE)
4911 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4912 #else
4913 SetMenuItemText(parent->submenu_handle, idx+1, name);
4914 #endif
4915 SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4916 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4917 InsertMenu(menu->submenu_handle, hierMenu);
4920 #if defined(FEAT_MBYTE)
4921 CFRelease(name);
4922 #else
4923 vim_free(name);
4924 #endif
4926 #if 0
4927 /* Done by Vim later on */
4928 DrawMenuBar();
4929 #endif
4933 * Add a menu item to a menu
4935 void
4936 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4938 #if defined(FEAT_MBYTE)
4939 CFStringRef name;
4940 #else
4941 char_u *name;
4942 #endif
4943 vimmenu_T *parent = menu->parent;
4944 int menu_inserted;
4946 /* Cannot add item, if the menu have not been created */
4947 if (parent->submenu_id == 0)
4948 return;
4950 /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4951 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4953 /* Convert the name */
4954 #ifdef MACOS_CONVERT
4955 name = menu_title_removing_mnemonic(menu);
4956 #else
4957 name = C2Pascal_save(menu->dname);
4958 #endif
4960 /* Where are just a menu item, so no handle, no id */
4961 menu->submenu_id = 0;
4962 menu->submenu_handle = NULL;
4964 menu_inserted = 0;
4965 if (menu->actext)
4967 /* If the accelerator text for the menu item looks like it describes
4968 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4969 * item's command equivalent.
4971 int key = 0;
4972 int modifiers = 0;
4973 char_u *p_actext;
4975 p_actext = menu->actext;
4976 key = find_special_key(&p_actext, &modifiers, /*keycode=*/0);
4977 if (*p_actext != 0)
4978 key = 0; /* error: trailing text */
4979 /* find_special_key() returns a keycode with as many of the
4980 * specified modifiers as appropriate already applied (e.g., for
4981 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4982 * as the only modifier). Since we want to display all of the
4983 * modifiers, we need to convert the keycode back to a printable
4984 * character plus modifiers.
4985 * TODO: Write an alternative find_special_key() that doesn't
4986 * apply modifiers.
4988 if (key > 0 && key < 32)
4990 /* Convert a control key to an uppercase letter. Note that
4991 * by this point it is no longer possible to distinguish
4992 * between, e.g., Ctrl-S and Ctrl-Shift-S.
4994 modifiers |= MOD_MASK_CTRL;
4995 key += '@';
4997 /* If the keycode is an uppercase letter, set the Shift modifier.
4998 * If it is a lowercase letter, don't set the modifier, but convert
4999 * the letter to uppercase for display in the menu.
5001 else if (key >= 'A' && key <= 'Z')
5002 modifiers |= MOD_MASK_SHIFT;
5003 else if (key >= 'a' && key <= 'z')
5004 key += 'A' - 'a';
5005 /* Note: keycodes below 0x22 are reserved by Apple. */
5006 if (key >= 0x22 && vim_isprintc_strict(key))
5008 int valid = 1;
5009 char_u mac_mods = kMenuNoModifiers;
5010 /* Convert Vim modifier codes to Menu Manager equivalents. */
5011 if (modifiers & MOD_MASK_SHIFT)
5012 mac_mods |= kMenuShiftModifier;
5013 if (modifiers & MOD_MASK_CTRL)
5014 mac_mods |= kMenuControlModifier;
5015 if (!(modifiers & MOD_MASK_CMD))
5016 mac_mods |= kMenuNoCommandModifier;
5017 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
5018 valid = 0; /* TODO: will Alt someday map to Option? */
5019 if (valid)
5021 char_u item_txt[10];
5022 /* Insert the menu item after idx, with its command key. */
5023 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
5024 item_txt[3] = key;
5025 InsertMenuItem(parent->submenu_handle, item_txt, idx);
5026 /* Set the modifier keys. */
5027 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
5028 menu_inserted = 1;
5032 /* Call InsertMenuItem followed by SetMenuItemText
5033 * to avoid special character recognition by InsertMenuItem
5035 if (!menu_inserted)
5036 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
5037 /* Set the menu item name. */
5038 #if defined(FEAT_MBYTE)
5039 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
5040 #else
5041 SetMenuItemText(parent->submenu_handle, idx+1, name);
5042 #endif
5044 #if 0
5045 /* Called by Vim */
5046 DrawMenuBar();
5047 #endif
5049 #if defined(FEAT_MBYTE)
5050 CFRelease(name);
5051 #else
5052 /* TODO: Can name be freed? */
5053 vim_free(name);
5054 #endif
5057 void
5058 gui_mch_toggle_tearoffs(int enable)
5060 /* no tearoff menus */
5064 * Destroy the machine specific menu widget.
5066 void
5067 gui_mch_destroy_menu(vimmenu_T *menu)
5069 short index = gui_mac_get_menu_item_index(menu);
5071 if (index > 0)
5073 if (menu->parent)
5076 /* For now just don't delete help menu items. (Huh? Dany) */
5077 DeleteMenuItem(menu->parent->submenu_handle, index);
5079 /* Delete the Menu if it was a hierarchical Menu */
5080 if (menu->submenu_id != 0)
5082 DeleteMenu(menu->submenu_id);
5083 DisposeMenu(menu->submenu_handle);
5087 #ifdef DEBUG_MAC_MENU
5088 else
5090 printf("gmdm 2\n");
5092 #endif
5094 else
5097 DeleteMenu(menu->submenu_id);
5098 DisposeMenu(menu->submenu_handle);
5101 /* Shouldn't this be already done by Vim. TODO: Check */
5102 DrawMenuBar();
5106 * Make a menu either grey or not grey.
5108 void
5109 gui_mch_menu_grey(vimmenu_T *menu, int grey)
5111 /* TODO: Check if menu really exists */
5112 short index = gui_mac_get_menu_item_index(menu);
5114 index = menu->index;
5116 if (grey)
5118 if (menu->children)
5119 DisableMenuItem(menu->submenu_handle, index);
5120 if (menu->parent)
5121 if (menu->parent->submenu_handle)
5122 DisableMenuItem(menu->parent->submenu_handle, index);
5124 else
5126 if (menu->children)
5127 EnableMenuItem(menu->submenu_handle, index);
5128 if (menu->parent)
5129 if (menu->parent->submenu_handle)
5130 EnableMenuItem(menu->parent->submenu_handle, index);
5135 * Make menu item hidden or not hidden
5137 void
5138 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5140 /* There's no hidden mode on MacOS */
5141 gui_mch_menu_grey(menu, hidden);
5146 * This is called after setting all the menus to grey/hidden or not.
5148 void
5149 gui_mch_draw_menubar(void)
5151 DrawMenuBar();
5156 * Scrollbar stuff.
5159 void
5160 gui_mch_enable_scrollbar(
5161 scrollbar_T *sb,
5162 int flag)
5164 if (flag)
5165 ShowControl(sb->id);
5166 else
5167 HideControl(sb->id);
5169 #ifdef DEBUG_MAC_SB
5170 printf("enb_sb (%x) %x\n",sb->id, flag);
5171 #endif
5174 void
5175 gui_mch_set_scrollbar_thumb(
5176 scrollbar_T *sb,
5177 long val,
5178 long size,
5179 long max)
5181 SetControl32BitMaximum (sb->id, max);
5182 SetControl32BitMinimum (sb->id, 0);
5183 SetControl32BitValue (sb->id, val);
5184 SetControlViewSize (sb->id, size);
5185 #ifdef DEBUG_MAC_SB
5186 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
5187 #endif
5190 void
5191 gui_mch_set_scrollbar_pos(
5192 scrollbar_T *sb,
5193 int x,
5194 int y,
5195 int w,
5196 int h)
5198 gui_mch_set_bg_color(gui.back_pixel);
5199 /* if (gui.which_scrollbars[SBAR_LEFT])
5201 MoveControl(sb->id, x-16, y);
5202 SizeControl(sb->id, w + 1, h);
5204 else
5206 MoveControl(sb->id, x, y);
5207 SizeControl(sb->id, w + 1, h);
5209 if (sb == &gui.bottom_sbar)
5210 h += 1;
5211 else
5212 w += 1;
5214 if (gui.which_scrollbars[SBAR_LEFT])
5215 x -= 15;
5217 MoveControl(sb->id, x, y);
5218 SizeControl(sb->id, w, h);
5219 #ifdef DEBUG_MAC_SB
5220 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
5221 #endif
5224 void
5225 gui_mch_create_scrollbar(
5226 scrollbar_T *sb,
5227 int orient) /* SBAR_VERT or SBAR_HORIZ */
5229 Rect bounds;
5231 bounds.top = -16;
5232 bounds.bottom = -10;
5233 bounds.right = -10;
5234 bounds.left = -16;
5236 sb->id = NewControl(gui.VimWindow,
5237 &bounds,
5238 "\pScrollBar",
5239 TRUE,
5240 0, /* current*/
5241 0, /* top */
5242 0, /* bottom */
5243 kControlScrollBarLiveProc,
5244 (long) sb->ident);
5245 #ifdef DEBUG_MAC_SB
5246 printf("create_sb (%x) %x\n",sb->id, orient);
5247 #endif
5250 void
5251 gui_mch_destroy_scrollbar(scrollbar_T *sb)
5253 gui_mch_set_bg_color(gui.back_pixel);
5254 DisposeControl(sb->id);
5255 #ifdef DEBUG_MAC_SB
5256 printf("dest_sb (%x) \n",sb->id);
5257 #endif
5262 * Cursor blink functions.
5264 * This is a simple state machine:
5265 * BLINK_NONE not blinking at all
5266 * BLINK_OFF blinking, cursor is not shown
5267 * BLINK_ON blinking, cursor is shown
5269 void
5270 gui_mch_set_blinking(long wait, long on, long off)
5272 /* TODO: TODO: TODO: TODO: */
5273 /* blink_waittime = wait;
5274 blink_ontime = on;
5275 blink_offtime = off;*/
5279 * Stop the cursor blinking. Show the cursor if it wasn't shown.
5281 void
5282 gui_mch_stop_blink(void)
5284 gui_update_cursor(TRUE, FALSE);
5285 /* TODO: TODO: TODO: TODO: */
5286 /* gui_w32_rm_blink_timer();
5287 if (blink_state == BLINK_OFF)
5288 gui_update_cursor(TRUE, FALSE);
5289 blink_state = BLINK_NONE;*/
5293 * Start the cursor blinking. If it was already blinking, this restarts the
5294 * waiting time and shows the cursor.
5296 void
5297 gui_mch_start_blink(void)
5299 gui_update_cursor(TRUE, FALSE);
5300 /* TODO: TODO: TODO: TODO: */
5301 /* gui_w32_rm_blink_timer(); */
5303 /* Only switch blinking on if none of the times is zero */
5304 /* if (blink_waittime && blink_ontime && blink_offtime)
5306 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5307 (TIMERPROC)_OnBlinkTimer);
5308 blink_state = BLINK_ON;
5309 gui_update_cursor(TRUE, FALSE);
5314 * Return the RGB value of a pixel as long.
5316 long_u
5317 gui_mch_get_rgb(guicolor_T pixel)
5319 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5324 #ifdef FEAT_BROWSE
5326 * Pop open a file browser and return the file selected, in allocated memory,
5327 * or NULL if Cancel is hit.
5328 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
5329 * title - Title message for the file browser dialog.
5330 * dflt - Default name of file.
5331 * ext - Default extension to be added to files without extensions.
5332 * initdir - directory in which to open the browser (NULL = current dir)
5333 * filter - Filter for matched files to choose from.
5334 * Has a format like this:
5335 * "C Files (*.c)\0*.c\0"
5336 * "All Files\0*.*\0\0"
5337 * If these two strings were concatenated, then a choice of two file
5338 * filters will be selectable to the user. Then only matching files will
5339 * be shown in the browser. If NULL, the default allows all files.
5341 * *NOTE* - the filter string must be terminated with TWO nulls.
5343 char_u *
5344 gui_mch_browse(
5345 int saving,
5346 char_u *title,
5347 char_u *dflt,
5348 char_u *ext,
5349 char_u *initdir,
5350 char_u *filter)
5352 /* TODO: Add Ammon's safety checl (Dany) */
5353 NavReplyRecord reply;
5354 char_u *fname = NULL;
5355 char_u **fnames = NULL;
5356 long numFiles;
5357 NavDialogOptions navOptions;
5358 OSErr error;
5360 /* Get Navigation Service Defaults value */
5361 NavGetDefaultDialogOptions(&navOptions);
5364 /* TODO: If we get a :browse args, set the Multiple bit. */
5365 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles
5366 | kNavDontAutoTranslate
5367 | kNavDontAddTranslateItems
5368 /* | kNavAllowMultipleFiles */
5369 | kNavAllowStationery;
5371 (void) C2PascalString(title, &navOptions.message);
5372 (void) C2PascalString(dflt, &navOptions.savedFileName);
5373 /* Could set clientName?
5374 * windowTitle? (there's no title bar?)
5377 if (saving)
5379 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5380 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5381 if (!reply.validRecord)
5382 return NULL;
5384 else
5386 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5387 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5388 if (!reply.validRecord)
5389 return NULL;
5392 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5394 NavDisposeReply(&reply);
5396 if (fnames)
5398 fname = fnames[0];
5399 vim_free(fnames);
5402 /* TODO: Shorten the file name if possible */
5403 return fname;
5405 #endif /* FEAT_BROWSE */
5407 #ifdef FEAT_GUI_DIALOG
5409 * Stuff for dialogues
5413 * Create a dialogue dynamically from the parameter strings.
5414 * type = type of dialogue (question, alert, etc.)
5415 * title = dialogue title. may be NULL for default title.
5416 * message = text to display. Dialogue sizes to accommodate it.
5417 * buttons = '\n' separated list of button captions, default first.
5418 * dfltbutton = number of default button.
5420 * This routine returns 1 if the first button is pressed,
5421 * 2 for the second, etc.
5423 * 0 indicates Esc was pressed.
5424 * -1 for unexpected error
5426 * If stubbing out this fn, return 1.
5429 typedef struct
5431 short idx;
5432 short width; /* Size of the text in pixel */
5433 Rect box;
5434 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5436 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5438 static void
5439 macMoveDialogItem(
5440 DialogRef theDialog,
5441 short itemNumber,
5442 short X,
5443 short Y,
5444 Rect *inBox)
5446 #if 0 /* USE_CARBONIZED */
5447 /* Untested */
5448 MoveDialogItem(theDialog, itemNumber, X, Y);
5449 if (inBox != nil)
5450 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5451 #else
5452 short itemType;
5453 Handle itemHandle;
5454 Rect localBox;
5455 Rect *itemBox = &localBox;
5457 if (inBox != nil)
5458 itemBox = inBox;
5460 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5461 OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5462 OffsetRect(itemBox, X, Y);
5463 /* To move a control (like a button) we need to call both
5464 * MoveControl and SetDialogItem. FAQ 6-18 */
5465 if (1) /*(itemType & kControlDialogItem) */
5466 MoveControl((ControlRef) itemHandle, X, Y);
5467 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5468 #endif
5471 static void
5472 macSizeDialogItem(
5473 DialogRef theDialog,
5474 short itemNumber,
5475 short width,
5476 short height)
5478 short itemType;
5479 Handle itemHandle;
5480 Rect itemBox;
5482 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5484 /* When width or height is zero do not change it */
5485 if (width == 0)
5486 width = itemBox.right - itemBox.left;
5487 if (height == 0)
5488 height = itemBox.bottom - itemBox.top;
5490 #if 0 /* USE_CARBONIZED */
5491 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5492 #else
5493 /* Resize the bounding box */
5494 itemBox.right = itemBox.left + width;
5495 itemBox.bottom = itemBox.top + height;
5497 /* To resize a control (like a button) we need to call both
5498 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5499 if (itemType & kControlDialogItem)
5500 SizeControl((ControlRef) itemHandle, width, height);
5502 /* Configure back the item */
5503 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5504 #endif
5507 static void
5508 macSetDialogItemText(
5509 DialogRef theDialog,
5510 short itemNumber,
5511 Str255 itemName)
5513 short itemType;
5514 Handle itemHandle;
5515 Rect itemBox;
5517 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5519 if (itemType & kControlDialogItem)
5520 SetControlTitle((ControlRef) itemHandle, itemName);
5521 else
5522 SetDialogItemText(itemHandle, itemName);
5526 /* ModalDialog() handler for message dialogs that have hotkey accelerators.
5527 * Expects a mapping of hotkey char to control index in gDialogHotKeys;
5528 * setting gDialogHotKeys to NULL disables any hotkey handling.
5530 static pascal Boolean
5531 DialogHotkeyFilterProc (
5532 DialogRef theDialog,
5533 EventRecord *event,
5534 DialogItemIndex *itemHit)
5536 char_u keyHit;
5538 if (event->what == keyDown || event->what == autoKey)
5540 keyHit = (event->message & charCodeMask);
5542 if (gDialogHotKeys && gDialogHotKeys[keyHit])
5544 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5545 printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]);
5546 #endif
5547 *itemHit = gDialogHotKeys[keyHit];
5549 /* When handing off to StdFilterProc, pretend that the user
5550 * clicked the control manually. Note that this is also supposed
5551 * to cause the button to hilite briefly (to give some user
5552 * feedback), but this seems not to actually work (or it's too
5553 * fast to be seen).
5555 event->what = kEventControlSimulateHit;
5557 return true; /* we took care of it */
5560 /* Defer to the OS's standard behavior for this event.
5561 * This ensures that Enter will still activate the default button. */
5562 return StdFilterProc(theDialog, event, itemHit);
5564 return false; /* Let ModalDialog deal with it */
5568 /* TODO: There have been some crashes with dialogs, check your inbox
5569 * (Jussi)
5572 gui_mch_dialog(
5573 int type,
5574 char_u *title,
5575 char_u *message,
5576 char_u *buttons,
5577 int dfltbutton,
5578 char_u *textfield)
5580 Handle buttonDITL;
5581 Handle iconDITL;
5582 Handle inputDITL;
5583 Handle messageDITL;
5584 Handle itemHandle;
5585 Handle iconHandle;
5586 DialogPtr theDialog;
5587 char_u len;
5588 char_u PascalTitle[256]; /* place holder for the title */
5589 char_u name[256];
5590 GrafPtr oldPort;
5591 short itemHit;
5592 char_u *buttonChar;
5593 short hotKeys[256]; /* map of hotkey -> control ID */
5594 char_u aHotKey;
5595 Rect box;
5596 short button;
5597 short lastButton;
5598 short itemType;
5599 short useIcon;
5600 short width;
5601 short totalButtonWidth = 0; /* the width of all buttons together
5602 including spacing */
5603 short widestButton = 0;
5604 short dfltButtonEdge = 20; /* gut feeling */
5605 short dfltElementSpacing = 13; /* from IM:V.2-29 */
5606 short dfltIconSideSpace = 23; /* from IM:V.2-29 */
5607 short maximumWidth = 400; /* gut feeling */
5608 short maxButtonWidth = 175; /* gut feeling */
5610 short vertical;
5611 short dialogHeight;
5612 short messageLines = 3;
5613 FontInfo textFontInfo;
5615 vgmDlgItm iconItm;
5616 vgmDlgItm messageItm;
5617 vgmDlgItm inputItm;
5618 vgmDlgItm buttonItm;
5620 WindowRef theWindow;
5622 ModalFilterUPP dialogUPP;
5624 /* Check 'v' flag in 'guioptions': vertical button placement. */
5625 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5627 /* Create a new Dialog Box from template. */
5628 theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5630 /* Get the WindowRef */
5631 theWindow = GetDialogWindow(theDialog);
5633 /* Hide the window.
5634 * 1. to avoid seeing slow drawing
5635 * 2. to prevent a problem seen while moving dialog item
5636 * within a visible window. (non-Carbon MacOS 9)
5637 * Could be avoided by changing the resource.
5639 HideWindow(theWindow);
5641 /* Change the graphical port to the dialog,
5642 * so we can measure the text with the proper font */
5643 GetPort(&oldPort);
5644 SetPortDialogPort(theDialog);
5646 /* Get the info about the default text,
5647 * used to calculate the height of the message
5648 * and of the text field */
5649 GetFontInfo(&textFontInfo);
5651 /* Set the dialog title */
5652 if (title != NULL)
5654 (void) C2PascalString(title, &PascalTitle);
5655 SetWTitle(theWindow, PascalTitle);
5658 /* Creates the buttons and add them to the Dialog Box. */
5659 buttonDITL = GetResource('DITL', 130);
5660 buttonChar = buttons;
5661 button = 0;
5663 /* initialize the hotkey mapping */
5664 memset(hotKeys, 0, sizeof(hotKeys));
5666 for (;*buttonChar != 0;)
5668 /* Get the name of the button */
5669 button++;
5670 len = 0;
5671 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5673 if (*buttonChar != DLG_HOTKEY_CHAR)
5674 name[++len] = *buttonChar;
5675 else
5677 aHotKey = (char_u)*(buttonChar+1);
5678 if (aHotKey >= 'A' && aHotKey <= 'Z')
5679 aHotKey = (char_u)((int)aHotKey + (int)'a' - (int)'A');
5680 hotKeys[aHotKey] = button;
5681 #ifdef DEBUG_MAC_DIALOG_HOTKEYS
5682 printf("### hotKey for button %d is '%c'\n", button, aHotKey);
5683 #endif
5687 if (*buttonChar != 0)
5688 buttonChar++;
5689 name[0] = len;
5691 /* Add the button */
5692 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5694 /* Change the button's name */
5695 macSetDialogItemText(theDialog, button, name);
5697 /* Resize the button to fit its name */
5698 width = StringWidth(name) + 2 * dfltButtonEdge;
5699 /* Limite the size of any button to an acceptable value. */
5700 /* TODO: Should be based on the message width */
5701 if (width > maxButtonWidth)
5702 width = maxButtonWidth;
5703 macSizeDialogItem(theDialog, button, width, 0);
5705 totalButtonWidth += width;
5707 if (width > widestButton)
5708 widestButton = width;
5710 ReleaseResource(buttonDITL);
5711 lastButton = button;
5713 /* Add the icon to the Dialog Box. */
5714 iconItm.idx = lastButton + 1;
5715 iconDITL = GetResource('DITL', 131);
5716 switch (type)
5718 case VIM_GENERIC: useIcon = kNoteIcon;
5719 case VIM_ERROR: useIcon = kStopIcon;
5720 case VIM_WARNING: useIcon = kCautionIcon;
5721 case VIM_INFO: useIcon = kNoteIcon;
5722 case VIM_QUESTION: useIcon = kNoteIcon;
5723 default: useIcon = kStopIcon;
5725 AppendDITL(theDialog, iconDITL, overlayDITL);
5726 ReleaseResource(iconDITL);
5727 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5728 /* TODO: Should the item be freed? */
5729 iconHandle = GetIcon(useIcon);
5730 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5732 /* Add the message to the Dialog box. */
5733 messageItm.idx = lastButton + 2;
5734 messageDITL = GetResource('DITL', 132);
5735 AppendDITL(theDialog, messageDITL, overlayDITL);
5736 ReleaseResource(messageDITL);
5737 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5738 (void) C2PascalString(message, &name);
5739 SetDialogItemText(itemHandle, name);
5740 messageItm.width = StringWidth(name);
5742 /* Add the input box if needed */
5743 if (textfield != NULL)
5745 /* Cheat for now reuse the message and convert to text edit */
5746 inputItm.idx = lastButton + 3;
5747 inputDITL = GetResource('DITL', 132);
5748 AppendDITL(theDialog, inputDITL, overlayDITL);
5749 ReleaseResource(inputDITL);
5750 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5751 /* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5752 (void) C2PascalString(textfield, &name);
5753 SetDialogItemText(itemHandle, name);
5754 inputItm.width = StringWidth(name);
5756 /* Hotkeys don't make sense if there's a text field */
5757 gDialogHotKeys = NULL;
5759 else
5760 /* Install hotkey table */
5761 gDialogHotKeys = (short *)&hotKeys;
5763 /* Set the <ENTER> and <ESC> button. */
5764 SetDialogDefaultItem(theDialog, dfltbutton);
5765 SetDialogCancelItem(theDialog, 0);
5767 /* Reposition element */
5769 /* Check if we need to force vertical */
5770 if (totalButtonWidth > maximumWidth)
5771 vertical = TRUE;
5773 /* Place icon */
5774 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5775 iconItm.box.right = box.right;
5776 iconItm.box.bottom = box.bottom;
5778 /* Place Message */
5779 messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5780 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent));
5781 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5783 /* Place Input */
5784 if (textfield != NULL)
5786 inputItm.box.left = messageItm.box.left;
5787 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5788 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5789 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5790 /* Convert the static text into a text edit.
5791 * For some reason this change need to be done last (Dany) */
5792 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5793 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5794 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5797 /* Place Button */
5798 if (textfield != NULL)
5800 buttonItm.box.left = inputItm.box.left;
5801 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing;
5803 else
5805 buttonItm.box.left = messageItm.box.left;
5806 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5809 for (button=1; button <= lastButton; button++)
5812 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5813 /* With vertical, it's better to have all buttons the same length */
5814 if (vertical)
5816 macSizeDialogItem(theDialog, button, widestButton, 0);
5817 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5819 /* Calculate position of next button */
5820 if (vertical)
5821 buttonItm.box.top = box.bottom + dfltElementSpacing;
5822 else
5823 buttonItm.box.left = box.right + dfltElementSpacing;
5826 /* Resize the dialog box */
5827 dialogHeight = box.bottom + dfltElementSpacing;
5828 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5830 /* Magic resize */
5831 AutoSizeDialog(theDialog);
5832 /* Need a horizontal resize anyway so not that useful */
5834 /* Display it */
5835 ShowWindow(theWindow);
5836 /* BringToFront(theWindow); */
5837 SelectWindow(theWindow);
5839 /* DrawDialog(theDialog); */
5840 #if 0
5841 GetPort(&oldPort);
5842 SetPortDialogPort(theDialog);
5843 #endif
5845 #ifdef USE_CARBONKEYHANDLER
5846 /* Avoid that we use key events for the main window. */
5847 dialog_busy = TRUE;
5848 #endif
5850 /* Prepare the shortcut-handling filterProc for handing to the dialog */
5851 dialogUPP = NewModalFilterUPP(DialogHotkeyFilterProc);
5853 /* Hang until one of the button is hit */
5856 ModalDialog(dialogUPP, &itemHit);
5857 } while ((itemHit < 1) || (itemHit > lastButton));
5859 #ifdef USE_CARBONKEYHANDLER
5860 dialog_busy = FALSE;
5861 #endif
5863 /* Copy back the text entered by the user into the param */
5864 if (textfield != NULL)
5866 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5867 GetDialogItemText(itemHandle, (char_u *) &name);
5868 #if IOSIZE < 256
5869 /* Truncate the name to IOSIZE if needed */
5870 if (name[0] > IOSIZE)
5871 name[0] = IOSIZE - 1;
5872 #endif
5873 vim_strncpy(textfield, &name[1], name[0]);
5876 /* Restore the original graphical port */
5877 SetPort(oldPort);
5879 /* Free the modal filterProc */
5880 DisposeRoutineDescriptor(dialogUPP);
5882 /* Get ride of th edialog (free memory) */
5883 DisposeDialog(theDialog);
5885 return itemHit;
5887 * Usefull thing which could be used
5888 * SetDialogTimeout(): Auto click a button after timeout
5889 * SetDialogTracksCursor() : Get the I-beam cursor over input box
5890 * MoveDialogItem(): Probably better than SetDialogItem
5891 * SizeDialogItem(): (but is it Carbon Only?)
5892 * AutoSizeDialog(): Magic resize of dialog based on text length
5895 #endif /* FEAT_DIALOG_GUI */
5898 * Display the saved error message(s).
5900 #ifdef USE_MCH_ERRMSG
5901 void
5902 display_errors(void)
5904 char *p;
5905 char_u pError[256];
5907 if (error_ga.ga_data == NULL)
5908 return;
5910 /* avoid putting up a message box with blanks only */
5911 for (p = (char *)error_ga.ga_data; *p; ++p)
5912 if (!isspace(*p))
5914 if (STRLEN(p) > 255)
5915 pError[0] = 255;
5916 else
5917 pError[0] = STRLEN(p);
5919 STRNCPY(&pError[1], p, pError[0]);
5920 ParamText(pError, nil, nil, nil);
5921 Alert(128, nil);
5922 break;
5923 /* TODO: handled message longer than 256 chars
5924 * use auto-sizeable alert
5925 * or dialog with scrollbars (TextEdit zone)
5928 ga_clear(&error_ga);
5930 #endif
5933 * Get current mouse coordinates in text window.
5935 void
5936 gui_mch_getmouse(int *x, int *y)
5938 Point where;
5940 GetMouse(&where);
5942 *x = where.h;
5943 *y = where.v;
5946 void
5947 gui_mch_setmouse(int x, int y)
5949 /* TODO */
5950 #if 0
5951 /* From FAQ 3-11 */
5953 CursorDevicePtr myMouse;
5954 Point where;
5956 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5957 != NGetTrapAddress(_Unimplemented, ToolTrap))
5959 /* New way */
5962 * Get first devoice with one button.
5963 * This will probably be the standad mouse
5964 * startat head of cursor dev list
5968 myMouse = nil;
5972 /* Get the next cursor device */
5973 CursorDeviceNextDevice(&myMouse);
5975 while ((myMouse != nil) && (myMouse->cntButtons != 1));
5977 CursorDeviceMoveTo(myMouse, x, y);
5979 else
5981 /* Old way */
5982 where.h = x;
5983 where.v = y;
5985 *(Point *)RawMouse = where;
5986 *(Point *)MTemp = where;
5987 *(Ptr) CrsrNew = 0xFFFF;
5989 #endif
5992 void
5993 gui_mch_show_popupmenu(vimmenu_T *menu)
5996 * Clone PopUp to use menu
5997 * Create a object descriptor for the current selection
5998 * Call the procedure
6001 MenuHandle CntxMenu;
6002 Point where;
6003 OSStatus status;
6004 UInt32 CntxType;
6005 SInt16 CntxMenuID;
6006 UInt16 CntxMenuItem;
6007 Str255 HelpName = "";
6008 GrafPtr savePort;
6010 /* Save Current Port: On MacOS X we seem to lose the port */
6011 GetPort(&savePort); /*OSX*/
6013 GetMouse(&where);
6014 LocalToGlobal(&where); /*OSX*/
6015 CntxMenu = menu->submenu_handle;
6017 /* TODO: Get the text selection from Vim */
6019 /* Call to Handle Popup */
6020 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
6021 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
6023 if (status == noErr)
6025 if (CntxType == kCMMenuItemSelected)
6027 /* Handle the menu CntxMenuID, CntxMenuItem */
6028 /* The submenu can be handle directly by gui_mac_handle_menu */
6029 /* But what about the current menu, is the menu changed by
6030 * ContextualMenuSelect */
6031 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
6033 else if (CntxMenuID == kCMShowHelpSelected)
6035 /* Should come up with the help */
6039 /* Restore original Port */
6040 SetPort(savePort); /*OSX*/
6043 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
6044 /* TODO: Is it need for MACOS_X? (Dany) */
6045 void
6046 mch_post_buffer_write(buf_T *buf)
6048 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
6049 Send_KAHL_MOD_AE(buf);
6051 #endif
6053 #ifdef FEAT_TITLE
6055 * Set the window title and icon.
6056 * (The icon is not taken care of).
6058 void
6059 gui_mch_settitle(char_u *title, char_u *icon)
6061 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
6062 * that 256. Even better get it to fit nicely in the titlebar.
6064 #ifdef MACOS_CONVERT
6065 CFStringRef windowTitle;
6066 size_t windowTitleLen;
6067 #else
6068 char_u *pascalTitle;
6069 #endif
6071 if (title == NULL) /* nothing to do */
6072 return;
6074 #ifdef MACOS_CONVERT
6075 windowTitleLen = STRLEN(title);
6076 windowTitle = (CFStringRef)mac_enc_to_cfstring(title, windowTitleLen);
6078 if (windowTitle)
6080 SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
6081 CFRelease(windowTitle);
6083 #else
6084 pascalTitle = C2Pascal_save(title);
6085 if (pascalTitle != NULL)
6087 SetWTitle(gui.VimWindow, pascalTitle);
6088 vim_free(pascalTitle);
6090 #endif
6092 #endif
6095 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
6099 C2PascalString(char_u *CString, Str255 *PascalString)
6101 char_u *PascalPtr = (char_u *) PascalString;
6102 int len;
6103 int i;
6105 PascalPtr[0] = 0;
6106 if (CString == NULL)
6107 return 0;
6109 len = STRLEN(CString);
6110 if (len > 255)
6111 len = 255;
6113 for (i = 0; i < len; i++)
6114 PascalPtr[i+1] = CString[i];
6116 PascalPtr[0] = len;
6118 return 0;
6122 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
6124 /* From FAQ 8-12 */
6125 Str255 filePascal;
6126 CInfoPBRec myCPB;
6127 OSErr err;
6129 (void) C2PascalString(file, &filePascal);
6131 myCPB.dirInfo.ioNamePtr = filePascal;
6132 myCPB.dirInfo.ioVRefNum = 0;
6133 myCPB.dirInfo.ioFDirIndex = 0;
6134 myCPB.dirInfo.ioDrDirID = 0;
6136 err= PBGetCatInfo(&myCPB, false);
6138 /* vRefNum, dirID, name */
6139 FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
6141 /* TODO: Use an error code mechanism */
6142 return 0;
6146 * Convert a FSSpec to a fuill path
6149 char_u *FullPathFromFSSpec_save(FSSpec file)
6152 * TODO: Add protection for 256 char max.
6155 CInfoPBRec theCPB;
6156 char_u fname[256];
6157 char_u *filenamePtr = fname;
6158 OSErr error;
6159 int folder = 1;
6160 #ifdef USE_UNIXFILENAME
6161 SInt16 dfltVol_vRefNum;
6162 SInt32 dfltVol_dirID;
6163 FSRef refFile;
6164 OSStatus status;
6165 UInt32 pathSize = 256;
6166 char_u pathname[256];
6167 char_u *path = pathname;
6168 #else
6169 Str255 directoryName;
6170 char_u temporary[255];
6171 char_u *temporaryPtr = temporary;
6172 #endif
6174 #ifdef USE_UNIXFILENAME
6175 /* Get the default volume */
6176 /* TODO: Remove as this only work if Vim is on the Boot Volume*/
6177 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
6179 if (error)
6180 return NULL;
6181 #endif
6183 /* Start filling fname with file.name */
6184 vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
6186 /* Get the info about the file specified in FSSpec */
6187 theCPB.dirInfo.ioFDirIndex = 0;
6188 theCPB.dirInfo.ioNamePtr = file.name;
6189 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6190 /*theCPB.hFileInfo.ioDirID = 0;*/
6191 theCPB.dirInfo.ioDrDirID = file.parID;
6193 /* As ioFDirIndex = 0, get the info of ioNamePtr,
6194 which is relative to ioVrefNum, ioDirID */
6195 error = PBGetCatInfo(&theCPB, false);
6197 /* If we are called for a new file we expect fnfErr */
6198 if ((error) && (error != fnfErr))
6199 return NULL;
6201 /* Check if it's a file or folder */
6202 /* default to file if file don't exist */
6203 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
6204 folder = 0; /* It's not a folder */
6205 else
6206 folder = 1;
6208 #ifdef USE_UNIXFILENAME
6210 * The function used here are available in Carbon, but
6211 * do nothing une MacOS 8 and 9
6213 if (error == fnfErr)
6215 /* If the file to be saved does not already exist, it isn't possible
6216 to convert its FSSpec into an FSRef. But we can construct an
6217 FSSpec for the file's parent folder (since we have its volume and
6218 directory IDs), and since that folder does exist, we can convert
6219 that FSSpec into an FSRef, convert the FSRef in turn into a path,
6220 and, finally, append the filename. */
6221 FSSpec dirSpec;
6222 FSRef dirRef;
6223 Str255 emptyFilename = "\p";
6224 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
6225 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
6226 if (error)
6227 return NULL;
6229 error = FSpMakeFSRef(&dirSpec, &dirRef);
6230 if (error)
6231 return NULL;
6233 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
6234 if (status)
6235 return NULL;
6237 STRCAT(path, "/");
6238 STRCAT(path, filenamePtr);
6240 else
6242 /* If the file to be saved already exists, we can get its full path
6243 by converting its FSSpec into an FSRef. */
6244 error=FSpMakeFSRef(&file, &refFile);
6245 if (error)
6246 return NULL;
6248 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
6249 if (status)
6250 return NULL;
6253 /* Add a slash at the end if needed */
6254 if (folder)
6255 STRCAT(path, "/");
6257 return (vim_strsave(path));
6258 #else
6259 /* TODO: Get rid of all USE_UNIXFILENAME below */
6260 /* Set ioNamePtr, it's the same area which is always reused. */
6261 theCPB.dirInfo.ioNamePtr = directoryName;
6263 /* Trick for first entry, set ioDrParID to the first value
6264 * we want for ioDrDirID*/
6265 theCPB.dirInfo.ioDrParID = file.parID;
6266 theCPB.dirInfo.ioDrDirID = file.parID;
6268 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
6271 theCPB.dirInfo.ioFDirIndex = -1;
6272 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6273 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6274 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6275 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6277 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6278 /* *ioNamePtr[0 TO 31] will be updated */
6279 error = PBGetCatInfo(&theCPB,false);
6281 if (error)
6282 return NULL;
6284 /* Put the new directoryName in front of the current fname */
6285 STRCPY(temporaryPtr, filenamePtr);
6286 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6287 STRCAT(filenamePtr, ":");
6288 STRCAT(filenamePtr, temporaryPtr);
6290 #if 1 /* def USE_UNIXFILENAME */
6291 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
6292 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
6293 #else
6294 while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
6295 #endif
6297 /* Get the information about the volume on which the file reside */
6298 theCPB.dirInfo.ioFDirIndex = -1;
6299 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6300 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6301 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6302 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6304 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6305 /* *ioNamePtr[0 TO 31] will be updated */
6306 error = PBGetCatInfo(&theCPB,false);
6308 if (error)
6309 return NULL;
6311 /* For MacOS Classic always add the volume name */
6312 /* For MacOS X add the volume name preceded by "Volumes" */
6313 /* when we are not referring to the boot volume */
6314 #ifdef USE_UNIXFILENAME
6315 if (file.vRefNum != dfltVol_vRefNum)
6316 #endif
6318 /* Add the volume name */
6319 STRCPY(temporaryPtr, filenamePtr);
6320 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6321 STRCAT(filenamePtr, ":");
6322 STRCAT(filenamePtr, temporaryPtr);
6324 #ifdef USE_UNIXFILENAME
6325 STRCPY(temporaryPtr, filenamePtr);
6326 filenamePtr[0] = 0; /* NULL terminate the string */
6327 STRCAT(filenamePtr, "Volumes:");
6328 STRCAT(filenamePtr, temporaryPtr);
6329 #endif
6332 /* Append final path separator if it's a folder */
6333 if (folder)
6334 STRCAT(fname, ":");
6336 /* As we use Unix File Name for MacOS X convert it */
6337 #ifdef USE_UNIXFILENAME
6338 /* Need to insert leading / */
6339 /* TODO: get the above code to use directly the / */
6340 STRCPY(&temporaryPtr[1], filenamePtr);
6341 temporaryPtr[0] = '/';
6342 STRCPY(filenamePtr, temporaryPtr);
6344 char *p;
6345 for (p = fname; *p; p++)
6346 if (*p == ':')
6347 *p = '/';
6349 #endif
6351 return (vim_strsave(fname));
6352 #endif
6355 #if (defined(USE_IM_CONTROL) || defined(PROTO)) && defined(USE_CARBONKEYHANDLER)
6357 * Input Method Control functions.
6361 * Notify cursor position to IM.
6363 void
6364 im_set_position(int row, int col)
6366 #if 0
6367 /* TODO: Implement me! */
6368 im_start_row = row;
6369 im_start_col = col;
6370 #endif
6373 static ScriptLanguageRecord gTSLWindow;
6374 static ScriptLanguageRecord gTSLInsert;
6375 static ScriptLanguageRecord gTSLDefault = { 0, 0 };
6377 static Component gTSCWindow;
6378 static Component gTSCInsert;
6379 static Component gTSCDefault;
6381 static int im_initialized = 0;
6383 static void
6384 im_on_window_switch(int active)
6386 ScriptLanguageRecord *slptr = NULL;
6387 OSStatus err;
6389 if (! gui.in_use)
6390 return;
6392 if (im_initialized == 0)
6394 im_initialized = 1;
6396 /* save default TSM component (should be U.S.) to default */
6397 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6398 kKeyboardInputMethodClass);
6401 if (active == TRUE)
6403 im_is_active = TRUE;
6404 ActivateTSMDocument(gTSMDocument);
6405 slptr = &gTSLWindow;
6407 if (slptr)
6409 err = SetDefaultInputMethodOfClass(gTSCWindow, slptr,
6410 kKeyboardInputMethodClass);
6411 if (err == noErr)
6412 err = SetTextServiceLanguage(slptr);
6414 if (err == noErr)
6415 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6418 else
6420 err = GetTextServiceLanguage(&gTSLWindow);
6421 if (err == noErr)
6422 slptr = &gTSLWindow;
6424 if (slptr)
6425 GetDefaultInputMethodOfClass(&gTSCWindow, slptr,
6426 kKeyboardInputMethodClass);
6428 im_is_active = FALSE;
6429 DeactivateTSMDocument(gTSMDocument);
6434 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6436 void
6437 im_set_active(int active)
6439 ScriptLanguageRecord *slptr = NULL;
6440 OSStatus err;
6442 if (! gui.in_use)
6443 return;
6445 if (im_initialized == 0)
6447 im_initialized = 1;
6449 /* save default TSM component (should be U.S.) to default */
6450 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6451 kKeyboardInputMethodClass);
6454 if (active == TRUE)
6456 im_is_active = TRUE;
6457 ActivateTSMDocument(gTSMDocument);
6458 slptr = &gTSLInsert;
6460 if (slptr)
6462 err = SetDefaultInputMethodOfClass(gTSCInsert, slptr,
6463 kKeyboardInputMethodClass);
6464 if (err == noErr)
6465 err = SetTextServiceLanguage(slptr);
6467 if (err == noErr)
6468 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6471 else
6473 err = GetTextServiceLanguage(&gTSLInsert);
6474 if (err == noErr)
6475 slptr = &gTSLInsert;
6477 if (slptr)
6478 GetDefaultInputMethodOfClass(&gTSCInsert, slptr,
6479 kKeyboardInputMethodClass);
6481 /* restore to default when switch to normal mode, so than we could
6482 * enter commands easier */
6483 SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault,
6484 kKeyboardInputMethodClass);
6485 SetTextServiceLanguage(&gTSLDefault);
6487 im_is_active = FALSE;
6488 DeactivateTSMDocument(gTSMDocument);
6493 * Get IM status. When IM is on, return not 0. Else return 0.
6496 im_get_status(void)
6498 if (! gui.in_use)
6499 return 0;
6501 return im_is_active;
6504 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6509 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6510 // drawer implementation
6511 static MenuRef contextMenu = NULL;
6512 enum
6514 kTabContextMenuId = 42,
6517 // the caller has to CFRelease() the returned string
6518 static CFStringRef
6519 getTabLabel(tabpage_T *page)
6521 get_tabline_label(page, FALSE);
6522 #ifdef MACOS_CONVERT
6523 return (CFStringRef)mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6524 #else
6525 // TODO: check internal encoding?
6526 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6527 kCFStringEncodingMacRoman);
6528 #endif
6532 #define DRAWER_SIZE 150
6533 #define DRAWER_INSET 16
6535 static ControlRef dataBrowser = NULL;
6537 // when the tabline is hidden, vim doesn't call update_tabline(). When
6538 // the tabline is shown again, show_tabline() is called before upate_tabline(),
6539 // and because of this, the tab labels and vims internal tabs are out of sync
6540 // for a very short time. to prevent inconsistent state, we store the labels
6541 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6542 static CFStringRef *tabLabels = NULL;
6543 static int tabLabelsSize = 0;
6545 enum
6547 kTabsColumn = 'Tabs'
6550 static int
6551 getTabCount(void)
6553 tabpage_T *tp;
6554 int numTabs = 0;
6556 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6557 ++numTabs;
6558 return numTabs;
6561 // data browser item display callback
6562 static OSStatus
6563 dbItemDataCallback(ControlRef browser,
6564 DataBrowserItemID itemID,
6565 DataBrowserPropertyID property /* column id */,
6566 DataBrowserItemDataRef itemData,
6567 Boolean changeValue)
6569 OSStatus status = noErr;
6571 // assert(property == kTabsColumn); // why is this violated??
6573 // changeValue is true if we have a modifieable list and data was changed.
6574 // In our case, it's always false.
6575 // (that is: if (changeValue) updateInternalData(); else return
6576 // internalData();
6577 if (!changeValue)
6579 CFStringRef str;
6581 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6582 str = tabLabels[itemID - 1];
6583 status = SetDataBrowserItemDataText(itemData, str);
6585 else
6586 status = errDataBrowserPropertyNotSupported;
6588 return status;
6591 // data browser action callback
6592 static void
6593 dbItemNotificationCallback(ControlRef browser,
6594 DataBrowserItemID item,
6595 DataBrowserItemNotification message)
6597 switch (message)
6599 case kDataBrowserItemSelected:
6600 send_tabline_event(item);
6601 break;
6605 // callbacks needed for contextual menu:
6606 static void
6607 dbGetContextualMenuCallback(ControlRef browser,
6608 MenuRef *menu,
6609 UInt32 *helpType,
6610 CFStringRef *helpItemString,
6611 AEDesc *selection)
6613 // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6614 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6615 *helpItemString = NULL;
6617 *menu = contextMenu;
6620 static void
6621 dbSelectContextualMenuCallback(ControlRef browser,
6622 MenuRef menu,
6623 UInt32 selectionType,
6624 SInt16 menuID,
6625 MenuItemIndex menuItem)
6627 if (selectionType == kCMMenuItemSelected)
6629 MenuCommand command;
6630 GetMenuItemCommandID(menu, menuItem, &command);
6632 // get tab that was selected when the context menu appeared
6633 // (there is always one tab selected). TODO: check if the context menu
6634 // isn't opened on an item but on empty space (has to be possible some
6635 // way, the finder does it too ;-) )
6636 Handle items = NewHandle(0);
6637 if (items != NULL)
6639 int numItems;
6641 GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6642 kDataBrowserItemIsSelected, items);
6643 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6644 if (numItems > 0)
6646 int idx;
6647 DataBrowserItemID *itemsPtr;
6649 HLock(items);
6650 itemsPtr = (DataBrowserItemID *)*items;
6651 idx = itemsPtr[0];
6652 HUnlock(items);
6653 send_tabline_menu_event(idx, command);
6655 DisposeHandle(items);
6660 // focus callback of the data browser to always leave focus in vim
6661 static OSStatus
6662 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6664 assert(GetEventClass(event) == kEventClassControl
6665 && GetEventKind(event) == kEventControlSetFocusPart);
6667 return paramErr;
6671 // drawer callback to resize data browser to drawer size
6672 static OSStatus
6673 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6675 switch (GetEventKind(event))
6677 case kEventWindowBoundsChanged: // move or resize
6679 UInt32 attribs;
6680 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6681 NULL, sizeof(attribs), NULL, &attribs);
6682 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6684 Rect r;
6685 GetWindowBounds(drawer, kWindowContentRgn, &r);
6686 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6687 SetControlBounds(dataBrowser, &r);
6688 SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6689 kTabsColumn, r.right);
6692 break;
6695 return eventNotHandledErr;
6698 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6699 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6700 // blue highlights in the list view on these systems, though. Oh well.)
6703 #import <mach-o/dyld.h>
6705 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6707 static OSStatus
6708 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6709 OptionBits inAttributesToSet,
6710 OptionBits inAttributesToClear)
6712 long osVersion;
6713 char *symbolName;
6714 NSSymbol symbol = NULL;
6715 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6716 OptionBits inAttributesToSet, OptionBits inAttributesToClear);
6718 Gestalt(gestaltSystemVersion, &osVersion);
6719 if (osVersion < 0x1040) // only supported for 10.4 (and up)
6720 return noErr;
6722 // C name mangling...
6723 symbolName = "_DataBrowserChangeAttributes";
6724 if (!NSIsSymbolNameDefined(symbolName)
6725 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6726 return noErr;
6728 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6729 if (dataBrowserChangeAttributes == NULL)
6730 return noErr; // well...
6731 return dataBrowserChangeAttributes(inDataBrowser,
6732 inAttributesToSet, inAttributesToClear);
6735 static void
6736 initialise_tabline(void)
6738 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6739 DataBrowserCallbacks dbCallbacks;
6740 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6741 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6742 DataBrowserListViewColumnDesc colDesc;
6744 // drawers have to have compositing enabled
6745 CreateNewWindow(kDrawerWindowClass,
6746 kWindowStandardHandlerAttribute
6747 | kWindowCompositingAttribute
6748 | kWindowResizableAttribute
6749 | kWindowLiveResizeAttribute,
6750 &drawerRect, &drawer);
6752 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6753 SetDrawerParent(drawer, gui.VimWindow);
6754 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6757 // create list view embedded in drawer
6758 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6759 &dataBrowser);
6761 dbCallbacks.version = kDataBrowserLatestCallbacks;
6762 InitDataBrowserCallbacks(&dbCallbacks);
6763 dbCallbacks.u.v1.itemDataCallback =
6764 NewDataBrowserItemDataUPP(dbItemDataCallback);
6765 dbCallbacks.u.v1.itemNotificationCallback =
6766 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6767 dbCallbacks.u.v1.getContextualMenuCallback =
6768 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6769 dbCallbacks.u.v1.selectContextualMenuCallback =
6770 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6772 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6774 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6775 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6776 SetDataBrowserSelectionFlags(dataBrowser,
6777 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6778 SetDataBrowserTableViewHiliteStyle(dataBrowser,
6779 kDataBrowserTableViewFillHilite);
6780 Boolean b = false;
6781 SetControlData(dataBrowser, kControlEntireControl,
6782 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6784 // enable blue background in data browser (this is only in 10.4 and vim
6785 // has to support older osx versions as well, so we have to load this
6786 // function dynamically)
6787 myDataBrowserChangeAttributes(dataBrowser,
6788 kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6790 // install callback that keeps focus in vim and away from the data browser
6791 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6792 NULL, NULL);
6794 // install callback that keeps data browser at the size of the drawer
6795 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6796 NULL, NULL);
6798 // add "tabs" column to data browser
6799 colDesc.propertyDesc.propertyID = kTabsColumn;
6800 colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6802 // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6803 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6805 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6806 colDesc.headerBtnDesc.minimumWidth = 100;
6807 colDesc.headerBtnDesc.maximumWidth = 150;
6808 colDesc.headerBtnDesc.titleOffset = 0;
6809 colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6810 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6811 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6812 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6814 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6816 // create tabline popup menu required by vim docs (see :he tabline-menu)
6817 CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6818 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6819 TABLINE_MENU_CLOSE, NULL);
6820 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6821 TABLINE_MENU_NEW, NULL);
6822 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6823 TABLINE_MENU_OPEN, NULL);
6828 * Show or hide the tabline.
6830 void
6831 gui_mch_show_tabline(int showit)
6833 if (showit == 0)
6834 CloseDrawer(drawer, true);
6835 else
6836 OpenDrawer(drawer, kWindowEdgeRight, true);
6840 * Return TRUE when tabline is displayed.
6843 gui_mch_showing_tabline(void)
6845 WindowDrawerState state = GetDrawerState(drawer);
6847 return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6851 * Update the labels of the tabline.
6853 void
6854 gui_mch_update_tabline(void)
6856 tabpage_T *tp;
6857 int numTabs = getTabCount();
6858 int nr = 1;
6859 int curtabidx = 1;
6861 // adjust data browser
6862 if (tabLabels != NULL)
6864 int i;
6866 for (i = 0; i < tabLabelsSize; ++i)
6867 CFRelease(tabLabels[i]);
6868 free(tabLabels);
6870 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6871 tabLabelsSize = numTabs;
6873 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6875 if (tp == curtab)
6876 curtabidx = nr;
6877 tabLabels[nr-1] = getTabLabel(tp);
6880 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6881 kDataBrowserItemNoProperty);
6882 // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6883 // can pass NULL for the id array
6884 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6885 kDataBrowserItemNoProperty);
6887 DataBrowserItemID item = curtabidx;
6888 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6892 * Set the current tab to "nr". First tab is 1.
6894 void
6895 gui_mch_set_curtab(nr)
6896 int nr;
6898 DataBrowserItemID item = nr;
6899 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6901 // TODO: call something like this?: (or restore scroll position, or...)
6902 RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6903 kDataBrowserRevealOnly);
6906 #endif // FEAT_GUI_TABLINE