Merged from the latest developing branch.
[MacVim.git] / src / gui_mac.c
blob77006a73a0a2b23a80b2fe4d2907a02ac6188e68
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 static struct
158 FMFontFamily family;
159 FMFontSize size;
160 FMFontStyle style;
161 Boolean isPanelVisible;
162 } gFontPanelInfo = { 0, 0, 0, false };
164 #ifdef MACOS_CONVERT
165 # define USE_ATSUI_DRAWING
166 int p_macatsui_last;
167 ATSUStyle gFontStyle;
168 # ifdef FEAT_MBYTE
169 ATSUStyle gWideFontStyle;
170 # endif
171 Boolean gIsFontFallbackSet;
172 #endif
174 /* Colors Macros */
175 #define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b)
176 #define Red(c) ((c & 0x00FF0000) >> 16)
177 #define Green(c) ((c & 0x0000FF00) >> 8)
178 #define Blue(c) ((c & 0x000000FF) >> 0)
180 /* Key mapping */
182 #define vk_Esc 0x35 /* -> 1B */
184 #define vk_F1 0x7A /* -> 10 */
185 #define vk_F2 0x78 /*0x63*/
186 #define vk_F3 0x63 /*0x76*/
187 #define vk_F4 0x76 /*0x60*/
188 #define vk_F5 0x60 /*0x61*/
189 #define vk_F6 0x61 /*0x62*/
190 #define vk_F7 0x62 /*0x63*/ /*?*/
191 #define vk_F8 0x64
192 #define vk_F9 0x65
193 #define vk_F10 0x6D
194 #define vk_F11 0x67
195 #define vk_F12 0x6F
196 #define vk_F13 0x69
197 #define vk_F14 0x6B
198 #define vk_F15 0x71
200 #define vk_Clr 0x47 /* -> 1B (ESC) */
201 #define vk_Enter 0x4C /* -> 03 */
203 #define vk_Space 0x31 /* -> 20 */
204 #define vk_Tab 0x30 /* -> 09 */
205 #define vk_Return 0x24 /* -> 0D */
206 /* This is wrong for OSX, what is it for? */
207 #define vk_Delete 0X08 /* -> 08 BackSpace */
209 #define vk_Help 0x72 /* -> 05 */
210 #define vk_Home 0x73 /* -> 01 */
211 #define vk_PageUp 0x74 /* -> 0D */
212 #define vk_FwdDelete 0x75 /* -> 7F */
213 #define vk_End 0x77 /* -> 04 */
214 #define vk_PageDown 0x79 /* -> 0C */
216 #define vk_Up 0x7E /* -> 1E */
217 #define vk_Down 0x7D /* -> 1F */
218 #define vk_Left 0x7B /* -> 1C */
219 #define vk_Right 0x7C /* -> 1D */
221 #define vk_Undo vk_F1
222 #define vk_Cut vk_F2
223 #define vk_Copy vk_F3
224 #define vk_Paste vk_F4
225 #define vk_PrintScreen vk_F13
226 #define vk_SCrollLock vk_F14
227 #define vk_Pause vk_F15
228 #define vk_NumLock vk_Clr
229 #define vk_Insert vk_Help
231 #define KeySym char
233 static struct
235 KeySym key_sym;
236 char_u vim_code0;
237 char_u vim_code1;
238 } special_keys[] =
240 {vk_Up, 'k', 'u'},
241 {vk_Down, 'k', 'd'},
242 {vk_Left, 'k', 'l'},
243 {vk_Right, 'k', 'r'},
245 {vk_F1, 'k', '1'},
246 {vk_F2, 'k', '2'},
247 {vk_F3, 'k', '3'},
248 {vk_F4, 'k', '4'},
249 {vk_F5, 'k', '5'},
250 {vk_F6, 'k', '6'},
251 {vk_F7, 'k', '7'},
252 {vk_F8, 'k', '8'},
253 {vk_F9, 'k', '9'},
254 {vk_F10, 'k', ';'},
256 {vk_F11, 'F', '1'},
257 {vk_F12, 'F', '2'},
258 {vk_F13, 'F', '3'},
259 {vk_F14, 'F', '4'},
260 {vk_F15, 'F', '5'},
262 /* {XK_Help, '%', '1'}, */
263 /* {XK_Undo, '&', '8'}, */
264 /* {XK_BackSpace, 'k', 'b'}, */
265 #ifndef MACOS_X
266 {vk_Delete, 'k', 'b'},
267 #endif
268 {vk_Insert, 'k', 'I'},
269 {vk_FwdDelete, 'k', 'D'},
270 {vk_Home, 'k', 'h'},
271 {vk_End, '@', '7'},
272 /* {XK_Prior, 'k', 'P'}, */
273 /* {XK_Next, 'k', 'N'}, */
274 /* {XK_Print, '%', '9'}, */
276 {vk_PageUp, 'k', 'P'},
277 {vk_PageDown, 'k', 'N'},
279 /* End of list marker: */
280 {(KeySym)0, 0, 0}
284 * ------------------------------------------------------------
285 * Forward declaration (for those needed)
286 * ------------------------------------------------------------
289 #ifdef USE_AEVENT
290 OSErr HandleUnusedParms(const AppleEvent *theAEvent);
291 #endif
293 #ifdef FEAT_GUI_TABLINE
294 static void initialise_tabline(void);
295 static WindowRef drawer = NULL; // TODO: put into gui.h
296 #endif
298 #ifdef USE_ATSUI_DRAWING
299 static void gui_mac_set_font_attributes(GuiFont font);
300 static void gui_mac_dispose_atsui_style(void);
301 #endif
304 * ------------------------------------------------------------
305 * Conversion Utility
306 * ------------------------------------------------------------
310 * C2Pascal_save
312 * Allocate memory and convert the C-String passed in
313 * into a pascal string
317 char_u *
318 C2Pascal_save(char_u *Cstring)
320 char_u *PascalString;
321 int len;
323 if (Cstring == NULL)
324 return NULL;
326 len = STRLEN(Cstring);
328 if (len > 255) /* Truncate if necessary */
329 len = 255;
331 PascalString = alloc(len + 1);
332 if (PascalString != NULL)
334 mch_memmove(PascalString + 1, Cstring, len);
335 PascalString[0] = len;
338 return PascalString;
342 * C2Pascal_save_and_remove_backslash
344 * Allocate memory and convert the C-String passed in
345 * into a pascal string. Also remove the backslash at the same time
349 char_u *
350 C2Pascal_save_and_remove_backslash(char_u *Cstring)
352 char_u *PascalString;
353 int len;
354 char_u *p, *c;
356 len = STRLEN(Cstring);
358 if (len > 255) /* Truncate if necessary */
359 len = 255;
361 PascalString = alloc(len + 1);
362 if (PascalString != NULL)
364 for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++)
366 if ((*c == '\\') && (c[1] != 0))
368 c++;
370 *p = *c;
371 p++;
372 len++;
374 PascalString[0] = len;
377 return PascalString;
381 * Convert the modifiers of an Event into vim's modifiers (mouse)
384 int_u
385 EventModifiers2VimMouseModifiers(EventModifiers macModifiers)
387 int_u vimModifiers = 0x00;
389 if (macModifiers & (shiftKey | rightShiftKey))
390 vimModifiers |= MOUSE_SHIFT;
391 if (macModifiers & (controlKey | rightControlKey))
392 vimModifiers |= MOUSE_CTRL;
393 if (macModifiers & (optionKey | rightOptionKey))
394 vimModifiers |= MOUSE_ALT;
395 #if 0
396 /* Not yet supported */
397 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
398 vimModifiers |= MOUSE_CMD;
399 #endif
400 return (vimModifiers);
404 * Convert the modifiers of an Event into vim's modifiers (keys)
407 static int_u
408 EventModifiers2VimModifiers(EventModifiers macModifiers)
410 int_u vimModifiers = 0x00;
412 if (macModifiers & (shiftKey | rightShiftKey))
413 vimModifiers |= MOD_MASK_SHIFT;
414 if (macModifiers & (controlKey | rightControlKey))
415 vimModifiers |= MOD_MASK_CTRL;
416 if (macModifiers & (optionKey | rightOptionKey))
417 vimModifiers |= MOD_MASK_ALT;
418 #ifdef USE_CMD_KEY
419 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */
420 vimModifiers |= MOD_MASK_CMD;
421 #endif
422 return (vimModifiers);
425 /* Convert a string representing a point size into pixels. The string should
426 * be a positive decimal number, with an optional decimal point (eg, "12", or
427 * "10.5"). The pixel value is returned, and a pointer to the next unconverted
428 * character is stored in *end. The flag "vertical" says whether this
429 * calculation is for a vertical (height) size or a horizontal (width) one.
431 * From gui_w48.c
433 static int
434 points_to_pixels(char_u *str, char_u **end, int vertical)
436 int pixels;
437 int points = 0;
438 int divisor = 0;
440 while (*str)
442 if (*str == '.' && divisor == 0)
444 /* Start keeping a divisor, for later */
445 divisor = 1;
446 continue;
449 if (!isdigit(*str))
450 break;
452 points *= 10;
453 points += *str - '0';
454 divisor *= 10;
456 ++str;
459 if (divisor == 0)
460 divisor = 1;
462 pixels = points/divisor;
463 *end = str;
464 return pixels;
467 #ifdef MACOS_CONVERT
469 * Deletes all traces of any Windows-style mnemonic text (including any
470 * parentheses) from a menu item and returns the cleaned menu item title.
471 * The caller is responsible for releasing the returned string.
473 static CFStringRef
474 menu_title_removing_mnemonic(vimmenu_T *menu)
476 CFStringRef name;
477 size_t menuTitleLen;
478 CFIndex displayLen;
479 CFRange mnemonicStart;
480 CFRange mnemonicEnd;
481 CFMutableStringRef cleanedName;
483 menuTitleLen = STRLEN(menu->dname);
484 name = mac_enc_to_cfstring(menu->dname, menuTitleLen);
486 if (name)
488 /* Simple mnemonic-removal algorithm, assumes single parenthesized
489 * mnemonic character towards the end of the menu text */
490 mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards);
491 displayLen = CFStringGetLength(name);
493 if (mnemonicStart.location != kCFNotFound
494 && (mnemonicStart.location + 2) < displayLen
495 && CFStringGetCharacterAtIndex(name,
496 mnemonicStart.location + 1) == (UniChar)menu->mnemonic)
498 if (CFStringFindWithOptions(name, CFSTR(")"),
499 CFRangeMake(mnemonicStart.location + 1,
500 displayLen - mnemonicStart.location - 1),
501 kCFCompareBackwards, &mnemonicEnd) &&
502 (mnemonicStart.location + 2) == mnemonicEnd.location)
504 cleanedName = CFStringCreateMutableCopy(NULL, 0, name);
505 if (cleanedName)
507 CFStringDelete(cleanedName,
508 CFRangeMake(mnemonicStart.location,
509 mnemonicEnd.location + 1 -
510 mnemonicStart.location));
512 CFRelease(name);
513 name = cleanedName;
519 return name;
521 #endif
524 * Convert a list of FSSpec aliases into a list of fullpathname
525 * character strings.
528 char_u **
529 new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error)
531 char_u **fnames = NULL;
532 OSErr newError;
533 long fileCount;
534 FSSpec fileToOpen;
535 long actualSize;
536 AEKeyword dummyKeyword;
537 DescType dummyType;
539 /* Get number of files in list */
540 *error = AECountItems(theList, numFiles);
541 if (*error)
542 return fnames;
544 /* Allocate the pointer list */
545 fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
547 /* Empty out the list */
548 for (fileCount = 0; fileCount < *numFiles; fileCount++)
549 fnames[fileCount] = NULL;
551 /* Scan the list of FSSpec */
552 for (fileCount = 1; fileCount <= *numFiles; fileCount++)
554 /* Get the alias for the nth file, convert to an FSSpec */
555 newError = AEGetNthPtr(theList, fileCount, typeFSS,
556 &dummyKeyword, &dummyType,
557 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize);
558 if (newError)
560 /* Caller is able to clean up */
561 /* TODO: Should be clean up or not? For safety. */
562 return fnames;
565 /* Convert the FSSpec to a pathname */
566 fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen);
569 return (fnames);
573 * ------------------------------------------------------------
574 * CodeWarrior External Editor Support
575 * ------------------------------------------------------------
577 #ifdef FEAT_CW_EDITOR
580 * Handle the Window Search event from CodeWarrior
582 * Description
583 * -----------
585 * The IDE sends the Window Search AppleEvent to the editor when it
586 * needs to know whether a particular file is open in the editor.
588 * Event Reply
589 * -----------
591 * None. Put data in the location specified in the structure received.
593 * Remarks
594 * -------
596 * When the editor receives this event, determine whether the specified
597 * file is open. If it is, return the modification date/time for that file
598 * in the appropriate location specified in the structure. If the file is
599 * not opened, put the value fnfErr(file not found) in that location.
603 typedef struct WindowSearch WindowSearch;
604 struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/
606 FSSpec theFile; // identifies the file
607 long *theDate; // where to put the modification date/time
610 pascal OSErr
611 Handle_KAHL_SRCH_AE(
612 const AppleEvent *theAEvent,
613 AppleEvent *theReply,
614 long refCon)
616 OSErr error = noErr;
617 buf_T *buf;
618 int foundFile = false;
619 DescType typeCode;
620 WindowSearch SearchData;
621 Size actualSize;
623 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize);
624 if (error)
625 return error;
627 error = HandleUnusedParms(theAEvent);
628 if (error)
629 return error;
631 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
632 if (buf->b_ml.ml_mfp != NULL
633 && SearchData.theFile.parID == buf->b_FSSpec.parID
634 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0]
635 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0)
637 foundFile = true;
638 break;
641 if (foundFile == false)
642 *SearchData.theDate = fnfErr;
643 else
644 *SearchData.theDate = buf->b_mtime;
646 return error;
650 * Handle the Modified (from IDE to Editor) event from CodeWarrior
652 * Description
653 * -----------
655 * The IDE sends this event to the external editor when it wants to
656 * know which files that are open in the editor have been modified.
658 * Parameters None.
659 * ----------
661 * Event Reply
662 * -----------
663 * The reply for this event is:
665 * keyDirectObject typeAEList required
666 * each element in the list is a structure of typeChar
668 * Remarks
669 * -------
671 * When building the reply event, include one element in the list for
672 * each open file that has been modified.
676 typedef struct ModificationInfo ModificationInfo;
677 struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/
679 FSSpec theFile; // identifies the file
680 long theDate; // the date/time the file was last modified
681 short saved; // set this to zero when replying, unused
684 pascal OSErr
685 Handle_KAHL_MOD_AE(
686 const AppleEvent *theAEvent,
687 AppleEvent *theReply,
688 long refCon)
690 OSErr error = noErr;
691 AEDescList replyList;
692 long numFiles;
693 ModificationInfo theFile;
694 buf_T *buf;
696 theFile.saved = 0;
698 error = HandleUnusedParms(theAEvent);
699 if (error)
700 return error;
702 /* Send the reply */
703 /* replyObject.descriptorType = typeNull;
704 replyObject.dataHandle = nil;*/
706 /* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */
707 error = AECreateList(nil, 0, false, &replyList);
708 if (error)
709 return error;
711 #if 0
712 error = AECountItems(&replyList, &numFiles);
714 /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
715 * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
716 * sizeof(DescType))
719 /* AEPutDesc */
720 #endif
722 numFiles = 0;
723 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
724 if (buf->b_ml.ml_mfp != NULL)
726 /* Add this file to the list */
727 theFile.theFile = buf->b_FSSpec;
728 theFile.theDate = buf->b_mtime;
729 /* theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */
730 error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile));
733 #if 0
734 error = AECountItems(&replyList, &numFiles);
735 #endif
737 /* We can add data only if something to reply */
738 error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
740 if (replyList.dataHandle)
741 AEDisposeDesc(&replyList);
743 return error;
747 * Handle the Get Text event from CodeWarrior
749 * Description
750 * -----------
752 * The IDE sends the Get Text AppleEvent to the editor when it needs
753 * the source code from a file. For example, when the user issues a
754 * Check Syntax or Compile command, the compiler needs access to
755 * the source code contained in the file.
757 * Event Reply
758 * -----------
760 * None. Put data in locations specified in the structure received.
762 * Remarks
763 * -------
765 * When the editor receives this event, it must set the size of the handle
766 * in theText to fit the data in the file. It must then copy the entire
767 * contents of the specified file into the memory location specified in
768 * theText.
772 typedef struct CW_GetText CW_GetText;
773 struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/
775 FSSpec theFile; /* identifies the file */
776 Handle theText; /* the location where you return the text (must be resized properly) */
777 long *unused; /* 0 (not used) */
778 long *theDate; /* where to put the modification date/time */
781 pascal OSErr
782 Handle_KAHL_GTTX_AE(
783 const AppleEvent *theAEvent,
784 AppleEvent *theReply,
785 long refCon)
787 OSErr error = noErr;
788 buf_T *buf;
789 int foundFile = false;
790 DescType typeCode;
791 CW_GetText GetTextData;
792 Size actualSize;
793 char_u *line;
794 char_u *fullbuffer = NULL;
795 long linesize;
796 long lineStart;
797 long BufferSize;
798 long lineno;
800 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize);
802 if (error)
803 return error;
805 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
806 if (buf->b_ml.ml_mfp != NULL)
807 if (GetTextData.theFile.parID == buf->b_FSSpec.parID)
809 foundFile = true;
810 break;
813 if (foundFile)
815 BufferSize = 0; /* GetHandleSize(GetTextData.theText); */
816 for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++)
818 /* Must use the right buffer */
819 line = ml_get_buf(buf, (linenr_T) lineno, FALSE);
820 linesize = STRLEN(line) + 1;
821 lineStart = BufferSize;
822 BufferSize += linesize;
823 /* Resize handle to linesize+1 to include the linefeed */
824 SetHandleSize(GetTextData.theText, BufferSize);
825 if (GetHandleSize(GetTextData.theText) != BufferSize)
827 break; /* Simple handling for now */
829 else
831 HLock(GetTextData.theText);
832 fullbuffer = (char_u *) *GetTextData.theText;
833 STRCPY((char_u *)(fullbuffer + lineStart), line);
834 fullbuffer[BufferSize-1] = '\r';
835 HUnlock(GetTextData.theText);
838 if (fullbuffer != NULL)
840 HLock(GetTextData.theText);
841 fullbuffer[BufferSize-1] = 0;
842 HUnlock(GetTextData.theText);
844 if (foundFile == false)
845 *GetTextData.theDate = fnfErr;
846 else
847 /* *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/
848 *GetTextData.theDate = buf->b_mtime;
851 error = HandleUnusedParms(theAEvent);
853 return error;
860 /* Taken from MoreAppleEvents:ProcessHelpers*/
861 pascal OSErr
862 FindProcessBySignature(
863 const OSType targetType,
864 const OSType targetCreator,
865 ProcessSerialNumberPtr psnPtr)
867 OSErr anErr = noErr;
868 Boolean lookingForProcess = true;
870 ProcessInfoRec infoRec;
872 infoRec.processInfoLength = sizeof(ProcessInfoRec);
873 infoRec.processName = nil;
874 infoRec.processAppSpec = nil;
876 psnPtr->lowLongOfPSN = kNoProcess;
877 psnPtr->highLongOfPSN = kNoProcess;
879 while (lookingForProcess)
881 anErr = GetNextProcess(psnPtr);
882 if (anErr != noErr)
883 lookingForProcess = false;
884 else
886 anErr = GetProcessInformation(psnPtr, &infoRec);
887 if ((anErr == noErr)
888 && (infoRec.processType == targetType)
889 && (infoRec.processSignature == targetCreator))
890 lookingForProcess = false;
894 return anErr;
895 }//end FindProcessBySignature
897 void
898 Send_KAHL_MOD_AE(buf_T *buf)
900 OSErr anErr = noErr;
901 AEDesc targetAppDesc = { typeNull, nil };
902 ProcessSerialNumber psn = { kNoProcess, kNoProcess };
903 AppleEvent theReply = { typeNull, nil };
904 AESendMode sendMode;
905 AppleEvent theEvent = {typeNull, nil };
906 AEIdleUPP idleProcUPP = nil;
907 ModificationInfo ModData;
910 anErr = FindProcessBySignature('APPL', 'CWIE', &psn);
911 if (anErr == noErr)
913 anErr = AECreateDesc(typeProcessSerialNumber, &psn,
914 sizeof(ProcessSerialNumber), &targetAppDesc);
916 if (anErr == noErr)
918 anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc,
919 kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
922 AEDisposeDesc(&targetAppDesc);
924 /* Add the parms */
925 ModData.theFile = buf->b_FSSpec;
926 ModData.theDate = buf->b_mtime;
928 if (anErr == noErr)
929 anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData));
931 if (idleProcUPP == nil)
932 sendMode = kAENoReply;
933 else
934 sendMode = kAEWaitReply;
936 if (anErr == noErr)
937 anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil);
938 if (anErr == noErr && sendMode == kAEWaitReply)
940 /* anErr = AEHGetHandlerError(&theReply);*/
942 (void) AEDisposeDesc(&theReply);
945 #endif /* FEAT_CW_EDITOR */
948 * ------------------------------------------------------------
949 * Apple Event Handling procedure
950 * ------------------------------------------------------------
952 #ifdef USE_AEVENT
955 * Handle the Unused parms of an AppleEvent
958 OSErr
959 HandleUnusedParms(const AppleEvent *theAEvent)
961 OSErr error;
962 long actualSize;
963 DescType dummyType;
964 AEKeyword missedKeyword;
966 /* Get the "missed keyword" attribute from the AppleEvent. */
967 error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr,
968 typeKeyword, &dummyType,
969 (Ptr)&missedKeyword, sizeof(missedKeyword),
970 &actualSize);
972 /* If the descriptor isn't found, then we got the required parameters. */
973 if (error == errAEDescNotFound)
975 error = noErr;
977 else
979 #if 0
980 /* Why is this removed? */
981 error = errAEEventNotHandled;
982 #endif
985 return error;
990 * Handle the ODoc AppleEvent
992 * Deals with all files dragged to the application icon.
996 typedef struct SelectionRange SelectionRange;
997 struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */
999 short unused1; // 0 (not used)
1000 short lineNum; // line to select (<0 to specify range)
1001 long startRange; // start of selection range (if line < 0)
1002 long endRange; // end of selection range (if line < 0)
1003 long unused2; // 0 (not used)
1004 long theDate; // modification date/time
1007 /* The IDE uses the optional keyAEPosition parameter to tell the ed-
1008 itor the selection range. If lineNum is zero or greater, scroll the text
1009 to the specified line. If lineNum is less than zero, use the values in
1010 startRange and endRange to select the specified characters. Scroll
1011 the text to display the selection. If lineNum, startRange, and
1012 endRange are all negative, there is no selection range specified.
1015 pascal OSErr
1016 HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
1019 * TODO: Clean up the code with convert the AppleEvent into
1020 * a ":args"
1022 OSErr error = noErr;
1023 // OSErr firstError = noErr;
1024 // short numErrors = 0;
1025 AEDesc theList;
1026 DescType typeCode;
1027 long numFiles;
1028 // long fileCount;
1029 char_u **fnames;
1030 // char_u fname[256];
1031 Size actualSize;
1032 SelectionRange thePosition;
1033 short gotPosition = false;
1034 long lnum;
1036 /* the direct object parameter is the list of aliases to files (one or more) */
1037 error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
1038 if (error)
1039 return error;
1042 error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize);
1043 if (error == noErr)
1044 gotPosition = true;
1045 if (error == errAEDescNotFound)
1046 error = noErr;
1047 if (error)
1048 return error;
1051 error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
1053 if (^error) then
1055 if (thePosition.lineNum >= 0)
1057 // Goto this line
1059 else
1061 // Set the range char wise
1067 #ifdef FEAT_VISUAL
1068 reset_VIsual();
1069 #endif
1071 fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error);
1073 if (error)
1075 /* TODO: empty fnames[] first */
1076 vim_free(fnames);
1077 return (error);
1080 if (starting > 0)
1082 int i;
1083 char_u *p;
1084 int fnum = -1;
1086 /* these are the initial files dropped on the Vim icon */
1087 for (i = 0 ; i < numFiles; i++)
1089 if (ga_grow(&global_alist.al_ga, 1) == FAIL
1090 || (p = vim_strsave(fnames[i])) == NULL)
1091 mch_exit(2);
1092 else
1093 alist_add(&global_alist, p, 2);
1094 if (fnum == -1)
1095 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum;
1098 /* If the file name was already in the buffer list we need to switch
1099 * to it. */
1100 if (curbuf->b_fnum != fnum)
1102 char_u cmd[30];
1104 vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum);
1105 do_cmdline_cmd(cmd);
1108 /* Change directory to the location of the first file. */
1109 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK)
1110 shorten_fnames(TRUE);
1112 goto finished;
1115 /* Handle the drop, :edit to get to the file */
1116 handle_drop(numFiles, fnames, FALSE);
1118 /* TODO: Handle the goto/select line more cleanly */
1119 if ((numFiles == 1) & (gotPosition))
1121 if (thePosition.lineNum >= 0)
1123 lnum = thePosition.lineNum + 1;
1124 /* oap->motion_type = MLINE;
1125 setpcmark();*/
1126 if (lnum < 1L)
1127 lnum = 1L;
1128 else if (lnum > curbuf->b_ml.ml_line_count)
1129 lnum = curbuf->b_ml.ml_line_count;
1130 curwin->w_cursor.lnum = lnum;
1131 curwin->w_cursor.col = 0;
1132 /* beginline(BL_SOL | BL_FIX);*/
1134 else
1135 goto_byte(thePosition.startRange + 1);
1138 /* Update the screen display */
1139 update_screen(NOT_VALID);
1140 #ifdef FEAT_VISUAL
1141 /* Select the text if possible */
1142 if (gotPosition)
1144 VIsual_active = TRUE;
1145 VIsual_select = FALSE;
1146 VIsual = curwin->w_cursor;
1147 if (thePosition.lineNum < 0)
1149 VIsual_mode = 'v';
1150 goto_byte(thePosition.endRange);
1152 else
1154 VIsual_mode = 'V';
1155 VIsual.col = 0;
1158 #endif
1159 setcursor();
1160 out_flush();
1162 /* Fake mouse event to wake from stall */
1163 PostEvent(mouseUp, 0);
1165 finished:
1166 AEDisposeDesc(&theList); /* dispose what we allocated */
1168 error = HandleUnusedParms(theAEvent);
1169 return error;
1176 pascal OSErr
1177 Handle_aevt_oapp_AE(
1178 const AppleEvent *theAEvent,
1179 AppleEvent *theReply,
1180 long refCon)
1182 OSErr error = noErr;
1184 error = HandleUnusedParms(theAEvent);
1185 return error;
1192 pascal OSErr
1193 Handle_aevt_quit_AE(
1194 const AppleEvent *theAEvent,
1195 AppleEvent *theReply,
1196 long refCon)
1198 OSErr error = noErr;
1200 error = HandleUnusedParms(theAEvent);
1201 if (error)
1202 return error;
1204 /* Need to fake a :confirm qa */
1205 do_cmdline_cmd((char_u *)"confirm qa");
1207 return error;
1214 pascal OSErr
1215 Handle_aevt_pdoc_AE(
1216 const AppleEvent *theAEvent,
1217 AppleEvent *theReply,
1218 long refCon)
1220 OSErr error = noErr;
1222 error = HandleUnusedParms(theAEvent);
1224 return error;
1228 * Handling of unknown AppleEvent
1230 * (Just get rid of all the parms)
1232 pascal OSErr
1233 Handle_unknown_AE(
1234 const AppleEvent *theAEvent,
1235 AppleEvent *theReply,
1236 long refCon)
1238 OSErr error = noErr;
1240 error = HandleUnusedParms(theAEvent);
1242 return error;
1247 * Install the various AppleEvent Handlers
1249 OSErr
1250 InstallAEHandlers(void)
1252 OSErr error;
1254 /* install open application handler */
1255 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
1256 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false);
1257 if (error)
1259 return error;
1262 /* install quit application handler */
1263 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
1264 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false);
1265 if (error)
1267 return error;
1270 /* install open document handler */
1271 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
1272 NewAEEventHandlerUPP(HandleODocAE), 0, false);
1273 if (error)
1275 return error;
1278 /* install print document handler */
1279 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
1280 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false);
1282 /* Install Core Suite */
1283 /* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
1284 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1286 error = AEInstallEventHandler(kAECoreSuite, kAEClose,
1287 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1289 error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
1290 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1292 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
1293 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1295 error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
1296 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1298 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
1299 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1301 error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
1302 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false);
1304 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
1305 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false);
1307 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
1308 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1310 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
1311 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1313 error = AEInstallEventHandler(kAECoreSuite, kAEMove,
1314 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1316 error = AEInstallEventHandler(kAECoreSuite, kAESave,
1317 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1319 error = AEInstallEventHandler(kAECoreSuite, kAESetData,
1320 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false);
1323 #ifdef FEAT_CW_EDITOR
1325 * Bind codewarrior support handlers
1327 error = AEInstallEventHandler('KAHL', 'GTTX',
1328 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false);
1329 if (error)
1331 return error;
1333 error = AEInstallEventHandler('KAHL', 'SRCH',
1334 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false);
1335 if (error)
1337 return error;
1339 error = AEInstallEventHandler('KAHL', 'MOD ',
1340 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false);
1341 if (error)
1343 return error;
1345 #endif
1347 return error;
1350 #endif /* USE_AEVENT */
1354 * Callback function, installed by InstallFontPanelHandler(), below,
1355 * to handle Font Panel events.
1357 static OSStatus
1358 FontPanelHandler(
1359 EventHandlerCallRef inHandlerCallRef,
1360 EventRef inEvent,
1361 void *inUserData)
1363 if (GetEventKind(inEvent) == kEventFontPanelClosed)
1365 gFontPanelInfo.isPanelVisible = false;
1366 return noErr;
1369 if (GetEventKind(inEvent) == kEventFontSelection)
1371 OSStatus status;
1372 FMFontFamily newFamily;
1373 FMFontSize newSize;
1374 FMFontStyle newStyle;
1376 /* Retrieve the font family ID number. */
1377 status = GetEventParameter(inEvent, kEventParamFMFontFamily,
1378 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL,
1379 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL,
1380 &newFamily);
1381 if (status == noErr)
1382 gFontPanelInfo.family = newFamily;
1384 /* Retrieve the font size. */
1385 status = GetEventParameter(inEvent, kEventParamFMFontSize,
1386 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize);
1387 if (status == noErr)
1388 gFontPanelInfo.size = newSize;
1390 /* Retrieve the font style (bold, etc.). Currently unused. */
1391 status = GetEventParameter(inEvent, kEventParamFMFontStyle,
1392 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle);
1393 if (status == noErr)
1394 gFontPanelInfo.style = newStyle;
1396 return noErr;
1400 static void
1401 InstallFontPanelHandler(void)
1403 EventTypeSpec eventTypes[2];
1404 EventHandlerUPP handlerUPP;
1405 /* EventHandlerRef handlerRef; */
1407 eventTypes[0].eventClass = kEventClassFont;
1408 eventTypes[0].eventKind = kEventFontSelection;
1409 eventTypes[1].eventClass = kEventClassFont;
1410 eventTypes[1].eventKind = kEventFontPanelClosed;
1412 handlerUPP = NewEventHandlerUPP(FontPanelHandler);
1414 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes,
1415 /*userData=*/NULL, /*handlerRef=*/NULL);
1420 * Fill the buffer pointed to by outName with the name and size
1421 * of the font currently selected in the Font Panel.
1423 #define FONT_STYLE_BUFFER_SIZE 32
1424 static void
1425 GetFontPanelSelection(char_u *outName)
1427 Str255 buf;
1428 ByteCount fontNameLen = 0;
1429 ATSUFontID fid;
1430 char_u styleString[FONT_STYLE_BUFFER_SIZE];
1432 if (!outName)
1433 return;
1435 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr)
1437 /* Canonicalize localized font names */
1438 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family,
1439 gFontPanelInfo.style, &fid, NULL) != noErr)
1440 return;
1442 /* Request font name with Mac encoding (otherwise we could
1443 * get an unwanted utf-16 name) */
1444 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform,
1445 kFontNoScriptCode, kFontNoLanguageCode,
1446 255, (char *)outName, &fontNameLen, NULL) != noErr)
1447 return;
1449 /* Only encode font size, because style (bold, italic, etc) is
1450 * already part of the font full name */
1451 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d",
1452 gFontPanelInfo.size/*,
1453 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""),
1454 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""),
1455 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/);
1457 if ((fontNameLen + STRLEN(styleString)) < 255)
1458 STRCPY(outName + fontNameLen, styleString);
1460 else
1462 *outName = NUL;
1468 * ------------------------------------------------------------
1469 * Unfiled yet
1470 * ------------------------------------------------------------
1474 * gui_mac_get_menu_item_index
1476 * Returns the index inside the menu wher
1478 short /* Shoulde we return MenuItemIndex? */
1479 gui_mac_get_menu_item_index(vimmenu_T *pMenu)
1481 short index;
1482 short itemIndex = -1;
1483 vimmenu_T *pBrother;
1485 /* Only menu without parent are the:
1486 * -menu in the menubar
1487 * -popup menu
1488 * -toolbar (guess)
1490 * Which are not items anyway.
1492 if (pMenu->parent)
1494 /* Start from the Oldest Brother */
1495 pBrother = pMenu->parent->children;
1496 index = 1;
1497 while ((pBrother) && (itemIndex == -1))
1499 if (pBrother == pMenu)
1500 itemIndex = index;
1501 index++;
1502 pBrother = pBrother->next;
1505 return itemIndex;
1508 static vimmenu_T *
1509 gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu)
1511 short index;
1512 vimmenu_T *pChildMenu;
1513 vimmenu_T *pElder = pMenu->parent;
1516 /* Only menu without parent are the:
1517 * -menu in the menubar
1518 * -popup menu
1519 * -toolbar (guess)
1521 * Which are not items anyway.
1524 if ((pElder) && (pElder->submenu_id == menuID))
1526 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++)
1527 pMenu = pMenu->next;
1529 else
1531 for (; pMenu != NULL; pMenu = pMenu->next)
1533 if (pMenu->children != NULL)
1535 pChildMenu = gui_mac_get_vim_menu
1536 (menuID, itemIndex, pMenu->children);
1537 if (pChildMenu)
1539 pMenu = pChildMenu;
1540 break;
1545 return pMenu;
1549 * ------------------------------------------------------------
1550 * MacOS Feedback procedures
1551 * ------------------------------------------------------------
1553 pascal
1554 void
1555 gui_mac_drag_thumb(ControlHandle theControl, short partCode)
1557 scrollbar_T *sb;
1558 int value, dragging;
1559 ControlHandle theControlToUse;
1560 int dont_scroll_save = dont_scroll;
1562 theControlToUse = dragged_sb;
1564 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse));
1566 if (sb == NULL)
1567 return;
1569 /* Need to find value by diff between Old Poss New Pos */
1570 value = GetControl32BitValue(theControlToUse);
1571 dragging = (partCode != 0);
1573 /* When "allow_scrollbar" is FALSE still need to remember the new
1574 * position, but don't actually scroll by setting "dont_scroll". */
1575 dont_scroll = !allow_scrollbar;
1576 gui_drag_scrollbar(sb, value, dragging);
1577 dont_scroll = dont_scroll_save;
1580 pascal
1581 void
1582 gui_mac_scroll_action(ControlHandle theControl, short partCode)
1584 /* TODO: have live support */
1585 scrollbar_T *sb, *sb_info;
1586 long data;
1587 long value;
1588 int page;
1589 int dragging = FALSE;
1590 int dont_scroll_save = dont_scroll;
1592 sb = gui_find_scrollbar((long)GetControlReference(theControl));
1594 if (sb == NULL)
1595 return;
1597 if (sb->wp != NULL) /* Left or right scrollbar */
1600 * Careful: need to get scrollbar info out of first (left) scrollbar
1601 * for window, but keep real scrollbar too because we must pass it to
1602 * gui_drag_scrollbar().
1604 sb_info = &sb->wp->w_scrollbars[0];
1606 if (sb_info->size > 5)
1607 page = sb_info->size - 2; /* use two lines of context */
1608 else
1609 page = sb_info->size;
1611 else /* Bottom scrollbar */
1613 sb_info = sb;
1614 page = W_WIDTH(curwin) - 5;
1617 switch (partCode)
1619 case kControlUpButtonPart: data = -1; break;
1620 case kControlDownButtonPart: data = 1; break;
1621 case kControlPageDownPart: data = page; break;
1622 case kControlPageUpPart: data = -page; break;
1623 default: data = 0; break;
1626 value = sb_info->value + data;
1627 /* if (value > sb_info->max)
1628 value = sb_info->max;
1629 else if (value < 0)
1630 value = 0;*/
1632 /* When "allow_scrollbar" is FALSE still need to remember the new
1633 * position, but don't actually scroll by setting "dont_scroll". */
1634 dont_scroll = !allow_scrollbar;
1635 gui_drag_scrollbar(sb, value, dragging);
1636 dont_scroll = dont_scroll_save;
1638 out_flush();
1639 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1641 /* if (sb_info->wp != NULL)
1643 win_T *wp;
1644 int sb_num;
1646 sb_num = 0;
1647 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp))
1648 sb_num++;
1650 if (wp != NULL)
1652 current_scrollbar = sb_num;
1653 scrollbar_value = value;
1654 gui_do_scroll();
1655 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max);
1661 * ------------------------------------------------------------
1662 * MacOS Click Handling procedures
1663 * ------------------------------------------------------------
1668 * Handle a click inside the window, it may happens in the
1669 * scrollbar or the contents.
1671 * TODO: Add support for potential TOOLBAR
1673 void
1674 gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow)
1676 Point thePoint;
1677 int_u vimModifiers;
1678 short thePortion;
1679 ControlHandle theControl;
1680 int vimMouseButton;
1681 short dblClick;
1683 thePoint = theEvent->where;
1684 GlobalToLocal(&thePoint);
1685 SelectWindow(whichWindow);
1687 thePortion = FindControl(thePoint, whichWindow, &theControl);
1689 if (theControl != NUL)
1691 /* We hit a scollbar */
1693 if (thePortion != kControlIndicatorPart)
1695 dragged_sb = theControl;
1696 TrackControl(theControl, thePoint, gScrollAction);
1697 dragged_sb = NULL;
1699 else
1701 dragged_sb = theControl;
1702 #if 1
1703 TrackControl(theControl, thePoint, gScrollDrag);
1704 #else
1705 TrackControl(theControl, thePoint, NULL);
1706 #endif
1707 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse
1708 * button has been released */
1709 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */
1710 dragged_sb = NULL;
1713 else
1715 /* We are inside the contents */
1717 /* Convert the CTRL, OPTION, SHIFT and CMD key */
1718 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
1720 /* Defaults to MOUSE_LEFT as there's only one mouse button */
1721 vimMouseButton = MOUSE_LEFT;
1723 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
1724 /* TODO: NEEDED? */
1725 clickIsPopup = FALSE;
1727 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent))
1729 vimMouseButton = MOUSE_RIGHT;
1730 vimModifiers &= ~MOUSE_CTRL;
1731 clickIsPopup = TRUE;
1734 /* Is it a double click ? */
1735 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
1737 /* Send the mouse click to Vim */
1738 gui_send_mouse_event(vimMouseButton, thePoint.h,
1739 thePoint.v, dblClick, vimModifiers);
1741 /* Create the rectangle around the cursor to detect
1742 * the mouse dragging
1744 #if 0
1745 /* TODO: Do we need to this even for the contextual menu?
1746 * It may be require for popup_setpos, but for popup?
1748 if (vimMouseButton == MOUSE_LEFT)
1749 #endif
1751 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
1752 FILL_Y(Y_2_ROW(thePoint.v)),
1753 FILL_X(X_2_COL(thePoint.h)+1),
1754 FILL_Y(Y_2_ROW(thePoint.v)+1));
1756 dragRectEnbl = TRUE;
1757 dragRectControl = kCreateRect;
1763 * Handle the click in the titlebar (to move the window)
1765 void
1766 gui_mac_doInDragClick(Point where, WindowPtr whichWindow)
1768 Rect movingLimits;
1769 Rect *movingLimitsPtr = &movingLimits;
1771 /* TODO: may try to prevent move outside screen? */
1772 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
1773 DragWindow(whichWindow, where, movingLimitsPtr);
1777 * Handle the click in the grow box
1779 void
1780 gui_mac_doInGrowClick(Point where, WindowPtr whichWindow)
1783 long newSize;
1784 unsigned short newWidth;
1785 unsigned short newHeight;
1786 Rect resizeLimits;
1787 Rect *resizeLimitsPtr = &resizeLimits;
1788 Rect NewContentRect;
1790 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
1792 /* Set the minimum size */
1793 /* TODO: Should this come from Vim? */
1794 resizeLimits.top = 100;
1795 resizeLimits.left = 100;
1797 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect);
1798 newWidth = NewContentRect.right - NewContentRect.left;
1799 newHeight = NewContentRect.bottom - NewContentRect.top;
1800 gui_resize_shell(newWidth, newHeight);
1801 gui_mch_set_bg_color(gui.back_pixel);
1802 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1806 * Handle the click in the zoom box
1808 static void
1809 gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow)
1811 Rect r;
1812 Point p;
1813 short thePart;
1815 /* ideal width is current */
1816 p.h = Columns * gui.char_width + 2 * gui.border_offset;
1817 if (gui.which_scrollbars[SBAR_LEFT])
1818 p.h += gui.scrollbar_width;
1819 if (gui.which_scrollbars[SBAR_RIGHT])
1820 p.h += gui.scrollbar_width;
1821 /* ideal height is as heigh as we can get */
1822 p.v = 15 * 1024;
1824 thePart = IsWindowInStandardState(whichWindow, &p, &r)
1825 ? inZoomIn : inZoomOut;
1827 if (!TrackBox(whichWindow, theEvent->where, thePart))
1828 return;
1830 /* use returned width */
1831 p.h = r.right - r.left;
1832 /* adjust returned height */
1833 p.v = r.bottom - r.top - 2 * gui.border_offset;
1834 if (gui.which_scrollbars[SBAR_BOTTOM])
1835 p.v -= gui.scrollbar_height;
1836 p.v -= p.v % gui.char_height;
1837 p.v += 2 * gui.border_width;
1838 if (gui.which_scrollbars[SBAR_BOTTOM]);
1839 p.v += gui.scrollbar_height;
1841 ZoomWindowIdeal(whichWindow, thePart, &p);
1843 GetWindowBounds(whichWindow, kWindowContentRgn, &r);
1844 gui_resize_shell(r.right - r.left, r.bottom - r.top);
1845 gui_mch_set_bg_color(gui.back_pixel);
1846 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
1850 * ------------------------------------------------------------
1851 * MacOS Event Handling procedure
1852 * ------------------------------------------------------------
1856 * Handle the Update Event
1859 void
1860 gui_mac_doUpdateEvent(EventRecord *event)
1862 WindowPtr whichWindow;
1863 GrafPtr savePort;
1864 RgnHandle updateRgn;
1865 Rect updateRect;
1866 Rect *updateRectPtr;
1867 Rect rc;
1868 Rect growRect;
1869 RgnHandle saveRgn;
1872 updateRgn = NewRgn();
1873 if (updateRgn == NULL)
1874 return;
1876 /* This could be done by the caller as we
1877 * don't require anything else out of the event
1879 whichWindow = (WindowPtr) event->message;
1881 /* Save Current Port */
1882 GetPort(&savePort);
1884 /* Select the Window's Port */
1885 SetPortWindowPort(whichWindow);
1887 /* Let's update the window */
1888 BeginUpdate(whichWindow);
1889 /* Redraw the biggest rectangle covering the area
1890 * to be updated.
1892 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
1893 # if 0
1894 /* Would be more appropriate to use the following but doesn't
1895 * seem to work under MacOS X (Dany)
1897 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
1898 # endif
1900 /* Use the HLock useless in Carbon? Is it harmful?*/
1901 HLock((Handle) updateRgn);
1903 updateRectPtr = GetRegionBounds(updateRgn, &updateRect);
1904 # if 0
1905 /* Code from original Carbon Port (using GetWindowRegion.
1906 * I believe the UpdateRgn is already in local (Dany)
1908 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */
1909 GlobalToLocal(&botRight(updateRect));
1910 # endif
1911 /* Update the content (i.e. the text) */
1912 gui_redraw(updateRectPtr->left, updateRectPtr->top,
1913 updateRectPtr->right - updateRectPtr->left,
1914 updateRectPtr->bottom - updateRectPtr->top);
1915 /* Clear the border areas if needed */
1916 gui_mch_set_bg_color(gui.back_pixel);
1917 if (updateRectPtr->left < FILL_X(0))
1919 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows));
1920 EraseRect(&rc);
1922 if (updateRectPtr->top < FILL_Y(0))
1924 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0));
1925 EraseRect(&rc);
1927 if (updateRectPtr->right > FILL_X(Columns))
1929 SetRect(&rc, FILL_X(Columns), 0,
1930 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows));
1931 EraseRect(&rc);
1933 if (updateRectPtr->bottom > FILL_Y(Rows))
1935 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset,
1936 FILL_Y(Rows) + gui.border_offset);
1937 EraseRect(&rc);
1939 HUnlock((Handle) updateRgn);
1940 DisposeRgn(updateRgn);
1942 /* Update scrollbars */
1943 DrawControls(whichWindow);
1945 /* Update the GrowBox */
1946 /* Taken from FAQ 33-27 */
1947 saveRgn = NewRgn();
1948 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
1949 GetClip(saveRgn);
1950 ClipRect(&growRect);
1951 DrawGrowIcon(whichWindow);
1952 SetClip(saveRgn);
1953 DisposeRgn(saveRgn);
1954 EndUpdate(whichWindow);
1956 /* Restore original Port */
1957 SetPort(savePort);
1961 * Handle the activate/deactivate event
1962 * (apply to a window)
1964 void
1965 gui_mac_doActivateEvent(EventRecord *event)
1967 WindowPtr whichWindow;
1969 whichWindow = (WindowPtr) event->message;
1970 /* Dim scrollbars */
1971 if (whichWindow == gui.VimWindow)
1973 ControlRef rootControl;
1974 GetRootControl(gui.VimWindow, &rootControl);
1975 if ((event->modifiers) & activeFlag)
1976 ActivateControl(rootControl);
1977 else
1978 DeactivateControl(rootControl);
1981 /* Activate */
1982 gui_focus_change((event->modifiers) & activeFlag);
1987 * Handle the suspend/resume event
1988 * (apply to the application)
1990 void
1991 gui_mac_doSuspendEvent(EventRecord *event)
1993 /* The frontmost application just changed */
1995 /* NOTE: the suspend may happen before the deactivate
1996 * seen on MacOS X
1999 /* May not need to change focus as the window will
2000 * get an activate/deactivate event
2002 if (event->message & 1)
2003 /* Resume */
2004 gui_focus_change(TRUE);
2005 else
2006 /* Suspend */
2007 gui_focus_change(FALSE);
2011 * Handle the key
2013 #ifdef USE_CARBONKEYHANDLER
2014 static pascal OSStatus
2015 gui_mac_handle_window_activate(
2016 EventHandlerCallRef nextHandler,
2017 EventRef theEvent,
2018 void *data)
2020 UInt32 eventClass = GetEventClass(theEvent);
2021 UInt32 eventKind = GetEventKind(theEvent);
2023 if (eventClass == kEventClassWindow)
2025 switch (eventKind)
2027 case kEventWindowActivated:
2028 #if defined(USE_IM_CONTROL)
2029 im_on_window_switch(TRUE);
2030 #endif
2031 return noErr;
2033 case kEventWindowDeactivated:
2034 #if defined(USE_IM_CONTROL)
2035 im_on_window_switch(FALSE);
2036 #endif
2037 return noErr;
2041 return eventNotHandledErr;
2044 static pascal OSStatus
2045 gui_mac_handle_text_input(
2046 EventHandlerCallRef nextHandler,
2047 EventRef theEvent,
2048 void *data)
2050 UInt32 eventClass = GetEventClass(theEvent);
2051 UInt32 eventKind = GetEventKind(theEvent);
2053 if (eventClass != kEventClassTextInput)
2054 return eventNotHandledErr;
2056 if ((kEventTextInputUpdateActiveInputArea != eventKind) &&
2057 (kEventTextInputUnicodeForKeyEvent != eventKind) &&
2058 (kEventTextInputOffsetToPos != eventKind) &&
2059 (kEventTextInputPosToOffset != eventKind) &&
2060 (kEventTextInputGetSelectedText != eventKind))
2061 return eventNotHandledErr;
2063 switch (eventKind)
2065 case kEventTextInputUpdateActiveInputArea:
2066 return gui_mac_update_input_area(nextHandler, theEvent);
2067 case kEventTextInputUnicodeForKeyEvent:
2068 return gui_mac_unicode_key_event(nextHandler, theEvent);
2070 case kEventTextInputOffsetToPos:
2071 case kEventTextInputPosToOffset:
2072 case kEventTextInputGetSelectedText:
2073 break;
2076 return eventNotHandledErr;
2079 static pascal
2080 OSStatus gui_mac_update_input_area(
2081 EventHandlerCallRef nextHandler,
2082 EventRef theEvent)
2084 return eventNotHandledErr;
2087 static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the
2088 keys */
2090 # define INLINE_KEY_BUFFER_SIZE 80
2091 static pascal OSStatus
2092 gui_mac_unicode_key_event(
2093 EventHandlerCallRef nextHandler,
2094 EventRef theEvent)
2096 /* Multibyte-friendly key event handler */
2097 OSStatus err = -1;
2098 UInt32 actualSize;
2099 UniChar *text;
2100 char_u result[INLINE_KEY_BUFFER_SIZE];
2101 short len = 0;
2102 UInt32 key_sym;
2103 char charcode;
2104 int key_char;
2105 UInt32 modifiers, vimModifiers;
2106 size_t encLen;
2107 char_u *to = NULL;
2108 Boolean isSpecial = FALSE;
2109 int i;
2110 EventRef keyEvent;
2112 /* Mask the mouse (as per user setting) */
2113 if (p_mh)
2114 ObscureCursor();
2116 /* Don't use the keys when the dialog wants them. */
2117 if (dialog_busy)
2118 return eventNotHandledErr;
2120 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
2121 typeUnicodeText, NULL, 0, &actualSize, NULL))
2122 return eventNotHandledErr;
2124 text = (UniChar *)alloc(actualSize);
2125 if (!text)
2126 return eventNotHandledErr;
2128 err = GetEventParameter(theEvent, kEventParamTextInputSendText,
2129 typeUnicodeText, NULL, actualSize, NULL, text);
2130 require_noerr(err, done);
2132 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent,
2133 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent);
2134 require_noerr(err, done);
2136 err = GetEventParameter(keyEvent, kEventParamKeyModifiers,
2137 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
2138 require_noerr(err, done);
2140 err = GetEventParameter(keyEvent, kEventParamKeyCode,
2141 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym);
2142 require_noerr(err, done);
2144 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes,
2145 typeChar, NULL, sizeof(char), NULL, &charcode);
2146 require_noerr(err, done);
2148 #ifndef USE_CMD_KEY
2149 if (modifiers & cmdKey)
2150 goto done; /* Let system handle Cmd+... */
2151 #endif
2153 key_char = charcode;
2154 vimModifiers = EventModifiers2VimModifiers(modifiers);
2156 /* Find the special key (eg., for cursor keys) */
2157 if (actualSize <= sizeof(UniChar) &&
2158 ((text[0] < 0x20) || (text[0] == 0x7f)))
2160 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i)
2161 if (special_keys[i].key_sym == key_sym)
2163 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2164 special_keys[i].vim_code1);
2165 key_char = simplify_key(key_char,
2166 (int *)&vimModifiers);
2167 isSpecial = TRUE;
2168 break;
2172 /* Intercept CMD-. and CTRL-c */
2173 if (((modifiers & controlKey) && key_char == 'c') ||
2174 ((modifiers & cmdKey) && key_char == '.'))
2175 got_int = TRUE;
2177 if (!isSpecial)
2179 /* remove SHIFT for keys that are already shifted, e.g.,
2180 * '(' and '*' */
2181 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char))
2182 vimModifiers &= ~MOD_MASK_SHIFT;
2184 /* remove CTRL from keys that already have it */
2185 if (key_char < 0x20)
2186 vimModifiers &= ~MOD_MASK_CTRL;
2188 /* don't process unicode characters here */
2189 if (!IS_SPECIAL(key_char))
2191 /* Following code to simplify and consolidate vimModifiers
2192 * taken liberally from gui_w48.c */
2193 key_char = simplify_key(key_char, (int *)&vimModifiers);
2195 /* Interpret META, include SHIFT, etc. */
2196 key_char = extract_modifiers(key_char, (int *)&vimModifiers);
2197 if (key_char == CSI)
2198 key_char = K_CSI;
2200 if (IS_SPECIAL(key_char))
2201 isSpecial = TRUE;
2205 if (vimModifiers)
2207 result[len++] = CSI;
2208 result[len++] = KS_MODIFIER;
2209 result[len++] = vimModifiers;
2212 if (isSpecial && IS_SPECIAL(key_char))
2214 result[len++] = CSI;
2215 result[len++] = K_SECOND(key_char);
2216 result[len++] = K_THIRD(key_char);
2218 else
2220 encLen = actualSize;
2221 to = mac_utf16_to_enc(text, actualSize, &encLen);
2222 if (to)
2224 /* This is basically add_to_input_buf_csi() */
2225 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i)
2227 result[len++] = to[i];
2228 if (to[i] == CSI)
2230 result[len++] = KS_EXTRA;
2231 result[len++] = (int)KE_CSI;
2234 vim_free(to);
2238 add_to_input_buf(result, len);
2239 err = noErr;
2241 done:
2242 vim_free(text);
2243 if (err == noErr)
2245 /* Fake event to wake up WNE (required to get
2246 * key repeat working */
2247 PostEvent(keyUp, 0);
2248 return noErr;
2251 return eventNotHandledErr;
2253 #else
2254 void
2255 gui_mac_doKeyEvent(EventRecord *theEvent)
2257 /* TODO: add support for COMMAND KEY */
2258 long menu;
2259 unsigned char string[20];
2260 short num, i;
2261 short len = 0;
2262 KeySym key_sym;
2263 int key_char;
2264 int modifiers;
2265 int simplify = FALSE;
2267 /* Mask the mouse (as per user setting) */
2268 if (p_mh)
2269 ObscureCursor();
2271 /* Get the key code and it's ASCII representation */
2272 key_sym = ((theEvent->message & keyCodeMask) >> 8);
2273 key_char = theEvent->message & charCodeMask;
2274 num = 1;
2276 /* Intercept CTRL-C */
2277 if (theEvent->modifiers & controlKey)
2279 if (key_char == Ctrl_C && ctrl_c_interrupts)
2280 got_int = TRUE;
2281 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0
2282 && (key_char == '2' || key_char == '6'))
2284 /* CTRL-^ and CTRL-@ don't work in the normal way. */
2285 if (key_char == '2')
2286 key_char = Ctrl_AT;
2287 else
2288 key_char = Ctrl_HAT;
2289 theEvent->modifiers = 0;
2293 /* Intercept CMD-. */
2294 if (theEvent->modifiers & cmdKey)
2295 if (key_char == '.')
2296 got_int = TRUE;
2298 /* Handle command key as per menu */
2299 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */
2300 if (theEvent->modifiers & cmdKey)
2301 /* Only accept CMD alone or with CAPLOCKS and the mouse button.
2302 * Why the mouse button? */
2303 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0)
2305 menu = MenuKey(key_char);
2306 if (HiWord(menu))
2308 gui_mac_handle_menu(menu);
2309 return;
2313 /* Convert the modifiers */
2314 modifiers = EventModifiers2VimModifiers(theEvent->modifiers);
2317 /* Handle special keys. */
2318 #if 0
2319 /* Why has this been removed? */
2320 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey)))
2321 #endif
2323 /* Find the special key (for non-printable keyt_char) */
2324 if ((key_char < 0x20) || (key_char == 0x7f))
2325 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2326 if (special_keys[i].key_sym == key_sym)
2328 # if 0
2329 /* We currently don't have not so special key */
2330 if (special_keys[i].vim_code1 == NUL)
2331 key_char = special_keys[i].vim_code0;
2332 else
2333 # endif
2334 key_char = TO_SPECIAL(special_keys[i].vim_code0,
2335 special_keys[i].vim_code1);
2336 simplify = TRUE;
2337 break;
2341 /* For some keys the modifier is included in the char itself. */
2342 if (simplify || key_char == TAB || key_char == ' ')
2343 key_char = simplify_key(key_char, &modifiers);
2345 /* Add the modifier to the input bu if needed */
2346 /* Do not want SHIFT-A or CTRL-A with modifier */
2347 if (!IS_SPECIAL(key_char)
2348 && key_sym != vk_Space
2349 && key_sym != vk_Tab
2350 && key_sym != vk_Return
2351 && key_sym != vk_Enter
2352 && key_sym != vk_Esc)
2354 #if 1
2355 /* Clear modifiers when only one modifier is set */
2356 if ((modifiers == MOD_MASK_SHIFT)
2357 || (modifiers == MOD_MASK_CTRL)
2358 || (modifiers == MOD_MASK_ALT))
2359 modifiers = 0;
2360 #else
2361 if (modifiers & MOD_MASK_CTRL)
2362 modifiers = modifiers & ~MOD_MASK_CTRL;
2363 if (modifiers & MOD_MASK_ALT)
2364 modifiers = modifiers & ~MOD_MASK_ALT;
2365 if (modifiers & MOD_MASK_SHIFT)
2366 modifiers = modifiers & ~MOD_MASK_SHIFT;
2367 #endif
2369 if (modifiers)
2371 string[len++] = CSI;
2372 string[len++] = KS_MODIFIER;
2373 string[len++] = modifiers;
2376 if (IS_SPECIAL(key_char))
2378 string[len++] = CSI;
2379 string[len++] = K_SECOND(key_char);
2380 string[len++] = K_THIRD(key_char);
2382 else
2384 #ifdef FEAT_MBYTE
2385 /* Convert characters when needed (e.g., from MacRoman to latin1).
2386 * This doesn't work for the NUL byte. */
2387 if (input_conv.vc_type != CONV_NONE && key_char > 0)
2389 char_u from[2], *to;
2390 int l;
2392 from[0] = key_char;
2393 from[1] = NUL;
2394 l = 1;
2395 to = string_convert(&input_conv, from, &l);
2396 if (to != NULL)
2398 for (i = 0; i < l && len < 19; i++)
2400 if (to[i] == CSI)
2402 string[len++] = KS_EXTRA;
2403 string[len++] = KE_CSI;
2405 else
2406 string[len++] = to[i];
2408 vim_free(to);
2410 else
2411 string[len++] = key_char;
2413 else
2414 #endif
2415 string[len++] = key_char;
2418 if (len == 1 && string[0] == CSI)
2420 /* Turn CSI into K_CSI. */
2421 string[ len++ ] = KS_EXTRA;
2422 string[ len++ ] = KE_CSI;
2425 add_to_input_buf(string, len);
2427 #endif
2430 * Handle MouseClick
2432 void
2433 gui_mac_doMouseDownEvent(EventRecord *theEvent)
2435 short thePart;
2436 WindowPtr whichWindow;
2438 thePart = FindWindow(theEvent->where, &whichWindow);
2440 #ifdef FEAT_GUI_TABLINE
2441 /* prevent that the vim window size changes if it's activated by a
2442 click into the tab pane */
2443 if (whichWindow == drawer)
2444 return;
2445 #endif
2447 switch (thePart)
2449 case (inDesk):
2450 /* TODO: what to do? */
2451 break;
2453 case (inMenuBar):
2454 gui_mac_handle_menu(MenuSelect(theEvent->where));
2455 break;
2457 case (inContent):
2458 gui_mac_doInContentClick(theEvent, whichWindow);
2459 break;
2461 case (inDrag):
2462 gui_mac_doInDragClick(theEvent->where, whichWindow);
2463 break;
2465 case (inGrow):
2466 gui_mac_doInGrowClick(theEvent->where, whichWindow);
2467 break;
2469 case (inGoAway):
2470 if (TrackGoAway(whichWindow, theEvent->where))
2471 gui_shell_closed();
2472 break;
2474 case (inZoomIn):
2475 case (inZoomOut):
2476 gui_mac_doInZoomClick(theEvent, whichWindow);
2477 break;
2482 * Handle MouseMoved
2483 * [this event is a moving in and out of a region]
2485 void
2486 gui_mac_doMouseMovedEvent(EventRecord *event)
2488 Point thePoint;
2489 int_u vimModifiers;
2491 thePoint = event->where;
2492 GlobalToLocal(&thePoint);
2493 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers);
2495 if (!Button())
2496 gui_mouse_moved(thePoint.h, thePoint.v);
2497 else
2498 if (!clickIsPopup)
2499 gui_send_mouse_event(MOUSE_DRAG, thePoint.h,
2500 thePoint.v, FALSE, vimModifiers);
2502 /* Reset the region from which we move in and out */
2503 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)),
2504 FILL_Y(Y_2_ROW(thePoint.v)),
2505 FILL_X(X_2_COL(thePoint.h)+1),
2506 FILL_Y(Y_2_ROW(thePoint.v)+1));
2508 if (dragRectEnbl)
2509 dragRectControl = kCreateRect;
2514 * Handle the mouse release
2516 void
2517 gui_mac_doMouseUpEvent(EventRecord *theEvent)
2519 Point thePoint;
2520 int_u vimModifiers;
2522 /* TODO: Properly convert the Contextual menu mouse-up */
2523 /* Potential source of the double menu */
2524 lastMouseTick = theEvent->when;
2525 dragRectEnbl = FALSE;
2526 dragRectControl = kCreateEmpty;
2527 thePoint = theEvent->where;
2528 GlobalToLocal(&thePoint);
2530 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers);
2531 if (clickIsPopup)
2533 vimModifiers &= ~MOUSE_CTRL;
2534 clickIsPopup = FALSE;
2536 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
2539 static pascal OSStatus
2540 gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
2541 void *data)
2543 EventRef bogusEvent;
2544 Point point;
2545 Rect bounds;
2546 UInt32 mod;
2547 SInt32 delta;
2548 int_u vim_mod;
2549 EventMouseWheelAxis axis;
2551 if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis,
2552 typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis)
2553 && axis != kEventMouseWheelAxisY)
2554 goto bail; /* Vim only does up-down scrolling */
2556 if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta,
2557 typeSInt32, NULL, sizeof(SInt32), NULL, &delta))
2558 goto bail;
2559 if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation,
2560 typeQDPoint, NULL, sizeof(Point), NULL, &point))
2561 goto bail;
2562 if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers,
2563 typeUInt32, NULL, sizeof(UInt32), NULL, &mod))
2564 goto bail;
2566 vim_mod = 0;
2567 if (mod & shiftKey)
2568 vim_mod |= MOUSE_SHIFT;
2569 if (mod & controlKey)
2570 vim_mod |= MOUSE_CTRL;
2571 if (mod & optionKey)
2572 vim_mod |= MOUSE_ALT;
2574 /* post a bogus event to wake up WaitNextEvent */
2575 if (noErr != CreateEvent(NULL, kEventClassMouse, kEventMouseMoved, 0,
2576 kEventAttributeNone, &bogusEvent))
2577 goto bail;
2578 if (noErr != PostEventToQueue(GetMainEventQueue(), bogusEvent,
2579 kEventPriorityLow))
2580 goto bail;
2582 ReleaseEvent(bogusEvent);
2584 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds))
2586 point.h -= bounds.left;
2587 point.v -= bounds.top;
2590 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5,
2591 point.h, point.v, FALSE, vim_mod);
2593 return noErr;
2595 bail:
2597 * when we fail give any additional callback handler a chance to perform
2598 * it's actions
2600 return CallNextEventHandler(nextHandler, theEvent);
2603 #if 0
2606 * This would be the normal way of invoking the contextual menu
2607 * but the Vim API doesn't seem to a support a request to get
2608 * the menu that we should display
2610 void
2611 gui_mac_handle_contextual_menu(event)
2612 EventRecord *event;
2615 * Clone PopUp to use menu
2616 * Create a object descriptor for the current selection
2617 * Call the procedure
2620 // Call to Handle Popup
2621 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
2623 if (status != noErr)
2624 return;
2626 if (CntxType == kCMMenuItemSelected)
2628 /* Handle the menu CntxMenuID, CntxMenuItem */
2629 /* The submenu can be handle directly by gui_mac_handle_menu */
2630 /* But what about the current menu, is the meny changed by ContextualMenuSelect */
2631 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
2633 else if (CntxMenuID == kCMShowHelpSelected)
2635 /* Should come up with the help */
2639 #endif
2642 * Handle menubar selection
2644 void
2645 gui_mac_handle_menu(long menuChoice)
2647 short menu = HiWord(menuChoice);
2648 short item = LoWord(menuChoice);
2649 vimmenu_T *theVimMenu = root_menu;
2651 if (menu == 256) /* TODO: use constant or gui.xyz */
2653 if (item == 1)
2654 gui_mch_beep(); /* TODO: Popup dialog or do :intro */
2656 else if (item != 0)
2658 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu);
2660 if (theVimMenu)
2661 gui_menu_cb(theVimMenu);
2663 HiliteMenu(0);
2667 * Dispatch the event to proper handler
2670 void
2671 gui_mac_handle_event(EventRecord *event)
2673 OSErr error;
2675 /* Handle contextual menu right now (if needed) */
2676 if (IsShowContextualMenuClick(event))
2678 # if 0
2679 gui_mac_handle_contextual_menu(event);
2680 # else
2681 gui_mac_doMouseDownEvent(event);
2682 # endif
2683 return;
2686 /* Handle normal event */
2687 switch (event->what)
2689 #ifndef USE_CARBONKEYHANDLER
2690 case (keyDown):
2691 case (autoKey):
2692 gui_mac_doKeyEvent(event);
2693 break;
2694 #endif
2695 case (keyUp):
2696 /* We don't care about when the key is released */
2697 break;
2699 case (mouseDown):
2700 gui_mac_doMouseDownEvent(event);
2701 break;
2703 case (mouseUp):
2704 gui_mac_doMouseUpEvent(event);
2705 break;
2707 case (updateEvt):
2708 gui_mac_doUpdateEvent(event);
2709 break;
2711 case (diskEvt):
2712 /* We don't need special handling for disk insertion */
2713 break;
2715 case (activateEvt):
2716 gui_mac_doActivateEvent(event);
2717 break;
2719 case (osEvt):
2720 switch ((event->message >> 24) & 0xFF)
2722 case (0xFA): /* mouseMovedMessage */
2723 gui_mac_doMouseMovedEvent(event);
2724 break;
2725 case (0x01): /* suspendResumeMessage */
2726 gui_mac_doSuspendEvent(event);
2727 break;
2729 break;
2731 #ifdef USE_AEVENT
2732 case (kHighLevelEvent):
2733 /* Someone's talking to us, through AppleEvents */
2734 error = AEProcessAppleEvent(event); /* TODO: Error Handling */
2735 break;
2736 #endif
2741 * ------------------------------------------------------------
2742 * Unknown Stuff
2743 * ------------------------------------------------------------
2747 GuiFont
2748 gui_mac_find_font(char_u *font_name)
2750 char_u c;
2751 char_u *p;
2752 char_u pFontName[256];
2753 Str255 systemFontname;
2754 short font_id;
2755 short size=9;
2756 GuiFont font;
2757 #if 0
2758 char_u *fontNamePtr;
2759 #endif
2761 for (p = font_name; ((*p != 0) && (*p != ':')); p++)
2764 c = *p;
2765 *p = 0;
2767 #if 1
2768 STRCPY(&pFontName[1], font_name);
2769 pFontName[0] = STRLEN(font_name);
2770 *p = c;
2772 /* Get the font name, minus the style suffix (:h, etc) */
2773 char_u fontName[256];
2774 char_u *styleStart = vim_strchr(font_name, ':');
2775 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName);
2776 vim_strncpy(fontName, font_name, fontNameLen);
2778 ATSUFontID fontRef;
2779 FMFontStyle fontStyle;
2780 font_id = 0;
2782 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName,
2783 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode,
2784 &fontRef) == noErr)
2786 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2787 font_id = 0;
2790 if (font_id == 0)
2793 * Try again, this time replacing underscores in the font name
2794 * with spaces (:set guifont allows the two to be used
2795 * interchangeably; the Font Manager doesn't).
2797 int i, changed = FALSE;
2799 for (i = pFontName[0]; i > 0; --i)
2801 if (pFontName[i] == '_')
2803 pFontName[i] = ' ';
2804 changed = TRUE;
2807 if (changed)
2808 if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
2809 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
2810 kFontNoLanguageCode, &fontRef) == noErr)
2812 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
2813 font_id = 0;
2817 #else
2818 /* name = C2Pascal_save(menu->dname); */
2819 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name);
2821 GetFNum(fontNamePtr, &font_id);
2822 #endif
2825 if (font_id == 0)
2827 /* Oups, the system font was it the one the user want */
2829 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
2830 return NOFONT;
2831 if (!EqualString(pFontName, systemFontname, false, false))
2832 return NOFONT;
2834 if (*p == ':')
2836 p++;
2837 /* Set the values found after ':' */
2838 while (*p)
2840 switch (*p++)
2842 case 'h':
2843 size = points_to_pixels(p, &p, TRUE);
2844 break;
2846 * TODO: Maybe accept width and styles
2849 while (*p == ':')
2850 p++;
2854 if (size < 1)
2855 size = 1; /* Avoid having a size of 0 with system font */
2857 font = (size << 16) + ((long) font_id & 0xFFFF);
2859 return font;
2863 * ------------------------------------------------------------
2864 * GUI_MCH functionality
2865 * ------------------------------------------------------------
2869 * Parse the GUI related command-line arguments. Any arguments used are
2870 * deleted from argv, and *argc is decremented accordingly. This is called
2871 * when vim is started, whether or not the GUI has been started.
2873 void
2874 gui_mch_prepare(int *argc, char **argv)
2876 /* TODO: Move most of this stuff toward gui_mch_init */
2877 #ifdef USE_EXE_NAME
2878 FSSpec applDir;
2879 # ifndef USE_FIND_BUNDLE_PATH
2880 short applVRefNum;
2881 long applDirID;
2882 Str255 volName;
2883 # else
2884 ProcessSerialNumber psn;
2885 FSRef applFSRef;
2886 # endif
2887 #endif
2889 #if 0
2890 InitCursor();
2892 RegisterAppearanceClient();
2894 #ifdef USE_AEVENT
2895 (void) InstallAEHandlers();
2896 #endif
2898 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
2900 AppendMenu(pomme, "\pAbout VIM");
2902 InsertMenu(pomme, 0);
2904 DrawMenuBar();
2907 #ifndef USE_OFFSETED_WINDOW
2908 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
2909 #else
2910 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
2911 #endif
2914 CreateNewWindow(kDocumentWindowClass,
2915 kWindowResizableAttribute | kWindowCollapseBoxAttribute,
2916 &windRect, &gui.VimWindow);
2917 SetPortWindowPort(gui.VimWindow);
2919 gui.char_width = 7;
2920 gui.char_height = 11;
2921 gui.char_ascent = 6;
2922 gui.num_rows = 24;
2923 gui.num_cols = 80;
2924 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
2926 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
2927 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
2929 dragRectEnbl = FALSE;
2930 dragRgn = NULL;
2931 dragRectControl = kCreateEmpty;
2932 cursorRgn = NewRgn();
2933 #endif
2934 #ifdef USE_EXE_NAME
2935 # ifndef USE_FIND_BUNDLE_PATH
2936 HGetVol(volName, &applVRefNum, &applDirID);
2937 /* TN2015: mention a possible bad VRefNum */
2938 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir);
2939 # else
2940 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
2941 * of TN2015
2943 (void)GetCurrentProcess(&psn);
2944 /* if (err != noErr) return err; */
2946 (void)GetProcessBundleLocation(&psn, &applFSRef);
2947 /* if (err != noErr) return err; */
2949 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL);
2951 /* This technic return NIL when we disallow_gui */
2952 # endif
2953 exe_name = FullPathFromFSSpec_save(applDir);
2954 #endif
2957 #ifndef ALWAYS_USE_GUI
2959 * Check if the GUI can be started. Called before gvimrc is sourced.
2960 * Return OK or FAIL.
2963 gui_mch_init_check(void)
2965 /* TODO: For MacOS X find a way to return FAIL, if the user logged in
2966 * using the >console
2968 if (disallow_gui) /* see main.c for reason to disallow */
2969 return FAIL;
2970 return OK;
2972 #endif
2974 static OSErr
2975 receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag)
2977 int x, y;
2978 int_u modifiers;
2979 char_u **fnames = NULL;
2980 int count;
2981 int i, j;
2983 /* Get drop position, modifiers and count of items */
2985 Point point;
2986 SInt16 mouseUpModifiers;
2987 UInt16 countItem;
2989 GetDragMouse(theDrag, &point, NULL);
2990 GlobalToLocal(&point);
2991 x = point.h;
2992 y = point.v;
2993 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers);
2994 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers);
2995 CountDragItems(theDrag, &countItem);
2996 count = countItem;
2999 fnames = (char_u **)alloc(count * sizeof(char_u *));
3000 if (fnames == NULL)
3001 return dragNotAcceptedErr;
3003 /* Get file names dropped */
3004 for (i = j = 0; i < count; ++i)
3006 DragItemRef item;
3007 OSErr err;
3008 Size size;
3009 FlavorType type = flavorTypeHFS;
3010 HFSFlavor hfsFlavor;
3012 fnames[i] = NULL;
3013 GetDragItemReferenceNumber(theDrag, i + 1, &item);
3014 err = GetFlavorDataSize(theDrag, item, type, &size);
3015 if (err != noErr || size > sizeof(hfsFlavor))
3016 continue;
3017 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0);
3018 if (err != noErr)
3019 continue;
3020 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec);
3022 count = j;
3024 gui_handle_drop(x, y, modifiers, fnames, count);
3026 /* Fake mouse event to wake from stall */
3027 PostEvent(mouseUp, 0);
3029 return noErr;
3033 * Initialise the GUI. Create all the windows, set up all the call-backs
3034 * etc.
3037 gui_mch_init(void)
3039 /* TODO: Move most of this stuff toward gui_mch_init */
3040 Rect windRect;
3041 MenuHandle pomme;
3042 EventHandlerRef mouseWheelHandlerRef;
3043 EventTypeSpec eventTypeSpec;
3044 ControlRef rootControl;
3046 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
3047 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */
3049 #if 1
3050 InitCursor();
3052 RegisterAppearanceClient();
3054 #ifdef USE_AEVENT
3055 (void) InstallAEHandlers();
3056 #endif
3058 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
3060 AppendMenu(pomme, "\pAbout VIM");
3062 InsertMenu(pomme, 0);
3064 DrawMenuBar();
3067 #ifndef USE_OFFSETED_WINDOW
3068 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11);
3069 #else
3070 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11);
3071 #endif
3073 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true,
3074 zoomDocProc,
3075 (WindowPtr)-1L, true, 0);
3076 CreateRootControl(gui.VimWindow, &rootControl);
3077 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
3078 gui.VimWindow, NULL);
3079 SetPortWindowPort(gui.VimWindow);
3081 gui.char_width = 7;
3082 gui.char_height = 11;
3083 gui.char_ascent = 6;
3084 gui.num_rows = 24;
3085 gui.num_cols = 80;
3086 gui.in_focus = TRUE; /* For the moment -> syn. of front application */
3088 gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
3089 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
3091 /* Install Carbon event callbacks. */
3092 (void)InstallFontPanelHandler();
3094 dragRectEnbl = FALSE;
3095 dragRgn = NULL;
3096 dragRectControl = kCreateEmpty;
3097 cursorRgn = NewRgn();
3098 #endif
3099 /* Display any pending error messages */
3100 display_errors();
3102 /* Get background/foreground colors from system */
3103 /* TODO: do the appropriate call to get real defaults */
3104 gui.norm_pixel = 0x00000000;
3105 gui.back_pixel = 0x00FFFFFF;
3107 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3108 * file). */
3109 set_normal_colors();
3112 * Check that none of the colors are the same as the background color.
3113 * Then store the current values as the defaults.
3115 gui_check_colors();
3116 gui.def_norm_pixel = gui.norm_pixel;
3117 gui.def_back_pixel = gui.back_pixel;
3119 /* Get the colors for the highlight groups (gui_check_colors() might have
3120 * changed them) */
3121 highlight_gui_started();
3124 * Setting the gui constants
3126 #ifdef FEAT_MENU
3127 gui.menu_height = 0;
3128 #endif
3129 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */
3130 gui.border_offset = gui.border_width = 2;
3132 /* If Quartz-style text anti aliasing is available (see
3133 gui_mch_draw_string() below), enable it for all font sizes. */
3134 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
3136 eventTypeSpec.eventClass = kEventClassMouse;
3137 eventTypeSpec.eventKind = kEventMouseWheelMoved;
3138 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel);
3139 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1,
3140 &eventTypeSpec, NULL, &mouseWheelHandlerRef))
3142 mouseWheelHandlerRef = NULL;
3143 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3144 mouseWheelHandlerUPP = NULL;
3147 #ifdef USE_CARBONKEYHANDLER
3148 InterfaceTypeList supportedServices = { kUnicodeDocument };
3149 NewTSMDocument(1, supportedServices, &gTSMDocument, 0);
3151 /* We don't support inline input yet, use input window by default */
3152 UseInputWindow(gTSMDocument, TRUE);
3154 /* Should we activate the document by default? */
3155 // ActivateTSMDocument(gTSMDocument);
3157 EventTypeSpec textEventTypes[] = {
3158 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea },
3159 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
3160 { kEventClassTextInput, kEventTextInputPosToOffset },
3161 { kEventClassTextInput, kEventTextInputOffsetToPos },
3164 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input);
3165 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP,
3166 NR_ELEMS(textEventTypes),
3167 textEventTypes, NULL, NULL))
3169 DisposeEventHandlerUPP(keyEventHandlerUPP);
3170 keyEventHandlerUPP = NULL;
3173 EventTypeSpec windowEventTypes[] = {
3174 { kEventClassWindow, kEventWindowActivated },
3175 { kEventClassWindow, kEventWindowDeactivated },
3178 /* Install window event handler to support TSMDocument activate and
3179 * deactivate */
3180 winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate);
3181 if (noErr != InstallWindowEventHandler(gui.VimWindow,
3182 winEventHandlerUPP,
3183 NR_ELEMS(windowEventTypes),
3184 windowEventTypes, NULL, NULL))
3186 DisposeEventHandlerUPP(winEventHandlerUPP);
3187 winEventHandlerUPP = NULL;
3189 #endif
3192 #ifdef FEAT_MBYTE
3193 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
3194 #endif
3197 #ifdef FEAT_GUI_TABLINE
3199 * Create the tabline
3201 initialise_tabline();
3202 #endif
3204 /* TODO: Load bitmap if using TOOLBAR */
3205 return OK;
3209 * Called when the foreground or background color has been changed.
3211 void
3212 gui_mch_new_colors(void)
3214 /* TODO:
3215 * This proc is called when Normal is set to a value
3216 * so what msut be done? I don't know
3221 * Open the GUI window which was created by a call to gui_mch_init().
3224 gui_mch_open(void)
3226 ShowWindow(gui.VimWindow);
3228 if (gui_win_x != -1 && gui_win_y != -1)
3229 gui_mch_set_winpos(gui_win_x, gui_win_y);
3232 * Make the GUI the foreground process (in case it was launched
3233 * from the Terminal or via :gui).
3236 ProcessSerialNumber psn;
3237 if (GetCurrentProcess(&psn) == noErr)
3238 SetFrontProcess(&psn);
3241 return OK;
3244 #ifdef USE_ATSUI_DRAWING
3245 static void
3246 gui_mac_dispose_atsui_style(void)
3248 if (p_macatsui && gFontStyle)
3249 ATSUDisposeStyle(gFontStyle);
3250 #ifdef FEAT_MBYTE
3251 if (p_macatsui && gWideFontStyle)
3252 ATSUDisposeStyle(gWideFontStyle);
3253 #endif
3255 #endif
3257 void
3258 gui_mch_exit(int rc)
3260 /* TODO: find out all what is missing here? */
3261 DisposeRgn(cursorRgn);
3263 #ifdef USE_CARBONKEYHANDLER
3264 if (keyEventHandlerUPP)
3265 DisposeEventHandlerUPP(keyEventHandlerUPP);
3266 #endif
3268 if (mouseWheelHandlerUPP != NULL)
3269 DisposeEventHandlerUPP(mouseWheelHandlerUPP);
3271 #ifdef USE_ATSUI_DRAWING
3272 gui_mac_dispose_atsui_style();
3273 #endif
3275 #ifdef USE_CARBONKEYHANDLER
3276 FixTSMDocument(gTSMDocument);
3277 DeactivateTSMDocument(gTSMDocument);
3278 DeleteTSMDocument(gTSMDocument);
3279 #endif
3281 /* Exit to shell? */
3282 exit(rc);
3286 * Get the position of the top left corner of the window.
3289 gui_mch_get_winpos(int *x, int *y)
3291 /* TODO */
3292 Rect bounds;
3293 OSStatus status;
3295 /* Carbon >= 1.0.2, MacOS >= 8.5 */
3296 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds);
3298 if (status != noErr)
3299 return FAIL;
3300 *x = bounds.left;
3301 *y = bounds.top;
3302 return OK;
3303 return FAIL;
3307 * Set the position of the top left corner of the window to the given
3308 * coordinates.
3310 void
3311 gui_mch_set_winpos(int x, int y)
3313 /* TODO: Should make sure the window is move within range
3314 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width
3316 MoveWindowStructure(gui.VimWindow, x, y);
3319 void
3320 gui_mch_set_shellsize(
3321 int width,
3322 int height,
3323 int min_width,
3324 int min_height,
3325 int base_width,
3326 int base_height,
3327 int direction)
3329 CGrafPtr VimPort;
3330 Rect VimBound;
3332 if (gui.which_scrollbars[SBAR_LEFT])
3334 VimPort = GetWindowPort(gui.VimWindow);
3335 GetPortBounds(VimPort, &VimBound);
3336 VimBound.left = -gui.scrollbar_width; /* + 1;*/
3337 SetPortBounds(VimPort, &VimBound);
3338 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
3340 else
3342 VimPort = GetWindowPort(gui.VimWindow);
3343 GetPortBounds(VimPort, &VimBound);
3344 VimBound.left = 0;
3345 SetPortBounds(VimPort, &VimBound);
3348 SizeWindow(gui.VimWindow, width, height, TRUE);
3350 gui_resize_shell(width, height);
3354 * Get the screen dimensions.
3355 * Allow 10 pixels for horizontal borders, 40 for vertical borders.
3356 * Is there no way to find out how wide the borders really are?
3357 * TODO: Add live update of those value on suspend/resume.
3359 void
3360 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3362 GDHandle dominantDevice = GetMainDevice();
3363 Rect screenRect = (**dominantDevice).gdRect;
3365 *screen_w = screenRect.right - 10;
3366 *screen_h = screenRect.bottom - 40;
3371 * Open the Font Panel and wait for the user to select a font and
3372 * close the panel. Then fill the buffer pointed to by font_name with
3373 * the name and size of the selected font and return the font's handle,
3374 * or NOFONT in case of an error.
3376 static GuiFont
3377 gui_mac_select_font(char_u *font_name)
3379 GuiFont selected_font = NOFONT;
3380 OSStatus status;
3381 FontSelectionQDStyle curr_font;
3383 /* Initialize the Font Panel with the current font. */
3384 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF;
3385 curr_font.size = (gui.norm_font >> 16);
3386 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */
3387 curr_font.instance.fontStyle = 0;
3388 curr_font.hasColor = false;
3389 curr_font.version = 0; /* version number of the style structure */
3390 status = SetFontInfoForSelection(kFontSelectionQDType,
3391 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL);
3393 gFontPanelInfo.family = curr_font.instance.fontFamily;
3394 gFontPanelInfo.style = curr_font.instance.fontStyle;
3395 gFontPanelInfo.size = curr_font.size;
3397 /* Pop up the Font Panel. */
3398 status = FPShowHideFontPanel();
3399 if (status == noErr)
3402 * The Font Panel is modeless. We really need it to be modal,
3403 * so we spin in an event loop until the panel is closed.
3405 gFontPanelInfo.isPanelVisible = true;
3406 while (gFontPanelInfo.isPanelVisible)
3408 EventRecord e;
3409 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL);
3412 GetFontPanelSelection(font_name);
3413 selected_font = gui_mac_find_font(font_name);
3415 return selected_font;
3418 #ifdef USE_ATSUI_DRAWING
3419 static void
3420 gui_mac_create_atsui_style(void)
3422 if (p_macatsui && gFontStyle == NULL)
3424 if (ATSUCreateStyle(&gFontStyle) != noErr)
3425 gFontStyle = NULL;
3427 #ifdef FEAT_MBYTE
3428 if (p_macatsui && gWideFontStyle == NULL)
3430 if (ATSUCreateStyle(&gWideFontStyle) != noErr)
3431 gWideFontStyle = NULL;
3433 #endif
3435 p_macatsui_last = p_macatsui;
3437 #endif
3440 * Initialise vim to use the font with the given name. Return FAIL if the font
3441 * could not be loaded, OK otherwise.
3444 gui_mch_init_font(char_u *font_name, int fontset)
3446 /* TODO: Add support for bold italic underline proportional etc... */
3447 Str255 suggestedFont = "\pMonaco";
3448 int suggestedSize = 10;
3449 FontInfo font_info;
3450 short font_id;
3451 GuiFont font;
3452 char_u used_font_name[512];
3454 #ifdef USE_ATSUI_DRAWING
3455 gui_mac_create_atsui_style();
3456 #endif
3458 if (font_name == NULL)
3460 /* First try to get the suggested font */
3461 GetFNum(suggestedFont, &font_id);
3463 if (font_id == 0)
3465 /* Then pickup the standard application font */
3466 font_id = GetAppFont();
3467 STRCPY(used_font_name, "default");
3469 else
3470 STRCPY(used_font_name, "Monaco");
3471 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF);
3473 else if (STRCMP(font_name, "*") == 0)
3475 char_u *new_p_guifont;
3477 font = gui_mac_select_font(used_font_name);
3478 if (font == NOFONT)
3479 return FAIL;
3481 /* Set guifont to the name of the selected font. */
3482 new_p_guifont = alloc(STRLEN(used_font_name) + 1);
3483 if (new_p_guifont != NULL)
3485 STRCPY(new_p_guifont, used_font_name);
3486 vim_free(p_guifont);
3487 p_guifont = new_p_guifont;
3488 /* Replace spaces in the font name with underscores. */
3489 for ( ; *new_p_guifont; ++new_p_guifont)
3491 if (*new_p_guifont == ' ')
3492 *new_p_guifont = '_';
3496 else
3498 font = gui_mac_find_font(font_name);
3499 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
3501 if (font == NOFONT)
3502 return FAIL;
3505 gui.norm_font = font;
3507 hl_set_font_name(used_font_name);
3509 TextSize(font >> 16);
3510 TextFont(font & 0xFFFF);
3512 GetFontInfo(&font_info);
3514 gui.char_ascent = font_info.ascent;
3515 gui.char_width = CharWidth('_');
3516 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3518 #ifdef USE_ATSUI_DRAWING
3519 if (p_macatsui && gFontStyle)
3520 gui_mac_set_font_attributes(font);
3521 #endif
3523 return OK;
3527 * Adjust gui.char_height (after 'linespace' was changed).
3530 gui_mch_adjust_charheight(void)
3532 FontInfo font_info;
3534 GetFontInfo(&font_info);
3535 gui.char_height = font_info.ascent + font_info.descent + p_linespace;
3536 gui.char_ascent = font_info.ascent + p_linespace / 2;
3537 return OK;
3541 * Get a font structure for highlighting.
3543 GuiFont
3544 gui_mch_get_font(char_u *name, int giveErrorIfMissing)
3546 GuiFont font;
3548 font = gui_mac_find_font(name);
3550 if (font == NOFONT)
3552 if (giveErrorIfMissing)
3553 EMSG2(_(e_font), name);
3554 return NOFONT;
3557 * TODO : Accept only monospace
3560 return font;
3563 #if defined(FEAT_EVAL) || defined(PROTO)
3565 * Return the name of font "font" in allocated memory.
3566 * Don't know how to get the actual name, thus use the provided name.
3568 char_u *
3569 gui_mch_get_fontname(GuiFont font, char_u *name)
3571 if (name == NULL)
3572 return NULL;
3573 return vim_strsave(name);
3575 #endif
3577 #ifdef USE_ATSUI_DRAWING
3578 static void
3579 gui_mac_set_font_attributes(GuiFont font)
3581 ATSUFontID fontID;
3582 Fixed fontSize;
3583 Fixed fontWidth;
3585 fontID = font & 0xFFFF;
3586 fontSize = Long2Fix(font >> 16);
3587 fontWidth = Long2Fix(gui.char_width);
3589 ATSUAttributeTag attribTags[] =
3591 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag,
3592 kATSUMaxATSUITagValue + 1
3595 ByteCount attribSizes[] =
3597 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth),
3598 sizeof(font)
3601 ATSUAttributeValuePtr attribValues[] =
3603 &fontID, &fontSize, &fontWidth, &font
3606 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
3608 if (ATSUSetAttributes(gFontStyle,
3609 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3610 attribTags, attribSizes, attribValues) != noErr)
3612 # ifndef NDEBUG
3613 fprintf(stderr, "couldn't set font style\n");
3614 # endif
3615 ATSUDisposeStyle(gFontStyle);
3616 gFontStyle = NULL;
3619 #ifdef FEAT_MBYTE
3620 if (has_mbyte)
3622 /* FIXME: we should use a more mbyte sensitive way to support
3623 * wide font drawing */
3624 fontWidth = Long2Fix(gui.char_width * 2);
3626 if (ATSUSetAttributes(gWideFontStyle,
3627 (sizeof attribTags) / sizeof(ATSUAttributeTag),
3628 attribTags, attribSizes, attribValues) != noErr)
3630 ATSUDisposeStyle(gWideFontStyle);
3631 gWideFontStyle = NULL;
3634 #endif
3637 #endif
3640 * Set the current text font.
3642 void
3643 gui_mch_set_font(GuiFont font)
3645 #ifdef USE_ATSUI_DRAWING
3646 GuiFont currFont;
3647 ByteCount actualFontByteCount;
3649 if (p_macatsui && gFontStyle)
3651 /* Avoid setting same font again */
3652 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1,
3653 sizeof(font), &currFont, &actualFontByteCount) == noErr
3654 && actualFontByteCount == (sizeof font))
3656 if (currFont == font)
3657 return;
3660 gui_mac_set_font_attributes(font);
3663 if (p_macatsui && !gIsFontFallbackSet)
3665 /* Setup automatic font substitution. The user's guifontwide
3666 * is tried first, then the system tries other fonts. */
3668 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
3669 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
3670 ATSUCreateFontFallbacks(&gFontFallbacks);
3671 ATSUSetObjFontFallbacks(gFontFallbacks, );
3673 if (gui.wide_font)
3675 ATSUFontID fallbackFonts;
3676 gIsFontFallbackSet = TRUE;
3678 if (FMGetFontFromFontFamilyInstance(
3679 (gui.wide_font & 0xFFFF),
3681 &fallbackFonts,
3682 NULL) == noErr)
3684 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID),
3685 &fallbackFonts,
3686 kATSUSequentialFallbacksPreferred);
3689 ATSUAttributeValuePtr fallbackValues[] = { };
3693 #endif
3694 TextSize(font >> 16);
3695 TextFont(font & 0xFFFF);
3699 * If a font is not going to be used, free its structure.
3701 void
3702 gui_mch_free_font(font)
3703 GuiFont font;
3706 * Free font when "font" is not 0.
3707 * Nothing to do in the current implementation, since
3708 * nothing is allocated for each font used.
3712 static int
3713 hex_digit(int c)
3715 if (isdigit(c))
3716 return c - '0';
3717 c = TOLOWER_ASC(c);
3718 if (c >= 'a' && c <= 'f')
3719 return c - 'a' + 10;
3720 return -1000;
3724 * Return the Pixel value (color) for the given color name. This routine was
3725 * pretty much taken from example code in the Silicon Graphics OSF/Motif
3726 * Programmer's Guide.
3727 * Return INVALCOLOR when failed.
3729 guicolor_T
3730 gui_mch_get_color(char_u *name)
3732 /* TODO: Add support for the new named color of MacOS 8
3734 RGBColor MacColor;
3735 // guicolor_T color = 0;
3737 typedef struct guicolor_tTable
3739 char *name;
3740 guicolor_T color;
3741 } guicolor_tTable;
3744 * The comment at the end of each line is the source
3745 * (Mac, Window, Unix) and the number is the unix rgb.txt value
3747 static guicolor_tTable table[] =
3749 {"Black", RGB(0x00, 0x00, 0x00)},
3750 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
3751 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
3752 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3753 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
3754 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3755 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
3756 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3757 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/
3758 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/
3759 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/
3760 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3761 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/
3762 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/
3763 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/
3764 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3765 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/
3766 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/
3767 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/
3768 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3769 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/
3770 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3771 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/
3772 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3773 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/
3774 {"white", RGB(0xFF, 0xFF, 0xFF)},
3775 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
3776 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
3777 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
3778 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
3779 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
3780 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
3781 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
3782 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
3783 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
3784 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
3785 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
3786 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
3787 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
3788 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
3789 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
3790 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
3791 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
3792 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
3793 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
3794 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
3795 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
3796 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
3797 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
3798 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
3801 int r, g, b;
3802 int i;
3804 if (name[0] == '#' && strlen((char *) name) == 7)
3806 /* Name is in "#rrggbb" format */
3807 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
3808 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
3809 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
3810 if (r < 0 || g < 0 || b < 0)
3811 return INVALCOLOR;
3812 return RGB(r, g, b);
3814 else
3816 if (STRICMP(name, "hilite") == 0)
3818 LMGetHiliteRGB(&MacColor);
3819 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
3821 /* Check if the name is one of the colors we know */
3822 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
3823 if (STRICMP(name, table[i].name) == 0)
3824 return table[i].color;
3828 * Last attempt. Look in the file "$VIM/rgb.txt".
3831 #define LINE_LEN 100
3832 FILE *fd;
3833 char line[LINE_LEN];
3834 char_u *fname;
3836 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
3837 if (fname == NULL)
3838 return INVALCOLOR;
3840 fd = fopen((char *)fname, "rt");
3841 vim_free(fname);
3842 if (fd == NULL)
3843 return INVALCOLOR;
3845 while (!feof(fd))
3847 int len;
3848 int pos;
3849 char *color;
3851 fgets(line, LINE_LEN, fd);
3852 len = strlen(line);
3854 if (len <= 1 || line[len-1] != '\n')
3855 continue;
3857 line[len-1] = '\0';
3859 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
3860 if (i != 3)
3861 continue;
3863 color = line + pos;
3865 if (STRICMP(color, name) == 0)
3867 fclose(fd);
3868 return (guicolor_T) RGB(r, g, b);
3871 fclose(fd);
3874 return INVALCOLOR;
3878 * Set the current text foreground color.
3880 void
3881 gui_mch_set_fg_color(guicolor_T color)
3883 RGBColor TheColor;
3885 TheColor.red = Red(color) * 0x0101;
3886 TheColor.green = Green(color) * 0x0101;
3887 TheColor.blue = Blue(color) * 0x0101;
3889 RGBForeColor(&TheColor);
3893 * Set the current text background color.
3895 void
3896 gui_mch_set_bg_color(guicolor_T color)
3898 RGBColor TheColor;
3900 TheColor.red = Red(color) * 0x0101;
3901 TheColor.green = Green(color) * 0x0101;
3902 TheColor.blue = Blue(color) * 0x0101;
3904 RGBBackColor(&TheColor);
3907 RGBColor specialColor;
3910 * Set the current text special color.
3912 void
3913 gui_mch_set_sp_color(guicolor_T color)
3915 specialColor.red = Red(color) * 0x0101;
3916 specialColor.green = Green(color) * 0x0101;
3917 specialColor.blue = Blue(color) * 0x0101;
3921 * Draw undercurl at the bottom of the character cell.
3923 static void
3924 draw_undercurl(int flags, int row, int col, int cells)
3926 int x;
3927 int offset;
3928 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
3929 int y = FILL_Y(row + 1) - 1;
3931 RGBForeColor(&specialColor);
3933 offset = val[FILL_X(col) % 8];
3934 MoveTo(FILL_X(col), y - offset);
3936 for (x = FILL_X(col); x < FILL_X(col + cells); ++x)
3938 offset = val[x % 8];
3939 LineTo(x, y - offset);
3944 static void
3945 draw_string_QD(int row, int col, char_u *s, int len, int flags)
3947 #ifdef FEAT_MBYTE
3948 char_u *tofree = NULL;
3950 if (output_conv.vc_type != CONV_NONE)
3952 tofree = string_convert(&output_conv, s, &len);
3953 if (tofree != NULL)
3954 s = tofree;
3956 #endif
3959 * On OS X, try using Quartz-style text antialiasing.
3961 if (gMacSystemVersion >= 0x1020)
3963 /* Quartz antialiasing is available only in OS 10.2 and later. */
3964 UInt32 qd_flags = (p_antialias ?
3965 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0);
3966 QDSwapTextFlags(qd_flags);
3970 * When antialiasing we're using srcOr mode, we have to clear the block
3971 * before drawing the text.
3972 * Also needed when 'linespace' is non-zero to remove the cursor and
3973 * underlining.
3974 * But not when drawing transparently.
3975 * The following is like calling gui_mch_clear_block(row, col, row, col +
3976 * len - 1), but without setting the bg color to gui.back_pixel.
3978 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
3979 && !(flags & DRAW_TRANSP))
3981 Rect rc;
3983 rc.left = FILL_X(col);
3984 rc.top = FILL_Y(row);
3985 #ifdef FEAT_MBYTE
3986 /* Multibyte computation taken from gui_w32.c */
3987 if (has_mbyte)
3989 int cell_len = 0;
3990 int n;
3992 /* Compute the length in display cells. */
3993 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
3994 cell_len += (*mb_ptr2cells)(s + n);
3995 rc.right = FILL_X(col + cell_len);
3997 else
3998 #endif
3999 rc.right = FILL_X(col + len) + (col + len == Columns);
4000 rc.bottom = FILL_Y(row + 1);
4001 EraseRect(&rc);
4004 if (gMacSystemVersion >= 0x1020 && p_antialias)
4006 StyleParameter face;
4008 face = normal;
4009 if (flags & DRAW_BOLD)
4010 face |= bold;
4011 if (flags & DRAW_UNDERL)
4012 face |= underline;
4013 TextFace(face);
4015 /* Quartz antialiasing works only in srcOr transfer mode. */
4016 TextMode(srcOr);
4018 MoveTo(TEXT_X(col), TEXT_Y(row));
4019 DrawText((char*)s, 0, len);
4021 else
4023 /* Use old-style, non-antialiased QuickDraw text rendering. */
4024 TextMode(srcCopy);
4025 TextFace(normal);
4027 /* SelectFont(hdc, gui.currFont); */
4029 if (flags & DRAW_TRANSP)
4031 TextMode(srcOr);
4034 MoveTo(TEXT_X(col), TEXT_Y(row));
4035 DrawText((char *)s, 0, len);
4037 if (flags & DRAW_BOLD)
4039 TextMode(srcOr);
4040 MoveTo(TEXT_X(col) + 1, TEXT_Y(row));
4041 DrawText((char *)s, 0, len);
4044 if (flags & DRAW_UNDERL)
4046 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1);
4047 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1);
4051 if (flags & DRAW_UNDERC)
4052 draw_undercurl(flags, row, col, len);
4054 #ifdef FEAT_MBYTE
4055 vim_free(tofree);
4056 #endif
4059 #ifdef USE_ATSUI_DRAWING
4061 static void
4062 draw_string_ATSUI(int row, int col, char_u *s, int len, int flags)
4064 /* ATSUI requires utf-16 strings */
4065 UniCharCount utf16_len;
4066 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
4067 utf16_len /= sizeof(UniChar);
4069 /* - ATSUI automatically antialiases text (Someone)
4070 * - for some reason it does not work... (Jussi) */
4071 #ifdef MAC_ATSUI_DEBUG
4072 fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n",
4073 row, col, len, len == 1 ? s[0] : ' ');
4074 #endif
4076 * When antialiasing we're using srcOr mode, we have to clear the block
4077 * before drawing the text.
4078 * Also needed when 'linespace' is non-zero to remove the cursor and
4079 * underlining.
4080 * But not when drawing transparently.
4081 * The following is like calling gui_mch_clear_block(row, col, row, col +
4082 * len - 1), but without setting the bg color to gui.back_pixel.
4084 if ((flags & DRAW_TRANSP) == 0)
4086 Rect rc;
4088 rc.left = FILL_X(col);
4089 rc.top = FILL_Y(row);
4090 /* Multibyte computation taken from gui_w32.c */
4091 if (has_mbyte)
4093 int cell_len = 0;
4094 int n;
4096 /* Compute the length in display cells. */
4097 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4098 cell_len += (*mb_ptr2cells)(s + n);
4099 rc.right = FILL_X(col + cell_len);
4101 else
4102 rc.right = FILL_X(col + len) + (col + len == Columns);
4104 rc.bottom = FILL_Y(row + 1);
4105 EraseRect(&rc);
4109 TextMode(srcCopy);
4110 TextFace(normal);
4112 /* SelectFont(hdc, gui.currFont); */
4113 if (flags & DRAW_TRANSP)
4115 TextMode(srcOr);
4118 MoveTo(TEXT_X(col), TEXT_Y(row));
4120 if (gFontStyle && flags & DRAW_BOLD)
4122 Boolean attValue = true;
4123 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4124 ByteCount attribSizes[] = { sizeof(Boolean) };
4125 ATSUAttributeValuePtr attribValues[] = { &attValue };
4127 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues);
4130 #ifdef FEAT_MBYTE
4131 if (has_mbyte)
4133 int n, width_in_cell, last_width_in_cell;
4134 UniCharArrayOffset offset = 0;
4135 UniCharCount yet_to_draw = 0;
4136 ATSUTextLayout textLayout;
4137 ATSUStyle textStyle;
4139 last_width_in_cell = 1;
4140 ATSUCreateTextLayout(&textLayout);
4141 ATSUSetTextPointerLocation(textLayout, tofree,
4142 kATSUFromTextBeginning,
4143 kATSUToTextEnd, utf16_len);
4145 ATSUSetRunStyle(textLayout, gFontStyle,
4146 kATSUFromTextBeginning, kATSUToTextEnd); */
4148 /* Compute the length in display cells. */
4149 for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
4151 width_in_cell = (*mb_ptr2cells)(s + n);
4153 /* probably we are switching from single byte character
4154 * to multibyte characters (which requires more than one
4155 * cell to draw) */
4156 if (width_in_cell != last_width_in_cell)
4158 #ifdef MAC_ATSUI_DEBUG
4159 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4160 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4161 #endif
4162 textStyle = last_width_in_cell > 1 ? gWideFontStyle
4163 : gFontStyle;
4165 ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw);
4166 offset += yet_to_draw;
4167 yet_to_draw = 0;
4168 last_width_in_cell = width_in_cell;
4171 yet_to_draw++;
4174 if (yet_to_draw)
4176 #ifdef MAC_ATSUI_DEBUG
4177 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n",
4178 n, last_width_in_cell, width_in_cell, offset, yet_to_draw);
4179 #endif
4180 /* finish the rest style */
4181 textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle;
4182 ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd);
4185 ATSUSetTransientFontMatching(textLayout, TRUE);
4186 ATSUDrawText(textLayout,
4187 kATSUFromTextBeginning, kATSUToTextEnd,
4188 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4189 ATSUDisposeTextLayout(textLayout);
4191 else
4192 #endif
4194 ATSUTextLayout textLayout;
4196 if (ATSUCreateTextLayoutWithTextPtr(tofree,
4197 kATSUFromTextBeginning, kATSUToTextEnd,
4198 utf16_len,
4199 (gFontStyle ? 1 : 0), &utf16_len,
4200 (gFontStyle ? &gFontStyle : NULL),
4201 &textLayout) == noErr)
4203 ATSUSetTransientFontMatching(textLayout, TRUE);
4205 ATSUDrawText(textLayout,
4206 kATSUFromTextBeginning, kATSUToTextEnd,
4207 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc);
4209 ATSUDisposeTextLayout(textLayout);
4213 /* drawing is done, now reset bold to normal */
4214 if (gFontStyle && flags & DRAW_BOLD)
4216 Boolean attValue = false;
4218 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag };
4219 ByteCount attribSizes[] = { sizeof(Boolean) };
4220 ATSUAttributeValuePtr attribValues[] = { &attValue };
4222 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes,
4223 attribValues);
4227 if (flags & DRAW_UNDERC)
4228 draw_undercurl(flags, row, col, len);
4230 vim_free(tofree);
4232 #endif
4234 void
4235 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
4237 #if defined(USE_ATSUI_DRAWING)
4238 if (p_macatsui == 0 && p_macatsui_last != 0)
4239 /* switch from macatsui to nomacatsui */
4240 gui_mac_dispose_atsui_style();
4241 else if (p_macatsui != 0 && p_macatsui_last == 0)
4242 /* switch from nomacatsui to macatsui */
4243 gui_mac_create_atsui_style();
4245 if (p_macatsui)
4246 draw_string_ATSUI(row, col, s, len, flags);
4247 else
4248 #endif
4249 draw_string_QD(row, col, s, len, flags);
4253 * Return OK if the key with the termcap name "name" is supported.
4256 gui_mch_haskey(char_u *name)
4258 int i;
4260 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
4261 if (name[0] == special_keys[i].vim_code0 &&
4262 name[1] == special_keys[i].vim_code1)
4263 return OK;
4264 return FAIL;
4267 void
4268 gui_mch_beep(void)
4270 SysBeep(1); /* Should this be 0? (????) */
4273 void
4274 gui_mch_flash(int msec)
4276 /* Do a visual beep by reversing the foreground and background colors */
4277 Rect rc;
4280 * Note: InvertRect() excludes right and bottom of rectangle.
4282 rc.left = 0;
4283 rc.top = 0;
4284 rc.right = gui.num_cols * gui.char_width;
4285 rc.bottom = gui.num_rows * gui.char_height;
4286 InvertRect(&rc);
4288 ui_delay((long)msec, TRUE); /* wait for some msec */
4290 InvertRect(&rc);
4294 * Invert a rectangle from row r, column c, for nr rows and nc columns.
4296 void
4297 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
4299 Rect rc;
4302 * Note: InvertRect() excludes right and bottom of rectangle.
4304 rc.left = FILL_X(c);
4305 rc.top = FILL_Y(r);
4306 rc.right = rc.left + nc * gui.char_width;
4307 rc.bottom = rc.top + nr * gui.char_height;
4308 InvertRect(&rc);
4312 * Iconify the GUI window.
4314 void
4315 gui_mch_iconify(void)
4317 /* TODO: find out what could replace iconify
4318 * -window shade?
4319 * -hide application?
4323 #if defined(FEAT_EVAL) || defined(PROTO)
4325 * Bring the Vim window to the foreground.
4327 void
4328 gui_mch_set_foreground(void)
4330 /* TODO */
4332 #endif
4335 * Draw a cursor without focus.
4337 void
4338 gui_mch_draw_hollow_cursor(guicolor_T color)
4340 Rect rc;
4343 * Note: FrameRect() excludes right and bottom of rectangle.
4345 rc.left = FILL_X(gui.col);
4346 rc.top = FILL_Y(gui.row);
4347 rc.right = rc.left + gui.char_width;
4348 #ifdef FEAT_MBYTE
4349 if (mb_lefthalve(gui.row, gui.col))
4350 rc.right += gui.char_width;
4351 #endif
4352 rc.bottom = rc.top + gui.char_height;
4354 gui_mch_set_fg_color(color);
4356 FrameRect(&rc);
4360 * Draw part of a cursor, only w pixels wide, and h pixels high.
4362 void
4363 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
4365 Rect rc;
4367 #ifdef FEAT_RIGHTLEFT
4368 /* vertical line should be on the right of current point */
4369 if (CURSOR_BAR_RIGHT)
4370 rc.left = FILL_X(gui.col + 1) - w;
4371 else
4372 #endif
4373 rc.left = FILL_X(gui.col);
4374 rc.top = FILL_Y(gui.row) + gui.char_height - h;
4375 rc.right = rc.left + w;
4376 rc.bottom = rc.top + h;
4378 gui_mch_set_fg_color(color);
4380 FrameRect(&rc);
4381 // PaintRect(&rc);
4387 * Catch up with any queued X events. This may put keyboard input into the
4388 * input buffer, call resize call-backs, trigger timers etc. If there is
4389 * nothing in the X event queue (& no timers pending), then we return
4390 * immediately.
4392 void
4393 gui_mch_update(void)
4395 /* TODO: find what to do
4396 * maybe call gui_mch_wait_for_chars (0)
4397 * more like look at EventQueue then
4398 * call heart of gui_mch_wait_for_chars;
4400 * if (eventther)
4401 * gui_mac_handle_event(&event);
4403 EventRecord theEvent;
4405 if (EventAvail(everyEvent, &theEvent))
4406 if (theEvent.what != nullEvent)
4407 gui_mch_wait_for_chars(0);
4411 * Simple wrapper to neglect more easily the time
4412 * spent inside WaitNextEvent while profiling.
4415 pascal
4416 Boolean
4417 WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn)
4419 if (((long) sleep) < -1)
4420 sleep = 32767;
4421 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn);
4425 * GUI input routine called by gui_wait_for_chars(). Waits for a character
4426 * from the keyboard.
4427 * wtime == -1 Wait forever.
4428 * wtime == 0 This should never happen.
4429 * wtime > 0 Wait wtime milliseconds for a character.
4430 * Returns OK if a character was found to be available within the given time,
4431 * or FAIL otherwise.
4434 gui_mch_wait_for_chars(int wtime)
4436 EventMask mask = (everyEvent);
4437 EventRecord event;
4438 long entryTick;
4439 long currentTick;
4440 long sleeppyTick;
4442 /* If we are providing life feedback with the scrollbar,
4443 * we don't want to try to wait for an event, or else
4444 * there won't be any life feedback.
4446 if (dragged_sb != NULL)
4447 return FAIL;
4448 /* TODO: Check if FAIL is the proper return code */
4450 entryTick = TickCount();
4452 allow_scrollbar = TRUE;
4456 /* if (dragRectControl == kCreateEmpty)
4458 dragRgn = NULL;
4459 dragRectControl = kNothing;
4461 else*/ if (dragRectControl == kCreateRect)
4463 dragRgn = cursorRgn;
4464 RectRgn(dragRgn, &dragRect);
4465 dragRectControl = kNothing;
4468 * Don't use gui_mch_update() because then we will spin-lock until a
4469 * char arrives, instead we use WaitNextEventWrp() to hang until an
4470 * event arrives. No need to check for input_buf_full because we are
4471 * returning as soon as it contains a single char.
4473 /* TODO: reduce wtime accordinly??? */
4474 if (wtime > -1)
4475 sleeppyTick = 60 * wtime / 1000;
4476 else
4477 sleeppyTick = 32767;
4479 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
4481 gui_mac_handle_event(&event);
4482 if (input_available())
4484 allow_scrollbar = FALSE;
4485 return OK;
4488 currentTick = TickCount();
4490 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000));
4492 allow_scrollbar = FALSE;
4493 return FAIL;
4497 * Output routines.
4500 /* Flush any output to the screen */
4501 void
4502 gui_mch_flush(void)
4504 /* TODO: Is anything needed here? */
4508 * Clear a rectangular region of the screen from text pos (row1, col1) to
4509 * (row2, col2) inclusive.
4511 void
4512 gui_mch_clear_block(int row1, int col1, int row2, int col2)
4514 Rect rc;
4517 * Clear one extra pixel at the far right, for when bold characters have
4518 * spilled over to the next column.
4520 rc.left = FILL_X(col1);
4521 rc.top = FILL_Y(row1);
4522 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1);
4523 rc.bottom = FILL_Y(row2 + 1);
4525 gui_mch_set_bg_color(gui.back_pixel);
4526 EraseRect(&rc);
4530 * Clear the whole text window.
4532 void
4533 gui_mch_clear_all(void)
4535 Rect rc;
4537 rc.left = 0;
4538 rc.top = 0;
4539 rc.right = Columns * gui.char_width + 2 * gui.border_width;
4540 rc.bottom = Rows * gui.char_height + 2 * gui.border_width;
4542 gui_mch_set_bg_color(gui.back_pixel);
4543 EraseRect(&rc);
4544 /* gui_mch_set_fg_color(gui.norm_pixel);
4545 FrameRect(&rc);
4550 * Delete the given number of lines from the given row, scrolling up any
4551 * text further down within the scroll region.
4553 void
4554 gui_mch_delete_lines(int row, int num_lines)
4556 Rect rc;
4558 /* changed without checking! */
4559 rc.left = FILL_X(gui.scroll_region_left);
4560 rc.right = FILL_X(gui.scroll_region_right + 1);
4561 rc.top = FILL_Y(row);
4562 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4564 gui_mch_set_bg_color(gui.back_pixel);
4565 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil);
4567 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
4568 gui.scroll_region_left,
4569 gui.scroll_region_bot, gui.scroll_region_right);
4573 * Insert the given number of lines before the given row, scrolling down any
4574 * following text within the scroll region.
4576 void
4577 gui_mch_insert_lines(int row, int num_lines)
4579 Rect rc;
4581 rc.left = FILL_X(gui.scroll_region_left);
4582 rc.right = FILL_X(gui.scroll_region_right + 1);
4583 rc.top = FILL_Y(row);
4584 rc.bottom = FILL_Y(gui.scroll_region_bot + 1);
4586 gui_mch_set_bg_color(gui.back_pixel);
4588 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil);
4590 /* Update gui.cursor_row if the cursor scrolled or copied over */
4591 if (gui.cursor_row >= gui.row
4592 && gui.cursor_col >= gui.scroll_region_left
4593 && gui.cursor_col <= gui.scroll_region_right)
4595 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
4596 gui.cursor_row += num_lines;
4597 else if (gui.cursor_row <= gui.scroll_region_bot)
4598 gui.cursor_is_valid = FALSE;
4601 gui_clear_block(row, gui.scroll_region_left,
4602 row + num_lines - 1, gui.scroll_region_right);
4606 * TODO: add a vim format to the clipboard which remember
4607 * LINEWISE, CHARWISE, BLOCKWISE
4610 void
4611 clip_mch_request_selection(VimClipboard *cbd)
4614 Handle textOfClip;
4615 int flavor = 0;
4616 Size scrapSize;
4617 ScrapFlavorFlags scrapFlags;
4618 ScrapRef scrap = nil;
4619 OSStatus error;
4620 int type;
4621 char *searchCR;
4622 char_u *tempclip;
4625 error = GetCurrentScrap(&scrap);
4626 if (error != noErr)
4627 return;
4629 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags);
4630 if (error == noErr)
4632 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize);
4633 if (error == noErr && scrapSize > 1)
4634 flavor = 1;
4637 if (flavor == 0)
4639 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags);
4640 if (error != noErr)
4641 return;
4643 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize);
4644 if (error != noErr)
4645 return;
4648 ReserveMem(scrapSize);
4650 /* In CARBON we don't need a Handle, a pointer is good */
4651 textOfClip = NewHandle(scrapSize);
4653 /* tempclip = lalloc(scrapSize+1, TRUE); */
4654 HLock(textOfClip);
4655 error = GetScrapFlavorData(scrap,
4656 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR,
4657 &scrapSize, *textOfClip);
4658 scrapSize -= flavor;
4660 if (flavor)
4661 type = **textOfClip;
4662 else
4663 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
4665 tempclip = lalloc(scrapSize + 1, TRUE);
4666 mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
4667 tempclip[scrapSize] = 0;
4669 #ifdef MACOS_CONVERT
4671 /* Convert from utf-16 (clipboard) */
4672 size_t encLen = 0;
4673 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
4675 if (to != NULL)
4677 scrapSize = encLen;
4678 vim_free(tempclip);
4679 tempclip = to;
4682 #endif
4684 searchCR = (char *)tempclip;
4685 while (searchCR != NULL)
4687 searchCR = strchr(searchCR, '\r');
4688 if (searchCR != NULL)
4689 *searchCR = '\n';
4692 clip_yank_selection(type, tempclip, scrapSize, cbd);
4694 vim_free(tempclip);
4695 HUnlock(textOfClip);
4697 DisposeHandle(textOfClip);
4700 void
4701 clip_mch_lose_selection(VimClipboard *cbd)
4704 * TODO: Really nothing to do?
4709 clip_mch_own_selection(VimClipboard *cbd)
4711 return OK;
4715 * Send the current selection to the clipboard.
4717 void
4718 clip_mch_set_selection(VimClipboard *cbd)
4720 Handle textOfClip;
4721 long scrapSize;
4722 int type;
4723 ScrapRef scrap;
4725 char_u *str = NULL;
4727 if (!cbd->owned)
4728 return;
4730 clip_get_selection(cbd);
4733 * Once we set the clipboard, lose ownership. If another application sets
4734 * the clipboard, we don't want to think that we still own it.
4736 cbd->owned = FALSE;
4738 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd);
4740 #ifdef MACOS_CONVERT
4741 size_t utf16_len = 0;
4742 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len);
4743 if (to)
4745 scrapSize = utf16_len;
4746 vim_free(str);
4747 str = (char_u *)to;
4749 #endif
4751 if (type >= 0)
4753 ClearCurrentScrap();
4755 textOfClip = NewHandle(scrapSize + 1);
4756 HLock(textOfClip);
4758 **textOfClip = type;
4759 mch_memmove(*textOfClip + 1, str, scrapSize);
4760 GetCurrentScrap(&scrap);
4761 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone,
4762 scrapSize, *textOfClip + 1);
4763 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
4764 scrapSize + 1, *textOfClip);
4765 HUnlock(textOfClip);
4766 DisposeHandle(textOfClip);
4769 vim_free(str);
4772 void
4773 gui_mch_set_text_area_pos(int x, int y, int w, int h)
4775 Rect VimBound;
4777 /* HideWindow(gui.VimWindow); */
4778 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4780 if (gui.which_scrollbars[SBAR_LEFT])
4782 VimBound.left = -gui.scrollbar_width + 1;
4784 else
4786 VimBound.left = 0;
4789 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
4791 ShowWindow(gui.VimWindow);
4795 * Menu stuff.
4798 void
4799 gui_mch_enable_menu(int flag)
4802 * Menu is always active.
4806 void
4807 gui_mch_set_menu_pos(int x, int y, int w, int h)
4810 * The menu is always at the top of the screen.
4815 * Add a sub menu to the menu bar.
4817 void
4818 gui_mch_add_menu(vimmenu_T *menu, int idx)
4821 * TODO: Try to use only menu_id instead of both menu_id and menu_handle.
4822 * TODO: use menu->mnemonic and menu->actext
4823 * TODO: Try to reuse menu id
4824 * Carbon Help suggest to use only id between 1 and 235
4826 static long next_avail_id = 128;
4827 long menu_after_me = 0; /* Default to the end */
4828 #if defined(FEAT_MBYTE)
4829 CFStringRef name;
4830 #else
4831 char_u *name;
4832 #endif
4833 short index;
4834 vimmenu_T *parent = menu->parent;
4835 vimmenu_T *brother = menu->next;
4837 /* Cannot add a menu if ... */
4838 if ((parent != NULL && parent->submenu_id == 0))
4839 return;
4841 /* menu ID greater than 1024 are reserved for ??? */
4842 if (next_avail_id == 1024)
4843 return;
4845 /* My brother could be the PopUp, find my real brother */
4846 while ((brother != NULL) && (!menu_is_menubar(brother->name)))
4847 brother = brother->next;
4849 /* Find where to insert the menu (for MenuBar) */
4850 if ((parent == NULL) && (brother != NULL))
4851 menu_after_me = brother->submenu_id;
4853 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */
4854 if (!menu_is_menubar(menu->name))
4855 menu_after_me = hierMenu;
4857 /* Convert the name */
4858 #ifdef MACOS_CONVERT
4859 name = menu_title_removing_mnemonic(menu);
4860 #else
4861 name = C2Pascal_save(menu->dname);
4862 #endif
4863 if (name == NULL)
4864 return;
4866 /* Create the menu unless it's the help menu */
4868 /* Carbon suggest use of
4869 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
4870 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
4872 menu->submenu_id = next_avail_id;
4873 #if defined(FEAT_MBYTE)
4874 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
4875 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
4876 #else
4877 menu->submenu_handle = NewMenu(menu->submenu_id, name);
4878 #endif
4879 next_avail_id++;
4882 if (parent == NULL)
4884 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */
4886 /* TODO: Verify if we could only Insert Menu if really part of the
4887 * menubar The Inserted menu are scanned or the Command-key combos
4890 /* Insert the menu */
4891 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */
4892 #if 1
4893 /* Vim should normally update it. TODO: verify */
4894 DrawMenuBar();
4895 #endif
4897 else
4899 /* Adding as a submenu */
4901 index = gui_mac_get_menu_item_index(menu);
4903 /* Call InsertMenuItem followed by SetMenuItemText
4904 * to avoid special character recognition by InsertMenuItem
4906 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
4907 #if defined(FEAT_MBYTE)
4908 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
4909 #else
4910 SetMenuItemText(parent->submenu_handle, idx+1, name);
4911 #endif
4912 SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
4913 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
4914 InsertMenu(menu->submenu_handle, hierMenu);
4917 #if defined(FEAT_MBYTE)
4918 CFRelease(name);
4919 #else
4920 vim_free(name);
4921 #endif
4923 #if 0
4924 /* Done by Vim later on */
4925 DrawMenuBar();
4926 #endif
4930 * Add a menu item to a menu
4932 void
4933 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
4935 #if defined(FEAT_MBYTE)
4936 CFStringRef name;
4937 #else
4938 char_u *name;
4939 #endif
4940 vimmenu_T *parent = menu->parent;
4941 int menu_inserted;
4943 /* Cannot add item, if the menu have not been created */
4944 if (parent->submenu_id == 0)
4945 return;
4947 /* Could call SetMenuRefCon [CARBON] to associate with the Menu,
4948 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */
4950 /* Convert the name */
4951 #ifdef MACOS_CONVERT
4952 name = menu_title_removing_mnemonic(menu);
4953 #else
4954 name = C2Pascal_save(menu->dname);
4955 #endif
4957 /* Where are just a menu item, so no handle, no id */
4958 menu->submenu_id = 0;
4959 menu->submenu_handle = NULL;
4961 menu_inserted = 0;
4962 if (menu->actext)
4964 /* If the accelerator text for the menu item looks like it describes
4965 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the
4966 * item's command equivalent.
4968 int key = 0;
4969 int modifiers = 0;
4970 char_u *p_actext;
4972 p_actext = menu->actext;
4973 key = find_special_key(&p_actext, &modifiers, /*keycode=*/0);
4974 if (*p_actext != 0)
4975 key = 0; /* error: trailing text */
4976 /* find_special_key() returns a keycode with as many of the
4977 * specified modifiers as appropriate already applied (e.g., for
4978 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD
4979 * as the only modifier). Since we want to display all of the
4980 * modifiers, we need to convert the keycode back to a printable
4981 * character plus modifiers.
4982 * TODO: Write an alternative find_special_key() that doesn't
4983 * apply modifiers.
4985 if (key > 0 && key < 32)
4987 /* Convert a control key to an uppercase letter. Note that
4988 * by this point it is no longer possible to distinguish
4989 * between, e.g., Ctrl-S and Ctrl-Shift-S.
4991 modifiers |= MOD_MASK_CTRL;
4992 key += '@';
4994 /* If the keycode is an uppercase letter, set the Shift modifier.
4995 * If it is a lowercase letter, don't set the modifier, but convert
4996 * the letter to uppercase for display in the menu.
4998 else if (key >= 'A' && key <= 'Z')
4999 modifiers |= MOD_MASK_SHIFT;
5000 else if (key >= 'a' && key <= 'z')
5001 key += 'A' - 'a';
5002 /* Note: keycodes below 0x22 are reserved by Apple. */
5003 if (key >= 0x22 && vim_isprintc_strict(key))
5005 int valid = 1;
5006 char_u mac_mods = kMenuNoModifiers;
5007 /* Convert Vim modifier codes to Menu Manager equivalents. */
5008 if (modifiers & MOD_MASK_SHIFT)
5009 mac_mods |= kMenuShiftModifier;
5010 if (modifiers & MOD_MASK_CTRL)
5011 mac_mods |= kMenuControlModifier;
5012 if (!(modifiers & MOD_MASK_CMD))
5013 mac_mods |= kMenuNoCommandModifier;
5014 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK)
5015 valid = 0; /* TODO: will Alt someday map to Option? */
5016 if (valid)
5018 char_u item_txt[10];
5019 /* Insert the menu item after idx, with its command key. */
5020 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/';
5021 item_txt[3] = key;
5022 InsertMenuItem(parent->submenu_handle, item_txt, idx);
5023 /* Set the modifier keys. */
5024 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods);
5025 menu_inserted = 1;
5029 /* Call InsertMenuItem followed by SetMenuItemText
5030 * to avoid special character recognition by InsertMenuItem
5032 if (!menu_inserted)
5033 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */
5034 /* Set the menu item name. */
5035 #if defined(FEAT_MBYTE)
5036 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
5037 #else
5038 SetMenuItemText(parent->submenu_handle, idx+1, name);
5039 #endif
5041 #if 0
5042 /* Called by Vim */
5043 DrawMenuBar();
5044 #endif
5046 #if defined(FEAT_MBYTE)
5047 CFRelease(name);
5048 #else
5049 /* TODO: Can name be freed? */
5050 vim_free(name);
5051 #endif
5054 void
5055 gui_mch_toggle_tearoffs(int enable)
5057 /* no tearoff menus */
5061 * Destroy the machine specific menu widget.
5063 void
5064 gui_mch_destroy_menu(vimmenu_T *menu)
5066 short index = gui_mac_get_menu_item_index(menu);
5068 if (index > 0)
5070 if (menu->parent)
5073 /* For now just don't delete help menu items. (Huh? Dany) */
5074 DeleteMenuItem(menu->parent->submenu_handle, index);
5076 /* Delete the Menu if it was a hierarchical Menu */
5077 if (menu->submenu_id != 0)
5079 DeleteMenu(menu->submenu_id);
5080 DisposeMenu(menu->submenu_handle);
5084 #ifdef DEBUG_MAC_MENU
5085 else
5087 printf("gmdm 2\n");
5089 #endif
5091 else
5094 DeleteMenu(menu->submenu_id);
5095 DisposeMenu(menu->submenu_handle);
5098 /* Shouldn't this be already done by Vim. TODO: Check */
5099 DrawMenuBar();
5103 * Make a menu either grey or not grey.
5105 void
5106 gui_mch_menu_grey(vimmenu_T *menu, int grey)
5108 /* TODO: Check if menu really exists */
5109 short index = gui_mac_get_menu_item_index(menu);
5111 index = menu->index;
5113 if (grey)
5115 if (menu->children)
5116 DisableMenuItem(menu->submenu_handle, index);
5117 if (menu->parent)
5118 if (menu->parent->submenu_handle)
5119 DisableMenuItem(menu->parent->submenu_handle, index);
5121 else
5123 if (menu->children)
5124 EnableMenuItem(menu->submenu_handle, index);
5125 if (menu->parent)
5126 if (menu->parent->submenu_handle)
5127 EnableMenuItem(menu->parent->submenu_handle, index);
5132 * Make menu item hidden or not hidden
5134 void
5135 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5137 /* There's no hidden mode on MacOS */
5138 gui_mch_menu_grey(menu, hidden);
5143 * This is called after setting all the menus to grey/hidden or not.
5145 void
5146 gui_mch_draw_menubar(void)
5148 DrawMenuBar();
5153 * Scrollbar stuff.
5156 void
5157 gui_mch_enable_scrollbar(
5158 scrollbar_T *sb,
5159 int flag)
5161 if (flag)
5162 ShowControl(sb->id);
5163 else
5164 HideControl(sb->id);
5166 #ifdef DEBUG_MAC_SB
5167 printf("enb_sb (%x) %x\n",sb->id, flag);
5168 #endif
5171 void
5172 gui_mch_set_scrollbar_thumb(
5173 scrollbar_T *sb,
5174 long val,
5175 long size,
5176 long max)
5178 SetControl32BitMaximum (sb->id, max);
5179 SetControl32BitMinimum (sb->id, 0);
5180 SetControl32BitValue (sb->id, val);
5181 SetControlViewSize (sb->id, size);
5182 #ifdef DEBUG_MAC_SB
5183 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
5184 #endif
5187 void
5188 gui_mch_set_scrollbar_pos(
5189 scrollbar_T *sb,
5190 int x,
5191 int y,
5192 int w,
5193 int h)
5195 gui_mch_set_bg_color(gui.back_pixel);
5196 /* if (gui.which_scrollbars[SBAR_LEFT])
5198 MoveControl(sb->id, x-16, y);
5199 SizeControl(sb->id, w + 1, h);
5201 else
5203 MoveControl(sb->id, x, y);
5204 SizeControl(sb->id, w + 1, h);
5206 if (sb == &gui.bottom_sbar)
5207 h += 1;
5208 else
5209 w += 1;
5211 if (gui.which_scrollbars[SBAR_LEFT])
5212 x -= 15;
5214 MoveControl(sb->id, x, y);
5215 SizeControl(sb->id, w, h);
5216 #ifdef DEBUG_MAC_SB
5217 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h);
5218 #endif
5221 void
5222 gui_mch_create_scrollbar(
5223 scrollbar_T *sb,
5224 int orient) /* SBAR_VERT or SBAR_HORIZ */
5226 Rect bounds;
5228 bounds.top = -16;
5229 bounds.bottom = -10;
5230 bounds.right = -10;
5231 bounds.left = -16;
5233 sb->id = NewControl(gui.VimWindow,
5234 &bounds,
5235 "\pScrollBar",
5236 TRUE,
5237 0, /* current*/
5238 0, /* top */
5239 0, /* bottom */
5240 kControlScrollBarLiveProc,
5241 (long) sb->ident);
5242 #ifdef DEBUG_MAC_SB
5243 printf("create_sb (%x) %x\n",sb->id, orient);
5244 #endif
5247 void
5248 gui_mch_destroy_scrollbar(scrollbar_T *sb)
5250 gui_mch_set_bg_color(gui.back_pixel);
5251 DisposeControl(sb->id);
5252 #ifdef DEBUG_MAC_SB
5253 printf("dest_sb (%x) \n",sb->id);
5254 #endif
5259 * Cursor blink functions.
5261 * This is a simple state machine:
5262 * BLINK_NONE not blinking at all
5263 * BLINK_OFF blinking, cursor is not shown
5264 * BLINK_ON blinking, cursor is shown
5266 void
5267 gui_mch_set_blinking(long wait, long on, long off)
5269 /* TODO: TODO: TODO: TODO: */
5270 /* blink_waittime = wait;
5271 blink_ontime = on;
5272 blink_offtime = off;*/
5276 * Stop the cursor blinking. Show the cursor if it wasn't shown.
5278 void
5279 gui_mch_stop_blink(void)
5281 gui_update_cursor(TRUE, FALSE);
5282 /* TODO: TODO: TODO: TODO: */
5283 /* gui_w32_rm_blink_timer();
5284 if (blink_state == BLINK_OFF)
5285 gui_update_cursor(TRUE, FALSE);
5286 blink_state = BLINK_NONE;*/
5290 * Start the cursor blinking. If it was already blinking, this restarts the
5291 * waiting time and shows the cursor.
5293 void
5294 gui_mch_start_blink(void)
5296 gui_update_cursor(TRUE, FALSE);
5297 /* TODO: TODO: TODO: TODO: */
5298 /* gui_w32_rm_blink_timer(); */
5300 /* Only switch blinking on if none of the times is zero */
5301 /* if (blink_waittime && blink_ontime && blink_offtime)
5303 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime,
5304 (TIMERPROC)_OnBlinkTimer);
5305 blink_state = BLINK_ON;
5306 gui_update_cursor(TRUE, FALSE);
5311 * Return the RGB value of a pixel as long.
5313 long_u
5314 gui_mch_get_rgb(guicolor_T pixel)
5316 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
5321 #ifdef FEAT_BROWSE
5323 * Pop open a file browser and return the file selected, in allocated memory,
5324 * or NULL if Cancel is hit.
5325 * saving - TRUE if the file will be saved to, FALSE if it will be opened.
5326 * title - Title message for the file browser dialog.
5327 * dflt - Default name of file.
5328 * ext - Default extension to be added to files without extensions.
5329 * initdir - directory in which to open the browser (NULL = current dir)
5330 * filter - Filter for matched files to choose from.
5331 * Has a format like this:
5332 * "C Files (*.c)\0*.c\0"
5333 * "All Files\0*.*\0\0"
5334 * If these two strings were concatenated, then a choice of two file
5335 * filters will be selectable to the user. Then only matching files will
5336 * be shown in the browser. If NULL, the default allows all files.
5338 * *NOTE* - the filter string must be terminated with TWO nulls.
5340 char_u *
5341 gui_mch_browse(
5342 int saving,
5343 char_u *title,
5344 char_u *dflt,
5345 char_u *ext,
5346 char_u *initdir,
5347 char_u *filter)
5349 /* TODO: Add Ammon's safety checl (Dany) */
5350 NavReplyRecord reply;
5351 char_u *fname = NULL;
5352 char_u **fnames = NULL;
5353 long numFiles;
5354 NavDialogOptions navOptions;
5355 OSErr error;
5357 /* Get Navigation Service Defaults value */
5358 NavGetDefaultDialogOptions(&navOptions);
5361 /* TODO: If we get a :browse args, set the Multiple bit. */
5362 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles
5363 | kNavDontAutoTranslate
5364 | kNavDontAddTranslateItems
5365 /* | kNavAllowMultipleFiles */
5366 | kNavAllowStationery;
5368 (void) C2PascalString(title, &navOptions.message);
5369 (void) C2PascalString(dflt, &navOptions.savedFileName);
5370 /* Could set clientName?
5371 * windowTitle? (there's no title bar?)
5374 if (saving)
5376 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5377 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL);
5378 if (!reply.validRecord)
5379 return NULL;
5381 else
5383 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */
5384 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL);
5385 if (!reply.validRecord)
5386 return NULL;
5389 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error);
5391 NavDisposeReply(&reply);
5393 if (fnames)
5395 fname = fnames[0];
5396 vim_free(fnames);
5399 /* TODO: Shorten the file name if possible */
5400 return fname;
5402 #endif /* FEAT_BROWSE */
5404 #ifdef FEAT_GUI_DIALOG
5406 * Stuff for dialogues
5410 * Create a dialogue dynamically from the parameter strings.
5411 * type = type of dialogue (question, alert, etc.)
5412 * title = dialogue title. may be NULL for default title.
5413 * message = text to display. Dialogue sizes to accommodate it.
5414 * buttons = '\n' separated list of button captions, default first.
5415 * dfltbutton = number of default button.
5417 * This routine returns 1 if the first button is pressed,
5418 * 2 for the second, etc.
5420 * 0 indicates Esc was pressed.
5421 * -1 for unexpected error
5423 * If stubbing out this fn, return 1.
5426 typedef struct
5428 short idx;
5429 short width; /* Size of the text in pixel */
5430 Rect box;
5431 } vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */
5433 #define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top)
5435 static void
5436 macMoveDialogItem(
5437 DialogRef theDialog,
5438 short itemNumber,
5439 short X,
5440 short Y,
5441 Rect *inBox)
5443 #if 0 /* USE_CARBONIZED */
5444 /* Untested */
5445 MoveDialogItem(theDialog, itemNumber, X, Y);
5446 if (inBox != nil)
5447 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox);
5448 #else
5449 short itemType;
5450 Handle itemHandle;
5451 Rect localBox;
5452 Rect *itemBox = &localBox;
5454 if (inBox != nil)
5455 itemBox = inBox;
5457 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox);
5458 OffsetRect(itemBox, -itemBox->left, -itemBox->top);
5459 OffsetRect(itemBox, X, Y);
5460 /* To move a control (like a button) we need to call both
5461 * MoveControl and SetDialogItem. FAQ 6-18 */
5462 if (1) /*(itemType & kControlDialogItem) */
5463 MoveControl((ControlRef) itemHandle, X, Y);
5464 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox);
5465 #endif
5468 static void
5469 macSizeDialogItem(
5470 DialogRef theDialog,
5471 short itemNumber,
5472 short width,
5473 short height)
5475 short itemType;
5476 Handle itemHandle;
5477 Rect itemBox;
5479 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5481 /* When width or height is zero do not change it */
5482 if (width == 0)
5483 width = itemBox.right - itemBox.left;
5484 if (height == 0)
5485 height = itemBox.bottom - itemBox.top;
5487 #if 0 /* USE_CARBONIZED */
5488 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */
5489 #else
5490 /* Resize the bounding box */
5491 itemBox.right = itemBox.left + width;
5492 itemBox.bottom = itemBox.top + height;
5494 /* To resize a control (like a button) we need to call both
5495 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */
5496 if (itemType & kControlDialogItem)
5497 SizeControl((ControlRef) itemHandle, width, height);
5499 /* Configure back the item */
5500 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox);
5501 #endif
5504 static void
5505 macSetDialogItemText(
5506 DialogRef theDialog,
5507 short itemNumber,
5508 Str255 itemName)
5510 short itemType;
5511 Handle itemHandle;
5512 Rect itemBox;
5514 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox);
5516 if (itemType & kControlDialogItem)
5517 SetControlTitle((ControlRef) itemHandle, itemName);
5518 else
5519 SetDialogItemText(itemHandle, itemName);
5522 /* TODO: There have been some crashes with dialogs, check your inbox
5523 * (Jussi)
5526 gui_mch_dialog(
5527 int type,
5528 char_u *title,
5529 char_u *message,
5530 char_u *buttons,
5531 int dfltbutton,
5532 char_u *textfield)
5534 Handle buttonDITL;
5535 Handle iconDITL;
5536 Handle inputDITL;
5537 Handle messageDITL;
5538 Handle itemHandle;
5539 Handle iconHandle;
5540 DialogPtr theDialog;
5541 char_u len;
5542 char_u PascalTitle[256]; /* place holder for the title */
5543 char_u name[256];
5544 GrafPtr oldPort;
5545 short itemHit;
5546 char_u *buttonChar;
5547 Rect box;
5548 short button;
5549 short lastButton;
5550 short itemType;
5551 short useIcon;
5552 short width;
5553 short totalButtonWidth = 0; /* the width of all buttons together
5554 including spacing */
5555 short widestButton = 0;
5556 short dfltButtonEdge = 20; /* gut feeling */
5557 short dfltElementSpacing = 13; /* from IM:V.2-29 */
5558 short dfltIconSideSpace = 23; /* from IM:V.2-29 */
5559 short maximumWidth = 400; /* gut feeling */
5560 short maxButtonWidth = 175; /* gut feeling */
5562 short vertical;
5563 short dialogHeight;
5564 short messageLines = 3;
5565 FontInfo textFontInfo;
5567 vgmDlgItm iconItm;
5568 vgmDlgItm messageItm;
5569 vgmDlgItm inputItm;
5570 vgmDlgItm buttonItm;
5572 WindowRef theWindow;
5574 /* Check 'v' flag in 'guioptions': vertical button placement. */
5575 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
5577 /* Create a new Dialog Box from template. */
5578 theDialog = GetNewDialog(129, nil, (WindowRef) -1);
5580 /* Get the WindowRef */
5581 theWindow = GetDialogWindow(theDialog);
5583 /* Hide the window.
5584 * 1. to avoid seeing slow drawing
5585 * 2. to prevent a problem seen while moving dialog item
5586 * within a visible window. (non-Carbon MacOS 9)
5587 * Could be avoided by changing the resource.
5589 HideWindow(theWindow);
5591 /* Change the graphical port to the dialog,
5592 * so we can measure the text with the proper font */
5593 GetPort(&oldPort);
5594 SetPortDialogPort(theDialog);
5596 /* Get the info about the default text,
5597 * used to calculate the height of the message
5598 * and of the text field */
5599 GetFontInfo(&textFontInfo);
5601 /* Set the dialog title */
5602 if (title != NULL)
5604 (void) C2PascalString(title, &PascalTitle);
5605 SetWTitle(theWindow, PascalTitle);
5608 /* Creates the buttons and add them to the Dialog Box. */
5609 buttonDITL = GetResource('DITL', 130);
5610 buttonChar = buttons;
5611 button = 0;
5613 for (;*buttonChar != 0;)
5615 /* Get the name of the button */
5616 button++;
5617 len = 0;
5618 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++)
5620 if (*buttonChar != DLG_HOTKEY_CHAR)
5621 name[++len] = *buttonChar;
5623 if (*buttonChar != 0)
5624 buttonChar++;
5625 name[0] = len;
5627 /* Add the button */
5628 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */
5630 /* Change the button's name */
5631 macSetDialogItemText(theDialog, button, name);
5633 /* Resize the button to fit its name */
5634 width = StringWidth(name) + 2 * dfltButtonEdge;
5635 /* Limite the size of any button to an acceptable value. */
5636 /* TODO: Should be based on the message width */
5637 if (width > maxButtonWidth)
5638 width = maxButtonWidth;
5639 macSizeDialogItem(theDialog, button, width, 0);
5641 totalButtonWidth += width;
5643 if (width > widestButton)
5644 widestButton = width;
5646 ReleaseResource(buttonDITL);
5647 lastButton = button;
5649 /* Add the icon to the Dialog Box. */
5650 iconItm.idx = lastButton + 1;
5651 iconDITL = GetResource('DITL', 131);
5652 switch (type)
5654 case VIM_GENERIC: useIcon = kNoteIcon;
5655 case VIM_ERROR: useIcon = kStopIcon;
5656 case VIM_WARNING: useIcon = kCautionIcon;
5657 case VIM_INFO: useIcon = kNoteIcon;
5658 case VIM_QUESTION: useIcon = kNoteIcon;
5659 default: useIcon = kStopIcon;
5661 AppendDITL(theDialog, iconDITL, overlayDITL);
5662 ReleaseResource(iconDITL);
5663 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box);
5664 /* TODO: Should the item be freed? */
5665 iconHandle = GetIcon(useIcon);
5666 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box);
5668 /* Add the message to the Dialog box. */
5669 messageItm.idx = lastButton + 2;
5670 messageDITL = GetResource('DITL', 132);
5671 AppendDITL(theDialog, messageDITL, overlayDITL);
5672 ReleaseResource(messageDITL);
5673 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box);
5674 (void) C2PascalString(message, &name);
5675 SetDialogItemText(itemHandle, name);
5676 messageItm.width = StringWidth(name);
5678 /* Add the input box if needed */
5679 if (textfield != NULL)
5681 /* Cheat for now reuse the message and convert to text edit */
5682 inputItm.idx = lastButton + 3;
5683 inputDITL = GetResource('DITL', 132);
5684 AppendDITL(theDialog, inputDITL, overlayDITL);
5685 ReleaseResource(inputDITL);
5686 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5687 /* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/
5688 (void) C2PascalString(textfield, &name);
5689 SetDialogItemText(itemHandle, name);
5690 inputItm.width = StringWidth(name);
5693 /* Set the <ENTER> and <ESC> button. */
5694 SetDialogDefaultItem(theDialog, dfltbutton);
5695 SetDialogCancelItem(theDialog, 0);
5697 /* Reposition element */
5699 /* Check if we need to force vertical */
5700 if (totalButtonWidth > maximumWidth)
5701 vertical = TRUE;
5703 /* Place icon */
5704 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box);
5705 iconItm.box.right = box.right;
5706 iconItm.box.bottom = box.bottom;
5708 /* Place Message */
5709 messageItm.box.left = iconItm.box.right + dfltIconSideSpace;
5710 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent));
5711 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box);
5713 /* Place Input */
5714 if (textfield != NULL)
5716 inputItm.box.left = messageItm.box.left;
5717 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5718 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent);
5719 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box);
5720 /* Convert the static text into a text edit.
5721 * For some reason this change need to be done last (Dany) */
5722 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box);
5723 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box);
5724 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767);
5727 /* Place Button */
5728 if (textfield != NULL)
5730 buttonItm.box.left = inputItm.box.left;
5731 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing;
5733 else
5735 buttonItm.box.left = messageItm.box.left;
5736 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing;
5739 for (button=1; button <= lastButton; button++)
5742 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box);
5743 /* With vertical, it's better to have all buttons the same length */
5744 if (vertical)
5746 macSizeDialogItem(theDialog, button, widestButton, 0);
5747 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box);
5749 /* Calculate position of next button */
5750 if (vertical)
5751 buttonItm.box.top = box.bottom + dfltElementSpacing;
5752 else
5753 buttonItm.box.left = box.right + dfltElementSpacing;
5756 /* Resize the dialog box */
5757 dialogHeight = box.bottom + dfltElementSpacing;
5758 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE);
5760 /* Magic resize */
5761 AutoSizeDialog(theDialog);
5762 /* Need a horizontal resize anyway so not that useful */
5764 /* Display it */
5765 ShowWindow(theWindow);
5766 /* BringToFront(theWindow); */
5767 SelectWindow(theWindow);
5769 /* DrawDialog(theDialog); */
5770 #if 0
5771 GetPort(&oldPort);
5772 SetPortDialogPort(theDialog);
5773 #endif
5775 #ifdef USE_CARBONKEYHANDLER
5776 /* Avoid that we use key events for the main window. */
5777 dialog_busy = TRUE;
5778 #endif
5780 /* Hang until one of the button is hit */
5783 ModalDialog(nil, &itemHit);
5784 } while ((itemHit < 1) || (itemHit > lastButton));
5786 #ifdef USE_CARBONKEYHANDLER
5787 dialog_busy = FALSE;
5788 #endif
5790 /* Copy back the text entered by the user into the param */
5791 if (textfield != NULL)
5793 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box);
5794 GetDialogItemText(itemHandle, (char_u *) &name);
5795 #if IOSIZE < 256
5796 /* Truncate the name to IOSIZE if needed */
5797 if (name[0] > IOSIZE)
5798 name[0] = IOSIZE - 1;
5799 #endif
5800 vim_strncpy(textfield, &name[1], name[0]);
5803 /* Restore the original graphical port */
5804 SetPort(oldPort);
5806 /* Get ride of th edialog (free memory) */
5807 DisposeDialog(theDialog);
5809 return itemHit;
5811 * Usefull thing which could be used
5812 * SetDialogTimeout(): Auto click a button after timeout
5813 * SetDialogTracksCursor() : Get the I-beam cursor over input box
5814 * MoveDialogItem(): Probably better than SetDialogItem
5815 * SizeDialogItem(): (but is it Carbon Only?)
5816 * AutoSizeDialog(): Magic resize of dialog based on text length
5819 #endif /* FEAT_DIALOG_GUI */
5822 * Display the saved error message(s).
5824 #ifdef USE_MCH_ERRMSG
5825 void
5826 display_errors(void)
5828 char *p;
5829 char_u pError[256];
5831 if (error_ga.ga_data == NULL)
5832 return;
5834 /* avoid putting up a message box with blanks only */
5835 for (p = (char *)error_ga.ga_data; *p; ++p)
5836 if (!isspace(*p))
5838 if (STRLEN(p) > 255)
5839 pError[0] = 255;
5840 else
5841 pError[0] = STRLEN(p);
5843 STRNCPY(&pError[1], p, pError[0]);
5844 ParamText(pError, nil, nil, nil);
5845 Alert(128, nil);
5846 break;
5847 /* TODO: handled message longer than 256 chars
5848 * use auto-sizeable alert
5849 * or dialog with scrollbars (TextEdit zone)
5852 ga_clear(&error_ga);
5854 #endif
5857 * Get current mouse coordinates in text window.
5859 void
5860 gui_mch_getmouse(int *x, int *y)
5862 Point where;
5864 GetMouse(&where);
5866 *x = where.h;
5867 *y = where.v;
5870 void
5871 gui_mch_setmouse(int x, int y)
5873 /* TODO */
5874 #if 0
5875 /* From FAQ 3-11 */
5877 CursorDevicePtr myMouse;
5878 Point where;
5880 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap)
5881 != NGetTrapAddress(_Unimplemented, ToolTrap))
5883 /* New way */
5886 * Get first devoice with one button.
5887 * This will probably be the standad mouse
5888 * startat head of cursor dev list
5892 myMouse = nil;
5896 /* Get the next cursor device */
5897 CursorDeviceNextDevice(&myMouse);
5899 while ((myMouse != nil) && (myMouse->cntButtons != 1));
5901 CursorDeviceMoveTo(myMouse, x, y);
5903 else
5905 /* Old way */
5906 where.h = x;
5907 where.v = y;
5909 *(Point *)RawMouse = where;
5910 *(Point *)MTemp = where;
5911 *(Ptr) CrsrNew = 0xFFFF;
5913 #endif
5916 void
5917 gui_mch_show_popupmenu(vimmenu_T *menu)
5920 * Clone PopUp to use menu
5921 * Create a object descriptor for the current selection
5922 * Call the procedure
5925 MenuHandle CntxMenu;
5926 Point where;
5927 OSStatus status;
5928 UInt32 CntxType;
5929 SInt16 CntxMenuID;
5930 UInt16 CntxMenuItem;
5931 Str255 HelpName = "";
5932 GrafPtr savePort;
5934 /* Save Current Port: On MacOS X we seem to lose the port */
5935 GetPort(&savePort); /*OSX*/
5937 GetMouse(&where);
5938 LocalToGlobal(&where); /*OSX*/
5939 CntxMenu = menu->submenu_handle;
5941 /* TODO: Get the text selection from Vim */
5943 /* Call to Handle Popup */
5944 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp,
5945 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem);
5947 if (status == noErr)
5949 if (CntxType == kCMMenuItemSelected)
5951 /* Handle the menu CntxMenuID, CntxMenuItem */
5952 /* The submenu can be handle directly by gui_mac_handle_menu */
5953 /* But what about the current menu, is the menu changed by
5954 * ContextualMenuSelect */
5955 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem);
5957 else if (CntxMenuID == kCMShowHelpSelected)
5959 /* Should come up with the help */
5963 /* Restore original Port */
5964 SetPort(savePort); /*OSX*/
5967 #if defined(FEAT_CW_EDITOR) || defined(PROTO)
5968 /* TODO: Is it need for MACOS_X? (Dany) */
5969 void
5970 mch_post_buffer_write(buf_T *buf)
5972 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec);
5973 Send_KAHL_MOD_AE(buf);
5975 #endif
5977 #ifdef FEAT_TITLE
5979 * Set the window title and icon.
5980 * (The icon is not taken care of).
5982 void
5983 gui_mch_settitle(char_u *title, char_u *icon)
5985 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller
5986 * that 256. Even better get it to fit nicely in the titlebar.
5988 #ifdef MACOS_CONVERT
5989 CFStringRef windowTitle;
5990 size_t windowTitleLen;
5991 #else
5992 char_u *pascalTitle;
5993 #endif
5995 if (title == NULL) /* nothing to do */
5996 return;
5998 #ifdef MACOS_CONVERT
5999 windowTitleLen = STRLEN(title);
6000 windowTitle = mac_enc_to_cfstring(title, windowTitleLen);
6002 if (windowTitle)
6004 SetWindowTitleWithCFString(gui.VimWindow, windowTitle);
6005 CFRelease(windowTitle);
6007 #else
6008 pascalTitle = C2Pascal_save(title);
6009 if (pascalTitle != NULL)
6011 SetWTitle(gui.VimWindow, pascalTitle);
6012 vim_free(pascalTitle);
6014 #endif
6016 #endif
6019 * Transfered from os_mac.c for MacOS X using os_unix.c prep work
6023 C2PascalString(char_u *CString, Str255 *PascalString)
6025 char_u *PascalPtr = (char_u *) PascalString;
6026 int len;
6027 int i;
6029 PascalPtr[0] = 0;
6030 if (CString == NULL)
6031 return 0;
6033 len = STRLEN(CString);
6034 if (len > 255)
6035 len = 255;
6037 for (i = 0; i < len; i++)
6038 PascalPtr[i+1] = CString[i];
6040 PascalPtr[0] = len;
6042 return 0;
6046 GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec)
6048 /* From FAQ 8-12 */
6049 Str255 filePascal;
6050 CInfoPBRec myCPB;
6051 OSErr err;
6053 (void) C2PascalString(file, &filePascal);
6055 myCPB.dirInfo.ioNamePtr = filePascal;
6056 myCPB.dirInfo.ioVRefNum = 0;
6057 myCPB.dirInfo.ioFDirIndex = 0;
6058 myCPB.dirInfo.ioDrDirID = 0;
6060 err= PBGetCatInfo(&myCPB, false);
6062 /* vRefNum, dirID, name */
6063 FSMakeFSSpec(0, 0, filePascal, fileFSSpec);
6065 /* TODO: Use an error code mechanism */
6066 return 0;
6070 * Convert a FSSpec to a fuill path
6073 char_u *FullPathFromFSSpec_save(FSSpec file)
6076 * TODO: Add protection for 256 char max.
6079 CInfoPBRec theCPB;
6080 char_u fname[256];
6081 char_u *filenamePtr = fname;
6082 OSErr error;
6083 int folder = 1;
6084 #ifdef USE_UNIXFILENAME
6085 SInt16 dfltVol_vRefNum;
6086 SInt32 dfltVol_dirID;
6087 FSRef refFile;
6088 OSStatus status;
6089 UInt32 pathSize = 256;
6090 char_u pathname[256];
6091 char_u *path = pathname;
6092 #else
6093 Str255 directoryName;
6094 char_u temporary[255];
6095 char_u *temporaryPtr = temporary;
6096 #endif
6098 #ifdef USE_UNIXFILENAME
6099 /* Get the default volume */
6100 /* TODO: Remove as this only work if Vim is on the Boot Volume*/
6101 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID);
6103 if (error)
6104 return NULL;
6105 #endif
6107 /* Start filling fname with file.name */
6108 vim_strncpy(filenamePtr, &file.name[1], file.name[0]);
6110 /* Get the info about the file specified in FSSpec */
6111 theCPB.dirInfo.ioFDirIndex = 0;
6112 theCPB.dirInfo.ioNamePtr = file.name;
6113 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6114 /*theCPB.hFileInfo.ioDirID = 0;*/
6115 theCPB.dirInfo.ioDrDirID = file.parID;
6117 /* As ioFDirIndex = 0, get the info of ioNamePtr,
6118 which is relative to ioVrefNum, ioDirID */
6119 error = PBGetCatInfo(&theCPB, false);
6121 /* If we are called for a new file we expect fnfErr */
6122 if ((error) && (error != fnfErr))
6123 return NULL;
6125 /* Check if it's a file or folder */
6126 /* default to file if file don't exist */
6127 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error))
6128 folder = 0; /* It's not a folder */
6129 else
6130 folder = 1;
6132 #ifdef USE_UNIXFILENAME
6134 * The function used here are available in Carbon, but
6135 * do nothing une MacOS 8 and 9
6137 if (error == fnfErr)
6139 /* If the file to be saved does not already exist, it isn't possible
6140 to convert its FSSpec into an FSRef. But we can construct an
6141 FSSpec for the file's parent folder (since we have its volume and
6142 directory IDs), and since that folder does exist, we can convert
6143 that FSSpec into an FSRef, convert the FSRef in turn into a path,
6144 and, finally, append the filename. */
6145 FSSpec dirSpec;
6146 FSRef dirRef;
6147 Str255 emptyFilename = "\p";
6148 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum,
6149 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec);
6150 if (error)
6151 return NULL;
6153 error = FSpMakeFSRef(&dirSpec, &dirRef);
6154 if (error)
6155 return NULL;
6157 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize);
6158 if (status)
6159 return NULL;
6161 STRCAT(path, "/");
6162 STRCAT(path, filenamePtr);
6164 else
6166 /* If the file to be saved already exists, we can get its full path
6167 by converting its FSSpec into an FSRef. */
6168 error=FSpMakeFSRef(&file, &refFile);
6169 if (error)
6170 return NULL;
6172 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize);
6173 if (status)
6174 return NULL;
6177 /* Add a slash at the end if needed */
6178 if (folder)
6179 STRCAT(path, "/");
6181 return (vim_strsave(path));
6182 #else
6183 /* TODO: Get rid of all USE_UNIXFILENAME below */
6184 /* Set ioNamePtr, it's the same area which is always reused. */
6185 theCPB.dirInfo.ioNamePtr = directoryName;
6187 /* Trick for first entry, set ioDrParID to the first value
6188 * we want for ioDrDirID*/
6189 theCPB.dirInfo.ioDrParID = file.parID;
6190 theCPB.dirInfo.ioDrDirID = file.parID;
6192 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/))
6195 theCPB.dirInfo.ioFDirIndex = -1;
6196 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6197 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6198 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6199 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6201 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6202 /* *ioNamePtr[0 TO 31] will be updated */
6203 error = PBGetCatInfo(&theCPB,false);
6205 if (error)
6206 return NULL;
6208 /* Put the new directoryName in front of the current fname */
6209 STRCPY(temporaryPtr, filenamePtr);
6210 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6211 STRCAT(filenamePtr, ":");
6212 STRCAT(filenamePtr, temporaryPtr);
6214 #if 1 /* def USE_UNIXFILENAME */
6215 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */
6216 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/);
6217 #else
6218 while (theCPB.dirInfo.ioDrDirID != fsRtDirID);
6219 #endif
6221 /* Get the information about the volume on which the file reside */
6222 theCPB.dirInfo.ioFDirIndex = -1;
6223 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */
6224 theCPB.dirInfo.ioVRefNum = file.vRefNum;
6225 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */
6226 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID;
6228 /* As ioFDirIndex = -1, get the info of ioDrDirID, */
6229 /* *ioNamePtr[0 TO 31] will be updated */
6230 error = PBGetCatInfo(&theCPB,false);
6232 if (error)
6233 return NULL;
6235 /* For MacOS Classic always add the volume name */
6236 /* For MacOS X add the volume name preceded by "Volumes" */
6237 /* when we are not referring to the boot volume */
6238 #ifdef USE_UNIXFILENAME
6239 if (file.vRefNum != dfltVol_vRefNum)
6240 #endif
6242 /* Add the volume name */
6243 STRCPY(temporaryPtr, filenamePtr);
6244 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]);
6245 STRCAT(filenamePtr, ":");
6246 STRCAT(filenamePtr, temporaryPtr);
6248 #ifdef USE_UNIXFILENAME
6249 STRCPY(temporaryPtr, filenamePtr);
6250 filenamePtr[0] = 0; /* NULL terminate the string */
6251 STRCAT(filenamePtr, "Volumes:");
6252 STRCAT(filenamePtr, temporaryPtr);
6253 #endif
6256 /* Append final path separator if it's a folder */
6257 if (folder)
6258 STRCAT(fname, ":");
6260 /* As we use Unix File Name for MacOS X convert it */
6261 #ifdef USE_UNIXFILENAME
6262 /* Need to insert leading / */
6263 /* TODO: get the above code to use directly the / */
6264 STRCPY(&temporaryPtr[1], filenamePtr);
6265 temporaryPtr[0] = '/';
6266 STRCPY(filenamePtr, temporaryPtr);
6268 char *p;
6269 for (p = fname; *p; p++)
6270 if (*p == ':')
6271 *p = '/';
6273 #endif
6275 return (vim_strsave(fname));
6276 #endif
6279 #if (defined(USE_IM_CONTROL) || defined(PROTO)) && defined(USE_CARBONKEYHANDLER)
6281 * Input Method Control functions.
6285 * Notify cursor position to IM.
6287 void
6288 im_set_position(int row, int col)
6290 #if 0
6291 /* TODO: Implement me! */
6292 im_start_row = row;
6293 im_start_col = col;
6294 #endif
6297 static ScriptLanguageRecord gTSLWindow;
6298 static ScriptLanguageRecord gTSLInsert;
6299 static ScriptLanguageRecord gTSLDefault = { 0, 0 };
6301 static Component gTSCWindow;
6302 static Component gTSCInsert;
6303 static Component gTSCDefault;
6305 static int im_initialized = 0;
6307 static void
6308 im_on_window_switch(int active)
6310 ScriptLanguageRecord *slptr = NULL;
6311 OSStatus err;
6313 if (! gui.in_use)
6314 return;
6316 if (im_initialized == 0)
6318 im_initialized = 1;
6320 /* save default TSM component (should be U.S.) to default */
6321 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6322 kKeyboardInputMethodClass);
6325 if (active == TRUE)
6327 im_is_active = TRUE;
6328 ActivateTSMDocument(gTSMDocument);
6329 slptr = &gTSLWindow;
6331 if (slptr)
6333 err = SetDefaultInputMethodOfClass(gTSCWindow, slptr,
6334 kKeyboardInputMethodClass);
6335 if (err == noErr)
6336 err = SetTextServiceLanguage(slptr);
6338 if (err == noErr)
6339 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6342 else
6344 err = GetTextServiceLanguage(&gTSLWindow);
6345 if (err == noErr)
6346 slptr = &gTSLWindow;
6348 if (slptr)
6349 GetDefaultInputMethodOfClass(&gTSCWindow, slptr,
6350 kKeyboardInputMethodClass);
6352 im_is_active = FALSE;
6353 DeactivateTSMDocument(gTSMDocument);
6358 * Set IM status on ("active" is TRUE) or off ("active" is FALSE).
6360 void
6361 im_set_active(int active)
6363 ScriptLanguageRecord *slptr = NULL;
6364 OSStatus err;
6366 if (! gui.in_use)
6367 return;
6369 if (im_initialized == 0)
6371 im_initialized = 1;
6373 /* save default TSM component (should be U.S.) to default */
6374 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault,
6375 kKeyboardInputMethodClass);
6378 if (active == TRUE)
6380 im_is_active = TRUE;
6381 ActivateTSMDocument(gTSMDocument);
6382 slptr = &gTSLInsert;
6384 if (slptr)
6386 err = SetDefaultInputMethodOfClass(gTSCInsert, slptr,
6387 kKeyboardInputMethodClass);
6388 if (err == noErr)
6389 err = SetTextServiceLanguage(slptr);
6391 if (err == noErr)
6392 KeyScript(slptr->fScript | smKeyForceKeyScriptMask);
6395 else
6397 err = GetTextServiceLanguage(&gTSLInsert);
6398 if (err == noErr)
6399 slptr = &gTSLInsert;
6401 if (slptr)
6402 GetDefaultInputMethodOfClass(&gTSCInsert, slptr,
6403 kKeyboardInputMethodClass);
6405 /* restore to default when switch to normal mode, so than we could
6406 * enter commands easier */
6407 SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault,
6408 kKeyboardInputMethodClass);
6409 SetTextServiceLanguage(&gTSLDefault);
6411 im_is_active = FALSE;
6412 DeactivateTSMDocument(gTSMDocument);
6417 * Get IM status. When IM is on, return not 0. Else return 0.
6420 im_get_status(void)
6422 if (! gui.in_use)
6423 return 0;
6425 return im_is_active;
6428 #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
6433 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
6434 // drawer implementation
6435 static MenuRef contextMenu = NULL;
6436 enum
6438 kTabContextMenuId = 42,
6441 // the caller has to CFRelease() the returned string
6442 static CFStringRef
6443 getTabLabel(tabpage_T *page)
6445 get_tabline_label(page, FALSE);
6446 #ifdef MACOS_CONVERT
6447 return mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
6448 #else
6449 // TODO: check internal encoding?
6450 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff,
6451 kCFStringEncodingMacRoman);
6452 #endif
6456 #define DRAWER_SIZE 150
6457 #define DRAWER_INSET 16
6459 static ControlRef dataBrowser = NULL;
6461 // when the tabline is hidden, vim doesn't call update_tabline(). When
6462 // the tabline is shown again, show_tabline() is called before upate_tabline(),
6463 // and because of this, the tab labels and vims internal tabs are out of sync
6464 // for a very short time. to prevent inconsistent state, we store the labels
6465 // of the tabs, not pointers to the tabs (which are invalid for a short time).
6466 static CFStringRef *tabLabels = NULL;
6467 static int tabLabelsSize = 0;
6469 enum
6471 kTabsColumn = 'Tabs'
6474 static int
6475 getTabCount(void)
6477 tabpage_T *tp;
6478 int numTabs = 0;
6480 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
6481 ++numTabs;
6482 return numTabs;
6485 // data browser item display callback
6486 static OSStatus
6487 dbItemDataCallback(ControlRef browser,
6488 DataBrowserItemID itemID,
6489 DataBrowserPropertyID property /* column id */,
6490 DataBrowserItemDataRef itemData,
6491 Boolean changeValue)
6493 OSStatus status = noErr;
6495 // assert(property == kTabsColumn); // why is this violated??
6497 // changeValue is true if we have a modifieable list and data was changed.
6498 // In our case, it's always false.
6499 // (that is: if (changeValue) updateInternalData(); else return
6500 // internalData();
6501 if (!changeValue)
6503 CFStringRef str;
6505 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
6506 str = tabLabels[itemID - 1];
6507 status = SetDataBrowserItemDataText(itemData, str);
6509 else
6510 status = errDataBrowserPropertyNotSupported;
6512 return status;
6515 // data browser action callback
6516 static void
6517 dbItemNotificationCallback(ControlRef browser,
6518 DataBrowserItemID item,
6519 DataBrowserItemNotification message)
6521 switch (message)
6523 case kDataBrowserItemSelected:
6524 send_tabline_event(item);
6525 break;
6529 // callbacks needed for contextual menu:
6530 static void
6531 dbGetContextualMenuCallback(ControlRef browser,
6532 MenuRef *menu,
6533 UInt32 *helpType,
6534 CFStringRef *helpItemString,
6535 AEDesc *selection)
6537 // on mac os 9: kCMHelpItemNoHelp, but it's not the same
6538 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-)
6539 *helpItemString = NULL;
6541 *menu = contextMenu;
6544 static void
6545 dbSelectContextualMenuCallback(ControlRef browser,
6546 MenuRef menu,
6547 UInt32 selectionType,
6548 SInt16 menuID,
6549 MenuItemIndex menuItem)
6551 if (selectionType == kCMMenuItemSelected)
6553 MenuCommand command;
6554 GetMenuItemCommandID(menu, menuItem, &command);
6556 // get tab that was selected when the context menu appeared
6557 // (there is always one tab selected). TODO: check if the context menu
6558 // isn't opened on an item but on empty space (has to be possible some
6559 // way, the finder does it too ;-) )
6560 Handle items = NewHandle(0);
6561 if (items != NULL)
6563 int numItems;
6565 GetDataBrowserItems(browser, kDataBrowserNoItem, false,
6566 kDataBrowserItemIsSelected, items);
6567 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID);
6568 if (numItems > 0)
6570 int idx;
6571 DataBrowserItemID *itemsPtr;
6573 HLock(items);
6574 itemsPtr = (DataBrowserItemID *)*items;
6575 idx = itemsPtr[0];
6576 HUnlock(items);
6577 send_tabline_menu_event(idx, command);
6579 DisposeHandle(items);
6584 // focus callback of the data browser to always leave focus in vim
6585 static OSStatus
6586 dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data)
6588 assert(GetEventClass(event) == kEventClassControl
6589 && GetEventKind(event) == kEventControlSetFocusPart);
6591 return paramErr;
6595 // drawer callback to resize data browser to drawer size
6596 static OSStatus
6597 drawerCallback(EventHandlerCallRef handler, EventRef event, void *data)
6599 switch (GetEventKind(event))
6601 case kEventWindowBoundsChanged: // move or resize
6603 UInt32 attribs;
6604 GetEventParameter(event, kEventParamAttributes, typeUInt32,
6605 NULL, sizeof(attribs), NULL, &attribs);
6606 if (attribs & kWindowBoundsChangeSizeChanged) // resize
6608 Rect r;
6609 GetWindowBounds(drawer, kWindowContentRgn, &r);
6610 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
6611 SetControlBounds(dataBrowser, &r);
6612 SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
6613 kTabsColumn, r.right);
6616 break;
6619 return eventNotHandledErr;
6622 // Load DataBrowserChangeAttributes() dynamically on tiger (and better).
6623 // This way the code works on 10.2 and 10.3 as well (it doesn't have the
6624 // blue highlights in the list view on these systems, though. Oh well.)
6627 #import <mach-o/dyld.h>
6629 enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) };
6631 static OSStatus
6632 myDataBrowserChangeAttributes(ControlRef inDataBrowser,
6633 OptionBits inAttributesToSet,
6634 OptionBits inAttributesToClear)
6636 long osVersion;
6637 char *symbolName;
6638 NSSymbol symbol = NULL;
6639 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser,
6640 OptionBits inAttributesToSet, OptionBits inAttributesToClear);
6642 Gestalt(gestaltSystemVersion, &osVersion);
6643 if (osVersion < 0x1040) // only supported for 10.4 (and up)
6644 return noErr;
6646 // C name mangling...
6647 symbolName = "_DataBrowserChangeAttributes";
6648 if (!NSIsSymbolNameDefined(symbolName)
6649 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL)
6650 return noErr;
6652 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol);
6653 if (dataBrowserChangeAttributes == NULL)
6654 return noErr; // well...
6655 return dataBrowserChangeAttributes(inDataBrowser,
6656 inAttributesToSet, inAttributesToClear);
6659 static void
6660 initialise_tabline(void)
6662 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
6663 DataBrowserCallbacks dbCallbacks;
6664 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
6665 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
6666 DataBrowserListViewColumnDesc colDesc;
6668 // drawers have to have compositing enabled
6669 CreateNewWindow(kDrawerWindowClass,
6670 kWindowStandardHandlerAttribute
6671 | kWindowCompositingAttribute
6672 | kWindowResizableAttribute
6673 | kWindowLiveResizeAttribute,
6674 &drawerRect, &drawer);
6676 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true);
6677 SetDrawerParent(drawer, gui.VimWindow);
6678 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET);
6681 // create list view embedded in drawer
6682 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
6683 &dataBrowser);
6685 dbCallbacks.version = kDataBrowserLatestCallbacks;
6686 InitDataBrowserCallbacks(&dbCallbacks);
6687 dbCallbacks.u.v1.itemDataCallback =
6688 NewDataBrowserItemDataUPP(dbItemDataCallback);
6689 dbCallbacks.u.v1.itemNotificationCallback =
6690 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
6691 dbCallbacks.u.v1.getContextualMenuCallback =
6692 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
6693 dbCallbacks.u.v1.selectContextualMenuCallback =
6694 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
6696 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
6698 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header
6699 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical
6700 SetDataBrowserSelectionFlags(dataBrowser,
6701 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
6702 SetDataBrowserTableViewHiliteStyle(dataBrowser,
6703 kDataBrowserTableViewFillHilite);
6704 Boolean b = false;
6705 SetControlData(dataBrowser, kControlEntireControl,
6706 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
6708 // enable blue background in data browser (this is only in 10.4 and vim
6709 // has to support older osx versions as well, so we have to load this
6710 // function dynamically)
6711 myDataBrowserChangeAttributes(dataBrowser,
6712 kMyDataBrowserAttributeListViewAlternatingRowColors, 0);
6714 // install callback that keeps focus in vim and away from the data browser
6715 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
6716 NULL, NULL);
6718 // install callback that keeps data browser at the size of the drawer
6719 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
6720 NULL, NULL);
6722 // add "tabs" column to data browser
6723 colDesc.propertyDesc.propertyID = kTabsColumn;
6724 colDesc.propertyDesc.propertyType = kDataBrowserTextType;
6726 // add if items can be selected (?): kDataBrowserListViewSelectionColumn
6727 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
6729 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
6730 colDesc.headerBtnDesc.minimumWidth = 100;
6731 colDesc.headerBtnDesc.maximumWidth = 150;
6732 colDesc.headerBtnDesc.titleOffset = 0;
6733 colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
6734 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
6735 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font
6736 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
6738 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
6740 // create tabline popup menu required by vim docs (see :he tabline-menu)
6741 CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
6742 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0,
6743 TABLINE_MENU_CLOSE, NULL);
6744 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
6745 TABLINE_MENU_NEW, NULL);
6746 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
6747 TABLINE_MENU_OPEN, NULL);
6752 * Show or hide the tabline.
6754 void
6755 gui_mch_show_tabline(int showit)
6757 if (showit == 0)
6758 CloseDrawer(drawer, true);
6759 else
6760 OpenDrawer(drawer, kWindowEdgeRight, true);
6764 * Return TRUE when tabline is displayed.
6767 gui_mch_showing_tabline(void)
6769 WindowDrawerState state = GetDrawerState(drawer);
6771 return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
6775 * Update the labels of the tabline.
6777 void
6778 gui_mch_update_tabline(void)
6780 tabpage_T *tp;
6781 int numTabs = getTabCount();
6782 int nr = 1;
6783 int curtabidx = 1;
6785 // adjust data browser
6786 if (tabLabels != NULL)
6788 int i;
6790 for (i = 0; i < tabLabelsSize; ++i)
6791 CFRelease(tabLabels[i]);
6792 free(tabLabels);
6794 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef));
6795 tabLabelsSize = numTabs;
6797 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
6799 if (tp == curtab)
6800 curtabidx = nr;
6801 tabLabels[nr-1] = getTabLabel(tp);
6804 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
6805 kDataBrowserItemNoProperty);
6806 // data browser uses ids 1, 2, 3, ... numTabs per default, so we
6807 // can pass NULL for the id array
6808 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
6809 kDataBrowserItemNoProperty);
6811 DataBrowserItemID item = curtabidx;
6812 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6816 * Set the current tab to "nr". First tab is 1.
6818 void
6819 gui_mch_set_curtab(nr)
6820 int nr;
6822 DataBrowserItemID item = nr;
6823 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
6825 // TODO: call something like this?: (or restore scroll position, or...)
6826 RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
6827 kDataBrowserRevealOnly);
6830 #endif // FEAT_GUI_TABLINE