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

Mat*_* B. 14 java matlab swing keyboard-shortcuts

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

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

从一个与相关的问题中偶然发现了这个问题.简而言之,他定义了一个Java类,它可以处理键盘事件并用其他键击输入替换它们.但是,该解决方案仅适用于Windows上的规定.要在Mac上运行,需要进行以下修改:

我需要将密钥代码更改为重新映射以在字符串中"按下",如下所示:

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

至:

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

不幸的是,在运行代码之后,按下选项-f yield cursor-next-wordƒ字符,就像之前一样.但是,如果我cursor-next-word从首选项中禁用绑定,那么我得到两个 ƒ^!实际上,即使我使用类似的简单动作pressed F,KeyReplacementAction也不会取代动作,而是增加动作.看起来这种行为对OS X上的MATLAB来说是独一无二的.

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


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

在堆栈跟踪之后,看起来好像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确实使用系统键盘布局.我创建了一个没有任何绑定为RM建议.这似乎有效 - MATLAB确实表现得很好.不幸的是,它也破坏了我在所有其他程序中的自定义Cocoa键绑定.关闭,但没有雪茄.(实际上,足够接近,RM因为一直认为它已经奏效而赢得了+500赏金......直到我试图撰写我的祝贺评论并发现我无法照常浏览文本字段.)

Mat*_* B. 3

我终于有机会在假期里进一步追求这个目标。我找到了一个解决方案。请注意,这是在运行时动态更改 Matlab GUI 使用的 Java 类;它完全没有支撑,并且可能非常脆弱。它适用于我的 OS X Lion 版本的 Matlab (r2011a)。

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

  1. 按下按键。
  2. 搜索活动文本组件inputMap以查看是否存在该击键的绑定。
    • 如果有一个操作绑定到该击键,则分派该操作的actionPerformed方法
    • 如果存在与该击键关联的字符串,请从文本组件的 中查找操作actionMap,然后分派该操作的actionPerformed方法
  3. 无论如何,作为最后一步,调度在文本组件的Keymap.getDefaultAction(). 问题就出在这里。

此解决方案使用包装器覆盖键盘映射的默认操作,该包装器仅检查是否按下了任何修饰键。如果是的话,该操作将被默默地忽略。


第 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编译为.jar

javac IgnoreModifiedKeystrokesAction.java && jar cvf IgnoreModifiedKeystrokesAction.jar IgnoreModifiedKeystrokesAction.class
Run Code Online (Sandbox Code Playgroud)

步骤 2:在命令窗口和编辑器(在 MATLAB 内部)中覆盖 MATLAB 的默认键盘映射处理程序

这里最难的部分是获取命令窗口和编辑器的 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
Run Code Online (Sandbox Code Playgroud)

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


步骤 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')
Run Code Online (Sandbox Code Playgroud)