blob: 70393be1a70f8a5adca2da33bfe9c44135b2327f [file] [log] [blame]
/* vi:set ts=8 sts=4 sw=4:
*
* 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 PrŸmmer
* 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.
*/
/*
* NOTE: Comment mentionning FAQ refer to the book:
* "Macworld Mac Programming FAQs" from "IDG Books"
*/
/*
* WARNING: Vim must be able to compile without Carbon
* As the desired minimum requirement are circa System 7
* (I want to run it on my Mac Classic) (Dany)
*/
/*
* 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: find the best place for this (Dany) */
#if 0
# if ! TARGET_API_MAC_CARBON
/* Enable the new API functions even when not compiling for Carbon */
/* Apple recomends Universal Interface 3.3.2 or later */
# define OPAQUE_TOOLBOX_STRUCTS 1
# define ACCESSOR_CALLS_ARE_FUNCTIONS 1
/* Help Menu not supported by Carbon */
# define USE_HELPMENU
# endif
#endif
#include <Devices.h> /* included first to avoid CR problems */
#include "vim.h"
/* Enable Contextual Menu Support */
#if UNIVERSAL_INTERFACES_VERSION >= 0x0320
# define USE_CTRLCLICKMENU
#endif
/* Put Vim Help in MacOS Help */
#define USE_HELPMENU
/* Enable AEVENT */
#define USE_AEVENT
/* Compile as CodeWarior External Editor */
#if defined(FEAT_CW_EDITOR) && !defined(USE_AEVENT)
# define USE_AEVENT /* Need Apple Event Support */
#endif
/* The VIM creator is CodeWarior specific */
#if !(defined(__MRC__) || defined(__SC__) || defined(__APPLE_CC__))
# define USE_VIM_CREATOR_ID
#else
# if 0 /* Was this usefull for some compiler? (Dany) */
static OSType _fcreator = 'VIM!';
static OSType _ftype = 'TEXT';
# endif
#endif
/* Vim's Scrap flavor. */
#define VIMSCRAPFLAVOR 'VIM!'
/* CARBON version only tested with Project Builder under MacOS X */
#undef USE_CARBONIZED
#if (defined(__APPLE_CC__) || defined(__MRC__)) && defined(TARGET_API_MAC_CARBON)
# if TARGET_API_MAC_CARBON
# define USE_CARBONIZED
# endif
#endif
#undef USE_MOUSEWHEEL
#if defined(MACOS_X) && defined(USE_CARBONIZED)
# define USE_MOUSEWHEEL
static EventHandlerUPP mouseWheelHandlerUPP = NULL;
#endif
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
# define USE_CARBONKEYHANDLER
static EventHandlerUPP keyEventHandlerUPP = NULL;
/* Defined in os_mac_conv.c */
extern char_u *mac_utf16_to_enc __ARGS((UniChar *from, size_t fromLen, size_t *actualLen));
extern UniChar *mac_enc_to_utf16 __ARGS((char_u *from, size_t fromLen, size_t *actualLen));
extern CFStringRef mac_enc_to_cfstring __ARGS((char_u *from, size_t fromLen));
#endif
#ifdef MACOS_X
SInt32 gMacSystemVersion;
#endif
/* Debugging feature: start Vim window OFFSETed */
#undef USE_OFFSETED_WINDOW
/* Debugging feature: use CodeWarior SIOUX */
#undef USE_SIOUX
/* Include some file. TODO: move into os_mac.h */
#include <Menus.h>
#include <Resources.h>
#if !TARGET_API_MAC_CARBON
#include <StandardFile.h>
#include <Traps.h>
#endif
#include <Balloons.h>
#include <Processes.h>
#ifdef USE_AEVENT
# include <AppleEvents.h>
# include <AERegistry.h>
#endif
#ifdef USE_CTRLCLICKMENU
# include <Gestalt.h>
#endif
#ifdef USE_SIOUX
# include <stdio.h>
# include <sioux.h>
# include <console.h>
#endif
#if UNIVERSAL_INTERFACES_VERSION >= 0x0330
# include <ControlDefinitions.h>
# include <Navigation.h> /* Navigation only part of ?? */
#endif
#if TARGET_API_MAC_CARBON && 0
/* New Help Interface for Mac, not implemented yet.*/
# include <MacHelp.h>
#endif
/*
* Translate new name to old ones
* New function only available in MacOS 8.5,
* So use old one to be compatible back to System 7
*/
#ifndef USE_CARBONIZED
# undef EnableMenuItem
# define EnableMenuItem EnableItem
# undef DisableMenuItem
# define DisableMenuItem DisableItem
#endif
/* Carbon does not support the Get/SetControll functions,
* use Get/SetControl32Bit instead and rename for non-carbon
* systems.
*/
#ifndef USE_CARBONIZED
# undef SetControl32BitMaximum
# define SetControl32BitMaximum SetControlMaximum
# undef SetControl32BitMinimum
# define SetControl32BitMinimum SetControlMinimum
# undef SetControl32BitValue
# define SetControl32BitValue SetControlValue
# undef GetControl32BitValue
# define GetControl32BitValue GetControlValue
#endif
/*
* ???
*/
#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) */
#ifdef USE_CTRLCLICKMENU
static short clickIsPopup;
#endif
/* Feedback Action for Scrollbar */
ControlActionUPP gScrollAction;
ControlActionUPP gScrollDrag;
/* Keeping track of which scrollbar is being dragged */
static ControlHandle dragged_sb = NULL;
#if defined(USE_CARBONIZED) && defined(MACOS_X)
static struct
{
FMFontFamily family;
FMFontSize size;
FMFontStyle style;
Boolean isPanelVisible;
} gFontPanelInfo = { 0, 0, 0, false };
#endif
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
# define USE_ATSUI_DRAWING
ATSUStyle gFontStyle;
Boolean gIsFontFallbackSet;
#endif
/*
* The Quickdraw global is predefined in CodeWarior
* but is not in Apple MPW
*/
#if (defined(__MRC__) || defined(__SC__))
# if !(defined(TARGET_API_MAC_CARBON) && TARGET_API_MAC_CARBON)
QDGlobals qd;
# endif
#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'}, */
#ifndef MACOS_X
{vk_Delete, 'k', 'b'},
#endif
{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
/*
* ------------------------------------------------------------
* 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;
}
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
/*
* 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(menu)
vimmenu_T *menu;
{
CFStringRef name;
size_t menuTitleLen;
CFIndex displayLen;
CFRange mnemonicStart;
CFRange mnemonicEnd;
CFMutableStringRef cleanedName;
menuTitleLen = STRLEN(menu->dname);
name = 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)
{
#ifdef USE_SIOUX
printf("fname_from_AEDesc: AECountItems error: %d\n", error);
#endif
return(fnames);
}
/* Allocate the pointer list */
fnames = (char_u **) alloc(*numFiles * sizeof(char_u *));
/* 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. */
#ifdef USE_SIOUX
printf("aevt_odoc: AEGetNthPtr error: %ld\n", (long)newError);
#endif
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.
*
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=mac68k
#endif
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
};
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=reset
#endif
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)
{
#ifdef USE_SIOUX
printf("KAHL_SRCH: AEGetParamPtr error: %d\n", error);
#endif
return(error);
}
error = HandleUnusedParms(theAEvent);
if (error)
{
#ifdef USE_SIOUX
printf("KAHL_SRCH: HandleUnusedParms error: %d\n", error);
#endif
return(error);
}
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
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;
#ifdef USE_SIOUX
printf("KAHL_SRCH: file \"%#s\" {%d}", SearchData.theFile.name,SearchData.theFile.parID);
if (foundFile == false)
printf(" NOT");
printf(" found. [date %lx, %lx]\n", *SearchData.theDate, buf->b_mtime_read);
#endif
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.
*
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=mac68k
#endif
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
};
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=reset
#endif
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)
{
#ifdef USE_SIOUX
printf("KAHL_MOD: HandleUnusedParms error: %d\n", error);
#endif
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)
{
#ifdef USE_SIOUX
printf("KAHL_MOD: AECreateList error: %d\n", error);
#endif
return(error);
}
#if 0
error = AECountItems(&replyList, &numFiles);
#ifdef USE_SIOUX
printf("KAHL_MOD ReplyList: %x %x\n", replyList.descriptorType, replyList.dataHandle);
printf("KAHL_MOD ItemInList: %d\n", numFiles);
#endif
/* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc)
* AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType,
* sizeof(DescType))
*/
/* AEPutDesc */
#endif
numFiles = 0;
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
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));
#ifdef USE_SIOUX
if (numFiles == 0)
printf("KAHL_MOD: ");
else
printf(", ");
printf("\"%#s\" {%d} [date %lx, %lx]", theFile.theFile.name, theFile.theFile.parID, theFile.theDate, buf->b_mtime_read);
if (error)
printf(" (%d)", error);
numFiles++;
#endif
};
#ifdef USE_SIOUX
printf("\n");
#endif
#if 0
error = AECountItems(&replyList, &numFiles);
#ifdef USE_SIOUX
printf("KAHL_MOD ItemInList: %d\n", numFiles);
#endif
#endif
/* We can add data only if something to reply */
error = AEPutParamDesc(theReply, keyDirectObject, &replyList);
#ifdef USE_SIOUX
if (error)
printf("KAHL_MOD: AEPutParamDesc error: %d\n", error);
#endif
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.
*
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=mac68k
#endif
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 */
};
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=reset
#endif
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)
{
#ifdef USE_SIOUX
printf("KAHL_GTTX: AEGetParamPtr error: %d\n", error);
#endif
return(error);
}
for (buf = firstbuf; buf != NULL; buf = buf->b_next)
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)
{
#ifdef USE_SIOUX
printf("KAHL_GTTX: SetHandleSize increase: %d, size %d\n",
linesize, BufferSize);
#endif
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;
}
#ifdef USE_SIOUX
printf("KAHL_GTTX: file \"%#s\" {%d} [date %lx, %lx]", GetTextData.theFile.name, GetTextData.theFile.parID, *GetTextData.theDate, buf->b_mtime_read);
if (foundFile == false)
printf(" NOT");
printf(" found. (BufferSize = %d)\n", BufferSize);
#endif
error = HandleUnusedParms(theAEvent);
if (error)
{
#ifdef USE_SIOUX
printf("KAHL_GTTX: HandleUnusedParms error: %d\n", error);
#endif
return(error);
}
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);
#ifdef USE_SIOUX
printf("CodeWarrior is");
if (anErr != noErr)
printf(" NOT");
printf(" running\n");
#endif
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)
{
#ifdef USE_SIOUX
printf("KAHL_MOD: Send error: %d\n", anErr);
#endif
/* 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.
*
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=mac68k
#endif
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
};
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma options align=reset
#endif
/* 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;
#ifdef USE_SIOUX
printf("aevt_odoc:\n");
#endif
/* the direct object parameter is the list of aliases to files (one or more) */
error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList);
if (error)
{
#ifdef USE_SIOUX
printf("aevt_odoc: AEGetParamDesc error: %ld\n", (long)error);
#endif
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)
{
#ifdef USE_SIOUX
printf("aevt_odoc: AEGetParamPtr error: %ld\n", (long)error);
#endif
return(error);
}
#ifdef USE_SIOUX
printf("aevt_odoc: lineNum: %d, startRange %ld, endRange %ld, [date %lx]\n",
(int)thePosition.lineNum,
(long)thePosition.startRange, (long)thePosition.endRange,
(long)thePosition.theDate);
#endif
/*
error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition);
if (^error) then
{
if (thePosition.lineNum >= 0)
{
// Goto this line
}
else
{
// Set the range char wise
}
}
*/
#ifdef FEAT_VISUAL
reset_VIsual();
#endif
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;
/* 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);
}
goto finished;
}
/* Handle the drop, :edit to get to the file */
handle_drop(numFiles, fnames, FALSE);
/* TODO: Handle the goto/select line more cleanly */
if ((numFiles == 1) & (gotPosition))
{
if (thePosition.lineNum >= 0)
{
lnum = 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(thePosition.startRange + 1);
}
/* Update the screen display */
update_screen(NOT_VALID);
#ifdef FEAT_VISUAL
/* Select the text if possible */
if (gotPosition)
{
VIsual_active = TRUE;
VIsual_select = FALSE;
VIsual = curwin->w_cursor;
if (thePosition.lineNum < 0)
{
VIsual_mode = 'v';
goto_byte(thePosition.endRange);
}
else
{
VIsual_mode = 'V';
VIsual.col = 0;
}
}
#endif
setcursor();
out_flush();
/* Fake mouse event to wake from stall */
PostEvent(mouseUp, 0);
finished:
AEDisposeDesc(&theList); /* dispose what we allocated */
error = HandleUnusedParms(theAEvent);
if (error)
{
#ifdef USE_SIOUX
printf("aevt_odoc: HandleUnusedParms error: %ld\n", (long)error);
#endif
return(error);
}
return(error);
}
/*
*
*/
pascal OSErr
Handle_aevt_oapp_AE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
{
OSErr error = noErr;
#ifdef USE_SIOUX
printf("aevt_oapp:\n");
#endif
error = HandleUnusedParms(theAEvent);
if (error)
{
return(error);
}
return(error);
}
/*
*
*/
pascal OSErr
Handle_aevt_quit_AE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon)
{
OSErr error = noErr;
#ifdef USE_SIOUX
printf("aevt_quit\n");
#endif
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;
#ifdef USE_SIOUX
printf("aevt_pdoc:\n");
#endif
error = HandleUnusedParms(theAEvent);
if (error)
{
return(error);
}
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;
#ifdef USE_SIOUX
printf("Unknown Event: %x\n", theAEvent->descriptorType);
#endif
error = HandleUnusedParms(theAEvent);
if (error)
{
return(error);
}
return(error);
}
#if TARGET_API_MAC_CARBON
# define NewAEEventHandlerProc(x) NewAEEventHandlerUPP(x)
#endif
/*
* Install the various AppleEvent Handlers
*/
OSErr
InstallAEHandlers(void)
{
OSErr error;
/* install open application handler */
error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
NewAEEventHandlerProc(Handle_aevt_oapp_AE), 0, false);
if (error)
{
return error;
}
/* install quit application handler */
error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
NewAEEventHandlerProc(Handle_aevt_quit_AE), 0, false);
if (error)
{
return error;
}
/* install open document handler */
error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
NewAEEventHandlerProc(HandleODocAE), 0, false);
if (error)
{
return error;
}
/* install print document handler */
error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
NewAEEventHandlerProc(Handle_aevt_pdoc_AE), 0, false);
/* Install Core Suite */
/* error = AEInstallEventHandler(kAECoreSuite, kAEClone,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEClose,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAECountElements,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAECreateElement,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEDelete,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEGetData,
NewAEEventHandlerProc(Handle_unknown_AE), kAEGetData, false);
error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize,
NewAEEventHandlerProc(Handle_unknown_AE), kAEGetDataSize, false);
error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAEMove,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAESave,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
error = AEInstallEventHandler(kAECoreSuite, kAESetData,
NewAEEventHandlerProc(Handle_unknown_AE), nil, false);
*/
#ifdef FEAT_CW_EDITOR
/*
* Bind codewarrior support handlers
*/
error = AEInstallEventHandler('KAHL', 'GTTX',
NewAEEventHandlerProc(Handle_KAHL_GTTX_AE), 0, false);
if (error)
{
return error;
}
error = AEInstallEventHandler('KAHL', 'SRCH',
NewAEEventHandlerProc(Handle_KAHL_SRCH_AE), 0, false);
if (error)
{
return error;
}
error = AEInstallEventHandler('KAHL', 'MOD ',
NewAEEventHandlerProc(Handle_KAHL_MOD_AE), 0, false);
if (error)
{
return error;
}
#endif
return error;
}
#endif /* USE_AEVENT */
#if defined(USE_CARBONIZED) && defined(MACOS_X)
/*
* 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()
{
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, outName, &fontNameLen, NULL) != noErr)
return;
/* Only encode font size, because style (bold, italic, etc) is
* already part of the font full name */
vim_snprintf(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 = NULL;
}
}
#endif
/*
* ------------------------------------------------------------
* Unfiled yet
* ------------------------------------------------------------
*/
/*
* gui_mac_get_menu_item_index
*
* Returns the index inside the menu wher
*/
short /* Shoulde we return MenuItemIndex? */
gui_mac_get_menu_item_index(pMenu)
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;
}
#ifdef USE_HELPMENU
/* Adjust index in help menu (for predefined ones) */
if (itemIndex != -1)
if (pMenu->parent->submenu_id == kHMHelpMenuID)
itemIndex += gui.MacOSHelpItems;
#endif
}
return itemIndex;
}
static vimmenu_T *
gui_mac_get_vim_menu(menuID, itemIndex, pMenu)
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))
{
#ifdef USE_HELPMENU
if (menuID == kHMHelpMenuID)
itemIndex -= gui.MacOSHelpItems;
#endif
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 = W_WIDTH(curwin) - 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 (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);
}
}*/
}
/*
* ------------------------------------------------------------
* 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(theEvent, whichWindow)
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 scollbar */
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;
#ifdef USE_CTRLCLICKMENU
/* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */
clickIsPopup = FALSE;
if ((gui.MacOSHaveCntxMenu) && (mouse_model_popup()))
if (IsShowContextualMenuClick(theEvent))
{
vimMouseButton = MOUSE_RIGHT;
vimModifiers &= ~MOUSE_CTRL;
clickIsPopup = TRUE;
}
#endif
/* Is it a double click ? */
dblClick = ((theEvent->when - lastMouseTick) < GetDblTime());
/* Send the mouse clicj to Vim */
gui_send_mouse_event(vimMouseButton, thePoint.h,
thePoint.v, dblClick, vimModifiers);
/* Create the rectangle around the cursor to detect
* the mouse dragging
*/
#ifdef USE_CTRLCLICKMENU
#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
#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(where, whichWindow)
Point where;
WindowPtr whichWindow;
{
Rect movingLimits;
Rect *movingLimitsPtr = &movingLimits;
/* TODO: may try to prevent move outside screen? */
#ifdef USE_CARBONIZED
movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits);
#else
movingLimitsPtr = &(*GetGrayRgn())->rgnBBox;
#endif
DragWindow(whichWindow, where, movingLimitsPtr);
}
/*
* Handle the click in the grow box
*/
void
gui_mac_doInGrowClick(where, whichWindow)
Point where;
WindowPtr whichWindow;
{
long newSize;
unsigned short newWidth;
unsigned short newHeight;
Rect resizeLimits;
Rect *resizeLimitsPtr = &resizeLimits;
#ifdef USE_CARBONIZED
Rect NewContentRect;
resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits);
#else
resizeLimits = qd.screenBits.bounds;
#endif
/* Set the minimun size */
/* TODO: Should this come from Vim? */
resizeLimits.top = 100;
resizeLimits.left = 100;
#ifdef USE_CARBONIZED
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);
#else
newSize = GrowWindow(whichWindow, where, &resizeLimits);
if (newSize != 0)
{
newWidth = newSize & 0x0000FFFF;
newHeight = (newSize >> 16) & 0x0000FFFF;
gui_mch_set_bg_color(gui.back_pixel);
gui_resize_shell(newWidth, newHeight);
/*
* We need to call gui_set_shellsize as the size
* used by Vim may be smaller than the size selected
* by the user. This cause some overhead
* TODO: add a check inside gui_resize_shell?
*/
gui_set_shellsize(TRUE, FALSE);
/*
* Origin of the code below is unknown.
* Functionality is unknown.
* Time of commented out is unknown.
*/
/* SetPort(wp);
InvalRect(&wp->portRect);
if (isUserWindow(wp)) {
DrawingWindowPeek aWindow = (DrawingWindowPeek)wp;
if (aWindow->toolRoutines.toolWindowResizedProc)
CallToolWindowResizedProc(aWindow->toolRoutines.toolWindowResizedProc, wp);
}*/
};
#endif
}
/*
* Handle the click in the zoom box
*/
#ifdef USE_CARBONIZED
static void
gui_mac_doInZoomClick(theEvent, whichWindow)
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 heigh 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);
}
#endif /* defined(USE_CARBONIZED) */
/*
* ------------------------------------------------------------
* MacOS Event Handling procedure
* ------------------------------------------------------------
*/
/*
* Handle the Update Event
*/
void
gui_mac_doUpdateEvent(event)
EventRecord *event;
{
WindowPtr whichWindow;
GrafPtr savePort;
RgnHandle updateRgn;
#ifdef USE_CARBONIZED
Rect updateRect;
#endif
Rect *updateRectPtr;
Rect rc;
Rect growRect;
RgnHandle saveRgn;
#ifdef USE_CARBONIZED
updateRgn = NewRgn();
if (updateRgn == NULL)
return;
#endif
/* 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 */
#ifdef USE_CARBONIZED
SetPortWindowPort(whichWindow);
#else
SetPort(whichWindow);
#endif
/* Let's update the window */
BeginUpdate(whichWindow);
/* Redraw the biggest rectangle covering the area
* to be updated.
*/
#ifdef USE_CARBONIZED
GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn);
# if 0
/* Would be more appropriate to use the follwing but doesn't
* seem to work under MacOS X (Dany)
*/
GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn);
# endif
#else
updateRgn = whichWindow->visRgn;
#endif
/* Use the HLock useless in Carbon? Is it harmful?*/
HLock((Handle) updateRgn);
#ifdef USE_CARBONIZED
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
#else
updateRectPtr = &(*updateRgn)->rgnBBox;
#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);
#ifdef USE_CARBONIZED
DisposeRgn(updateRgn);
#endif
/* Update scrollbars */
DrawControls(whichWindow);
/* Update the GrowBox */
/* Taken from FAQ 33-27 */
saveRgn = NewRgn();
#ifdef USE_CARBONIZED
GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect);
#else
growRect = whichWindow->portRect;
growRect.top = growRect.bottom - 15;
growRect.left = growRect.right - 15;
#endif
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(event)
EventRecord *event;
{
WindowPtr whichWindow;
whichWindow = (WindowPtr) event->message;
if ((event->modifiers) & activeFlag)
/* Activate */
gui_focus_change(TRUE);
else
{
/* Deactivate */
gui_focus_change(FALSE);
/* DON'T KNOW what the code below was doing
found in the deactivate clause, but the
clause writting TRUE into in_focus (BUG)
*/
#if 0 /* Removed by Dany as per above June 2001 */
a_bool = false;
SetPreserveGlyph(a_bool);
SetOutlinePreferred(a_bool);
#endif
}
}
/*
* Handle the suspend/resume event
* (apply to the application)
*/
void
gui_mac_doSuspendEvent(event)
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/desactivate event
*/
if (event->message & 1)
/* Resume */
gui_focus_change(TRUE);
else
/* Suspend */
gui_focus_change(FALSE);
}
/*
* Handle the key
*/
#ifdef USE_CARBONKEYHANDLER
# define INLINE_KEY_BUFFER_SIZE 80
static pascal OSStatus
gui_mac_doKeyEventCarbon(EventHandlerCallRef nextHandler, EventRef theEvent,
void *data)
{
/* Multibyte-friendly key event handler */
OSStatus e = -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;
size_t encLen;
char_u *to = NULL;
Boolean isSpecial = FALSE;
int i;
/* Mask the mouse (as per user setting) */
if (p_mh)
ObscureCursor();
do
{
if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText,
typeUnicodeText, NULL, 0, &actualSize, NULL))
break;
text = (UniChar *)alloc(actualSize);
if (text)
{
do
{
if (noErr != GetEventParameter(theEvent,
kEventParamTextInputSendText,
typeUnicodeText, NULL, actualSize, NULL, text))
break;
EventRef keyEvent;
if (noErr != GetEventParameter(theEvent,
kEventParamTextInputSendKeyboardEvent,
typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent))
break;
if (noErr != GetEventParameter(keyEvent,
kEventParamKeyModifiers,
typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers))
break;
if (noErr != GetEventParameter(keyEvent,
kEventParamKeyCode,
typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym))
break;
if (noErr != GetEventParameter(keyEvent,
kEventParamKeyMacCharCodes,
typeChar, NULL, sizeof(char), NULL, &charcode))
break;
key_char = charcode;
if (modifiers & controlKey)
{
if ((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;
text[0] = (UniChar)key_char;
modifiers = 0;
}
}
if (modifiers & cmdKey)
#ifndef USE_CMD_KEY
break; /* Let system handle Cmd+... */
#else
{
/* Intercept CMD-. */
if (key_char == '.')
got_int = TRUE;
/* Convert the modifiers */
modifiers = EventModifiers2VimModifiers(modifiers);
/* Following code to simplify and consolidate modifiers
* taken liberally from gui_w48.c */
key_char = simplify_key(key_char, (int *)&modifiers);
/* remove SHIFT for keys that are already shifted, e.g.,
* '(' and '*' */
if (key_char < 0x100 &&
!isalpha(key_char) && isprint(key_char))
modifiers &= ~MOD_MASK_SHIFT;
/* Interpret META, include SHIFT, etc. */
key_char = extract_modifiers(key_char, (int *)&modifiers);
if (key_char == CSI)
key_char = K_CSI;
if (modifiers)
{
result[len++] = CSI;
result[len++] = KS_MODIFIER;
result[len++] = modifiers;
}
isSpecial = TRUE;
}
#endif
else
{
/* 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 *)&modifiers);
isSpecial = TRUE;
break;
}
}
}
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);
e = noErr;
}
while (0);
vim_free(text);
if (e == noErr)
{
/* Fake event to wake up WNE (required to get
* key repeat working */
PostEvent(keyUp, 0);
return noErr;
}
}
}
while (0);
return CallNextEventHandler(nextHandler, theEvent);
}
#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 it's 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
{
#ifdef FEAT_MBYTE
/* 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
#endif
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(theEvent)
EventRecord *theEvent;
{
short thePart;
WindowPtr whichWindow;
thePart = FindWindow(theEvent->where, &whichWindow);
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):
#ifdef USE_CARBONIZED
gui_mac_doInZoomClick(theEvent, whichWindow);
#endif
break;
}
}
/*
* Handle MouseMoved
* [this event is a moving in and out of a region]
*/
void
gui_mac_doMouseMovedEvent(event)
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
#ifdef USE_CTRLCLICKMENU
if (!clickIsPopup)
#endif
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(theEvent)
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);
#ifdef USE_CTRLCLICKMENU
if (clickIsPopup)
{
vimModifiers &= ~MOUSE_CTRL;
clickIsPopup = FALSE;
}
#endif
gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers);
}
#ifdef USE_MOUSEWHEEL
static pascal OSStatus
gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent,
void *data)
{
EventRef bogusEvent;
Point point;
Rect bounds;
UInt32 mod;
SInt32 delta;
int_u vim_mod;
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;
/* post a bogus event to wake up WaitNextEvent */
if (noErr != CreateEvent(NULL, kEventClassMouse, kEventMouseMoved, 0,
kEventAttributeNone, &bogusEvent))
goto bail;
if (noErr != PostEventToQueue(GetMainEventQueue(), bogusEvent,
kEventPriorityLow))
goto bail;
ReleaseEvent(bogusEvent);
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);
return noErr;
bail:
/*
* when we fail give any additional callback handler a chance to perform
* it's actions
*/
return CallNextEventHandler(nextHandler, theEvent);
}
#endif /* defined(USE_MOUSEWHEEL) */
#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(event)
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 meny 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(menuChoice)
long menuChoice;
{
short menu = HiWord(menuChoice);
short item = LoWord(menuChoice);
vimmenu_T *theVimMenu = root_menu;
#ifndef USE_CARBONIZED
MenuHandle appleMenu;
Str255 itemName;
#endif
if (menu == 256) /* TODO: use constant or gui.xyz */
{
if (item == 1)
gui_mch_beep(); /* TODO: Popup dialog or do :intro */
else
{
#ifndef USE_CARBONIZED
/* Desk Accessory doesn't exist in Carbon */
appleMenu = GetMenuHandle(menu);
GetMenuItemText(appleMenu, item, itemName);
(void) OpenDeskAcc(itemName);
#endif
}
}
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(event)
EventRecord *event;
{
OSErr error;
/* Handle contextual menu right now (if needed) */
#ifdef USE_CTRLCLICKMENU
if (gui.MacOSHaveCntxMenu)
if (IsShowContextualMenuClick(event))
{
# if 0
gui_mac_handle_contextual_menu(event);
# else
gui_mac_doMouseDownEvent(event);
# endif
return;
}
#endif
/* 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 get release */
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(font_name)
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) */
#if defined(MACOS_X) && defined(USE_CARBONIZED)
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;
}
#else
GetFNum(pFontName, &font_id);
#endif
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 defined(MACOS_X) && defined(USE_CARBONIZED)
if (ATSUFindFontFromName(&pFontName[1], pFontName[0],
kFontFullName, kFontNoPlatformCode, kFontNoScriptCode,
kFontNoLanguageCode, &fontRef) == noErr)
{
if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr)
font_id = 0;
}
#else
GetFNum(pFontName, &font_id);
#endif
}
#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 defined(MACOS_X) && defined(USE_CARBONIZED)
if (FMGetFontFamilyName(systemFont, systemFontname) != noErr)
return NOFONT;
#else
GetFontName(0, systemFontname);
#endif
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 functionnality
* ------------------------------------------------------------
*/
/*
* 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(argc, argv)
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
#ifndef USE_CARBONIZED
MaxApplZone(); /* What could replace thos */
/* In Carbon, all shared library are automatically load in
* there's no need to init them
*/
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
#else
/* Why did I put that in? (Dany) */
MoreMasterPointers (0x40 * 3); /* we love handles */
#endif
#if 0
InitCursor();
#ifdef USE_CARBONIZED
RegisterAppearanceClient();
#endif
#ifdef USE_AEVENT
(void) InstallAEHandlers();
#endif
#ifdef USE_CTRLCLICKMENU
if (Gestalt(gestaltContextualMenuAttr, &gestalt_rc) == noErr)
gui.MacOSHaveCntxMenu = BitTst(&gestalt_rc, 31-gestaltContextualMenuTrapAvailable);
else
gui.MacOSHaveCntxMenu = false;
if (gui.MacOSHaveCntxMenu)
gui.MacOSHaveCntxMenu = (InitContextualMenus()==noErr);
#endif
#ifdef USE_SIOUX
SIOUXSettings.standalone = false;
SIOUXSettings.initializeTB = false;
SIOUXSettings.setupmenus = false;
SIOUXSettings.asktosaveonclose = false;
SIOUXSettings.showstatusline = true;
SIOUXSettings.toppixel = 300;
SIOUXSettings.leftpixel = 10;
InstallConsole(1); /* fileno(stdout) = 1, on page 430 of MSL C */
printf("Debugging console enabled\n");
/* SIOUXSetTitle((char_u *) "Vim Stdout"); */
#endif
pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
AppendMenu(pomme, "\pAbout VIM");
#ifndef USE_CARBONIZED
AppendMenu(pomme, "\p-");
AppendResMenu(pomme, 'DRVR');
#endif
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
#ifdef USE_CARBONIZED
CreateNewWindow(kDocumentWindowClass,
kWindowResizableAttribute | kWindowCollapseBoxAttribute,
&windRect, &gui.VimWindow);
SetPortWindowPort(gui.VimWindow);
#else
gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true, documentProc,
(WindowPtr) -1L, false, 0);
SetPort(gui.VimWindow);
#endif
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 */
#if TARGET_API_MAC_CARBON
gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
#else
gScrollAction = NewControlActionProc(gui_mac_scroll_action);
gScrollDrag = NewControlActionProc(gui_mac_drag_thumb);
#endif
/* Getting a handle to the Help menu */
#ifdef USE_HELPMENU
# ifdef USE_CARBONIZED
HMGetHelpMenu(&gui.MacOSHelpMenu, NULL);
# else
(void) HMGetHelpMenuHandle(&gui.MacOSHelpMenu);
# endif
if (gui.MacOSHelpMenu != nil)
gui.MacOSHelpItems = CountMenuItems(gui.MacOSHelpMenu);
else
gui.MacOSHelpItems = 0;
#endif
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
* This technic remove the ../Contents/MacOS/etc part
*/
(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 technic return NIL when we disallow_gui */
# endif
exe_name = FullPathFromFSSpec_save(applDir);
#endif
#ifdef USE_VIM_CREATOR_ID
_fcreator = 'VIM!';
_ftype = 'TEXT';
#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 = (char_u **)alloc(count * sizeof(char_u *));
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()
{
/* TODO: Move most of this stuff toward gui_mch_init */
Rect windRect;
MenuHandle pomme;
#ifdef USE_CTRLCLICKMENU
long gestalt_rc;
#endif
#ifdef USE_MOUSEWHEEL
EventTypeSpec eventTypeSpec;
EventHandlerRef mouseWheelHandlerRef;
#endif
#ifdef USE_CARBONKEYHANDLER
EventHandlerRef keyEventHandlerRef;
#endif
#ifdef MACOS_X
if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr)
gMacSystemVersion = 0x1000; /* Default to minimum sensible value */
#endif
#if 1
InitCursor();
#ifdef USE_CARBONIZED
RegisterAppearanceClient();
#endif
#ifdef USE_AEVENT
(void) InstallAEHandlers();
#endif
#ifdef USE_CTRLCLICKMENU
if (Gestalt(gestaltContextualMenuAttr, &gestalt_rc) == noErr)
gui.MacOSHaveCntxMenu = BitTst(&gestalt_rc, 31-gestaltContextualMenuTrapAvailable);
else
gui.MacOSHaveCntxMenu = false;
if (gui.MacOSHaveCntxMenu)
gui.MacOSHaveCntxMenu = (InitContextualMenus()==noErr);
#endif
#ifdef USE_SIOUX
SIOUXSettings.standalone = false;
SIOUXSettings.initializeTB = false;
SIOUXSettings.setupmenus = false;
SIOUXSettings.asktosaveonclose = false;
SIOUXSettings.showstatusline = true;
SIOUXSettings.toppixel = 300;
SIOUXSettings.leftpixel = 10;
InstallConsole(1); /* fileno(stdout) = 1, on page 430 of MSL C */
printf("Debugging console enabled\n");
/* SIOUXSetTitle((char_u *) "Vim Stdout"); */
#endif
pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */
AppendMenu(pomme, "\pAbout VIM");
#ifndef USE_CARBONIZED
AppendMenu(pomme, "\p-");
AppendResMenu(pomme, 'DRVR');
#endif
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,
#ifdef USE_CARBONIZED
zoomDocProc,
#else
documentProc,
#endif
(WindowPtr)-1L, true, 0);
InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler,
gui.VimWindow, NULL);
#ifdef USE_CARBONIZED
SetPortWindowPort(gui.VimWindow);
#else
SetPort(gui.VimWindow);
#endif
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 */
#if TARGET_API_MAC_CARBON
gScrollAction = NewControlActionUPP(gui_mac_scroll_action);
gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb);
#else
gScrollAction = NewControlActionProc(gui_mac_scroll_action);
gScrollDrag = NewControlActionProc(gui_mac_drag_thumb);
#endif
#if defined(USE_CARBONIZED) && defined(MACOS_X)
/* Install Carbon event callbacks. */
(void)InstallFontPanelHandler();
#endif
/* Getting a handle to the Help menu */
#ifdef USE_HELPMENU
# ifdef USE_CARBONIZED
HMGetHelpMenu(&gui.MacOSHelpMenu, NULL);
# else
(void) HMGetHelpMenuHandle(&gui.MacOSHelpMenu);
# endif
if (gui.MacOSHelpMenu != nil)
gui.MacOSHelpItems = CountMenuItems(gui.MacOSHelpMenu);
else
gui.MacOSHelpItems = 0;
#endif
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 approriate 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 defined(FEAT_GUI) && defined(MACOS_X)
/* If Quartz-style text antialiasing is available (see
gui_mch_draw_string() below), enable it for all font sizes. */
vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1");
#endif
#ifdef USE_MOUSEWHEEL
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;
}
#endif
#ifdef USE_CARBONKEYHANDLER
eventTypeSpec.eventClass = kEventClassTextInput;
eventTypeSpec.eventKind = kEventUnicodeForKeyEvent;
keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_doKeyEventCarbon);
if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP, 1,
&eventTypeSpec, NULL, &keyEventHandlerRef))
{
keyEventHandlerRef = NULL;
DisposeEventHandlerUPP(keyEventHandlerUPP);
keyEventHandlerUPP = NULL;
}
#endif
/*
#ifdef FEAT_MBYTE
set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0);
#endif
*/
/* TODO: Load bitmap if using TOOLBAR */
return OK;
}
/*
* Called when the foreground or background color has been changed.
*/
void
gui_mch_new_colors()
{
/* TODO:
* This proc is called when Normal is set to a value
* so what msut be done? I don't know
*/
}
/*
* Open the GUI window which was created by a call to gui_mch_init().
*/
int
gui_mch_open()
{
ShowWindow(gui.VimWindow);
if (gui_win_x != -1 && gui_win_y != -1)
gui_mch_set_winpos(gui_win_x, gui_win_y);
#ifdef USE_CARBONIZED
/*
* 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);
}
#endif
return OK;
}
void
gui_mch_exit(int rc)
{
/* TODO: find out all what is missing here? */
DisposeRgn(cursorRgn);
#ifdef USE_CARBONKEYHANDLER
if (keyEventHandlerUPP)
DisposeEventHandlerUPP(keyEventHandlerUPP);
#endif
#ifdef USE_MOUSEWHEEL
if (mouseWheelHandlerUPP != NULL)
DisposeEventHandlerUPP(mouseWheelHandlerUPP);
#endif
#ifdef USE_ATSUI_DRAWING
if (gFontStyle)
ATSUDisposeStyle(gFontStyle);
#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 */
#ifdef USE_CARBONIZED
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;
#endif
return FAIL;
}
/*
* 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
*/
MoveWindow(gui.VimWindow, x, y, TRUE);
}
void
gui_mch_set_shellsize(
int width,
int height,
int min_width,
int min_height,
int base_width,
int base_height)
{
#ifdef USE_CARBONIZED
CGrafPtr VimPort;
Rect VimBound;
#endif
if (gui.which_scrollbars[SBAR_LEFT])
{
#ifdef USE_CARBONIZED
VimPort = GetWindowPort(gui.VimWindow);
GetPortBounds(VimPort, &VimBound);
VimBound.left = -gui.scrollbar_width; /* + 1;*/
SetPortBounds(VimPort, &VimBound);
/* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
#else
gui.VimWindow->portRect.left = -gui.scrollbar_width; /* + 1;*/
/* SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/
#endif
}
else
{
#ifdef USE_CARBONIZED
VimPort = GetWindowPort(gui.VimWindow);
GetPortBounds(VimPort, &VimBound);
VimBound.left = 0;
SetPortBounds(VimPort, &VimBound);
#else
gui.VimWindow->portRect.left = 0;
#endif;
}
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 udate of those value on suspend/resume.
*/
void
gui_mch_get_screen_dimensions(screen_w, screen_h)
int *screen_w;
int *screen_h;
{
GDHandle dominantDevice = GetMainDevice();
Rect screenRect = (**dominantDevice).gdRect;
*screen_w = screenRect.right - 10;
*screen_h = screenRect.bottom - 40;
}
#if defined(USE_CARBONIZED) && defined(MACOS_X)
/*
* 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;
}
#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(font_name, fontset)
char_u *font_name;
int fontset; /* not used */
{
/* 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
if (gFontStyle == NULL)
{
if (ATSUCreateStyle(&gFontStyle) != noErr)
gFontStyle = NULL;
}
#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);
}
#if defined(USE_CARBONIZED) && defined(MACOS_X)
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 = '_';
}
}
}
#endif
else
{
font = gui_mac_find_font(font_name);
STRNCPY(used_font_name, font_name, sizeof(used_font_name));
used_font_name[sizeof(used_font_name) - 1] = NUL;
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
ATSUFontID fontID;
Fixed fontSize;
ATSStyleRenderingOptions fontOptions;
if (gFontStyle)
{
fontID = font & 0xFFFF;
fontSize = Long2Fix(font >> 16);
/* No antialiasing by default (do not attempt to touch antialising
* options on pre-Jaguar) */
fontOptions =
#ifdef MACOS_X
(gMacSystemVersion >= 0x1020) ?
kATSStyleNoAntiAliasing :
#endif
kATSStyleNoOptions;
ATSUAttributeTag attribTags[] =
{
kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
kATSUMaxATSUITagValue+1
};
ByteCount attribSizes[] =
{
sizeof(ATSUFontID), sizeof(Fixed),
sizeof(ATSStyleRenderingOptions), sizeof font
};
ATSUAttributeValuePtr attribValues[] =
{
&fontID, &fontSize, &fontOptions, &font
};
/* Convert font id to ATSUFontID */
if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr)
{
if (ATSUSetAttributes(gFontStyle,
(sizeof attribTags)/sizeof(ATSUAttributeTag),
attribTags, attribSizes, attribValues) != noErr)
{
ATSUDisposeStyle(gFontStyle);
gFontStyle = NULL;
}
}
}
#endif
return OK;
}
int
gui_mch_adjust_charsize()
{
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(name, giveErrorIfMissing)
char_u *name;
int giveErrorIfMissing;
{
GuiFont font;
font = gui_mac_find_font(name);
if (font == NOFONT)
{
if (giveErrorIfMissing)
EMSG2(_(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(font, name)
GuiFont font;
char_u *name;
{
if (name == NULL)
return NULL;
return vim_strsave(name);
}
#endif
/*
* Set the current text font.
*/
void
gui_mch_set_font(font)
GuiFont font;
{
#ifdef USE_ATSUI_DRAWING
GuiFont currFont;
ByteCount actualFontByteCount;
ATSUFontID fontID;
Fixed fontSize;
ATSStyleRenderingOptions fontOptions;
if (gFontStyle)
{
/* Avoid setting same font again */
if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue+1, sizeof font,
&currFont, &actualFontByteCount) == noErr &&
actualFontByteCount == (sizeof font))
{
if (currFont == font)
return;
}
fontID = font & 0xFFFF;
fontSize = Long2Fix(font >> 16);
/* Respect p_antialias setting only for wide font.
* The reason for doing this at the moment is a bit complicated,
* but it's mainly because a) latin (non-wide) aliased fonts
* look bad in OS X 10.3.x and below (due to a bug in ATS), and
* b) wide multibyte input does not suffer from that problem. */
fontOptions =
#ifdef MACOS_X
(p_antialias && (font == gui.wide_font)) ?
kATSStyleNoOptions : kATSStyleNoAntiAliasing;
#else
kATSStyleNoOptions;
#endif
ATSUAttributeTag attribTags[] =
{
kATSUFontTag, kATSUSizeTag, kATSUStyleRenderingOptionsTag,
kATSUMaxATSUITagValue+1
};
ByteCount attribSizes[] =
{
sizeof(ATSUFontID), sizeof(Fixed),
sizeof(ATSStyleRenderingOptions), sizeof font
};
ATSUAttributeValuePtr attribValues[] =
{
&fontID, &fontSize, &fontOptions, &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 (!gIsFontFallbackSet)
{
/* Setup automatic font substitution. The user's guifontwide
* is tried first, then the system tries other fonts. */
/*
ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag };
ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) };
ATSUCreateFontFallbacks(&gFontFallbacks);
ATSUSetObjFontFallbacks(gFontFallbacks, );
*/
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 0 /* not used */
/*
* Return TRUE if the two fonts given are equivalent.
*/
int
gui_mch_same_font(f1, f2)
GuiFont f1;
GuiFont f2;
{
return f1 == f2;
}
#endif
/*
* If a font is not going to be used, free its structure.
*/
void
gui_mch_free_font(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.
*/
}
static int
hex_digit(c)
int c;
{
if (isdigit(c))
return c - '0';
c = TOLOWER_ASC(c);
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1000;
}
/*
* 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(name)
char_u *name;
{
/* TODO: Add support for the new named color of MacOS 8
*/
RGBColor MacColor;
// guicolor_T color = 0;
typedef struct guicolor_tTable
{
char *name;
guicolor_T color;
} guicolor_tTable;
/*
* The comment at the end of each line is the source
* (Mac, Window, Unix) and the number is the unix rgb.txt value
*/
static guicolor_tTable table[] =
{
{"Black", RGB(0x00, 0x00, 0x00)},
{"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/
{"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/
{"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
{"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/
{"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
{"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/
{"white", RGB(0xFF, 0xFF, 0xFF)},
{"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/
{"red", RGB(0xDD, 0x08, 0x06)}, /*M*/
{"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/
{"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/
{"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/
{"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/
{"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/
{"Green", RGB(0x00, 0x64, 0x11)}, /*M*/
{"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/
{"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */
{"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/
{"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/
{"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/
{"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/
{"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/
{"brown", RGB(0x80, 0x40, 0x40)}, /*W*/
{"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/
{"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/
{"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/
{"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */
{"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */
{"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */
{"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */
{"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/
};
int r, g, b;
int i;
if (name[0] == '#' && strlen((char *) name) == 7)
{
/* Name is in "#rrggbb" format */
r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
if (r < 0 || g < 0 || b < 0)
return INVALCOLOR;
return RGB(r, g, b);
}
else
{
if (STRICMP(name, "hilite") == 0)
{
LMGetHiliteRGB(&MacColor);
return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8));
}
/* Check if the name is one of the colors we know */
for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
if (STRICMP(name, table[i].name) == 0)
return table[i].color;
}
/*
* Last attempt. Look in the file "$VIM/rgb.txt".
*/
{
#define LINE_LEN 100
FILE *fd;
char line[LINE_LEN];
char_u *fname;
#ifdef COLON_AS_PATHSEP
fname = expand_env_save((char_u *)"$VIMRUNTIME:rgb.txt");
#else
fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
#endif
if (fname == NULL)
return INVALCOLOR;
fd = fopen((char *)fname, "rt");
vim_free(fname);
if (fd == NULL)
return INVALCOLOR;
while (!feof(fd))
{
int len;
int pos;
char *color;
fgets(line, LINE_LEN, fd);
len = strlen(line);
if (len <= 1 || line[len-1] != '\n')
continue;
line[len-1] = '\0';
i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
if (i != 3)
continue;
color = line + pos;
if (STRICMP(color, name) == 0)
{
fclose(fd);
return (guicolor_T) RGB(r, g, b);
}
}
fclose(fd);
}
return INVALCOLOR;
}
/*
* Set the current text foreground color.
*/
void
gui_mch_set_fg_color(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(color)
guicolor_T color;
{
RGBColor TheColor;
TheColor.red = Red(color) * 0x0101;
TheColor.green = Green(color) * 0x0101;
TheColor.blue = Blue(color) * 0x0101;
RGBBackColor(&TheColor);
}
/*
* Set the current text speail color. TODO
*/
void
gui_mch_set_sp_color(color)
guicolor_T color;
{
}
void
gui_mch_draw_string(row, col, s, len, flags)
int row;
int col;
char_u *s;
int len;
int flags;
{
#ifdef FEAT_MBYTE
#ifdef USE_ATSUI_DRAWING
/* ATSUI requires utf-16 strings */
UniCharCount utf16_len;
UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len);
utf16_len /= sizeof(UniChar);
#else
char_u *tofree = NULL;
if (output_conv.vc_type != CONV_NONE)
{
tofree = string_convert(&output_conv, s, &len);
if (tofree != NULL)
s = tofree;
}
#endif
#endif
#if defined(FEAT_GUI) && defined(MACOS_X)
/* ATSUI automatically antialiases text */
#ifndef USE_ATSUI_DRAWING
/*
* 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);
}
#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.
*/
#ifdef USE_ATSUI_DRAWING
if ((flags & DRAW_TRANSP) == 0)
#else
if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0)
&& !(flags & DRAW_TRANSP))
#endif
{
Rect rc;
rc.left = FILL_X(col);
rc.top = FILL_Y(row);
#ifdef FEAT_MBYTE
/* Multibyte computation taken from gui_w32.c */
if (has_mbyte)
{
int cell_len = 0;
int n;
/* Compute the length in display cells. */
for (n = 0; n < len; n += MB_BYTE2LEN(s[n]))
cell_len += (*mb_ptr2cells)(s + n);
rc.right = FILL_X(col + cell_len);
}
else
#endif
rc.right = FILL_X(col + len) + (col + len == Columns);
rc.bottom = FILL_Y(row + 1);
EraseRect(&rc);
}
#ifndef USE_ATSUI_DRAWING
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
#endif
#endif
{
/* 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));
#ifdef USE_ATSUI_DRAWING
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);
}
#else
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);
}
#endif
}
#ifdef FEAT_MBYTE
vim_free(tofree);
#endif
}
/*
* Return OK if the key with the termcap name "name" is supported.
*/
int
gui_mch_haskey(name)
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()
{
SysBeep(1); /* Should this be 0? (????) */
}
void
gui_mch_flash(msec)
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(r, c, nr, nc)
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()
{
/* 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()
{
/* TODO */
}
#endif
/*
* Draw a cursor without focus.
*/
void
gui_mch_draw_hollow_cursor(color)
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;
#ifdef FEAT_MBYTE
if (mb_lefthalve(gui.row, gui.col))
rc.right += gui.char_width;
#endif
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(w, h, color)
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()
{
/* 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.
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma profile reset
#endif
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.
*/
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma profile reset
#endif
int
gui_mch_wait_for_chars(wtime)
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 (dragRectControl == kCreateEmpty)
{
dragRgn = NULL;
dragRectControl = kNothing;
}
else*/ 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 accordinly??? */
if (wtime > -1)
sleeppyTick = 60*wtime/1000;
else
sleeppyTick = 32767;
if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn))
{
#ifdef USE_SIOUX
if (!SIOUXHandleOneEvent(&event))
#endif
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;
}
#if defined(__MWERKS__) /* only in Codewarrior */
# pragma profile reset
#endif
/*
* Output routines.
*/
/* Flush any output to the screen */
void
gui_mch_flush()
{
/* 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(row1, col1, row2, col2)
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()
{
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(row, num_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(row, num_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(cbd)
VimClipboard *cbd;
{
Handle textOfClip;
int flavor = 0;
#ifdef USE_CARBONIZED
Size scrapSize;
ScrapFlavorFlags scrapFlags;
ScrapRef scrap = nil;
OSStatus error;
#else
long scrapOffset;
long scrapSize;
#endif
int type;
char *searchCR;
char_u *tempclip;
#ifdef USE_CARBONIZED
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, kScrapFlavorTypeUnicode, &scrapFlags);
if (error != noErr)
return;
error = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &scrapSize);
if (error != noErr)
return;
}
ReserveMem(scrapSize);
#else
/* Call to LoadScrap seem to avoid problem with crash on first paste */
scrapSize = LoadScrap();
scrapSize = GetScrap(nil, 'TEXT', &scrapOffset);
if (scrapSize > 0)
#endif
{
#ifdef USE_CARBONIZED
/* In CARBON we don't need a Handle, a pointer is good */
textOfClip = NewHandle(scrapSize);
/* tempclip = lalloc(scrapSize+1, TRUE); */
#else
textOfClip = NewHandle(0);
#endif
HLock(textOfClip);
#ifdef USE_CARBONIZED
error = GetScrapFlavorData(scrap,
flavor ? VIMSCRAPFLAVOR : kScrapFlavorTypeUnicode,
&scrapSize, *textOfClip);
#else
scrapSize = GetScrap(textOfClip, 'TEXT', &scrapOffset);
#endif
scrapSize -= flavor;
if (flavor)
type = **textOfClip;
else
type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR;
tempclip = lalloc(scrapSize + 1, TRUE);
#if defined(FEAT_MBYTE) && defined(USE_CARBONIZED)
mch_memmove(tempclip, *textOfClip + flavor, scrapSize);
#else
STRNCPY(tempclip, *textOfClip + flavor, scrapSize);
#endif
tempclip[scrapSize] = 0;
searchCR = (char *)tempclip;
while (searchCR != NULL)
{
searchCR = strchr(searchCR, '\r');
if (searchCR != NULL)
searchCR[0] = '\n';
}
#if defined(FEAT_MBYTE) && defined(USE_CARBONIZED)
/* Convert from utf-16 (clipboard) */
size_t encLen = 0;
char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen);
if (to)
{
scrapSize = encLen;
vim_free(tempclip);
tempclip = to;
}
#endif
clip_yank_selection(type, tempclip, scrapSize, cbd);
vim_free(tempclip);
HUnlock(textOfClip);
DisposeHandle(textOfClip);
}
}
void
clip_mch_lose_selection(cbd)
VimClipboard *cbd;
{
/*
* TODO: Really nothing to do?
*/
}
int
clip_mch_own_selection(cbd)
VimClipboard *cbd;
{
return OK;
}
/*
* Send the current selection to the clipboard.
*/
void
clip_mch_set_selection(cbd)
VimClipboard *cbd;
{
Handle textOfClip;
long scrapSize;
int type;
#ifdef USE_CARBONIZED
ScrapRef scrap;
#endif
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);
#if defined(FEAT_MBYTE) && defined(USE_CARBONIZED)
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)
{
#ifdef USE_CARBONIZED
ClearCurrentScrap();
#else
ZeroScrap();
#endif
#ifdef USE_CARBONIZED
textOfClip = NewHandle(scrapSize + 1);
#else
textOfClip = NewHandle(scrapSize);
#endif
HLock(textOfClip);
#ifdef USE_CARBONIZED
**textOfClip = type;
mch_memmove(*textOfClip + 1, str, scrapSize);
GetCurrentScrap(&scrap);
PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone,
scrapSize, *textOfClip + 1);
PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone,
scrapSize + 1, *textOfClip);
#else
STRNCPY(*textOfClip, str, scrapSize);
PutScrap(scrapSize, 'TEXT', *textOfClip);
#endif
HUnlock(textOfClip);
DisposeHandle(textOfClip);
}
vim_free(str);
}
void
gui_mch_set_text_area_pos(x, y, w, h)
int x;
int y;
int w;
int h;
{
Rect VimBound;
/* HideWindow(gui.VimWindow); */
#ifdef USE_CARBONIZED
GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
#else
VimBound = gui.VimWindow->portRect;
#endif
if (gui.which_scrollbars[SBAR_LEFT])
{
VimBound.left = -gui.scrollbar_width + 1;
}
else
{
VimBound.left = 0;
}
#ifdef USE_CARBONIZED
SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound);
#endif
ShowWindow(gui.VimWindow);
}
/*
* Menu stuff.
*/
void
gui_mch_enable_menu(flag)
int flag;
{
/*
* Menu is always active in itself
* (maybe we should only disable a vim menu
* and keep standard menu)
*
*/
}
void
gui_mch_set_menu_pos(x, y, w, h)
int x;
int y;
int w;
int h;
{
/*
* The menu is always at the top of the screen
* Maybe a futur version will permit a menu in the window
*
*/
}
/*
* Add a sub menu to the menu bar.
*/
void
gui_mch_add_menu(menu, idx)
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 */
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
CFStringRef name;
#else
char_u *name;
#endif
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 */
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
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 */
#ifdef USE_HELPMENU
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
if (menu->priority == 9999)
#else
if (STRNCMP(name, "\4Help", 5) == 0)
#endif
{
menu->submenu_id = kHMHelpMenuID;
menu->submenu_handle = gui.MacOSHelpMenu;
}
else
#endif
{
/* Carbon suggest use of
* OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *);
* OSStatus SetMenuTitle(MenuRef, ConstStr255Param title);
*/
menu->submenu_id = next_avail_id;
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr)
SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name);
#else
menu->submenu_handle = NewMenu(menu->submenu_id, name);
#endif
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 unless it's the Help menu */
#ifdef USE_HELPMENU
if (menu->submenu_id != kHMHelpMenuID)
#endif
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 */
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
#else
SetMenuItemText(parent->submenu_handle, idx+1, name);
#endif
SetItemCmd(parent->submenu_handle, idx+1, 0x1B);
SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id);
InsertMenu(menu->submenu_handle, hierMenu);
}
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
CFRelease(name);
#else
vim_free(name);
#endif
#if 0
/* Done by Vim later on */
DrawMenuBar();
#endif
}
/*
* Add a menu item to a menu
*/
void
gui_mch_add_menu_item(menu, idx)
vimmenu_T *menu;
int idx;
{
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
CFStringRef name;
#else
char_u *name;
#endif
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 */
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
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;
#ifdef USE_HELPMENU
/* The index in the help menu are offseted */
if (parent->submenu_id == kHMHelpMenuID)
idx += gui.MacOSHelpItems;
#endif
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, /*keycode=*/0);
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. */
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name);
#else
SetMenuItemText(parent->submenu_handle, idx+1, name);
#endif
#if 0
/* Called by Vim */
DrawMenuBar();
#endif
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
CFRelease(name);
#else
/* TODO: Can name be freed? */
vim_free(name);
#endif
}
void
gui_mch_toggle_tearoffs(enable)
int enable;
{
/* no tearoff menus */
}
/*
* Destroy the machine specific menu widget.
*/
void
gui_mch_destroy_menu(menu)
vimmenu_T *menu;
{
short index = gui_mac_get_menu_item_index(menu);
if (index > 0)
{
if (menu->parent)
{
#ifdef USE_HELPMENU
if (menu->parent->submenu_handle != nil) /*gui.MacOSHelpMenu)*/
#endif
{
/* 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 USE_HELPMENU
# ifdef DEBUG_MAC_MENU
else
{
printf("gmdm 1\n");
}
# endif
#endif
}
#ifdef DEBUG_MAC_MENU
else
{
printf("gmdm 2\n");
}
#endif
}
else
{
/* Do not delete the Help Menu */
#ifdef USE_HELPMENU
if (menu->submenu_id != kHMHelpMenuID)
#endif
{
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(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(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()
{
DrawMenuBar();
}
/*
* Scrollbar stuff.
*/
void
gui_mch_enable_scrollbar(sb, flag)
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(sb, val, size, max)
scrollbar_T *sb;
long val;
long size;
long max;
{
SetControl32BitMaximum (sb->id, max);
SetControl32BitMinimum (sb->id, 0);
SetControl32BitValue (sb->id, val);
#ifdef DEBUG_MAC_SB
printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max);
#endif
}
void
gui_mch_set_scrollbar_pos(sb, x, y, w, h)
scrollbar_T *sb;
int x;
int y;
int w;
int h;
{
gui_mch_set_bg_color(gui.back_pixel);
/* 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);
}*/
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
}
void
gui_mch_create_scrollbar(sb, orient)
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 */
#ifdef USE_CARBONIZED
kControlScrollBarLiveProc,
#else
scrollBarProc,
#endif
(long) sb->ident);
#ifdef DEBUG_MAC_SB
printf("create_sb (%x) %x\n",sb->id, orient);
#endif
}
void
gui_mch_destroy_scrollbar(sb)
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
}
/*
* 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)
{
/* TODO: TODO: TODO: TODO: */
/* blink_waittime = wait;
blink_ontime = on;
blink_offtime = off;*/
}
/*
* Stop the cursor blinking. Show the cursor if it wasn't shown.
*/
void
gui_mch_stop_blink()
{
gui_update_cursor(TRUE, FALSE);
/* TODO: TODO: TODO: TODO: */
/* gui_w32_rm_blink_timer();
if (blink_state == BLINK_OFF)
gui_update_cursor(TRUE, FALSE);
blink_state = BLINK_NONE;*/
}
/*
* Start the cursor blinking. If it was already blinking, this restarts the
* waiting time and shows the cursor.
*/
void
gui_mch_start_blink()
{
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 (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);
}*/
}
/*
* Return the RGB value of a pixel as long.
*/
long_u
gui_mch_get_rgb(guicolor_T pixel)
{
return (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)
{
#if defined (USE_NAVIGATION_SERVICE) || defined (USE_CARBONIZED)
/* TODO: Add Ammon's safety checl (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;
#else
SFTypeList fileTypes;
StandardFileReply reply;
Str255 Prompt;
Str255 DefaultName;
Str255 Directory;
/* TODO: split dflt in path and filename */
(void) C2PascalString(title, &Prompt);
(void) C2PascalString(dflt, &DefaultName);
(void) C2PascalString(initdir, &Directory);
if (saving)
{
/* Use a custon filter instead of nil FAQ 9-4 */
StandardPutFile(Prompt, DefaultName, &reply);
if (!reply.sfGood)
return NULL;
}
else
{
StandardGetFile(nil, -1, fileTypes, &reply);
if (!reply.sfGood)
return NULL;
}
/* Work fine but append a : for new file */
return (FullPathFromFSSpec_save(reply.sfFile));
/* Shorten the file name if possible */
/* mch_dirname(IObuff, IOSIZE);
p = shorten_fname(fileBuf, IObuff);
if (p == NULL)
p = fileBuf;
return vim_strsave(p);
*/
#endif
}
#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);
}
int
gui_mch_dialog(
int type,
char_u *title,
char_u *message,
char_u *buttons,
int dfltbutton,
char_u *textfield)
{
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;
Rect box;
short button;
short lastButton;
short itemType;
short useIcon;
short width;
short totalButtonWidth = 0; /* the width of all button together incuding 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;
/* 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);
#ifdef USE_CARBONIZED
SetPortDialogPort(theDialog);
#else
SetPort(theDialog);
#endif
/* 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;
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;
}
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;
/* Limite 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: useIcon = kNoteIcon;
case VIM_ERROR: useIcon = kStopIcon;
case VIM_WARNING: useIcon = kCautionIcon;
case VIM_INFO: useIcon = kNoteIcon;
case VIM_QUESTION: useIcon = kNoteIcon;
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 convet 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);
}
/* 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 button the same lenght */
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);
#ifdef USE_CARBONIZED
/* Magic resize */
AutoSizeDialog(theDialog);
/* Need a horizontal resize anyway so not that useful */
#endif
/* Display it */
ShowWindow(theWindow);
/* BringToFront(theWindow); */
SelectWindow(theWindow);
/* DrawDialog(theDialog); */
#if 0
GetPort(&oldPort);
#ifdef USE_CARBONIZED
SetPortDialogPort(theDialog);
#else
SetPort(theDialog);
#endif
#endif
/* Hang until one of the button is hit */
do
{
ModalDialog(nil, &itemHit);
} while ((itemHit < 1) || (itemHit > lastButton));
/* 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
STRNCPY(textfield, &name[1], name[0]);
textfield[name[0]] = NUL;
}
/* Restore the original graphical port */
SetPort(oldPort);
/* Get ride of th edialog (free memory) */
DisposeDialog(theDialog);
return itemHit;
/*
* Usefull 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 lenght
*/
}
#endif /* FEAT_DIALOG_GUI */
/*
* Display the saved error message(s).
*/
#ifdef USE_MCH_ERRMSG
void
display_errors()
{
char *p;
char_u pError[256];
if (error_ga.ga_data != NULL)
{
/* 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(x, y)
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 devoice with one button.
* This will probably be the standad mouse
* startat 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(menu)
vimmenu_T *menu;
{
#ifdef USE_CTRLCLICKMENU
/*
* 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, kCMHelpItemNoHelp, 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*/
#endif
}
#if defined(FEAT_CW_EDITOR) || defined(PROTO)
/* TODO: Is it need for MACOS_X? (Dany) */
void
mch_post_buffer_write(buf_T *buf)
{
# ifdef USE_SIOUX
printf("Writing Buf...\n");
# endif
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(title, icon)
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.
*/
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
CFStringRef windowTitle;
size_t windowTitleLen;
#else
char_u *pascalTitle;
#endif
if (title == NULL) /* nothing to do */
return;
#if defined(USE_CARBONIZED) && defined(FEAT_MBYTE)
windowTitleLen = STRLEN(title);
windowTitle = 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
/*
* Transfered from os_mac.c for MacOS X using os_unix.c prep work
*/
int
C2PascalString(CString, PascalString)
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(file, fileFSSpec)
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 fuill 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 */
STRNCPY(filenamePtr, &file.name[1], file.name[0]);
filenamePtr[file.name[0]] = 0; /* NULL terminate the string */
/* 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 function used here are available in Carbon, but
* do nothing une 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 = irrevelant 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);
STRNCPY(filenamePtr, &directoryName[1], directoryName[0]);
filenamePtr[directoryName[0]] = 0; /* NULL terminate the string */
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 = irrevelant 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 refering to the boot volume */
#ifdef USE_UNIXFILENAME
if (file.vRefNum != dfltVol_vRefNum)
#endif
{
/* Add the volume name */
STRCPY(temporaryPtr, filenamePtr);
STRNCPY(filenamePtr, &directoryName[1], directoryName[0]);
filenamePtr[directoryName[0]] = 0; /* NULL terminate the string */
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_IM_CONTROL) || defined(PROTO)
/*
* Input Method Control functions.
*/
/*
* Notify cursor position to IM.
*/
void
im_set_position(int row, int col)
{
/* TODO: Implement me! */
}
/*
* Set IM status on ("active" is TRUE) or off ("active" is FALSE).
*/
void
im_set_active(int active)
{
KeyScript(active ? smKeySysScript : smKeyRoman);
}
/*
* Get IM status. When IM is on, return not 0. Else return 0.
*/
int
im_get_status()
{
SInt32 script = GetScriptManagerVariable(smKeyScript);
return (script != smRoman
&& script == GetScriptManagerVariable(smSysScript)) ? 1 : 0;
}
#endif /* defined(USE_IM_CONTROL) || defined(PROTO) */