我终于有机会在假期里进一步追求这个目标。我找到了一个解决方案。请注意,这是在运行时动态更改 Matlab GUI 使用的 Java 类;它完全没有支撑,并且可能非常脆弱。它适用于我的 OS X Lion 版本的 Matlab (r2011a)。
以下是我了解到的 Swing/Matlab 如何处理按键事件的知识:
- 按下按键。
- The active text component's
inputMap
is searched to see if there's a binding for that keystroke.
- 如果有一个操作绑定到该击键,则调度该操作的
actionPerformed
method
- 如果存在与该击键关联的字符串,则从文本组件的
actionMap
,然后调度该操作的actionPerformed
method
-
无论作为最后一步,调度在文本组件中找到的操作
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;
}
}
编译为.jar
:
javac IgnoreModifiedKeystrokesAction.java && jar cvf IgnoreModifiedKeystrokesAction.jar IgnoreModifiedKeystrokesAction.class
步骤 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
现在可以在使用选项键的标准首选项窗口中定义键绑定!
步骤 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')