diff options
Diffstat (limited to 'src/gui_mac.c')
-rw-r--r-- | src/gui_mac.c | 6706 |
1 files changed, 0 insertions, 6706 deletions
diff --git a/src/gui_mac.c b/src/gui_mac.c deleted file mode 100644 index b19061705..000000000 --- a/src/gui_mac.c +++ /dev/null @@ -1,6706 +0,0 @@ -/* vi:set ts=8 sts=4 sw=4 noet: - * - * VIM - Vi IMproved by Bram Moolenaar - * GUI/Motif support by Robert Webb - * Macintosh port by Dany St-Amant - * and Axel Kielhorn - * Port to MPW by Bernhard Pruemmer - * Initial Carbon port by Ammon Skidmore - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * NOTES: - Vim 7+ does not support classic MacOS. Please use Vim 6.x - * - Comments mentioning FAQ refer to the book: - * "Macworld Mac Programming FAQs" from "IDG Books" - */ - -/* - * TODO: Change still to merge from the macvim's iDisk - * - * error_ga, mch_errmsg, Navigation's changes in gui_mch_browse - * uses of MenuItemIndex, changes in gui_mch_set_shellsize, - * ScrapManager error handling. - * Comments about function remaining to Carbonize. - * - */ - -/* - * TODO (Jussi) - * * Clipboard does not work (at least some cases) - * * ATSU font rendering has some problems - * * Investigate and remove dead code (there is still lots of that) - */ - -#include <Devices.h> // included first to avoid CR problems -#include "vim.h" - -#define USE_CARBONIZED -#define USE_AEVENT // Enable AEVENT -#undef USE_OFFSETED_WINDOW // Debugging feature: start Vim window OFFSETed - -// Compile as CodeWarrior External Editor -#if defined(FEAT_CW_EDITOR) && !defined(USE_AEVENT) -# define USE_AEVENT // Need Apple Event Support -#endif - -// Vim's Scrap flavor. -#define VIMSCRAPFLAVOR 'VIM!' -#define SCRAPTEXTFLAVOR kScrapFlavorTypeUnicode - -static EventHandlerUPP mouseWheelHandlerUPP = NULL; -SInt32 gMacSystemVersion; - -#ifdef MACOS_CONVERT -# define USE_CARBONKEYHANDLER - -static int im_is_active = FALSE; -# if 0 - // TODO: Implement me! -static int im_start_row = 0; -static int im_start_col = 0; -# endif - -# define NR_ELEMS(x) (sizeof(x) / sizeof(x[0])) - -static TSMDocumentID gTSMDocument; - -static void im_on_window_switch(int active); -static EventHandlerUPP keyEventHandlerUPP = NULL; -static EventHandlerUPP winEventHandlerUPP = NULL; - -static pascal OSStatus gui_mac_handle_window_activate( - EventHandlerCallRef nextHandler, EventRef theEvent, void *data); - -static pascal OSStatus gui_mac_handle_text_input( - EventHandlerCallRef nextHandler, EventRef theEvent, void *data); - -static pascal OSStatus gui_mac_update_input_area( - EventHandlerCallRef nextHandler, EventRef theEvent); - -static pascal OSStatus gui_mac_unicode_key_event( - EventHandlerCallRef nextHandler, EventRef theEvent); - -#endif - - -// Include some file. TODO: move into os_mac.h -#include <Menus.h> -#include <Resources.h> -#include <Processes.h> -#ifdef USE_AEVENT -# include <AppleEvents.h> -# include <AERegistry.h> -#endif -# include <Gestalt.h> -#if UNIVERSAL_INTERFACES_VERSION >= 0x0330 -# include <ControlDefinitions.h> -# include <Navigation.h> // Navigation only part of ?? -#endif - -// Help Manager (balloon.h, HM prefixed functions) are not supported -// under Carbon (Jussi) -# if 0 -// New Help Interface for Mac, not implemented yet. -# include <MacHelp.h> -# endif - -/* - * These seem to be rectangle options. Why are they not found in - * headers? (Jussi) - */ -#define kNothing 0 -#define kCreateEmpty 2 //1 -#define kCreateRect 2 -#define kDestroy 3 - -/* - * Dany: Don't like those... - */ -#define topLeft(r) (((Point*)&(r))[0]) -#define botRight(r) (((Point*)&(r))[1]) - - -// Time of last mouse click, to detect double-click -static long lastMouseTick = 0; - -// ??? -static RgnHandle cursorRgn; -static RgnHandle dragRgn; -static Rect dragRect; -static short dragRectEnbl; -static short dragRectControl; - -// This variable is set when waiting for an event, which is the only moment -// scrollbar dragging can be done directly. It's not allowed while commands -// are executed, because it may move the cursor and that may cause unexpected -// problems (e.g., while ":s" is working). -static int allow_scrollbar = FALSE; - -// Last mouse click caused contextual menu, (to provide proper release) -static short clickIsPopup; - -// Feedback Action for Scrollbar -ControlActionUPP gScrollAction; -ControlActionUPP gScrollDrag; - -// Keeping track of which scrollbar is being dragged -static ControlHandle dragged_sb = NULL; - -// Vector of char_u --> control index for hotkeys in dialogs -static short *gDialogHotKeys; - -static struct -{ - FMFontFamily family; - FMFontSize size; - FMFontStyle style; - Boolean isPanelVisible; -} gFontPanelInfo = { 0, 0, 0, false }; - -#ifdef MACOS_CONVERT -# define USE_ATSUI_DRAWING -int p_macatsui_last; -ATSUStyle gFontStyle; -ATSUStyle gWideFontStyle; -Boolean gIsFontFallbackSet; -UInt32 useAntialias_cached = 0x0; -#endif - -// Colors Macros -#define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b) -#define Red(c) ((c & 0x00FF0000) >> 16) -#define Green(c) ((c & 0x0000FF00) >> 8) -#define Blue(c) ((c & 0x000000FF) >> 0) - -// Key mapping - -#define vk_Esc 0x35 // -> 1B - -#define vk_F1 0x7A // -> 10 -#define vk_F2 0x78 //0x63 -#define vk_F3 0x63 //0x76 -#define vk_F4 0x76 //0x60 -#define vk_F5 0x60 //0x61 -#define vk_F6 0x61 //0x62 -#define vk_F7 0x62 //0x63 ? -#define vk_F8 0x64 -#define vk_F9 0x65 -#define vk_F10 0x6D -#define vk_F11 0x67 -#define vk_F12 0x6F -#define vk_F13 0x69 -#define vk_F14 0x6B -#define vk_F15 0x71 - -#define vk_Clr 0x47 // -> 1B (ESC) -#define vk_Enter 0x4C // -> 03 - -#define vk_Space 0x31 // -> 20 -#define vk_Tab 0x30 // -> 09 -#define vk_Return 0x24 // -> 0D -// This is wrong for OSX, what is it for? -#define vk_Delete 0X08 // -> 08 BackSpace - -#define vk_Help 0x72 // -> 05 -#define vk_Home 0x73 // -> 01 -#define vk_PageUp 0x74 // -> 0D -#define vk_FwdDelete 0x75 // -> 7F -#define vk_End 0x77 // -> 04 -#define vk_PageDown 0x79 // -> 0C - -#define vk_Up 0x7E // -> 1E -#define vk_Down 0x7D // -> 1F -#define vk_Left 0x7B // -> 1C -#define vk_Right 0x7C // -> 1D - -#define vk_Undo vk_F1 -#define vk_Cut vk_F2 -#define vk_Copy vk_F3 -#define vk_Paste vk_F4 -#define vk_PrintScreen vk_F13 -#define vk_SCrollLock vk_F14 -#define vk_Pause vk_F15 -#define vk_NumLock vk_Clr -#define vk_Insert vk_Help - -#define KeySym char - -static struct -{ - KeySym key_sym; - char_u vim_code0; - char_u vim_code1; -} special_keys[] = -{ - {vk_Up, 'k', 'u'}, - {vk_Down, 'k', 'd'}, - {vk_Left, 'k', 'l'}, - {vk_Right, 'k', 'r'}, - - {vk_F1, 'k', '1'}, - {vk_F2, 'k', '2'}, - {vk_F3, 'k', '3'}, - {vk_F4, 'k', '4'}, - {vk_F5, 'k', '5'}, - {vk_F6, 'k', '6'}, - {vk_F7, 'k', '7'}, - {vk_F8, 'k', '8'}, - {vk_F9, 'k', '9'}, - {vk_F10, 'k', ';'}, - - {vk_F11, 'F', '1'}, - {vk_F12, 'F', '2'}, - {vk_F13, 'F', '3'}, - {vk_F14, 'F', '4'}, - {vk_F15, 'F', '5'}, - -// {XK_Help, '%', '1'}, -// {XK_Undo, '&', '8'}, -// {XK_BackSpace, 'k', 'b'}, -// {vk_Delete, 'k', 'b'}, - {vk_Insert, 'k', 'I'}, - {vk_FwdDelete, 'k', 'D'}, - {vk_Home, 'k', 'h'}, - {vk_End, '@', '7'}, -// {XK_Prior, 'k', 'P'}, -// {XK_Next, 'k', 'N'}, -// {XK_Print, '%', '9'}, - - {vk_PageUp, 'k', 'P'}, - {vk_PageDown, 'k', 'N'}, - - // End of list marker: - {(KeySym)0, 0, 0} -}; - -/* - * ------------------------------------------------------------ - * Forward declaration (for those needed) - * ------------------------------------------------------------ - */ - -#ifdef USE_AEVENT -OSErr HandleUnusedParms(const AppleEvent *theAEvent); -#endif - -#ifdef FEAT_GUI_TABLINE -static void initialise_tabline(void); -static WindowRef drawer = NULL; // TODO: put into gui.h -#endif - -#ifdef USE_ATSUI_DRAWING -static void gui_mac_set_font_attributes(GuiFont font); -#endif - -/* - * ------------------------------------------------------------ - * Conversion Utility - * ------------------------------------------------------------ - */ - -/* - * C2Pascal_save - * - * Allocate memory and convert the C-String passed in - * into a pascal string - * - */ - - char_u * -C2Pascal_save(char_u *Cstring) -{ - char_u *PascalString; - int len; - - if (Cstring == NULL) - return NULL; - - len = STRLEN(Cstring); - - if (len > 255) // Truncate if necessary - len = 255; - - PascalString = alloc(len + 1); - if (PascalString != NULL) - { - mch_memmove(PascalString + 1, Cstring, len); - PascalString[0] = len; - } - - return PascalString; -} - -/* - * C2Pascal_save_and_remove_backslash - * - * Allocate memory and convert the C-String passed in - * into a pascal string. Also remove the backslash at the same time - * - */ - - char_u * -C2Pascal_save_and_remove_backslash(char_u *Cstring) -{ - char_u *PascalString; - int len; - char_u *p, *c; - - len = STRLEN(Cstring); - - if (len > 255) // Truncate if necessary - len = 255; - - PascalString = alloc(len + 1); - if (PascalString != NULL) - { - for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++) - { - if ((*c == '\\') && (c[1] != 0)) - c++; - *p = *c; - p++; - len++; - } - PascalString[0] = len; - } - - return PascalString; -} - -/* - * Convert the modifiers of an Event into vim's modifiers (mouse) - */ - - int_u -EventModifiers2VimMouseModifiers(EventModifiers macModifiers) -{ - int_u vimModifiers = 0x00; - - if (macModifiers & (shiftKey | rightShiftKey)) - vimModifiers |= MOUSE_SHIFT; - if (macModifiers & (controlKey | rightControlKey)) - vimModifiers |= MOUSE_CTRL; - if (macModifiers & (optionKey | rightOptionKey)) - vimModifiers |= MOUSE_ALT; -#if 0 - // Not yet supported - if (macModifiers & (cmdKey)) // There's no rightCmdKey - vimModifiers |= MOUSE_CMD; -#endif - return (vimModifiers); -} - -/* - * Convert the modifiers of an Event into vim's modifiers (keys) - */ - - static int_u -EventModifiers2VimModifiers(EventModifiers macModifiers) -{ - int_u vimModifiers = 0x00; - - if (macModifiers & (shiftKey | rightShiftKey)) - vimModifiers |= MOD_MASK_SHIFT; - if (macModifiers & (controlKey | rightControlKey)) - vimModifiers |= MOD_MASK_CTRL; - if (macModifiers & (optionKey | rightOptionKey)) - vimModifiers |= MOD_MASK_ALT; -#ifdef USE_CMD_KEY - if (macModifiers & (cmdKey)) // There's no rightCmdKey - vimModifiers |= MOD_MASK_CMD; -#endif - return (vimModifiers); -} - -/* - * Convert a string representing a point size into pixels. The string should - * be a positive decimal number, with an optional decimal point (eg, "12", or - * "10.5"). The pixel value is returned, and a pointer to the next unconverted - * character is stored in *end. The flag "vertical" says whether this - * calculation is for a vertical (height) size or a horizontal (width) one. - * - * From gui_w48.c - */ - static int -points_to_pixels(char_u *str, char_u **end, int vertical) -{ - int pixels; - int points = 0; - int divisor = 0; - - while (*str) - { - if (*str == '.' && divisor == 0) - { - // Start keeping a divisor, for later - divisor = 1; - continue; - } - - if (!isdigit(*str)) - break; - - points *= 10; - points += *str - '0'; - divisor *= 10; - - ++str; - } - - if (divisor == 0) - divisor = 1; - - pixels = points/divisor; - *end = str; - return pixels; -} - -#ifdef MACOS_CONVERT -/* - * Deletes all traces of any Windows-style mnemonic text (including any - * parentheses) from a menu item and returns the cleaned menu item title. - * The caller is responsible for releasing the returned string. - */ - static CFStringRef -menu_title_removing_mnemonic(vimmenu_T *menu) -{ - CFStringRef name; - size_t menuTitleLen; - CFIndex displayLen; - CFRange mnemonicStart; - CFRange mnemonicEnd; - CFMutableStringRef cleanedName; - - menuTitleLen = STRLEN(menu->dname); - name = (CFStringRef) mac_enc_to_cfstring(menu->dname, menuTitleLen); - - if (name) - { - // Simple mnemonic-removal algorithm, assumes single parenthesized - // mnemonic character towards the end of the menu text - mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards); - displayLen = CFStringGetLength(name); - - if (mnemonicStart.location != kCFNotFound - && (mnemonicStart.location + 2) < displayLen - && CFStringGetCharacterAtIndex(name, - mnemonicStart.location + 1) == (UniChar)menu->mnemonic) - { - if (CFStringFindWithOptions(name, CFSTR(")"), - CFRangeMake(mnemonicStart.location + 1, - displayLen - mnemonicStart.location - 1), - kCFCompareBackwards, &mnemonicEnd) && - (mnemonicStart.location + 2) == mnemonicEnd.location) - { - cleanedName = CFStringCreateMutableCopy(NULL, 0, name); - if (cleanedName) - { - CFStringDelete(cleanedName, - CFRangeMake(mnemonicStart.location, - mnemonicEnd.location + 1 - - mnemonicStart.location)); - - CFRelease(name); - name = cleanedName; - } - } - } - } - - return name; -} -#endif - -/* - * Convert a list of FSSpec aliases into a list of fullpathname - * character strings. - */ - - char_u ** -new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error) -{ - char_u **fnames = NULL; - OSErr newError; - long fileCount; - FSSpec fileToOpen; - long actualSize; - AEKeyword dummyKeyword; - DescType dummyType; - - // Get number of files in list - *error = AECountItems(theList, numFiles); - if (*error) - return fnames; - - // Allocate the pointer list - fnames = ALLOC_MULT(char_u *, *numFiles); - - // Empty out the list - for (fileCount = 0; fileCount < *numFiles; fileCount++) - fnames[fileCount] = NULL; - - // Scan the list of FSSpec - for (fileCount = 1; fileCount <= *numFiles; fileCount++) - { - // Get the alias for the nth file, convert to an FSSpec - newError = AEGetNthPtr(theList, fileCount, typeFSS, - &dummyKeyword, &dummyType, - (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize); - if (newError) - { - // Caller is able to clean up - // TODO: Should be clean up or not? For safety. - return fnames; - } - - // Convert the FSSpec to a pathname - fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen); - } - - return (fnames); -} - -/* - * ------------------------------------------------------------ - * CodeWarrior External Editor Support - * ------------------------------------------------------------ - */ -#ifdef FEAT_CW_EDITOR - -/* - * Handle the Window Search event from CodeWarrior - * - * Description - * ----------- - * - * The IDE sends the Window Search AppleEvent to the editor when it - * needs to know whether a particular file is open in the editor. - * - * Event Reply - * ----------- - * - * None. Put data in the location specified in the structure received. - * - * Remarks - * ------- - * - * When the editor receives this event, determine whether the specified - * file is open. If it is, return the modification date/time for that file - * in the appropriate location specified in the structure. If the file is - * not opened, put the value fnfErr(file not found) in that location. - * - */ - -typedef struct WindowSearch WindowSearch; -struct WindowSearch // for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar -{ - FSSpec theFile; // identifies the file - long *theDate; // where to put the modification date/time -}; - - pascal OSErr -Handle_KAHL_SRCH_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - buf_T *buf; - int foundFile = false; - DescType typeCode; - WindowSearch SearchData; - Size actualSize; - - error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize); - if (error) - return error; - - error = HandleUnusedParms(theAEvent); - if (error) - return error; - - FOR_ALL_BUFFERS(buf) - if (buf->b_ml.ml_mfp != NULL - && SearchData.theFile.parID == buf->b_FSSpec.parID - && SearchData.theFile.name[0] == buf->b_FSSpec.name[0] - && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0) - { - foundFile = true; - break; - } - - if (foundFile == false) - *SearchData.theDate = fnfErr; - else - *SearchData.theDate = buf->b_mtime; - - return error; -}; - -/* - * Handle the Modified (from IDE to Editor) event from CodeWarrior - * - * Description - * ----------- - * - * The IDE sends this event to the external editor when it wants to - * know which files that are open in the editor have been modified. - * - * Parameters None. - * ---------- - * - * Event Reply - * ----------- - * The reply for this event is: - * - * keyDirectObject typeAEList required - * each element in the list is a structure of typeChar - * - * Remarks - * ------- - * - * When building the reply event, include one element in the list for - * each open file that has been modified. - * - */ - -typedef struct ModificationInfo ModificationInfo; -struct ModificationInfo // for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList -{ - FSSpec theFile; // identifies the file - long theDate; // the date/time the file was last modified - short saved; // set this to zero when replying, unused -}; - - pascal OSErr -Handle_KAHL_MOD_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - AEDescList replyList; - long numFiles; - ModificationInfo theFile; - buf_T *buf; - - theFile.saved = 0; - - error = HandleUnusedParms(theAEvent); - if (error) - return error; - - // Send the reply -// replyObject.descriptorType = typeNull; -// replyObject.dataHandle = nil; - -// AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) - error = AECreateList(nil, 0, false, &replyList); - if (error) - return error; - -#if 0 - error = AECountItems(&replyList, &numFiles); - - // AEPutKeyDesc(&replyList, keyAEPnject, &aDesc) - // AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType, - // sizeof(DescType)) - - // AEPutDesc -#endif - - numFiles = 0; - FOR_ALL_BUFFERS(buf) - if (buf->b_ml.ml_mfp != NULL) - { - // Add this file to the list - theFile.theFile = buf->b_FSSpec; - theFile.theDate = buf->b_mtime; -// theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; - error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile)); - }; - -#if 0 - error = AECountItems(&replyList, &numFiles); -#endif - - // We can add data only if something to reply - error = AEPutParamDesc(theReply, keyDirectObject, &replyList); - - if (replyList.dataHandle) - AEDisposeDesc(&replyList); - - return error; -}; - -/* - * Handle the Get Text event from CodeWarrior - * - * Description - * ----------- - * - * The IDE sends the Get Text AppleEvent to the editor when it needs - * the source code from a file. For example, when the user issues a - * Check Syntax or Compile command, the compiler needs access to - * the source code contained in the file. - * - * Event Reply - * ----------- - * - * None. Put data in locations specified in the structure received. - * - * Remarks - * ------- - * - * When the editor receives this event, it must set the size of the handle - * in theText to fit the data in the file. It must then copy the entire - * contents of the specified file into the memory location specified in - * theText. - * - */ - -typedef struct CW_GetText CW_GetText; -struct CW_GetText // for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar -{ - FSSpec theFile; // identifies the file - Handle theText; // the location where you return the text (must be resized properly) - long *unused; // 0 (not used) - long *theDate; // where to put the modification date/time -}; - - pascal OSErr -Handle_KAHL_GTTX_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - buf_T *buf; - int foundFile = false; - DescType typeCode; - CW_GetText GetTextData; - Size actualSize; - char_u *line; - char_u *fullbuffer = NULL; - long linesize; - long lineStart; - long BufferSize; - long lineno; - - error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize); - - if (error) - return error; - - FOR_ALL_BUFFERS(buf) - if (buf->b_ml.ml_mfp != NULL) - if (GetTextData.theFile.parID == buf->b_FSSpec.parID) - { - foundFile = true; - break; - } - - if (foundFile) - { - BufferSize = 0; // GetHandleSize(GetTextData.theText); - for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++) - { - // Must use the right buffer - line = ml_get_buf(buf, (linenr_T) lineno, FALSE); - linesize = STRLEN(line) + 1; - lineStart = BufferSize; - BufferSize += linesize; - // Resize handle to linesize+1 to include the linefeed - SetHandleSize(GetTextData.theText, BufferSize); - if (GetHandleSize(GetTextData.theText) != BufferSize) - { - break; // Simple handling for now - } - else - { - HLock(GetTextData.theText); - fullbuffer = (char_u *) *GetTextData.theText; - STRCPY((char_u *)(fullbuffer + lineStart), line); - fullbuffer[BufferSize-1] = '\r'; - HUnlock(GetTextData.theText); - } - } - if (fullbuffer != NULL) - { - HLock(GetTextData.theText); - fullbuffer[BufferSize-1] = 0; - HUnlock(GetTextData.theText); - } - if (foundFile == false) - *GetTextData.theDate = fnfErr; - else -// *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0; - *GetTextData.theDate = buf->b_mtime; - } - - error = HandleUnusedParms(theAEvent); - - return error; -} - -/* - * - */ - -/* - * Taken from MoreAppleEvents:ProcessHelpers - */ - pascal OSErr -FindProcessBySignature( - const OSType targetType, - const OSType targetCreator, - ProcessSerialNumberPtr psnPtr) -{ - OSErr anErr = noErr; - Boolean lookingForProcess = true; - - ProcessInfoRec infoRec; - - infoRec.processInfoLength = sizeof(ProcessInfoRec); - infoRec.processName = nil; - infoRec.processAppSpec = nil; - - psnPtr->lowLongOfPSN = kNoProcess; - psnPtr->highLongOfPSN = kNoProcess; - - while (lookingForProcess) - { - anErr = GetNextProcess(psnPtr); - if (anErr != noErr) - lookingForProcess = false; - else - { - anErr = GetProcessInformation(psnPtr, &infoRec); - if ((anErr == noErr) - && (infoRec.processType == targetType) - && (infoRec.processSignature == targetCreator)) - lookingForProcess = false; - } - } - - return anErr; -}//end FindProcessBySignature - - void -Send_KAHL_MOD_AE(buf_T *buf) -{ - OSErr anErr = noErr; - AEDesc targetAppDesc = { typeNull, nil }; - ProcessSerialNumber psn = { kNoProcess, kNoProcess }; - AppleEvent theReply = { typeNull, nil }; - AESendMode sendMode; - AppleEvent theEvent = {typeNull, nil }; - AEIdleUPP idleProcUPP = nil; - ModificationInfo ModData; - - - anErr = FindProcessBySignature('APPL', 'CWIE', &psn); - if (anErr == noErr) - { - anErr = AECreateDesc(typeProcessSerialNumber, &psn, - sizeof(ProcessSerialNumber), &targetAppDesc); - - if (anErr == noErr) - { - anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc, - kAutoGenerateReturnID, kAnyTransactionID, &theEvent); - } - - AEDisposeDesc(&targetAppDesc); - - // Add the parms - ModData.theFile = buf->b_FSSpec; - ModData.theDate = buf->b_mtime; - - if (anErr == noErr) - anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData)); - - if (idleProcUPP == nil) - sendMode = kAENoReply; - else - sendMode = kAEWaitReply; - - if (anErr == noErr) - anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil); - if (anErr == noErr && sendMode == kAEWaitReply) - { -// anErr = AEHGetHandlerError(&theReply); - } - (void) AEDisposeDesc(&theReply); - } -} -#endif // FEAT_CW_EDITOR - -/* - * ------------------------------------------------------------ - * Apple Event Handling procedure - * ------------------------------------------------------------ - */ -#ifdef USE_AEVENT - -/* - * Handle the Unused parms of an AppleEvent - */ - - OSErr -HandleUnusedParms(const AppleEvent *theAEvent) -{ - OSErr error; - long actualSize; - DescType dummyType; - AEKeyword missedKeyword; - - // Get the "missed keyword" attribute from the AppleEvent. - error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr, - typeKeyword, &dummyType, - (Ptr)&missedKeyword, sizeof(missedKeyword), - &actualSize); - - // If the descriptor isn't found, then we got the required parameters. - if (error == errAEDescNotFound) - { - error = noErr; - } - else - { -#if 0 - // Why is this removed? - error = errAEEventNotHandled; -#endif - } - - return error; -} - - -/* - * Handle the ODoc AppleEvent - * - * Deals with all files dragged to the application icon. - * - */ - -typedef struct SelectionRange SelectionRange; -struct SelectionRange // for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar -{ - short unused1; // 0 (not used) - short lineNum; // line to select (<0 to specify range) - long startRange; // start of selection range (if line < 0) - long endRange; // end of selection range (if line < 0) - long unused2; // 0 (not used) - long theDate; // modification date/time -}; - -static long drop_numFiles; -static short drop_gotPosition; -static SelectionRange drop_thePosition; - - static void -drop_callback(void *cookie UNUSED) -{ - // TODO: Handle the goto/select line more cleanly - if ((drop_numFiles == 1) & (drop_gotPosition)) - { - if (drop_thePosition.lineNum >= 0) - { - lnum = drop_thePosition.lineNum + 1; - // oap->motion_type = MLINE; - // setpcmark(); - if (lnum < 1L) - lnum = 1L; - else if (lnum > curbuf->b_ml.ml_line_count) - lnum = curbuf->b_ml.ml_line_count; - curwin->w_cursor.lnum = lnum; - curwin->w_cursor.col = 0; - // beginline(BL_SOL | BL_FIX); - } - else - goto_byte(drop_thePosition.startRange + 1); - } - - // Update the screen display - update_screen(NOT_VALID); - - // Select the text if possible - if (drop_gotPosition) - { - VIsual_active = TRUE; - VIsual_select = FALSE; - VIsual = curwin->w_cursor; - if (drop_thePosition.lineNum < 0) - { - VIsual_mode = 'v'; - goto_byte(drop_thePosition.endRange); - } - else - { - VIsual_mode = 'V'; - VIsual.col = 0; - } - } -} - -/* - * The IDE uses the optional keyAEPosition parameter to tell the ed- - * itor the selection range. If lineNum is zero or greater, scroll the text - * to the specified line. If lineNum is less than zero, use the values in - * startRange and endRange to select the specified characters. Scroll - * the text to display the selection. If lineNum, startRange, and - * endRange are all negative, there is no selection range specified. - */ - pascal OSErr -HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon) -{ - /* - * TODO: Clean up the code with convert the AppleEvent into - * a ":args" - */ - OSErr error = noErr; -// OSErr firstError = noErr; -// short numErrors = 0; - AEDesc theList; - DescType typeCode; - long numFiles; - // long fileCount; - char_u **fnames; -// char_u fname[256]; - Size actualSize; - SelectionRange thePosition; - short gotPosition = false; - long lnum; - - // the direct object parameter is the list of aliases to files (one or more) - error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList); - if (error) - return error; - - - error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize); - if (error == noErr) - gotPosition = true; - if (error == errAEDescNotFound) - error = noErr; - if (error) - return error; - -/* - error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition); - - if (^error) then - { - if (thePosition.lineNum >= 0) - { - // Goto this line - } - else - { - // Set the range char wise - } - } - */ - - reset_VIsual(); - fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error); - - if (error) - { - // TODO: empty fnames[] first - vim_free(fnames); - return (error); - } - - if (starting > 0) - { - int i; - char_u *p; - int fnum = -1; - - // these are the initial files dropped on the Vim icon - for (i = 0 ; i < numFiles; i++) - { - if (ga_grow(&global_alist.al_ga, 1) == FAIL - || (p = vim_strsave(fnames[i])) == NULL) - mch_exit(2); - else - alist_add(&global_alist, p, 2); - if (fnum == -1) - fnum = GARGLIST[GARGCOUNT - 1].ae_fnum; - } - - // If the file name was already in the buffer list we need to switch - // to it. - if (curbuf->b_fnum != fnum) - { - char_u cmd[30]; - - vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum); - do_cmdline_cmd(cmd); - } - - // Change directory to the location of the first file. - if (GARGCOUNT > 0 - && vim_chdirfile(alist_name(&GARGLIST[0]), "drop") == OK) - shorten_fnames(TRUE); - - goto finished; - } - - // Handle the drop, :edit to get to the file - drop_numFiles = numFiles; - drop_gotPosition = gotPosition; - drop_thePosition = thePosition; - handle_drop(numFiles, fnames, FALSE, drop_callback, NULL); - - setcursor(); - out_flush(); - - // Fake mouse event to wake from stall - PostEvent(mouseUp, 0); - -finished: - AEDisposeDesc(&theList); // dispose what we allocated - - error = HandleUnusedParms(theAEvent); - return error; -} - -/* - * - */ - pascal OSErr -Handle_aevt_oapp_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - - error = HandleUnusedParms(theAEvent); - return error; -} - -/* - * - */ - pascal OSErr -Handle_aevt_quit_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - - error = HandleUnusedParms(theAEvent); - if (error) - return error; - - // Need to fake a :confirm qa - do_cmdline_cmd((char_u *)"confirm qa"); - - return error; -} - -/* - * - */ - pascal OSErr -Handle_aevt_pdoc_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - - error = HandleUnusedParms(theAEvent); - - return error; -} - -/* - * Handling of unknown AppleEvent - * - * (Just get rid of all the parms) - */ - pascal OSErr -Handle_unknown_AE( - const AppleEvent *theAEvent, - AppleEvent *theReply, - long refCon) -{ - OSErr error = noErr; - - error = HandleUnusedParms(theAEvent); - - return error; -} - - -/* - * Install the various AppleEvent Handlers - */ - OSErr -InstallAEHandlers(void) -{ - OSErr error; - - // install open application handler - error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, - NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false); - if (error) - return error; - - // install quit application handler - error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, - NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false); - if (error) - return error; - - // install open document handler - error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, - NewAEEventHandlerUPP(HandleODocAE), 0, false); - if (error) - return error; - - // install print document handler - error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, - NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false); - -// Install Core Suite -#if 0 - error = AEInstallEventHandler(kAECoreSuite, kAEClone, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEClose, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAECountElements, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAECreateElement, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEDelete, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEGetData, - NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize, - NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAEMove, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAESave, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); - - error = AEInstallEventHandler(kAECoreSuite, kAESetData, - NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); -#endif - -#ifdef FEAT_CW_EDITOR - /* - * Bind codewarrior support handlers - */ - error = AEInstallEventHandler('KAHL', 'GTTX', - NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false); - if (error) - return error; - error = AEInstallEventHandler('KAHL', 'SRCH', - NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false); - if (error) - return error; - error = AEInstallEventHandler('KAHL', 'MOD ', - NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false); -#endif - - return error; - -} -#endif // USE_AEVENT - - -/* - * Callback function, installed by InstallFontPanelHandler(), below, - * to handle Font Panel events. - */ - static OSStatus -FontPanelHandler( - EventHandlerCallRef inHandlerCallRef, - EventRef inEvent, - void *inUserData) -{ - if (GetEventKind(inEvent) == kEventFontPanelClosed) - { - gFontPanelInfo.isPanelVisible = false; - return noErr; - } - - if (GetEventKind(inEvent) == kEventFontSelection) - { - OSStatus status; - FMFontFamily newFamily; - FMFontSize newSize; - FMFontStyle newStyle; - - // Retrieve the font family ID number. - status = GetEventParameter(inEvent, kEventParamFMFontFamily, - /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL, - /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL, - &newFamily); - if (status == noErr) - gFontPanelInfo.family = newFamily; - - // Retrieve the font size. - status = GetEventParameter(inEvent, kEventParamFMFontSize, - typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize); - if (status == noErr) - gFontPanelInfo.size = newSize; - - // Retrieve the font style (bold, etc.). Currently unused. - status = GetEventParameter(inEvent, kEventParamFMFontStyle, - typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle); - if (status == noErr) - gFontPanelInfo.style = newStyle; - } - return noErr; -} - - - static void -InstallFontPanelHandler(void) -{ - EventTypeSpec eventTypes[2]; - EventHandlerUPP handlerUPP; - // EventHandlerRef handlerRef; - - eventTypes[0].eventClass = kEventClassFont; - eventTypes[0].eventKind = kEventFontSelection; - eventTypes[1].eventClass = kEventClassFont; - eventTypes[1].eventKind = kEventFontPanelClosed; - - handlerUPP = NewEventHandlerUPP(FontPanelHandler); - - InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes, - /*userData=*/NULL, /*handlerRef=*/NULL); -} - - -/* - * Fill the buffer pointed to by outName with the name and size - * of the font currently selected in the Font Panel. - */ -#define FONT_STYLE_BUFFER_SIZE 32 - static void -GetFontPanelSelection(char_u *outName) -{ - Str255 buf; - ByteCount fontNameLen = 0; - ATSUFontID fid; - char_u styleString[FONT_STYLE_BUFFER_SIZE]; - - if (!outName) - return; - - if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr) - { - // Canonicalize localized font names - if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family, - gFontPanelInfo.style, &fid, NULL) != noErr) - return; - - // Request font name with Mac encoding (otherwise we could - // get an unwanted utf-16 name) - if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform, - kFontNoScriptCode, kFontNoLanguageCode, - 255, (char *)outName, &fontNameLen, NULL) != noErr) - return; - - // Only encode font size, because style (bold, italic, etc) is - // already part of the font full name - vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d", - gFontPanelInfo.size/*, - ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""), - ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""), - ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/); - - if ((fontNameLen + STRLEN(styleString)) < 255) - STRCPY(outName + fontNameLen, styleString); - } - else - { - *outName = NUL; - } -} - - -/* - * ------------------------------------------------------------ - * Unfiled yet - * ------------------------------------------------------------ - */ - -/* - * gui_mac_get_menu_item_index - * - * Returns the index inside the menu where - */ - short // Should we return MenuItemIndex? -gui_mac_get_menu_item_index(vimmenu_T *pMenu) -{ - short index; - short itemIndex = -1; - vimmenu_T *pBrother; - - // Only menu without parent are the: - // -menu in the menubar - // -popup menu - // -toolbar (guess) - // - // Which are not items anyway. - if (pMenu->parent) - { - // Start from the Oldest Brother - pBrother = pMenu->parent->children; - index = 1; - while ((pBrother) && (itemIndex == -1)) - { - if (pBrother == pMenu) - itemIndex = index; - index++; - pBrother = pBrother->next; - } - } - return itemIndex; -} - - static vimmenu_T * -gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu) -{ - short index; - vimmenu_T *pChildMenu; - vimmenu_T *pElder = pMenu->parent; - - - // Only menu without parent are the: - // -menu in the menubar - // -popup menu - // -toolbar (guess) - // - // Which are not items anyway. - - if ((pElder) && (pElder->submenu_id == menuID)) - { - for (index = 1; (index != itemIndex) && (pMenu != NULL); index++) - pMenu = pMenu->next; - } - else - { - for (; pMenu != NULL; pMenu = pMenu->next) - { - if (pMenu->children != NULL) - { - pChildMenu = gui_mac_get_vim_menu - (menuID, itemIndex, pMenu->children); - if (pChildMenu) - { - pMenu = pChildMenu; - break; - } - } - } - } - return pMenu; -} - -/* - * ------------------------------------------------------------ - * MacOS Feedback procedures - * ------------------------------------------------------------ - */ - pascal - void -gui_mac_drag_thumb(ControlHandle theControl, short partCode) -{ - scrollbar_T *sb; - int value, dragging; - ControlHandle theControlToUse; - int dont_scroll_save = dont_scroll; - - theControlToUse = dragged_sb; - - sb = gui_find_scrollbar((long) GetControlReference(theControlToUse)); - - if (sb == NULL) - return; - - // Need to find value by diff between Old Poss New Pos - value = GetControl32BitValue(theControlToUse); - dragging = (partCode != 0); - - // When "allow_scrollbar" is FALSE still need to remember the new - // position, but don't actually scroll by setting "dont_scroll". - dont_scroll = !allow_scrollbar; - gui_drag_scrollbar(sb, value, dragging); - dont_scroll = dont_scroll_save; -} - - pascal - void -gui_mac_scroll_action(ControlHandle theControl, short partCode) -{ - // TODO: have live support - scrollbar_T *sb, *sb_info; - long data; - long value; - int page; - int dragging = FALSE; - int dont_scroll_save = dont_scroll; - - sb = gui_find_scrollbar((long)GetControlReference(theControl)); - - if (sb == NULL) - return; - - if (sb->wp != NULL) // Left or right scrollbar - { - /* - * Careful: need to get scrollbar info out of first (left) scrollbar - * for window, but keep real scrollbar too because we must pass it to - * gui_drag_scrollbar(). - */ - sb_info = &sb->wp->w_scrollbars[0]; - - if (sb_info->size > 5) - page = sb_info->size - 2; // use two lines of context - else - page = sb_info->size; - } - else // Bottom scrollbar - { - sb_info = sb; - page = curwin->w_width - 5; - } - - switch (partCode) - { - case kControlUpButtonPart: data = -1; break; - case kControlDownButtonPart: data = 1; break; - case kControlPageDownPart: data = page; break; - case kControlPageUpPart: data = -page; break; - default: data = 0; break; - } - - value = sb_info->value + data; -// if (value > sb_info->max) -// value = sb_info->max; -// else if (value < 0) -// value = 0; - - // When "allow_scrollbar" is FALSE still need to remember the new - // position, but don't actually scroll by setting "dont_scroll". - dont_scroll = !allow_scrollbar; - gui_drag_scrollbar(sb, value, dragging); - dont_scroll = dont_scroll_save; - - out_flush(); - gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max); - -#if 0 - if (sb_info->wp != NULL) - { - win_T *wp; - int sb_num; - - sb_num = 0; - for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp)) - sb_num++; - - if (wp != NULL) - { - current_scrollbar = sb_num; - scrollbar_value = value; - gui_do_scroll(); - gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max); - } - } -#endif -} - -/* - * ------------------------------------------------------------ - * MacOS Click Handling procedures - * ------------------------------------------------------------ - */ - - -/* - * Handle a click inside the window, it may happens in the - * scrollbar or the contents. - * - * TODO: Add support for potential TOOLBAR - */ - void -gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow) -{ - Point thePoint; - int_u vimModifiers; - short thePortion; - ControlHandle theControl; - int vimMouseButton; - short dblClick; - - thePoint = theEvent->where; - GlobalToLocal(&thePoint); - SelectWindow(whichWindow); - - thePortion = FindControl(thePoint, whichWindow, &theControl); - - if (theControl != NUL) - { - // We hit a scrollbar - - if (thePortion != kControlIndicatorPart) - { - dragged_sb = theControl; - TrackControl(theControl, thePoint, gScrollAction); - dragged_sb = NULL; - } - else - { - dragged_sb = theControl; -#if 1 - TrackControl(theControl, thePoint, gScrollDrag); -#else - TrackControl(theControl, thePoint, NULL); -#endif - // pass 0 as the part to tell gui_mac_drag_thumb, that the mouse - // button has been released - gui_mac_drag_thumb(theControl, 0); // Should it be thePortion ? (Dany) - dragged_sb = NULL; - } - } - else - { - // We are inside the contents - - // Convert the CTRL, OPTION, SHIFT and CMD key - vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers); - - // Defaults to MOUSE_LEFT as there's only one mouse button - vimMouseButton = MOUSE_LEFT; - - // Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT - // TODO: NEEDED? - clickIsPopup = FALSE; - - if (mouse_model_popup() && IsShowContextualMenuClick(theEvent)) - { - vimMouseButton = MOUSE_RIGHT; - vimModifiers &= ~MOUSE_CTRL; - clickIsPopup = TRUE; - } - - // Is it a double click ? - dblClick = ((theEvent->when - lastMouseTick) < GetDblTime()); - - // Send the mouse click to Vim - gui_send_mouse_event(vimMouseButton, thePoint.h, - thePoint.v, dblClick, vimModifiers); - - // Create the rectangle around the cursor to detect - // the mouse dragging -#if 0 - // TODO: Do we need to this even for the contextual menu? - // It may be require for popup_setpos, but for popup? - if (vimMouseButton == MOUSE_LEFT) -#endif - { - SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)), - FILL_Y(Y_2_ROW(thePoint.v)), - FILL_X(X_2_COL(thePoint.h)+1), - FILL_Y(Y_2_ROW(thePoint.v)+1)); - - dragRectEnbl = TRUE; - dragRectControl = kCreateRect; - } - } -} - -/* - * Handle the click in the titlebar (to move the window) - */ - void -gui_mac_doInDragClick(Point where, WindowPtr whichWindow) -{ - Rect movingLimits; - Rect *movingLimitsPtr = &movingLimits; - - // TODO: may try to prevent move outside screen? - movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits); - DragWindow(whichWindow, where, movingLimitsPtr); -} - -/* - * Handle the click in the grow box - */ - void -gui_mac_doInGrowClick(Point where, WindowPtr whichWindow) -{ - - long newSize; - unsigned short newWidth; - unsigned short newHeight; - Rect resizeLimits; - Rect *resizeLimitsPtr = &resizeLimits; - Rect NewContentRect; - - resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits); - - // Set the minimum size - // TODO: Should this come from Vim? - resizeLimits.top = 100; - resizeLimits.left = 100; - - newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect); - newWidth = NewContentRect.right - NewContentRect.left; - newHeight = NewContentRect.bottom - NewContentRect.top; - gui_resize_shell(newWidth, newHeight); - gui_mch_set_bg_color(gui.back_pixel); - gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH); -} - -/* - * Handle the click in the zoom box - */ - static void -gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow) -{ - Rect r; - Point p; - short thePart; - - // ideal width is current - p.h = Columns * gui.char_width + 2 * gui.border_offset; - if (gui.which_scrollbars[SBAR_LEFT]) - p.h += gui.scrollbar_width; - if (gui.which_scrollbars[SBAR_RIGHT]) - p.h += gui.scrollbar_width; - // ideal height is as high as we can get - p.v = 15 * 1024; - - thePart = IsWindowInStandardState(whichWindow, &p, &r) - ? inZoomIn : inZoomOut; - - if (!TrackBox(whichWindow, theEvent->where, thePart)) - return; - - // use returned width - p.h = r.right - r.left; - // adjust returned height - p.v = r.bottom - r.top - 2 * gui.border_offset; - if (gui.which_scrollbars[SBAR_BOTTOM]) - p.v -= gui.scrollbar_height; - p.v -= p.v % gui.char_height; - p.v += 2 * gui.border_width; - if (gui.which_scrollbars[SBAR_BOTTOM]) - p.v += gui.scrollbar_height; - - ZoomWindowIdeal(whichWindow, thePart, &p); - - GetWindowBounds(whichWindow, kWindowContentRgn, &r); - gui_resize_shell(r.right - r.left, r.bottom - r.top); - gui_mch_set_bg_color(gui.back_pixel); - gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH); -} - -/* - * ------------------------------------------------------------ - * MacOS Event Handling procedure - * ------------------------------------------------------------ - */ - -/* - * Handle the Update Event - */ - - void -gui_mac_doUpdateEvent(EventRecord *event) -{ - WindowPtr whichWindow; - GrafPtr savePort; - RgnHandle updateRgn; - Rect updateRect; - Rect *updateRectPtr; - Rect rc; - Rect growRect; - RgnHandle saveRgn; - - - updateRgn = NewRgn(); - if (updateRgn == NULL) - return; - - // This could be done by the caller as we - // don't require anything else out of the event - whichWindow = (WindowPtr) event->message; - - // Save Current Port - GetPort(&savePort); - - // Select the Window's Port - SetPortWindowPort(whichWindow); - - // Let's update the window - BeginUpdate(whichWindow); - // Redraw the biggest rectangle covering the area - // to be updated. - GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn); -# if 0 - // Would be more appropriate to use the following but doesn't - // seem to work under MacOS X (Dany) - GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn); -# endif - - // Use the HLock useless in Carbon? Is it harmful? - HLock((Handle) updateRgn); - - updateRectPtr = GetRegionBounds(updateRgn, &updateRect); -# if 0 - // Code from original Carbon Port (using GetWindowRegion. - // I believe the UpdateRgn is already in local (Dany) - GlobalToLocal(&topLeft(updateRect)); // preCarbon? - GlobalToLocal(&botRight(updateRect)); -# endif - // Update the content (i.e. the text) - gui_redraw(updateRectPtr->left, updateRectPtr->top, - updateRectPtr->right - updateRectPtr->left, - updateRectPtr->bottom - updateRectPtr->top); - // Clear the border areas if needed - gui_mch_set_bg_color(gui.back_pixel); - if (updateRectPtr->left < FILL_X(0)) - { - SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows)); - EraseRect(&rc); - } - if (updateRectPtr->top < FILL_Y(0)) - { - SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0)); - EraseRect(&rc); - } - if (updateRectPtr->right > FILL_X(Columns)) - { - SetRect(&rc, FILL_X(Columns), 0, - FILL_X(Columns) + gui.border_offset, FILL_Y(Rows)); - EraseRect(&rc); - } - if (updateRectPtr->bottom > FILL_Y(Rows)) - { - SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset, - FILL_Y(Rows) + gui.border_offset); - EraseRect(&rc); - } - HUnlock((Handle) updateRgn); - DisposeRgn(updateRgn); - - // Update scrollbars - DrawControls(whichWindow); - - // Update the GrowBox - // Taken from FAQ 33-27 - saveRgn = NewRgn(); - GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect); - GetClip(saveRgn); - ClipRect(&growRect); - DrawGrowIcon(whichWindow); - SetClip(saveRgn); - DisposeRgn(saveRgn); - EndUpdate(whichWindow); - - // Restore original Port - SetPort(savePort); -} - -/* - * Handle the activate/deactivate event - * (apply to a window) - */ - void -gui_mac_doActivateEvent(EventRecord *event) -{ - WindowPtr whichWindow; - - whichWindow = (WindowPtr) event->message; - // Dim scrollbars - if (whichWindow == gui.VimWindow) - { - ControlRef rootControl; - GetRootControl(gui.VimWindow, &rootControl); - if ((event->modifiers) & activeFlag) - ActivateControl(rootControl); - else - DeactivateControl(rootControl); - } - - // Activate - gui_focus_change((event->modifiers) & activeFlag); -} - - -/* - * Handle the suspend/resume event - * (apply to the application) - */ - void -gui_mac_doSuspendEvent(EventRecord *event) -{ - // The frontmost application just changed - - // NOTE: the suspend may happen before the deactivate - // seen on MacOS X - - // May not need to change focus as the window will - // get an activate/deactivate event - if (event->message & 1) - // Resume - gui_focus_change(TRUE); - else - // Suspend - gui_focus_change(FALSE); -} - -/* - * Handle the key - */ -#ifdef USE_CARBONKEYHANDLER - static pascal OSStatus -gui_mac_handle_window_activate( - EventHandlerCallRef nextHandler, - EventRef theEvent, - void *data) -{ - UInt32 eventClass = GetEventClass(theEvent); - UInt32 eventKind = GetEventKind(theEvent); - - if (eventClass == kEventClassWindow) - { - switch (eventKind) - { - case kEventWindowActivated: - im_on_window_switch(TRUE); - return noErr; - - case kEventWindowDeactivated: - im_on_window_switch(FALSE); - return noErr; - } - } - - return eventNotHandledErr; -} - - static pascal OSStatus -gui_mac_handle_text_input( - EventHandlerCallRef nextHandler, - EventRef theEvent, - void *data) -{ - UInt32 eventClass = GetEventClass(theEvent); - UInt32 eventKind = GetEventKind(theEvent); - - if (eventClass != kEventClassTextInput) - return eventNotHandledErr; - - if ((kEventTextInputUpdateActiveInputArea != eventKind) && - (kEventTextInputUnicodeForKeyEvent != eventKind) && - (kEventTextInputOffsetToPos != eventKind) && - (kEventTextInputPosToOffset != eventKind) && - (kEventTextInputGetSelectedText != eventKind)) - return eventNotHandledErr; - - switch (eventKind) - { - case kEventTextInputUpdateActiveInputArea: - return gui_mac_update_input_area(nextHandler, theEvent); - case kEventTextInputUnicodeForKeyEvent: - return gui_mac_unicode_key_event(nextHandler, theEvent); - - case kEventTextInputOffsetToPos: - case kEventTextInputPosToOffset: - case kEventTextInputGetSelectedText: - break; - } - - return eventNotHandledErr; -} - - static pascal -OSStatus gui_mac_update_input_area( - EventHandlerCallRef nextHandler, - EventRef theEvent) -{ - return eventNotHandledErr; -} - -static int dialog_busy = FALSE; // TRUE when gui_mch_dialog() wants the - // keys - -# define INLINE_KEY_BUFFER_SIZE 80 - static pascal OSStatus -gui_mac_unicode_key_event( - EventHandlerCallRef nextHandler, - EventRef theEvent) -{ - // Multibyte-friendly key event handler - OSStatus err = -1; - UInt32 actualSize; - UniChar *text; - char_u result[INLINE_KEY_BUFFER_SIZE]; - short len = 0; - UInt32 key_sym; - char charcode; - int key_char; - UInt32 modifiers, vimModifiers; - size_t encLen; - char_u *to = NULL; - Boolean isSpecial = FALSE; - int i; - EventRef keyEvent; - - // Mask the mouse (as per user setting) - if (p_mh) - ObscureCursor(); - - // Don't use the keys when the dialog wants them. - if (dialog_busy) - return eventNotHandledErr; - - if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText, - typeUnicodeText, NULL, 0, &actualSize, NULL)) - return eventNotHandledErr; - - text = alloc(actualSize); - if (!text) - return eventNotHandledErr; - - err = GetEventParameter(theEvent, kEventParamTextInputSendText, - typeUnicodeText, NULL, actualSize, NULL, text); - require_noerr(err, done); - - err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent, - typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent); - require_noerr(err, done); - - err = GetEventParameter(keyEvent, kEventParamKeyModifiers, - typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers); - require_noerr(err, done); - - err = GetEventParameter(keyEvent, kEventParamKeyCode, - typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym); - require_noerr(err, done); - - err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes, - typeChar, NULL, sizeof(char), NULL, &charcode); - require_noerr(err, done); - -#ifndef USE_CMD_KEY - if (modifiers & cmdKey) - goto done; // Let system handle Cmd+... -#endif - - key_char = charcode; - vimModifiers = EventModifiers2VimModifiers(modifiers); - - // Find the special key (eg., for cursor keys) - if (actualSize <= sizeof(UniChar) && - ((text[0] < 0x20) || (text[0] == 0x7f))) - { - for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i) - if (special_keys[i].key_sym == key_sym) - { - key_char = TO_SPECIAL(special_keys[i].vim_code0, - special_keys[i].vim_code1); - key_char = simplify_key(key_char, - (int *)&vimModifiers); - isSpecial = TRUE; - break; - } - } - - // Intercept CMD-. and CTRL-c - if (((modifiers & controlKey) && key_char == 'c') || - ((modifiers & cmdKey) && key_char == '.')) - got_int = TRUE; - - if (!isSpecial) - { - // remove SHIFT for keys that are already shifted, e.g., - // '(' and '*' - if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char)) - vimModifiers &= ~MOD_MASK_SHIFT; - - // remove CTRL from keys that already have it - if (key_char < 0x20) - vimModifiers &= ~MOD_MASK_CTRL; - - // don't process unicode characters here - if (!IS_SPECIAL(key_char)) - { - // Following code to simplify and consolidate vimModifiers - // taken liberally from gui_w48.c - key_char = simplify_key(key_char, (int *)&vimModifiers); - - // Unify modifiers somewhat. No longer use ALT to set the 8th bit. - key_char = extract_modifiers(key_char, (int *)&vimModifiers, - FALSE, NULL); - if (key_char == CSI) - key_char = K_CSI; - - if (IS_SPECIAL(key_char)) - isSpecial = TRUE; - } - } - - if (vimModifiers) - { - result[len++] = CSI; - result[len++] = KS_MODIFIER; - result[len++] = vimModifiers; - } - - if (isSpecial && IS_SPECIAL(key_char)) - { - result[len++] = CSI; - result[len++] = K_SECOND(key_char); - result[len++] = K_THIRD(key_char); - } - else - { - encLen = actualSize; - to = mac_utf16_to_enc(text, actualSize, &encLen); - if (to) - { - // This is basically add_to_input_buf_csi() - for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i) - { - result[len++] = to[i]; - if (to[i] == CSI) - { - result[len++] = KS_EXTRA; - result[len++] = (int)KE_CSI; - } - } - vim_free(to); - } - } - - add_to_input_buf(result, len); - err = noErr; - -done: - vim_free(text); - if (err == noErr) - { - // Fake event to wake up WNE (required to get - // key repeat working - PostEvent(keyUp, 0); - return noErr; - } - - return eventNotHandledErr; -} -#else - void -gui_mac_doKeyEvent(EventRecord *theEvent) -{ - // TODO: add support for COMMAND KEY - long menu; - unsigned char string[20]; - short num, i; - short len = 0; - KeySym key_sym; - int key_char; - int modifiers; - int simplify = FALSE; - - // Mask the mouse (as per user setting) - if (p_mh) - ObscureCursor(); - - // Get the key code and its ASCII representation - key_sym = ((theEvent->message & keyCodeMask) >> 8); - key_char = theEvent->message & charCodeMask; - num = 1; - - // Intercept CTRL-C - if (theEvent->modifiers & controlKey) - { - if (key_char == Ctrl_C && ctrl_c_interrupts) - got_int = TRUE; - else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0 - && (key_char == '2' || key_char == '6')) - { - // CTRL-^ and CTRL-@ don't work in the normal way. - if (key_char == '2') - key_char = Ctrl_AT; - else - key_char = Ctrl_HAT; - theEvent->modifiers = 0; - } - } - - // Intercept CMD-. - if (theEvent->modifiers & cmdKey) - if (key_char == '.') - got_int = TRUE; - - // Handle command key as per menu - // TODO: should override be allowed? Require YAO or could use 'winaltkey' - if (theEvent->modifiers & cmdKey) - // Only accept CMD alone or with CAPLOCKS and the mouse button. - // Why the mouse button? - if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0) - { - menu = MenuKey(key_char); - if (HiWord(menu)) - { - gui_mac_handle_menu(menu); - return; - } - } - - // Convert the modifiers - modifiers = EventModifiers2VimModifiers(theEvent->modifiers); - - - // Handle special keys. -#if 0 - // Why has this been removed? - if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey))) -#endif - { - // Find the special key (for non-printable keyt_char) - if ((key_char < 0x20) || (key_char == 0x7f)) - for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) - if (special_keys[i].key_sym == key_sym) - { -# if 0 - // We currently don't have not so special key - if (special_keys[i].vim_code1 == NUL) - key_char = special_keys[i].vim_code0; - else -# endif - key_char = TO_SPECIAL(special_keys[i].vim_code0, - special_keys[i].vim_code1); - simplify = TRUE; - break; - } - } - - // For some keys the modifier is included in the char itself. - if (simplify || key_char == TAB || key_char == ' ') - key_char = simplify_key(key_char, &modifiers); - - // Add the modifier to the input bu if needed - // Do not want SHIFT-A or CTRL-A with modifier - if (!IS_SPECIAL(key_char) - && key_sym != vk_Space - && key_sym != vk_Tab - && key_sym != vk_Return - && key_sym != vk_Enter - && key_sym != vk_Esc) - { -#if 1 - // Clear modifiers when only one modifier is set - if ((modifiers == MOD_MASK_SHIFT) - || (modifiers == MOD_MASK_CTRL) - || (modifiers == MOD_MASK_ALT)) - modifiers = 0; -#else - if (modifiers & MOD_MASK_CTRL) - modifiers = modifiers & ~MOD_MASK_CTRL; - if (modifiers & MOD_MASK_ALT) - modifiers = modifiers & ~MOD_MASK_ALT; - if (modifiers & MOD_MASK_SHIFT) - modifiers = modifiers & ~MOD_MASK_SHIFT; -#endif - } - if (modifiers) - { - string[len++] = CSI; - string[len++] = KS_MODIFIER; - string[len++] = modifiers; - } - - if (IS_SPECIAL(key_char)) - { - string[len++] = CSI; - string[len++] = K_SECOND(key_char); - string[len++] = K_THIRD(key_char); - } - else - { - // Convert characters when needed (e.g., from MacRoman to latin1). - // This doesn't work for the NUL byte. - if (input_conv.vc_type != CONV_NONE && key_char > 0) - { - char_u from[2], *to; - int l; - - from[0] = key_char; - from[1] = NUL; - l = 1; - to = string_convert(&input_conv, from, &l); - if (to != NULL) - { - for (i = 0; i < l && len < 19; i++) - { - if (to[i] == CSI) - { - string[len++] = KS_EXTRA; - string[len++] = KE_CSI; - } - else - string[len++] = to[i]; - } - vim_free(to); - } - else - string[len++] = key_char; - } - else - string[len++] = key_char; - } - - if (len == 1 && string[0] == CSI) - { - // Turn CSI into K_CSI. - string[ len++ ] = KS_EXTRA; - string[ len++ ] = KE_CSI; - } - - add_to_input_buf(string, len); -} -#endif - -/* - * Handle MouseClick - */ - void -gui_mac_doMouseDownEvent(EventRecord *theEvent) -{ - short thePart; - WindowPtr whichWindow; - - thePart = FindWindow(theEvent->where, &whichWindow); - -#ifdef FEAT_GUI_TABLINE - // prevent that the vim window size changes if it's activated by a - // click into the tab pane - if (whichWindow == drawer) - return; -#endif - - switch (thePart) - { - case (inDesk): - // TODO: what to do? - break; - - case (inMenuBar): - gui_mac_handle_menu(MenuSelect(theEvent->where)); - break; - - case (inContent): - gui_mac_doInContentClick(theEvent, whichWindow); - break; - - case (inDrag): - gui_mac_doInDragClick(theEvent->where, whichWindow); - break; - - case (inGrow): - gui_mac_doInGrowClick(theEvent->where, whichWindow); - break; - - case (inGoAway): - if (TrackGoAway(whichWindow, theEvent->where)) - gui_shell_closed(); - break; - - case (inZoomIn): - case (inZoomOut): - gui_mac_doInZoomClick(theEvent, whichWindow); - break; - } -} - -/* - * Handle MouseMoved - * [this event is a moving in and out of a region] - */ - void -gui_mac_doMouseMovedEvent(EventRecord *event) -{ - Point thePoint; - int_u vimModifiers; - - thePoint = event->where; - GlobalToLocal(&thePoint); - vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers); - - if (!Button()) - gui_mouse_moved(thePoint.h, thePoint.v); - else - if (!clickIsPopup) - gui_send_mouse_event(MOUSE_DRAG, thePoint.h, - thePoint.v, FALSE, vimModifiers); - - // Reset the region from which we move in and out - SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)), - FILL_Y(Y_2_ROW(thePoint.v)), - FILL_X(X_2_COL(thePoint.h)+1), - FILL_Y(Y_2_ROW(thePoint.v)+1)); - - if (dragRectEnbl) - dragRectControl = kCreateRect; - -} - -/* - * Handle the mouse release - */ - void -gui_mac_doMouseUpEvent(EventRecord *theEvent) -{ - Point thePoint; - int_u vimModifiers; - - // TODO: Properly convert the Contextual menu mouse-up - // Potential source of the double menu - lastMouseTick = theEvent->when; - dragRectEnbl = FALSE; - dragRectControl = kCreateEmpty; - thePoint = theEvent->where; - GlobalToLocal(&thePoint); - - vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers); - if (clickIsPopup) - { - vimModifiers &= ~MOUSE_CTRL; - clickIsPopup = FALSE; - } - gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers); -} - - static pascal OSStatus -gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent, - void *data) -{ - Point point; - Rect bounds; - UInt32 mod; - SInt32 delta; - int_u vim_mod; - EventMouseWheelAxis axis; - - if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis, - typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis) - && axis != kEventMouseWheelAxisY) - goto bail; // Vim only does up-down scrolling - - if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta, - typeSInt32, NULL, sizeof(SInt32), NULL, &delta)) - goto bail; - if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation, - typeQDPoint, NULL, sizeof(Point), NULL, &point)) - goto bail; - if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers, - typeUInt32, NULL, sizeof(UInt32), NULL, &mod)) - goto bail; - - vim_mod = 0; - if (mod & shiftKey) - vim_mod |= MOUSE_SHIFT; - if (mod & controlKey) - vim_mod |= MOUSE_CTRL; - if (mod & optionKey) - vim_mod |= MOUSE_ALT; - - if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds)) - { - point.h -= bounds.left; - point.v -= bounds.top; - } - - gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5, - point.h, point.v, FALSE, vim_mod); - - // post a bogus event to wake up WaitNextEvent - PostEvent(keyUp, 0); - - return noErr; - -bail: - /* - * when we fail give any additional callback handler a chance to perform - * its actions - */ - return CallNextEventHandler(nextHandler, theEvent); -} - - void -gui_mch_mousehide(int hide) -{ - // TODO -} - -#if 0 - -/* - * This would be the normal way of invoking the contextual menu - * but the Vim API doesn't seem to a support a request to get - * the menu that we should display - */ - void -gui_mac_handle_contextual_menu(EventRecord *event) -{ -/* - * Clone PopUp to use menu - * Create a object descriptor for the current selection - * Call the procedure - */ - -// Call to Handle Popup - OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem); - - if (status != noErr) - return; - - if (CntxType == kCMMenuItemSelected) - { - // Handle the menu CntxMenuID, CntxMenuItem - // The submenu can be handle directly by gui_mac_handle_menu - // But what about the current menu, is the many changed by ContextualMenuSelect - gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem); - } - else if (CntxMenuID == kCMShowHelpSelected) - { - // Should come up with the help - } - -} -#endif - -/* - * Handle menubar selection - */ - void -gui_mac_handle_menu(long menuChoice) -{ - short menu = HiWord(menuChoice); - short item = LoWord(menuChoice); - vimmenu_T *theVimMenu = root_menu; - - if (menu == 256) // TODO: use constant or gui.xyz - { - if (item == 1) - gui_mch_beep(); // TODO: Popup dialog or do :intro - } - else if (item != 0) - { - theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu); - - if (theVimMenu) - gui_menu_cb(theVimMenu); - } - HiliteMenu(0); -} - -/* - * Dispatch the event to proper handler - */ - - void -gui_mac_handle_event(EventRecord *event) -{ - OSErr error; - - // Handle contextual menu right now (if needed) - if (IsShowContextualMenuClick(event)) - { -# if 0 - gui_mac_handle_contextual_menu(event); -# else - gui_mac_doMouseDownEvent(event); -# endif - return; - } - - // Handle normal event - switch (event->what) - { -#ifndef USE_CARBONKEYHANDLER - case (keyDown): - case (autoKey): - gui_mac_doKeyEvent(event); - break; -#endif - case (keyUp): - // We don't care about when the key is released - break; - - case (mouseDown): - gui_mac_doMouseDownEvent(event); - break; - - case (mouseUp): - gui_mac_doMouseUpEvent(event); - break; - - case (updateEvt): - gui_mac_doUpdateEvent(event); - break; - - case (diskEvt): - // We don't need special handling for disk insertion - break; - - case (activateEvt): - gui_mac_doActivateEvent(event); - break; - - case (osEvt): - switch ((event->message >> 24) & 0xFF) - { - case (0xFA): // mouseMovedMessage - gui_mac_doMouseMovedEvent(event); - break; - case (0x01): // suspendResumeMessage - gui_mac_doSuspendEvent(event); - break; - } - break; - -#ifdef USE_AEVENT - case (kHighLevelEvent): - // Someone's talking to us, through AppleEvents - error = AEProcessAppleEvent(event); // TODO: Error Handling - break; -#endif - } -} - -/* - * ------------------------------------------------------------ - * Unknown Stuff - * ------------------------------------------------------------ - */ - - - GuiFont -gui_mac_find_font(char_u *font_name) -{ - char_u c; - char_u *p; - char_u pFontName[256]; - Str255 systemFontname; - short font_id; - short size=9; - GuiFont font; -#if 0 - char_u *fontNamePtr; -#endif - - for (p = font_name; ((*p != 0) && (*p != ':')); p++) - ; - - c = *p; - *p = 0; - -#if 1 - STRCPY(&pFontName[1], font_name); - pFontName[0] = STRLEN(font_name); - *p = c; - - // Get the font name, minus the style suffix (:h, etc) - char_u fontName[256]; - char_u *styleStart = vim_strchr(font_name, ':'); - size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName); - vim_strncpy(fontName, font_name, fontNameLen); - - ATSUFontID fontRef; - FMFontStyle fontStyle; - font_id = 0; - - if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName, - kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode, - &fontRef) == noErr) - { - if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr) - font_id = 0; - } - - if (font_id == 0) - { - /* - * Try again, this time replacing underscores in the font name - * with spaces (:set guifont allows the two to be used - * interchangeably; the Font Manager doesn't). - */ - int i, changed = FALSE; - - for (i = pFontName[0]; i > 0; --i) - { - if (pFontName[i] == '_') - { - pFontName[i] = ' '; - changed = TRUE; - } - } - if (changed) - if (ATSUFindFontFromName(&pFontName[1], pFontName[0], - kFontFullName, kFontNoPlatformCode, kFontNoScriptCode, - kFontNoLanguageCode, &fontRef) == noErr) - { - if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr) - font_id = 0; - } - } - -#else - // name = C2Pascal_save(menu->dname); - fontNamePtr = C2Pascal_save_and_remove_backslash(font_name); - - GetFNum(fontNamePtr, &font_id); -#endif - - - if (font_id == 0) - { - // Oups, the system font was it the one the user want - - if (FMGetFontFamilyName(systemFont, systemFontname) != noErr) - return NOFONT; - if (!EqualString(pFontName, systemFontname, false, false)) - return NOFONT; - } - if (*p == ':') - { - p++; - // Set the values found after ':' - while (*p) - { - switch (*p++) - { - case 'h': - size = points_to_pixels(p, &p, TRUE); - break; - /* - * TODO: Maybe accept width and styles - */ - } - while (*p == ':') - p++; - } - } - - if (size < 1) - size = 1; // Avoid having a size of 0 with system font - - font = (size << 16) + ((long) font_id & 0xFFFF); - - return font; -} - -/* - * ------------------------------------------------------------ - * GUI_MCH functionality - * ------------------------------------------------------------ - */ - -/* - * Parse the GUI related command-line arguments. Any arguments used are - * deleted from argv, and *argc is decremented accordingly. This is called - * when vim is started, whether or not the GUI has been started. - */ - void -gui_mch_prepare(int *argc, char **argv) -{ - // TODO: Move most of this stuff toward gui_mch_init -#ifdef USE_EXE_NAME - FSSpec applDir; -# ifndef USE_FIND_BUNDLE_PATH - short applVRefNum; - long applDirID; - Str255 volName; -# else - ProcessSerialNumber psn; - FSRef applFSRef; -# endif -#endif - -#if 0 - InitCursor(); - - RegisterAppearanceClient(); - -#ifdef USE_AEVENT - (void) InstallAEHandlers(); -#endif - - pomme = NewMenu(256, "\p\024"); // 0x14= = Apple Menu - - AppendMenu(pomme, "\pAbout VIM"); - - InsertMenu(pomme, 0); - - DrawMenuBar(); - - -#ifndef USE_OFFSETED_WINDOW - SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11); -#else - SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11); -#endif - - - CreateNewWindow(kDocumentWindowClass, - kWindowResizableAttribute | kWindowCollapseBoxAttribute, - &windRect, &gui.VimWindow); - SetPortWindowPort(gui.VimWindow); - - gui.char_width = 7; - gui.char_height = 11; - gui.char_ascent = 6; - gui.num_rows = 24; - gui.num_cols = 80; - gui.in_focus = TRUE; // For the moment -> syn. of front application - - gScrollAction = NewControlActionUPP(gui_mac_scroll_action); - gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb); - - dragRectEnbl = FALSE; - dragRgn = NULL; - dragRectControl = kCreateEmpty; - cursorRgn = NewRgn(); -#endif -#ifdef USE_EXE_NAME -# ifndef USE_FIND_BUNDLE_PATH - HGetVol(volName, &applVRefNum, &applDirID); - // TN2015: mention a possible bad VRefNum - FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir); -# else - // OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr) - // of TN2015 - (void)GetCurrentProcess(&psn); - // if (err != noErr) return err; - - (void)GetProcessBundleLocation(&psn, &applFSRef); - // if (err != noErr) return err; - - (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL); - - // This technique returns NIL when we disallow_gui -# endif - exe_name = FullPathFromFSSpec_save(applDir); -#endif -} - -#ifndef ALWAYS_USE_GUI -/* - * Check if the GUI can be started. Called before gvimrc is sourced. - * Return OK or FAIL. - */ - int -gui_mch_init_check(void) -{ - // TODO: For MacOS X find a way to return FAIL, if the user logged in - // using the >console - if (disallow_gui) // see main.c for reason to disallow - return FAIL; - return OK; -} -#endif - - static OSErr -receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag) -{ - int x, y; - int_u modifiers; - char_u **fnames = NULL; - int count; - int i, j; - - // Get drop position, modifiers and count of items - { - Point point; - SInt16 mouseUpModifiers; - UInt16 countItem; - - GetDragMouse(theDrag, &point, NULL); - GlobalToLocal(&point); - x = point.h; - y = point.v; - GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers); - modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers); - CountDragItems(theDrag, &countItem); - count = countItem; - } - - fnames = ALLOC_MULT(char_u *, count); - if (fnames == NULL) - return dragNotAcceptedErr; - - // Get file names dropped - for (i = j = 0; i < count; ++i) - { - DragItemRef item; - OSErr err; - Size size; - FlavorType type = flavorTypeHFS; - HFSFlavor hfsFlavor; - - fnames[i] = NULL; - GetDragItemReferenceNumber(theDrag, i + 1, &item); - err = GetFlavorDataSize(theDrag, item, type, &size); - if (err != noErr || size > sizeof(hfsFlavor)) - continue; - err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0); - if (err != noErr) - continue; - fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec); - } - count = j; - - gui_handle_drop(x, y, modifiers, fnames, count); - - // Fake mouse event to wake from stall - PostEvent(mouseUp, 0); - - return noErr; -} - -/* - * Initialise the GUI. Create all the windows, set up all the call-backs - * etc. - */ - int -gui_mch_init(void) -{ - // TODO: Move most of this stuff toward gui_mch_init - Rect windRect; - MenuHandle pomme; - EventHandlerRef mouseWheelHandlerRef; - EventTypeSpec eventTypeSpec; - ControlRef rootControl; - - if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr) - gMacSystemVersion = 0x1000; // TODO: Default to minimum sensible value - -#if 1 - InitCursor(); - - RegisterAppearanceClient(); - -#ifdef USE_AEVENT - (void) InstallAEHandlers(); -#endif - - pomme = NewMenu(256, "\p\024"); // 0x14= = Apple Menu - - AppendMenu(pomme, "\pAbout VIM"); - - InsertMenu(pomme, 0); - - DrawMenuBar(); - - -#ifndef USE_OFFSETED_WINDOW - SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11); -#else - SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11); -#endif - - gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true, - zoomDocProc, - (WindowPtr)-1L, true, 0); - CreateRootControl(gui.VimWindow, &rootControl); - InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler, - gui.VimWindow, NULL); - SetPortWindowPort(gui.VimWindow); - - gui.char_width = 7; - gui.char_height = 11; - gui.char_ascent = 6; - gui.num_rows = 24; - gui.num_cols = 80; - gui.in_focus = TRUE; // For the moment -> syn. of front application - - gScrollAction = NewControlActionUPP(gui_mac_scroll_action); - gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb); - - // Install Carbon event callbacks. - (void)InstallFontPanelHandler(); - - dragRectEnbl = FALSE; - dragRgn = NULL; - dragRectControl = kCreateEmpty; - cursorRgn = NewRgn(); -#endif - // Display any pending error messages - display_errors(); - - // Get background/foreground colors from system - // TODO: do the appropriate call to get real defaults - gui.norm_pixel = 0x00000000; - gui.back_pixel = 0x00FFFFFF; - - // Get the colors from the "Normal" group (set in syntax.c or in a vimrc - // file). - set_normal_colors(); - - /* - * Check that none of the colors are the same as the background color. - * Then store the current values as the defaults. - */ - gui_check_colors(); - gui.def_norm_pixel = gui.norm_pixel; - gui.def_back_pixel = gui.back_pixel; - - // Get the colors for the highlight groups (gui_check_colors() might have - // changed them) - highlight_gui_started(); - - /* - * Setting the gui constants - */ -#ifdef FEAT_MENU - gui.menu_height = 0; -#endif - gui.scrollbar_height = gui.scrollbar_width = 15; // cheat 1 overlap - gui.border_offset = gui.border_width = 2; - - // If Quartz-style text anti aliasing is available (see - // gui_mch_draw_string() below), enable it for all font sizes. - vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1"); - - eventTypeSpec.eventClass = kEventClassMouse; - eventTypeSpec.eventKind = kEventMouseWheelMoved; - mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel); - if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1, - &eventTypeSpec, NULL, &mouseWheelHandlerRef)) - { - mouseWheelHandlerRef = NULL; - DisposeEventHandlerUPP(mouseWheelHandlerUPP); - mouseWheelHandlerUPP = NULL; - } - -#ifdef USE_CARBONKEYHANDLER - InterfaceTypeList supportedServices = { kUnicodeDocument }; - NewTSMDocument(1, supportedServices, &gTSMDocument, 0); - - // We don't support inline input yet, use input window by default - UseInputWindow(gTSMDocument, TRUE); - - // Should we activate the document by default? - // ActivateTSMDocument(gTSMDocument); - - EventTypeSpec textEventTypes[] = { - { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, - { kEventClassTextInput, kEventTextInputPosToOffset }, - { kEventClassTextInput, kEventTextInputOffsetToPos }, - }; - - keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input); - if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP, - NR_ELEMS(textEventTypes), - textEventTypes, NULL, NULL)) - { - DisposeEventHandlerUPP(keyEventHandlerUPP); - keyEventHandlerUPP = NULL; - } - - EventTypeSpec windowEventTypes[] = { - { kEventClassWindow, kEventWindowActivated }, - { kEventClassWindow, kEventWindowDeactivated }, - }; - - // Install window event handler to support TSMDocument activate and - // deactivate - winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate); - if (noErr != InstallWindowEventHandler(gui.VimWindow, - winEventHandlerUPP, - NR_ELEMS(windowEventTypes), - windowEventTypes, NULL, NULL)) - { - DisposeEventHandlerUPP(winEventHandlerUPP); - winEventHandlerUPP = NULL; - } -#endif - -#ifdef FEAT_GUI_TABLINE - /* - * Create the tabline - */ - initialise_tabline(); -#endif - - // TODO: Load bitmap if using TOOLBAR - return OK; -} - -/* - * Called when the foreground or background color has been changed. - */ - void -gui_mch_new_colors(void) -{ - // TODO: - // This proc is called when Normal is set to a value - // so what must be done? I don't know -} - -/* - * Open the GUI window which was created by a call to gui_mch_init(). - */ - int -gui_mch_open(void) -{ - ShowWindow(gui.VimWindow); - - if (gui_win_x != -1 && gui_win_y != -1) - gui_mch_set_winpos(gui_win_x, gui_win_y); - - /* - * Make the GUI the foreground process (in case it was launched - * from the Terminal or via :gui). - */ - { - ProcessSerialNumber psn; - if (GetCurrentProcess(&psn) == noErr) - SetFrontProcess(&psn); - } - - return OK; -} - -#ifdef USE_ATSUI_DRAWING - static void -gui_mac_dispose_atsui_style(void) -{ - if (p_macatsui && gFontStyle) - ATSUDisposeStyle(gFontStyle); - if (p_macatsui && gWideFontStyle) - ATSUDisposeStyle(gWideFontStyle); -} -#endif - - void -gui_mch_exit(int rc) -{ - // TODO: find out all what is missing here? - DisposeRgn(cursorRgn); - -#ifdef USE_CARBONKEYHANDLER - if (keyEventHandlerUPP) - DisposeEventHandlerUPP(keyEventHandlerUPP); -#endif - - if (mouseWheelHandlerUPP != NULL) - DisposeEventHandlerUPP(mouseWheelHandlerUPP); - -#ifdef USE_ATSUI_DRAWING - gui_mac_dispose_atsui_style(); -#endif - -#ifdef USE_CARBONKEYHANDLER - FixTSMDocument(gTSMDocument); - DeactivateTSMDocument(gTSMDocument); - DeleteTSMDocument(gTSMDocument); -#endif - - // Exit to shell? - exit(rc); -} - -/* - * Get the position of the top left corner of the window. - */ - int -gui_mch_get_winpos(int *x, int *y) -{ - // TODO - Rect bounds; - OSStatus status; - - // Carbon >= 1.0.2, MacOS >= 8.5 - status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds); - - if (status != noErr) - return FAIL; - *x = bounds.left; - *y = bounds.top; - return OK; -} - -/* - * Set the position of the top left corner of the window to the given - * coordinates. - */ - void -gui_mch_set_winpos(int x, int y) -{ - // TODO: Should make sure the window is move within range - // e.g.: y > ~16 [Menu bar], x > 0, x < screen width - MoveWindowStructure(gui.VimWindow, x, y); -} - - void -gui_mch_set_shellsize( - int width, - int height, - int min_width, - int min_height, - int base_width, - int base_height, - int direction) -{ - CGrafPtr VimPort; - Rect VimBound; - - if (gui.which_scrollbars[SBAR_LEFT]) - { - VimPort = GetWindowPort(gui.VimWindow); - GetPortBounds(VimPort, &VimBound); - VimBound.left = -gui.scrollbar_width; // + 1; - SetPortBounds(VimPort, &VimBound); - // GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ?? - } - else - { - VimPort = GetWindowPort(gui.VimWindow); - GetPortBounds(VimPort, &VimBound); - VimBound.left = 0; - SetPortBounds(VimPort, &VimBound); - } - - SizeWindow(gui.VimWindow, width, height, TRUE); - - gui_resize_shell(width, height); -} - -/* - * Get the screen dimensions. - * Allow 10 pixels for horizontal borders, 40 for vertical borders. - * Is there no way to find out how wide the borders really are? - * TODO: Add live update of those value on suspend/resume. - */ - void -gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) -{ - GDHandle dominantDevice = GetMainDevice(); - Rect screenRect = (**dominantDevice).gdRect; - - *screen_w = screenRect.right - 10; - *screen_h = screenRect.bottom - 40; -} - - -/* - * Open the Font Panel and wait for the user to select a font and - * close the panel. Then fill the buffer pointed to by font_name with - * the name and size of the selected font and return the font's handle, - * or NOFONT in case of an error. - */ - static GuiFont -gui_mac_select_font(char_u *font_name) -{ - GuiFont selected_font = NOFONT; - OSStatus status; - FontSelectionQDStyle curr_font; - - // Initialize the Font Panel with the current font. - curr_font.instance.fontFamily = gui.norm_font & 0xFFFF; - curr_font.size = (gui.norm_font >> 16); - // TODO: set fontStyle once styles are supported in gui_mac_find_font() - curr_font.instance.fontStyle = 0; - curr_font.hasColor = false; - curr_font.version = 0; // version number of the style structure - status = SetFontInfoForSelection(kFontSelectionQDType, - /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL); - - gFontPanelInfo.family = curr_font.instance.fontFamily; - gFontPanelInfo.style = curr_font.instance.fontStyle; - gFontPanelInfo.size = curr_font.size; - - // Pop up the Font Panel. - status = FPShowHideFontPanel(); - if (status == noErr) - { - /* - * The Font Panel is modeless. We really need it to be modal, - * so we spin in an event loop until the panel is closed. - */ - gFontPanelInfo.isPanelVisible = true; - while (gFontPanelInfo.isPanelVisible) - { - EventRecord e; - WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL); - } - - GetFontPanelSelection(font_name); - selected_font = gui_mac_find_font(font_name); - } - return selected_font; -} - -#ifdef USE_ATSUI_DRAWING - static void -gui_mac_create_atsui_style(void) -{ - if (p_macatsui && gFontStyle == NULL) - { - if (ATSUCreateStyle(&gFontStyle) != noErr) - gFontStyle = NULL; - } - if (p_macatsui && gWideFontStyle == NULL) - { - if (ATSUCreateStyle(&gWideFontStyle) != noErr) - gWideFontStyle = NULL; - } - - p_macatsui_last = p_macatsui; -} -#endif - -/* - * Initialise vim to use the font with the given name. Return FAIL if the font - * could not be loaded, OK otherwise. - */ - int -gui_mch_init_font(char_u *font_name, int fontset) -{ - // TODO: Add support for bold italic underline proportional etc... - Str255 suggestedFont = "\pMonaco"; - int suggestedSize = 10; - FontInfo font_info; - short font_id; - GuiFont font; - char_u used_font_name[512]; - -#ifdef USE_ATSUI_DRAWING - gui_mac_create_atsui_style(); -#endif - - if (font_name == NULL) - { - // First try to get the suggested font - GetFNum(suggestedFont, &font_id); - - if (font_id == 0) - { - // Then pickup the standard application font - font_id = GetAppFont(); - STRCPY(used_font_name, "default"); - } - else - STRCPY(used_font_name, "Monaco"); - font = (suggestedSize << 16) + ((long) font_id & 0xFFFF); - } - else if (STRCMP(font_name, "*") == 0) - { - char_u *new_p_guifont; - - font = gui_mac_select_font(used_font_name); - if (font == NOFONT) - return FAIL; - - // Set guifont to the name of the selected font. - new_p_guifont = alloc(STRLEN(used_font_name) + 1); - if (new_p_guifont != NULL) - { - STRCPY(new_p_guifont, used_font_name); - vim_free(p_guifont); - p_guifont = new_p_guifont; - // Replace spaces in the font name with underscores. - for ( ; *new_p_guifont; ++new_p_guifont) - { - if (*new_p_guifont == ' ') - *new_p_guifont = '_'; - } - } - } - else - { - font = gui_mac_find_font(font_name); - vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1); - - if (font == NOFONT) - return FAIL; - } - - gui.norm_font = font; - - hl_set_font_name(used_font_name); - - TextSize(font >> 16); - TextFont(font & 0xFFFF); - - GetFontInfo(&font_info); - - gui.char_ascent = font_info.ascent; - gui.char_width = CharWidth('_'); - gui.char_height = font_info.ascent + font_info.descent + p_linespace; - -#ifdef USE_ATSUI_DRAWING - if (p_macatsui && gFontStyle) - gui_mac_set_font_attributes(font); -#endif - - return OK; -} - -/* - * Adjust gui.char_height (after 'linespace' was changed). - */ - int -gui_mch_adjust_charheight(void) -{ - FontInfo font_info; - - GetFontInfo(&font_info); - gui.char_height = font_info.ascent + font_info.descent + p_linespace; - gui.char_ascent = font_info.ascent + p_linespace / 2; - return OK; -} - -/* - * Get a font structure for highlighting. - */ - GuiFont -gui_mch_get_font(char_u *name, int giveErrorIfMissing) -{ - GuiFont font; - - font = gui_mac_find_font(name); - - if (font == NOFONT) - { - if (giveErrorIfMissing) - semsg(_(e_font), name); - return NOFONT; - } - /* - * TODO : Accept only monospace - */ - - return font; -} - -#if defined(FEAT_EVAL) || defined(PROTO) -/* - * Return the name of font "font" in allocated memory. - * Don't know how to get the actual name, thus use the provided name. - */ - char_u * -gui_mch_get_fontname(GuiFont font, char_u *name) -{ - if (name == NULL) - return NULL; - return vim_strsave(name); -} -#endif - -#ifdef USE_ATSUI_DRAWING - static void -gui_mac_set_font_attributes(GuiFont font) -{ - ATSUFontID fontID; - Fixed fontSize; - Fixed fontWidth; - - fontID = font & 0xFFFF; - fontSize = Long2Fix(font >> 16); - fontWidth = Long2Fix(gui.char_width); - - ATSUAttributeTag attribTags[] = - { - kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag, - kATSUMaxATSUITagValue + 1 - }; - - ByteCount attribSizes[] = - { - sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth), - sizeof(font) - }; - - ATSUAttributeValuePtr attribValues[] = - { - &fontID, &fontSize, &fontWidth, &font - }; - - if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr) - { - if (ATSUSetAttributes(gFontStyle, - (sizeof attribTags) / sizeof(ATSUAttributeTag), - attribTags, attribSizes, attribValues) != noErr) - { -# ifndef NDEBUG - fprintf(stderr, "couldn't set font style\n"); -# endif - ATSUDisposeStyle(gFontStyle); - gFontStyle = NULL; - } - - if (has_mbyte) - { - // FIXME: we should use a more mbyte sensitive way to support - // wide font drawing - fontWidth = Long2Fix(gui.char_width * 2); - - if (ATSUSetAttributes(gWideFontStyle, - (sizeof attribTags) / sizeof(ATSUAttributeTag), - attribTags, attribSizes, attribValues) != noErr) - { - ATSUDisposeStyle(gWideFontStyle); - gWideFontStyle = NULL; - } - } - } -} -#endif - -/* - * Set the current text font. - */ - void -gui_mch_set_font(GuiFont font) -{ -#ifdef USE_ATSUI_DRAWING - GuiFont currFont; - ByteCount actualFontByteCount; - - if (p_macatsui && gFontStyle) - { - // Avoid setting same font again - if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1, - sizeof(font), &currFont, &actualFontByteCount) == noErr - && actualFontByteCount == (sizeof font)) - { - if (currFont == font) - return; - } - - gui_mac_set_font_attributes(font); - } - - if (p_macatsui && !gIsFontFallbackSet) - { - // Setup automatic font substitution. The user's guifontwide - // is tried first, then the system tries other fonts. -#if 0 - ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag }; - ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) }; - ATSUCreateFontFallbacks(&gFontFallbacks); - ATSUSetObjFontFallbacks(gFontFallbacks, ); -#endif - if (gui.wide_font) - { - ATSUFontID fallbackFonts; - gIsFontFallbackSet = TRUE; - - if (FMGetFontFromFontFamilyInstance( - (gui.wide_font & 0xFFFF), - 0, - &fallbackFonts, - NULL) == noErr) - { - ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID), - &fallbackFonts, - kATSUSequentialFallbacksPreferred); - } -// ATSUAttributeValuePtr fallbackValues[] = { }; - } - } -#endif - TextSize(font >> 16); - TextFont(font & 0xFFFF); -} - -/* - * If a font is not going to be used, free its structure. - */ - void -gui_mch_free_font(GuiFont font) -{ - /* - * Free font when "font" is not 0. - * Nothing to do in the current implementation, since - * nothing is allocated for each font used. - */ -} - -/* - * Return the Pixel value (color) for the given color name. This routine was - * pretty much taken from example code in the Silicon Graphics OSF/Motif - * Programmer's Guide. - * Return INVALCOLOR when failed. - */ - guicolor_T -gui_mch_get_color(char_u *name) -{ - // TODO: Add support for the new named color of MacOS 8 - RGBColor MacColor; - - if (STRICMP(name, "hilite") == 0) - { - LMGetHiliteRGB(&MacColor); - return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8)); - } - return gui_get_color_cmn(name); -} - - guicolor_T -gui_mch_get_rgb_color(int r, int g, int b) -{ - return gui_get_rgb_color_cmn(r, g, b); -} - -/* - * Set the current text foreground color. - */ - void -gui_mch_set_fg_color(guicolor_T color) -{ - RGBColor TheColor; - - TheColor.red = Red(color) * 0x0101; - TheColor.green = Green(color) * 0x0101; - TheColor.blue = Blue(color) * 0x0101; - - RGBForeColor(&TheColor); -} - -/* - * Set the current text background color. - */ - void -gui_mch_set_bg_color(guicolor_T color) -{ - RGBColor TheColor; - - TheColor.red = Red(color) * 0x0101; - TheColor.green = Green(color) * 0x0101; - TheColor.blue = Blue(color) * 0x0101; - - RGBBackColor(&TheColor); -} - -RGBColor specialColor; - -/* - * Set the current text special color. - */ - void -gui_mch_set_sp_color(guicolor_T color) -{ - specialColor.red = Red(color) * 0x0101; - specialColor.green = Green(color) * 0x0101; - specialColor.blue = Blue(color) * 0x0101; -} - -/* - * Draw undercurl at the bottom of the character cell. - */ - static void -draw_undercurl(int flags, int row, int col, int cells) -{ - int x; - int offset; - const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 }; - int y = FILL_Y(row + 1) - 1; - - RGBForeColor(&specialColor); - - offset = val[FILL_X(col) % 8]; - MoveTo(FILL_X(col), y - offset); - - for (x = FILL_X(col); x < FILL_X(col + cells); ++x) - { - offset = val[x % 8]; - LineTo(x, y - offset); - } -} - - - static void -draw_string_QD(int row, int col, char_u *s, int len, int flags) -{ - char_u *tofree = NULL; - - if (output_conv.vc_type != CONV_NONE) - { - tofree = string_convert(&output_conv, s, &len); - if (tofree != NULL) - s = tofree; - } - - /* - * On OS X, try using Quartz-style text antialiasing. - */ - if (gMacSystemVersion >= 0x1020) - { - // Quartz antialiasing is available only in OS 10.2 and later. - UInt32 qd_flags = (p_antialias ? - kQDUseCGTextRendering | kQDUseCGTextMetrics : 0); - QDSwapTextFlags(qd_flags); - } - - /* - * When antialiasing we're using srcOr mode, we have to clear the block - * before drawing the text. - * Also needed when 'linespace' is non-zero to remove the cursor and - * underlining. - * But not when drawing transparently. - * The following is like calling gui_mch_clear_block(row, col, row, col + - * len - 1), but without setting the bg color to gui.back_pixel. - */ - if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0) - && !(flags & DRAW_TRANSP)) - { - Rect rc; - - rc.left = FILL_X(col); - rc.top = FILL_Y(row); - // Multibyte computation taken from gui_w32.c - if (has_mbyte) - { - // Compute the length in display cells. - rc.right = FILL_X(col + mb_string2cells(s, len)); - } - else - rc.right = FILL_X(col + len) + (col + len == Columns); - rc.bottom = FILL_Y(row + 1); - EraseRect(&rc); - } - - if (gMacSystemVersion >= 0x1020 && p_antialias) - { - StyleParameter face; - - face = normal; - if (flags & DRAW_BOLD) - face |= bold; - if (flags & DRAW_UNDERL) - face |= underline; - TextFace(face); - - // Quartz antialiasing works only in srcOr transfer mode. - TextMode(srcOr); - - MoveTo(TEXT_X(col), TEXT_Y(row)); - DrawText((char*)s, 0, len); - } - else - { - // Use old-style, non-antialiased QuickDraw text rendering. - TextMode(srcCopy); - TextFace(normal); - - // SelectFont(hdc, gui.currFont); - - if (flags & DRAW_TRANSP) - TextMode(srcOr); - - MoveTo(TEXT_X(col), TEXT_Y(row)); - DrawText((char *)s, 0, len); - - if (flags & DRAW_BOLD) - { - TextMode(srcOr); - MoveTo(TEXT_X(col) + 1, TEXT_Y(row)); - DrawText((char *)s, 0, len); - } - - if (flags & DRAW_UNDERL) - { - MoveTo(FILL_X(col), FILL_Y(row + 1) - 1); - LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1); - } - if (flags & DRAW_STRIKE) - { - MoveTo(FILL_X(col), FILL_Y(row + 1) - gui.char_height/2); - LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - gui.char_height/2); - } - } - - if (flags & DRAW_UNDERC) - draw_undercurl(flags, row, col, len); - - vim_free(tofree); -} - -#ifdef USE_ATSUI_DRAWING - - static void -draw_string_ATSUI(int row, int col, char_u *s, int len, int flags) -{ - // ATSUI requires utf-16 strings - UniCharCount utf16_len; - UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len); - utf16_len /= sizeof(UniChar); - - // - ATSUI automatically antialiases text (Someone) - // - for some reason it does not work... (Jussi) -#ifdef MAC_ATSUI_DEBUG - fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n", - row, col, len, len == 1 ? s[0] : ' '); -#endif - /* - * When antialiasing we're using srcOr mode, we have to clear the block - * before drawing the text. - * Also needed when 'linespace' is non-zero to remove the cursor and - * underlining. - * But not when drawing transparently. - * The following is like calling gui_mch_clear_block(row, col, row, col + - * len - 1), but without setting the bg color to gui.back_pixel. - */ - if ((flags & DRAW_TRANSP) == 0) - { - Rect rc; - - rc.left = FILL_X(col); - rc.top = FILL_Y(row); - // Multibyte computation taken from gui_w32.c - if (has_mbyte) - { - // Compute the length in display cells. - rc.right = FILL_X(col + mb_string2cells(s, len)); - } - else - rc.right = FILL_X(col + len) + (col + len == Columns); - - rc.bottom = FILL_Y(row + 1); - EraseRect(&rc); - } - - { - TextMode(srcCopy); - TextFace(normal); - - // SelectFont(hdc, gui.currFont); - if (flags & DRAW_TRANSP) - TextMode(srcOr); - - MoveTo(TEXT_X(col), TEXT_Y(row)); - - if (gFontStyle && flags & DRAW_BOLD) - { - Boolean attValue = true; - ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag }; - ByteCount attribSizes[] = { sizeof(Boolean) }; - ATSUAttributeValuePtr attribValues[] = { &attValue }; - - ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues); - } - - UInt32 useAntialias = p_antialias ? kATSStyleApplyAntiAliasing - : kATSStyleNoAntiAliasing; - if (useAntialias != useAntialias_cached) - { - ATSUAttributeTag attribTags[] = { kATSUStyleRenderingOptionsTag }; - ByteCount attribSizes[] = { sizeof(UInt32) }; - ATSUAttributeValuePtr attribValues[] = { &useAntialias }; - - if (gFontStyle) - ATSUSetAttributes(gFontStyle, 1, attribTags, - attribSizes, attribValues); - if (gWideFontStyle) - ATSUSetAttributes(gWideFontStyle, 1, attribTags, - attribSizes, attribValues); - - useAntialias_cached = useAntialias; - } - - if (has_mbyte) - { - int n, width_in_cell, last_width_in_cell; - UniCharArrayOffset offset = 0; - UniCharCount yet_to_draw = 0; - ATSUTextLayout textLayout; - ATSUStyle textStyle; - - last_width_in_cell = 1; - ATSUCreateTextLayout(&textLayout); - ATSUSetTextPointerLocation(textLayout, tofree, - kATSUFromTextBeginning, - kATSUToTextEnd, utf16_len); - /* - ATSUSetRunStyle(textLayout, gFontStyle, - kATSUFromTextBeginning, kATSUToTextEnd); */ - - // Compute the length in display cells. - for (n = 0; n < len; n += MB_BYTE2LEN(s[n])) - { - width_in_cell = (*mb_ptr2cells)(s + n); - - // probably we are switching from single byte character - // to multibyte characters (which requires more than one - // cell to draw) - if (width_in_cell != last_width_in_cell) - { -#ifdef MAC_ATSUI_DEBUG - fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n", - n, last_width_in_cell, width_in_cell, offset, yet_to_draw); -#endif - textStyle = last_width_in_cell > 1 ? gWideFontStyle - : gFontStyle; - - ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw); - offset += yet_to_draw; - yet_to_draw = 0; - last_width_in_cell = width_in_cell; - } - - yet_to_draw++; - } - - if (yet_to_draw) - { -#ifdef MAC_ATSUI_DEBUG - fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n", - n, last_width_in_cell, width_in_cell, offset, yet_to_draw); -#endif - // finish the rest style - textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle; - ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd); - } - - ATSUSetTransientFontMatching(textLayout, TRUE); - ATSUDrawText(textLayout, - kATSUFromTextBeginning, kATSUToTextEnd, - kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc); - ATSUDisposeTextLayout(textLayout); - } - else - { - ATSUTextLayout textLayout; - - if (ATSUCreateTextLayoutWithTextPtr(tofree, - kATSUFromTextBeginning, kATSUToTextEnd, - utf16_len, - (gFontStyle ? 1 : 0), &utf16_len, - (gFontStyle ? &gFontStyle : NULL), - &textLayout) == noErr) - { - ATSUSetTransientFontMatching(textLayout, TRUE); - - ATSUDrawText(textLayout, - kATSUFromTextBeginning, kATSUToTextEnd, - kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc); - - ATSUDisposeTextLayout(textLayout); - } - } - - // drawing is done, now reset bold to normal - if (gFontStyle && flags & DRAW_BOLD) - { - Boolean attValue = false; - - ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag }; - ByteCount attribSizes[] = { sizeof(Boolean) }; - ATSUAttributeValuePtr attribValues[] = { &attValue }; - - ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, - attribValues); - } - } - - if (flags & DRAW_UNDERC) - draw_undercurl(flags, row, col, len); - - vim_free(tofree); -} -#endif - - void -gui_mch_draw_string(int row, int col, char_u *s, int len, int flags) -{ -#if defined(USE_ATSUI_DRAWING) - if (p_macatsui == 0 && p_macatsui_last != 0) - // switch from macatsui to nomacatsui - gui_mac_dispose_atsui_style(); - else if (p_macatsui != 0 && p_macatsui_last == 0) - // switch from nomacatsui to macatsui - gui_mac_create_atsui_style(); - - if (p_macatsui) - draw_string_ATSUI(row, col, s, len, flags); - else -#endif - draw_string_QD(row, col, s, len, flags); -} - -/* - * Return OK if the key with the termcap name "name" is supported. - */ - int -gui_mch_haskey(char_u *name) -{ - int i; - - for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) - if (name[0] == special_keys[i].vim_code0 && - name[1] == special_keys[i].vim_code1) - return OK; - return FAIL; -} - - void -gui_mch_beep(void) -{ - SysBeep(1); // Should this be 0? (????) -} - - void -gui_mch_flash(int msec) -{ - // Do a visual beep by reversing the foreground and background colors - Rect rc; - - /* - * Note: InvertRect() excludes right and bottom of rectangle. - */ - rc.left = 0; - rc.top = 0; - rc.right = gui.num_cols * gui.char_width; - rc.bottom = gui.num_rows * gui.char_height; - InvertRect(&rc); - - ui_delay((long)msec, TRUE); // wait for some msec - - InvertRect(&rc); -} - -/* - * Invert a rectangle from row r, column c, for nr rows and nc columns. - */ - void -gui_mch_invert_rectangle(int r, int c, int nr, int nc) -{ - Rect rc; - - /* - * Note: InvertRect() excludes right and bottom of rectangle. - */ - rc.left = FILL_X(c); - rc.top = FILL_Y(r); - rc.right = rc.left + nc * gui.char_width; - rc.bottom = rc.top + nr * gui.char_height; - InvertRect(&rc); -} - -/* - * Iconify the GUI window. - */ - void -gui_mch_iconify(void) -{ - // TODO: find out what could replace iconify - // -window shade? - // -hide application? -} - -#if defined(FEAT_EVAL) || defined(PROTO) -/* - * Bring the Vim window to the foreground. - */ - void -gui_mch_set_foreground(void) -{ - // TODO -} -#endif - -/* - * Draw a cursor without focus. - */ - void -gui_mch_draw_hollow_cursor(guicolor_T color) -{ - Rect rc; - - /* - * Note: FrameRect() excludes right and bottom of rectangle. - */ - rc.left = FILL_X(gui.col); - rc.top = FILL_Y(gui.row); - rc.right = rc.left + gui.char_width; - if (mb_lefthalve(gui.row, gui.col)) - rc.right += gui.char_width; - rc.bottom = rc.top + gui.char_height; - - gui_mch_set_fg_color(color); - - FrameRect(&rc); -} - -/* - * Draw part of a cursor, only w pixels wide, and h pixels high. - */ - void -gui_mch_draw_part_cursor(int w, int h, guicolor_T color) -{ - Rect rc; - -#ifdef FEAT_RIGHTLEFT - // vertical line should be on the right of current point - if (CURSOR_BAR_RIGHT) - rc.left = FILL_X(gui.col + 1) - w; - else -#endif - rc.left = FILL_X(gui.col); - rc.top = FILL_Y(gui.row) + gui.char_height - h; - rc.right = rc.left + w; - rc.bottom = rc.top + h; - - gui_mch_set_fg_color(color); - - FrameRect(&rc); -// PaintRect(&rc); -} - - - -/* - * Catch up with any queued X events. This may put keyboard input into the - * input buffer, call resize call-backs, trigger timers etc. If there is - * nothing in the X event queue (& no timers pending), then we return - * immediately. - */ - void -gui_mch_update(void) -{ - // TODO: find what to do - // maybe call gui_mch_wait_for_chars (0) - // more like look at EventQueue then - // call heart of gui_mch_wait_for_chars; - // - // if (eventther) - // gui_mac_handle_event(&event); - EventRecord theEvent; - - if (EventAvail(everyEvent, &theEvent)) - if (theEvent.what != nullEvent) - gui_mch_wait_for_chars(0); -} - -/* - * Simple wrapper to neglect more easily the time - * spent inside WaitNextEvent while profiling. - */ - - pascal - Boolean -WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn) -{ - if (((long) sleep) < -1) - sleep = 32767; - return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn); -} - -/* - * GUI input routine called by gui_wait_for_chars(). Waits for a character - * from the keyboard. - * wtime == -1 Wait forever. - * wtime == 0 This should never happen. - * wtime > 0 Wait wtime milliseconds for a character. - * Returns OK if a character was found to be available within the given time, - * or FAIL otherwise. - */ - int -gui_mch_wait_for_chars(int wtime) -{ - EventMask mask = (everyEvent); - EventRecord event; - long entryTick; - long currentTick; - long sleeppyTick; - - // If we are providing life feedback with the scrollbar, - // we don't want to try to wait for an event, or else - // there won't be any life feedback. - if (dragged_sb != NULL) - return FAIL; - // TODO: Check if FAIL is the proper return code - - entryTick = TickCount(); - - allow_scrollbar = TRUE; - - do - { -#if 0 - if (dragRectControl == kCreateEmpty) - { - dragRgn = NULL; - dragRectControl = kNothing; - } - else -#endif - if (dragRectControl == kCreateRect) - { - dragRgn = cursorRgn; - RectRgn(dragRgn, &dragRect); - dragRectControl = kNothing; - } - /* - * Don't use gui_mch_update() because then we will spin-lock until a - * char arrives, instead we use WaitNextEventWrp() to hang until an - * event arrives. No need to check for input_buf_full because we are - * returning as soon as it contains a single char. - */ - // TODO: reduce wtime accordingly??? - if (wtime > -1) - sleeppyTick = 60 * wtime / 1000; - else - sleeppyTick = 32767; - - if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn)) - { - gui_mac_handle_event(&event); - if (input_available()) - { - allow_scrollbar = FALSE; - return OK; - } - } - currentTick = TickCount(); - } - while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000)); - - allow_scrollbar = FALSE; - return FAIL; -} - -/* - * Output routines. - */ - -/* - * Flush any output to the screen - */ - void -gui_mch_flush(void) -{ - // TODO: Is anything needed here? -} - -/* - * Clear a rectangular region of the screen from text pos (row1, col1) to - * (row2, col2) inclusive. - */ - void -gui_mch_clear_block(int row1, int col1, int row2, int col2) -{ - Rect rc; - - /* - * Clear one extra pixel at the far right, for when bold characters have - * spilled over to the next column. - */ - rc.left = FILL_X(col1); - rc.top = FILL_Y(row1); - rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1); - rc.bottom = FILL_Y(row2 + 1); - - gui_mch_set_bg_color(gui.back_pixel); - EraseRect(&rc); -} - -/* - * Clear the whole text window. - */ - void -gui_mch_clear_all(void) -{ - Rect rc; - - rc.left = 0; - rc.top = 0; - rc.right = Columns * gui.char_width + 2 * gui.border_width; - rc.bottom = Rows * gui.char_height + 2 * gui.border_width; - - gui_mch_set_bg_color(gui.back_pixel); - EraseRect(&rc); -// gui_mch_set_fg_color(gui.norm_pixel); -// FrameRect(&rc); -} - -/* - * Delete the given number of lines from the given row, scrolling up any - * text further down within the scroll region. - */ - void -gui_mch_delete_lines(int row, int num_lines) -{ - Rect rc; - - // changed without checking! - rc.left = FILL_X(gui.scroll_region_left); - rc.right = FILL_X(gui.scroll_region_right + 1); - rc.top = FILL_Y(row); - rc.bottom = FILL_Y(gui.scroll_region_bot + 1); - - gui_mch_set_bg_color(gui.back_pixel); - ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil); - - gui_clear_block(gui.scroll_region_bot - num_lines + 1, - gui.scroll_region_left, - gui.scroll_region_bot, gui.scroll_region_right); -} - -/* - * Insert the given number of lines before the given row, scrolling down any - * following text within the scroll region. - */ - void -gui_mch_insert_lines(int row, int num_lines) -{ - Rect rc; - - rc.left = FILL_X(gui.scroll_region_left); - rc.right = FILL_X(gui.scroll_region_right + 1); - rc.top = FILL_Y(row); - rc.bottom = FILL_Y(gui.scroll_region_bot + 1); - - gui_mch_set_bg_color(gui.back_pixel); - - ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil); - - // Update gui.cursor_row if the cursor scrolled or copied over - if (gui.cursor_row >= gui.row - && gui.cursor_col >= gui.scroll_region_left - && gui.cursor_col <= gui.scroll_region_right) - { - if (gui.cursor_row <= gui.scroll_region_bot - num_lines) - gui.cursor_row += num_lines; - else if (gui.cursor_row <= gui.scroll_region_bot) - gui.cursor_is_valid = FALSE; - } - - gui_clear_block(row, gui.scroll_region_left, - row + num_lines - 1, gui.scroll_region_right); -} - - /* - * TODO: add a vim format to the clipboard which remember - * LINEWISE, CHARWISE, BLOCKWISE - */ - - void -clip_mch_request_selection(Clipboard_T *cbd) -{ - - Handle textOfClip; - int flavor = 0; - Size scrapSize; - ScrapFlavorFlags scrapFlags; - ScrapRef scrap = nil; - OSStatus error; - int type; - char *searchCR; - char_u *tempclip; - - - error = GetCurrentScrap(&scrap); - if (error != noErr) - return; - - error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags); - if (error == noErr) - { - error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize); - if (error == noErr && scrapSize > 1) - flavor = 1; - } - - if (flavor == 0) - { - error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags); - if (error != noErr) - return; - - error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize); - if (error != noErr) - return; - } - - ReserveMem(scrapSize); - - // In CARBON we don't need a Handle, a pointer is good - textOfClip = NewHandle(scrapSize); - - // tempclip = alloc(scrapSize+1); - HLock(textOfClip); - error = GetScrapFlavorData(scrap, - flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR, - &scrapSize, *textOfClip); - scrapSize -= flavor; - - if (flavor) - type = **textOfClip; - else - type = MAUTO; - - tempclip = alloc(scrapSize + 1); - mch_memmove(tempclip, *textOfClip + flavor, scrapSize); - tempclip[scrapSize] = 0; - -#ifdef MACOS_CONVERT - { - // Convert from utf-16 (clipboard) - size_t encLen = 0; - char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen); - - if (to != NULL) - { - scrapSize = encLen; - vim_free(tempclip); - tempclip = to; - } - } -#endif - - searchCR = (char *)tempclip; - while (searchCR != NULL) - { - searchCR = strchr(searchCR, '\r'); - if (searchCR != NULL) - *searchCR = '\n'; - } - - clip_yank_selection(type, tempclip, scrapSize, cbd); - - vim_free(tempclip); - HUnlock(textOfClip); - - DisposeHandle(textOfClip); -} - - void -clip_mch_lose_selection(Clipboard_T *cbd) -{ - /* - * TODO: Really nothing to do? - */ -} - - int -clip_mch_own_selection(Clipboard_T *cbd) -{ - return OK; -} - -/* - * Send the current selection to the clipboard. - */ - void -clip_mch_set_selection(Clipboard_T *cbd) -{ - Handle textOfClip; - long scrapSize; - int type; - ScrapRef scrap; - - char_u *str = NULL; - - if (!cbd->owned) - return; - - clip_get_selection(cbd); - - /* - * Once we set the clipboard, lose ownership. If another application sets - * the clipboard, we don't want to think that we still own it. - */ - cbd->owned = FALSE; - - type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd); - -#ifdef MACOS_CONVERT - size_t utf16_len = 0; - UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len); - if (to) - { - scrapSize = utf16_len; - vim_free(str); - str = (char_u *)to; - } -#endif - - if (type >= 0) - { - ClearCurrentScrap(); - - textOfClip = NewHandle(scrapSize + 1); - HLock(textOfClip); - - **textOfClip = type; - mch_memmove(*textOfClip + 1, str, scrapSize); - GetCurrentScrap(&scrap); - PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone, - scrapSize, *textOfClip + 1); - PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone, - scrapSize + 1, *textOfClip); - HUnlock(textOfClip); - DisposeHandle(textOfClip); - } - - vim_free(str); -} - - void -gui_mch_set_text_area_pos(int x, int y, int w, int h) -{ - Rect VimBound; - -// HideWindow(gui.VimWindow); - GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound); - - if (gui.which_scrollbars[SBAR_LEFT]) - VimBound.left = -gui.scrollbar_width + 1; - else - VimBound.left = 0; - - SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound); - - ShowWindow(gui.VimWindow); -} - -/* - * Menu stuff. - */ - - void -gui_mch_enable_menu(int flag) -{ - /* - * Menu is always active. - */ -} - - void -gui_mch_set_menu_pos(int x, int y, int w, int h) -{ - /* - * The menu is always at the top of the screen. - */ -} - -/* - * Add a sub menu to the menu bar. - */ - void -gui_mch_add_menu(vimmenu_T *menu, int idx) -{ - /* - * TODO: Try to use only menu_id instead of both menu_id and menu_handle. - * TODO: use menu->mnemonic and menu->actext - * TODO: Try to reuse menu id - * Carbon Help suggest to use only id between 1 and 235 - */ - static long next_avail_id = 128; - long menu_after_me = 0; // Default to the end - CFStringRef name; - short index; - vimmenu_T *parent = menu->parent; - vimmenu_T *brother = menu->next; - - // Cannot add a menu if ... - if ((parent != NULL && parent->submenu_id == 0)) - return; - - // menu ID greater than 1024 are reserved for ??? - if (next_avail_id == 1024) - return; - - // My brother could be the PopUp, find my real brother - while ((brother != NULL) && (!menu_is_menubar(brother->name))) - brother = brother->next; - - // Find where to insert the menu (for MenuBar) - if ((parent == NULL) && (brother != NULL)) - menu_after_me = brother->submenu_id; - - // If the menu is not part of the menubar (and its submenus), add it 'nowhere' - if (!menu_is_menubar(menu->name)) - menu_after_me = hierMenu; - - // Convert the name -#ifdef MACOS_CONVERT - name = menu_title_removing_mnemonic(menu); -#else - name = C2Pascal_save(menu->dname); -#endif - if (name == NULL) - return; - - // Create the menu unless it's the help menu - { - // Carbon suggest use of - // OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *); - // OSStatus SetMenuTitle(MenuRef, ConstStr255Param title); - menu->submenu_id = next_avail_id; - if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr) - SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name); - next_avail_id++; - } - - if (parent == NULL) - { - // Adding a menu to the menubar, or in the no mans land (for PopUp) - - // TODO: Verify if we could only Insert Menu if really part of the - // menubar The Inserted menu are scanned or the Command-key combos - - // Insert the menu - InsertMenu(menu->submenu_handle, menu_after_me); // insert before -#if 1 - // Vim should normally update it. TODO: verify - DrawMenuBar(); -#endif - } - else - { - // Adding as a submenu - - index = gui_mac_get_menu_item_index(menu); - - // Call InsertMenuItem followed by SetMenuItemText - // to avoid special character recognition by InsertMenuItem - InsertMenuItem(parent->submenu_handle, "\p ", idx); // afterItem - SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name); - SetItemCmd(parent->submenu_handle, idx+1, 0x1B); - SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id); - InsertMenu(menu->submenu_handle, hierMenu); - } - - CFRelease(name); - -#if 0 - // Done by Vim later on - DrawMenuBar(); -#endif -} - -/* - * Add a menu item to a menu - */ - void -gui_mch_add_menu_item(vimmenu_T *menu, int idx) -{ - CFStringRef name; - vimmenu_T *parent = menu->parent; - int menu_inserted; - - // Cannot add item, if the menu have not been created - if (parent->submenu_id == 0) - return; - - // Could call SetMenuRefCon [CARBON] to associate with the Menu, - // for older OS call GetMenuItemData (menu, item, isCommandID?, data) - - // Convert the name -#ifdef MACOS_CONVERT - name = menu_title_removing_mnemonic(menu); -#else - name = C2Pascal_save(menu->dname); -#endif - - // Where are just a menu item, so no handle, no id - menu->submenu_id = 0; - menu->submenu_handle = NULL; - - menu_inserted = 0; - if (menu->actext) - { - // If the accelerator text for the menu item looks like it describes - // a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the - // item's command equivalent. - int key = 0; - int modifiers = 0; - char_u *p_actext; - - p_actext = menu->actext; - key = find_special_key(&p_actext, &modifiers, FSK_SIMPLIFY, NULL); - if (*p_actext != 0) - key = 0; // error: trailing text - // find_special_key() returns a keycode with as many of the - // specified modifiers as appropriate already applied (e.g., for - // "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD - // as the only modifier). Since we want to display all of the - // modifiers, we need to convert the keycode back to a printable - // character plus modifiers. - // TODO: Write an alternative find_special_key() that doesn't - // apply modifiers. - if (key > 0 && key < 32) - { - // Convert a control key to an uppercase letter. Note that - // by this point it is no longer possible to distinguish - // between, e.g., Ctrl-S and Ctrl-Shift-S. - modifiers |= MOD_MASK_CTRL; - key += '@'; - } - // If the keycode is an uppercase letter, set the Shift modifier. - // If it is a lowercase letter, don't set the modifier, but convert - // the letter to uppercase for display in the menu. - else if (key >= 'A' && key <= 'Z') - modifiers |= MOD_MASK_SHIFT; - else if (key >= 'a' && key <= 'z') - key += 'A' - 'a'; - // Note: keycodes below 0x22 are reserved by Apple. - if (key >= 0x22 && vim_isprintc_strict(key)) - { - int valid = 1; - char_u mac_mods = kMenuNoModifiers; - // Convert Vim modifier codes to Menu Manager equivalents. - if (modifiers & MOD_MASK_SHIFT) - mac_mods |= kMenuShiftModifier; - if (modifiers & MOD_MASK_CTRL) - mac_mods |= kMenuControlModifier; - if (!(modifiers & MOD_MASK_CMD)) - mac_mods |= kMenuNoCommandModifier; - if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK) - valid = 0; // TODO: will Alt someday map to Option? - if (valid) - { - char_u item_txt[10]; - // Insert the menu item after idx, with its command key. - item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/'; - item_txt[3] = key; - InsertMenuItem(parent->submenu_handle, item_txt, idx); - // Set the modifier keys. - SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods); - menu_inserted = 1; - } - } - } - // Call InsertMenuItem followed by SetMenuItemText - // to avoid special character recognition by InsertMenuItem - if (!menu_inserted) - InsertMenuItem(parent->submenu_handle, "\p ", idx); // afterItem - // Set the menu item name. - SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name); - -#if 0 - // Called by Vim - DrawMenuBar(); -#endif - - CFRelease(name); -} - - void -gui_mch_toggle_tearoffs(int enable) -{ - // no tearoff menus -} - -/* - * Destroy the machine specific menu widget. - */ - void -gui_mch_destroy_menu(vimmenu_T *menu) -{ - short index = gui_mac_get_menu_item_index(menu); - - if (index > 0) - { - if (menu->parent) - { - { - // For now just don't delete help menu items. (Huh? Dany) - DeleteMenuItem(menu->parent->submenu_handle, index); - - // Delete the Menu if it was a hierarchical Menu - if (menu->submenu_id != 0) - { - DeleteMenu(menu->submenu_id); - DisposeMenu(menu->submenu_handle); - } - } - } -#ifdef DEBUG_MAC_MENU - else - { - printf("gmdm 2\n"); - } -#endif - } - else - { - { - DeleteMenu(menu->submenu_id); - DisposeMenu(menu->submenu_handle); - } - } - // Shouldn't this be already done by Vim. TODO: Check - DrawMenuBar(); -} - -/* - * Make a menu either grey or not grey. - */ - void -gui_mch_menu_grey(vimmenu_T *menu, int grey) -{ - // TODO: Check if menu really exists - short index = gui_mac_get_menu_item_index(menu); -/* - index = menu->index; -*/ - if (grey) - { - if (menu->children) - DisableMenuItem(menu->submenu_handle, index); - if (menu->parent) - if (menu->parent->submenu_handle) - DisableMenuItem(menu->parent->submenu_handle, index); - } - else - { - if (menu->children) - EnableMenuItem(menu->submenu_handle, index); - if (menu->parent) - if (menu->parent->submenu_handle) - EnableMenuItem(menu->parent->submenu_handle, index); - } -} - -/* - * Make menu item hidden or not hidden - */ - void -gui_mch_menu_hidden(vimmenu_T *menu, int hidden) -{ - // There's no hidden mode on MacOS - gui_mch_menu_grey(menu, hidden); -} - - -/* - * This is called after setting all the menus to grey/hidden or not. - */ - void -gui_mch_draw_menubar(void) -{ - DrawMenuBar(); -} - - -/* - * Scrollbar stuff. - */ - - void -gui_mch_enable_scrollbar( - scrollbar_T *sb, - int flag) -{ - if (flag) - ShowControl(sb->id); - else - HideControl(sb->id); - -#ifdef DEBUG_MAC_SB - printf("enb_sb (%x) %x\n",sb->id, flag); -#endif -} - - void -gui_mch_set_scrollbar_thumb( - scrollbar_T *sb, - long val, - long size, - long max) -{ - SetControl32BitMaximum (sb->id, max); - SetControl32BitMinimum (sb->id, 0); - SetControl32BitValue (sb->id, val); - SetControlViewSize (sb->id, size); -#ifdef DEBUG_MAC_SB - printf("thumb_sb (%x) %lx, %lx,%lx\n",sb->id, val, size, max); -#endif -} - - void -gui_mch_set_scrollbar_pos( - scrollbar_T *sb, - int x, - int y, - int w, - int h) -{ - gui_mch_set_bg_color(gui.back_pixel); -#if 0 - if (gui.which_scrollbars[SBAR_LEFT]) - { - MoveControl(sb->id, x-16, y); - SizeControl(sb->id, w + 1, h); - } - else - { - MoveControl(sb->id, x, y); - SizeControl(sb->id, w + 1, h); - } -#endif - if (sb == &gui.bottom_sbar) - h += 1; - else - w += 1; - - if (gui.which_scrollbars[SBAR_LEFT]) - x -= 15; - - MoveControl(sb->id, x, y); - SizeControl(sb->id, w, h); -#ifdef DEBUG_MAC_SB - printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h); -#endif -} - - int -gui_mch_get_scrollbar_xpadding(void) -{ - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; -} - - int -gui_mch_get_scrollbar_ypadding(void) -{ - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; -} - - void -gui_mch_create_scrollbar( - scrollbar_T *sb, - int orient) // SBAR_VERT or SBAR_HORIZ -{ - Rect bounds; - - bounds.top = -16; - bounds.bottom = -10; - bounds.right = -10; - bounds.left = -16; - - sb->id = NewControl(gui.VimWindow, - &bounds, - "\pScrollBar", - TRUE, - 0, // current - 0, // top - 0, // bottom - kControlScrollBarLiveProc, - (long) sb->ident); -#ifdef DEBUG_MAC_SB - printf("create_sb (%x) %x\n",sb->id, orient); -#endif -} - - void -gui_mch_destroy_scrollbar(scrollbar_T *sb) -{ - gui_mch_set_bg_color(gui.back_pixel); - DisposeControl(sb->id); -#ifdef DEBUG_MAC_SB - printf("dest_sb (%x) \n",sb->id); -#endif -} - - int -gui_mch_is_blinking(void) -{ - return FALSE; -} - - int -gui_mch_is_blink_off(void) -{ - return FALSE; -} - -/* - * Cursor blink functions. - * - * This is a simple state machine: - * BLINK_NONE not blinking at all - * BLINK_OFF blinking, cursor is not shown - * BLINK_ON blinking, cursor is shown - */ - void -gui_mch_set_blinking(long wait, long on, long off) -{ -#if 0 - // TODO: TODO: TODO: TODO: - blink_waittime = wait; - blink_ontime = on; - blink_offtime = off; -#endif -} - -/* - * Stop the cursor blinking. Show the cursor if it wasn't shown. - */ - void -gui_mch_stop_blink(int may_call_gui_update_cursor) -{ - if (may_call_gui_update_cursor) - gui_update_cursor(TRUE, FALSE); -#if 0 - // TODO: TODO: TODO: TODO: - gui_w32_rm_blink_timer(); - if (blink_state == BLINK_OFF) - gui_update_cursor(TRUE, FALSE); - blink_state = BLINK_NONE; -#endif -} - -/* - * Start the cursor blinking. If it was already blinking, this restarts the - * waiting time and shows the cursor. - */ - void -gui_mch_start_blink(void) -{ - gui_update_cursor(TRUE, FALSE); - // TODO: TODO: TODO: TODO: -// gui_w32_rm_blink_timer(); - - // Only switch blinking on if none of the times is zero -#if 0 - if (blink_waittime && blink_ontime && blink_offtime) - { - blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime, - (TIMERPROC)_OnBlinkTimer); - blink_state = BLINK_ON; - gui_update_cursor(TRUE, FALSE); - } -#endif -} - -/* - * Return the RGB value of a pixel as long. - */ - guicolor_T -gui_mch_get_rgb(guicolor_T pixel) -{ - return (guicolor_T)((Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel)); -} - - - -#ifdef FEAT_BROWSE -/* - * Pop open a file browser and return the file selected, in allocated memory, - * or NULL if Cancel is hit. - * saving - TRUE if the file will be saved to, FALSE if it will be opened. - * title - Title message for the file browser dialog. - * dflt - Default name of file. - * ext - Default extension to be added to files without extensions. - * initdir - directory in which to open the browser (NULL = current dir) - * filter - Filter for matched files to choose from. - * Has a format like this: - * "C Files (*.c)\0*.c\0" - * "All Files\0*.*\0\0" - * If these two strings were concatenated, then a choice of two file - * filters will be selectable to the user. Then only matching files will - * be shown in the browser. If NULL, the default allows all files. - * - * *NOTE* - the filter string must be terminated with TWO nulls. - */ - char_u * -gui_mch_browse( - int saving, - char_u *title, - char_u *dflt, - char_u *ext, - char_u *initdir, - char_u *filter) -{ - // TODO: Add Ammon's safety check (Dany) - NavReplyRecord reply; - char_u *fname = NULL; - char_u **fnames = NULL; - long numFiles; - NavDialogOptions navOptions; - OSErr error; - - // Get Navigation Service Defaults value - NavGetDefaultDialogOptions(&navOptions); - - - // TODO: If we get a :browse args, set the Multiple bit. - navOptions.dialogOptionFlags = kNavAllowInvisibleFiles - | kNavDontAutoTranslate - | kNavDontAddTranslateItems - // | kNavAllowMultipleFiles - | kNavAllowStationery; - - (void) C2PascalString(title, &navOptions.message); - (void) C2PascalString(dflt, &navOptions.savedFileName); - // Could set clientName? - // windowTitle? (there's no title bar?) - - if (saving) - { - // Change first parm AEDesc (typeFSS) *defaultLocation to match dflt - NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL); - if (!reply.validRecord) - return NULL; - } - else - { - // Change first parm AEDesc (typeFSS) *defaultLocation to match dflt - NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL); - if (!reply.validRecord) - return NULL; - } - - fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error); - - NavDisposeReply(&reply); - - if (fnames) - { - fname = fnames[0]; - vim_free(fnames); - } - - // TODO: Shorten the file name if possible - return fname; -} -#endif // FEAT_BROWSE - -#ifdef FEAT_GUI_DIALOG -/* - * Stuff for dialogues - */ - -/* - * Create a dialogue dynamically from the parameter strings. - * type = type of dialogue (question, alert, etc.) - * title = dialogue title. may be NULL for default title. - * message = text to display. Dialogue sizes to accommodate it. - * buttons = '\n' separated list of button captions, default first. - * dfltbutton = number of default button. - * - * This routine returns 1 if the first button is pressed, - * 2 for the second, etc. - * - * 0 indicates Esc was pressed. - * -1 for unexpected error - * - * If stubbing out this fn, return 1. - */ - -typedef struct -{ - short idx; - short width; // Size of the text in pixel - Rect box; -} vgmDlgItm; // Vim Gui_Mac.c Dialog Item - -#define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top) - - static void -macMoveDialogItem( - DialogRef theDialog, - short itemNumber, - short X, - short Y, - Rect *inBox) -{ -#if 0 // USE_CARBONIZED - // Untested - MoveDialogItem(theDialog, itemNumber, X, Y); - if (inBox != nil) - GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox); -#else - short itemType; - Handle itemHandle; - Rect localBox; - Rect *itemBox = &localBox; - - if (inBox != nil) - itemBox = inBox; - - GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox); - OffsetRect(itemBox, -itemBox->left, -itemBox->top); - OffsetRect(itemBox, X, Y); - // To move a control (like a button) we need to call both - // MoveControl and SetDialogItem. FAQ 6-18 - if (1) //(itemType & kControlDialogItem) - MoveControl((ControlRef) itemHandle, X, Y); - SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox); -#endif -} - - static void -macSizeDialogItem( - DialogRef theDialog, - short itemNumber, - short width, - short height) -{ - short itemType; - Handle itemHandle; - Rect itemBox; - - GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox); - - // When width or height is zero do not change it - if (width == 0) - width = itemBox.right - itemBox.left; - if (height == 0) - height = itemBox.bottom - itemBox.top; - -#if 0 // USE_CARBONIZED - SizeDialogItem(theDialog, itemNumber, width, height); // Untested -#else - // Resize the bounding box - itemBox.right = itemBox.left + width; - itemBox.bottom = itemBox.top + height; - - // To resize a control (like a button) we need to call both - // SizeControl and SetDialogItem. (deducted from FAQ 6-18) - if (itemType & kControlDialogItem) - SizeControl((ControlRef) itemHandle, width, height); - - // Configure back the item - SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox); -#endif -} - - static void -macSetDialogItemText( - DialogRef theDialog, - short itemNumber, - Str255 itemName) -{ - short itemType; - Handle itemHandle; - Rect itemBox; - - GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox); - - if (itemType & kControlDialogItem) - SetControlTitle((ControlRef) itemHandle, itemName); - else - SetDialogItemText(itemHandle, itemName); -} - - -/* - * ModalDialog() handler for message dialogs that have hotkey accelerators. - * Expects a mapping of hotkey char to control index in gDialogHotKeys; - * setting gDialogHotKeys to NULL disables any hotkey handling. - */ - static pascal Boolean -DialogHotkeyFilterProc ( - DialogRef theDialog, - EventRecord *event, - DialogItemIndex *itemHit) -{ - char_u keyHit; - - if (event->what == keyDown || event->what == autoKey) - { - keyHit = (event->message & charCodeMask); - - if (gDialogHotKeys && gDialogHotKeys[keyHit]) - { -#ifdef DEBUG_MAC_DIALOG_HOTKEYS - printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]); -#endif - *itemHit = gDialogHotKeys[keyHit]; - - // When handing off to StdFilterProc, pretend that the user - // clicked the control manually. Note that this is also supposed - // to cause the button to hilite briefly (to give some user - // feedback), but this seems not to actually work (or it's too - // fast to be seen). - event->what = kEventControlSimulateHit; - - return true; // we took care of it - } - - // Defer to the OS's standard behavior for this event. - // This ensures that Enter will still activate the default button. - return StdFilterProc(theDialog, event, itemHit); - } - return false; // Let ModalDialog deal with it -} - - -/* - * TODO: There have been some crashes with dialogs, check your inbox - * (Jussi) - */ - int -gui_mch_dialog( - int type, - char_u *title, - char_u *message, - char_u *buttons, - int dfltbutton, - char_u *textfield, - int ex_cmd) -{ - Handle buttonDITL; - Handle iconDITL; - Handle inputDITL; - Handle messageDITL; - Handle itemHandle; - Handle iconHandle; - DialogPtr theDialog; - char_u len; - char_u PascalTitle[256]; // place holder for the title - char_u name[256]; - GrafPtr oldPort; - short itemHit; - char_u *buttonChar; - short hotKeys[256]; // map of hotkey -> control ID - char_u aHotKey; - Rect box; - short button; - short lastButton; - short itemType; - short useIcon; - short width; - short totalButtonWidth = 0; // the width of all buttons together - // including spacing - short widestButton = 0; - short dfltButtonEdge = 20; // gut feeling - short dfltElementSpacing = 13; // from IM:V.2-29 - short dfltIconSideSpace = 23; // from IM:V.2-29 - short maximumWidth = 400; // gut feeling - short maxButtonWidth = 175; // gut feeling - - short vertical; - short dialogHeight; - short messageLines = 3; - FontInfo textFontInfo; - - vgmDlgItm iconItm; - vgmDlgItm messageItm; - vgmDlgItm inputItm; - vgmDlgItm buttonItm; - - WindowRef theWindow; - - ModalFilterUPP dialogUPP; - - // Check 'v' flag in 'guioptions': vertical button placement. - vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); - - // Create a new Dialog Box from template. - theDialog = GetNewDialog(129, nil, (WindowRef) -1); - - // Get the WindowRef - theWindow = GetDialogWindow(theDialog); - - // Hide the window. - // 1. to avoid seeing slow drawing - // 2. to prevent a problem seen while moving dialog item - // within a visible window. (non-Carbon MacOS 9) - // Could be avoided by changing the resource. - HideWindow(theWindow); - - // Change the graphical port to the dialog, - // so we can measure the text with the proper font - GetPort(&oldPort); - SetPortDialogPort(theDialog); - - // Get the info about the default text, - // used to calculate the height of the message - // and of the text field - GetFontInfo(&textFontInfo); - - // Set the dialog title - if (title != NULL) - { - (void) C2PascalString(title, &PascalTitle); - SetWTitle(theWindow, PascalTitle); - } - - // Creates the buttons and add them to the Dialog Box. - buttonDITL = GetResource('DITL', 130); - buttonChar = buttons; - button = 0; - - // initialize the hotkey mapping - CLEAR_FIELD(hotKeys); - - for (;*buttonChar != 0;) - { - // Get the name of the button - button++; - len = 0; - for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++) - { - if (*buttonChar != DLG_HOTKEY_CHAR) - name[++len] = *buttonChar; - else - { - aHotKey = (char_u)*(buttonChar+1); - if (aHotKey >= 'A' && aHotKey <= 'Z') - aHotKey = (char_u)((int)aHotKey + (int)'a' - (int)'A'); - hotKeys[aHotKey] = button; -#ifdef DEBUG_MAC_DIALOG_HOTKEYS - printf("### hotKey for button %d is '%c'\n", button, aHotKey); -#endif - } - } - - if (*buttonChar != 0) - buttonChar++; - name[0] = len; - - // Add the button - AppendDITL(theDialog, buttonDITL, overlayDITL); // appendDITLRight); - - // Change the button's name - macSetDialogItemText(theDialog, button, name); - - // Resize the button to fit its name - width = StringWidth(name) + 2 * dfltButtonEdge; - // Limit the size of any button to an acceptable value. - // TODO: Should be based on the message width - if (width > maxButtonWidth) - width = maxButtonWidth; - macSizeDialogItem(theDialog, button, width, 0); - - totalButtonWidth += width; - - if (width > widestButton) - widestButton = width; - } - ReleaseResource(buttonDITL); - lastButton = button; - - // Add the icon to the Dialog Box. - iconItm.idx = lastButton + 1; - iconDITL = GetResource('DITL', 131); - switch (type) - { - case VIM_GENERIC: - case VIM_INFO: - case VIM_QUESTION: useIcon = kNoteIcon; break; - case VIM_WARNING: useIcon = kCautionIcon; break; - case VIM_ERROR: useIcon = kStopIcon; break; - default: useIcon = kStopIcon; - } - AppendDITL(theDialog, iconDITL, overlayDITL); - ReleaseResource(iconDITL); - GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box); - // TODO: Should the item be freed? - iconHandle = GetIcon(useIcon); - SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box); - - // Add the message to the Dialog box. - messageItm.idx = lastButton + 2; - messageDITL = GetResource('DITL', 132); - AppendDITL(theDialog, messageDITL, overlayDITL); - ReleaseResource(messageDITL); - GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box); - (void) C2PascalString(message, &name); - SetDialogItemText(itemHandle, name); - messageItm.width = StringWidth(name); - - // Add the input box if needed - if (textfield != NULL) - { - // Cheat for now reuse the message and convert to text edit - inputItm.idx = lastButton + 3; - inputDITL = GetResource('DITL', 132); - AppendDITL(theDialog, inputDITL, overlayDITL); - ReleaseResource(inputDITL); - GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box); -// SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box); - (void) C2PascalString(textfield, &name); - SetDialogItemText(itemHandle, name); - inputItm.width = StringWidth(name); - - // Hotkeys don't make sense if there's a text field - gDialogHotKeys = NULL; - } - else - // Install hotkey table - gDialogHotKeys = (short *)&hotKeys; - - // Set the <ENTER> and <ESC> button. - SetDialogDefaultItem(theDialog, dfltbutton); - SetDialogCancelItem(theDialog, 0); - - // Reposition element - - // Check if we need to force vertical - if (totalButtonWidth > maximumWidth) - vertical = TRUE; - - // Place icon - macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box); - iconItm.box.right = box.right; - iconItm.box.bottom = box.bottom; - - // Place Message - messageItm.box.left = iconItm.box.right + dfltIconSideSpace; - macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent)); - macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box); - - // Place Input - if (textfield != NULL) - { - inputItm.box.left = messageItm.box.left; - inputItm.box.top = messageItm.box.bottom + dfltElementSpacing; - macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent); - macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box); - // Convert the static text into a text edit. - // For some reason this change need to be done last (Dany) - GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box); - SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box); - SelectDialogItemText(theDialog, inputItm.idx, 0, 32767); - } - - // Place Button - if (textfield != NULL) - { - buttonItm.box.left = inputItm.box.left; - buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing; - } - else - { - buttonItm.box.left = messageItm.box.left; - buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing; - } - - for (button=1; button <= lastButton; button++) - { - - macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box); - // With vertical, it's better to have all buttons the same length - if (vertical) - { - macSizeDialogItem(theDialog, button, widestButton, 0); - GetDialogItem(theDialog, button, &itemType, &itemHandle, &box); - } - // Calculate position of next button - if (vertical) - buttonItm.box.top = box.bottom + dfltElementSpacing; - else - buttonItm.box.left = box.right + dfltElementSpacing; - } - - // Resize the dialog box - dialogHeight = box.bottom + dfltElementSpacing; - SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE); - - // Magic resize - AutoSizeDialog(theDialog); - // Need a horizontal resize anyway so not that useful - - // Display it - ShowWindow(theWindow); -// BringToFront(theWindow); - SelectWindow(theWindow); - -// DrawDialog(theDialog); -#if 0 - GetPort(&oldPort); - SetPortDialogPort(theDialog); -#endif - -#ifdef USE_CARBONKEYHANDLER - // Avoid that we use key events for the main window. - dialog_busy = TRUE; -#endif - - // Prepare the shortcut-handling filterProc for handing to the dialog - dialogUPP = NewModalFilterUPP(DialogHotkeyFilterProc); - - // Hang until one of the button is hit - do - ModalDialog(dialogUPP, &itemHit); - while ((itemHit < 1) || (itemHit > lastButton)); - -#ifdef USE_CARBONKEYHANDLER - dialog_busy = FALSE; -#endif - - // Copy back the text entered by the user into the param - if (textfield != NULL) - { - GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box); - GetDialogItemText(itemHandle, (char_u *) &name); -#if IOSIZE < 256 - // Truncate the name to IOSIZE if needed - if (name[0] > IOSIZE) - name[0] = IOSIZE - 1; -#endif - vim_strncpy(textfield, &name[1], name[0]); - } - - // Restore the original graphical port - SetPort(oldPort); - - // Free the modal filterProc - DisposeRoutineDescriptor(dialogUPP); - - // Get ride of the dialog (free memory) - DisposeDialog(theDialog); - - return itemHit; -/* - * Useful thing which could be used - * SetDialogTimeout(): Auto click a button after timeout - * SetDialogTracksCursor() : Get the I-beam cursor over input box - * MoveDialogItem(): Probably better than SetDialogItem - * SizeDialogItem(): (but is it Carbon Only?) - * AutoSizeDialog(): Magic resize of dialog based on text length - */ -} -#endif // FEAT_DIALOG_GUI - -/* - * Display the saved error message(s). - */ -#ifdef USE_MCH_ERRMSG - void -display_errors(void) -{ - char *p; - char_u pError[256]; - - if (error_ga.ga_data == NULL) - return; - - // avoid putting up a message box with blanks only - for (p = (char *)error_ga.ga_data; *p; ++p) - if (!isspace(*p)) - { - if (STRLEN(p) > 255) - pError[0] = 255; - else - pError[0] = STRLEN(p); - - STRNCPY(&pError[1], p, pError[0]); - ParamText(pError, nil, nil, nil); - Alert(128, nil); - break; - // TODO: handled message longer than 256 chars - // use auto-sizeable alert - // or dialog with scrollbars (TextEdit zone) - } - ga_clear(&error_ga); -} -#endif - -/* - * Get current mouse coordinates in text window. - */ - void -gui_mch_getmouse(int *x, int *y) -{ - Point where; - - GetMouse(&where); - - *x = where.h; - *y = where.v; -} - - void -gui_mch_setmouse(int x, int y) -{ - // TODO -#if 0 - // From FAQ 3-11 - - CursorDevicePtr myMouse; - Point where; - - if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap) - != NGetTrapAddress(_Unimplemented, ToolTrap)) - { - // New way - - /* - * Get first device with one button. - * This will probably be the standard mouse - * start at head of cursor dev list - * - */ - - myMouse = nil; - - do - { - // Get the next cursor device - CursorDeviceNextDevice(&myMouse); - } - while ((myMouse != nil) && (myMouse->cntButtons != 1)); - - CursorDeviceMoveTo(myMouse, x, y); - } - else - { - // Old way - where.h = x; - where.v = y; - - *(Point *)RawMouse = where; - *(Point *)MTemp = where; - *(Ptr) CrsrNew = 0xFFFF; - } -#endif -} - - void -gui_mch_show_popupmenu(vimmenu_T *menu) -{ -/* - * Clone PopUp to use menu - * Create a object descriptor for the current selection - * Call the procedure - */ - - MenuHandle CntxMenu; - Point where; - OSStatus status; - UInt32 CntxType; - SInt16 CntxMenuID; - UInt16 CntxMenuItem; - Str255 HelpName = ""; - GrafPtr savePort; - - // Save Current Port: On MacOS X we seem to lose the port - GetPort(&savePort); //OSX - - GetMouse(&where); - LocalToGlobal(&where); //OSX - CntxMenu = menu->submenu_handle; - - // TODO: Get the text selection from Vim - - // Call to Handle Popup - status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp, - HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem); - - if (status == noErr) - { - if (CntxType == kCMMenuItemSelected) - { - // Handle the menu CntxMenuID, CntxMenuItem - // The submenu can be handle directly by gui_mac_handle_menu - // But what about the current menu, is the menu changed by - // ContextualMenuSelect - gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem); - } - else if (CntxMenuID == kCMShowHelpSelected) - { - // Should come up with the help - } - } - - // Restore original Port - SetPort(savePort); //OSX -} - -#if defined(FEAT_CW_EDITOR) || defined(PROTO) -// TODO: Is it need for MACOS_X? (Dany) - void -mch_post_buffer_write(buf_T *buf) -{ - GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec); - Send_KAHL_MOD_AE(buf); -} -#endif - -#ifdef FEAT_TITLE -/* - * Set the window title and icon. - * (The icon is not taken care of). - */ - void -gui_mch_settitle(char_u *title, char_u *icon) -{ - // TODO: Get vim to make sure maxlen (from p_titlelen) is smaller - // that 256. Even better get it to fit nicely in the titlebar. -#ifdef MACOS_CONVERT - CFStringRef windowTitle; - size_t windowTitleLen; -#else - char_u *pascalTitle; -#endif - - if (title == NULL) // nothing to do - return; - -#ifdef MACOS_CONVERT - windowTitleLen = STRLEN(title); - windowTitle = (CFStringRef)mac_enc_to_cfstring(title, windowTitleLen); - - if (windowTitle) - { - SetWindowTitleWithCFString(gui.VimWindow, windowTitle); - CFRelease(windowTitle); - } -#else - pascalTitle = C2Pascal_save(title); - if (pascalTitle != NULL) - { - SetWTitle(gui.VimWindow, pascalTitle); - vim_free(pascalTitle); - } -#endif -} -#endif - -/* - * Transferred from os_mac.c for MacOS X using os_unix.c prep work - */ - - int -C2PascalString(char_u *CString, Str255 *PascalString) -{ - char_u *PascalPtr = (char_u *) PascalString; - int len; - int i; - - PascalPtr[0] = 0; - if (CString == NULL) - return 0; - - len = STRLEN(CString); - if (len > 255) - len = 255; - - for (i = 0; i < len; i++) - PascalPtr[i+1] = CString[i]; - - PascalPtr[0] = len; - - return 0; -} - - int -GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec) -{ - // From FAQ 8-12 - Str255 filePascal; - CInfoPBRec myCPB; - OSErr err; - - (void) C2PascalString(file, &filePascal); - - myCPB.dirInfo.ioNamePtr = filePascal; - myCPB.dirInfo.ioVRefNum = 0; - myCPB.dirInfo.ioFDirIndex = 0; - myCPB.dirInfo.ioDrDirID = 0; - - err= PBGetCatInfo(&myCPB, false); - - // vRefNum, dirID, name - FSMakeFSSpec(0, 0, filePascal, fileFSSpec); - - // TODO: Use an error code mechanism - return 0; -} - -/* - * Convert a FSSpec to a full path - */ - -char_u *FullPathFromFSSpec_save(FSSpec file) -{ - /* - * TODO: Add protection for 256 char max. - */ - - CInfoPBRec theCPB; - char_u fname[256]; - char_u *filenamePtr = fname; - OSErr error; - int folder = 1; -#ifdef USE_UNIXFILENAME - SInt16 dfltVol_vRefNum; - SInt32 dfltVol_dirID; - FSRef refFile; - OSStatus status; - UInt32 pathSize = 256; - char_u pathname[256]; - char_u *path = pathname; -#else - Str255 directoryName; - char_u temporary[255]; - char_u *temporaryPtr = temporary; -#endif - -#ifdef USE_UNIXFILENAME - // Get the default volume - // TODO: Remove as this only work if Vim is on the Boot Volume - error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID); - - if (error) - return NULL; -#endif - - // Start filling fname with file.name - vim_strncpy(filenamePtr, &file.name[1], file.name[0]); - - // Get the info about the file specified in FSSpec - theCPB.dirInfo.ioFDirIndex = 0; - theCPB.dirInfo.ioNamePtr = file.name; - theCPB.dirInfo.ioVRefNum = file.vRefNum; - //theCPB.hFileInfo.ioDirID = 0; - theCPB.dirInfo.ioDrDirID = file.parID; - - // As ioFDirIndex = 0, get the info of ioNamePtr, - // which is relative to ioVrefNum, ioDirID - error = PBGetCatInfo(&theCPB, false); - - // If we are called for a new file we expect fnfErr - if ((error) && (error != fnfErr)) - return NULL; - - // Check if it's a file or folder - // default to file if file don't exist - if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error)) - folder = 0; // It's not a folder - else - folder = 1; - -#ifdef USE_UNIXFILENAME - /* - * The functions used here are available in Carbon, but do nothing on - * MacOS 8 and 9. - */ - if (error == fnfErr) - { - // If the file to be saved does not already exist, it isn't possible - // to convert its FSSpec into an FSRef. But we can construct an - // FSSpec for the file's parent folder (since we have its volume and - // directory IDs), and since that folder does exist, we can convert - // that FSSpec into an FSRef, convert the FSRef in turn into a path, - // and, finally, append the filename. - FSSpec dirSpec; - FSRef dirRef; - Str255 emptyFilename = "\p"; - error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum, - theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec); - if (error) - return NULL; - - error = FSpMakeFSRef(&dirSpec, &dirRef); - if (error) - return NULL; - - status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize); - if (status) - return NULL; - - STRCAT(path, "/"); - STRCAT(path, filenamePtr); - } - else - { - // If the file to be saved already exists, we can get its full path - // by converting its FSSpec into an FSRef. - error=FSpMakeFSRef(&file, &refFile); - if (error) - return NULL; - - status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize); - if (status) - return NULL; - } - - // Add a slash at the end if needed - if (folder) - STRCAT(path, "/"); - - return (vim_strsave(path)); -#else - // TODO: Get rid of all USE_UNIXFILENAME below - // Set ioNamePtr, it's the same area which is always reused. - theCPB.dirInfo.ioNamePtr = directoryName; - - // Trick for first entry, set ioDrParID to the first value - // we want for ioDrDirID - theCPB.dirInfo.ioDrParID = file.parID; - theCPB.dirInfo.ioDrDirID = file.parID; - - if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/)) - do - { - theCPB.dirInfo.ioFDirIndex = -1; - // theCPB.dirInfo.ioNamePtr = directoryName; Already done above. - theCPB.dirInfo.ioVRefNum = file.vRefNum; - // theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 - theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID; - - // As ioFDirIndex = -1, get the info of ioDrDirID, - // *ioNamePtr[0 TO 31] will be updated - error = PBGetCatInfo(&theCPB,false); - - if (error) - return NULL; - - // Put the new directoryName in front of the current fname - STRCPY(temporaryPtr, filenamePtr); - vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]); - STRCAT(filenamePtr, ":"); - STRCAT(filenamePtr, temporaryPtr); - } -#if 1 // def USE_UNIXFILENAME - while ((theCPB.dirInfo.ioDrParID != fsRtDirID) - /* && (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/); -#else - while (theCPB.dirInfo.ioDrDirID != fsRtDirID); -#endif - - // Get the information about the volume on which the file reside - theCPB.dirInfo.ioFDirIndex = -1; - // theCPB.dirInfo.ioNamePtr = directoryName; Already done above. - theCPB.dirInfo.ioVRefNum = file.vRefNum; - // theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 - theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID; - - // As ioFDirIndex = -1, get the info of ioDrDirID, - // *ioNamePtr[0 TO 31] will be updated - error = PBGetCatInfo(&theCPB,false); - - if (error) - return NULL; - - // For MacOS Classic always add the volume name - // For MacOS X add the volume name preceded by "Volumes" - // when we are not referring to the boot volume -#ifdef USE_UNIXFILENAME - if (file.vRefNum != dfltVol_vRefNum) -#endif - { - // Add the volume name - STRCPY(temporaryPtr, filenamePtr); - vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]); - STRCAT(filenamePtr, ":"); - STRCAT(filenamePtr, temporaryPtr); - -#ifdef USE_UNIXFILENAME - STRCPY(temporaryPtr, filenamePtr); - filenamePtr[0] = 0; // NULL terminate the string - STRCAT(filenamePtr, "Volumes:"); - STRCAT(filenamePtr, temporaryPtr); -#endif - } - - // Append final path separator if it's a folder - if (folder) - STRCAT(fname, ":"); - - // As we use Unix File Name for MacOS X convert it -#ifdef USE_UNIXFILENAME - // Need to insert leading / - // TODO: get the above code to use directly the / - STRCPY(&temporaryPtr[1], filenamePtr); - temporaryPtr[0] = '/'; - STRCPY(filenamePtr, temporaryPtr); - { - char *p; - for (p = fname; *p; p++) - if (*p == ':') - *p = '/'; - } -#endif - - return (vim_strsave(fname)); -#endif -} - -#if defined(USE_CARBONKEYHANDLER) || defined(PROTO) -/* - * Input Method Control functions. - */ - -/* - * Notify cursor position to IM. - */ - void -im_set_position(int row, int col) -{ -# if 0 - // TODO: Implement me! - im_start_row = row; - im_start_col = col; -# endif -} - -static ScriptLanguageRecord gTSLWindow; -static ScriptLanguageRecord gTSLInsert; -static ScriptLanguageRecord gTSLDefault = { 0, 0 }; - -static Component gTSCWindow; -static Component gTSCInsert; -static Component gTSCDefault; - -static int im_initialized = 0; - - static void -im_on_window_switch(int active) -{ - ScriptLanguageRecord *slptr = NULL; - OSStatus err; - - if (! gui.in_use) - return; - - if (im_initialized == 0) - { - im_initialized = 1; - - // save default TSM component (should be U.S.) to default - GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault, - kKeyboardInputMethodClass); - } - - if (active == TRUE) - { - im_is_active = TRUE; - ActivateTSMDocument(gTSMDocument); - slptr = &gTSLWindow; - - if (slptr) - { - err = SetDefaultInputMethodOfClass(gTSCWindow, slptr, - kKeyboardInputMethodClass); - if (err == noErr) - err = SetTextServiceLanguage(slptr); - - if (err == noErr) - KeyScript(slptr->fScript | smKeyForceKeyScriptMask); - } - } - else - { - err = GetTextServiceLanguage(&gTSLWindow); - if (err == noErr) - slptr = &gTSLWindow; - - if (slptr) - GetDefaultInputMethodOfClass(&gTSCWindow, slptr, - kKeyboardInputMethodClass); - - im_is_active = FALSE; - DeactivateTSMDocument(gTSMDocument); - } -} - -/* - * Set IM status on ("active" is TRUE) or off ("active" is FALSE). - */ - void -im_set_active(int active) -{ - ScriptLanguageRecord *slptr = NULL; - OSStatus err; - - if (!gui.in_use) - return; - - if (im_initialized == 0) - { - im_initialized = 1; - - // save default TSM component (should be U.S.) to default - GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault, - kKeyboardInputMethodClass); - } - - if (active == TRUE) - { - im_is_active = TRUE; - ActivateTSMDocument(gTSMDocument); - slptr = &gTSLInsert; - - if (slptr) - { - err = SetDefaultInputMethodOfClass(gTSCInsert, slptr, - kKeyboardInputMethodClass); - if (err == noErr) - err = SetTextServiceLanguage(slptr); - - if (err == noErr) - KeyScript(slptr->fScript | smKeyForceKeyScriptMask); - } - } - else - { - err = GetTextServiceLanguage(&gTSLInsert); - if (err == noErr) - slptr = &gTSLInsert; - - if (slptr) - GetDefaultInputMethodOfClass(&gTSCInsert, slptr, - kKeyboardInputMethodClass); - - // restore to default when switch to normal mode, so than we could - // enter commands easier - SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault, - kKeyboardInputMethodClass); - SetTextServiceLanguage(&gTSLDefault); - - im_is_active = FALSE; - DeactivateTSMDocument(gTSMDocument); - } -} - -/* - * Get IM status. When IM is on, return not 0. Else return 0. - */ - int -im_get_status(void) -{ - if (! gui.in_use) - return 0; - - return im_is_active; -} - -#endif - - - -#if defined(FEAT_GUI_TABLINE) || defined(PROTO) -// drawer implementation -static MenuRef contextMenu = NULL; -enum -{ - kTabContextMenuId = 42 -}; - -// the caller has to CFRelease() the returned string - static CFStringRef -getTabLabel(tabpage_T *page) -{ - get_tabline_label(page, FALSE); -#ifdef MACOS_CONVERT - return (CFStringRef)mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff)); -#else - // TODO: check internal encoding? - return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff, - kCFStringEncodingMacRoman); -#endif -} - - -#define DRAWER_SIZE 150 -#define DRAWER_INSET 16 - -static ControlRef dataBrowser = NULL; - -// when the tabline is hidden, vim doesn't call update_tabline(). When -// the tabline is shown again, show_tabline() is called before update_tabline(), -// and because of this, the tab labels and vim's internal tabs are out of sync -// for a very short time. to prevent inconsistent state, we store the labels -// of the tabs, not pointers to the tabs (which are invalid for a short time). -static CFStringRef *tabLabels = NULL; -static int tabLabelsSize = 0; - -enum -{ - kTabsColumn = 'Tabs' -}; - - static int -getTabCount(void) -{ - tabpage_T *tp; - int numTabs = 0; - - FOR_ALL_TABPAGES(tp) - ++numTabs; - return numTabs; -} - -// data browser item display callback - static OSStatus -dbItemDataCallback(ControlRef browser, - DataBrowserItemID itemID, - DataBrowserPropertyID property /* column id */, - DataBrowserItemDataRef itemData, - Boolean changeValue) -{ - OSStatus status = noErr; - - // assert(property == kTabsColumn); // why is this violated?? - - // changeValue is true if we have a modifiable list and data was changed. - // In our case, it's always false. - // (that is: if (changeValue) updateInternalData(); else return - // internalData(); - if (!changeValue) - { - CFStringRef str; - - assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize); - str = tabLabels[itemID - 1]; - status = SetDataBrowserItemDataText(itemData, str); - } - else - status = errDataBrowserPropertyNotSupported; - - return status; -} - -// data browser action callback - static void -dbItemNotificationCallback(ControlRef browser, - DataBrowserItemID item, - DataBrowserItemNotification message) -{ - switch (message) - { - case kDataBrowserItemSelected: - send_tabline_event(item); - break; - } -} - -// callbacks needed for contextual menu: - static void -dbGetContextualMenuCallback(ControlRef browser, - MenuRef *menu, - UInt32 *helpType, - CFStringRef *helpItemString, - AEDesc *selection) -{ - // on mac os 9: kCMHelpItemNoHelp, but it's not the same - *helpType = kCMHelpItemRemoveHelp; // OS X only ;-) - *helpItemString = NULL; - - *menu = contextMenu; -} - - static void -dbSelectContextualMenuCallback(ControlRef browser, - MenuRef menu, - UInt32 selectionType, - SInt16 menuID, - MenuItemIndex menuItem) -{ - if (selectionType == kCMMenuItemSelected) - { - MenuCommand command; - GetMenuItemCommandID(menu, menuItem, &command); - - // get tab that was selected when the context menu appeared - // (there is always one tab selected). TODO: check if the context menu - // isn't opened on an item but on empty space (has to be possible some - // way, the finder does it too ;-) ) - Handle items = NewHandle(0); - if (items != NULL) - { - int numItems; - - GetDataBrowserItems(browser, kDataBrowserNoItem, false, - kDataBrowserItemIsSelected, items); - numItems = GetHandleSize(items) / sizeof(DataBrowserItemID); - if (numItems > 0) - { - int idx; - DataBrowserItemID *itemsPtr; - - HLock(items); - itemsPtr = (DataBrowserItemID *)*items; - idx = itemsPtr[0]; - HUnlock(items); - send_tabline_menu_event(idx, command); - } - DisposeHandle(items); - } - } -} - -// focus callback of the data browser to always leave focus in vim - static OSStatus -dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data) -{ - assert(GetEventClass(event) == kEventClassControl - && GetEventKind(event) == kEventControlSetFocusPart); - - return paramErr; -} - - -// drawer callback to resize data browser to drawer size - static OSStatus -drawerCallback(EventHandlerCallRef handler, EventRef event, void *data) -{ - switch (GetEventKind(event)) - { - case kEventWindowBoundsChanged: // move or resize - { - UInt32 attribs; - GetEventParameter(event, kEventParamAttributes, typeUInt32, - NULL, sizeof(attribs), NULL, &attribs); - if (attribs & kWindowBoundsChangeSizeChanged) // resize - { - Rect r; - GetWindowBounds(drawer, kWindowContentRgn, &r); - SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top); - SetControlBounds(dataBrowser, &r); - SetDataBrowserTableViewNamedColumnWidth(dataBrowser, - kTabsColumn, r.right); - } - } - break; - } - - return eventNotHandledErr; -} - -// Load DataBrowserChangeAttributes() dynamically on tiger (and better). -// This way the code works on 10.2 and 10.3 as well (it doesn't have the -// blue highlights in the list view on these systems, though. Oh well.) - - -#import <mach-o/dyld.h> - -enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) }; - - static OSStatus -myDataBrowserChangeAttributes(ControlRef inDataBrowser, - OptionBits inAttributesToSet, - OptionBits inAttributesToClear) -{ - long osVersion; - char *symbolName; - NSSymbol symbol = NULL; - OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser, - OptionBits inAttributesToSet, OptionBits inAttributesToClear); - - Gestalt(gestaltSystemVersion, &osVersion); - if (osVersion < 0x1040) // only supported for 10.4 (and up) - return noErr; - - // C name mangling... - symbolName = "_DataBrowserChangeAttributes"; - if (!NSIsSymbolNameDefined(symbolName) - || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL) - return noErr; - - dataBrowserChangeAttributes = NSAddressOfSymbol(symbol); - if (dataBrowserChangeAttributes == NULL) - return noErr; // well... - return dataBrowserChangeAttributes(inDataBrowser, - inAttributesToSet, inAttributesToClear); -} - - static void -initialise_tabline(void) -{ - Rect drawerRect = { 0, 0, 0, DRAWER_SIZE }; - DataBrowserCallbacks dbCallbacks; - EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart}; - EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged}; - DataBrowserListViewColumnDesc colDesc; - - // drawers have to have compositing enabled - CreateNewWindow(kDrawerWindowClass, - kWindowStandardHandlerAttribute - | kWindowCompositingAttribute - | kWindowResizableAttribute - | kWindowLiveResizeAttribute, - &drawerRect, &drawer); - - SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true); - SetDrawerParent(drawer, gui.VimWindow); - SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET); - - - // create list view embedded in drawer - CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView, - &dataBrowser); - - dbCallbacks.version = kDataBrowserLatestCallbacks; - InitDataBrowserCallbacks(&dbCallbacks); - dbCallbacks.u.v1.itemDataCallback = - NewDataBrowserItemDataUPP(dbItemDataCallback); - dbCallbacks.u.v1.itemNotificationCallback = - NewDataBrowserItemNotificationUPP(dbItemNotificationCallback); - dbCallbacks.u.v1.getContextualMenuCallback = - NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback); - dbCallbacks.u.v1.selectContextualMenuCallback = - NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback); - - SetDataBrowserCallbacks(dataBrowser, &dbCallbacks); - - SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header - SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical - SetDataBrowserSelectionFlags(dataBrowser, - kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet); - SetDataBrowserTableViewHiliteStyle(dataBrowser, - kDataBrowserTableViewFillHilite); - Boolean b = false; - SetControlData(dataBrowser, kControlEntireControl, - kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b); - - // enable blue background in data browser (this is only in 10.4 and vim - // has to support older osx versions as well, so we have to load this - // function dynamically) - myDataBrowserChangeAttributes(dataBrowser, - kMyDataBrowserAttributeListViewAlternatingRowColors, 0); - - // install callback that keeps focus in vim and away from the data browser - InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent, - NULL, NULL); - - // install callback that keeps data browser at the size of the drawer - InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent, - NULL, NULL); - - // add "tabs" column to data browser - colDesc.propertyDesc.propertyID = kTabsColumn; - colDesc.propertyDesc.propertyType = kDataBrowserTextType; - - // add if items can be selected (?): kDataBrowserListViewSelectionColumn - colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags; - - colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; - colDesc.headerBtnDesc.minimumWidth = 100; - colDesc.headerBtnDesc.maximumWidth = 150; - colDesc.headerBtnDesc.titleOffset = 0; - colDesc.headerBtnDesc.titleString = CFSTR("Tabs"); - colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing; - colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font - colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly; - - AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0); - - // create tabline popup menu required by vim docs (see :he tabline-menu) - CreateNewMenu(kTabContextMenuId, 0, &contextMenu); - AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close Tab"), 0, - TABLINE_MENU_CLOSE, NULL); - AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0, - TABLINE_MENU_NEW, NULL); - AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0, - TABLINE_MENU_OPEN, NULL); -} - - -/* - * Show or hide the tabline. - */ - void -gui_mch_show_tabline(int showit) -{ - if (showit == 0) - CloseDrawer(drawer, true); - else - OpenDrawer(drawer, kWindowEdgeRight, true); -} - -/* - * Return TRUE when tabline is displayed. - */ - int -gui_mch_showing_tabline(void) -{ - WindowDrawerState state = GetDrawerState(drawer); - - return state == kWindowDrawerOpen || state == kWindowDrawerOpening; -} - -/* - * Update the labels of the tabline. - */ - void -gui_mch_update_tabline(void) -{ - tabpage_T *tp; - int numTabs = getTabCount(); - int nr = 1; - int curtabidx = 1; - - // adjust data browser - if (tabLabels != NULL) - { - int i; - - for (i = 0; i < tabLabelsSize; ++i) - CFRelease(tabLabels[i]); - free(tabLabels); - } - tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef)); - tabLabelsSize = numTabs; - - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) - { - if (tp == curtab) - curtabidx = nr; - tabLabels[nr-1] = getTabLabel(tp); - } - - RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL, - kDataBrowserItemNoProperty); - // data browser uses ids 1, 2, 3, ... numTabs per default, so we - // can pass NULL for the id array - AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL, - kDataBrowserItemNoProperty); - - DataBrowserItemID item = curtabidx; - SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign); -} - -/* - * Set the current tab to "nr". First tab is 1. - */ - void -gui_mch_set_curtab(int nr) -{ - DataBrowserItemID item = nr; - SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign); - - // TODO: call something like this?: (or restore scroll position, or...) - RevealDataBrowserItem(dataBrowser, item, kTabsColumn, - kDataBrowserRevealOnly); -} - -#endif // FEAT_GUI_TABLINE |