在MATLAB for Mac中启用选项键快捷键

时间:2011-03-11 15:40:44

标签: java swing matlab keyboard-shortcuts

自R2009b起,MATLAB通过其Keyboard Shortcuts Preferences获得了非常出色的键盘快捷键。这非常适合在Mac上使用命令和控件自定义快捷方式。

不幸的是,那些键绑定似乎无法覆盖MATLAB的内置字符映射。例如,如果我将option-f定义为cursor-next-word(la emacs),则它接受绑定。按下组合键可以正确地将光标移动到下一个单词,但它还会打印ƒ字符!我相信这是来自角色地图(也许与输入地图相对?)。 EditorMacroKeyBindings都无法覆盖此行为。

我偶然发现了this answer from a tangentially related question,这给了我希望。简而言之,他定义了一个Java类,它可以处理键盘事件并用其他键击输入替换它们。但是,该解决方案仅适用于Windows上的规定。要在Mac上运行,需要进行以下修改:

我需要更改密钥代码以重新映射到字符串中“按下”,如下所示:

map = {
    '$' '^'
    '#' char(181)  % might be useful for text formatting
};

为:

map = {
    'alt pressed F' '^'
    'alt pressed B' char(181)  % might be useful for text formatting
};

不幸的是,在运行代码后,按下选项-f会产生cursor-next-wordƒ字符,就像之前一样。但是,如果我从首选项中禁用cursor-next-word绑定,那么我 ƒ^!实际上,即使我使用像pressed F之类的简单操作,KeyReplacementAction也不会替换操作,而是扩充它。看起来这种行为对于OS X上的MATLAB来说是独一无二的。

好像我只是没有覆盖正确的键盘映射。我已经尝试过挖掘Java运行时,但我对事件调度模型不太熟悉,知道接下来要去哪里看。也许是Java的OS级关键映射中的东西?


编辑:我已经做了更多的挖掘工作。似乎Mac版的MATLAB没有正确地遵守keyEvent的'consume'属性。我可以将KeyReplacementAction附加到inputMapkeymap,在这两种情况下,我都会增加键绑定而不是替换它。我使用反射'取消保护'AWTEvents的consume()方法,但效果和以前一样。

在堆栈跟踪之后,似乎keyEvent正在落入javax.swing.KeyboardManager的实例。看起来我应该能够取消绑定KeyboardManager中的键击,但我无法弄清楚如何从我的MATLAB句柄访问该实例。也许更熟悉Swing事件模型和Java调试器的人可能会更进一步。


编辑2 flolo的回答促使我调查X11的键盘图。几点说明:

  • Matlab似乎不尊重~/.Xmodmap或任何当前加载的模式映射。
  • 如果$XKEYSYMDB环境变量在启动时存在,它将使用它。否则,它会从$MATLAB/X11/app-defaults/XKeysymDB
  • 加载它
  • 整个$MATLAB/X11/app-defaults/目录看起来非常有趣;也许有些hackery可以使这个工作?
  • 哪里 Mac上的X11键盘图? MATLAB如何切换到国际键盘布局?

编辑3 :嗯,我认为X11是红鲱鱼。 lsof -c MATLAB表示正在访问/System/Library/Keyboard Layouts/AppleKeyboardLayouts.bundle。现在正在努力......


编辑4 :MATLAB确实使用系统键盘布局。我创建了一个没有任何绑定的R.M.建议。这似乎有效 - MATLAB确实表现得很好。不幸的是,它也破坏了我在所有其他程序中的自定义Cocoa键绑定。关闭,但没有雪茄。 (实际上,足够接近,RM赢得了+500赏金,原因是我认为它已经奏效了......直到我试图撰写我的祝贺评论并发现我无法像往常一样浏览文本字段。)

3 个答案:

答案 0 :(得分:3)

我终于有机会在假期进一步追求这一点。我发现了一个解决方案。请注意,这是在运行时动态更改Matlab GUI使用的Java类;它完全不受支持,可能非常脆弱。它适用于OS X Lion上我的Matlab版本(r2011a)。

以下是我对Swing / Matlab如何处理按键事件的了解:

  1. 按下按键。
  2. 搜索活动文本组件的inputMap以查看是否存在该键击的绑定。
    • 如果有一个绑定到该击键的操作,则调度该操作的actionPerformed方法
    • 如果有与该击键相关联的字符串,请从文本组件的actionMap中找到该操作,然后发送该操作的actionPerformed方法
  3. 无论是什么,作为最后一步,都会调度在文本组件Keymap.getDefaultAction()中找到的操作。这就是问题所在。
  4. 此解决方案使用包装器覆盖Keymap的默认操作,该包装器只检查是否按下了任何修改键。如果是,那么行动就会被忽略。


    步骤1:在Java中创建自定义TextAction以忽略修饰键

    import javax.swing.text.TextAction;
    import javax.swing.Action;
    import java.awt.event.ActionEvent;
    
    public class IgnoreModifiedKeystrokesAction extends TextAction
    {
        private static final int ignoredModifiersMask = 
            ActionEvent.CTRL_MASK | ActionEvent.ALT_MASK;
        private Action original;
    
        public IgnoreModifiedKeystrokesAction(Action act)
        {
            super((String)act.getValue("Name"));
            original = act;
        }
    
        public void actionPerformed(ActionEvent e)
        {
            if ((e.getModifiers() & ignoredModifiersMask) == 0) {
                /* Only dispatch the original action if no modifiers were used */
                original.actionPerformed(e);
            }
        }
    
        public Action getOriginalAction()
        {
            return original;
        }
    }
    

    编译为.jar

    javac IgnoreModifiedKeystrokesAction.java && jar cvf IgnoreModifiedKeystrokesAction.jar IgnoreModifiedKeystrokesAction.class
    

    步骤2:在命令窗口和编辑器中(在MATLAB中)覆盖MATLAB的默认Keymap处理程序

    这里最难的部分是获取命令窗口和编辑器的java句柄。它取决于各个编辑器窗格的布局和类名。这可能会在Matlab版本之间发生变化。

    javaaddpath('/path/to/IgnoreModifiedKeystrokesAction.jar')
    cmdwin = getCommandWindow();
    editor = getEditor();
    
    for t = [cmdwin,editor]
        defaultAction = t.getKeymap().getDefaultAction();
        if ~strcmp(defaultAction.class(),'IgnoreModifiedKeystrokesAction')
            newAction = IgnoreModifiedKeystrokesAction(defaultAction);
            t.getKeymap().setDefaultAction(newAction);
        end
    end
    
    %% Subfunctions to retrieve handles to the java text pane elements
    function cw = getCommandWindow()
        try
            cw = handle(com.mathworks.mde.desk.MLDesktop.getInstance.getClient('Command Window').getComponent(0).getComponent(0).getComponent(0),'CallbackProperties');
            assert(strcmp(cw.class(),'javahandle_withcallbacks.com.mathworks.mde.cmdwin.XCmdWndView'));
        catch %#ok<CTCH>
            cw_client = com.mathworks.mde.desk.MLDesktop.getInstance.getClient('Command Window');
            cw = searchChildComponentsForClass(cw_client,'com.mathworks.mde.cmdwin.XCmdWndView');
        end
        if isempty(cw)
            error('Unable to find the Command Window');
        end
    end
    
    function ed = getEditor()
        try
            ed = handle(com.mathworks.mde.desk.MLDesktop.getInstance.getGroupContainer('Editor').getComponent(1).getComponent(0).getComponent(0).getComponent(0).getComponent(1).getComponent(0).getComponent(0).getComponent(0).getComponent(0).getComponent(1).getComponent(0).getComponent(0),'CallbackProperties');
            assert(strcmp(ed.class(),'javahandle_withcallbacks.com.mathworks.mde.editor.EditorSyntaxTextPane'));
        catch %#ok<CTCH>
            ed_group = com.mathworks.mde.desk.MLDesktop.getInstance.getGroupContainer('Editor');
            ed = searchChildComponentsForClass(ed_group,'com.mathworks.mde.editor.EditorSyntaxTextPane');
            % TODO: When in split pane mode, there are two editor panes. Do I need
            % to change actionMaps/inputMaps/Keymaps on both panes?
        end
        if isempty(ed)
            error('Unable to find the Editor Window');
        end
    end
    
    function obj = searchChildComponentsForClass(parent,classname)
        % Search Java parent object for a child component with the specified classname
        obj = [];
        if ~ismethod(parent,'getComponentCount') || ~ismethod(parent,'getComponent')
            return
        end
        for i=0:parent.getComponentCount()-1
            child = parent.getComponent(i);
            if strcmp(child.class(),classname)
                obj = child;
            else
                obj = searchChildComponentsForClass(child,classname);
            end
            if ~isempty(obj)
                obj = handle(obj,'CallbackProperties');
                break
            end
        end
    end
    

    现在可以在标准首选项窗口中定义使用选项键的键绑定!


    步骤3(可选):删除自定义操作

    cmdwin = getCommandWindow();
    editor = getEditor();
    
    for t = [cmdwin,editor]
        defaultAction = t.getKeymap().getDefaultAction();
        if strcmp(defaultAction.class(),'IgnoreModifiedKeystrokesAction')
            oldAction = defaultAction.getOriginalAction();
            t.getKeymap().setDefaultAction(oldAction);
        end
    end
    javarmpath('/path/to/IgnoreModifiedKeystrokesAction.jar')
    

答案 1 :(得分:2)

我没有完整的解决方案,但如果你还没有看过,我有一些可以尝试的方法。

MATLAB的键盘缩略图保存在XML的{​​{1}}文件中,其中/Users/$user/.matlab/$version/$name_keybindings.xml是您的用户名,$user是MATLAB版本,$version是无论你把关键字绑定保存为什么。它看起来像这样

$name

我尝试将<?xml version="1.0" encoding="utf-8"?> <CustomKeySet derivedfrom="Mac" modifieddefault="false"> <Context id="Global"> <Action id="eval-file"> <Stroke alt="on" code="VK_ENTER" meta="on" sysctrl="on"/> </Action> <stuff here /stuff> </Context> </CustomKeySet> 值更改为derivedfrom,看看会发生什么。正如预期的那样,没有一个快捷方式有效,但EmptyBaseSet - Opt仍然会复制一个unicode角色。这似乎表明character或任何基于选项的快捷方式行为都归功于Mac,并且不在MATLAB手中。这是Windows相当于按下Opt-f + numpad键的unicode字符。如果MATLAB知道选项快捷方式,它会表明Alt中可能存在冲突,但事实并非如此。我不太了解MATLAB>Preferences>Keyboard>Shortcuts是否可以通过编辑此文件来禁用XML = Opt-f

我的猜测是Apple非常有可能确保应用程序不会对此功能进行修补,因为一些非英语(德语/斯拉夫语/拉丁语)语言经常使用这些键盘快捷键

另一种选择是尝试Ukelele,它是用于mac的键盘布局编辑器。有关于S.O.的问题。和相关网站,他们使用Ukelele remap dead keysanother example for dead keysconfigure the left & right Cmd differentlyswapping € and $等。您可以尝试重新定义键盘布局以禁用{{1} (除非你需要在MATLAB之外的特定字符),这应该可以解决问题。

最后,当问题是“我该怎么做?”时,我并不是说“你应该做别的事情”,但总的来说,对于mac,我发现了映射{{1}对于windows / emacs ƒ快捷方式比Opt-f更容易。大多数情况下,出于同样的原因,也因为Ctrl非常接近我笔记本电脑上的Alt键,当我不是故意的时候,我的胖手指最后按Opt (它从来没有发生过相反的情况)。

答案 2 :(得分:1)

请求一个丑陋的解决方法:我没有在MacOS上工作,所以我进入了“可能是一个好的提示”的领域:在X下存在可以捕获X事件并按需执行命令的工具,快速google给出了那个,例如xkbevd也存在于MacOS下(我假设您提到的java工具做了类似的事情)。在那里你可以捕获ctrl-f并用ctrl-f退格替换它,这样你就可以否定附加字符。 (非常难看,它在matlab之外打破了那些代码)。

如果您不想更改命令行上的行为,而只是在编辑器中使用外部编辑器,则可以使用另一个选项:您可以在首选项中定义另一个,与默认值不同。在那里你可以选择emacs,vi或任何适合你的地方(以及重新映射的地方)

BTW:在Linux下这个问题与matlab不存在,所以它看起来真的是MacOS特有的。