1/* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * GUI/Motif support by Robert Webb 5 * Macintosh port by Dany St-Amant 6 * and Axel Kielhorn 7 * Port to MPW by Bernhard Pruemmer 8 * Initial Carbon port by Ammon Skidmore 9 * 10 * Do ":help uganda" in Vim to read copying and usage conditions. 11 * Do ":help credits" in Vim to see a list of people who contributed. 12 * See README.txt for an overview of the Vim source code. 13 */ 14 15/* 16 * NOTES: - Vim 7+ does not support classic MacOS. Please use Vim 6.x 17 * - Comments mentioning FAQ refer to the book: 18 * "Macworld Mac Programming FAQs" from "IDG Books" 19 */ 20 21/* 22 * TODO: Change still to merge from the macvim's iDisk 23 * 24 * error_ga, mch_errmsg, Navigation's changes in gui_mch_browse 25 * uses of MenuItemIndex, changes in gui_mch_set_shellsize, 26 * ScrapManager error handling. 27 * Comments about function remaining to Carbonize. 28 * 29 */ 30 31/* TODO (Jussi) 32 * * Clipboard does not work (at least some cases) 33 * * ATSU font rendering has some problems 34 * * Investigate and remove dead code (there is still lots of that) 35 */ 36 37#include <Devices.h> /* included first to avoid CR problems */ 38#include "vim.h" 39 40#define USE_CARBONIZED 41#define USE_AEVENT /* Enable AEVENT */ 42#undef USE_OFFSETED_WINDOW /* Debugging feature: start Vim window OFFSETed */ 43 44/* Compile as CodeWarior External Editor */ 45#if defined(FEAT_CW_EDITOR) && !defined(USE_AEVENT) 46# define USE_AEVENT /* Need Apple Event Support */ 47#endif 48 49/* Vim's Scrap flavor. */ 50#define VIMSCRAPFLAVOR 'VIM!' 51#ifdef FEAT_MBYTE 52# define SCRAPTEXTFLAVOR kScrapFlavorTypeUnicode 53#else 54# define SCRAPTEXTFLAVOR kScrapFlavorTypeText 55#endif 56 57static EventHandlerUPP mouseWheelHandlerUPP = NULL; 58SInt32 gMacSystemVersion; 59 60#ifdef MACOS_CONVERT 61# define USE_CARBONKEYHANDLER 62 63static int im_is_active = FALSE; 64#if 0 65 /* TODO: Implement me! */ 66static int im_start_row = 0; 67static int im_start_col = 0; 68#endif 69 70#define NR_ELEMS(x) (sizeof(x) / sizeof(x[0])) 71 72static TSMDocumentID gTSMDocument; 73 74static void im_on_window_switch(int active); 75static EventHandlerUPP keyEventHandlerUPP = NULL; 76static EventHandlerUPP winEventHandlerUPP = NULL; 77 78static pascal OSStatus gui_mac_handle_window_activate( 79 EventHandlerCallRef nextHandler, EventRef theEvent, void *data); 80 81static pascal OSStatus gui_mac_handle_text_input( 82 EventHandlerCallRef nextHandler, EventRef theEvent, void *data); 83 84static pascal OSStatus gui_mac_update_input_area( 85 EventHandlerCallRef nextHandler, EventRef theEvent); 86 87static pascal OSStatus gui_mac_unicode_key_event( 88 EventHandlerCallRef nextHandler, EventRef theEvent); 89 90#endif 91 92 93/* Include some file. TODO: move into os_mac.h */ 94#include <Menus.h> 95#include <Resources.h> 96#include <Processes.h> 97#ifdef USE_AEVENT 98# include <AppleEvents.h> 99# include <AERegistry.h> 100#endif 101# include <Gestalt.h> 102#if UNIVERSAL_INTERFACES_VERSION >= 0x0330 103# include <ControlDefinitions.h> 104# include <Navigation.h> /* Navigation only part of ?? */ 105#endif 106 107/* Help Manager (balloon.h, HM prefixed functions) are not supported 108 * under Carbon (Jussi) */ 109# if 0 110/* New Help Interface for Mac, not implemented yet.*/ 111# include <MacHelp.h> 112# endif 113 114/* 115 * These seem to be rectangle options. Why are they not found in 116 * headers? (Jussi) 117 */ 118#define kNothing 0 119#define kCreateEmpty 2 /*1*/ 120#define kCreateRect 2 121#define kDestroy 3 122 123/* 124 * Dany: Don't like those... 125 */ 126#define topLeft(r) (((Point*)&(r))[0]) 127#define botRight(r) (((Point*)&(r))[1]) 128 129 130/* Time of last mouse click, to detect double-click */ 131static long lastMouseTick = 0; 132 133/* ??? */ 134static RgnHandle cursorRgn; 135static RgnHandle dragRgn; 136static Rect dragRect; 137static short dragRectEnbl; 138static short dragRectControl; 139 140/* This variable is set when waiting for an event, which is the only moment 141 * scrollbar dragging can be done directly. It's not allowed while commands 142 * are executed, because it may move the cursor and that may cause unexpected 143 * problems (e.g., while ":s" is working). 144 */ 145static int allow_scrollbar = FALSE; 146 147/* Last mouse click caused contextual menu, (to provide proper release) */ 148static short clickIsPopup; 149 150/* Feedback Action for Scrollbar */ 151ControlActionUPP gScrollAction; 152ControlActionUPP gScrollDrag; 153 154/* Keeping track of which scrollbar is being dragged */ 155static ControlHandle dragged_sb = NULL; 156 157/* Vector of char_u --> control index for hotkeys in dialogs */ 158static short *gDialogHotKeys; 159 160static struct 161{ 162 FMFontFamily family; 163 FMFontSize size; 164 FMFontStyle style; 165 Boolean isPanelVisible; 166} gFontPanelInfo = { 0, 0, 0, false }; 167 168#ifdef MACOS_CONVERT 169# define USE_ATSUI_DRAWING 170int p_macatsui_last; 171ATSUStyle gFontStyle; 172# ifdef FEAT_MBYTE 173ATSUStyle gWideFontStyle; 174# endif 175Boolean gIsFontFallbackSet; 176UInt32 useAntialias_cached = 0x0; 177#endif 178 179/* Colors Macros */ 180#define RGB(r,g,b) ((r) << 16) + ((g) << 8) + (b) 181#define Red(c) ((c & 0x00FF0000) >> 16) 182#define Green(c) ((c & 0x0000FF00) >> 8) 183#define Blue(c) ((c & 0x000000FF) >> 0) 184 185/* Key mapping */ 186 187#define vk_Esc 0x35 /* -> 1B */ 188 189#define vk_F1 0x7A /* -> 10 */ 190#define vk_F2 0x78 /*0x63*/ 191#define vk_F3 0x63 /*0x76*/ 192#define vk_F4 0x76 /*0x60*/ 193#define vk_F5 0x60 /*0x61*/ 194#define vk_F6 0x61 /*0x62*/ 195#define vk_F7 0x62 /*0x63*/ /*?*/ 196#define vk_F8 0x64 197#define vk_F9 0x65 198#define vk_F10 0x6D 199#define vk_F11 0x67 200#define vk_F12 0x6F 201#define vk_F13 0x69 202#define vk_F14 0x6B 203#define vk_F15 0x71 204 205#define vk_Clr 0x47 /* -> 1B (ESC) */ 206#define vk_Enter 0x4C /* -> 03 */ 207 208#define vk_Space 0x31 /* -> 20 */ 209#define vk_Tab 0x30 /* -> 09 */ 210#define vk_Return 0x24 /* -> 0D */ 211/* This is wrong for OSX, what is it for? */ 212#define vk_Delete 0X08 /* -> 08 BackSpace */ 213 214#define vk_Help 0x72 /* -> 05 */ 215#define vk_Home 0x73 /* -> 01 */ 216#define vk_PageUp 0x74 /* -> 0D */ 217#define vk_FwdDelete 0x75 /* -> 7F */ 218#define vk_End 0x77 /* -> 04 */ 219#define vk_PageDown 0x79 /* -> 0C */ 220 221#define vk_Up 0x7E /* -> 1E */ 222#define vk_Down 0x7D /* -> 1F */ 223#define vk_Left 0x7B /* -> 1C */ 224#define vk_Right 0x7C /* -> 1D */ 225 226#define vk_Undo vk_F1 227#define vk_Cut vk_F2 228#define vk_Copy vk_F3 229#define vk_Paste vk_F4 230#define vk_PrintScreen vk_F13 231#define vk_SCrollLock vk_F14 232#define vk_Pause vk_F15 233#define vk_NumLock vk_Clr 234#define vk_Insert vk_Help 235 236#define KeySym char 237 238static struct 239{ 240 KeySym key_sym; 241 char_u vim_code0; 242 char_u vim_code1; 243} special_keys[] = 244{ 245 {vk_Up, 'k', 'u'}, 246 {vk_Down, 'k', 'd'}, 247 {vk_Left, 'k', 'l'}, 248 {vk_Right, 'k', 'r'}, 249 250 {vk_F1, 'k', '1'}, 251 {vk_F2, 'k', '2'}, 252 {vk_F3, 'k', '3'}, 253 {vk_F4, 'k', '4'}, 254 {vk_F5, 'k', '5'}, 255 {vk_F6, 'k', '6'}, 256 {vk_F7, 'k', '7'}, 257 {vk_F8, 'k', '8'}, 258 {vk_F9, 'k', '9'}, 259 {vk_F10, 'k', ';'}, 260 261 {vk_F11, 'F', '1'}, 262 {vk_F12, 'F', '2'}, 263 {vk_F13, 'F', '3'}, 264 {vk_F14, 'F', '4'}, 265 {vk_F15, 'F', '5'}, 266 267/* {XK_Help, '%', '1'}, */ 268/* {XK_Undo, '&', '8'}, */ 269/* {XK_BackSpace, 'k', 'b'}, */ 270#ifndef MACOS_X 271 {vk_Delete, 'k', 'b'}, 272#endif 273 {vk_Insert, 'k', 'I'}, 274 {vk_FwdDelete, 'k', 'D'}, 275 {vk_Home, 'k', 'h'}, 276 {vk_End, '@', '7'}, 277/* {XK_Prior, 'k', 'P'}, */ 278/* {XK_Next, 'k', 'N'}, */ 279/* {XK_Print, '%', '9'}, */ 280 281 {vk_PageUp, 'k', 'P'}, 282 {vk_PageDown, 'k', 'N'}, 283 284 /* End of list marker: */ 285 {(KeySym)0, 0, 0} 286}; 287 288/* 289 * ------------------------------------------------------------ 290 * Forward declaration (for those needed) 291 * ------------------------------------------------------------ 292 */ 293 294#ifdef USE_AEVENT 295OSErr HandleUnusedParms(const AppleEvent *theAEvent); 296#endif 297 298#ifdef FEAT_GUI_TABLINE 299static void initialise_tabline(void); 300static WindowRef drawer = NULL; // TODO: put into gui.h 301#endif 302 303#ifdef USE_ATSUI_DRAWING 304static void gui_mac_set_font_attributes(GuiFont font); 305static void gui_mac_dispose_atsui_style(void); 306#endif 307 308/* 309 * ------------------------------------------------------------ 310 * Conversion Utility 311 * ------------------------------------------------------------ 312 */ 313 314/* 315 * C2Pascal_save 316 * 317 * Allocate memory and convert the C-String passed in 318 * into a pascal string 319 * 320 */ 321 322 char_u * 323C2Pascal_save(char_u *Cstring) 324{ 325 char_u *PascalString; 326 int len; 327 328 if (Cstring == NULL) 329 return NULL; 330 331 len = STRLEN(Cstring); 332 333 if (len > 255) /* Truncate if necessary */ 334 len = 255; 335 336 PascalString = alloc(len + 1); 337 if (PascalString != NULL) 338 { 339 mch_memmove(PascalString + 1, Cstring, len); 340 PascalString[0] = len; 341 } 342 343 return PascalString; 344} 345 346/* 347 * C2Pascal_save_and_remove_backslash 348 * 349 * Allocate memory and convert the C-String passed in 350 * into a pascal string. Also remove the backslash at the same time 351 * 352 */ 353 354 char_u * 355C2Pascal_save_and_remove_backslash(char_u *Cstring) 356{ 357 char_u *PascalString; 358 int len; 359 char_u *p, *c; 360 361 len = STRLEN(Cstring); 362 363 if (len > 255) /* Truncate if necessary */ 364 len = 255; 365 366 PascalString = alloc(len + 1); 367 if (PascalString != NULL) 368 { 369 for (c = Cstring, p = PascalString+1, len = 0; (*c != 0) && (len < 255); c++) 370 { 371 if ((*c == '\\') && (c[1] != 0)) 372 { 373 c++; 374 } 375 *p = *c; 376 p++; 377 len++; 378 } 379 PascalString[0] = len; 380 } 381 382 return PascalString; 383} 384 385/* 386 * Convert the modifiers of an Event into vim's modifiers (mouse) 387 */ 388 389 int_u 390EventModifiers2VimMouseModifiers(EventModifiers macModifiers) 391{ 392 int_u vimModifiers = 0x00; 393 394 if (macModifiers & (shiftKey | rightShiftKey)) 395 vimModifiers |= MOUSE_SHIFT; 396 if (macModifiers & (controlKey | rightControlKey)) 397 vimModifiers |= MOUSE_CTRL; 398 if (macModifiers & (optionKey | rightOptionKey)) 399 vimModifiers |= MOUSE_ALT; 400#if 0 401 /* Not yet supported */ 402 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */ 403 vimModifiers |= MOUSE_CMD; 404#endif 405 return (vimModifiers); 406} 407 408/* 409 * Convert the modifiers of an Event into vim's modifiers (keys) 410 */ 411 412 static int_u 413EventModifiers2VimModifiers(EventModifiers macModifiers) 414{ 415 int_u vimModifiers = 0x00; 416 417 if (macModifiers & (shiftKey | rightShiftKey)) 418 vimModifiers |= MOD_MASK_SHIFT; 419 if (macModifiers & (controlKey | rightControlKey)) 420 vimModifiers |= MOD_MASK_CTRL; 421 if (macModifiers & (optionKey | rightOptionKey)) 422 vimModifiers |= MOD_MASK_ALT; 423#ifdef USE_CMD_KEY 424 if (macModifiers & (cmdKey)) /* There's no rightCmdKey */ 425 vimModifiers |= MOD_MASK_CMD; 426#endif 427 return (vimModifiers); 428} 429 430/* Convert a string representing a point size into pixels. The string should 431 * be a positive decimal number, with an optional decimal point (eg, "12", or 432 * "10.5"). The pixel value is returned, and a pointer to the next unconverted 433 * character is stored in *end. The flag "vertical" says whether this 434 * calculation is for a vertical (height) size or a horizontal (width) one. 435 * 436 * From gui_w48.c 437 */ 438 static int 439points_to_pixels(char_u *str, char_u **end, int vertical) 440{ 441 int pixels; 442 int points = 0; 443 int divisor = 0; 444 445 while (*str) 446 { 447 if (*str == '.' && divisor == 0) 448 { 449 /* Start keeping a divisor, for later */ 450 divisor = 1; 451 continue; 452 } 453 454 if (!isdigit(*str)) 455 break; 456 457 points *= 10; 458 points += *str - '0'; 459 divisor *= 10; 460 461 ++str; 462 } 463 464 if (divisor == 0) 465 divisor = 1; 466 467 pixels = points/divisor; 468 *end = str; 469 return pixels; 470} 471 472#ifdef MACOS_CONVERT 473/* 474 * Deletes all traces of any Windows-style mnemonic text (including any 475 * parentheses) from a menu item and returns the cleaned menu item title. 476 * The caller is responsible for releasing the returned string. 477 */ 478 static CFStringRef 479menu_title_removing_mnemonic(vimmenu_T *menu) 480{ 481 CFStringRef name; 482 size_t menuTitleLen; 483 CFIndex displayLen; 484 CFRange mnemonicStart; 485 CFRange mnemonicEnd; 486 CFMutableStringRef cleanedName; 487 488 menuTitleLen = STRLEN(menu->dname); 489 name = (CFStringRef) mac_enc_to_cfstring(menu->dname, menuTitleLen); 490 491 if (name) 492 { 493 /* Simple mnemonic-removal algorithm, assumes single parenthesized 494 * mnemonic character towards the end of the menu text */ 495 mnemonicStart = CFStringFind(name, CFSTR("("), kCFCompareBackwards); 496 displayLen = CFStringGetLength(name); 497 498 if (mnemonicStart.location != kCFNotFound 499 && (mnemonicStart.location + 2) < displayLen 500 && CFStringGetCharacterAtIndex(name, 501 mnemonicStart.location + 1) == (UniChar)menu->mnemonic) 502 { 503 if (CFStringFindWithOptions(name, CFSTR(")"), 504 CFRangeMake(mnemonicStart.location + 1, 505 displayLen - mnemonicStart.location - 1), 506 kCFCompareBackwards, &mnemonicEnd) && 507 (mnemonicStart.location + 2) == mnemonicEnd.location) 508 { 509 cleanedName = CFStringCreateMutableCopy(NULL, 0, name); 510 if (cleanedName) 511 { 512 CFStringDelete(cleanedName, 513 CFRangeMake(mnemonicStart.location, 514 mnemonicEnd.location + 1 - 515 mnemonicStart.location)); 516 517 CFRelease(name); 518 name = cleanedName; 519 } 520 } 521 } 522 } 523 524 return name; 525} 526#endif 527 528/* 529 * Convert a list of FSSpec aliases into a list of fullpathname 530 * character strings. 531 */ 532 533 char_u ** 534new_fnames_from_AEDesc(AEDesc *theList, long *numFiles, OSErr *error) 535{ 536 char_u **fnames = NULL; 537 OSErr newError; 538 long fileCount; 539 FSSpec fileToOpen; 540 long actualSize; 541 AEKeyword dummyKeyword; 542 DescType dummyType; 543 544 /* Get number of files in list */ 545 *error = AECountItems(theList, numFiles); 546 if (*error) 547 return fnames; 548 549 /* Allocate the pointer list */ 550 fnames = (char_u **) alloc(*numFiles * sizeof(char_u *)); 551 552 /* Empty out the list */ 553 for (fileCount = 0; fileCount < *numFiles; fileCount++) 554 fnames[fileCount] = NULL; 555 556 /* Scan the list of FSSpec */ 557 for (fileCount = 1; fileCount <= *numFiles; fileCount++) 558 { 559 /* Get the alias for the nth file, convert to an FSSpec */ 560 newError = AEGetNthPtr(theList, fileCount, typeFSS, 561 &dummyKeyword, &dummyType, 562 (Ptr) &fileToOpen, sizeof(FSSpec), &actualSize); 563 if (newError) 564 { 565 /* Caller is able to clean up */ 566 /* TODO: Should be clean up or not? For safety. */ 567 return fnames; 568 } 569 570 /* Convert the FSSpec to a pathname */ 571 fnames[fileCount - 1] = FullPathFromFSSpec_save(fileToOpen); 572 } 573 574 return (fnames); 575} 576 577/* 578 * ------------------------------------------------------------ 579 * CodeWarrior External Editor Support 580 * ------------------------------------------------------------ 581 */ 582#ifdef FEAT_CW_EDITOR 583 584/* 585 * Handle the Window Search event from CodeWarrior 586 * 587 * Description 588 * ----------- 589 * 590 * The IDE sends the Window Search AppleEvent to the editor when it 591 * needs to know whether a particular file is open in the editor. 592 * 593 * Event Reply 594 * ----------- 595 * 596 * None. Put data in the location specified in the structure received. 597 * 598 * Remarks 599 * ------- 600 * 601 * When the editor receives this event, determine whether the specified 602 * file is open. If it is, return the modification date/time for that file 603 * in the appropriate location specified in the structure. If the file is 604 * not opened, put the value fnfErr(file not found) in that location. 605 * 606 */ 607 608typedef struct WindowSearch WindowSearch; 609struct WindowSearch /* for handling class 'KAHL', event 'SRCH', keyDirectObject typeChar*/ 610{ 611 FSSpec theFile; // identifies the file 612 long *theDate; // where to put the modification date/time 613}; 614 615 pascal OSErr 616Handle_KAHL_SRCH_AE( 617 const AppleEvent *theAEvent, 618 AppleEvent *theReply, 619 long refCon) 620{ 621 OSErr error = noErr; 622 buf_T *buf; 623 int foundFile = false; 624 DescType typeCode; 625 WindowSearch SearchData; 626 Size actualSize; 627 628 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &SearchData, sizeof(WindowSearch), &actualSize); 629 if (error) 630 return error; 631 632 error = HandleUnusedParms(theAEvent); 633 if (error) 634 return error; 635 636 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 637 if (buf->b_ml.ml_mfp != NULL 638 && SearchData.theFile.parID == buf->b_FSSpec.parID 639 && SearchData.theFile.name[0] == buf->b_FSSpec.name[0] 640 && STRNCMP(SearchData.theFile.name, buf->b_FSSpec.name, buf->b_FSSpec.name[0] + 1) == 0) 641 { 642 foundFile = true; 643 break; 644 } 645 646 if (foundFile == false) 647 *SearchData.theDate = fnfErr; 648 else 649 *SearchData.theDate = buf->b_mtime; 650 651 return error; 652}; 653 654/* 655 * Handle the Modified (from IDE to Editor) event from CodeWarrior 656 * 657 * Description 658 * ----------- 659 * 660 * The IDE sends this event to the external editor when it wants to 661 * know which files that are open in the editor have been modified. 662 * 663 * Parameters None. 664 * ---------- 665 * 666 * Event Reply 667 * ----------- 668 * The reply for this event is: 669 * 670 * keyDirectObject typeAEList required 671 * each element in the list is a structure of typeChar 672 * 673 * Remarks 674 * ------- 675 * 676 * When building the reply event, include one element in the list for 677 * each open file that has been modified. 678 * 679 */ 680 681typedef struct ModificationInfo ModificationInfo; 682struct ModificationInfo /* for replying to class 'KAHL', event 'MOD ', keyDirectObject typeAEList*/ 683{ 684 FSSpec theFile; // identifies the file 685 long theDate; // the date/time the file was last modified 686 short saved; // set this to zero when replying, unused 687}; 688 689 pascal OSErr 690Handle_KAHL_MOD_AE( 691 const AppleEvent *theAEvent, 692 AppleEvent *theReply, 693 long refCon) 694{ 695 OSErr error = noErr; 696 AEDescList replyList; 697 long numFiles; 698 ModificationInfo theFile; 699 buf_T *buf; 700 701 theFile.saved = 0; 702 703 error = HandleUnusedParms(theAEvent); 704 if (error) 705 return error; 706 707 /* Send the reply */ 708/* replyObject.descriptorType = typeNull; 709 replyObject.dataHandle = nil;*/ 710 711/* AECreateDesc(typeChar, (Ptr)&title[1], title[0], &data) */ 712 error = AECreateList(nil, 0, false, &replyList); 713 if (error) 714 return error; 715 716#if 0 717 error = AECountItems(&replyList, &numFiles); 718 719 /* AEPutKeyDesc(&replyList, keyAEPnject, &aDesc) 720 * AEPutKeyPtr(&replyList, keyAEPosition, typeChar, (Ptr)&theType, 721 * sizeof(DescType)) 722 */ 723 724 /* AEPutDesc */ 725#endif 726 727 numFiles = 0; 728 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 729 if (buf->b_ml.ml_mfp != NULL) 730 { 731 /* Add this file to the list */ 732 theFile.theFile = buf->b_FSSpec; 733 theFile.theDate = buf->b_mtime; 734/* theFile.theDate = time(NULL) & (time_t) 0xFFFFFFF0; */ 735 error = AEPutPtr(&replyList, numFiles, typeChar, (Ptr) &theFile, sizeof(theFile)); 736 }; 737 738#if 0 739 error = AECountItems(&replyList, &numFiles); 740#endif 741 742 /* We can add data only if something to reply */ 743 error = AEPutParamDesc(theReply, keyDirectObject, &replyList); 744 745 if (replyList.dataHandle) 746 AEDisposeDesc(&replyList); 747 748 return error; 749}; 750 751/* 752 * Handle the Get Text event from CodeWarrior 753 * 754 * Description 755 * ----------- 756 * 757 * The IDE sends the Get Text AppleEvent to the editor when it needs 758 * the source code from a file. For example, when the user issues a 759 * Check Syntax or Compile command, the compiler needs access to 760 * the source code contained in the file. 761 * 762 * Event Reply 763 * ----------- 764 * 765 * None. Put data in locations specified in the structure received. 766 * 767 * Remarks 768 * ------- 769 * 770 * When the editor receives this event, it must set the size of the handle 771 * in theText to fit the data in the file. It must then copy the entire 772 * contents of the specified file into the memory location specified in 773 * theText. 774 * 775 */ 776 777typedef struct CW_GetText CW_GetText; 778struct CW_GetText /* for handling class 'KAHL', event 'GTTX', keyDirectObject typeChar*/ 779{ 780 FSSpec theFile; /* identifies the file */ 781 Handle theText; /* the location where you return the text (must be resized properly) */ 782 long *unused; /* 0 (not used) */ 783 long *theDate; /* where to put the modification date/time */ 784}; 785 786 pascal OSErr 787Handle_KAHL_GTTX_AE( 788 const AppleEvent *theAEvent, 789 AppleEvent *theReply, 790 long refCon) 791{ 792 OSErr error = noErr; 793 buf_T *buf; 794 int foundFile = false; 795 DescType typeCode; 796 CW_GetText GetTextData; 797 Size actualSize; 798 char_u *line; 799 char_u *fullbuffer = NULL; 800 long linesize; 801 long lineStart; 802 long BufferSize; 803 long lineno; 804 805 error = AEGetParamPtr(theAEvent, keyDirectObject, typeChar, &typeCode, (Ptr) &GetTextData, sizeof(GetTextData), &actualSize); 806 807 if (error) 808 return error; 809 810 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 811 if (buf->b_ml.ml_mfp != NULL) 812 if (GetTextData.theFile.parID == buf->b_FSSpec.parID) 813 { 814 foundFile = true; 815 break; 816 } 817 818 if (foundFile) 819 { 820 BufferSize = 0; /* GetHandleSize(GetTextData.theText); */ 821 for (lineno = 0; lineno <= buf->b_ml.ml_line_count; lineno++) 822 { 823 /* Must use the right buffer */ 824 line = ml_get_buf(buf, (linenr_T) lineno, FALSE); 825 linesize = STRLEN(line) + 1; 826 lineStart = BufferSize; 827 BufferSize += linesize; 828 /* Resize handle to linesize+1 to include the linefeed */ 829 SetHandleSize(GetTextData.theText, BufferSize); 830 if (GetHandleSize(GetTextData.theText) != BufferSize) 831 { 832 break; /* Simple handling for now */ 833 } 834 else 835 { 836 HLock(GetTextData.theText); 837 fullbuffer = (char_u *) *GetTextData.theText; 838 STRCPY((char_u *)(fullbuffer + lineStart), line); 839 fullbuffer[BufferSize-1] = '\r'; 840 HUnlock(GetTextData.theText); 841 } 842 } 843 if (fullbuffer != NULL) 844 { 845 HLock(GetTextData.theText); 846 fullbuffer[BufferSize-1] = 0; 847 HUnlock(GetTextData.theText); 848 } 849 if (foundFile == false) 850 *GetTextData.theDate = fnfErr; 851 else 852/* *GetTextData.theDate = time(NULL) & (time_t) 0xFFFFFFF0;*/ 853 *GetTextData.theDate = buf->b_mtime; 854 } 855 856 error = HandleUnusedParms(theAEvent); 857 858 return error; 859} 860 861/* 862 * 863 */ 864 865/* Taken from MoreAppleEvents:ProcessHelpers*/ 866 pascal OSErr 867FindProcessBySignature( 868 const OSType targetType, 869 const OSType targetCreator, 870 ProcessSerialNumberPtr psnPtr) 871{ 872 OSErr anErr = noErr; 873 Boolean lookingForProcess = true; 874 875 ProcessInfoRec infoRec; 876 877 infoRec.processInfoLength = sizeof(ProcessInfoRec); 878 infoRec.processName = nil; 879 infoRec.processAppSpec = nil; 880 881 psnPtr->lowLongOfPSN = kNoProcess; 882 psnPtr->highLongOfPSN = kNoProcess; 883 884 while (lookingForProcess) 885 { 886 anErr = GetNextProcess(psnPtr); 887 if (anErr != noErr) 888 lookingForProcess = false; 889 else 890 { 891 anErr = GetProcessInformation(psnPtr, &infoRec); 892 if ((anErr == noErr) 893 && (infoRec.processType == targetType) 894 && (infoRec.processSignature == targetCreator)) 895 lookingForProcess = false; 896 } 897 } 898 899 return anErr; 900}//end FindProcessBySignature 901 902 void 903Send_KAHL_MOD_AE(buf_T *buf) 904{ 905 OSErr anErr = noErr; 906 AEDesc targetAppDesc = { typeNull, nil }; 907 ProcessSerialNumber psn = { kNoProcess, kNoProcess }; 908 AppleEvent theReply = { typeNull, nil }; 909 AESendMode sendMode; 910 AppleEvent theEvent = {typeNull, nil }; 911 AEIdleUPP idleProcUPP = nil; 912 ModificationInfo ModData; 913 914 915 anErr = FindProcessBySignature('APPL', 'CWIE', &psn); 916 if (anErr == noErr) 917 { 918 anErr = AECreateDesc(typeProcessSerialNumber, &psn, 919 sizeof(ProcessSerialNumber), &targetAppDesc); 920 921 if (anErr == noErr) 922 { 923 anErr = AECreateAppleEvent( 'KAHL', 'MOD ', &targetAppDesc, 924 kAutoGenerateReturnID, kAnyTransactionID, &theEvent); 925 } 926 927 AEDisposeDesc(&targetAppDesc); 928 929 /* Add the parms */ 930 ModData.theFile = buf->b_FSSpec; 931 ModData.theDate = buf->b_mtime; 932 933 if (anErr == noErr) 934 anErr = AEPutParamPtr(&theEvent, keyDirectObject, typeChar, &ModData, sizeof(ModData)); 935 936 if (idleProcUPP == nil) 937 sendMode = kAENoReply; 938 else 939 sendMode = kAEWaitReply; 940 941 if (anErr == noErr) 942 anErr = AESend(&theEvent, &theReply, sendMode, kAENormalPriority, kNoTimeOut, idleProcUPP, nil); 943 if (anErr == noErr && sendMode == kAEWaitReply) 944 { 945/* anErr = AEHGetHandlerError(&theReply);*/ 946 } 947 (void) AEDisposeDesc(&theReply); 948 } 949} 950#endif /* FEAT_CW_EDITOR */ 951 952/* 953 * ------------------------------------------------------------ 954 * Apple Event Handling procedure 955 * ------------------------------------------------------------ 956 */ 957#ifdef USE_AEVENT 958 959/* 960 * Handle the Unused parms of an AppleEvent 961 */ 962 963 OSErr 964HandleUnusedParms(const AppleEvent *theAEvent) 965{ 966 OSErr error; 967 long actualSize; 968 DescType dummyType; 969 AEKeyword missedKeyword; 970 971 /* Get the "missed keyword" attribute from the AppleEvent. */ 972 error = AEGetAttributePtr(theAEvent, keyMissedKeywordAttr, 973 typeKeyword, &dummyType, 974 (Ptr)&missedKeyword, sizeof(missedKeyword), 975 &actualSize); 976 977 /* If the descriptor isn't found, then we got the required parameters. */ 978 if (error == errAEDescNotFound) 979 { 980 error = noErr; 981 } 982 else 983 { 984#if 0 985 /* Why is this removed? */ 986 error = errAEEventNotHandled; 987#endif 988 } 989 990 return error; 991} 992 993 994/* 995 * Handle the ODoc AppleEvent 996 * 997 * Deals with all files dragged to the application icon. 998 * 999 */ 1000 1001typedef struct SelectionRange SelectionRange; 1002struct SelectionRange /* for handling kCoreClassEvent:kOpenDocuments:keyAEPosition typeChar */ 1003{ 1004 short unused1; // 0 (not used) 1005 short lineNum; // line to select (<0 to specify range) 1006 long startRange; // start of selection range (if line < 0) 1007 long endRange; // end of selection range (if line < 0) 1008 long unused2; // 0 (not used) 1009 long theDate; // modification date/time 1010}; 1011 1012/* The IDE uses the optional keyAEPosition parameter to tell the ed- 1013 itor the selection range. If lineNum is zero or greater, scroll the text 1014 to the specified line. If lineNum is less than zero, use the values in 1015 startRange and endRange to select the specified characters. Scroll 1016 the text to display the selection. If lineNum, startRange, and 1017 endRange are all negative, there is no selection range specified. 1018 */ 1019 1020 pascal OSErr 1021HandleODocAE(const AppleEvent *theAEvent, AppleEvent *theReply, long refCon) 1022{ 1023 /* 1024 * TODO: Clean up the code with convert the AppleEvent into 1025 * a ":args" 1026 */ 1027 OSErr error = noErr; 1028// OSErr firstError = noErr; 1029// short numErrors = 0; 1030 AEDesc theList; 1031 DescType typeCode; 1032 long numFiles; 1033 // long fileCount; 1034 char_u **fnames; 1035// char_u fname[256]; 1036 Size actualSize; 1037 SelectionRange thePosition; 1038 short gotPosition = false; 1039 long lnum; 1040 1041 /* the direct object parameter is the list of aliases to files (one or more) */ 1042 error = AEGetParamDesc(theAEvent, keyDirectObject, typeAEList, &theList); 1043 if (error) 1044 return error; 1045 1046 1047 error = AEGetParamPtr(theAEvent, keyAEPosition, typeChar, &typeCode, (Ptr) &thePosition, sizeof(SelectionRange), &actualSize); 1048 if (error == noErr) 1049 gotPosition = true; 1050 if (error == errAEDescNotFound) 1051 error = noErr; 1052 if (error) 1053 return error; 1054 1055/* 1056 error = AEGetParamDesc(theAEvent, keyAEPosition, typeChar, &thePosition); 1057 1058 if (^error) then 1059 { 1060 if (thePosition.lineNum >= 0) 1061 { 1062 // Goto this line 1063 } 1064 else 1065 { 1066 // Set the range char wise 1067 } 1068 } 1069 */ 1070 1071 1072#ifdef FEAT_VISUAL 1073 reset_VIsual(); 1074#endif 1075 1076 fnames = new_fnames_from_AEDesc(&theList, &numFiles, &error); 1077 1078 if (error) 1079 { 1080 /* TODO: empty fnames[] first */ 1081 vim_free(fnames); 1082 return (error); 1083 } 1084 1085 if (starting > 0) 1086 { 1087 int i; 1088 char_u *p; 1089 int fnum = -1; 1090 1091 /* these are the initial files dropped on the Vim icon */ 1092 for (i = 0 ; i < numFiles; i++) 1093 { 1094 if (ga_grow(&global_alist.al_ga, 1) == FAIL 1095 || (p = vim_strsave(fnames[i])) == NULL) 1096 mch_exit(2); 1097 else 1098 alist_add(&global_alist, p, 2); 1099 if (fnum == -1) 1100 fnum = GARGLIST[GARGCOUNT - 1].ae_fnum; 1101 } 1102 1103 /* If the file name was already in the buffer list we need to switch 1104 * to it. */ 1105 if (curbuf->b_fnum != fnum) 1106 { 1107 char_u cmd[30]; 1108 1109 vim_snprintf((char *)cmd, 30, "silent %dbuffer", fnum); 1110 do_cmdline_cmd(cmd); 1111 } 1112 1113 /* Change directory to the location of the first file. */ 1114 if (GARGCOUNT > 0 && vim_chdirfile(alist_name(&GARGLIST[0])) == OK) 1115 shorten_fnames(TRUE); 1116 1117 goto finished; 1118 } 1119 1120 /* Handle the drop, :edit to get to the file */ 1121 handle_drop(numFiles, fnames, FALSE); 1122 1123 /* TODO: Handle the goto/select line more cleanly */ 1124 if ((numFiles == 1) & (gotPosition)) 1125 { 1126 if (thePosition.lineNum >= 0) 1127 { 1128 lnum = thePosition.lineNum + 1; 1129 /* oap->motion_type = MLINE; 1130 setpcmark();*/ 1131 if (lnum < 1L) 1132 lnum = 1L; 1133 else if (lnum > curbuf->b_ml.ml_line_count) 1134 lnum = curbuf->b_ml.ml_line_count; 1135 curwin->w_cursor.lnum = lnum; 1136 curwin->w_cursor.col = 0; 1137 /* beginline(BL_SOL | BL_FIX);*/ 1138 } 1139 else 1140 goto_byte(thePosition.startRange + 1); 1141 } 1142 1143 /* Update the screen display */ 1144 update_screen(NOT_VALID); 1145#ifdef FEAT_VISUAL 1146 /* Select the text if possible */ 1147 if (gotPosition) 1148 { 1149 VIsual_active = TRUE; 1150 VIsual_select = FALSE; 1151 VIsual = curwin->w_cursor; 1152 if (thePosition.lineNum < 0) 1153 { 1154 VIsual_mode = 'v'; 1155 goto_byte(thePosition.endRange); 1156 } 1157 else 1158 { 1159 VIsual_mode = 'V'; 1160 VIsual.col = 0; 1161 } 1162 } 1163#endif 1164 setcursor(); 1165 out_flush(); 1166 1167 /* Fake mouse event to wake from stall */ 1168 PostEvent(mouseUp, 0); 1169 1170finished: 1171 AEDisposeDesc(&theList); /* dispose what we allocated */ 1172 1173 error = HandleUnusedParms(theAEvent); 1174 return error; 1175} 1176 1177/* 1178 * 1179 */ 1180 1181 pascal OSErr 1182Handle_aevt_oapp_AE( 1183 const AppleEvent *theAEvent, 1184 AppleEvent *theReply, 1185 long refCon) 1186{ 1187 OSErr error = noErr; 1188 1189 error = HandleUnusedParms(theAEvent); 1190 return error; 1191} 1192 1193/* 1194 * 1195 */ 1196 1197 pascal OSErr 1198Handle_aevt_quit_AE( 1199 const AppleEvent *theAEvent, 1200 AppleEvent *theReply, 1201 long refCon) 1202{ 1203 OSErr error = noErr; 1204 1205 error = HandleUnusedParms(theAEvent); 1206 if (error) 1207 return error; 1208 1209 /* Need to fake a :confirm qa */ 1210 do_cmdline_cmd((char_u *)"confirm qa"); 1211 1212 return error; 1213} 1214 1215/* 1216 * 1217 */ 1218 1219 pascal OSErr 1220Handle_aevt_pdoc_AE( 1221 const AppleEvent *theAEvent, 1222 AppleEvent *theReply, 1223 long refCon) 1224{ 1225 OSErr error = noErr; 1226 1227 error = HandleUnusedParms(theAEvent); 1228 1229 return error; 1230} 1231 1232/* 1233 * Handling of unknown AppleEvent 1234 * 1235 * (Just get rid of all the parms) 1236 */ 1237 pascal OSErr 1238Handle_unknown_AE( 1239 const AppleEvent *theAEvent, 1240 AppleEvent *theReply, 1241 long refCon) 1242{ 1243 OSErr error = noErr; 1244 1245 error = HandleUnusedParms(theAEvent); 1246 1247 return error; 1248} 1249 1250 1251/* 1252 * Install the various AppleEvent Handlers 1253 */ 1254 OSErr 1255InstallAEHandlers(void) 1256{ 1257 OSErr error; 1258 1259 /* install open application handler */ 1260 error = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, 1261 NewAEEventHandlerUPP(Handle_aevt_oapp_AE), 0, false); 1262 if (error) 1263 { 1264 return error; 1265 } 1266 1267 /* install quit application handler */ 1268 error = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 1269 NewAEEventHandlerUPP(Handle_aevt_quit_AE), 0, false); 1270 if (error) 1271 { 1272 return error; 1273 } 1274 1275 /* install open document handler */ 1276 error = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, 1277 NewAEEventHandlerUPP(HandleODocAE), 0, false); 1278 if (error) 1279 { 1280 return error; 1281 } 1282 1283 /* install print document handler */ 1284 error = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, 1285 NewAEEventHandlerUPP(Handle_aevt_pdoc_AE), 0, false); 1286 1287/* Install Core Suite */ 1288/* error = AEInstallEventHandler(kAECoreSuite, kAEClone, 1289 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1290 1291 error = AEInstallEventHandler(kAECoreSuite, kAEClose, 1292 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1293 1294 error = AEInstallEventHandler(kAECoreSuite, kAECountElements, 1295 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1296 1297 error = AEInstallEventHandler(kAECoreSuite, kAECreateElement, 1298 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1299 1300 error = AEInstallEventHandler(kAECoreSuite, kAEDelete, 1301 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1302 1303 error = AEInstallEventHandler(kAECoreSuite, kAEDoObjectsExist, 1304 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1305 1306 error = AEInstallEventHandler(kAECoreSuite, kAEGetData, 1307 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetData, false); 1308 1309 error = AEInstallEventHandler(kAECoreSuite, kAEGetDataSize, 1310 NewAEEventHandlerUPP(Handle_unknown_AE), kAEGetDataSize, false); 1311 1312 error = AEInstallEventHandler(kAECoreSuite, kAEGetClassInfo, 1313 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1314 1315 error = AEInstallEventHandler(kAECoreSuite, kAEGetEventInfo, 1316 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1317 1318 error = AEInstallEventHandler(kAECoreSuite, kAEMove, 1319 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1320 1321 error = AEInstallEventHandler(kAECoreSuite, kAESave, 1322 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1323 1324 error = AEInstallEventHandler(kAECoreSuite, kAESetData, 1325 NewAEEventHandlerUPP(Handle_unknown_AE), nil, false); 1326*/ 1327 1328#ifdef FEAT_CW_EDITOR 1329 /* 1330 * Bind codewarrior support handlers 1331 */ 1332 error = AEInstallEventHandler('KAHL', 'GTTX', 1333 NewAEEventHandlerUPP(Handle_KAHL_GTTX_AE), 0, false); 1334 if (error) 1335 { 1336 return error; 1337 } 1338 error = AEInstallEventHandler('KAHL', 'SRCH', 1339 NewAEEventHandlerUPP(Handle_KAHL_SRCH_AE), 0, false); 1340 if (error) 1341 { 1342 return error; 1343 } 1344 error = AEInstallEventHandler('KAHL', 'MOD ', 1345 NewAEEventHandlerUPP(Handle_KAHL_MOD_AE), 0, false); 1346 if (error) 1347 { 1348 return error; 1349 } 1350#endif 1351 1352 return error; 1353 1354} 1355#endif /* USE_AEVENT */ 1356 1357 1358/* 1359 * Callback function, installed by InstallFontPanelHandler(), below, 1360 * to handle Font Panel events. 1361 */ 1362 static OSStatus 1363FontPanelHandler( 1364 EventHandlerCallRef inHandlerCallRef, 1365 EventRef inEvent, 1366 void *inUserData) 1367{ 1368 if (GetEventKind(inEvent) == kEventFontPanelClosed) 1369 { 1370 gFontPanelInfo.isPanelVisible = false; 1371 return noErr; 1372 } 1373 1374 if (GetEventKind(inEvent) == kEventFontSelection) 1375 { 1376 OSStatus status; 1377 FMFontFamily newFamily; 1378 FMFontSize newSize; 1379 FMFontStyle newStyle; 1380 1381 /* Retrieve the font family ID number. */ 1382 status = GetEventParameter(inEvent, kEventParamFMFontFamily, 1383 /*inDesiredType=*/typeFMFontFamily, /*outActualType=*/NULL, 1384 /*inBufferSize=*/sizeof(FMFontFamily), /*outActualSize=*/NULL, 1385 &newFamily); 1386 if (status == noErr) 1387 gFontPanelInfo.family = newFamily; 1388 1389 /* Retrieve the font size. */ 1390 status = GetEventParameter(inEvent, kEventParamFMFontSize, 1391 typeFMFontSize, NULL, sizeof(FMFontSize), NULL, &newSize); 1392 if (status == noErr) 1393 gFontPanelInfo.size = newSize; 1394 1395 /* Retrieve the font style (bold, etc.). Currently unused. */ 1396 status = GetEventParameter(inEvent, kEventParamFMFontStyle, 1397 typeFMFontStyle, NULL, sizeof(FMFontStyle), NULL, &newStyle); 1398 if (status == noErr) 1399 gFontPanelInfo.style = newStyle; 1400 } 1401 return noErr; 1402} 1403 1404 1405 static void 1406InstallFontPanelHandler(void) 1407{ 1408 EventTypeSpec eventTypes[2]; 1409 EventHandlerUPP handlerUPP; 1410 /* EventHandlerRef handlerRef; */ 1411 1412 eventTypes[0].eventClass = kEventClassFont; 1413 eventTypes[0].eventKind = kEventFontSelection; 1414 eventTypes[1].eventClass = kEventClassFont; 1415 eventTypes[1].eventKind = kEventFontPanelClosed; 1416 1417 handlerUPP = NewEventHandlerUPP(FontPanelHandler); 1418 1419 InstallApplicationEventHandler(handlerUPP, /*numTypes=*/2, eventTypes, 1420 /*userData=*/NULL, /*handlerRef=*/NULL); 1421} 1422 1423 1424/* 1425 * Fill the buffer pointed to by outName with the name and size 1426 * of the font currently selected in the Font Panel. 1427 */ 1428#define FONT_STYLE_BUFFER_SIZE 32 1429 static void 1430GetFontPanelSelection(char_u *outName) 1431{ 1432 Str255 buf; 1433 ByteCount fontNameLen = 0; 1434 ATSUFontID fid; 1435 char_u styleString[FONT_STYLE_BUFFER_SIZE]; 1436 1437 if (!outName) 1438 return; 1439 1440 if (FMGetFontFamilyName(gFontPanelInfo.family, buf) == noErr) 1441 { 1442 /* Canonicalize localized font names */ 1443 if (FMGetFontFromFontFamilyInstance(gFontPanelInfo.family, 1444 gFontPanelInfo.style, &fid, NULL) != noErr) 1445 return; 1446 1447 /* Request font name with Mac encoding (otherwise we could 1448 * get an unwanted utf-16 name) */ 1449 if (ATSUFindFontName(fid, kFontFullName, kFontMacintoshPlatform, 1450 kFontNoScriptCode, kFontNoLanguageCode, 1451 255, (char *)outName, &fontNameLen, NULL) != noErr) 1452 return; 1453 1454 /* Only encode font size, because style (bold, italic, etc) is 1455 * already part of the font full name */ 1456 vim_snprintf((char *)styleString, FONT_STYLE_BUFFER_SIZE, ":h%d", 1457 gFontPanelInfo.size/*, 1458 ((gFontPanelInfo.style & bold)!=0 ? ":b" : ""), 1459 ((gFontPanelInfo.style & italic)!=0 ? ":i" : ""), 1460 ((gFontPanelInfo.style & underline)!=0 ? ":u" : "")*/); 1461 1462 if ((fontNameLen + STRLEN(styleString)) < 255) 1463 STRCPY(outName + fontNameLen, styleString); 1464 } 1465 else 1466 { 1467 *outName = NUL; 1468 } 1469} 1470 1471 1472/* 1473 * ------------------------------------------------------------ 1474 * Unfiled yet 1475 * ------------------------------------------------------------ 1476 */ 1477 1478/* 1479 * gui_mac_get_menu_item_index 1480 * 1481 * Returns the index inside the menu wher 1482 */ 1483 short /* Shoulde we return MenuItemIndex? */ 1484gui_mac_get_menu_item_index(vimmenu_T *pMenu) 1485{ 1486 short index; 1487 short itemIndex = -1; 1488 vimmenu_T *pBrother; 1489 1490 /* Only menu without parent are the: 1491 * -menu in the menubar 1492 * -popup menu 1493 * -toolbar (guess) 1494 * 1495 * Which are not items anyway. 1496 */ 1497 if (pMenu->parent) 1498 { 1499 /* Start from the Oldest Brother */ 1500 pBrother = pMenu->parent->children; 1501 index = 1; 1502 while ((pBrother) && (itemIndex == -1)) 1503 { 1504 if (pBrother == pMenu) 1505 itemIndex = index; 1506 index++; 1507 pBrother = pBrother->next; 1508 } 1509 } 1510 return itemIndex; 1511} 1512 1513 static vimmenu_T * 1514gui_mac_get_vim_menu(short menuID, short itemIndex, vimmenu_T *pMenu) 1515{ 1516 short index; 1517 vimmenu_T *pChildMenu; 1518 vimmenu_T *pElder = pMenu->parent; 1519 1520 1521 /* Only menu without parent are the: 1522 * -menu in the menubar 1523 * -popup menu 1524 * -toolbar (guess) 1525 * 1526 * Which are not items anyway. 1527 */ 1528 1529 if ((pElder) && (pElder->submenu_id == menuID)) 1530 { 1531 for (index = 1; (index != itemIndex) && (pMenu != NULL); index++) 1532 pMenu = pMenu->next; 1533 } 1534 else 1535 { 1536 for (; pMenu != NULL; pMenu = pMenu->next) 1537 { 1538 if (pMenu->children != NULL) 1539 { 1540 pChildMenu = gui_mac_get_vim_menu 1541 (menuID, itemIndex, pMenu->children); 1542 if (pChildMenu) 1543 { 1544 pMenu = pChildMenu; 1545 break; 1546 } 1547 } 1548 } 1549 } 1550 return pMenu; 1551} 1552 1553/* 1554 * ------------------------------------------------------------ 1555 * MacOS Feedback procedures 1556 * ------------------------------------------------------------ 1557 */ 1558 pascal 1559 void 1560gui_mac_drag_thumb(ControlHandle theControl, short partCode) 1561{ 1562 scrollbar_T *sb; 1563 int value, dragging; 1564 ControlHandle theControlToUse; 1565 int dont_scroll_save = dont_scroll; 1566 1567 theControlToUse = dragged_sb; 1568 1569 sb = gui_find_scrollbar((long) GetControlReference(theControlToUse)); 1570 1571 if (sb == NULL) 1572 return; 1573 1574 /* Need to find value by diff between Old Poss New Pos */ 1575 value = GetControl32BitValue(theControlToUse); 1576 dragging = (partCode != 0); 1577 1578 /* When "allow_scrollbar" is FALSE still need to remember the new 1579 * position, but don't actually scroll by setting "dont_scroll". */ 1580 dont_scroll = !allow_scrollbar; 1581 gui_drag_scrollbar(sb, value, dragging); 1582 dont_scroll = dont_scroll_save; 1583} 1584 1585 pascal 1586 void 1587gui_mac_scroll_action(ControlHandle theControl, short partCode) 1588{ 1589 /* TODO: have live support */ 1590 scrollbar_T *sb, *sb_info; 1591 long data; 1592 long value; 1593 int page; 1594 int dragging = FALSE; 1595 int dont_scroll_save = dont_scroll; 1596 1597 sb = gui_find_scrollbar((long)GetControlReference(theControl)); 1598 1599 if (sb == NULL) 1600 return; 1601 1602 if (sb->wp != NULL) /* Left or right scrollbar */ 1603 { 1604 /* 1605 * Careful: need to get scrollbar info out of first (left) scrollbar 1606 * for window, but keep real scrollbar too because we must pass it to 1607 * gui_drag_scrollbar(). 1608 */ 1609 sb_info = &sb->wp->w_scrollbars[0]; 1610 1611 if (sb_info->size > 5) 1612 page = sb_info->size - 2; /* use two lines of context */ 1613 else 1614 page = sb_info->size; 1615 } 1616 else /* Bottom scrollbar */ 1617 { 1618 sb_info = sb; 1619 page = W_WIDTH(curwin) - 5; 1620 } 1621 1622 switch (partCode) 1623 { 1624 case kControlUpButtonPart: data = -1; break; 1625 case kControlDownButtonPart: data = 1; break; 1626 case kControlPageDownPart: data = page; break; 1627 case kControlPageUpPart: data = -page; break; 1628 default: data = 0; break; 1629 } 1630 1631 value = sb_info->value + data; 1632/* if (value > sb_info->max) 1633 value = sb_info->max; 1634 else if (value < 0) 1635 value = 0;*/ 1636 1637 /* When "allow_scrollbar" is FALSE still need to remember the new 1638 * position, but don't actually scroll by setting "dont_scroll". */ 1639 dont_scroll = !allow_scrollbar; 1640 gui_drag_scrollbar(sb, value, dragging); 1641 dont_scroll = dont_scroll_save; 1642 1643 out_flush(); 1644 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max); 1645 1646/* if (sb_info->wp != NULL) 1647 { 1648 win_T *wp; 1649 int sb_num; 1650 1651 sb_num = 0; 1652 for (wp = firstwin; wp != sb->wp && wp != NULL; wp = W_NEXT(wp)) 1653 sb_num++; 1654 1655 if (wp != NULL) 1656 { 1657 current_scrollbar = sb_num; 1658 scrollbar_value = value; 1659 gui_do_scroll(); 1660 gui_mch_set_scrollbar_thumb(sb, value, sb_info->size, sb_info->max); 1661 } 1662 }*/ 1663} 1664 1665/* 1666 * ------------------------------------------------------------ 1667 * MacOS Click Handling procedures 1668 * ------------------------------------------------------------ 1669 */ 1670 1671 1672/* 1673 * Handle a click inside the window, it may happens in the 1674 * scrollbar or the contents. 1675 * 1676 * TODO: Add support for potential TOOLBAR 1677 */ 1678 void 1679gui_mac_doInContentClick(EventRecord *theEvent, WindowPtr whichWindow) 1680{ 1681 Point thePoint; 1682 int_u vimModifiers; 1683 short thePortion; 1684 ControlHandle theControl; 1685 int vimMouseButton; 1686 short dblClick; 1687 1688 thePoint = theEvent->where; 1689 GlobalToLocal(&thePoint); 1690 SelectWindow(whichWindow); 1691 1692 thePortion = FindControl(thePoint, whichWindow, &theControl); 1693 1694 if (theControl != NUL) 1695 { 1696 /* We hit a scollbar */ 1697 1698 if (thePortion != kControlIndicatorPart) 1699 { 1700 dragged_sb = theControl; 1701 TrackControl(theControl, thePoint, gScrollAction); 1702 dragged_sb = NULL; 1703 } 1704 else 1705 { 1706 dragged_sb = theControl; 1707#if 1 1708 TrackControl(theControl, thePoint, gScrollDrag); 1709#else 1710 TrackControl(theControl, thePoint, NULL); 1711#endif 1712 /* pass 0 as the part to tell gui_mac_drag_thumb, that the mouse 1713 * button has been released */ 1714 gui_mac_drag_thumb(theControl, 0); /* Should it be thePortion ? (Dany) */ 1715 dragged_sb = NULL; 1716 } 1717 } 1718 else 1719 { 1720 /* We are inside the contents */ 1721 1722 /* Convert the CTRL, OPTION, SHIFT and CMD key */ 1723 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers); 1724 1725 /* Defaults to MOUSE_LEFT as there's only one mouse button */ 1726 vimMouseButton = MOUSE_LEFT; 1727 1728 /* Convert the CTRL_MOUSE_LEFT to MOUSE_RIGHT */ 1729 /* TODO: NEEDED? */ 1730 clickIsPopup = FALSE; 1731 1732 if (mouse_model_popup() && IsShowContextualMenuClick(theEvent)) 1733 { 1734 vimMouseButton = MOUSE_RIGHT; 1735 vimModifiers &= ~MOUSE_CTRL; 1736 clickIsPopup = TRUE; 1737 } 1738 1739 /* Is it a double click ? */ 1740 dblClick = ((theEvent->when - lastMouseTick) < GetDblTime()); 1741 1742 /* Send the mouse click to Vim */ 1743 gui_send_mouse_event(vimMouseButton, thePoint.h, 1744 thePoint.v, dblClick, vimModifiers); 1745 1746 /* Create the rectangle around the cursor to detect 1747 * the mouse dragging 1748 */ 1749#if 0 1750 /* TODO: Do we need to this even for the contextual menu? 1751 * It may be require for popup_setpos, but for popup? 1752 */ 1753 if (vimMouseButton == MOUSE_LEFT) 1754#endif 1755 { 1756 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)), 1757 FILL_Y(Y_2_ROW(thePoint.v)), 1758 FILL_X(X_2_COL(thePoint.h)+1), 1759 FILL_Y(Y_2_ROW(thePoint.v)+1)); 1760 1761 dragRectEnbl = TRUE; 1762 dragRectControl = kCreateRect; 1763 } 1764 } 1765} 1766 1767/* 1768 * Handle the click in the titlebar (to move the window) 1769 */ 1770 void 1771gui_mac_doInDragClick(Point where, WindowPtr whichWindow) 1772{ 1773 Rect movingLimits; 1774 Rect *movingLimitsPtr = &movingLimits; 1775 1776 /* TODO: may try to prevent move outside screen? */ 1777 movingLimitsPtr = GetRegionBounds(GetGrayRgn(), &movingLimits); 1778 DragWindow(whichWindow, where, movingLimitsPtr); 1779} 1780 1781/* 1782 * Handle the click in the grow box 1783 */ 1784 void 1785gui_mac_doInGrowClick(Point where, WindowPtr whichWindow) 1786{ 1787 1788 long newSize; 1789 unsigned short newWidth; 1790 unsigned short newHeight; 1791 Rect resizeLimits; 1792 Rect *resizeLimitsPtr = &resizeLimits; 1793 Rect NewContentRect; 1794 1795 resizeLimitsPtr = GetRegionBounds(GetGrayRgn(), &resizeLimits); 1796 1797 /* Set the minimum size */ 1798 /* TODO: Should this come from Vim? */ 1799 resizeLimits.top = 100; 1800 resizeLimits.left = 100; 1801 1802 newSize = ResizeWindow(whichWindow, where, &resizeLimits, &NewContentRect); 1803 newWidth = NewContentRect.right - NewContentRect.left; 1804 newHeight = NewContentRect.bottom - NewContentRect.top; 1805 gui_resize_shell(newWidth, newHeight); 1806 gui_mch_set_bg_color(gui.back_pixel); 1807 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH); 1808} 1809 1810/* 1811 * Handle the click in the zoom box 1812 */ 1813 static void 1814gui_mac_doInZoomClick(EventRecord *theEvent, WindowPtr whichWindow) 1815{ 1816 Rect r; 1817 Point p; 1818 short thePart; 1819 1820 /* ideal width is current */ 1821 p.h = Columns * gui.char_width + 2 * gui.border_offset; 1822 if (gui.which_scrollbars[SBAR_LEFT]) 1823 p.h += gui.scrollbar_width; 1824 if (gui.which_scrollbars[SBAR_RIGHT]) 1825 p.h += gui.scrollbar_width; 1826 /* ideal height is as heigh as we can get */ 1827 p.v = 15 * 1024; 1828 1829 thePart = IsWindowInStandardState(whichWindow, &p, &r) 1830 ? inZoomIn : inZoomOut; 1831 1832 if (!TrackBox(whichWindow, theEvent->where, thePart)) 1833 return; 1834 1835 /* use returned width */ 1836 p.h = r.right - r.left; 1837 /* adjust returned height */ 1838 p.v = r.bottom - r.top - 2 * gui.border_offset; 1839 if (gui.which_scrollbars[SBAR_BOTTOM]) 1840 p.v -= gui.scrollbar_height; 1841 p.v -= p.v % gui.char_height; 1842 p.v += 2 * gui.border_width; 1843 if (gui.which_scrollbars[SBAR_BOTTOM]); 1844 p.v += gui.scrollbar_height; 1845 1846 ZoomWindowIdeal(whichWindow, thePart, &p); 1847 1848 GetWindowBounds(whichWindow, kWindowContentRgn, &r); 1849 gui_resize_shell(r.right - r.left, r.bottom - r.top); 1850 gui_mch_set_bg_color(gui.back_pixel); 1851 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH); 1852} 1853 1854/* 1855 * ------------------------------------------------------------ 1856 * MacOS Event Handling procedure 1857 * ------------------------------------------------------------ 1858 */ 1859 1860/* 1861 * Handle the Update Event 1862 */ 1863 1864 void 1865gui_mac_doUpdateEvent(EventRecord *event) 1866{ 1867 WindowPtr whichWindow; 1868 GrafPtr savePort; 1869 RgnHandle updateRgn; 1870 Rect updateRect; 1871 Rect *updateRectPtr; 1872 Rect rc; 1873 Rect growRect; 1874 RgnHandle saveRgn; 1875 1876 1877 updateRgn = NewRgn(); 1878 if (updateRgn == NULL) 1879 return; 1880 1881 /* This could be done by the caller as we 1882 * don't require anything else out of the event 1883 */ 1884 whichWindow = (WindowPtr) event->message; 1885 1886 /* Save Current Port */ 1887 GetPort(&savePort); 1888 1889 /* Select the Window's Port */ 1890 SetPortWindowPort(whichWindow); 1891 1892 /* Let's update the window */ 1893 BeginUpdate(whichWindow); 1894 /* Redraw the biggest rectangle covering the area 1895 * to be updated. 1896 */ 1897 GetPortVisibleRegion(GetWindowPort(whichWindow), updateRgn); 1898# if 0 1899 /* Would be more appropriate to use the following but doesn't 1900 * seem to work under MacOS X (Dany) 1901 */ 1902 GetWindowRegion(whichWindow, kWindowUpdateRgn, updateRgn); 1903# endif 1904 1905 /* Use the HLock useless in Carbon? Is it harmful?*/ 1906 HLock((Handle) updateRgn); 1907 1908 updateRectPtr = GetRegionBounds(updateRgn, &updateRect); 1909# if 0 1910 /* Code from original Carbon Port (using GetWindowRegion. 1911 * I believe the UpdateRgn is already in local (Dany) 1912 */ 1913 GlobalToLocal(&topLeft(updateRect)); /* preCarbon? */ 1914 GlobalToLocal(&botRight(updateRect)); 1915# endif 1916 /* Update the content (i.e. the text) */ 1917 gui_redraw(updateRectPtr->left, updateRectPtr->top, 1918 updateRectPtr->right - updateRectPtr->left, 1919 updateRectPtr->bottom - updateRectPtr->top); 1920 /* Clear the border areas if needed */ 1921 gui_mch_set_bg_color(gui.back_pixel); 1922 if (updateRectPtr->left < FILL_X(0)) 1923 { 1924 SetRect(&rc, 0, 0, FILL_X(0), FILL_Y(Rows)); 1925 EraseRect(&rc); 1926 } 1927 if (updateRectPtr->top < FILL_Y(0)) 1928 { 1929 SetRect(&rc, 0, 0, FILL_X(Columns), FILL_Y(0)); 1930 EraseRect(&rc); 1931 } 1932 if (updateRectPtr->right > FILL_X(Columns)) 1933 { 1934 SetRect(&rc, FILL_X(Columns), 0, 1935 FILL_X(Columns) + gui.border_offset, FILL_Y(Rows)); 1936 EraseRect(&rc); 1937 } 1938 if (updateRectPtr->bottom > FILL_Y(Rows)) 1939 { 1940 SetRect(&rc, 0, FILL_Y(Rows), FILL_X(Columns) + gui.border_offset, 1941 FILL_Y(Rows) + gui.border_offset); 1942 EraseRect(&rc); 1943 } 1944 HUnlock((Handle) updateRgn); 1945 DisposeRgn(updateRgn); 1946 1947 /* Update scrollbars */ 1948 DrawControls(whichWindow); 1949 1950 /* Update the GrowBox */ 1951 /* Taken from FAQ 33-27 */ 1952 saveRgn = NewRgn(); 1953 GetWindowBounds(whichWindow, kWindowGrowRgn, &growRect); 1954 GetClip(saveRgn); 1955 ClipRect(&growRect); 1956 DrawGrowIcon(whichWindow); 1957 SetClip(saveRgn); 1958 DisposeRgn(saveRgn); 1959 EndUpdate(whichWindow); 1960 1961 /* Restore original Port */ 1962 SetPort(savePort); 1963} 1964 1965/* 1966 * Handle the activate/deactivate event 1967 * (apply to a window) 1968 */ 1969 void 1970gui_mac_doActivateEvent(EventRecord *event) 1971{ 1972 WindowPtr whichWindow; 1973 1974 whichWindow = (WindowPtr) event->message; 1975 /* Dim scrollbars */ 1976 if (whichWindow == gui.VimWindow) 1977 { 1978 ControlRef rootControl; 1979 GetRootControl(gui.VimWindow, &rootControl); 1980 if ((event->modifiers) & activeFlag) 1981 ActivateControl(rootControl); 1982 else 1983 DeactivateControl(rootControl); 1984 } 1985 1986 /* Activate */ 1987 gui_focus_change((event->modifiers) & activeFlag); 1988} 1989 1990 1991/* 1992 * Handle the suspend/resume event 1993 * (apply to the application) 1994 */ 1995 void 1996gui_mac_doSuspendEvent(EventRecord *event) 1997{ 1998 /* The frontmost application just changed */ 1999 2000 /* NOTE: the suspend may happen before the deactivate 2001 * seen on MacOS X 2002 */ 2003 2004 /* May not need to change focus as the window will 2005 * get an activate/deactivate event 2006 */ 2007 if (event->message & 1) 2008 /* Resume */ 2009 gui_focus_change(TRUE); 2010 else 2011 /* Suspend */ 2012 gui_focus_change(FALSE); 2013} 2014 2015/* 2016 * Handle the key 2017 */ 2018#ifdef USE_CARBONKEYHANDLER 2019 static pascal OSStatus 2020gui_mac_handle_window_activate( 2021 EventHandlerCallRef nextHandler, 2022 EventRef theEvent, 2023 void *data) 2024{ 2025 UInt32 eventClass = GetEventClass(theEvent); 2026 UInt32 eventKind = GetEventKind(theEvent); 2027 2028 if (eventClass == kEventClassWindow) 2029 { 2030 switch (eventKind) 2031 { 2032 case kEventWindowActivated: 2033#if defined(USE_IM_CONTROL) 2034 im_on_window_switch(TRUE); 2035#endif 2036 return noErr; 2037 2038 case kEventWindowDeactivated: 2039#if defined(USE_IM_CONTROL) 2040 im_on_window_switch(FALSE); 2041#endif 2042 return noErr; 2043 } 2044 } 2045 2046 return eventNotHandledErr; 2047} 2048 2049 static pascal OSStatus 2050gui_mac_handle_text_input( 2051 EventHandlerCallRef nextHandler, 2052 EventRef theEvent, 2053 void *data) 2054{ 2055 UInt32 eventClass = GetEventClass(theEvent); 2056 UInt32 eventKind = GetEventKind(theEvent); 2057 2058 if (eventClass != kEventClassTextInput) 2059 return eventNotHandledErr; 2060 2061 if ((kEventTextInputUpdateActiveInputArea != eventKind) && 2062 (kEventTextInputUnicodeForKeyEvent != eventKind) && 2063 (kEventTextInputOffsetToPos != eventKind) && 2064 (kEventTextInputPosToOffset != eventKind) && 2065 (kEventTextInputGetSelectedText != eventKind)) 2066 return eventNotHandledErr; 2067 2068 switch (eventKind) 2069 { 2070 case kEventTextInputUpdateActiveInputArea: 2071 return gui_mac_update_input_area(nextHandler, theEvent); 2072 case kEventTextInputUnicodeForKeyEvent: 2073 return gui_mac_unicode_key_event(nextHandler, theEvent); 2074 2075 case kEventTextInputOffsetToPos: 2076 case kEventTextInputPosToOffset: 2077 case kEventTextInputGetSelectedText: 2078 break; 2079 } 2080 2081 return eventNotHandledErr; 2082} 2083 2084 static pascal 2085OSStatus gui_mac_update_input_area( 2086 EventHandlerCallRef nextHandler, 2087 EventRef theEvent) 2088{ 2089 return eventNotHandledErr; 2090} 2091 2092static int dialog_busy = FALSE; /* TRUE when gui_mch_dialog() wants the 2093 keys */ 2094 2095# define INLINE_KEY_BUFFER_SIZE 80 2096 static pascal OSStatus 2097gui_mac_unicode_key_event( 2098 EventHandlerCallRef nextHandler, 2099 EventRef theEvent) 2100{ 2101 /* Multibyte-friendly key event handler */ 2102 OSStatus err = -1; 2103 UInt32 actualSize; 2104 UniChar *text; 2105 char_u result[INLINE_KEY_BUFFER_SIZE]; 2106 short len = 0; 2107 UInt32 key_sym; 2108 char charcode; 2109 int key_char; 2110 UInt32 modifiers, vimModifiers; 2111 size_t encLen; 2112 char_u *to = NULL; 2113 Boolean isSpecial = FALSE; 2114 int i; 2115 EventRef keyEvent; 2116 2117 /* Mask the mouse (as per user setting) */ 2118 if (p_mh) 2119 ObscureCursor(); 2120 2121 /* Don't use the keys when the dialog wants them. */ 2122 if (dialog_busy) 2123 return eventNotHandledErr; 2124 2125 if (noErr != GetEventParameter(theEvent, kEventParamTextInputSendText, 2126 typeUnicodeText, NULL, 0, &actualSize, NULL)) 2127 return eventNotHandledErr; 2128 2129 text = (UniChar *)alloc(actualSize); 2130 if (!text) 2131 return eventNotHandledErr; 2132 2133 err = GetEventParameter(theEvent, kEventParamTextInputSendText, 2134 typeUnicodeText, NULL, actualSize, NULL, text); 2135 require_noerr(err, done); 2136 2137 err = GetEventParameter(theEvent, kEventParamTextInputSendKeyboardEvent, 2138 typeEventRef, NULL, sizeof(EventRef), NULL, &keyEvent); 2139 require_noerr(err, done); 2140 2141 err = GetEventParameter(keyEvent, kEventParamKeyModifiers, 2142 typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers); 2143 require_noerr(err, done); 2144 2145 err = GetEventParameter(keyEvent, kEventParamKeyCode, 2146 typeUInt32, NULL, sizeof(UInt32), NULL, &key_sym); 2147 require_noerr(err, done); 2148 2149 err = GetEventParameter(keyEvent, kEventParamKeyMacCharCodes, 2150 typeChar, NULL, sizeof(char), NULL, &charcode); 2151 require_noerr(err, done); 2152 2153#ifndef USE_CMD_KEY 2154 if (modifiers & cmdKey) 2155 goto done; /* Let system handle Cmd+... */ 2156#endif 2157 2158 key_char = charcode; 2159 vimModifiers = EventModifiers2VimModifiers(modifiers); 2160 2161 /* Find the special key (eg., for cursor keys) */ 2162 if (actualSize <= sizeof(UniChar) && 2163 ((text[0] < 0x20) || (text[0] == 0x7f))) 2164 { 2165 for (i = 0; special_keys[i].key_sym != (KeySym)0; ++i) 2166 if (special_keys[i].key_sym == key_sym) 2167 { 2168 key_char = TO_SPECIAL(special_keys[i].vim_code0, 2169 special_keys[i].vim_code1); 2170 key_char = simplify_key(key_char, 2171 (int *)&vimModifiers); 2172 isSpecial = TRUE; 2173 break; 2174 } 2175 } 2176 2177 /* Intercept CMD-. and CTRL-c */ 2178 if (((modifiers & controlKey) && key_char == 'c') || 2179 ((modifiers & cmdKey) && key_char == '.')) 2180 got_int = TRUE; 2181 2182 if (!isSpecial) 2183 { 2184 /* remove SHIFT for keys that are already shifted, e.g., 2185 * '(' and '*' */ 2186 if (key_char < 0x100 && !isalpha(key_char) && isprint(key_char)) 2187 vimModifiers &= ~MOD_MASK_SHIFT; 2188 2189 /* remove CTRL from keys that already have it */ 2190 if (key_char < 0x20) 2191 vimModifiers &= ~MOD_MASK_CTRL; 2192 2193 /* don't process unicode characters here */ 2194 if (!IS_SPECIAL(key_char)) 2195 { 2196 /* Following code to simplify and consolidate vimModifiers 2197 * taken liberally from gui_w48.c */ 2198 key_char = simplify_key(key_char, (int *)&vimModifiers); 2199 2200 /* Interpret META, include SHIFT, etc. */ 2201 key_char = extract_modifiers(key_char, (int *)&vimModifiers); 2202 if (key_char == CSI) 2203 key_char = K_CSI; 2204 2205 if (IS_SPECIAL(key_char)) 2206 isSpecial = TRUE; 2207 } 2208 } 2209 2210 if (vimModifiers) 2211 { 2212 result[len++] = CSI; 2213 result[len++] = KS_MODIFIER; 2214 result[len++] = vimModifiers; 2215 } 2216 2217 if (isSpecial && IS_SPECIAL(key_char)) 2218 { 2219 result[len++] = CSI; 2220 result[len++] = K_SECOND(key_char); 2221 result[len++] = K_THIRD(key_char); 2222 } 2223 else 2224 { 2225 encLen = actualSize; 2226 to = mac_utf16_to_enc(text, actualSize, &encLen); 2227 if (to) 2228 { 2229 /* This is basically add_to_input_buf_csi() */ 2230 for (i = 0; i < encLen && len < (INLINE_KEY_BUFFER_SIZE-1); ++i) 2231 { 2232 result[len++] = to[i]; 2233 if (to[i] == CSI) 2234 { 2235 result[len++] = KS_EXTRA; 2236 result[len++] = (int)KE_CSI; 2237 } 2238 } 2239 vim_free(to); 2240 } 2241 } 2242 2243 add_to_input_buf(result, len); 2244 err = noErr; 2245 2246done: 2247 vim_free(text); 2248 if (err == noErr) 2249 { 2250 /* Fake event to wake up WNE (required to get 2251 * key repeat working */ 2252 PostEvent(keyUp, 0); 2253 return noErr; 2254 } 2255 2256 return eventNotHandledErr; 2257} 2258#else 2259 void 2260gui_mac_doKeyEvent(EventRecord *theEvent) 2261{ 2262 /* TODO: add support for COMMAND KEY */ 2263 long menu; 2264 unsigned char string[20]; 2265 short num, i; 2266 short len = 0; 2267 KeySym key_sym; 2268 int key_char; 2269 int modifiers; 2270 int simplify = FALSE; 2271 2272 /* Mask the mouse (as per user setting) */ 2273 if (p_mh) 2274 ObscureCursor(); 2275 2276 /* Get the key code and it's ASCII representation */ 2277 key_sym = ((theEvent->message & keyCodeMask) >> 8); 2278 key_char = theEvent->message & charCodeMask; 2279 num = 1; 2280 2281 /* Intercept CTRL-C */ 2282 if (theEvent->modifiers & controlKey) 2283 { 2284 if (key_char == Ctrl_C && ctrl_c_interrupts) 2285 got_int = TRUE; 2286 else if ((theEvent->modifiers & ~(controlKey|shiftKey)) == 0 2287 && (key_char == '2' || key_char == '6')) 2288 { 2289 /* CTRL-^ and CTRL-@ don't work in the normal way. */ 2290 if (key_char == '2') 2291 key_char = Ctrl_AT; 2292 else 2293 key_char = Ctrl_HAT; 2294 theEvent->modifiers = 0; 2295 } 2296 } 2297 2298 /* Intercept CMD-. */ 2299 if (theEvent->modifiers & cmdKey) 2300 if (key_char == '.') 2301 got_int = TRUE; 2302 2303 /* Handle command key as per menu */ 2304 /* TODO: should override be allowed? Require YAO or could use 'winaltkey' */ 2305 if (theEvent->modifiers & cmdKey) 2306 /* Only accept CMD alone or with CAPLOCKS and the mouse button. 2307 * Why the mouse button? */ 2308 if ((theEvent->modifiers & (~(cmdKey | btnState | alphaLock))) == 0) 2309 { 2310 menu = MenuKey(key_char); 2311 if (HiWord(menu)) 2312 { 2313 gui_mac_handle_menu(menu); 2314 return; 2315 } 2316 } 2317 2318 /* Convert the modifiers */ 2319 modifiers = EventModifiers2VimModifiers(theEvent->modifiers); 2320 2321 2322 /* Handle special keys. */ 2323#if 0 2324 /* Why has this been removed? */ 2325 if (!(theEvent->modifiers & (cmdKey | controlKey | rightControlKey))) 2326#endif 2327 { 2328 /* Find the special key (for non-printable keyt_char) */ 2329 if ((key_char < 0x20) || (key_char == 0x7f)) 2330 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) 2331 if (special_keys[i].key_sym == key_sym) 2332 { 2333# if 0 2334 /* We currently don't have not so special key */ 2335 if (special_keys[i].vim_code1 == NUL) 2336 key_char = special_keys[i].vim_code0; 2337 else 2338# endif 2339 key_char = TO_SPECIAL(special_keys[i].vim_code0, 2340 special_keys[i].vim_code1); 2341 simplify = TRUE; 2342 break; 2343 } 2344 } 2345 2346 /* For some keys the modifier is included in the char itself. */ 2347 if (simplify || key_char == TAB || key_char == ' ') 2348 key_char = simplify_key(key_char, &modifiers); 2349 2350 /* Add the modifier to the input bu if needed */ 2351 /* Do not want SHIFT-A or CTRL-A with modifier */ 2352 if (!IS_SPECIAL(key_char) 2353 && key_sym != vk_Space 2354 && key_sym != vk_Tab 2355 && key_sym != vk_Return 2356 && key_sym != vk_Enter 2357 && key_sym != vk_Esc) 2358 { 2359#if 1 2360 /* Clear modifiers when only one modifier is set */ 2361 if ((modifiers == MOD_MASK_SHIFT) 2362 || (modifiers == MOD_MASK_CTRL) 2363 || (modifiers == MOD_MASK_ALT)) 2364 modifiers = 0; 2365#else 2366 if (modifiers & MOD_MASK_CTRL) 2367 modifiers = modifiers & ~MOD_MASK_CTRL; 2368 if (modifiers & MOD_MASK_ALT) 2369 modifiers = modifiers & ~MOD_MASK_ALT; 2370 if (modifiers & MOD_MASK_SHIFT) 2371 modifiers = modifiers & ~MOD_MASK_SHIFT; 2372#endif 2373 } 2374 if (modifiers) 2375 { 2376 string[len++] = CSI; 2377 string[len++] = KS_MODIFIER; 2378 string[len++] = modifiers; 2379 } 2380 2381 if (IS_SPECIAL(key_char)) 2382 { 2383 string[len++] = CSI; 2384 string[len++] = K_SECOND(key_char); 2385 string[len++] = K_THIRD(key_char); 2386 } 2387 else 2388 { 2389#ifdef FEAT_MBYTE 2390 /* Convert characters when needed (e.g., from MacRoman to latin1). 2391 * This doesn't work for the NUL byte. */ 2392 if (input_conv.vc_type != CONV_NONE && key_char > 0) 2393 { 2394 char_u from[2], *to; 2395 int l; 2396 2397 from[0] = key_char; 2398 from[1] = NUL; 2399 l = 1; 2400 to = string_convert(&input_conv, from, &l); 2401 if (to != NULL) 2402 { 2403 for (i = 0; i < l && len < 19; i++) 2404 { 2405 if (to[i] == CSI) 2406 { 2407 string[len++] = KS_EXTRA; 2408 string[len++] = KE_CSI; 2409 } 2410 else 2411 string[len++] = to[i]; 2412 } 2413 vim_free(to); 2414 } 2415 else 2416 string[len++] = key_char; 2417 } 2418 else 2419#endif 2420 string[len++] = key_char; 2421 } 2422 2423 if (len == 1 && string[0] == CSI) 2424 { 2425 /* Turn CSI into K_CSI. */ 2426 string[ len++ ] = KS_EXTRA; 2427 string[ len++ ] = KE_CSI; 2428 } 2429 2430 add_to_input_buf(string, len); 2431} 2432#endif 2433 2434/* 2435 * Handle MouseClick 2436 */ 2437 void 2438gui_mac_doMouseDownEvent(EventRecord *theEvent) 2439{ 2440 short thePart; 2441 WindowPtr whichWindow; 2442 2443 thePart = FindWindow(theEvent->where, &whichWindow); 2444 2445#ifdef FEAT_GUI_TABLINE 2446 /* prevent that the vim window size changes if it's activated by a 2447 click into the tab pane */ 2448 if (whichWindow == drawer) 2449 return; 2450#endif 2451 2452 switch (thePart) 2453 { 2454 case (inDesk): 2455 /* TODO: what to do? */ 2456 break; 2457 2458 case (inMenuBar): 2459 gui_mac_handle_menu(MenuSelect(theEvent->where)); 2460 break; 2461 2462 case (inContent): 2463 gui_mac_doInContentClick(theEvent, whichWindow); 2464 break; 2465 2466 case (inDrag): 2467 gui_mac_doInDragClick(theEvent->where, whichWindow); 2468 break; 2469 2470 case (inGrow): 2471 gui_mac_doInGrowClick(theEvent->where, whichWindow); 2472 break; 2473 2474 case (inGoAway): 2475 if (TrackGoAway(whichWindow, theEvent->where)) 2476 gui_shell_closed(); 2477 break; 2478 2479 case (inZoomIn): 2480 case (inZoomOut): 2481 gui_mac_doInZoomClick(theEvent, whichWindow); 2482 break; 2483 } 2484} 2485 2486/* 2487 * Handle MouseMoved 2488 * [this event is a moving in and out of a region] 2489 */ 2490 void 2491gui_mac_doMouseMovedEvent(EventRecord *event) 2492{ 2493 Point thePoint; 2494 int_u vimModifiers; 2495 2496 thePoint = event->where; 2497 GlobalToLocal(&thePoint); 2498 vimModifiers = EventModifiers2VimMouseModifiers(event->modifiers); 2499 2500 if (!Button()) 2501 gui_mouse_moved(thePoint.h, thePoint.v); 2502 else 2503 if (!clickIsPopup) 2504 gui_send_mouse_event(MOUSE_DRAG, thePoint.h, 2505 thePoint.v, FALSE, vimModifiers); 2506 2507 /* Reset the region from which we move in and out */ 2508 SetRect(&dragRect, FILL_X(X_2_COL(thePoint.h)), 2509 FILL_Y(Y_2_ROW(thePoint.v)), 2510 FILL_X(X_2_COL(thePoint.h)+1), 2511 FILL_Y(Y_2_ROW(thePoint.v)+1)); 2512 2513 if (dragRectEnbl) 2514 dragRectControl = kCreateRect; 2515 2516} 2517 2518/* 2519 * Handle the mouse release 2520 */ 2521 void 2522gui_mac_doMouseUpEvent(EventRecord *theEvent) 2523{ 2524 Point thePoint; 2525 int_u vimModifiers; 2526 2527 /* TODO: Properly convert the Contextual menu mouse-up */ 2528 /* Potential source of the double menu */ 2529 lastMouseTick = theEvent->when; 2530 dragRectEnbl = FALSE; 2531 dragRectControl = kCreateEmpty; 2532 thePoint = theEvent->where; 2533 GlobalToLocal(&thePoint); 2534 2535 vimModifiers = EventModifiers2VimMouseModifiers(theEvent->modifiers); 2536 if (clickIsPopup) 2537 { 2538 vimModifiers &= ~MOUSE_CTRL; 2539 clickIsPopup = FALSE; 2540 } 2541 gui_send_mouse_event(MOUSE_RELEASE, thePoint.h, thePoint.v, FALSE, vimModifiers); 2542} 2543 2544 static pascal OSStatus 2545gui_mac_mouse_wheel(EventHandlerCallRef nextHandler, EventRef theEvent, 2546 void *data) 2547{ 2548 Point point; 2549 Rect bounds; 2550 UInt32 mod; 2551 SInt32 delta; 2552 int_u vim_mod; 2553 EventMouseWheelAxis axis; 2554 2555 if (noErr == GetEventParameter(theEvent, kEventParamMouseWheelAxis, 2556 typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis) 2557 && axis != kEventMouseWheelAxisY) 2558 goto bail; /* Vim only does up-down scrolling */ 2559 2560 if (noErr != GetEventParameter(theEvent, kEventParamMouseWheelDelta, 2561 typeSInt32, NULL, sizeof(SInt32), NULL, &delta)) 2562 goto bail; 2563 if (noErr != GetEventParameter(theEvent, kEventParamMouseLocation, 2564 typeQDPoint, NULL, sizeof(Point), NULL, &point)) 2565 goto bail; 2566 if (noErr != GetEventParameter(theEvent, kEventParamKeyModifiers, 2567 typeUInt32, NULL, sizeof(UInt32), NULL, &mod)) 2568 goto bail; 2569 2570 vim_mod = 0; 2571 if (mod & shiftKey) 2572 vim_mod |= MOUSE_SHIFT; 2573 if (mod & controlKey) 2574 vim_mod |= MOUSE_CTRL; 2575 if (mod & optionKey) 2576 vim_mod |= MOUSE_ALT; 2577 2578 if (noErr == GetWindowBounds(gui.VimWindow, kWindowContentRgn, &bounds)) 2579 { 2580 point.h -= bounds.left; 2581 point.v -= bounds.top; 2582 } 2583 2584 gui_send_mouse_event((delta > 0) ? MOUSE_4 : MOUSE_5, 2585 point.h, point.v, FALSE, vim_mod); 2586 2587 /* post a bogus event to wake up WaitNextEvent */ 2588 PostEvent(keyUp, 0); 2589 2590 return noErr; 2591 2592bail: 2593 /* 2594 * when we fail give any additional callback handler a chance to perform 2595 * it's actions 2596 */ 2597 return CallNextEventHandler(nextHandler, theEvent); 2598} 2599 2600 void 2601gui_mch_mousehide(int hide) 2602{ 2603 /* TODO */ 2604} 2605 2606#if 0 2607 2608/* 2609 * This would be the normal way of invoking the contextual menu 2610 * but the Vim API doesn't seem to a support a request to get 2611 * the menu that we should display 2612 */ 2613 void 2614gui_mac_handle_contextual_menu(event) 2615 EventRecord *event; 2616{ 2617/* 2618 * Clone PopUp to use menu 2619 * Create a object descriptor for the current selection 2620 * Call the procedure 2621 */ 2622 2623// Call to Handle Popup 2624 OSStatus status = ContextualMenuSelect(CntxMenu, event->where, false, kCMHelpItemNoHelp, "", NULL, &CntxType, &CntxMenuID, &CntxMenuItem); 2625 2626 if (status != noErr) 2627 return; 2628 2629 if (CntxType == kCMMenuItemSelected) 2630 { 2631 /* Handle the menu CntxMenuID, CntxMenuItem */ 2632 /* The submenu can be handle directly by gui_mac_handle_menu */ 2633 /* But what about the current menu, is the meny changed by ContextualMenuSelect */ 2634 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem); 2635 } 2636 else if (CntxMenuID == kCMShowHelpSelected) 2637 { 2638 /* Should come up with the help */ 2639 } 2640 2641} 2642#endif 2643 2644/* 2645 * Handle menubar selection 2646 */ 2647 void 2648gui_mac_handle_menu(long menuChoice) 2649{ 2650 short menu = HiWord(menuChoice); 2651 short item = LoWord(menuChoice); 2652 vimmenu_T *theVimMenu = root_menu; 2653 2654 if (menu == 256) /* TODO: use constant or gui.xyz */ 2655 { 2656 if (item == 1) 2657 gui_mch_beep(); /* TODO: Popup dialog or do :intro */ 2658 } 2659 else if (item != 0) 2660 { 2661 theVimMenu = gui_mac_get_vim_menu(menu, item, root_menu); 2662 2663 if (theVimMenu) 2664 gui_menu_cb(theVimMenu); 2665 } 2666 HiliteMenu(0); 2667} 2668 2669/* 2670 * Dispatch the event to proper handler 2671 */ 2672 2673 void 2674gui_mac_handle_event(EventRecord *event) 2675{ 2676 OSErr error; 2677 2678 /* Handle contextual menu right now (if needed) */ 2679 if (IsShowContextualMenuClick(event)) 2680 { 2681# if 0 2682 gui_mac_handle_contextual_menu(event); 2683# else 2684 gui_mac_doMouseDownEvent(event); 2685# endif 2686 return; 2687 } 2688 2689 /* Handle normal event */ 2690 switch (event->what) 2691 { 2692#ifndef USE_CARBONKEYHANDLER 2693 case (keyDown): 2694 case (autoKey): 2695 gui_mac_doKeyEvent(event); 2696 break; 2697#endif 2698 case (keyUp): 2699 /* We don't care about when the key is released */ 2700 break; 2701 2702 case (mouseDown): 2703 gui_mac_doMouseDownEvent(event); 2704 break; 2705 2706 case (mouseUp): 2707 gui_mac_doMouseUpEvent(event); 2708 break; 2709 2710 case (updateEvt): 2711 gui_mac_doUpdateEvent(event); 2712 break; 2713 2714 case (diskEvt): 2715 /* We don't need special handling for disk insertion */ 2716 break; 2717 2718 case (activateEvt): 2719 gui_mac_doActivateEvent(event); 2720 break; 2721 2722 case (osEvt): 2723 switch ((event->message >> 24) & 0xFF) 2724 { 2725 case (0xFA): /* mouseMovedMessage */ 2726 gui_mac_doMouseMovedEvent(event); 2727 break; 2728 case (0x01): /* suspendResumeMessage */ 2729 gui_mac_doSuspendEvent(event); 2730 break; 2731 } 2732 break; 2733 2734#ifdef USE_AEVENT 2735 case (kHighLevelEvent): 2736 /* Someone's talking to us, through AppleEvents */ 2737 error = AEProcessAppleEvent(event); /* TODO: Error Handling */ 2738 break; 2739#endif 2740 } 2741} 2742 2743/* 2744 * ------------------------------------------------------------ 2745 * Unknown Stuff 2746 * ------------------------------------------------------------ 2747 */ 2748 2749 2750 GuiFont 2751gui_mac_find_font(char_u *font_name) 2752{ 2753 char_u c; 2754 char_u *p; 2755 char_u pFontName[256]; 2756 Str255 systemFontname; 2757 short font_id; 2758 short size=9; 2759 GuiFont font; 2760#if 0 2761 char_u *fontNamePtr; 2762#endif 2763 2764 for (p = font_name; ((*p != 0) && (*p != ':')); p++) 2765 ; 2766 2767 c = *p; 2768 *p = 0; 2769 2770#if 1 2771 STRCPY(&pFontName[1], font_name); 2772 pFontName[0] = STRLEN(font_name); 2773 *p = c; 2774 2775 /* Get the font name, minus the style suffix (:h, etc) */ 2776 char_u fontName[256]; 2777 char_u *styleStart = vim_strchr(font_name, ':'); 2778 size_t fontNameLen = styleStart ? styleStart - font_name : STRLEN(fontName); 2779 vim_strncpy(fontName, font_name, fontNameLen); 2780 2781 ATSUFontID fontRef; 2782 FMFontStyle fontStyle; 2783 font_id = 0; 2784 2785 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], kFontFullName, 2786 kFontMacintoshPlatform, kFontNoScriptCode, kFontNoLanguageCode, 2787 &fontRef) == noErr) 2788 { 2789 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr) 2790 font_id = 0; 2791 } 2792 2793 if (font_id == 0) 2794 { 2795 /* 2796 * Try again, this time replacing underscores in the font name 2797 * with spaces (:set guifont allows the two to be used 2798 * interchangeably; the Font Manager doesn't). 2799 */ 2800 int i, changed = FALSE; 2801 2802 for (i = pFontName[0]; i > 0; --i) 2803 { 2804 if (pFontName[i] == '_') 2805 { 2806 pFontName[i] = ' '; 2807 changed = TRUE; 2808 } 2809 } 2810 if (changed) 2811 if (ATSUFindFontFromName(&pFontName[1], pFontName[0], 2812 kFontFullName, kFontNoPlatformCode, kFontNoScriptCode, 2813 kFontNoLanguageCode, &fontRef) == noErr) 2814 { 2815 if (FMGetFontFamilyInstanceFromFont(fontRef, &font_id, &fontStyle) != noErr) 2816 font_id = 0; 2817 } 2818 } 2819 2820#else 2821 /* name = C2Pascal_save(menu->dname); */ 2822 fontNamePtr = C2Pascal_save_and_remove_backslash(font_name); 2823 2824 GetFNum(fontNamePtr, &font_id); 2825#endif 2826 2827 2828 if (font_id == 0) 2829 { 2830 /* Oups, the system font was it the one the user want */ 2831 2832 if (FMGetFontFamilyName(systemFont, systemFontname) != noErr) 2833 return NOFONT; 2834 if (!EqualString(pFontName, systemFontname, false, false)) 2835 return NOFONT; 2836 } 2837 if (*p == ':') 2838 { 2839 p++; 2840 /* Set the values found after ':' */ 2841 while (*p) 2842 { 2843 switch (*p++) 2844 { 2845 case 'h': 2846 size = points_to_pixels(p, &p, TRUE); 2847 break; 2848 /* 2849 * TODO: Maybe accept width and styles 2850 */ 2851 } 2852 while (*p == ':') 2853 p++; 2854 } 2855 } 2856 2857 if (size < 1) 2858 size = 1; /* Avoid having a size of 0 with system font */ 2859 2860 font = (size << 16) + ((long) font_id & 0xFFFF); 2861 2862 return font; 2863} 2864 2865/* 2866 * ------------------------------------------------------------ 2867 * GUI_MCH functionality 2868 * ------------------------------------------------------------ 2869 */ 2870 2871/* 2872 * Parse the GUI related command-line arguments. Any arguments used are 2873 * deleted from argv, and *argc is decremented accordingly. This is called 2874 * when vim is started, whether or not the GUI has been started. 2875 */ 2876 void 2877gui_mch_prepare(int *argc, char **argv) 2878{ 2879 /* TODO: Move most of this stuff toward gui_mch_init */ 2880#ifdef USE_EXE_NAME 2881 FSSpec applDir; 2882# ifndef USE_FIND_BUNDLE_PATH 2883 short applVRefNum; 2884 long applDirID; 2885 Str255 volName; 2886# else 2887 ProcessSerialNumber psn; 2888 FSRef applFSRef; 2889# endif 2890#endif 2891 2892#if 0 2893 InitCursor(); 2894 2895 RegisterAppearanceClient(); 2896 2897#ifdef USE_AEVENT 2898 (void) InstallAEHandlers(); 2899#endif 2900 2901 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */ 2902 2903 AppendMenu(pomme, "\pAbout VIM"); 2904 2905 InsertMenu(pomme, 0); 2906 2907 DrawMenuBar(); 2908 2909 2910#ifndef USE_OFFSETED_WINDOW 2911 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11); 2912#else 2913 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11); 2914#endif 2915 2916 2917 CreateNewWindow(kDocumentWindowClass, 2918 kWindowResizableAttribute | kWindowCollapseBoxAttribute, 2919 &windRect, &gui.VimWindow); 2920 SetPortWindowPort(gui.VimWindow); 2921 2922 gui.char_width = 7; 2923 gui.char_height = 11; 2924 gui.char_ascent = 6; 2925 gui.num_rows = 24; 2926 gui.num_cols = 80; 2927 gui.in_focus = TRUE; /* For the moment -> syn. of front application */ 2928 2929 gScrollAction = NewControlActionUPP(gui_mac_scroll_action); 2930 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb); 2931 2932 dragRectEnbl = FALSE; 2933 dragRgn = NULL; 2934 dragRectControl = kCreateEmpty; 2935 cursorRgn = NewRgn(); 2936#endif 2937#ifdef USE_EXE_NAME 2938# ifndef USE_FIND_BUNDLE_PATH 2939 HGetVol(volName, &applVRefNum, &applDirID); 2940 /* TN2015: mention a possible bad VRefNum */ 2941 FSMakeFSSpec(applVRefNum, applDirID, "\p", &applDir); 2942# else 2943 /* OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr) 2944 * of TN2015 2945 */ 2946 (void)GetCurrentProcess(&psn); 2947 /* if (err != noErr) return err; */ 2948 2949 (void)GetProcessBundleLocation(&psn, &applFSRef); 2950 /* if (err != noErr) return err; */ 2951 2952 (void)FSGetCatalogInfo(&applFSRef, kFSCatInfoNone, NULL, NULL, &applDir, NULL); 2953 2954 /* This technic return NIL when we disallow_gui */ 2955# endif 2956 exe_name = FullPathFromFSSpec_save(applDir); 2957#endif 2958} 2959 2960#ifndef ALWAYS_USE_GUI 2961/* 2962 * Check if the GUI can be started. Called before gvimrc is sourced. 2963 * Return OK or FAIL. 2964 */ 2965 int 2966gui_mch_init_check(void) 2967{ 2968 /* TODO: For MacOS X find a way to return FAIL, if the user logged in 2969 * using the >console 2970 */ 2971 if (disallow_gui) /* see main.c for reason to disallow */ 2972 return FAIL; 2973 return OK; 2974} 2975#endif 2976 2977 static OSErr 2978receiveHandler(WindowRef theWindow, void *handlerRefCon, DragRef theDrag) 2979{ 2980 int x, y; 2981 int_u modifiers; 2982 char_u **fnames = NULL; 2983 int count; 2984 int i, j; 2985 2986 /* Get drop position, modifiers and count of items */ 2987 { 2988 Point point; 2989 SInt16 mouseUpModifiers; 2990 UInt16 countItem; 2991 2992 GetDragMouse(theDrag, &point, NULL); 2993 GlobalToLocal(&point); 2994 x = point.h; 2995 y = point.v; 2996 GetDragModifiers(theDrag, NULL, NULL, &mouseUpModifiers); 2997 modifiers = EventModifiers2VimMouseModifiers(mouseUpModifiers); 2998 CountDragItems(theDrag, &countItem); 2999 count = countItem; 3000 } 3001 3002 fnames = (char_u **)alloc(count * sizeof(char_u *)); 3003 if (fnames == NULL) 3004 return dragNotAcceptedErr; 3005 3006 /* Get file names dropped */ 3007 for (i = j = 0; i < count; ++i) 3008 { 3009 DragItemRef item; 3010 OSErr err; 3011 Size size; 3012 FlavorType type = flavorTypeHFS; 3013 HFSFlavor hfsFlavor; 3014 3015 fnames[i] = NULL; 3016 GetDragItemReferenceNumber(theDrag, i + 1, &item); 3017 err = GetFlavorDataSize(theDrag, item, type, &size); 3018 if (err != noErr || size > sizeof(hfsFlavor)) 3019 continue; 3020 err = GetFlavorData(theDrag, item, type, &hfsFlavor, &size, 0); 3021 if (err != noErr) 3022 continue; 3023 fnames[j++] = FullPathFromFSSpec_save(hfsFlavor.fileSpec); 3024 } 3025 count = j; 3026 3027 gui_handle_drop(x, y, modifiers, fnames, count); 3028 3029 /* Fake mouse event to wake from stall */ 3030 PostEvent(mouseUp, 0); 3031 3032 return noErr; 3033} 3034 3035/* 3036 * Initialise the GUI. Create all the windows, set up all the call-backs 3037 * etc. 3038 */ 3039 int 3040gui_mch_init(void) 3041{ 3042 /* TODO: Move most of this stuff toward gui_mch_init */ 3043 Rect windRect; 3044 MenuHandle pomme; 3045 EventHandlerRef mouseWheelHandlerRef; 3046 EventTypeSpec eventTypeSpec; 3047 ControlRef rootControl; 3048 3049 if (Gestalt(gestaltSystemVersion, &gMacSystemVersion) != noErr) 3050 gMacSystemVersion = 0x1000; /* TODO: Default to minimum sensible value */ 3051 3052#if 1 3053 InitCursor(); 3054 3055 RegisterAppearanceClient(); 3056 3057#ifdef USE_AEVENT 3058 (void) InstallAEHandlers(); 3059#endif 3060 3061 pomme = NewMenu(256, "\p\024"); /* 0x14= = Apple Menu */ 3062 3063 AppendMenu(pomme, "\pAbout VIM"); 3064 3065 InsertMenu(pomme, 0); 3066 3067 DrawMenuBar(); 3068 3069 3070#ifndef USE_OFFSETED_WINDOW 3071 SetRect(&windRect, 10, 48, 10+80*7 + 16, 48+24*11); 3072#else 3073 SetRect(&windRect, 300, 40, 300+80*7 + 16, 40+24*11); 3074#endif 3075 3076 gui.VimWindow = NewCWindow(nil, &windRect, "\pgVim on Macintosh", true, 3077 zoomDocProc, 3078 (WindowPtr)-1L, true, 0); 3079 CreateRootControl(gui.VimWindow, &rootControl); 3080 InstallReceiveHandler((DragReceiveHandlerUPP)receiveHandler, 3081 gui.VimWindow, NULL); 3082 SetPortWindowPort(gui.VimWindow); 3083 3084 gui.char_width = 7; 3085 gui.char_height = 11; 3086 gui.char_ascent = 6; 3087 gui.num_rows = 24; 3088 gui.num_cols = 80; 3089 gui.in_focus = TRUE; /* For the moment -> syn. of front application */ 3090 3091 gScrollAction = NewControlActionUPP(gui_mac_scroll_action); 3092 gScrollDrag = NewControlActionUPP(gui_mac_drag_thumb); 3093 3094 /* Install Carbon event callbacks. */ 3095 (void)InstallFontPanelHandler(); 3096 3097 dragRectEnbl = FALSE; 3098 dragRgn = NULL; 3099 dragRectControl = kCreateEmpty; 3100 cursorRgn = NewRgn(); 3101#endif 3102 /* Display any pending error messages */ 3103 display_errors(); 3104 3105 /* Get background/foreground colors from system */ 3106 /* TODO: do the appropriate call to get real defaults */ 3107 gui.norm_pixel = 0x00000000; 3108 gui.back_pixel = 0x00FFFFFF; 3109 3110 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc 3111 * file). */ 3112 set_normal_colors(); 3113 3114 /* 3115 * Check that none of the colors are the same as the background color. 3116 * Then store the current values as the defaults. 3117 */ 3118 gui_check_colors(); 3119 gui.def_norm_pixel = gui.norm_pixel; 3120 gui.def_back_pixel = gui.back_pixel; 3121 3122 /* Get the colors for the highlight groups (gui_check_colors() might have 3123 * changed them) */ 3124 highlight_gui_started(); 3125 3126 /* 3127 * Setting the gui constants 3128 */ 3129#ifdef FEAT_MENU 3130 gui.menu_height = 0; 3131#endif 3132 gui.scrollbar_height = gui.scrollbar_width = 15; /* cheat 1 overlap */ 3133 gui.border_offset = gui.border_width = 2; 3134 3135 /* If Quartz-style text anti aliasing is available (see 3136 gui_mch_draw_string() below), enable it for all font sizes. */ 3137 vim_setenv((char_u *)"QDTEXT_MINSIZE", (char_u *)"1"); 3138 3139 eventTypeSpec.eventClass = kEventClassMouse; 3140 eventTypeSpec.eventKind = kEventMouseWheelMoved; 3141 mouseWheelHandlerUPP = NewEventHandlerUPP(gui_mac_mouse_wheel); 3142 if (noErr != InstallApplicationEventHandler(mouseWheelHandlerUPP, 1, 3143 &eventTypeSpec, NULL, &mouseWheelHandlerRef)) 3144 { 3145 mouseWheelHandlerRef = NULL; 3146 DisposeEventHandlerUPP(mouseWheelHandlerUPP); 3147 mouseWheelHandlerUPP = NULL; 3148 } 3149 3150#ifdef USE_CARBONKEYHANDLER 3151 InterfaceTypeList supportedServices = { kUnicodeDocument }; 3152 NewTSMDocument(1, supportedServices, &gTSMDocument, 0); 3153 3154 /* We don't support inline input yet, use input window by default */ 3155 UseInputWindow(gTSMDocument, TRUE); 3156 3157 /* Should we activate the document by default? */ 3158 // ActivateTSMDocument(gTSMDocument); 3159 3160 EventTypeSpec textEventTypes[] = { 3161 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }, 3162 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, 3163 { kEventClassTextInput, kEventTextInputPosToOffset }, 3164 { kEventClassTextInput, kEventTextInputOffsetToPos }, 3165 }; 3166 3167 keyEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_text_input); 3168 if (noErr != InstallApplicationEventHandler(keyEventHandlerUPP, 3169 NR_ELEMS(textEventTypes), 3170 textEventTypes, NULL, NULL)) 3171 { 3172 DisposeEventHandlerUPP(keyEventHandlerUPP); 3173 keyEventHandlerUPP = NULL; 3174 } 3175 3176 EventTypeSpec windowEventTypes[] = { 3177 { kEventClassWindow, kEventWindowActivated }, 3178 { kEventClassWindow, kEventWindowDeactivated }, 3179 }; 3180 3181 /* Install window event handler to support TSMDocument activate and 3182 * deactivate */ 3183 winEventHandlerUPP = NewEventHandlerUPP(gui_mac_handle_window_activate); 3184 if (noErr != InstallWindowEventHandler(gui.VimWindow, 3185 winEventHandlerUPP, 3186 NR_ELEMS(windowEventTypes), 3187 windowEventTypes, NULL, NULL)) 3188 { 3189 DisposeEventHandlerUPP(winEventHandlerUPP); 3190 winEventHandlerUPP = NULL; 3191 } 3192#endif 3193 3194/* 3195#ifdef FEAT_MBYTE 3196 set_option_value((char_u *)"encoding", 0L, (char_u *)"utf-8", 0); 3197#endif 3198*/ 3199 3200#ifdef FEAT_GUI_TABLINE 3201 /* 3202 * Create the tabline 3203 */ 3204 initialise_tabline(); 3205#endif 3206 3207 /* TODO: Load bitmap if using TOOLBAR */ 3208 return OK; 3209} 3210 3211/* 3212 * Called when the foreground or background color has been changed. 3213 */ 3214 void 3215gui_mch_new_colors(void) 3216{ 3217 /* TODO: 3218 * This proc is called when Normal is set to a value 3219 * so what msut be done? I don't know 3220 */ 3221} 3222 3223/* 3224 * Open the GUI window which was created by a call to gui_mch_init(). 3225 */ 3226 int 3227gui_mch_open(void) 3228{ 3229 ShowWindow(gui.VimWindow); 3230 3231 if (gui_win_x != -1 && gui_win_y != -1) 3232 gui_mch_set_winpos(gui_win_x, gui_win_y); 3233 3234 /* 3235 * Make the GUI the foreground process (in case it was launched 3236 * from the Terminal or via :gui). 3237 */ 3238 { 3239 ProcessSerialNumber psn; 3240 if (GetCurrentProcess(&psn) == noErr) 3241 SetFrontProcess(&psn); 3242 } 3243 3244 return OK; 3245} 3246 3247#ifdef USE_ATSUI_DRAWING 3248 static void 3249gui_mac_dispose_atsui_style(void) 3250{ 3251 if (p_macatsui && gFontStyle) 3252 ATSUDisposeStyle(gFontStyle); 3253#ifdef FEAT_MBYTE 3254 if (p_macatsui && gWideFontStyle) 3255 ATSUDisposeStyle(gWideFontStyle); 3256#endif 3257} 3258#endif 3259 3260 void 3261gui_mch_exit(int rc) 3262{ 3263 /* TODO: find out all what is missing here? */ 3264 DisposeRgn(cursorRgn); 3265 3266#ifdef USE_CARBONKEYHANDLER 3267 if (keyEventHandlerUPP) 3268 DisposeEventHandlerUPP(keyEventHandlerUPP); 3269#endif 3270 3271 if (mouseWheelHandlerUPP != NULL) 3272 DisposeEventHandlerUPP(mouseWheelHandlerUPP); 3273 3274#ifdef USE_ATSUI_DRAWING 3275 gui_mac_dispose_atsui_style(); 3276#endif 3277 3278#ifdef USE_CARBONKEYHANDLER 3279 FixTSMDocument(gTSMDocument); 3280 DeactivateTSMDocument(gTSMDocument); 3281 DeleteTSMDocument(gTSMDocument); 3282#endif 3283 3284 /* Exit to shell? */ 3285 exit(rc); 3286} 3287 3288/* 3289 * Get the position of the top left corner of the window. 3290 */ 3291 int 3292gui_mch_get_winpos(int *x, int *y) 3293{ 3294 /* TODO */ 3295 Rect bounds; 3296 OSStatus status; 3297 3298 /* Carbon >= 1.0.2, MacOS >= 8.5 */ 3299 status = GetWindowBounds(gui.VimWindow, kWindowStructureRgn, &bounds); 3300 3301 if (status != noErr) 3302 return FAIL; 3303 *x = bounds.left; 3304 *y = bounds.top; 3305 return OK; 3306 return FAIL; 3307} 3308 3309/* 3310 * Set the position of the top left corner of the window to the given 3311 * coordinates. 3312 */ 3313 void 3314gui_mch_set_winpos(int x, int y) 3315{ 3316 /* TODO: Should make sure the window is move within range 3317 * e.g.: y > ~16 [Menu bar], x > 0, x < screen width 3318 */ 3319 MoveWindowStructure(gui.VimWindow, x, y); 3320} 3321 3322 void 3323gui_mch_set_shellsize( 3324 int width, 3325 int height, 3326 int min_width, 3327 int min_height, 3328 int base_width, 3329 int base_height, 3330 int direction) 3331{ 3332 CGrafPtr VimPort; 3333 Rect VimBound; 3334 3335 if (gui.which_scrollbars[SBAR_LEFT]) 3336 { 3337 VimPort = GetWindowPort(gui.VimWindow); 3338 GetPortBounds(VimPort, &VimBound); 3339 VimBound.left = -gui.scrollbar_width; /* + 1;*/ 3340 SetPortBounds(VimPort, &VimBound); 3341 /* GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &winPortRect); ??*/ 3342 } 3343 else 3344 { 3345 VimPort = GetWindowPort(gui.VimWindow); 3346 GetPortBounds(VimPort, &VimBound); 3347 VimBound.left = 0; 3348 SetPortBounds(VimPort, &VimBound); 3349 } 3350 3351 SizeWindow(gui.VimWindow, width, height, TRUE); 3352 3353 gui_resize_shell(width, height); 3354} 3355 3356/* 3357 * Get the screen dimensions. 3358 * Allow 10 pixels for horizontal borders, 40 for vertical borders. 3359 * Is there no way to find out how wide the borders really are? 3360 * TODO: Add live update of those value on suspend/resume. 3361 */ 3362 void 3363gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) 3364{ 3365 GDHandle dominantDevice = GetMainDevice(); 3366 Rect screenRect = (**dominantDevice).gdRect; 3367 3368 *screen_w = screenRect.right - 10; 3369 *screen_h = screenRect.bottom - 40; 3370} 3371 3372 3373/* 3374 * Open the Font Panel and wait for the user to select a font and 3375 * close the panel. Then fill the buffer pointed to by font_name with 3376 * the name and size of the selected font and return the font's handle, 3377 * or NOFONT in case of an error. 3378 */ 3379 static GuiFont 3380gui_mac_select_font(char_u *font_name) 3381{ 3382 GuiFont selected_font = NOFONT; 3383 OSStatus status; 3384 FontSelectionQDStyle curr_font; 3385 3386 /* Initialize the Font Panel with the current font. */ 3387 curr_font.instance.fontFamily = gui.norm_font & 0xFFFF; 3388 curr_font.size = (gui.norm_font >> 16); 3389 /* TODO: set fontStyle once styles are supported in gui_mac_find_font() */ 3390 curr_font.instance.fontStyle = 0; 3391 curr_font.hasColor = false; 3392 curr_font.version = 0; /* version number of the style structure */ 3393 status = SetFontInfoForSelection(kFontSelectionQDType, 3394 /*numStyles=*/1, &curr_font, /*eventTarget=*/NULL); 3395 3396 gFontPanelInfo.family = curr_font.instance.fontFamily; 3397 gFontPanelInfo.style = curr_font.instance.fontStyle; 3398 gFontPanelInfo.size = curr_font.size; 3399 3400 /* Pop up the Font Panel. */ 3401 status = FPShowHideFontPanel(); 3402 if (status == noErr) 3403 { 3404 /* 3405 * The Font Panel is modeless. We really need it to be modal, 3406 * so we spin in an event loop until the panel is closed. 3407 */ 3408 gFontPanelInfo.isPanelVisible = true; 3409 while (gFontPanelInfo.isPanelVisible) 3410 { 3411 EventRecord e; 3412 WaitNextEvent(everyEvent, &e, /*sleep=*/20, /*mouseRgn=*/NULL); 3413 } 3414 3415 GetFontPanelSelection(font_name); 3416 selected_font = gui_mac_find_font(font_name); 3417 } 3418 return selected_font; 3419} 3420 3421#ifdef USE_ATSUI_DRAWING 3422 static void 3423gui_mac_create_atsui_style(void) 3424{ 3425 if (p_macatsui && gFontStyle == NULL) 3426 { 3427 if (ATSUCreateStyle(&gFontStyle) != noErr) 3428 gFontStyle = NULL; 3429 } 3430#ifdef FEAT_MBYTE 3431 if (p_macatsui && gWideFontStyle == NULL) 3432 { 3433 if (ATSUCreateStyle(&gWideFontStyle) != noErr) 3434 gWideFontStyle = NULL; 3435 } 3436#endif 3437 3438 p_macatsui_last = p_macatsui; 3439} 3440#endif 3441 3442/* 3443 * Initialise vim to use the font with the given name. Return FAIL if the font 3444 * could not be loaded, OK otherwise. 3445 */ 3446 int 3447gui_mch_init_font(char_u *font_name, int fontset) 3448{ 3449 /* TODO: Add support for bold italic underline proportional etc... */ 3450 Str255 suggestedFont = "\pMonaco"; 3451 int suggestedSize = 10; 3452 FontInfo font_info; 3453 short font_id; 3454 GuiFont font; 3455 char_u used_font_name[512]; 3456 3457#ifdef USE_ATSUI_DRAWING 3458 gui_mac_create_atsui_style(); 3459#endif 3460 3461 if (font_name == NULL) 3462 { 3463 /* First try to get the suggested font */ 3464 GetFNum(suggestedFont, &font_id); 3465 3466 if (font_id == 0) 3467 { 3468 /* Then pickup the standard application font */ 3469 font_id = GetAppFont(); 3470 STRCPY(used_font_name, "default"); 3471 } 3472 else 3473 STRCPY(used_font_name, "Monaco"); 3474 font = (suggestedSize << 16) + ((long) font_id & 0xFFFF); 3475 } 3476 else if (STRCMP(font_name, "*") == 0) 3477 { 3478 char_u *new_p_guifont; 3479 3480 font = gui_mac_select_font(used_font_name); 3481 if (font == NOFONT) 3482 return FAIL; 3483 3484 /* Set guifont to the name of the selected font. */ 3485 new_p_guifont = alloc(STRLEN(used_font_name) + 1); 3486 if (new_p_guifont != NULL) 3487 { 3488 STRCPY(new_p_guifont, used_font_name); 3489 vim_free(p_guifont); 3490 p_guifont = new_p_guifont; 3491 /* Replace spaces in the font name with underscores. */ 3492 for ( ; *new_p_guifont; ++new_p_guifont) 3493 { 3494 if (*new_p_guifont == ' ') 3495 *new_p_guifont = '_'; 3496 } 3497 } 3498 } 3499 else 3500 { 3501 font = gui_mac_find_font(font_name); 3502 vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1); 3503 3504 if (font == NOFONT) 3505 return FAIL; 3506 } 3507 3508 gui.norm_font = font; 3509 3510 hl_set_font_name(used_font_name); 3511 3512 TextSize(font >> 16); 3513 TextFont(font & 0xFFFF); 3514 3515 GetFontInfo(&font_info); 3516 3517 gui.char_ascent = font_info.ascent; 3518 gui.char_width = CharWidth('_'); 3519 gui.char_height = font_info.ascent + font_info.descent + p_linespace; 3520 3521#ifdef USE_ATSUI_DRAWING 3522 if (p_macatsui && gFontStyle) 3523 gui_mac_set_font_attributes(font); 3524#endif 3525 3526 return OK; 3527} 3528 3529/* 3530 * Adjust gui.char_height (after 'linespace' was changed). 3531 */ 3532 int 3533gui_mch_adjust_charheight(void) 3534{ 3535 FontInfo font_info; 3536 3537 GetFontInfo(&font_info); 3538 gui.char_height = font_info.ascent + font_info.descent + p_linespace; 3539 gui.char_ascent = font_info.ascent + p_linespace / 2; 3540 return OK; 3541} 3542 3543/* 3544 * Get a font structure for highlighting. 3545 */ 3546 GuiFont 3547gui_mch_get_font(char_u *name, int giveErrorIfMissing) 3548{ 3549 GuiFont font; 3550 3551 font = gui_mac_find_font(name); 3552 3553 if (font == NOFONT) 3554 { 3555 if (giveErrorIfMissing) 3556 EMSG2(_(e_font), name); 3557 return NOFONT; 3558 } 3559 /* 3560 * TODO : Accept only monospace 3561 */ 3562 3563 return font; 3564} 3565 3566#if defined(FEAT_EVAL) || defined(PROTO) 3567/* 3568 * Return the name of font "font" in allocated memory. 3569 * Don't know how to get the actual name, thus use the provided name. 3570 */ 3571 char_u * 3572gui_mch_get_fontname(GuiFont font, char_u *name) 3573{ 3574 if (name == NULL) 3575 return NULL; 3576 return vim_strsave(name); 3577} 3578#endif 3579 3580#ifdef USE_ATSUI_DRAWING 3581 static void 3582gui_mac_set_font_attributes(GuiFont font) 3583{ 3584 ATSUFontID fontID; 3585 Fixed fontSize; 3586 Fixed fontWidth; 3587 3588 fontID = font & 0xFFFF; 3589 fontSize = Long2Fix(font >> 16); 3590 fontWidth = Long2Fix(gui.char_width); 3591 3592 ATSUAttributeTag attribTags[] = 3593 { 3594 kATSUFontTag, kATSUSizeTag, kATSUImposeWidthTag, 3595 kATSUMaxATSUITagValue + 1 3596 }; 3597 3598 ByteCount attribSizes[] = 3599 { 3600 sizeof(ATSUFontID), sizeof(Fixed), sizeof(fontWidth), 3601 sizeof(font) 3602 }; 3603 3604 ATSUAttributeValuePtr attribValues[] = 3605 { 3606 &fontID, &fontSize, &fontWidth, &font 3607 }; 3608 3609 if (FMGetFontFromFontFamilyInstance(fontID, 0, &fontID, NULL) == noErr) 3610 { 3611 if (ATSUSetAttributes(gFontStyle, 3612 (sizeof attribTags) / sizeof(ATSUAttributeTag), 3613 attribTags, attribSizes, attribValues) != noErr) 3614 { 3615# ifndef NDEBUG 3616 fprintf(stderr, "couldn't set font style\n"); 3617# endif 3618 ATSUDisposeStyle(gFontStyle); 3619 gFontStyle = NULL; 3620 } 3621 3622#ifdef FEAT_MBYTE 3623 if (has_mbyte) 3624 { 3625 /* FIXME: we should use a more mbyte sensitive way to support 3626 * wide font drawing */ 3627 fontWidth = Long2Fix(gui.char_width * 2); 3628 3629 if (ATSUSetAttributes(gWideFontStyle, 3630 (sizeof attribTags) / sizeof(ATSUAttributeTag), 3631 attribTags, attribSizes, attribValues) != noErr) 3632 { 3633 ATSUDisposeStyle(gWideFontStyle); 3634 gWideFontStyle = NULL; 3635 } 3636 } 3637#endif 3638 } 3639} 3640#endif 3641 3642/* 3643 * Set the current text font. 3644 */ 3645 void 3646gui_mch_set_font(GuiFont font) 3647{ 3648#ifdef USE_ATSUI_DRAWING 3649 GuiFont currFont; 3650 ByteCount actualFontByteCount; 3651 3652 if (p_macatsui && gFontStyle) 3653 { 3654 /* Avoid setting same font again */ 3655 if (ATSUGetAttribute(gFontStyle, kATSUMaxATSUITagValue + 1, 3656 sizeof(font), &currFont, &actualFontByteCount) == noErr 3657 && actualFontByteCount == (sizeof font)) 3658 { 3659 if (currFont == font) 3660 return; 3661 } 3662 3663 gui_mac_set_font_attributes(font); 3664 } 3665 3666 if (p_macatsui && !gIsFontFallbackSet) 3667 { 3668 /* Setup automatic font substitution. The user's guifontwide 3669 * is tried first, then the system tries other fonts. */ 3670/* 3671 ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag }; 3672 ByteCount fallbackSizes[] = { sizeof(ATSUFontFallbacks) }; 3673 ATSUCreateFontFallbacks(&gFontFallbacks); 3674 ATSUSetObjFontFallbacks(gFontFallbacks, ); 3675*/ 3676 if (gui.wide_font) 3677 { 3678 ATSUFontID fallbackFonts; 3679 gIsFontFallbackSet = TRUE; 3680 3681 if (FMGetFontFromFontFamilyInstance( 3682 (gui.wide_font & 0xFFFF), 3683 0, 3684 &fallbackFonts, 3685 NULL) == noErr) 3686 { 3687 ATSUSetFontFallbacks((sizeof fallbackFonts)/sizeof(ATSUFontID), 3688 &fallbackFonts, 3689 kATSUSequentialFallbacksPreferred); 3690 } 3691/* 3692 ATSUAttributeValuePtr fallbackValues[] = { }; 3693*/ 3694 } 3695 } 3696#endif 3697 TextSize(font >> 16); 3698 TextFont(font & 0xFFFF); 3699} 3700 3701/* 3702 * If a font is not going to be used, free its structure. 3703 */ 3704 void 3705gui_mch_free_font(font) 3706 GuiFont font; 3707{ 3708 /* 3709 * Free font when "font" is not 0. 3710 * Nothing to do in the current implementation, since 3711 * nothing is allocated for each font used. 3712 */ 3713} 3714 3715 static int 3716hex_digit(int c) 3717{ 3718 if (isdigit(c)) 3719 return c - '0'; 3720 c = TOLOWER_ASC(c); 3721 if (c >= 'a' && c <= 'f') 3722 return c - 'a' + 10; 3723 return -1000; 3724} 3725 3726/* 3727 * Return the Pixel value (color) for the given color name. This routine was 3728 * pretty much taken from example code in the Silicon Graphics OSF/Motif 3729 * Programmer's Guide. 3730 * Return INVALCOLOR when failed. 3731 */ 3732 guicolor_T 3733gui_mch_get_color(char_u *name) 3734{ 3735 /* TODO: Add support for the new named color of MacOS 8 3736 */ 3737 RGBColor MacColor; 3738// guicolor_T color = 0; 3739 3740 typedef struct guicolor_tTable 3741 { 3742 char *name; 3743 guicolor_T color; 3744 } guicolor_tTable; 3745 3746 /* 3747 * The comment at the end of each line is the source 3748 * (Mac, Window, Unix) and the number is the unix rgb.txt value 3749 */ 3750 static guicolor_tTable table[] = 3751 { 3752 {"Black", RGB(0x00, 0x00, 0x00)}, 3753 {"darkgray", RGB(0x80, 0x80, 0x80)}, /*W*/ 3754 {"darkgrey", RGB(0x80, 0x80, 0x80)}, /*W*/ 3755 {"Gray", RGB(0xC0, 0xC0, 0xC0)}, /*W*/ 3756 {"Grey", RGB(0xC0, 0xC0, 0xC0)}, /*W*/ 3757 {"lightgray", RGB(0xE0, 0xE0, 0xE0)}, /*W*/ 3758 {"lightgrey", RGB(0xE0, 0xE0, 0xE0)}, /*W*/ 3759 {"gray10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/ 3760 {"grey10", RGB(0x1A, 0x1A, 0x1A)}, /*W*/ 3761 {"gray20", RGB(0x33, 0x33, 0x33)}, /*W*/ 3762 {"grey20", RGB(0x33, 0x33, 0x33)}, /*W*/ 3763 {"gray30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/ 3764 {"grey30", RGB(0x4D, 0x4D, 0x4D)}, /*W*/ 3765 {"gray40", RGB(0x66, 0x66, 0x66)}, /*W*/ 3766 {"grey40", RGB(0x66, 0x66, 0x66)}, /*W*/ 3767 {"gray50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/ 3768 {"grey50", RGB(0x7F, 0x7F, 0x7F)}, /*W*/ 3769 {"gray60", RGB(0x99, 0x99, 0x99)}, /*W*/ 3770 {"grey60", RGB(0x99, 0x99, 0x99)}, /*W*/ 3771 {"gray70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/ 3772 {"grey70", RGB(0xB3, 0xB3, 0xB3)}, /*W*/ 3773 {"gray80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/ 3774 {"grey80", RGB(0xCC, 0xCC, 0xCC)}, /*W*/ 3775 {"gray90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/ 3776 {"grey90", RGB(0xE5, 0xE5, 0xE5)}, /*W*/ 3777 {"white", RGB(0xFF, 0xFF, 0xFF)}, 3778 {"darkred", RGB(0x80, 0x00, 0x00)}, /*W*/ 3779 {"red", RGB(0xDD, 0x08, 0x06)}, /*M*/ 3780 {"lightred", RGB(0xFF, 0xA0, 0xA0)}, /*W*/ 3781 {"DarkBlue", RGB(0x00, 0x00, 0x80)}, /*W*/ 3782 {"Blue", RGB(0x00, 0x00, 0xD4)}, /*M*/ 3783 {"lightblue", RGB(0xA0, 0xA0, 0xFF)}, /*W*/ 3784 {"DarkGreen", RGB(0x00, 0x80, 0x00)}, /*W*/ 3785 {"Green", RGB(0x00, 0x64, 0x11)}, /*M*/ 3786 {"lightgreen", RGB(0xA0, 0xFF, 0xA0)}, /*W*/ 3787 {"DarkCyan", RGB(0x00, 0x80, 0x80)}, /*W ?0x307D7E */ 3788 {"cyan", RGB(0x02, 0xAB, 0xEA)}, /*M*/ 3789 {"lightcyan", RGB(0xA0, 0xFF, 0xFF)}, /*W*/ 3790 {"darkmagenta", RGB(0x80, 0x00, 0x80)}, /*W*/ 3791 {"magenta", RGB(0xF2, 0x08, 0x84)}, /*M*/ 3792 {"lightmagenta",RGB(0xF0, 0xA0, 0xF0)}, /*W*/ 3793 {"brown", RGB(0x80, 0x40, 0x40)}, /*W*/ 3794 {"yellow", RGB(0xFC, 0xF3, 0x05)}, /*M*/ 3795 {"lightyellow", RGB(0xFF, 0xFF, 0xA0)}, /*M*/ 3796 {"darkyellow", RGB(0xBB, 0xBB, 0x00)}, /*U*/ 3797 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, /*W 0x4E8975 */ 3798 {"orange", RGB(0xFC, 0x80, 0x00)}, /*W 0xF87A17 */ 3799 {"Purple", RGB(0xA0, 0x20, 0xF0)}, /*W 0x8e35e5 */ 3800 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, /*W 0x737CA1 */ 3801 {"Violet", RGB(0x8D, 0x38, 0xC9)}, /*U*/ 3802 }; 3803 3804 int r, g, b; 3805 int i; 3806 3807 if (name[0] == '#' && strlen((char *) name) == 7) 3808 { 3809 /* Name is in "#rrggbb" format */ 3810 r = hex_digit(name[1]) * 16 + hex_digit(name[2]); 3811 g = hex_digit(name[3]) * 16 + hex_digit(name[4]); 3812 b = hex_digit(name[5]) * 16 + hex_digit(name[6]); 3813 if (r < 0 || g < 0 || b < 0) 3814 return INVALCOLOR; 3815 return RGB(r, g, b); 3816 } 3817 else 3818 { 3819 if (STRICMP(name, "hilite") == 0) 3820 { 3821 LMGetHiliteRGB(&MacColor); 3822 return (RGB(MacColor.red >> 8, MacColor.green >> 8, MacColor.blue >> 8)); 3823 } 3824 /* Check if the name is one of the colors we know */ 3825 for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) 3826 if (STRICMP(name, table[i].name) == 0) 3827 return table[i].color; 3828 } 3829 3830 /* 3831 * Last attempt. Look in the file "$VIM/rgb.txt". 3832 */ 3833 { 3834#define LINE_LEN 100 3835 FILE *fd; 3836 char line[LINE_LEN]; 3837 char_u *fname; 3838 3839 fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt"); 3840 if (fname == NULL) 3841 return INVALCOLOR; 3842 3843 fd = fopen((char *)fname, "rt"); 3844 vim_free(fname); 3845 if (fd == NULL) 3846 return INVALCOLOR; 3847 3848 while (!feof(fd)) 3849 { 3850 int len; 3851 int pos; 3852 char *color; 3853 3854 fgets(line, LINE_LEN, fd); 3855 len = strlen(line); 3856 3857 if (len <= 1 || line[len-1] != '\n') 3858 continue; 3859 3860 line[len-1] = '\0'; 3861 3862 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos); 3863 if (i != 3) 3864 continue; 3865 3866 color = line + pos; 3867 3868 if (STRICMP(color, name) == 0) 3869 { 3870 fclose(fd); 3871 return (guicolor_T) RGB(r, g, b); 3872 } 3873 } 3874 fclose(fd); 3875 } 3876 3877 return INVALCOLOR; 3878} 3879 3880/* 3881 * Set the current text foreground color. 3882 */ 3883 void 3884gui_mch_set_fg_color(guicolor_T color) 3885{ 3886 RGBColor TheColor; 3887 3888 TheColor.red = Red(color) * 0x0101; 3889 TheColor.green = Green(color) * 0x0101; 3890 TheColor.blue = Blue(color) * 0x0101; 3891 3892 RGBForeColor(&TheColor); 3893} 3894 3895/* 3896 * Set the current text background color. 3897 */ 3898 void 3899gui_mch_set_bg_color(guicolor_T color) 3900{ 3901 RGBColor TheColor; 3902 3903 TheColor.red = Red(color) * 0x0101; 3904 TheColor.green = Green(color) * 0x0101; 3905 TheColor.blue = Blue(color) * 0x0101; 3906 3907 RGBBackColor(&TheColor); 3908} 3909 3910RGBColor specialColor; 3911 3912/* 3913 * Set the current text special color. 3914 */ 3915 void 3916gui_mch_set_sp_color(guicolor_T color) 3917{ 3918 specialColor.red = Red(color) * 0x0101; 3919 specialColor.green = Green(color) * 0x0101; 3920 specialColor.blue = Blue(color) * 0x0101; 3921} 3922 3923/* 3924 * Draw undercurl at the bottom of the character cell. 3925 */ 3926 static void 3927draw_undercurl(int flags, int row, int col, int cells) 3928{ 3929 int x; 3930 int offset; 3931 const static int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 }; 3932 int y = FILL_Y(row + 1) - 1; 3933 3934 RGBForeColor(&specialColor); 3935 3936 offset = val[FILL_X(col) % 8]; 3937 MoveTo(FILL_X(col), y - offset); 3938 3939 for (x = FILL_X(col); x < FILL_X(col + cells); ++x) 3940 { 3941 offset = val[x % 8]; 3942 LineTo(x, y - offset); 3943 } 3944} 3945 3946 3947 static void 3948draw_string_QD(int row, int col, char_u *s, int len, int flags) 3949{ 3950#ifdef FEAT_MBYTE 3951 char_u *tofree = NULL; 3952 3953 if (output_conv.vc_type != CONV_NONE) 3954 { 3955 tofree = string_convert(&output_conv, s, &len); 3956 if (tofree != NULL) 3957 s = tofree; 3958 } 3959#endif 3960 3961 /* 3962 * On OS X, try using Quartz-style text antialiasing. 3963 */ 3964 if (gMacSystemVersion >= 0x1020) 3965 { 3966 /* Quartz antialiasing is available only in OS 10.2 and later. */ 3967 UInt32 qd_flags = (p_antialias ? 3968 kQDUseCGTextRendering | kQDUseCGTextMetrics : 0); 3969 QDSwapTextFlags(qd_flags); 3970 } 3971 3972 /* 3973 * When antialiasing we're using srcOr mode, we have to clear the block 3974 * before drawing the text. 3975 * Also needed when 'linespace' is non-zero to remove the cursor and 3976 * underlining. 3977 * But not when drawing transparently. 3978 * The following is like calling gui_mch_clear_block(row, col, row, col + 3979 * len - 1), but without setting the bg color to gui.back_pixel. 3980 */ 3981 if (((gMacSystemVersion >= 0x1020 && p_antialias) || p_linespace != 0) 3982 && !(flags & DRAW_TRANSP)) 3983 { 3984 Rect rc; 3985 3986 rc.left = FILL_X(col); 3987 rc.top = FILL_Y(row); 3988#ifdef FEAT_MBYTE 3989 /* Multibyte computation taken from gui_w32.c */ 3990 if (has_mbyte) 3991 { 3992 /* Compute the length in display cells. */ 3993 rc.right = FILL_X(col + mb_string2cells(s, len)); 3994 } 3995 else 3996#endif 3997 rc.right = FILL_X(col + len) + (col + len == Columns); 3998 rc.bottom = FILL_Y(row + 1); 3999 EraseRect(&rc); 4000 } 4001 4002 if (gMacSystemVersion >= 0x1020 && p_antialias) 4003 { 4004 StyleParameter face; 4005 4006 face = normal; 4007 if (flags & DRAW_BOLD) 4008 face |= bold; 4009 if (flags & DRAW_UNDERL) 4010 face |= underline; 4011 TextFace(face); 4012 4013 /* Quartz antialiasing works only in srcOr transfer mode. */ 4014 TextMode(srcOr); 4015 4016 MoveTo(TEXT_X(col), TEXT_Y(row)); 4017 DrawText((char*)s, 0, len); 4018 } 4019 else 4020 { 4021 /* Use old-style, non-antialiased QuickDraw text rendering. */ 4022 TextMode(srcCopy); 4023 TextFace(normal); 4024 4025 /* SelectFont(hdc, gui.currFont); */ 4026 4027 if (flags & DRAW_TRANSP) 4028 { 4029 TextMode(srcOr); 4030 } 4031 4032 MoveTo(TEXT_X(col), TEXT_Y(row)); 4033 DrawText((char *)s, 0, len); 4034 4035 if (flags & DRAW_BOLD) 4036 { 4037 TextMode(srcOr); 4038 MoveTo(TEXT_X(col) + 1, TEXT_Y(row)); 4039 DrawText((char *)s, 0, len); 4040 } 4041 4042 if (flags & DRAW_UNDERL) 4043 { 4044 MoveTo(FILL_X(col), FILL_Y(row + 1) - 1); 4045 LineTo(FILL_X(col + len) - 1, FILL_Y(row + 1) - 1); 4046 } 4047 } 4048 4049 if (flags & DRAW_UNDERC) 4050 draw_undercurl(flags, row, col, len); 4051 4052#ifdef FEAT_MBYTE 4053 vim_free(tofree); 4054#endif 4055} 4056 4057#ifdef USE_ATSUI_DRAWING 4058 4059 static void 4060draw_string_ATSUI(int row, int col, char_u *s, int len, int flags) 4061{ 4062 /* ATSUI requires utf-16 strings */ 4063 UniCharCount utf16_len; 4064 UniChar *tofree = mac_enc_to_utf16(s, len, (size_t *)&utf16_len); 4065 utf16_len /= sizeof(UniChar); 4066 4067 /* - ATSUI automatically antialiases text (Someone) 4068 * - for some reason it does not work... (Jussi) */ 4069#ifdef MAC_ATSUI_DEBUG 4070 fprintf(stderr, "row = %d, col = %d, len = %d: '%c'\n", 4071 row, col, len, len == 1 ? s[0] : ' '); 4072#endif 4073 /* 4074 * When antialiasing we're using srcOr mode, we have to clear the block 4075 * before drawing the text. 4076 * Also needed when 'linespace' is non-zero to remove the cursor and 4077 * underlining. 4078 * But not when drawing transparently. 4079 * The following is like calling gui_mch_clear_block(row, col, row, col + 4080 * len - 1), but without setting the bg color to gui.back_pixel. 4081 */ 4082 if ((flags & DRAW_TRANSP) == 0) 4083 { 4084 Rect rc; 4085 4086 rc.left = FILL_X(col); 4087 rc.top = FILL_Y(row); 4088 /* Multibyte computation taken from gui_w32.c */ 4089 if (has_mbyte) 4090 { 4091 /* Compute the length in display cells. */ 4092 rc.right = FILL_X(col + mb_string2cells(s, len)); 4093 } 4094 else 4095 rc.right = FILL_X(col + len) + (col + len == Columns); 4096 4097 rc.bottom = FILL_Y(row + 1); 4098 EraseRect(&rc); 4099 } 4100 4101 { 4102 TextMode(srcCopy); 4103 TextFace(normal); 4104 4105 /* SelectFont(hdc, gui.currFont); */ 4106 if (flags & DRAW_TRANSP) 4107 { 4108 TextMode(srcOr); 4109 } 4110 4111 MoveTo(TEXT_X(col), TEXT_Y(row)); 4112 4113 if (gFontStyle && flags & DRAW_BOLD) 4114 { 4115 Boolean attValue = true; 4116 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag }; 4117 ByteCount attribSizes[] = { sizeof(Boolean) }; 4118 ATSUAttributeValuePtr attribValues[] = { &attValue }; 4119 4120 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, attribValues); 4121 } 4122 4123 UInt32 useAntialias = p_antialias ? kATSStyleApplyAntiAliasing 4124 : kATSStyleNoAntiAliasing; 4125 if (useAntialias != useAntialias_cached) 4126 { 4127 ATSUAttributeTag attribTags[] = { kATSUStyleRenderingOptionsTag }; 4128 ByteCount attribSizes[] = { sizeof(UInt32) }; 4129 ATSUAttributeValuePtr attribValues[] = { &useAntialias }; 4130 4131 if (gFontStyle) 4132 ATSUSetAttributes(gFontStyle, 1, attribTags, 4133 attribSizes, attribValues); 4134 if (gWideFontStyle) 4135 ATSUSetAttributes(gWideFontStyle, 1, attribTags, 4136 attribSizes, attribValues); 4137 4138 useAntialias_cached = useAntialias; 4139 } 4140 4141#ifdef FEAT_MBYTE 4142 if (has_mbyte) 4143 { 4144 int n, width_in_cell, last_width_in_cell; 4145 UniCharArrayOffset offset = 0; 4146 UniCharCount yet_to_draw = 0; 4147 ATSUTextLayout textLayout; 4148 ATSUStyle textStyle; 4149 4150 last_width_in_cell = 1; 4151 ATSUCreateTextLayout(&textLayout); 4152 ATSUSetTextPointerLocation(textLayout, tofree, 4153 kATSUFromTextBeginning, 4154 kATSUToTextEnd, utf16_len); 4155 /* 4156 ATSUSetRunStyle(textLayout, gFontStyle, 4157 kATSUFromTextBeginning, kATSUToTextEnd); */ 4158 4159 /* Compute the length in display cells. */ 4160 for (n = 0; n < len; n += MB_BYTE2LEN(s[n])) 4161 { 4162 width_in_cell = (*mb_ptr2cells)(s + n); 4163 4164 /* probably we are switching from single byte character 4165 * to multibyte characters (which requires more than one 4166 * cell to draw) */ 4167 if (width_in_cell != last_width_in_cell) 4168 { 4169#ifdef MAC_ATSUI_DEBUG 4170 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n", 4171 n, last_width_in_cell, width_in_cell, offset, yet_to_draw); 4172#endif 4173 textStyle = last_width_in_cell > 1 ? gWideFontStyle 4174 : gFontStyle; 4175 4176 ATSUSetRunStyle(textLayout, textStyle, offset, yet_to_draw); 4177 offset += yet_to_draw; 4178 yet_to_draw = 0; 4179 last_width_in_cell = width_in_cell; 4180 } 4181 4182 yet_to_draw++; 4183 } 4184 4185 if (yet_to_draw) 4186 { 4187#ifdef MAC_ATSUI_DEBUG 4188 fprintf(stderr, "\tn = %2d, (%d-%d), offset = %d, yet_to_draw = %d\n", 4189 n, last_width_in_cell, width_in_cell, offset, yet_to_draw); 4190#endif 4191 /* finish the rest style */ 4192 textStyle = width_in_cell > 1 ? gWideFontStyle : gFontStyle; 4193 ATSUSetRunStyle(textLayout, textStyle, offset, kATSUToTextEnd); 4194 } 4195 4196 ATSUSetTransientFontMatching(textLayout, TRUE); 4197 ATSUDrawText(textLayout, 4198 kATSUFromTextBeginning, kATSUToTextEnd, 4199 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc); 4200 ATSUDisposeTextLayout(textLayout); 4201 } 4202 else 4203#endif 4204 { 4205 ATSUTextLayout textLayout; 4206 4207 if (ATSUCreateTextLayoutWithTextPtr(tofree, 4208 kATSUFromTextBeginning, kATSUToTextEnd, 4209 utf16_len, 4210 (gFontStyle ? 1 : 0), &utf16_len, 4211 (gFontStyle ? &gFontStyle : NULL), 4212 &textLayout) == noErr) 4213 { 4214 ATSUSetTransientFontMatching(textLayout, TRUE); 4215 4216 ATSUDrawText(textLayout, 4217 kATSUFromTextBeginning, kATSUToTextEnd, 4218 kATSUUseGrafPortPenLoc, kATSUUseGrafPortPenLoc); 4219 4220 ATSUDisposeTextLayout(textLayout); 4221 } 4222 } 4223 4224 /* drawing is done, now reset bold to normal */ 4225 if (gFontStyle && flags & DRAW_BOLD) 4226 { 4227 Boolean attValue = false; 4228 4229 ATSUAttributeTag attribTags[] = { kATSUQDBoldfaceTag }; 4230 ByteCount attribSizes[] = { sizeof(Boolean) }; 4231 ATSUAttributeValuePtr attribValues[] = { &attValue }; 4232 4233 ATSUSetAttributes(gFontStyle, 1, attribTags, attribSizes, 4234 attribValues); 4235 } 4236 } 4237 4238 if (flags & DRAW_UNDERC) 4239 draw_undercurl(flags, row, col, len); 4240 4241 vim_free(tofree); 4242} 4243#endif 4244 4245 void 4246gui_mch_draw_string(int row, int col, char_u *s, int len, int flags) 4247{ 4248#if defined(USE_ATSUI_DRAWING) 4249 if (p_macatsui == 0 && p_macatsui_last != 0) 4250 /* switch from macatsui to nomacatsui */ 4251 gui_mac_dispose_atsui_style(); 4252 else if (p_macatsui != 0 && p_macatsui_last == 0) 4253 /* switch from nomacatsui to macatsui */ 4254 gui_mac_create_atsui_style(); 4255 4256 if (p_macatsui) 4257 draw_string_ATSUI(row, col, s, len, flags); 4258 else 4259#endif 4260 draw_string_QD(row, col, s, len, flags); 4261} 4262 4263/* 4264 * Return OK if the key with the termcap name "name" is supported. 4265 */ 4266 int 4267gui_mch_haskey(char_u *name) 4268{ 4269 int i; 4270 4271 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) 4272 if (name[0] == special_keys[i].vim_code0 && 4273 name[1] == special_keys[i].vim_code1) 4274 return OK; 4275 return FAIL; 4276} 4277 4278 void 4279gui_mch_beep(void) 4280{ 4281 SysBeep(1); /* Should this be 0? (????) */ 4282} 4283 4284 void 4285gui_mch_flash(int msec) 4286{ 4287 /* Do a visual beep by reversing the foreground and background colors */ 4288 Rect rc; 4289 4290 /* 4291 * Note: InvertRect() excludes right and bottom of rectangle. 4292 */ 4293 rc.left = 0; 4294 rc.top = 0; 4295 rc.right = gui.num_cols * gui.char_width; 4296 rc.bottom = gui.num_rows * gui.char_height; 4297 InvertRect(&rc); 4298 4299 ui_delay((long)msec, TRUE); /* wait for some msec */ 4300 4301 InvertRect(&rc); 4302} 4303 4304/* 4305 * Invert a rectangle from row r, column c, for nr rows and nc columns. 4306 */ 4307 void 4308gui_mch_invert_rectangle(int r, int c, int nr, int nc) 4309{ 4310 Rect rc; 4311 4312 /* 4313 * Note: InvertRect() excludes right and bottom of rectangle. 4314 */ 4315 rc.left = FILL_X(c); 4316 rc.top = FILL_Y(r); 4317 rc.right = rc.left + nc * gui.char_width; 4318 rc.bottom = rc.top + nr * gui.char_height; 4319 InvertRect(&rc); 4320} 4321 4322/* 4323 * Iconify the GUI window. 4324 */ 4325 void 4326gui_mch_iconify(void) 4327{ 4328 /* TODO: find out what could replace iconify 4329 * -window shade? 4330 * -hide application? 4331 */ 4332} 4333 4334#if defined(FEAT_EVAL) || defined(PROTO) 4335/* 4336 * Bring the Vim window to the foreground. 4337 */ 4338 void 4339gui_mch_set_foreground(void) 4340{ 4341 /* TODO */ 4342} 4343#endif 4344 4345/* 4346 * Draw a cursor without focus. 4347 */ 4348 void 4349gui_mch_draw_hollow_cursor(guicolor_T color) 4350{ 4351 Rect rc; 4352 4353 /* 4354 * Note: FrameRect() excludes right and bottom of rectangle. 4355 */ 4356 rc.left = FILL_X(gui.col); 4357 rc.top = FILL_Y(gui.row); 4358 rc.right = rc.left + gui.char_width; 4359#ifdef FEAT_MBYTE 4360 if (mb_lefthalve(gui.row, gui.col)) 4361 rc.right += gui.char_width; 4362#endif 4363 rc.bottom = rc.top + gui.char_height; 4364 4365 gui_mch_set_fg_color(color); 4366 4367 FrameRect(&rc); 4368} 4369 4370/* 4371 * Draw part of a cursor, only w pixels wide, and h pixels high. 4372 */ 4373 void 4374gui_mch_draw_part_cursor(int w, int h, guicolor_T color) 4375{ 4376 Rect rc; 4377 4378#ifdef FEAT_RIGHTLEFT 4379 /* vertical line should be on the right of current point */ 4380 if (CURSOR_BAR_RIGHT) 4381 rc.left = FILL_X(gui.col + 1) - w; 4382 else 4383#endif 4384 rc.left = FILL_X(gui.col); 4385 rc.top = FILL_Y(gui.row) + gui.char_height - h; 4386 rc.right = rc.left + w; 4387 rc.bottom = rc.top + h; 4388 4389 gui_mch_set_fg_color(color); 4390 4391 FrameRect(&rc); 4392// PaintRect(&rc); 4393} 4394 4395 4396 4397/* 4398 * Catch up with any queued X events. This may put keyboard input into the 4399 * input buffer, call resize call-backs, trigger timers etc. If there is 4400 * nothing in the X event queue (& no timers pending), then we return 4401 * immediately. 4402 */ 4403 void 4404gui_mch_update(void) 4405{ 4406 /* TODO: find what to do 4407 * maybe call gui_mch_wait_for_chars (0) 4408 * more like look at EventQueue then 4409 * call heart of gui_mch_wait_for_chars; 4410 * 4411 * if (eventther) 4412 * gui_mac_handle_event(&event); 4413 */ 4414 EventRecord theEvent; 4415 4416 if (EventAvail(everyEvent, &theEvent)) 4417 if (theEvent.what != nullEvent) 4418 gui_mch_wait_for_chars(0); 4419} 4420 4421/* 4422 * Simple wrapper to neglect more easily the time 4423 * spent inside WaitNextEvent while profiling. 4424 */ 4425 4426 pascal 4427 Boolean 4428WaitNextEventWrp(EventMask eventMask, EventRecord *theEvent, UInt32 sleep, RgnHandle mouseRgn) 4429{ 4430 if (((long) sleep) < -1) 4431 sleep = 32767; 4432 return WaitNextEvent(eventMask, theEvent, sleep, mouseRgn); 4433} 4434 4435/* 4436 * GUI input routine called by gui_wait_for_chars(). Waits for a character 4437 * from the keyboard. 4438 * wtime == -1 Wait forever. 4439 * wtime == 0 This should never happen. 4440 * wtime > 0 Wait wtime milliseconds for a character. 4441 * Returns OK if a character was found to be available within the given time, 4442 * or FAIL otherwise. 4443 */ 4444 int 4445gui_mch_wait_for_chars(int wtime) 4446{ 4447 EventMask mask = (everyEvent); 4448 EventRecord event; 4449 long entryTick; 4450 long currentTick; 4451 long sleeppyTick; 4452 4453 /* If we are providing life feedback with the scrollbar, 4454 * we don't want to try to wait for an event, or else 4455 * there won't be any life feedback. 4456 */ 4457 if (dragged_sb != NULL) 4458 return FAIL; 4459 /* TODO: Check if FAIL is the proper return code */ 4460 4461 entryTick = TickCount(); 4462 4463 allow_scrollbar = TRUE; 4464 4465 do 4466 { 4467/* if (dragRectControl == kCreateEmpty) 4468 { 4469 dragRgn = NULL; 4470 dragRectControl = kNothing; 4471 } 4472 else*/ if (dragRectControl == kCreateRect) 4473 { 4474 dragRgn = cursorRgn; 4475 RectRgn(dragRgn, &dragRect); 4476 dragRectControl = kNothing; 4477 } 4478 /* 4479 * Don't use gui_mch_update() because then we will spin-lock until a 4480 * char arrives, instead we use WaitNextEventWrp() to hang until an 4481 * event arrives. No need to check for input_buf_full because we are 4482 * returning as soon as it contains a single char. 4483 */ 4484 /* TODO: reduce wtime accordinly??? */ 4485 if (wtime > -1) 4486 sleeppyTick = 60 * wtime / 1000; 4487 else 4488 sleeppyTick = 32767; 4489 4490 if (WaitNextEventWrp(mask, &event, sleeppyTick, dragRgn)) 4491 { 4492 gui_mac_handle_event(&event); 4493 if (input_available()) 4494 { 4495 allow_scrollbar = FALSE; 4496 return OK; 4497 } 4498 } 4499 currentTick = TickCount(); 4500 } 4501 while ((wtime == -1) || ((currentTick - entryTick) < 60*wtime/1000)); 4502 4503 allow_scrollbar = FALSE; 4504 return FAIL; 4505} 4506 4507/* 4508 * Output routines. 4509 */ 4510 4511/* Flush any output to the screen */ 4512 void 4513gui_mch_flush(void) 4514{ 4515 /* TODO: Is anything needed here? */ 4516} 4517 4518/* 4519 * Clear a rectangular region of the screen from text pos (row1, col1) to 4520 * (row2, col2) inclusive. 4521 */ 4522 void 4523gui_mch_clear_block(int row1, int col1, int row2, int col2) 4524{ 4525 Rect rc; 4526 4527 /* 4528 * Clear one extra pixel at the far right, for when bold characters have 4529 * spilled over to the next column. 4530 */ 4531 rc.left = FILL_X(col1); 4532 rc.top = FILL_Y(row1); 4533 rc.right = FILL_X(col2 + 1) + (col2 == Columns - 1); 4534 rc.bottom = FILL_Y(row2 + 1); 4535 4536 gui_mch_set_bg_color(gui.back_pixel); 4537 EraseRect(&rc); 4538} 4539 4540/* 4541 * Clear the whole text window. 4542 */ 4543 void 4544gui_mch_clear_all(void) 4545{ 4546 Rect rc; 4547 4548 rc.left = 0; 4549 rc.top = 0; 4550 rc.right = Columns * gui.char_width + 2 * gui.border_width; 4551 rc.bottom = Rows * gui.char_height + 2 * gui.border_width; 4552 4553 gui_mch_set_bg_color(gui.back_pixel); 4554 EraseRect(&rc); 4555/* gui_mch_set_fg_color(gui.norm_pixel); 4556 FrameRect(&rc); 4557*/ 4558} 4559 4560/* 4561 * Delete the given number of lines from the given row, scrolling up any 4562 * text further down within the scroll region. 4563 */ 4564 void 4565gui_mch_delete_lines(int row, int num_lines) 4566{ 4567 Rect rc; 4568 4569 /* changed without checking! */ 4570 rc.left = FILL_X(gui.scroll_region_left); 4571 rc.right = FILL_X(gui.scroll_region_right + 1); 4572 rc.top = FILL_Y(row); 4573 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 4574 4575 gui_mch_set_bg_color(gui.back_pixel); 4576 ScrollRect(&rc, 0, -num_lines * gui.char_height, (RgnHandle) nil); 4577 4578 gui_clear_block(gui.scroll_region_bot - num_lines + 1, 4579 gui.scroll_region_left, 4580 gui.scroll_region_bot, gui.scroll_region_right); 4581} 4582 4583/* 4584 * Insert the given number of lines before the given row, scrolling down any 4585 * following text within the scroll region. 4586 */ 4587 void 4588gui_mch_insert_lines(int row, int num_lines) 4589{ 4590 Rect rc; 4591 4592 rc.left = FILL_X(gui.scroll_region_left); 4593 rc.right = FILL_X(gui.scroll_region_right + 1); 4594 rc.top = FILL_Y(row); 4595 rc.bottom = FILL_Y(gui.scroll_region_bot + 1); 4596 4597 gui_mch_set_bg_color(gui.back_pixel); 4598 4599 ScrollRect(&rc, 0, gui.char_height * num_lines, (RgnHandle) nil); 4600 4601 /* Update gui.cursor_row if the cursor scrolled or copied over */ 4602 if (gui.cursor_row >= gui.row 4603 && gui.cursor_col >= gui.scroll_region_left 4604 && gui.cursor_col <= gui.scroll_region_right) 4605 { 4606 if (gui.cursor_row <= gui.scroll_region_bot - num_lines) 4607 gui.cursor_row += num_lines; 4608 else if (gui.cursor_row <= gui.scroll_region_bot) 4609 gui.cursor_is_valid = FALSE; 4610 } 4611 4612 gui_clear_block(row, gui.scroll_region_left, 4613 row + num_lines - 1, gui.scroll_region_right); 4614} 4615 4616 /* 4617 * TODO: add a vim format to the clipboard which remember 4618 * LINEWISE, CHARWISE, BLOCKWISE 4619 */ 4620 4621 void 4622clip_mch_request_selection(VimClipboard *cbd) 4623{ 4624 4625 Handle textOfClip; 4626 int flavor = 0; 4627 Size scrapSize; 4628 ScrapFlavorFlags scrapFlags; 4629 ScrapRef scrap = nil; 4630 OSStatus error; 4631 int type; 4632 char *searchCR; 4633 char_u *tempclip; 4634 4635 4636 error = GetCurrentScrap(&scrap); 4637 if (error != noErr) 4638 return; 4639 4640 error = GetScrapFlavorFlags(scrap, VIMSCRAPFLAVOR, &scrapFlags); 4641 if (error == noErr) 4642 { 4643 error = GetScrapFlavorSize(scrap, VIMSCRAPFLAVOR, &scrapSize); 4644 if (error == noErr && scrapSize > 1) 4645 flavor = 1; 4646 } 4647 4648 if (flavor == 0) 4649 { 4650 error = GetScrapFlavorFlags(scrap, SCRAPTEXTFLAVOR, &scrapFlags); 4651 if (error != noErr) 4652 return; 4653 4654 error = GetScrapFlavorSize(scrap, SCRAPTEXTFLAVOR, &scrapSize); 4655 if (error != noErr) 4656 return; 4657 } 4658 4659 ReserveMem(scrapSize); 4660 4661 /* In CARBON we don't need a Handle, a pointer is good */ 4662 textOfClip = NewHandle(scrapSize); 4663 4664 /* tempclip = lalloc(scrapSize+1, TRUE); */ 4665 HLock(textOfClip); 4666 error = GetScrapFlavorData(scrap, 4667 flavor ? VIMSCRAPFLAVOR : SCRAPTEXTFLAVOR, 4668 &scrapSize, *textOfClip); 4669 scrapSize -= flavor; 4670 4671 if (flavor) 4672 type = **textOfClip; 4673 else 4674 type = (strchr(*textOfClip, '\r') != NULL) ? MLINE : MCHAR; 4675 4676 tempclip = lalloc(scrapSize + 1, TRUE); 4677 mch_memmove(tempclip, *textOfClip + flavor, scrapSize); 4678 tempclip[scrapSize] = 0; 4679 4680#ifdef MACOS_CONVERT 4681 { 4682 /* Convert from utf-16 (clipboard) */ 4683 size_t encLen = 0; 4684 char_u *to = mac_utf16_to_enc((UniChar *)tempclip, scrapSize, &encLen); 4685 4686 if (to != NULL) 4687 { 4688 scrapSize = encLen; 4689 vim_free(tempclip); 4690 tempclip = to; 4691 } 4692 } 4693#endif 4694 4695 searchCR = (char *)tempclip; 4696 while (searchCR != NULL) 4697 { 4698 searchCR = strchr(searchCR, '\r'); 4699 if (searchCR != NULL) 4700 *searchCR = '\n'; 4701 } 4702 4703 clip_yank_selection(type, tempclip, scrapSize, cbd); 4704 4705 vim_free(tempclip); 4706 HUnlock(textOfClip); 4707 4708 DisposeHandle(textOfClip); 4709} 4710 4711 void 4712clip_mch_lose_selection(VimClipboard *cbd) 4713{ 4714 /* 4715 * TODO: Really nothing to do? 4716 */ 4717} 4718 4719 int 4720clip_mch_own_selection(VimClipboard *cbd) 4721{ 4722 return OK; 4723} 4724 4725/* 4726 * Send the current selection to the clipboard. 4727 */ 4728 void 4729clip_mch_set_selection(VimClipboard *cbd) 4730{ 4731 Handle textOfClip; 4732 long scrapSize; 4733 int type; 4734 ScrapRef scrap; 4735 4736 char_u *str = NULL; 4737 4738 if (!cbd->owned) 4739 return; 4740 4741 clip_get_selection(cbd); 4742 4743 /* 4744 * Once we set the clipboard, lose ownership. If another application sets 4745 * the clipboard, we don't want to think that we still own it. 4746 */ 4747 cbd->owned = FALSE; 4748 4749 type = clip_convert_selection(&str, (long_u *)&scrapSize, cbd); 4750 4751#ifdef MACOS_CONVERT 4752 size_t utf16_len = 0; 4753 UniChar *to = mac_enc_to_utf16(str, scrapSize, &utf16_len); 4754 if (to) 4755 { 4756 scrapSize = utf16_len; 4757 vim_free(str); 4758 str = (char_u *)to; 4759 } 4760#endif 4761 4762 if (type >= 0) 4763 { 4764 ClearCurrentScrap(); 4765 4766 textOfClip = NewHandle(scrapSize + 1); 4767 HLock(textOfClip); 4768 4769 **textOfClip = type; 4770 mch_memmove(*textOfClip + 1, str, scrapSize); 4771 GetCurrentScrap(&scrap); 4772 PutScrapFlavor(scrap, SCRAPTEXTFLAVOR, kScrapFlavorMaskNone, 4773 scrapSize, *textOfClip + 1); 4774 PutScrapFlavor(scrap, VIMSCRAPFLAVOR, kScrapFlavorMaskNone, 4775 scrapSize + 1, *textOfClip); 4776 HUnlock(textOfClip); 4777 DisposeHandle(textOfClip); 4778 } 4779 4780 vim_free(str); 4781} 4782 4783 void 4784gui_mch_set_text_area_pos(int x, int y, int w, int h) 4785{ 4786 Rect VimBound; 4787 4788/* HideWindow(gui.VimWindow); */ 4789 GetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound); 4790 4791 if (gui.which_scrollbars[SBAR_LEFT]) 4792 { 4793 VimBound.left = -gui.scrollbar_width + 1; 4794 } 4795 else 4796 { 4797 VimBound.left = 0; 4798 } 4799 4800 SetWindowBounds(gui.VimWindow, kWindowGlobalPortRgn, &VimBound); 4801 4802 ShowWindow(gui.VimWindow); 4803} 4804 4805/* 4806 * Menu stuff. 4807 */ 4808 4809 void 4810gui_mch_enable_menu(int flag) 4811{ 4812 /* 4813 * Menu is always active. 4814 */ 4815} 4816 4817 void 4818gui_mch_set_menu_pos(int x, int y, int w, int h) 4819{ 4820 /* 4821 * The menu is always at the top of the screen. 4822 */ 4823} 4824 4825/* 4826 * Add a sub menu to the menu bar. 4827 */ 4828 void 4829gui_mch_add_menu(vimmenu_T *menu, int idx) 4830{ 4831 /* 4832 * TODO: Try to use only menu_id instead of both menu_id and menu_handle. 4833 * TODO: use menu->mnemonic and menu->actext 4834 * TODO: Try to reuse menu id 4835 * Carbon Help suggest to use only id between 1 and 235 4836 */ 4837 static long next_avail_id = 128; 4838 long menu_after_me = 0; /* Default to the end */ 4839#if defined(FEAT_MBYTE) 4840 CFStringRef name; 4841#else 4842 char_u *name; 4843#endif 4844 short index; 4845 vimmenu_T *parent = menu->parent; 4846 vimmenu_T *brother = menu->next; 4847 4848 /* Cannot add a menu if ... */ 4849 if ((parent != NULL && parent->submenu_id == 0)) 4850 return; 4851 4852 /* menu ID greater than 1024 are reserved for ??? */ 4853 if (next_avail_id == 1024) 4854 return; 4855 4856 /* My brother could be the PopUp, find my real brother */ 4857 while ((brother != NULL) && (!menu_is_menubar(brother->name))) 4858 brother = brother->next; 4859 4860 /* Find where to insert the menu (for MenuBar) */ 4861 if ((parent == NULL) && (brother != NULL)) 4862 menu_after_me = brother->submenu_id; 4863 4864 /* If the menu is not part of the menubar (and its submenus), add it 'nowhere' */ 4865 if (!menu_is_menubar(menu->name)) 4866 menu_after_me = hierMenu; 4867 4868 /* Convert the name */ 4869#ifdef MACOS_CONVERT 4870 name = menu_title_removing_mnemonic(menu); 4871#else 4872 name = C2Pascal_save(menu->dname); 4873#endif 4874 if (name == NULL) 4875 return; 4876 4877 /* Create the menu unless it's the help menu */ 4878 { 4879 /* Carbon suggest use of 4880 * OSStatus CreateNewMenu(MenuID, MenuAttributes, MenuRef *); 4881 * OSStatus SetMenuTitle(MenuRef, ConstStr255Param title); 4882 */ 4883 menu->submenu_id = next_avail_id; 4884#if defined(FEAT_MBYTE) 4885 if (CreateNewMenu(menu->submenu_id, 0, (MenuRef *)&menu->submenu_handle) == noErr) 4886 SetMenuTitleWithCFString((MenuRef)menu->submenu_handle, name); 4887#else 4888 menu->submenu_handle = NewMenu(menu->submenu_id, name); 4889#endif 4890 next_avail_id++; 4891 } 4892 4893 if (parent == NULL) 4894 { 4895 /* Adding a menu to the menubar, or in the no mans land (for PopUp) */ 4896 4897 /* TODO: Verify if we could only Insert Menu if really part of the 4898 * menubar The Inserted menu are scanned or the Command-key combos 4899 */ 4900 4901 /* Insert the menu */ 4902 InsertMenu(menu->submenu_handle, menu_after_me); /* insert before */ 4903#if 1 4904 /* Vim should normally update it. TODO: verify */ 4905 DrawMenuBar(); 4906#endif 4907 } 4908 else 4909 { 4910 /* Adding as a submenu */ 4911 4912 index = gui_mac_get_menu_item_index(menu); 4913 4914 /* Call InsertMenuItem followed by SetMenuItemText 4915 * to avoid special character recognition by InsertMenuItem 4916 */ 4917 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */ 4918#if defined(FEAT_MBYTE) 4919 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name); 4920#else 4921 SetMenuItemText(parent->submenu_handle, idx+1, name); 4922#endif 4923 SetItemCmd(parent->submenu_handle, idx+1, 0x1B); 4924 SetItemMark(parent->submenu_handle, idx+1, menu->submenu_id); 4925 InsertMenu(menu->submenu_handle, hierMenu); 4926 } 4927 4928#if defined(FEAT_MBYTE) 4929 CFRelease(name); 4930#else 4931 vim_free(name); 4932#endif 4933 4934#if 0 4935 /* Done by Vim later on */ 4936 DrawMenuBar(); 4937#endif 4938} 4939 4940/* 4941 * Add a menu item to a menu 4942 */ 4943 void 4944gui_mch_add_menu_item(vimmenu_T *menu, int idx) 4945{ 4946#if defined(FEAT_MBYTE) 4947 CFStringRef name; 4948#else 4949 char_u *name; 4950#endif 4951 vimmenu_T *parent = menu->parent; 4952 int menu_inserted; 4953 4954 /* Cannot add item, if the menu have not been created */ 4955 if (parent->submenu_id == 0) 4956 return; 4957 4958 /* Could call SetMenuRefCon [CARBON] to associate with the Menu, 4959 for older OS call GetMenuItemData (menu, item, isCommandID?, data) */ 4960 4961 /* Convert the name */ 4962#ifdef MACOS_CONVERT 4963 name = menu_title_removing_mnemonic(menu); 4964#else 4965 name = C2Pascal_save(menu->dname); 4966#endif 4967 4968 /* Where are just a menu item, so no handle, no id */ 4969 menu->submenu_id = 0; 4970 menu->submenu_handle = NULL; 4971 4972 menu_inserted = 0; 4973 if (menu->actext) 4974 { 4975 /* If the accelerator text for the menu item looks like it describes 4976 * a command key (e.g., "<D-S-t>" or "<C-7>"), display it as the 4977 * item's command equivalent. 4978 */ 4979 int key = 0; 4980 int modifiers = 0; 4981 char_u *p_actext; 4982 4983 p_actext = menu->actext; 4984 key = find_special_key(&p_actext, &modifiers, FALSE, FALSE); 4985 if (*p_actext != 0) 4986 key = 0; /* error: trailing text */ 4987 /* find_special_key() returns a keycode with as many of the 4988 * specified modifiers as appropriate already applied (e.g., for 4989 * "<D-C-x>" it returns Ctrl-X as the keycode and MOD_MASK_CMD 4990 * as the only modifier). Since we want to display all of the 4991 * modifiers, we need to convert the keycode back to a printable 4992 * character plus modifiers. 4993 * TODO: Write an alternative find_special_key() that doesn't 4994 * apply modifiers. 4995 */ 4996 if (key > 0 && key < 32) 4997 { 4998 /* Convert a control key to an uppercase letter. Note that 4999 * by this point it is no longer possible to distinguish 5000 * between, e.g., Ctrl-S and Ctrl-Shift-S. 5001 */ 5002 modifiers |= MOD_MASK_CTRL; 5003 key += '@'; 5004 } 5005 /* If the keycode is an uppercase letter, set the Shift modifier. 5006 * If it is a lowercase letter, don't set the modifier, but convert 5007 * the letter to uppercase for display in the menu. 5008 */ 5009 else if (key >= 'A' && key <= 'Z') 5010 modifiers |= MOD_MASK_SHIFT; 5011 else if (key >= 'a' && key <= 'z') 5012 key += 'A' - 'a'; 5013 /* Note: keycodes below 0x22 are reserved by Apple. */ 5014 if (key >= 0x22 && vim_isprintc_strict(key)) 5015 { 5016 int valid = 1; 5017 char_u mac_mods = kMenuNoModifiers; 5018 /* Convert Vim modifier codes to Menu Manager equivalents. */ 5019 if (modifiers & MOD_MASK_SHIFT) 5020 mac_mods |= kMenuShiftModifier; 5021 if (modifiers & MOD_MASK_CTRL) 5022 mac_mods |= kMenuControlModifier; 5023 if (!(modifiers & MOD_MASK_CMD)) 5024 mac_mods |= kMenuNoCommandModifier; 5025 if (modifiers & MOD_MASK_ALT || modifiers & MOD_MASK_MULTI_CLICK) 5026 valid = 0; /* TODO: will Alt someday map to Option? */ 5027 if (valid) 5028 { 5029 char_u item_txt[10]; 5030 /* Insert the menu item after idx, with its command key. */ 5031 item_txt[0] = 3; item_txt[1] = ' '; item_txt[2] = '/'; 5032 item_txt[3] = key; 5033 InsertMenuItem(parent->submenu_handle, item_txt, idx); 5034 /* Set the modifier keys. */ 5035 SetMenuItemModifiers(parent->submenu_handle, idx+1, mac_mods); 5036 menu_inserted = 1; 5037 } 5038 } 5039 } 5040 /* Call InsertMenuItem followed by SetMenuItemText 5041 * to avoid special character recognition by InsertMenuItem 5042 */ 5043 if (!menu_inserted) 5044 InsertMenuItem(parent->submenu_handle, "\p ", idx); /* afterItem */ 5045 /* Set the menu item name. */ 5046#if defined(FEAT_MBYTE) 5047 SetMenuItemTextWithCFString(parent->submenu_handle, idx+1, name); 5048#else 5049 SetMenuItemText(parent->submenu_handle, idx+1, name); 5050#endif 5051 5052#if 0 5053 /* Called by Vim */ 5054 DrawMenuBar(); 5055#endif 5056 5057#if defined(FEAT_MBYTE) 5058 CFRelease(name); 5059#else 5060 /* TODO: Can name be freed? */ 5061 vim_free(name); 5062#endif 5063} 5064 5065 void 5066gui_mch_toggle_tearoffs(int enable) 5067{ 5068 /* no tearoff menus */ 5069} 5070 5071/* 5072 * Destroy the machine specific menu widget. 5073 */ 5074 void 5075gui_mch_destroy_menu(vimmenu_T *menu) 5076{ 5077 short index = gui_mac_get_menu_item_index(menu); 5078 5079 if (index > 0) 5080 { 5081 if (menu->parent) 5082 { 5083 { 5084 /* For now just don't delete help menu items. (Huh? Dany) */ 5085 DeleteMenuItem(menu->parent->submenu_handle, index); 5086 5087 /* Delete the Menu if it was a hierarchical Menu */ 5088 if (menu->submenu_id != 0) 5089 { 5090 DeleteMenu(menu->submenu_id); 5091 DisposeMenu(menu->submenu_handle); 5092 } 5093 } 5094 } 5095#ifdef DEBUG_MAC_MENU 5096 else 5097 { 5098 printf("gmdm 2\n"); 5099 } 5100#endif 5101 } 5102 else 5103 { 5104 { 5105 DeleteMenu(menu->submenu_id); 5106 DisposeMenu(menu->submenu_handle); 5107 } 5108 } 5109 /* Shouldn't this be already done by Vim. TODO: Check */ 5110 DrawMenuBar(); 5111} 5112 5113/* 5114 * Make a menu either grey or not grey. 5115 */ 5116 void 5117gui_mch_menu_grey(vimmenu_T *menu, int grey) 5118{ 5119 /* TODO: Check if menu really exists */ 5120 short index = gui_mac_get_menu_item_index(menu); 5121/* 5122 index = menu->index; 5123*/ 5124 if (grey) 5125 { 5126 if (menu->children) 5127 DisableMenuItem(menu->submenu_handle, index); 5128 if (menu->parent) 5129 if (menu->parent->submenu_handle) 5130 DisableMenuItem(menu->parent->submenu_handle, index); 5131 } 5132 else 5133 { 5134 if (menu->children) 5135 EnableMenuItem(menu->submenu_handle, index); 5136 if (menu->parent) 5137 if (menu->parent->submenu_handle) 5138 EnableMenuItem(menu->parent->submenu_handle, index); 5139 } 5140} 5141 5142/* 5143 * Make menu item hidden or not hidden 5144 */ 5145 void 5146gui_mch_menu_hidden(vimmenu_T *menu, int hidden) 5147{ 5148 /* There's no hidden mode on MacOS */ 5149 gui_mch_menu_grey(menu, hidden); 5150} 5151 5152 5153/* 5154 * This is called after setting all the menus to grey/hidden or not. 5155 */ 5156 void 5157gui_mch_draw_menubar(void) 5158{ 5159 DrawMenuBar(); 5160} 5161 5162 5163/* 5164 * Scrollbar stuff. 5165 */ 5166 5167 void 5168gui_mch_enable_scrollbar( 5169 scrollbar_T *sb, 5170 int flag) 5171{ 5172 if (flag) 5173 ShowControl(sb->id); 5174 else 5175 HideControl(sb->id); 5176 5177#ifdef DEBUG_MAC_SB 5178 printf("enb_sb (%x) %x\n",sb->id, flag); 5179#endif 5180} 5181 5182 void 5183gui_mch_set_scrollbar_thumb( 5184 scrollbar_T *sb, 5185 long val, 5186 long size, 5187 long max) 5188{ 5189 SetControl32BitMaximum (sb->id, max); 5190 SetControl32BitMinimum (sb->id, 0); 5191 SetControl32BitValue (sb->id, val); 5192 SetControlViewSize (sb->id, size); 5193#ifdef DEBUG_MAC_SB 5194 printf("thumb_sb (%x) %x, %x,%x\n",sb->id, val, size, max); 5195#endif 5196} 5197 5198 void 5199gui_mch_set_scrollbar_pos( 5200 scrollbar_T *sb, 5201 int x, 5202 int y, 5203 int w, 5204 int h) 5205{ 5206 gui_mch_set_bg_color(gui.back_pixel); 5207/* if (gui.which_scrollbars[SBAR_LEFT]) 5208 { 5209 MoveControl(sb->id, x-16, y); 5210 SizeControl(sb->id, w + 1, h); 5211 } 5212 else 5213 { 5214 MoveControl(sb->id, x, y); 5215 SizeControl(sb->id, w + 1, h); 5216 }*/ 5217 if (sb == &gui.bottom_sbar) 5218 h += 1; 5219 else 5220 w += 1; 5221 5222 if (gui.which_scrollbars[SBAR_LEFT]) 5223 x -= 15; 5224 5225 MoveControl(sb->id, x, y); 5226 SizeControl(sb->id, w, h); 5227#ifdef DEBUG_MAC_SB 5228 printf("size_sb (%x) %x, %x, %x, %x\n",sb->id, x, y, w, h); 5229#endif 5230} 5231 5232 void 5233gui_mch_create_scrollbar( 5234 scrollbar_T *sb, 5235 int orient) /* SBAR_VERT or SBAR_HORIZ */ 5236{ 5237 Rect bounds; 5238 5239 bounds.top = -16; 5240 bounds.bottom = -10; 5241 bounds.right = -10; 5242 bounds.left = -16; 5243 5244 sb->id = NewControl(gui.VimWindow, 5245 &bounds, 5246 "\pScrollBar", 5247 TRUE, 5248 0, /* current*/ 5249 0, /* top */ 5250 0, /* bottom */ 5251 kControlScrollBarLiveProc, 5252 (long) sb->ident); 5253#ifdef DEBUG_MAC_SB 5254 printf("create_sb (%x) %x\n",sb->id, orient); 5255#endif 5256} 5257 5258 void 5259gui_mch_destroy_scrollbar(scrollbar_T *sb) 5260{ 5261 gui_mch_set_bg_color(gui.back_pixel); 5262 DisposeControl(sb->id); 5263#ifdef DEBUG_MAC_SB 5264 printf("dest_sb (%x) \n",sb->id); 5265#endif 5266} 5267 5268 5269/* 5270 * Cursor blink functions. 5271 * 5272 * This is a simple state machine: 5273 * BLINK_NONE not blinking at all 5274 * BLINK_OFF blinking, cursor is not shown 5275 * BLINK_ON blinking, cursor is shown 5276 */ 5277 void 5278gui_mch_set_blinking(long wait, long on, long off) 5279{ 5280 /* TODO: TODO: TODO: TODO: */ 5281/* blink_waittime = wait; 5282 blink_ontime = on; 5283 blink_offtime = off;*/ 5284} 5285 5286/* 5287 * Stop the cursor blinking. Show the cursor if it wasn't shown. 5288 */ 5289 void 5290gui_mch_stop_blink(void) 5291{ 5292 gui_update_cursor(TRUE, FALSE); 5293 /* TODO: TODO: TODO: TODO: */ 5294/* gui_w32_rm_blink_timer(); 5295 if (blink_state == BLINK_OFF) 5296 gui_update_cursor(TRUE, FALSE); 5297 blink_state = BLINK_NONE;*/ 5298} 5299 5300/* 5301 * Start the cursor blinking. If it was already blinking, this restarts the 5302 * waiting time and shows the cursor. 5303 */ 5304 void 5305gui_mch_start_blink(void) 5306{ 5307 gui_update_cursor(TRUE, FALSE); 5308 /* TODO: TODO: TODO: TODO: */ 5309/* gui_w32_rm_blink_timer(); */ 5310 5311 /* Only switch blinking on if none of the times is zero */ 5312/* if (blink_waittime && blink_ontime && blink_offtime) 5313 { 5314 blink_timer = SetTimer(NULL, 0, (UINT)blink_waittime, 5315 (TIMERPROC)_OnBlinkTimer); 5316 blink_state = BLINK_ON; 5317 gui_update_cursor(TRUE, FALSE); 5318 }*/ 5319} 5320 5321/* 5322 * Return the RGB value of a pixel as long. 5323 */ 5324 long_u 5325gui_mch_get_rgb(guicolor_T pixel) 5326{ 5327 return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel); 5328} 5329 5330 5331 5332#ifdef FEAT_BROWSE 5333/* 5334 * Pop open a file browser and return the file selected, in allocated memory, 5335 * or NULL if Cancel is hit. 5336 * saving - TRUE if the file will be saved to, FALSE if it will be opened. 5337 * title - Title message for the file browser dialog. 5338 * dflt - Default name of file. 5339 * ext - Default extension to be added to files without extensions. 5340 * initdir - directory in which to open the browser (NULL = current dir) 5341 * filter - Filter for matched files to choose from. 5342 * Has a format like this: 5343 * "C Files (*.c)\0*.c\0" 5344 * "All Files\0*.*\0\0" 5345 * If these two strings were concatenated, then a choice of two file 5346 * filters will be selectable to the user. Then only matching files will 5347 * be shown in the browser. If NULL, the default allows all files. 5348 * 5349 * *NOTE* - the filter string must be terminated with TWO nulls. 5350 */ 5351 char_u * 5352gui_mch_browse( 5353 int saving, 5354 char_u *title, 5355 char_u *dflt, 5356 char_u *ext, 5357 char_u *initdir, 5358 char_u *filter) 5359{ 5360 /* TODO: Add Ammon's safety checl (Dany) */ 5361 NavReplyRecord reply; 5362 char_u *fname = NULL; 5363 char_u **fnames = NULL; 5364 long numFiles; 5365 NavDialogOptions navOptions; 5366 OSErr error; 5367 5368 /* Get Navigation Service Defaults value */ 5369 NavGetDefaultDialogOptions(&navOptions); 5370 5371 5372 /* TODO: If we get a :browse args, set the Multiple bit. */ 5373 navOptions.dialogOptionFlags = kNavAllowInvisibleFiles 5374 | kNavDontAutoTranslate 5375 | kNavDontAddTranslateItems 5376 /* | kNavAllowMultipleFiles */ 5377 | kNavAllowStationery; 5378 5379 (void) C2PascalString(title, &navOptions.message); 5380 (void) C2PascalString(dflt, &navOptions.savedFileName); 5381 /* Could set clientName? 5382 * windowTitle? (there's no title bar?) 5383 */ 5384 5385 if (saving) 5386 { 5387 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */ 5388 NavPutFile(NULL, &reply, &navOptions, NULL, 'TEXT', 'VIM!', NULL); 5389 if (!reply.validRecord) 5390 return NULL; 5391 } 5392 else 5393 { 5394 /* Change first parm AEDesc (typeFSS) *defaultLocation to match dflt */ 5395 NavGetFile(NULL, &reply, &navOptions, NULL, NULL, NULL, NULL, NULL); 5396 if (!reply.validRecord) 5397 return NULL; 5398 } 5399 5400 fnames = new_fnames_from_AEDesc(&reply.selection, &numFiles, &error); 5401 5402 NavDisposeReply(&reply); 5403 5404 if (fnames) 5405 { 5406 fname = fnames[0]; 5407 vim_free(fnames); 5408 } 5409 5410 /* TODO: Shorten the file name if possible */ 5411 return fname; 5412} 5413#endif /* FEAT_BROWSE */ 5414 5415#ifdef FEAT_GUI_DIALOG 5416/* 5417 * Stuff for dialogues 5418 */ 5419 5420/* 5421 * Create a dialogue dynamically from the parameter strings. 5422 * type = type of dialogue (question, alert, etc.) 5423 * title = dialogue title. may be NULL for default title. 5424 * message = text to display. Dialogue sizes to accommodate it. 5425 * buttons = '\n' separated list of button captions, default first. 5426 * dfltbutton = number of default button. 5427 * 5428 * This routine returns 1 if the first button is pressed, 5429 * 2 for the second, etc. 5430 * 5431 * 0 indicates Esc was pressed. 5432 * -1 for unexpected error 5433 * 5434 * If stubbing out this fn, return 1. 5435 */ 5436 5437typedef struct 5438{ 5439 short idx; 5440 short width; /* Size of the text in pixel */ 5441 Rect box; 5442} vgmDlgItm; /* Vim Gui_Mac.c Dialog Item */ 5443 5444#define MoveRectTo(r,x,y) OffsetRect(r,x-r->left,y-r->top) 5445 5446 static void 5447macMoveDialogItem( 5448 DialogRef theDialog, 5449 short itemNumber, 5450 short X, 5451 short Y, 5452 Rect *inBox) 5453{ 5454#if 0 /* USE_CARBONIZED */ 5455 /* Untested */ 5456 MoveDialogItem(theDialog, itemNumber, X, Y); 5457 if (inBox != nil) 5458 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, inBox); 5459#else 5460 short itemType; 5461 Handle itemHandle; 5462 Rect localBox; 5463 Rect *itemBox = &localBox; 5464 5465 if (inBox != nil) 5466 itemBox = inBox; 5467 5468 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, itemBox); 5469 OffsetRect(itemBox, -itemBox->left, -itemBox->top); 5470 OffsetRect(itemBox, X, Y); 5471 /* To move a control (like a button) we need to call both 5472 * MoveControl and SetDialogItem. FAQ 6-18 */ 5473 if (1) /*(itemType & kControlDialogItem) */ 5474 MoveControl((ControlRef) itemHandle, X, Y); 5475 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, itemBox); 5476#endif 5477} 5478 5479 static void 5480macSizeDialogItem( 5481 DialogRef theDialog, 5482 short itemNumber, 5483 short width, 5484 short height) 5485{ 5486 short itemType; 5487 Handle itemHandle; 5488 Rect itemBox; 5489 5490 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox); 5491 5492 /* When width or height is zero do not change it */ 5493 if (width == 0) 5494 width = itemBox.right - itemBox.left; 5495 if (height == 0) 5496 height = itemBox.bottom - itemBox.top; 5497 5498#if 0 /* USE_CARBONIZED */ 5499 SizeDialogItem(theDialog, itemNumber, width, height); /* Untested */ 5500#else 5501 /* Resize the bounding box */ 5502 itemBox.right = itemBox.left + width; 5503 itemBox.bottom = itemBox.top + height; 5504 5505 /* To resize a control (like a button) we need to call both 5506 * SizeControl and SetDialogItem. (deducted from FAQ 6-18) */ 5507 if (itemType & kControlDialogItem) 5508 SizeControl((ControlRef) itemHandle, width, height); 5509 5510 /* Configure back the item */ 5511 SetDialogItem(theDialog, itemNumber, itemType, itemHandle, &itemBox); 5512#endif 5513} 5514 5515 static void 5516macSetDialogItemText( 5517 DialogRef theDialog, 5518 short itemNumber, 5519 Str255 itemName) 5520{ 5521 short itemType; 5522 Handle itemHandle; 5523 Rect itemBox; 5524 5525 GetDialogItem(theDialog, itemNumber, &itemType, &itemHandle, &itemBox); 5526 5527 if (itemType & kControlDialogItem) 5528 SetControlTitle((ControlRef) itemHandle, itemName); 5529 else 5530 SetDialogItemText(itemHandle, itemName); 5531} 5532 5533 5534/* ModalDialog() handler for message dialogs that have hotkey accelerators. 5535 * Expects a mapping of hotkey char to control index in gDialogHotKeys; 5536 * setting gDialogHotKeys to NULL disables any hotkey handling. 5537 */ 5538 static pascal Boolean 5539DialogHotkeyFilterProc ( 5540 DialogRef theDialog, 5541 EventRecord *event, 5542 DialogItemIndex *itemHit) 5543{ 5544 char_u keyHit; 5545 5546 if (event->what == keyDown || event->what == autoKey) 5547 { 5548 keyHit = (event->message & charCodeMask); 5549 5550 if (gDialogHotKeys && gDialogHotKeys[keyHit]) 5551 { 5552#ifdef DEBUG_MAC_DIALOG_HOTKEYS 5553 printf("user pressed hotkey '%c' --> item %d\n", keyHit, gDialogHotKeys[keyHit]); 5554#endif 5555 *itemHit = gDialogHotKeys[keyHit]; 5556 5557 /* When handing off to StdFilterProc, pretend that the user 5558 * clicked the control manually. Note that this is also supposed 5559 * to cause the button to hilite briefly (to give some user 5560 * feedback), but this seems not to actually work (or it's too 5561 * fast to be seen). 5562 */ 5563 event->what = kEventControlSimulateHit; 5564 5565 return true; /* we took care of it */ 5566 } 5567 5568 /* Defer to the OS's standard behavior for this event. 5569 * This ensures that Enter will still activate the default button. */ 5570 return StdFilterProc(theDialog, event, itemHit); 5571 } 5572 return false; /* Let ModalDialog deal with it */ 5573} 5574 5575 5576/* TODO: There have been some crashes with dialogs, check your inbox 5577 * (Jussi) 5578 */ 5579 int 5580gui_mch_dialog( 5581 int type, 5582 char_u *title, 5583 char_u *message, 5584 char_u *buttons, 5585 int dfltbutton, 5586 char_u *textfield) 5587{ 5588 Handle buttonDITL; 5589 Handle iconDITL; 5590 Handle inputDITL; 5591 Handle messageDITL; 5592 Handle itemHandle; 5593 Handle iconHandle; 5594 DialogPtr theDialog; 5595 char_u len; 5596 char_u PascalTitle[256]; /* place holder for the title */ 5597 char_u name[256]; 5598 GrafPtr oldPort; 5599 short itemHit; 5600 char_u *buttonChar; 5601 short hotKeys[256]; /* map of hotkey -> control ID */ 5602 char_u aHotKey; 5603 Rect box; 5604 short button; 5605 short lastButton; 5606 short itemType; 5607 short useIcon; 5608 short width; 5609 short totalButtonWidth = 0; /* the width of all buttons together 5610 including spacing */ 5611 short widestButton = 0; 5612 short dfltButtonEdge = 20; /* gut feeling */ 5613 short dfltElementSpacing = 13; /* from IM:V.2-29 */ 5614 short dfltIconSideSpace = 23; /* from IM:V.2-29 */ 5615 short maximumWidth = 400; /* gut feeling */ 5616 short maxButtonWidth = 175; /* gut feeling */ 5617 5618 short vertical; 5619 short dialogHeight; 5620 short messageLines = 3; 5621 FontInfo textFontInfo; 5622 5623 vgmDlgItm iconItm; 5624 vgmDlgItm messageItm; 5625 vgmDlgItm inputItm; 5626 vgmDlgItm buttonItm; 5627 5628 WindowRef theWindow; 5629 5630 ModalFilterUPP dialogUPP; 5631 5632 /* Check 'v' flag in 'guioptions': vertical button placement. */ 5633 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); 5634 5635 /* Create a new Dialog Box from template. */ 5636 theDialog = GetNewDialog(129, nil, (WindowRef) -1); 5637 5638 /* Get the WindowRef */ 5639 theWindow = GetDialogWindow(theDialog); 5640 5641 /* Hide the window. 5642 * 1. to avoid seeing slow drawing 5643 * 2. to prevent a problem seen while moving dialog item 5644 * within a visible window. (non-Carbon MacOS 9) 5645 * Could be avoided by changing the resource. 5646 */ 5647 HideWindow(theWindow); 5648 5649 /* Change the graphical port to the dialog, 5650 * so we can measure the text with the proper font */ 5651 GetPort(&oldPort); 5652 SetPortDialogPort(theDialog); 5653 5654 /* Get the info about the default text, 5655 * used to calculate the height of the message 5656 * and of the text field */ 5657 GetFontInfo(&textFontInfo); 5658 5659 /* Set the dialog title */ 5660 if (title != NULL) 5661 { 5662 (void) C2PascalString(title, &PascalTitle); 5663 SetWTitle(theWindow, PascalTitle); 5664 } 5665 5666 /* Creates the buttons and add them to the Dialog Box. */ 5667 buttonDITL = GetResource('DITL', 130); 5668 buttonChar = buttons; 5669 button = 0; 5670 5671 /* initialize the hotkey mapping */ 5672 vim_memset(hotKeys, 0, sizeof(hotKeys)); 5673 5674 for (;*buttonChar != 0;) 5675 { 5676 /* Get the name of the button */ 5677 button++; 5678 len = 0; 5679 for (;((*buttonChar != DLG_BUTTON_SEP) && (*buttonChar != 0) && (len < 255)); buttonChar++) 5680 { 5681 if (*buttonChar != DLG_HOTKEY_CHAR) 5682 name[++len] = *buttonChar; 5683 else 5684 { 5685 aHotKey = (char_u)*(buttonChar+1); 5686 if (aHotKey >= 'A' && aHotKey <= 'Z') 5687 aHotKey = (char_u)((int)aHotKey + (int)'a' - (int)'A'); 5688 hotKeys[aHotKey] = button; 5689#ifdef DEBUG_MAC_DIALOG_HOTKEYS 5690 printf("### hotKey for button %d is '%c'\n", button, aHotKey); 5691#endif 5692 } 5693 } 5694 5695 if (*buttonChar != 0) 5696 buttonChar++; 5697 name[0] = len; 5698 5699 /* Add the button */ 5700 AppendDITL(theDialog, buttonDITL, overlayDITL); /* appendDITLRight); */ 5701 5702 /* Change the button's name */ 5703 macSetDialogItemText(theDialog, button, name); 5704 5705 /* Resize the button to fit its name */ 5706 width = StringWidth(name) + 2 * dfltButtonEdge; 5707 /* Limite the size of any button to an acceptable value. */ 5708 /* TODO: Should be based on the message width */ 5709 if (width > maxButtonWidth) 5710 width = maxButtonWidth; 5711 macSizeDialogItem(theDialog, button, width, 0); 5712 5713 totalButtonWidth += width; 5714 5715 if (width > widestButton) 5716 widestButton = width; 5717 } 5718 ReleaseResource(buttonDITL); 5719 lastButton = button; 5720 5721 /* Add the icon to the Dialog Box. */ 5722 iconItm.idx = lastButton + 1; 5723 iconDITL = GetResource('DITL', 131); 5724 switch (type) 5725 { 5726 case VIM_GENERIC: useIcon = kNoteIcon; 5727 case VIM_ERROR: useIcon = kStopIcon; 5728 case VIM_WARNING: useIcon = kCautionIcon; 5729 case VIM_INFO: useIcon = kNoteIcon; 5730 case VIM_QUESTION: useIcon = kNoteIcon; 5731 default: useIcon = kStopIcon; 5732 }; 5733 AppendDITL(theDialog, iconDITL, overlayDITL); 5734 ReleaseResource(iconDITL); 5735 GetDialogItem(theDialog, iconItm.idx, &itemType, &itemHandle, &box); 5736 /* TODO: Should the item be freed? */ 5737 iconHandle = GetIcon(useIcon); 5738 SetDialogItem(theDialog, iconItm.idx, itemType, iconHandle, &box); 5739 5740 /* Add the message to the Dialog box. */ 5741 messageItm.idx = lastButton + 2; 5742 messageDITL = GetResource('DITL', 132); 5743 AppendDITL(theDialog, messageDITL, overlayDITL); 5744 ReleaseResource(messageDITL); 5745 GetDialogItem(theDialog, messageItm.idx, &itemType, &itemHandle, &box); 5746 (void) C2PascalString(message, &name); 5747 SetDialogItemText(itemHandle, name); 5748 messageItm.width = StringWidth(name); 5749 5750 /* Add the input box if needed */ 5751 if (textfield != NULL) 5752 { 5753 /* Cheat for now reuse the message and convert to text edit */ 5754 inputItm.idx = lastButton + 3; 5755 inputDITL = GetResource('DITL', 132); 5756 AppendDITL(theDialog, inputDITL, overlayDITL); 5757 ReleaseResource(inputDITL); 5758 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box); 5759/* SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &box);*/ 5760 (void) C2PascalString(textfield, &name); 5761 SetDialogItemText(itemHandle, name); 5762 inputItm.width = StringWidth(name); 5763 5764 /* Hotkeys don't make sense if there's a text field */ 5765 gDialogHotKeys = NULL; 5766 } 5767 else 5768 /* Install hotkey table */ 5769 gDialogHotKeys = (short *)&hotKeys; 5770 5771 /* Set the <ENTER> and <ESC> button. */ 5772 SetDialogDefaultItem(theDialog, dfltbutton); 5773 SetDialogCancelItem(theDialog, 0); 5774 5775 /* Reposition element */ 5776 5777 /* Check if we need to force vertical */ 5778 if (totalButtonWidth > maximumWidth) 5779 vertical = TRUE; 5780 5781 /* Place icon */ 5782 macMoveDialogItem(theDialog, iconItm.idx, dfltIconSideSpace, dfltElementSpacing, &box); 5783 iconItm.box.right = box.right; 5784 iconItm.box.bottom = box.bottom; 5785 5786 /* Place Message */ 5787 messageItm.box.left = iconItm.box.right + dfltIconSideSpace; 5788 macSizeDialogItem(theDialog, messageItm.idx, 0, messageLines * (textFontInfo.ascent + textFontInfo.descent)); 5789 macMoveDialogItem(theDialog, messageItm.idx, messageItm.box.left, dfltElementSpacing, &messageItm.box); 5790 5791 /* Place Input */ 5792 if (textfield != NULL) 5793 { 5794 inputItm.box.left = messageItm.box.left; 5795 inputItm.box.top = messageItm.box.bottom + dfltElementSpacing; 5796 macSizeDialogItem(theDialog, inputItm.idx, 0, textFontInfo.ascent + textFontInfo.descent); 5797 macMoveDialogItem(theDialog, inputItm.idx, inputItm.box.left, inputItm.box.top, &inputItm.box); 5798 /* Convert the static text into a text edit. 5799 * For some reason this change need to be done last (Dany) */ 5800 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &inputItm.box); 5801 SetDialogItem(theDialog, inputItm.idx, kEditTextDialogItem, itemHandle, &inputItm.box); 5802 SelectDialogItemText(theDialog, inputItm.idx, 0, 32767); 5803 } 5804 5805 /* Place Button */ 5806 if (textfield != NULL) 5807 { 5808 buttonItm.box.left = inputItm.box.left; 5809 buttonItm.box.top = inputItm.box.bottom + dfltElementSpacing; 5810 } 5811 else 5812 { 5813 buttonItm.box.left = messageItm.box.left; 5814 buttonItm.box.top = messageItm.box.bottom + dfltElementSpacing; 5815 } 5816 5817 for (button=1; button <= lastButton; button++) 5818 { 5819 5820 macMoveDialogItem(theDialog, button, buttonItm.box.left, buttonItm.box.top, &box); 5821 /* With vertical, it's better to have all buttons the same length */ 5822 if (vertical) 5823 { 5824 macSizeDialogItem(theDialog, button, widestButton, 0); 5825 GetDialogItem(theDialog, button, &itemType, &itemHandle, &box); 5826 } 5827 /* Calculate position of next button */ 5828 if (vertical) 5829 buttonItm.box.top = box.bottom + dfltElementSpacing; 5830 else 5831 buttonItm.box.left = box.right + dfltElementSpacing; 5832 } 5833 5834 /* Resize the dialog box */ 5835 dialogHeight = box.bottom + dfltElementSpacing; 5836 SizeWindow(theWindow, maximumWidth, dialogHeight, TRUE); 5837 5838 /* Magic resize */ 5839 AutoSizeDialog(theDialog); 5840 /* Need a horizontal resize anyway so not that useful */ 5841 5842 /* Display it */ 5843 ShowWindow(theWindow); 5844/* BringToFront(theWindow); */ 5845 SelectWindow(theWindow); 5846 5847/* DrawDialog(theDialog); */ 5848#if 0 5849 GetPort(&oldPort); 5850 SetPortDialogPort(theDialog); 5851#endif 5852 5853#ifdef USE_CARBONKEYHANDLER 5854 /* Avoid that we use key events for the main window. */ 5855 dialog_busy = TRUE; 5856#endif 5857 5858 /* Prepare the shortcut-handling filterProc for handing to the dialog */ 5859 dialogUPP = NewModalFilterUPP(DialogHotkeyFilterProc); 5860 5861 /* Hang until one of the button is hit */ 5862 do 5863 { 5864 ModalDialog(dialogUPP, &itemHit); 5865 } while ((itemHit < 1) || (itemHit > lastButton)); 5866 5867#ifdef USE_CARBONKEYHANDLER 5868 dialog_busy = FALSE; 5869#endif 5870 5871 /* Copy back the text entered by the user into the param */ 5872 if (textfield != NULL) 5873 { 5874 GetDialogItem(theDialog, inputItm.idx, &itemType, &itemHandle, &box); 5875 GetDialogItemText(itemHandle, (char_u *) &name); 5876#if IOSIZE < 256 5877 /* Truncate the name to IOSIZE if needed */ 5878 if (name[0] > IOSIZE) 5879 name[0] = IOSIZE - 1; 5880#endif 5881 vim_strncpy(textfield, &name[1], name[0]); 5882 } 5883 5884 /* Restore the original graphical port */ 5885 SetPort(oldPort); 5886 5887 /* Free the modal filterProc */ 5888 DisposeRoutineDescriptor(dialogUPP); 5889 5890 /* Get ride of th edialog (free memory) */ 5891 DisposeDialog(theDialog); 5892 5893 return itemHit; 5894/* 5895 * Usefull thing which could be used 5896 * SetDialogTimeout(): Auto click a button after timeout 5897 * SetDialogTracksCursor() : Get the I-beam cursor over input box 5898 * MoveDialogItem(): Probably better than SetDialogItem 5899 * SizeDialogItem(): (but is it Carbon Only?) 5900 * AutoSizeDialog(): Magic resize of dialog based on text length 5901 */ 5902} 5903#endif /* FEAT_DIALOG_GUI */ 5904 5905/* 5906 * Display the saved error message(s). 5907 */ 5908#ifdef USE_MCH_ERRMSG 5909 void 5910display_errors(void) 5911{ 5912 char *p; 5913 char_u pError[256]; 5914 5915 if (error_ga.ga_data == NULL) 5916 return; 5917 5918 /* avoid putting up a message box with blanks only */ 5919 for (p = (char *)error_ga.ga_data; *p; ++p) 5920 if (!isspace(*p)) 5921 { 5922 if (STRLEN(p) > 255) 5923 pError[0] = 255; 5924 else 5925 pError[0] = STRLEN(p); 5926 5927 STRNCPY(&pError[1], p, pError[0]); 5928 ParamText(pError, nil, nil, nil); 5929 Alert(128, nil); 5930 break; 5931 /* TODO: handled message longer than 256 chars 5932 * use auto-sizeable alert 5933 * or dialog with scrollbars (TextEdit zone) 5934 */ 5935 } 5936 ga_clear(&error_ga); 5937} 5938#endif 5939 5940/* 5941 * Get current mouse coordinates in text window. 5942 */ 5943 void 5944gui_mch_getmouse(int *x, int *y) 5945{ 5946 Point where; 5947 5948 GetMouse(&where); 5949 5950 *x = where.h; 5951 *y = where.v; 5952} 5953 5954 void 5955gui_mch_setmouse(int x, int y) 5956{ 5957 /* TODO */ 5958#if 0 5959 /* From FAQ 3-11 */ 5960 5961 CursorDevicePtr myMouse; 5962 Point where; 5963 5964 if ( NGetTrapAddress(_CursorDeviceDispatch, ToolTrap) 5965 != NGetTrapAddress(_Unimplemented, ToolTrap)) 5966 { 5967 /* New way */ 5968 5969 /* 5970 * Get first devoice with one button. 5971 * This will probably be the standad mouse 5972 * startat head of cursor dev list 5973 * 5974 */ 5975 5976 myMouse = nil; 5977 5978 do 5979 { 5980 /* Get the next cursor device */ 5981 CursorDeviceNextDevice(&myMouse); 5982 } 5983 while ((myMouse != nil) && (myMouse->cntButtons != 1)); 5984 5985 CursorDeviceMoveTo(myMouse, x, y); 5986 } 5987 else 5988 { 5989 /* Old way */ 5990 where.h = x; 5991 where.v = y; 5992 5993 *(Point *)RawMouse = where; 5994 *(Point *)MTemp = where; 5995 *(Ptr) CrsrNew = 0xFFFF; 5996 } 5997#endif 5998} 5999 6000 void 6001gui_mch_show_popupmenu(vimmenu_T *menu) 6002{ 6003/* 6004 * Clone PopUp to use menu 6005 * Create a object descriptor for the current selection 6006 * Call the procedure 6007 */ 6008 6009 MenuHandle CntxMenu; 6010 Point where; 6011 OSStatus status; 6012 UInt32 CntxType; 6013 SInt16 CntxMenuID; 6014 UInt16 CntxMenuItem; 6015 Str255 HelpName = ""; 6016 GrafPtr savePort; 6017 6018 /* Save Current Port: On MacOS X we seem to lose the port */ 6019 GetPort(&savePort); /*OSX*/ 6020 6021 GetMouse(&where); 6022 LocalToGlobal(&where); /*OSX*/ 6023 CntxMenu = menu->submenu_handle; 6024 6025 /* TODO: Get the text selection from Vim */ 6026 6027 /* Call to Handle Popup */ 6028 status = ContextualMenuSelect(CntxMenu, where, false, kCMHelpItemRemoveHelp, 6029 HelpName, NULL, &CntxType, &CntxMenuID, &CntxMenuItem); 6030 6031 if (status == noErr) 6032 { 6033 if (CntxType == kCMMenuItemSelected) 6034 { 6035 /* Handle the menu CntxMenuID, CntxMenuItem */ 6036 /* The submenu can be handle directly by gui_mac_handle_menu */ 6037 /* But what about the current menu, is the menu changed by 6038 * ContextualMenuSelect */ 6039 gui_mac_handle_menu((CntxMenuID << 16) + CntxMenuItem); 6040 } 6041 else if (CntxMenuID == kCMShowHelpSelected) 6042 { 6043 /* Should come up with the help */ 6044 } 6045 } 6046 6047 /* Restore original Port */ 6048 SetPort(savePort); /*OSX*/ 6049} 6050 6051#if defined(FEAT_CW_EDITOR) || defined(PROTO) 6052/* TODO: Is it need for MACOS_X? (Dany) */ 6053 void 6054mch_post_buffer_write(buf_T *buf) 6055{ 6056 GetFSSpecFromPath(buf->b_ffname, &buf->b_FSSpec); 6057 Send_KAHL_MOD_AE(buf); 6058} 6059#endif 6060 6061#ifdef FEAT_TITLE 6062/* 6063 * Set the window title and icon. 6064 * (The icon is not taken care of). 6065 */ 6066 void 6067gui_mch_settitle(char_u *title, char_u *icon) 6068{ 6069 /* TODO: Get vim to make sure maxlen (from p_titlelen) is smaller 6070 * that 256. Even better get it to fit nicely in the titlebar. 6071 */ 6072#ifdef MACOS_CONVERT 6073 CFStringRef windowTitle; 6074 size_t windowTitleLen; 6075#else 6076 char_u *pascalTitle; 6077#endif 6078 6079 if (title == NULL) /* nothing to do */ 6080 return; 6081 6082#ifdef MACOS_CONVERT 6083 windowTitleLen = STRLEN(title); 6084 windowTitle = (CFStringRef)mac_enc_to_cfstring(title, windowTitleLen); 6085 6086 if (windowTitle) 6087 { 6088 SetWindowTitleWithCFString(gui.VimWindow, windowTitle); 6089 CFRelease(windowTitle); 6090 } 6091#else 6092 pascalTitle = C2Pascal_save(title); 6093 if (pascalTitle != NULL) 6094 { 6095 SetWTitle(gui.VimWindow, pascalTitle); 6096 vim_free(pascalTitle); 6097 } 6098#endif 6099} 6100#endif 6101 6102/* 6103 * Transfered from os_mac.c for MacOS X using os_unix.c prep work 6104 */ 6105 6106 int 6107C2PascalString(char_u *CString, Str255 *PascalString) 6108{ 6109 char_u *PascalPtr = (char_u *) PascalString; 6110 int len; 6111 int i; 6112 6113 PascalPtr[0] = 0; 6114 if (CString == NULL) 6115 return 0; 6116 6117 len = STRLEN(CString); 6118 if (len > 255) 6119 len = 255; 6120 6121 for (i = 0; i < len; i++) 6122 PascalPtr[i+1] = CString[i]; 6123 6124 PascalPtr[0] = len; 6125 6126 return 0; 6127} 6128 6129 int 6130GetFSSpecFromPath(char_u *file, FSSpec *fileFSSpec) 6131{ 6132 /* From FAQ 8-12 */ 6133 Str255 filePascal; 6134 CInfoPBRec myCPB; 6135 OSErr err; 6136 6137 (void) C2PascalString(file, &filePascal); 6138 6139 myCPB.dirInfo.ioNamePtr = filePascal; 6140 myCPB.dirInfo.ioVRefNum = 0; 6141 myCPB.dirInfo.ioFDirIndex = 0; 6142 myCPB.dirInfo.ioDrDirID = 0; 6143 6144 err= PBGetCatInfo(&myCPB, false); 6145 6146 /* vRefNum, dirID, name */ 6147 FSMakeFSSpec(0, 0, filePascal, fileFSSpec); 6148 6149 /* TODO: Use an error code mechanism */ 6150 return 0; 6151} 6152 6153/* 6154 * Convert a FSSpec to a fuill path 6155 */ 6156 6157char_u *FullPathFromFSSpec_save(FSSpec file) 6158{ 6159 /* 6160 * TODO: Add protection for 256 char max. 6161 */ 6162 6163 CInfoPBRec theCPB; 6164 char_u fname[256]; 6165 char_u *filenamePtr = fname; 6166 OSErr error; 6167 int folder = 1; 6168#ifdef USE_UNIXFILENAME 6169 SInt16 dfltVol_vRefNum; 6170 SInt32 dfltVol_dirID; 6171 FSRef refFile; 6172 OSStatus status; 6173 UInt32 pathSize = 256; 6174 char_u pathname[256]; 6175 char_u *path = pathname; 6176#else 6177 Str255 directoryName; 6178 char_u temporary[255]; 6179 char_u *temporaryPtr = temporary; 6180#endif 6181 6182#ifdef USE_UNIXFILENAME 6183 /* Get the default volume */ 6184 /* TODO: Remove as this only work if Vim is on the Boot Volume*/ 6185 error=HGetVol(NULL, &dfltVol_vRefNum, &dfltVol_dirID); 6186 6187 if (error) 6188 return NULL; 6189#endif 6190 6191 /* Start filling fname with file.name */ 6192 vim_strncpy(filenamePtr, &file.name[1], file.name[0]); 6193 6194 /* Get the info about the file specified in FSSpec */ 6195 theCPB.dirInfo.ioFDirIndex = 0; 6196 theCPB.dirInfo.ioNamePtr = file.name; 6197 theCPB.dirInfo.ioVRefNum = file.vRefNum; 6198 /*theCPB.hFileInfo.ioDirID = 0;*/ 6199 theCPB.dirInfo.ioDrDirID = file.parID; 6200 6201 /* As ioFDirIndex = 0, get the info of ioNamePtr, 6202 which is relative to ioVrefNum, ioDirID */ 6203 error = PBGetCatInfo(&theCPB, false); 6204 6205 /* If we are called for a new file we expect fnfErr */ 6206 if ((error) && (error != fnfErr)) 6207 return NULL; 6208 6209 /* Check if it's a file or folder */ 6210 /* default to file if file don't exist */ 6211 if (((theCPB.hFileInfo.ioFlAttrib & ioDirMask) == 0) || (error)) 6212 folder = 0; /* It's not a folder */ 6213 else 6214 folder = 1; 6215 6216#ifdef USE_UNIXFILENAME 6217 /* 6218 * The function used here are available in Carbon, but 6219 * do nothing une MacOS 8 and 9 6220 */ 6221 if (error == fnfErr) 6222 { 6223 /* If the file to be saved does not already exist, it isn't possible 6224 to convert its FSSpec into an FSRef. But we can construct an 6225 FSSpec for the file's parent folder (since we have its volume and 6226 directory IDs), and since that folder does exist, we can convert 6227 that FSSpec into an FSRef, convert the FSRef in turn into a path, 6228 and, finally, append the filename. */ 6229 FSSpec dirSpec; 6230 FSRef dirRef; 6231 Str255 emptyFilename = "\p"; 6232 error = FSMakeFSSpec(theCPB.dirInfo.ioVRefNum, 6233 theCPB.dirInfo.ioDrDirID, emptyFilename, &dirSpec); 6234 if (error) 6235 return NULL; 6236 6237 error = FSpMakeFSRef(&dirSpec, &dirRef); 6238 if (error) 6239 return NULL; 6240 6241 status = FSRefMakePath(&dirRef, (UInt8*)path, pathSize); 6242 if (status) 6243 return NULL; 6244 6245 STRCAT(path, "/"); 6246 STRCAT(path, filenamePtr); 6247 } 6248 else 6249 { 6250 /* If the file to be saved already exists, we can get its full path 6251 by converting its FSSpec into an FSRef. */ 6252 error=FSpMakeFSRef(&file, &refFile); 6253 if (error) 6254 return NULL; 6255 6256 status=FSRefMakePath(&refFile, (UInt8 *) path, pathSize); 6257 if (status) 6258 return NULL; 6259 } 6260 6261 /* Add a slash at the end if needed */ 6262 if (folder) 6263 STRCAT(path, "/"); 6264 6265 return (vim_strsave(path)); 6266#else 6267 /* TODO: Get rid of all USE_UNIXFILENAME below */ 6268 /* Set ioNamePtr, it's the same area which is always reused. */ 6269 theCPB.dirInfo.ioNamePtr = directoryName; 6270 6271 /* Trick for first entry, set ioDrParID to the first value 6272 * we want for ioDrDirID*/ 6273 theCPB.dirInfo.ioDrParID = file.parID; 6274 theCPB.dirInfo.ioDrDirID = file.parID; 6275 6276 if ((TRUE) && (file.parID != fsRtDirID /*fsRtParID*/)) 6277 do 6278 { 6279 theCPB.dirInfo.ioFDirIndex = -1; 6280 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */ 6281 theCPB.dirInfo.ioVRefNum = file.vRefNum; 6282 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */ 6283 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID; 6284 6285 /* As ioFDirIndex = -1, get the info of ioDrDirID, */ 6286 /* *ioNamePtr[0 TO 31] will be updated */ 6287 error = PBGetCatInfo(&theCPB,false); 6288 6289 if (error) 6290 return NULL; 6291 6292 /* Put the new directoryName in front of the current fname */ 6293 STRCPY(temporaryPtr, filenamePtr); 6294 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]); 6295 STRCAT(filenamePtr, ":"); 6296 STRCAT(filenamePtr, temporaryPtr); 6297 } 6298#if 1 /* def USE_UNIXFILENAME */ 6299 while ((theCPB.dirInfo.ioDrParID != fsRtDirID) /* && */ 6300 /* (theCPB.dirInfo.ioDrDirID != fsRtDirID)*/); 6301#else 6302 while (theCPB.dirInfo.ioDrDirID != fsRtDirID); 6303#endif 6304 6305 /* Get the information about the volume on which the file reside */ 6306 theCPB.dirInfo.ioFDirIndex = -1; 6307 /* theCPB.dirInfo.ioNamePtr = directoryName; Already done above. */ 6308 theCPB.dirInfo.ioVRefNum = file.vRefNum; 6309 /* theCPB.dirInfo.ioDirID = irrelevant when ioFDirIndex = -1 */ 6310 theCPB.dirInfo.ioDrDirID = theCPB.dirInfo.ioDrParID; 6311 6312 /* As ioFDirIndex = -1, get the info of ioDrDirID, */ 6313 /* *ioNamePtr[0 TO 31] will be updated */ 6314 error = PBGetCatInfo(&theCPB,false); 6315 6316 if (error) 6317 return NULL; 6318 6319 /* For MacOS Classic always add the volume name */ 6320 /* For MacOS X add the volume name preceded by "Volumes" */ 6321 /* when we are not referring to the boot volume */ 6322#ifdef USE_UNIXFILENAME 6323 if (file.vRefNum != dfltVol_vRefNum) 6324#endif 6325 { 6326 /* Add the volume name */ 6327 STRCPY(temporaryPtr, filenamePtr); 6328 vim_strncpy(filenamePtr, &directoryName[1], directoryName[0]); 6329 STRCAT(filenamePtr, ":"); 6330 STRCAT(filenamePtr, temporaryPtr); 6331 6332#ifdef USE_UNIXFILENAME 6333 STRCPY(temporaryPtr, filenamePtr); 6334 filenamePtr[0] = 0; /* NULL terminate the string */ 6335 STRCAT(filenamePtr, "Volumes:"); 6336 STRCAT(filenamePtr, temporaryPtr); 6337#endif 6338 } 6339 6340 /* Append final path separator if it's a folder */ 6341 if (folder) 6342 STRCAT(fname, ":"); 6343 6344 /* As we use Unix File Name for MacOS X convert it */ 6345#ifdef USE_UNIXFILENAME 6346 /* Need to insert leading / */ 6347 /* TODO: get the above code to use directly the / */ 6348 STRCPY(&temporaryPtr[1], filenamePtr); 6349 temporaryPtr[0] = '/'; 6350 STRCPY(filenamePtr, temporaryPtr); 6351 { 6352 char *p; 6353 for (p = fname; *p; p++) 6354 if (*p == ':') 6355 *p = '/'; 6356 } 6357#endif 6358 6359 return (vim_strsave(fname)); 6360#endif 6361} 6362 6363#if (defined(USE_IM_CONTROL) || defined(PROTO)) && defined(USE_CARBONKEYHANDLER) 6364/* 6365 * Input Method Control functions. 6366 */ 6367 6368/* 6369 * Notify cursor position to IM. 6370 */ 6371 void 6372im_set_position(int row, int col) 6373{ 6374#if 0 6375 /* TODO: Implement me! */ 6376 im_start_row = row; 6377 im_start_col = col; 6378#endif 6379} 6380 6381static ScriptLanguageRecord gTSLWindow; 6382static ScriptLanguageRecord gTSLInsert; 6383static ScriptLanguageRecord gTSLDefault = { 0, 0 }; 6384 6385static Component gTSCWindow; 6386static Component gTSCInsert; 6387static Component gTSCDefault; 6388 6389static int im_initialized = 0; 6390 6391 static void 6392im_on_window_switch(int active) 6393{ 6394 ScriptLanguageRecord *slptr = NULL; 6395 OSStatus err; 6396 6397 if (! gui.in_use) 6398 return; 6399 6400 if (im_initialized == 0) 6401 { 6402 im_initialized = 1; 6403 6404 /* save default TSM component (should be U.S.) to default */ 6405 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault, 6406 kKeyboardInputMethodClass); 6407 } 6408 6409 if (active == TRUE) 6410 { 6411 im_is_active = TRUE; 6412 ActivateTSMDocument(gTSMDocument); 6413 slptr = &gTSLWindow; 6414 6415 if (slptr) 6416 { 6417 err = SetDefaultInputMethodOfClass(gTSCWindow, slptr, 6418 kKeyboardInputMethodClass); 6419 if (err == noErr) 6420 err = SetTextServiceLanguage(slptr); 6421 6422 if (err == noErr) 6423 KeyScript(slptr->fScript | smKeyForceKeyScriptMask); 6424 } 6425 } 6426 else 6427 { 6428 err = GetTextServiceLanguage(&gTSLWindow); 6429 if (err == noErr) 6430 slptr = &gTSLWindow; 6431 6432 if (slptr) 6433 GetDefaultInputMethodOfClass(&gTSCWindow, slptr, 6434 kKeyboardInputMethodClass); 6435 6436 im_is_active = FALSE; 6437 DeactivateTSMDocument(gTSMDocument); 6438 } 6439} 6440 6441/* 6442 * Set IM status on ("active" is TRUE) or off ("active" is FALSE). 6443 */ 6444 void 6445im_set_active(int active) 6446{ 6447 ScriptLanguageRecord *slptr = NULL; 6448 OSStatus err; 6449 6450 if (! gui.in_use) 6451 return; 6452 6453 if (im_initialized == 0) 6454 { 6455 im_initialized = 1; 6456 6457 /* save default TSM component (should be U.S.) to default */ 6458 GetDefaultInputMethodOfClass(&gTSCDefault, &gTSLDefault, 6459 kKeyboardInputMethodClass); 6460 } 6461 6462 if (active == TRUE) 6463 { 6464 im_is_active = TRUE; 6465 ActivateTSMDocument(gTSMDocument); 6466 slptr = &gTSLInsert; 6467 6468 if (slptr) 6469 { 6470 err = SetDefaultInputMethodOfClass(gTSCInsert, slptr, 6471 kKeyboardInputMethodClass); 6472 if (err == noErr) 6473 err = SetTextServiceLanguage(slptr); 6474 6475 if (err == noErr) 6476 KeyScript(slptr->fScript | smKeyForceKeyScriptMask); 6477 } 6478 } 6479 else 6480 { 6481 err = GetTextServiceLanguage(&gTSLInsert); 6482 if (err == noErr) 6483 slptr = &gTSLInsert; 6484 6485 if (slptr) 6486 GetDefaultInputMethodOfClass(&gTSCInsert, slptr, 6487 kKeyboardInputMethodClass); 6488 6489 /* restore to default when switch to normal mode, so than we could 6490 * enter commands easier */ 6491 SetDefaultInputMethodOfClass(gTSCDefault, &gTSLDefault, 6492 kKeyboardInputMethodClass); 6493 SetTextServiceLanguage(&gTSLDefault); 6494 6495 im_is_active = FALSE; 6496 DeactivateTSMDocument(gTSMDocument); 6497 } 6498} 6499 6500/* 6501 * Get IM status. When IM is on, return not 0. Else return 0. 6502 */ 6503 int 6504im_get_status(void) 6505{ 6506 if (! gui.in_use) 6507 return 0; 6508 6509 return im_is_active; 6510} 6511 6512#endif /* defined(USE_IM_CONTROL) || defined(PROTO) */ 6513 6514 6515 6516 6517#if defined(FEAT_GUI_TABLINE) || defined(PROTO) 6518// drawer implementation 6519static MenuRef contextMenu = NULL; 6520enum 6521{ 6522 kTabContextMenuId = 42, 6523}; 6524 6525// the caller has to CFRelease() the returned string 6526 static CFStringRef 6527getTabLabel(tabpage_T *page) 6528{ 6529 get_tabline_label(page, FALSE); 6530#ifdef MACOS_CONVERT 6531 return (CFStringRef)mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff)); 6532#else 6533 // TODO: check internal encoding? 6534 return CFStringCreateWithCString(kCFAllocatorDefault, (char *)NameBuff, 6535 kCFStringEncodingMacRoman); 6536#endif 6537} 6538 6539 6540#define DRAWER_SIZE 150 6541#define DRAWER_INSET 16 6542 6543static ControlRef dataBrowser = NULL; 6544 6545// when the tabline is hidden, vim doesn't call update_tabline(). When 6546// the tabline is shown again, show_tabline() is called before upate_tabline(), 6547// and because of this, the tab labels and vims internal tabs are out of sync 6548// for a very short time. to prevent inconsistent state, we store the labels 6549// of the tabs, not pointers to the tabs (which are invalid for a short time). 6550static CFStringRef *tabLabels = NULL; 6551static int tabLabelsSize = 0; 6552 6553enum 6554{ 6555 kTabsColumn = 'Tabs' 6556}; 6557 6558 static int 6559getTabCount(void) 6560{ 6561 tabpage_T *tp; 6562 int numTabs = 0; 6563 6564 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) 6565 ++numTabs; 6566 return numTabs; 6567} 6568 6569// data browser item display callback 6570 static OSStatus 6571dbItemDataCallback(ControlRef browser, 6572 DataBrowserItemID itemID, 6573 DataBrowserPropertyID property /* column id */, 6574 DataBrowserItemDataRef itemData, 6575 Boolean changeValue) 6576{ 6577 OSStatus status = noErr; 6578 6579 // assert(property == kTabsColumn); // why is this violated?? 6580 6581 // changeValue is true if we have a modifieable list and data was changed. 6582 // In our case, it's always false. 6583 // (that is: if (changeValue) updateInternalData(); else return 6584 // internalData(); 6585 if (!changeValue) 6586 { 6587 CFStringRef str; 6588 6589 assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize); 6590 str = tabLabels[itemID - 1]; 6591 status = SetDataBrowserItemDataText(itemData, str); 6592 } 6593 else 6594 status = errDataBrowserPropertyNotSupported; 6595 6596 return status; 6597} 6598 6599// data browser action callback 6600 static void 6601dbItemNotificationCallback(ControlRef browser, 6602 DataBrowserItemID item, 6603 DataBrowserItemNotification message) 6604{ 6605 switch (message) 6606 { 6607 case kDataBrowserItemSelected: 6608 send_tabline_event(item); 6609 break; 6610 } 6611} 6612 6613// callbacks needed for contextual menu: 6614 static void 6615dbGetContextualMenuCallback(ControlRef browser, 6616 MenuRef *menu, 6617 UInt32 *helpType, 6618 CFStringRef *helpItemString, 6619 AEDesc *selection) 6620{ 6621 // on mac os 9: kCMHelpItemNoHelp, but it's not the same 6622 *helpType = kCMHelpItemRemoveHelp; // OS X only ;-) 6623 *helpItemString = NULL; 6624 6625 *menu = contextMenu; 6626} 6627 6628 static void 6629dbSelectContextualMenuCallback(ControlRef browser, 6630 MenuRef menu, 6631 UInt32 selectionType, 6632 SInt16 menuID, 6633 MenuItemIndex menuItem) 6634{ 6635 if (selectionType == kCMMenuItemSelected) 6636 { 6637 MenuCommand command; 6638 GetMenuItemCommandID(menu, menuItem, &command); 6639 6640 // get tab that was selected when the context menu appeared 6641 // (there is always one tab selected). TODO: check if the context menu 6642 // isn't opened on an item but on empty space (has to be possible some 6643 // way, the finder does it too ;-) ) 6644 Handle items = NewHandle(0); 6645 if (items != NULL) 6646 { 6647 int numItems; 6648 6649 GetDataBrowserItems(browser, kDataBrowserNoItem, false, 6650 kDataBrowserItemIsSelected, items); 6651 numItems = GetHandleSize(items) / sizeof(DataBrowserItemID); 6652 if (numItems > 0) 6653 { 6654 int idx; 6655 DataBrowserItemID *itemsPtr; 6656 6657 HLock(items); 6658 itemsPtr = (DataBrowserItemID *)*items; 6659 idx = itemsPtr[0]; 6660 HUnlock(items); 6661 send_tabline_menu_event(idx, command); 6662 } 6663 DisposeHandle(items); 6664 } 6665 } 6666} 6667 6668// focus callback of the data browser to always leave focus in vim 6669 static OSStatus 6670dbFocusCallback(EventHandlerCallRef handler, EventRef event, void *data) 6671{ 6672 assert(GetEventClass(event) == kEventClassControl 6673 && GetEventKind(event) == kEventControlSetFocusPart); 6674 6675 return paramErr; 6676} 6677 6678 6679// drawer callback to resize data browser to drawer size 6680 static OSStatus 6681drawerCallback(EventHandlerCallRef handler, EventRef event, void *data) 6682{ 6683 switch (GetEventKind(event)) 6684 { 6685 case kEventWindowBoundsChanged: // move or resize 6686 { 6687 UInt32 attribs; 6688 GetEventParameter(event, kEventParamAttributes, typeUInt32, 6689 NULL, sizeof(attribs), NULL, &attribs); 6690 if (attribs & kWindowBoundsChangeSizeChanged) // resize 6691 { 6692 Rect r; 6693 GetWindowBounds(drawer, kWindowContentRgn, &r); 6694 SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top); 6695 SetControlBounds(dataBrowser, &r); 6696 SetDataBrowserTableViewNamedColumnWidth(dataBrowser, 6697 kTabsColumn, r.right); 6698 } 6699 } 6700 break; 6701 } 6702 6703 return eventNotHandledErr; 6704} 6705 6706// Load DataBrowserChangeAttributes() dynamically on tiger (and better). 6707// This way the code works on 10.2 and 10.3 as well (it doesn't have the 6708// blue highlights in the list view on these systems, though. Oh well.) 6709 6710 6711#import <mach-o/dyld.h> 6712 6713enum { kMyDataBrowserAttributeListViewAlternatingRowColors = (1 << 1) }; 6714 6715 static OSStatus 6716myDataBrowserChangeAttributes(ControlRef inDataBrowser, 6717 OptionBits inAttributesToSet, 6718 OptionBits inAttributesToClear) 6719{ 6720 long osVersion; 6721 char *symbolName; 6722 NSSymbol symbol = NULL; 6723 OSStatus (*dataBrowserChangeAttributes)(ControlRef inDataBrowser, 6724 OptionBits inAttributesToSet, OptionBits inAttributesToClear); 6725 6726 Gestalt(gestaltSystemVersion, &osVersion); 6727 if (osVersion < 0x1040) // only supported for 10.4 (and up) 6728 return noErr; 6729 6730 // C name mangling... 6731 symbolName = "_DataBrowserChangeAttributes"; 6732 if (!NSIsSymbolNameDefined(symbolName) 6733 || (symbol = NSLookupAndBindSymbol(symbolName)) == NULL) 6734 return noErr; 6735 6736 dataBrowserChangeAttributes = NSAddressOfSymbol(symbol); 6737 if (dataBrowserChangeAttributes == NULL) 6738 return noErr; // well... 6739 return dataBrowserChangeAttributes(inDataBrowser, 6740 inAttributesToSet, inAttributesToClear); 6741} 6742 6743 static void 6744initialise_tabline(void) 6745{ 6746 Rect drawerRect = { 0, 0, 0, DRAWER_SIZE }; 6747 DataBrowserCallbacks dbCallbacks; 6748 EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart}; 6749 EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged}; 6750 DataBrowserListViewColumnDesc colDesc; 6751 6752 // drawers have to have compositing enabled 6753 CreateNewWindow(kDrawerWindowClass, 6754 kWindowStandardHandlerAttribute 6755 | kWindowCompositingAttribute 6756 | kWindowResizableAttribute 6757 | kWindowLiveResizeAttribute, 6758 &drawerRect, &drawer); 6759 6760 SetThemeWindowBackground(drawer, kThemeBrushDrawerBackground, true); 6761 SetDrawerParent(drawer, gui.VimWindow); 6762 SetDrawerOffsets(drawer, kWindowOffsetUnchanged, DRAWER_INSET); 6763 6764 6765 // create list view embedded in drawer 6766 CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView, 6767 &dataBrowser); 6768 6769 dbCallbacks.version = kDataBrowserLatestCallbacks; 6770 InitDataBrowserCallbacks(&dbCallbacks); 6771 dbCallbacks.u.v1.itemDataCallback = 6772 NewDataBrowserItemDataUPP(dbItemDataCallback); 6773 dbCallbacks.u.v1.itemNotificationCallback = 6774 NewDataBrowserItemNotificationUPP(dbItemNotificationCallback); 6775 dbCallbacks.u.v1.getContextualMenuCallback = 6776 NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback); 6777 dbCallbacks.u.v1.selectContextualMenuCallback = 6778 NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback); 6779 6780 SetDataBrowserCallbacks(dataBrowser, &dbCallbacks); 6781 6782 SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); // no header 6783 SetDataBrowserHasScrollBars(dataBrowser, false, true); // only vertical 6784 SetDataBrowserSelectionFlags(dataBrowser, 6785 kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet); 6786 SetDataBrowserTableViewHiliteStyle(dataBrowser, 6787 kDataBrowserTableViewFillHilite); 6788 Boolean b = false; 6789 SetControlData(dataBrowser, kControlEntireControl, 6790 kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b); 6791 6792 // enable blue background in data browser (this is only in 10.4 and vim 6793 // has to support older osx versions as well, so we have to load this 6794 // function dynamically) 6795 myDataBrowserChangeAttributes(dataBrowser, 6796 kMyDataBrowserAttributeListViewAlternatingRowColors, 0); 6797 6798 // install callback that keeps focus in vim and away from the data browser 6799 InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent, 6800 NULL, NULL); 6801 6802 // install callback that keeps data browser at the size of the drawer 6803 InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent, 6804 NULL, NULL); 6805 6806 // add "tabs" column to data browser 6807 colDesc.propertyDesc.propertyID = kTabsColumn; 6808 colDesc.propertyDesc.propertyType = kDataBrowserTextType; 6809 6810 // add if items can be selected (?): kDataBrowserListViewSelectionColumn 6811 colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags; 6812 6813 colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc; 6814 colDesc.headerBtnDesc.minimumWidth = 100; 6815 colDesc.headerBtnDesc.maximumWidth = 150; 6816 colDesc.headerBtnDesc.titleOffset = 0; 6817 colDesc.headerBtnDesc.titleString = CFSTR("Tabs"); 6818 colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing; 6819 colDesc.headerBtnDesc.btnFontStyle.flags = 0; // use default font 6820 colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly; 6821 6822 AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0); 6823 6824 // create tabline popup menu required by vim docs (see :he tabline-menu) 6825 CreateNewMenu(kTabContextMenuId, 0, &contextMenu); 6826 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0, 6827 TABLINE_MENU_CLOSE, NULL); 6828 AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0, 6829 TABLINE_MENU_NEW, NULL); 6830 AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0, 6831 TABLINE_MENU_OPEN, NULL); 6832} 6833 6834 6835/* 6836 * Show or hide the tabline. 6837 */ 6838 void 6839gui_mch_show_tabline(int showit) 6840{ 6841 if (showit == 0) 6842 CloseDrawer(drawer, true); 6843 else 6844 OpenDrawer(drawer, kWindowEdgeRight, true); 6845} 6846 6847/* 6848 * Return TRUE when tabline is displayed. 6849 */ 6850 int 6851gui_mch_showing_tabline(void) 6852{ 6853 WindowDrawerState state = GetDrawerState(drawer); 6854 6855 return state == kWindowDrawerOpen || state == kWindowDrawerOpening; 6856} 6857 6858/* 6859 * Update the labels of the tabline. 6860 */ 6861 void 6862gui_mch_update_tabline(void) 6863{ 6864 tabpage_T *tp; 6865 int numTabs = getTabCount(); 6866 int nr = 1; 6867 int curtabidx = 1; 6868 6869 // adjust data browser 6870 if (tabLabels != NULL) 6871 { 6872 int i; 6873 6874 for (i = 0; i < tabLabelsSize; ++i) 6875 CFRelease(tabLabels[i]); 6876 free(tabLabels); 6877 } 6878 tabLabels = (CFStringRef *)malloc(numTabs * sizeof(CFStringRef)); 6879 tabLabelsSize = numTabs; 6880 6881 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) 6882 { 6883 if (tp == curtab) 6884 curtabidx = nr; 6885 tabLabels[nr-1] = getTabLabel(tp); 6886 } 6887 6888 RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL, 6889 kDataBrowserItemNoProperty); 6890 // data browser uses ids 1, 2, 3, ... numTabs per default, so we 6891 // can pass NULL for the id array 6892 AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL, 6893 kDataBrowserItemNoProperty); 6894 6895 DataBrowserItemID item = curtabidx; 6896 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign); 6897} 6898 6899/* 6900 * Set the current tab to "nr". First tab is 1. 6901 */ 6902 void 6903gui_mch_set_curtab(nr) 6904 int nr; 6905{ 6906 DataBrowserItemID item = nr; 6907 SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign); 6908 6909 // TODO: call something like this?: (or restore scroll position, or...) 6910 RevealDataBrowserItem(dataBrowser, item, kTabsColumn, 6911 kDataBrowserRevealOnly); 6912} 6913 6914#endif // FEAT_GUI_TABLINE 6915