【问题标题】:Enabling option-key shortcuts in MATLAB for Mac在 MATLAB for Mac 中启用选项键快捷键
【发布时间】:2011-03-11 15:40:43
【问题描述】:

自 R2009b 以来,MATLAB 已通过其Keyboard Shortcuts Preferences 提供了惊人的可自定义键盘快捷键。这对于在 Mac 上使用命令和控制自定义快捷方式非常有效。

不幸的是,这些键绑定似乎无法覆盖 MATLAB 的内置字符映射。例如,如果我将 option-f 定义为 cursor-next-word (a 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
};

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

似乎我只是没有覆盖正确的键盘映射。我尝试过深入研究 Java 运行时,但我对事件分派模型不够熟悉,不知道下一步该往哪里看。也许在 Java 的操作系统级键盘映射中有些东西?


编辑:我已经做了更多的挖掘工作。似乎 Mac 版本的 MATLAB 没有正确尊重 keyEvent 的“消费”属性。我可以将 KeyReplacementAction 附加到 inputMapkeymap,在这两种情况下我都增加了键绑定而不是替换它。我使用反射对 AWTEvents 的 consume() 方法进行了“取消保护”,但效果与之前相同。

在堆栈跟踪之后,似乎 keyEvent 正在下降到 javax.swing.KeyboardManager 的实例。看起来我应该能够在 KeyboardManager 中取消绑定击键,但我不知道如何从我拥有的 MATLAB 句柄访问实例。也许对 Swing 的事件模型和 Java 调试器更熟悉的人可以走得更远。


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

  • Matlab 似乎不尊重 ~/.Xmodmap 或任何当前加载的 modmap。
  • 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 的赏金,因为我短暂地认为它已经奏效了……直到我尝试撰写我的祝贺评论并发现我无法像往常一样浏览文本字段。)

【问题讨论】:

  • 我已向 MathWorks 提交了“增强请求”。不过,我仍然希望看到一个未记录的解决方案。
  • 我会更加重视这个问题。 :)
  • 您是否更接近解决您的问题?我想知道,因为我也有一个 mac。
  • @d'o-o'b:不,自上周末以来,我一直没有机会在这方面工作太多。如果我发现解决方案,我一定会更新它。我仍然在这里抱有希望;看到这种赏金萎靡不振肯定是一种耻辱……
  • @d'o-o'b:您应该查看this bounty FAQ,以更加熟悉奖励赏金的工作原理。特别是,没有办法让你的赏金回来。如果您将其授予您自己的答案,那么您将一无所获。赏金就这样消失了。如果您不授予赏金,则在赏金期结束时,一半的赏金将自动授予在赏金开始后给出的最高投票答案至少 +​​2 分(这将是目前 R.M.s 的答案)。

标签: java swing matlab keyboard-shortcuts


【解决方案1】:

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

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

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

此解决方案使用包装器覆盖 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 的默认 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')

【讨论】:

  • 这令人印象深刻——从来没有想过这是一个如此痛苦的地方。我通常只是忍受我的不便:P。我已经开始了 500 分的赏金,以 1)奖励您所做的工作(尽管这是您自己的问题,而不是我的问题)和 2)还给您 +500,这是您“意外”给我的,因为您跳过了当我的解决方案 似乎 开始工作时,枪。我认为是你的赏金让我从 1.5kish 提高到 2k+(在当时,这似乎是代表的一大飞跃)。我想我现在有代表可以帮助您达到 3k 的下一个重要水平 =) 享受,干得好!
  • 哇!谢谢@尤达!这不是一个痛点,因为它是一个在我脑海中嗡嗡作响的悬而未决的问题。我在旅行时有空闲时间来研究它,并且更好地学习了 Swing/Matlab 事件模型。 :)
【解决方案2】:

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

MATLAB 的键盘快捷方式保存在 /Users/$user/.matlab/$version/$name_keybindings.xml 中的 XML 文件中,其中 $user 是您的用户名,$version 是 MATLAB 的版本,$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-character 仍然复制了一个 unicode 字符。这似乎表明Opt-f 或任何基于选项的快捷方式行为是由于 Mac 造成的,并且不在 MATLAB 的控制之下。它相当于按Alt+numpad 键输入 unicode 字符的 Windows 功能。如果 MATLAB 知道选项快捷方式,则表明 MATLAB&gt;Preferences&gt;Keyboard&gt;Shortcuts 中可能存在冲突,但事实并非如此。我不知道XML 是否可以通过编辑此文件来禁用Opt-f=ƒ

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

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

最后,当问题是“我该怎么做?”时,我并不是说“你应该做其他事情”,但总的来说,对于 mac,我发现映射 Ctrl 到windows/emacs Alt 快捷方式比Opt 更容易。大多数情况下,出于同样的原因,也因为Opt 非常接近我笔记本电脑上的Cmd 键,以至于我的胖手指最终会在我无意按下Cmd 时(它永远不会发生另一个方式)。

【讨论】:

  • 感谢您的意见,R.M.据我所见,需要覆盖的键盘映射是 Swing 的(也许是通过 Apple LAF?)。我认为。但我不是 Java/Swing 编码器。然而,在 Cocoa 中,Apple 确实允许这种行为非常动态——这正是这个问题的根源。我已经在 all Cocoa 应用程序中使用 Cocoa Keybindings 启用了 emacs 样式的键绑定。 Matlab 是个奇怪的人。因此,不幸的是,你的最后两点不会为我解决这个问题。再次感谢您的宝贵时间。
  • 啊,粗鲁。由于我在第一条评论中引用的原因,我最初驳回了您更改 OS X 键盘布局的答案。尽管如此,我还是试了一下,认为它奏效了。它确实阻止了 MATLAB 打印不需要的字符。但是,它也解除了我在所有 OS X 程序中的 move-word-forward 宏的绑定。由于缓存问题,我认为这是短暂的解决方案。享受+500! :) 但我仍在寻找答案。 :(
  • @Matt:谢谢:D!!这仍然是一个有趣的问题,我认为结合我给出的两个建议可能会奏效。我建议创建一个没有Opt-f 的布局,但不要将其设置为默认值。而是尝试调整 MATLAB 的 XML 文件以从这个新布局导入基本布局。我不知道该怎么做,但是看看我发布的示例文件内容,它确实从某个地方派生了它的设置。让我知道它是否有效。
  • 我在假期里拼凑了一个解决方案。详情见我的回答。
【解决方案3】:

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

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

顺便说一句:在 Linux 下,matlab 不存在这个问题,所以看起来它确实是 MacOS 特有的。

【讨论】:

  • xkbevd 是一个有趣的想法——MATLAB 将是我使用的唯一 X11 应用程序……如果它实际上仍然使用 X11。他们肯定习惯了,但最近的版本在没有 X11 应用程序的情况下打开。我不知道他们还使用多少机器。也就是说,理论上可以拦截例如 option-f 并将其替换为 option-control-f ,这可以在 MATLAB 中正确绑定。但是,伙计,关于 xkbevd 的文档(超出其简短的联机帮助页)基本上不存在。不过,很好的领导。我看看能不能把它带到任何地方。
猜你喜欢
  • 2010-10-04
  • 1970-01-01
  • 1970-01-01
  • 2015-12-03
  • 2020-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多