From 9c7523e9441e4fa1cab31af8de00bd77b7f17d82 Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Mon, 19 May 2008 19:09:05 +0200 Subject: [PATCH] Change ":macmenukey" to ":macmenu" The new command allows better control over menus with ability to specify key equivalent, action, and wheter menu item is an alternate. These are specified as "key=value" pairs to allow future expansion. The help files have also been rewritten to reflect this change. --- runtime/doc/gui_mac.txt | 115 +++++---- runtime/doc/index.txt | 2 +- src/MacVim/MMBackend.h | 3 +- src/MacVim/MMBackend.m | 8 +- src/MacVim/MMVimController.m | 11 +- src/MacVim/gui_macvim.m | 60 +++-- src/MacVim/gvimrc | 120 +++++----- src/ex_cmds.h | 4 +- src/ex_docmd.c | 4 +- src/menu.c | 452 +++++++++++++++++++++++++++-------- src/proto/gui_macvim.pro | 1 + src/proto/menu.pro | 1 + src/structs.h | 8 +- 13 files changed, 535 insertions(+), 254 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index b625e9164e..a2b44c75bf 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -1,4 +1,4 @@ -*gui_mac.txt* For Vim version 7.1. Last change: 2008 Mar 16 +*gui_mac.txt* For Vim version 7.1. Last change: 2008 May 19 VIM REFERENCE MANUAL by Bjorn Winckler @@ -310,50 +310,79 @@ than the default?) then post them to |vim_mac|. ============================================================================== 5. Menus *macvim-menus* - *:macm* *:macmenukey* -MacVim has a special way of binding keys to menu items that differs from other -Vim GUI ports. A menu binding is called a "key equivalent" in Mac OS X -terminology, this is displayed on the right side of a menu item. The -":macmenukey" command is used to set the key equivalent of a menu item. This -command takes two parameters, the first names the menu item to bind to, the -second gives the key combination. For example: > - :macmenukey File.New\ Tab -This sets the key equivalent of the "New Tab" menu item under the "File" menu -to Cmd+t. +Menus in Mac OS X behave slightly different from other platforms. For that +reason two new commands have been added to Vim. To understand what these +commands do you must first understand how menus work on OS X. -Note that key equivalents: - * must contain the Cmd modifier flag () - * take precedence over normal mappings made with ":map" - * can only be modified during startup (e.g. in .gvimrc) +Each entry in a menu is called a "menu item". With each menu item is +associated: a title, a key equivalent and an action message. When a menu is +displayed the title is shown on the left and the key equivalent (if any) is +shown on the right. Key equivalents enable you to access a menu item using +the keyboard instead of having to use the mouse. When a menu item is clicked +it will send it's associated action message. Actions can be used to instruct +MacVim to paste some text (paste:), open a new window (newWindow:), etc. +Certain actions are standard throughout OS X which is why MacVim must be able +to set these for each menu item. (E.g. the menu item "Edit.Paste" must be +bound to the action "paste:" otherwise pasting won't work in dialogs since +that is the action that instructs them to paste something.) -It is possible to reset a key equivalent by calling :macmenukey with a menu -name but no key. This is so that the default key equivalents can be reset in -"~/.gvimrc". For example, if you would like to free up (which is the -key equivalent of "File.Save") then add the following line to "~/.gvimrc": > - macmenukey File.Save -Now you can use :map to bind to whatever you like. +Menus are configured using the |:macmenu| command and the |:macaction| command +can be used to send action messages. -It is not necessary to reset a key equivalent if all you want to do is to -change the key equivalent of a menu item. For example, say you want to use - as the key equivalent for "Next Tab", then add the following line -to "~/.gvimrc": > - macmenukey Window.Next\ Tab -< *:maca* *:macaction* -It is typical for menu items in Cocoa applications to bind to Objective-C -selectors. To support this, MacVim introduces the |:macaction| command. This -command takes the name of an action message as its only parameter. (An action -message is an Objective-C message with "void" return type and a single -parameter of type "id".) For example, the "New Window" menu item on the -"File" menu is created in the following manner: > - :an 10.290 File.New\ Window :macaction newWindow: +:maca[ction] {action:} Send the message "action:" to the first responder. + The list of allowed actions can be seen by typing + :maca + An attempt to send an action not listed here will + result in an error. This list is specified in a + property list file called |Actions.plist|. -Note 1: A menu item which is bound to |:macaction| will automatically be bound -to that action in all modes (as if ":an" was used). It is not possible to -bind to |:macaction| in one mode only. -Note 2: The action is "nil-targeted", which means it is passed down the first -responder chain. + *:macm* *:macmenu* +:mac[menu] {menu} {key}={arg} ... + Set Mac specific properties for {menu}. The + properties that can be set are: + action the action this menu sends + alt "yes" if alternate of previous menu + key the key equivalent of this menu + This command must be used in a startup file, for + example in "~/.gvimrc". It has no effect otherwise. + For convenience, a menu with "action=name:" which is + bound to will act as if bound to + ":maca name:". Thus, if "Menu.Item" is given by + :an Menu.Item + :macm Menu.Item action=name: + then ":emenu Menu.Item" is equivalent to + ":maca name:". + + The key equivalent is specified with the + syntax. Note that key equivalents must contain the + Cmd modifier flag (), and they take precedence + over normal mappings. + Use the syntax "key=" to clear the key equivalent + of a menu. This can be used to free up a key + combination that is set in the system gvimrc so that + it may be mapped to using ":map". + + Recognised values of "alt" are "0", "no", "1", and + "yes". The default is "no". An alternate menu must + have the same key equivalent as the previous menu, + except the modifier flags must differ. The alternate + menu is by default hidden and only shows up when the + modifier is held down. + +Here are some examples on how to use these commands: + +1. Create a menu item with title "New Window" under the "File" menu, with key +equivalent Cmd-n, which opens a new window when selected: > + :an 10.290 File.New\ Window + :macm File.New\ Window action=newWindow: key= +2. Change the key equivalent to cycle through tabs to Cmd-Left/Right: > + :macm Window.Previous\ Tab key= + :macm Window.Next\ Tab key= +3. Create a mapping in normal mode which closes the current tab/window: > + :map :maca performClose: +> *Actions.plist* Some action messages would not be suitable to call from within Vim, so there is a dictionary called "Actions.plist" (in the Resources folder of the @@ -381,14 +410,6 @@ performZoom: Zoom window (same as clicking the green blob) selectNextWindow: Select next window (similar to ) selectPreviousWindow: Select previous window (similar to ) -As an example, to map to performZoom: you could do something like this: > - :map :macaction performZoom: -A better way to map to performZoom: would be to set the key equivalent of the -menu item "Window.Zoom" to the above action. This can be done by adding the -following line to "~/.gvimrc": > - macmenukey Window.Zoom -(Note that key equivalents must contain the 'D' flag.) - ============================================================================== 6. Toolbar *macvim-toolbar* diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index b9b0eab78c..0b0a15664d 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1289,7 +1289,7 @@ The commands are sorted on the non-optional part of their name. |:move| :m[ove] move lines |:mark| :ma[rk] set a mark |:macation| :maca[ction] send action message -|:macmenukey| :macm[eyequiv] set key equivalent for menu item +|:macmenu| :macm[enu] set Mac specific properties for menu item |:make| :mak[e] execute external command 'makeprg' and parse error messages |:map| :map show or enter a mapping diff --git a/src/MacVim/MMBackend.h b/src/MacVim/MMBackend.h index 058e88eebb..df754a66b1 100644 --- a/src/MacVim/MMBackend.h +++ b/src/MacVim/MMBackend.h @@ -89,7 +89,8 @@ - (void)addMenuItemWithTag:(int)tag parent:(int)parentTag name:(char *)name tip:(char *)tip icon:(char *)icon keyEquivalent:(int)key modifiers:(int)mods - action:(NSString *)action atIndex:(int)index; + action:(char *)action isAlternate:(int)isAlt + atIndex:(int)index; - (void)removeMenuItemWithTag:(int)tag; - (void)enableMenuItemWithTag:(int)tag state:(int)enabled; - (void)showPopupMenuWithName:(char *)name atMouseLocation:(BOOL)mouse; diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 0eed0ac662..2bde88c057 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -755,7 +755,8 @@ static NSString *MMSymlinkWarningString = - (void)addMenuItemWithTag:(int)tag parent:(int)parentTag name:(char *)name tip:(char *)tip icon:(char *)icon keyEquivalent:(int)key modifiers:(int)mods - action:(NSString *)action atIndex:(int)index + action:(char *)action isAlternate:(int)isAlt + atIndex:(int)index { //NSLog(@"addMenuItemWithTag:%d parent:%d name:%s tip:%s atIndex:%d", tag, // parentTag, name, tip, index); @@ -764,7 +765,7 @@ static NSString *MMSymlinkWarningString = int tiplen = tip ? strlen(tip) : 0; int iconlen = icon ? strlen(icon) : 0; int eventFlags = vimModMaskToEventModifierFlags(mods); - int actionlen = [action lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + int actionlen = action ? strlen(action) : 0; NSMutableData *data = [NSMutableData data]; key = specialKeyToNSKey(key); @@ -778,10 +779,11 @@ static NSString *MMSymlinkWarningString = [data appendBytes:&iconlen length:sizeof(int)]; if (iconlen > 0) [data appendBytes:icon length:iconlen]; [data appendBytes:&actionlen length:sizeof(int)]; - if (actionlen > 0) [data appendBytes:[action UTF8String] length:actionlen]; + if (actionlen > 0) [data appendBytes:action length:actionlen]; [data appendBytes:&index length:sizeof(int)]; [data appendBytes:&key length:sizeof(int)]; [data appendBytes:&eventFlags length:sizeof(int)]; + [data appendBytes:&isAlt length:sizeof(int)]; [self queueMessage:AddMenuItemMsgID data:data]; } diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index fb5d00aa72..4385ac7d33 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -70,7 +70,8 @@ static NSTimeInterval MMResendInterval = 0.5; - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent title:(NSString *)title tip:(NSString *)tip keyEquivalent:(int)key modifiers:(int)mask - action:(NSString *)action atIndex:(int)idx; + action:(NSString *)action isAlternate:(int)isAlt + atIndex:(int)idx; - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index; - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title toolTip:(NSString *)tip icon:(NSString *)icon; @@ -705,6 +706,7 @@ static NSTimeInterval MMResendInterval = 0.5; if (idx < 0) idx = 0; int key = *((int*)bytes); bytes += sizeof(int); int mask = *((int*)bytes); bytes += sizeof(int); + int isalt = *((int*)bytes); bytes += sizeof(int); NSString *ident = [NSString stringWithFormat:@"%d.%d", (int)self, parentTag]; @@ -715,7 +717,7 @@ static NSTimeInterval MMResendInterval = 0.5; NSMenu *parent = [self menuForTag:parentTag]; [self addMenuItemWithTag:tag parent:parent title:title tip:tip keyEquivalent:key modifiers:mask action:action - atIndex:idx]; + isAlternate:isalt atIndex:idx]; } [title release]; @@ -1086,7 +1088,8 @@ static NSTimeInterval MMResendInterval = 0.5; - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent title:(NSString *)title tip:(NSString *)tip keyEquivalent:(int)key modifiers:(int)mask - action:(NSString *)action atIndex:(int)idx + action:(NSString *)action isAlternate:(int)isAlt + atIndex:(int)idx { if (parent) { NSMenuItem *item = nil; @@ -1117,6 +1120,8 @@ static NSTimeInterval MMResendInterval = 0.5; [item setKeyEquivalent:keyString]; [item setKeyEquivalentModifierMask:mask]; } + + if (isAlt) [item setAlternate:YES]; } } diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index f49c3ac709..701712d835 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -32,7 +32,6 @@ static float MMMaxFontSize = 100.0f; static NSFont *gui_macvim_font_with_name(char_u *name); -static BOOL gui_macvim_is_valid_action(NSString *action); @@ -655,33 +654,16 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx) char_u *tip = menu->strings[MENU_INDEX_TIP] ? menu->strings[MENU_INDEX_TIP] : menu->actext; char_u *map_str = menu->strings[MENU_INDEX_NORMAL]; + char_u *mac_action = menu->mac_action; #ifdef FEAT_MBYTE icon = CONVERT_TO_UTF8(icon); name = CONVERT_TO_UTF8(name); tip = CONVERT_TO_UTF8(tip); map_str = CONVERT_TO_UTF8(map_str); + mac_action = CONVERT_TO_UTF8(mac_action); #endif - // HACK! Check if menu is mapped to ':macaction actionName:'; if so, pass - // the action along so that MacVim can bind the menu item to this action. - // This means that if a menu item maps to an action in normal mode, then - // all other modes will also use the same action. - NSString *action = nil; - if (map_str) { - NSString *mapping = [NSString stringWithCString:(char*)map_str - encoding:NSUTF8StringEncoding]; - NSArray *parts = [mapping componentsSeparatedByString:@" "]; - if ([parts count] >=2 - && [[parts objectAtIndex:0] hasPrefix:@":maca"]) { - action = [parts objectAtIndex:1]; - action = [action stringByTrimmingCharactersInSet: - [NSCharacterSet whitespaceAndNewlineCharacterSet]]; - if (!gui_macvim_is_valid_action(action)) - action = nil; - } - } - [[MMBackend sharedInstance] addMenuItemWithTag:(int)menu parent:(int)menu->parent @@ -690,7 +672,8 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx) icon:(char*)icon keyEquivalent:menu->mac_key modifiers:menu->mac_mods - action:action + action:(char*)mac_action + isAlternate:menu->mac_alternate atIndex:idx]; #ifdef FEAT_MBYTE @@ -698,6 +681,7 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx) CONVERT_TO_UTF8_FREE(name); CONVERT_TO_UTF8_FREE(tip); CONVERT_TO_UTF8_FREE(map_str); + CONVERT_TO_UTF8_FREE(mac_action); #endif } @@ -1203,12 +1187,12 @@ ex_macaction(eap) arg = CONVERT_TO_UTF8(arg); #endif - NSString *name = [NSString stringWithCString:(char*)arg - encoding:NSUTF8StringEncoding]; - if (gui_macvim_is_valid_action(name)) { + NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict]; + NSString *name = [NSString stringWithUTF8String:(char*)arg]; + if (actionDict && [actionDict objectForKey:name] != nil) { [[MMBackend sharedInstance] executeActionWithName:name]; } else { - EMSG2(_("E???: \"%s\" is not a valid action"), eap->arg); + EMSG2(_("E???: Invalid action: %s"), eap->arg); } #ifdef FEAT_MBYTE @@ -1513,13 +1497,6 @@ gui_mch_toggle_tearoffs(int enable) } - static BOOL -gui_macvim_is_valid_action(NSString *action) -{ - NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict]; - return actionDict && [actionDict objectForKey:action] != nil; -} - void gui_mch_enter_fullscreen(int fuoptions_flags, guicolor_T bg) @@ -1890,3 +1867,22 @@ get_macaction_name(expand_T *xp, int idx) return plainStr; } + + int +is_valid_macaction(char_u *action) +{ + int isValid = NO; + NSDictionary *actionDict = [[MMBackend sharedInstance] actionDict]; + if (actionDict) { +#ifdef FEAT_MBYTE + action = CONVERT_TO_UTF8(action); +#endif + NSString *string = [NSString stringWithUTF8String:(char*)action]; + isValid = (nil != [actionDict objectForKey:string]); +#ifdef FEAT_MBYTE + CONVERT_TO_UTF8_FREE(action); +#endif + } + + return isValid; +} diff --git a/src/MacVim/gvimrc b/src/MacVim/gvimrc index 022b806e65..8d03a046f5 100644 --- a/src/MacVim/gvimrc +++ b/src/MacVim/gvimrc @@ -1,7 +1,7 @@ " System gvimrc file for MacVim " " Maintainer: Bjorn Winckler -" Last Change: Sat May 10 2008 +" Last Change: Sun May 18 2008 " " This is a work in progress. If you feel so inclined, please help me improve " this file. @@ -42,14 +42,13 @@ aunmenu File.-SEP4- aunmenu File.Exit aunmenu File.Save-Exit -an 10.290 File.New\ Window :maca newWindow: +an 10.290 File.New\ Window an 10.295 File.New\ Tab :tabnew -an 10.310 File.&Open\.\.\. :maca fileOpen: -an 10.325 File.Open\ Recent :maca recentFilesDummy: +an 10.310 File.&Open\.\.\. +an 10.325 File.Open\ Recent an 10.328 File.-SEP0- an 10.330 File.Close\ Window:qa :confirm qa -an 10.331 File.Close :maca performClose: -"an 10.331 File.Close\ Tab :tabclose +an 10.332 File.Close " Edit menu @@ -58,8 +57,8 @@ aunmenu Edit.Find aunmenu Edit.Find\ and\ Replace an 20.410.10 Edit.Find.Find\.\.\./ / -an 20.410.20 Edit.Find.Find\ Next :maca findNext: -an 20.410.30 Edit.Find.Find\ Previous :maca findPrevious: +an 20.410.20 Edit.Find.Find\ Next +an 20.410.30 Edit.Find.Find\ Previous vmenu 20.410.35 Edit.Find.Use\ Selection\ for\ Find y:let @/=@"n an 20.410.40 Edit.Find.-SEP1- an 20.410.50 Edit.Find.Find\ and\ Replace\.\.\.:%s :%s/ @@ -67,30 +66,30 @@ vunmenu Edit.Find.Find\ and\ Replace\.\.\.:%s vnoremenu Edit.Find.Find\ and\ Replace\.\.\.:s :s/ an 20.460 Edit.-SEP4- -an 20.465.10 Edit.Font.Show\ Fonts :maca orderFrontFontPanel: +an 20.465.10 Edit.Font.Show\ Fonts an 20.465.20 Edit.Font.-SEP5- -an 20.465.30 Edit.Font.Bigger :maca fontSizeUp: -an 20.465.40 Edit.Font.Smaller :maca fontSizeDown: -an 20.470 Edit.Special\ Characters\.\.\. :maca orderFrontCharacterPalette: +an 20.465.30 Edit.Font.Bigger +an 20.465.40 Edit.Font.Smaller +an 20.470 Edit.Special\ Characters\.\.\. " Window menu (should be next to Help so give it a high priority) aunmenu Window -an 9900.300 Window.Minimize :maca performMiniaturize: -an 9900.310 Window.Zoom :maca performZoom: +an 9900.300 Window.Minimize +an 9900.310 Window.Zoom an 9900.320 Window.Toggle\ Full\ Screen\ Mode :set invfullscreen an 9900.330 Window.-SEP1- " TODO! Grey out if no tabs are visible. an 9900.340 Window.Previous\ Tab :tabprevious an 9900.350 Window.Next\ Tab :tabnext an 9900.360 Window.-SEP2- -an 9900.370 Window.Bring\ All\ To\ Front :maca arrangeInFront: +an 9900.370 Window.Bring\ All\ To\ Front " Help menu an 9999.1 Help.MacVim\ Help :h gui_mac -an 9999.2 Help.MacVim\ Website :maca openWebsite: +an 9999.2 Help.MacVim\ Website an 9999.3 Help.-sep0- @@ -189,55 +188,58 @@ endif " exists("macvim_hig_shift_movement") " -" Menu key equivalents (these should always have the 'D' modifier set) +" Set up menu key equivalents (these should always have the 'D' modifier set), +" action bindings, and alternate items. +" +" Note: menu items which should execute an action are bound to ; the +" action message is specified here via the :macmenu command. " -macmenukey File.New\ Window -macmenukey File.New\ Tab +macm File.New\ Window key= action=newWindow: +macm File.New\ Tab key= +macm File.Open\.\.\. key= action=fileOpen: +macm File.Open\ Tab\.\.\. key= +macm File.Open\ Recent action=recentFilesDummy: +macm File.Close\ Window key= +macm File.Close key= action=performClose: +macm File.Save key= +macm File.Save\ As\.\.\. key= +macm File.Print key= -macmenukey File.Open\.\.\. -macmenukey File.Open\ Tab\.\.\. -macmenukey File.Close\ Window -"macmenukey File.Close\ Tab -macmenukey File.Close -macmenukey File.Save -macmenukey File.Save\ As\.\.\. -macmenukey File.Print +macm Edit.Undo key= +macm Edit.Redo key= +macm Edit.Cut key= +macm Edit.Copy key= +macm Edit.Paste key= +macm Edit.Select\ All key= +macm Edit.Find.Find\.\.\. key= +macm Edit.Find.Find\ Next key= action=findNext: +macm Edit.Find.Find\ Previous key= action=findPrevious: +macm Edit.Find.Use\ Selection\ for\ Find key= +macm Edit.Special\ Characters\.\.\. key= +macm Edit.Font.Show\ Fonts action=orderFrontFontPanel: +macm Edit.Font.Bigger key= action=fontSizeUp: +macm Edit.Font.Smaller key= action=fontSizeDown: +macm Edit.Special\ Characters\.\.\. action=orderFrontCharacterPalette: -macmenukey Edit.Undo -macmenukey Edit.Redo -macmenukey Edit.Cut -macmenukey Edit.Copy -macmenukey Edit.Paste -macmenukey Edit.Select\ All -macmenukey Edit.Find.Find\.\.\. -macmenukey Edit.Find.Find\ Next -macmenukey Edit.Find.Find\ Previous -macmenukey Edit.Find.Use\ Selection\ for\ Find -macmenukey Edit.Special\ Characters\.\.\. -macmenukey Edit.Font.Bigger -macmenukey Edit.Font.Smaller +macm Tools.Spelling.To\ Next\ error key= +macm Tools.Spelling.Suggest\ Corrections key= +macm Tools.Make key= +macm Tools.List\ Errors key= +macm Tools.List\ Messages key= +macm Tools.Next\ Error key= +macm Tools.Previous\ Error key= +macm Tools.Older\ List key= +macm Tools.Newer\ List key= -macmenukey Tools.Spelling.To\ Next\ error -macmenukey Tools.Spelling.Suggest\ Corrections -macmenukey Tools.Make -macmenukey Tools.List\ Errors -macmenukey Tools.List\ Messages -macmenukey Tools.Next\ Error -macmenukey Tools.Previous\ Error -macmenukey Tools.Older\ List -macmenukey Tools.Newer\ List - -macmenukey Window.Minimize -macmenukey Window.Zoom -macmenukey Window.Toggle\ Full\ Screen\ Mode -macmenukey Window.Previous\ Tab -macmenukey Window.Next\ Tab - -" TODO: seems to be reserved by the system on Leopard. Disable this key -" equivalent until I can figure out what to do about it. -"macmenukey Help.MacVim\ Help +macm Window.Minimize key= action=performMiniaturize: +macm Window.Zoom key= action=performZoom: +macm Window.Toggle\ Full\ Screen\ Mode key= +macm Window.Previous\ Tab key= +macm Window.Next\ Tab key= +macm Window.Bring\ All\ To\ Front action=arrangeInFront: +macm Help.MacVim\ Website action=openWebsite: " Restore the previous value of 'cpoptions'. let &cpo = s:cpo_save diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 27a98daea5..7da810345d 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -597,8 +597,8 @@ EX(CMD_match, "match", ex_match, RANGE|NOTADR|EXTRA|CMDWIN), EX(CMD_macaction, "macaction", ex_macaction, EXTRA|NOSPC|NEEDARG), -EX(CMD_macmenukey, "macmenukey", ex_macmenukey, - EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), +EX(CMD_macmenu, "macmenu", ex_macmenu, + EXTRA|TRLBAR|CMDWIN), EX(CMD_menu, "menu", ex_menu, RANGE|NOTADR|ZEROR|BANG|EXTRA|TRLBAR|NOTRLCOM|USECTRLV|CMDWIN), EX(CMD_menutranslate, "menutranslate", ex_menutranslate, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index f1aa113ace..0474883777 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -466,7 +466,7 @@ static void ex_folddo __ARGS((exarg_T *eap)); #ifndef FEAT_GUI_MACVIM # define ex_macaction ex_ni -# define ex_macmenukey ex_ni +# define ex_macmenu ex_ni #endif /* @@ -3764,7 +3764,7 @@ set_one_cmd_context(xp, buff) case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_tearoff: case CMD_emenu: #ifdef FEAT_GUI_MACVIM - case CMD_macmenukey: + case CMD_macmenu: #endif return set_context_in_menu_cmd(xp, cmd, arg, forceit); #endif diff --git a/src/menu.c b/src/menu.c index bd4b4f292c..7d04b0c85c 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1038,6 +1038,9 @@ free_menu(menup) # ifdef FEAT_GUI_MOTIF vim_free(menu->xpm_fname); # endif +#ifdef FEAT_GUI_MACVIM + vim_free(menu->mac_action); +#endif #endif for (i = 0; i < MENU_MODES; i++) free_menu_string(menu, i); @@ -2137,6 +2140,9 @@ ex_emenu(eap) char_u *p; int idx; char_u *mode; +#ifdef FEAT_GUI_MACVIM + char_u *old_arg; +#endif saved_name = vim_strsave(eap->arg); if (saved_name == NULL) @@ -2243,8 +2249,10 @@ ex_emenu(eap) idx = MENU_INDEX_NORMAL; } - if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL) + if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL && + menu->strings[idx][0] != NUL) { + /* When executing a script or function execute the commands right now. * Otherwise put them in the typeahead buffer. */ #ifdef FEAT_EVAL @@ -2256,6 +2264,19 @@ ex_emenu(eap) ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, TRUE, menu->silent[idx]); } +#ifdef FEAT_GUI_MACVIM + else if (menu->mac_action != NULL && menu->strings[idx][0] == NUL) + { + /* This allows us to bind a menu to an action without mapping to + * anything so that pressing the menu's key equivalent and typing + * ":emenu ..." does the same thing. (HACK: We count on the fact that + * ex_macaction() only looks at eap->arg.) */ + old_arg = eap->arg; + eap->arg = menu->mac_action; + ex_macaction(eap); + eap->arg = old_arg; + } +#endif /* FEAT_GUI_MACVIM */ else EMSG2(_("E335: Menu not defined for %s mode"), mode); } @@ -2473,32 +2494,145 @@ menutrans_lookup(name, len) #ifdef FEAT_GUI_MACVIM + vimmenu_T * +menu_for_path(char_u *menu_path) +{ + vimmenu_T *menu; + char_u *name; + char_u *saved_name; + char_u *p; + + saved_name = vim_strsave(menu_path); + if (saved_name == NULL) + return NULL; + + menu = root_menu; + name = saved_name; + while (*name) + { + /* Find in the menu hierarchy */ + p = menu_name_skip(name); + + while (menu != NULL) + { + if (menu_name_equal(name, menu)) + { + if (*p == NUL && menu->children != NULL) + { + EMSG(_("E333: Menu path must lead to a menu item")); + menu = NULL; + } + else if (*p != NUL && menu->children == NULL) + { + EMSG(_(e_notsubmenu)); + menu = NULL; + } + break; + } + menu = menu->next; + } + if (menu == NULL || *p == NUL) + break; + menu = menu->children; + name = p; + } + + vim_free(saved_name); + + if (menu == NULL) + { + EMSG2(_("E334: Menu not found: %s"), menu_path); + return NULL; + } + + return menu; +} + +/* + * Handle the ":macmenu" command. + */ void -ex_macmenukey(eap) +ex_macmenu(eap) exarg_T *eap; { - char_u *arg = eap->arg; - char_u *keys, *name, *p; - vimmenu_T *menu; + vimmenu_T *menu = NULL; + char_u *arg; + char_u *menu_path; + char_u *p; + char_u *keys; + int len; +#ifdef FEAT_MULTI_LANG + char_u *tofree = NULL; + char_u *new_cmd; +#endif + char_u *linep; + char_u *key_start; + char_u *key = NULL; + char_u *arg_start = NULL; + int error = FALSE; + char_u *action = NULL; + int mac_key = 0; + int mac_mods = 0; + int mac_alternate = 0; char_u *last_dash; - int modifiers; int bit; - int key; + int set_action = FALSE; + int set_key = FALSE; + int set_alt = FALSE; + + arg = eap->arg; + +#ifdef FEAT_MULTI_LANG + /* + * Translate menu names as specified with ":menutrans" commands. + */ + menu_path = arg; + while (*menu_path) + { + /* find the end of one part and check if it should be translated */ + p = menu_skip_part(menu_path); + keys = menutrans_lookup(menu_path, (int)(p - menu_path)); + if (keys != NULL) + { + /* found a match: replace with the translated part */ + len = (int)STRLEN(keys); + new_cmd = alloc((unsigned)STRLEN(arg) + len + 1); + if (new_cmd == NULL) + break; + mch_memmove(new_cmd, arg, menu_path - arg); + mch_memmove(new_cmd + (menu_path - arg), keys, (size_t)len); + STRCPY(new_cmd + (menu_path - arg) + len, p); + p = new_cmd + (menu_path - arg) + len; + vim_free(tofree); + tofree = new_cmd; + arg = new_cmd; + } + if (*p != '.') + break; + menu_path = p + 1; + } +#endif /* * Isolate the menu name. + * Skip the menu name, and translate into a real TAB. */ - name = arg; - if (*name == '.') + menu_path = arg; + if (*menu_path == '.') { - EMSG2(_(e_invarg2), name); - return; + EMSG2(_(e_invarg2), menu_path); + goto theend; } while (*arg && !vim_iswhite(*arg)) { if ((*arg == '\\' || *arg == Ctrl_V) && arg[1] != NUL) arg++; + else if (STRNICMP(arg, "", 5) == 0) + { + *arg = TAB; + mch_memmove(arg + 1, arg + 5, STRLEN(arg + 4)); + } arg++; } if (*arg != NUL) @@ -2506,107 +2640,223 @@ ex_macmenukey(eap) arg = skipwhite(arg); keys = arg; - // TODO: move to gui_find_menu_item(path_name) - menu = root_menu; - while (*name) - { - /* find the end of one dot-separated name and put a NUL at the dot */ - p = menu_name_skip(name); + menu = menu_for_path(menu_path); + if (!menu) goto theend; - while (menu != NULL) + /* + * Parse all key=value arguments. + */ + arg = NULL; + linep = keys; + while (!ends_excmd(*linep)) + { + key_start = linep; + if (*linep == '=') { - if (STRCMP(name, menu->name) == 0 || STRCMP(name, menu->dname) == 0) - { - if (*p == NUL) - { - if (menu->children != NULL) - menu = NULL; - goto search_end; - } - break; - } - menu = menu->next; - } - if (menu == NULL) /* didn't find it */ + EMSG2(_("E415: unexpected equal sign: %s"), key_start); + error = TRUE; break; + } - /* Found a match, search the sub-menu. */ - menu = menu->children; - name = p; + /* + * Isolate the key ("action", "alt", or "key"). + */ + while (*linep && !vim_iswhite(*linep) && *linep != '=') + ++linep; + vim_free(key); + key = vim_strnsave_up(key_start, (int)(linep - key_start)); + if (key == NULL) + { + error = TRUE; + break; + } + linep = skipwhite(linep); + + /* + * Check for the equal sign. + */ + if (*linep != '=') + { + EMSG2(_("E416: missing equal sign: %s"), key_start); + error = TRUE; + break; + } + ++linep; + + /* + * Isolate the argument. + */ + linep = skipwhite(linep); + arg_start = linep; + linep = skiptowhite(linep); + + if (linep == arg_start) + { + EMSG2(_("E417: missing argument: %s"), key_start); + error = TRUE; + break; + } + vim_free(arg); + arg = vim_strnsave(arg_start, (int)(linep - arg_start)); + if (arg == NULL) + { + error = TRUE; + break; + } + + /* + * Store the argument. + */ + if (STRCMP(key, "ACTION") == 0) + { + action = vim_strsave(arg); + if (action == NULL) + { + error = TRUE; + break; + } + + if (!is_valid_macaction(action)) + { + EMSG2(_("E???: Invalid action: %s"), arg); + error = TRUE; + break; + } + + set_action = TRUE; + } + else if (STRCMP(key, "ALT") == 0) + { + len = (int)STRLEN(arg); + if ( (len == 1 && (*arg == '0' || *arg == '1')) || + (STRICMP("no", arg) == 0 || STRICMP("yes", arg) == 0) ) + { + mac_alternate = (*arg == '1' || STRICMP("yes", arg) == 0); + set_alt = TRUE; + } + else + { + EMSG2(_(e_invarg2), arg); + error = TRUE; + break; + } + } + else if (STRCMP(key, "KEY") == 0) + { + if (arg[0] != '<') + { + EMSG2(_(e_invarg2), arg); + error = TRUE; + break; + } + + if (STRICMP("", arg) == 0) + { + /* This is to let the user unset a key equivalent, thereby + * freeing up that key combination to be used for a :map + * command (which would otherwise not be possible since key + * equivalents take precedence over :map). */ + mac_key = 0; + mac_mods = 0; + set_key = TRUE; + continue; + } + + /* Find end of modifier list */ + last_dash = arg; + for (p = arg + 1; *p == '-' || vim_isIDc(*p); p++) + { + if (*p == '-') + { + last_dash = p; + if (p[1] != NUL && p[2] == '>') + ++p; /* anything accepted, like */ + } + if (p[0] == 't' && p[1] == '_' && p[2] && p[3]) + p += 3; /* skip t_xx, xx may be '-' or '>' */ + } + + if (*p == '>') /* found matching '>' */ + { + /* Which modifiers are given? */ + mac_mods = 0x0; + for (p = arg + 1; p < last_dash; p++) + { + if (*p != '-') + { + bit = name_to_mod_mask(*p); + if (bit == 0x0) + break; /* Illegal modifier name */ + mac_mods |= bit; + } + } + + /* + * Legal modifier name. + */ + if (p >= last_dash) + { + /* + * Modifier with single letter, or special key name. + */ + if (mac_mods != 0 && last_dash[2] == '>') + mac_key = last_dash[1]; + else + { + mac_key = get_special_key_code(last_dash + 1); + mac_key = handle_x_keys(mac_key); + } + } + } + + set_key = (mac_key != 0); + + if (mac_key == 0) + { + EMSG2(_(e_invarg2), arg); + error = TRUE; + break; + } + } + else + { + EMSG2(_(e_invarg2), key_start); + error = TRUE; + break; + } + + /* + * Continue with next argument. + */ + linep = skipwhite(linep); } -search_end: - if (!menu) { - EMSG(_("E337: Menu not found - check menu names")); - return; - } + vim_free(key); + vim_free(arg); - if (keys[0] == '<') +theend: +#ifdef FEAT_MULTI_LANG + vim_free(tofree); +#endif + + /* + * Store all the keys that were set in the menu item. + */ + if (!error) { - key = 0; - - /* Find end of modifier list */ - last_dash = keys; - for (p = keys + 1; *p == '-' || vim_isIDc(*p); p++) - { - if (*p == '-') - { - last_dash = p; - if (p[1] != NUL && p[2] == '>') - ++p; /* anything accepted, like */ - } - if (p[0] == 't' && p[1] == '_' && p[2] && p[3]) - p += 3; /* skip t_xx, xx may be '-' or '>' */ - } - - if (*p == '>') /* found matching '>' */ - { - /* Which modifiers are given? */ - modifiers = 0x0; - for (p = keys + 1; p < last_dash; p++) - { - if (*p != '-') - { - bit = name_to_mod_mask(*p); - if (bit == 0x0) - break; /* Illegal modifier name */ - modifiers |= bit; - } - } - - /* - * Legal modifier name. - */ - if (p >= last_dash) - { - /* - * Modifier with single letter, or special key name. - */ - if (modifiers != 0 && last_dash[2] == '>') - key = last_dash[1]; - else - { - key = get_special_key_code(last_dash + 1); - key = handle_x_keys(key); - } - } - } - - if (key != 0) - { - menu->mac_key = key; - menu->mac_mods = modifiers; - } - } - else if (keys[0] == NUL) - { - /* Clear the key equivalent */ - menu->mac_key = 0; - menu->mac_mods = 0; + if (set_action) + menu->mac_action = action; + if (set_key) + { + menu->mac_key = mac_key; + menu->mac_mods = mac_mods; + } + if (set_alt != -1) + menu->mac_alternate = mac_alternate; } else { - EMSG(_(e_invarg)); + vim_free(action); } } #endif /* FEAT_GUI_MACVIM */ diff --git a/src/proto/gui_macvim.pro b/src/proto/gui_macvim.pro index 52ddaa62fb..b7ecf6cbcf 100644 --- a/src/proto/gui_macvim.pro +++ b/src/proto/gui_macvim.pro @@ -202,3 +202,4 @@ OSErr odb_post_buffer_write(buf_T *buf); void odb_end(void); char_u *get_macaction_name(expand_T *xp, int idx); +int is_valid_macaction(char_u *action); diff --git a/src/proto/menu.pro b/src/proto/menu.pro index 3d85d7562b..3326b5be81 100644 --- a/src/proto/menu.pro +++ b/src/proto/menu.pro @@ -20,6 +20,7 @@ void ex_emenu __ARGS((exarg_T *eap)); vimmenu_T *gui_find_menu __ARGS((char_u *path_name)); void ex_menutranslate __ARGS((exarg_T *eap)); #ifdef FEAT_GUI_MACVIM +void ex_macmenu __ARGS((exarg_T *eap)); void ex_macmenukey __ARGS((exarg_T *eap)); #endif /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index 4244de1e45..ae0a4a94c2 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2254,9 +2254,11 @@ struct VimMenu PtWidget_t *submenu_id; #endif #ifdef FEAT_GUI_MACVIM - int mac_key; /* Key equivalent */ - int mac_mods; /* Modifier flags for the above */ - int was_grey; /* Remember last 'grey' state */ + char_u *mac_action; /* Action this menu sends */ + int mac_key; /* Key equivalent */ + int mac_mods; /* Modifier flags for the above */ + int mac_alternate; /* Item is alternate of previous item */ + int was_grey; /* Remember last 'grey' state */ #endif }; #else