在 MAC 版 MATLAB 中启用选项键快捷键

2022-09-04 04:23:51

自 R2009b 以来,MATLAB 通过其键盘快捷键首选项拥有了可自定义的键盘快捷键。这非常适合在Mac上使用命令和控制自定义快捷键。

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

我从一个切题相关的问题中偶然发现了这个答案,这个问题给了我希望。简而言之,他定义了一个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
};

不幸的是,在运行代码后,按option-f产生和字符,就像以前一样。但是,如果我从首选项中禁用绑定,那么我会得到和!事实上,即使我使用像 这样的简单操作,KeyReplacementAction也不会取代该操作,而是增强它。似乎这种行为是OS X上的MATLAB所独有的。cursor-next-wordƒcursor-next-wordƒ^pressed F

似乎我根本没有覆盖正确的键盘映射。我尝试过挖掘Java运行时,但我对事件调度模型不够熟悉,不知道下一步该去哪里看。也许是Java的操作系统级键盘映射中的某些内容?


编辑:从那以后,我又做了一些挖掘。看起来 Mac 版本的 MATLAB 没有正确遵守 keyEvent 的“已消费”属性。我可以将 KeyReplacementAction 附加到 或 ,在这两种情况下,我都会增加键绑定,而不是替换它。我使用反射来“取消保护”AWTEvents的方法,但效果与以前相同。inputMapkeymapconsume()

沿着堆栈跟踪,看起来好像 keyEvent 正在落入 的实例。看起来我应该能够在键盘管理器中取消绑定击键,但我无法弄清楚如何从我拥有的MATLAB句柄访问实例。也许更熟悉 Swing 的事件模型和 Java 调试器的人可能会走得更远。javax.swing.KeyboardManager


编辑2flolo的答案促使我研究X11的键盘映射。几个注意事项:

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

编辑3:嗯,我认为X11是一条红鲱鱼。 显示它正在访问 。现在正在研究这个问题...lsof -c MATLAB/System/Library/Keyboard Layouts/AppleKeyboardLayouts.bundle


编辑4:MATLAB确实使用系统键盘布局。我按照R.M.的建议创建了一个没有任何绑定的。这似乎有效 - MATLAB确实表现得正确。不幸的是,它还破坏了我在所有其他程序中的自定义Cocoa键绑定。靠近,但没有雪茄。(事实上,R.M.赢得了+500奖金,因为一个短暂的想法,它已经奏效了......直到我试图撰写我的祝贺评论,发现我无法像往常一样浏览文本字段。


答案 1

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

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

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

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


步骤 1:在 Java 中创建自定义文本操作以忽略修饰键

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 的默认 Keymap 处理程序(从 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

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


步骤 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')

答案 2

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

MATLAB 的键盘 shorcuts 保存在 的文件中,其中是您的用户名,MATLAB 的版本,并且是您保存键绑定的任何内容。它看起来像这样XML/Users/$user/.matlab/$version/$name_keybindings.xml$user$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>

我尝试将值更改为,以查看会发生什么。正如预期的那样,没有一个快捷方式工作,但是 - 仍然复制了一个unicode字符。这似乎表明或任何基于选项的快捷方式行为都是由于Mac引起的,并且不属于MATLAB的手。它相当于在 Windows 上按 unicode 字符的 +numpad 键。如果 MATLAB 知道选项快捷方式,则表明 中可能存在冲突,但事实并非如此。我不知道是否可以通过编辑此文件来禁用=。derivedfromEmptyBaseSetOptcharacterOpt-fAltMATLAB>Preferences>Keyboard>ShortcutsXMLOpt-fƒ

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

另一种选择是尝试Ukelele,它是Mac的键盘布局编辑器。在S.O.和相关网站上存在一些问题,他们使用Ukelele重新映射死键死键的另一个例子以不同的方式配置左右Cmd交换€和$等。您可以尝试重新定义要禁用的键盘布局(除非在 MATLAB 之外需要该特定字符),这应该可以解决问题。Opt-f

最后,当问题是“我该怎么做?”时,我并不是说“你应该做别的事情”,但总的来说,对于mac,我发现映射到windows / emacs快捷方式比.大多数情况下,出于同样的原因,也因为离笔记本电脑上的钥匙如此之近,以至于我的胖手指最终在我不想这样做的时候按了按(它永远不会以相反的方式发生)。CtrlAltOptOptCmdCmd


推荐