自己扩展CNF之后,导航栏的删除、复制、黏贴等快捷键失效了,在网上搜索了半天,结果最终不如自己看源码。

本篇文章的主要目的不止于解决快捷键失效,更在于如何处理类似的问题,如何利用debug快速定位。这种解决问题的思路能帮助我们更加理解他人的代码,快速熟悉一套陌生的框架。

1、理解问题的本质,为什么按键不生效?

在解决快捷键失效问题之前,我们需要理解快捷键是如何生效的。

为了更直观的理解,请使用debug模式打开一个新的Eclipse IDE,然后,对org.eclipse.jdt.internal.ui.refactoring.reorg.CopyToClipboardAction这个类的run方法加上断点。在新的EclipseIDE的Package Explorer上选中一个节点,按CTRL+C

在DEBUG视图中显示如下图所示:

我们过一眼这些类名和方法名,如果你是个有经验的程序员,那么,你应当能敏感的发现WorkbenchKeyboard就是是你需要的那个类。

我们看看executeCommand方法,关键代码标记上了红色。

    final boolean executeCommand(final Binding binding, final Event trigger)
throws CommandException {
final ParameterizedCommand parameterizedCommand = binding
.getParameterizedCommand();
if (DEBUG) {
Tracing.printTrace("KEYS", //$NON-NLS-1$
"WorkbenchKeyboard.executeCommand(commandId = '" //$NON-NLS-1$
+ parameterizedCommand.getId() + "', parameters = " //$NON-NLS-1$
+ parameterizedCommand.getParameterMap() + ')');
} // Reset the key binding state (close window, clear status line, etc.)
resetState(false); // Dispatch to the handler.
final IHandlerService handlerService = (IHandlerService) workbench
.getService(IHandlerService.class);
final Command command = parameterizedCommand.getCommand();
final boolean commandDefined = command.isDefined();
final boolean commandHandled = command.isHandled();
command.setEnabled(handlerService.getCurrentState());
final boolean commandEnabled = command.isEnabled(); if (DEBUG && DEBUG_VERBOSE) {
if (!commandDefined) {
Tracing.printTrace("KEYS", " not defined"); //$NON-NLS-1$ //$NON-NLS-2$
} else if (!commandHandled) {
Tracing.printTrace("KEYS", " not handled"); //$NON-NLS-1$ //$NON-NLS-2$
} else if (!commandEnabled) {
Tracing.printTrace("KEYS", " not enabled"); //$NON-NLS-1$ //$NON-NLS-2$
}
} try {
handlerService.executeCommand(parameterizedCommand, trigger);
} catch (final NotDefinedException e) {
// The command is not defined. Forwarded to the IExecutionListener.
} catch (final NotEnabledException e) {
// The command is not enabled. Forwarded to the IExecutionListener.
} catch (final NotHandledException e) {
// There is no handler. Forwarded to the IExecutionListener.
} /*
* Now that the command has executed (and had the opportunity to use the
* remembered state of the dialog), it is safe to delete that
* information.
*/
if (keyAssistDialog != null) {
keyAssistDialog.clearRememberedState();
} return (commandDefined && commandHandled);
}

我们发现了binding这个对象。binding的注释如下:

org.eclipse.jface.bindings.Binding

A binding is a link between user input and the triggering of a particular command. The most common example of a binding is a keyboard shortcut, but there are also mouse and gesture bindings. 

看第一段,就能明白,binding是用来做按键和command绑定的,command是最终的执行。

在debug模式下把鼠标放在binding对象上按F2,如下图所示:

相信你已经看到了关键部分,ActionHandler(CopyToClipAction);

是的,只要这里出现了ActionHandler和你的事件,就说明,绑定成功了。反之,则没有。

2、如何建立绑定关系?

通过上面的debug追溯过程,我们发现绑定服务源自org.eclipse.ui.internal.keys.WorkbenchKeyboard.isPartialMatch方法。代码如下:

    /**
* Determines whether the key sequence partially matches on of the active
* key bindings.
*
* @param keySequence
* The key sequence to check for a partial match; must never be
* <code>null</code>.
* @return <code>true</code> if there is a partial match;
* <code>false</code> otherwise.
*/
private boolean isPartialMatch(KeySequence keySequence) {
return getBindingService().isPartialMatch(keySequence);
}

getBindingService()方法返回一个IBindingService对象,注释如下,关键部分红色粗体字标注:

Provides services related to the binding architecture (e.g., keyboard shortcuts) within the workbench. This service can be used to access the currently active bindings, as well as the current state of the binding architecture. 

This service can be acquired from your service locator: 

     IBindingService service = (IBindingService) getSite().getService(IBindingService.class);

This service is available globally. 

getSite()指的是workbenchPart.getSite(),返回的是org.eclipse.ui.IWorkbenchPartSite对象。它是视图、编辑器(工作区部件)和工作区的原生接口,用于它们之间的交互协同。

你可以在任何地方得到它。

所以,你只需要使用它来注册你需要绑定的command,即可。

常用command譬如"org.eclipse.ui.edit.delete","org.eclipse.ui.edit.copy"

3、更加推荐的方式

很多人为导航器提供了ActionProvider,在默认情况下复制、删除、黏贴等action都会被正确的触发。

如果不行,则需要理解以下几个对象:

a、org.eclipse.jface.action.Action.getActionDefinitionId()

用于匹配commandId,该ID如果和注册了快捷键的commandId匹配,该action的run会被调用。

b、org.eclipse.ui.internal.handlers.IActionCommandMappingService

如果你使用了TextActionHandler之类的工具,你会发现上述内容失效了。

这是因为TextActionHandler对CopyAction等做了多一层封装,这时,你需要使用该类,对actionId和commandId多一次处理。

用于actionId和commandId的绑定,可以通过getSite().getService(IActionCommandMappingService.class)获取

我们可以看以下代码,org.eclipse.ui.SubActionBars.setGlobalActionHandler(String actionID, IAction handler),关键部分已标注红色:

    public void setGlobalActionHandler(String actionID, IAction handler) {
if (actionID == null) {
/*
* Bug 124061. It used to be invalid to pass null as an action id,
* but some people still did it. Handle this case by trapping the
* exception and logging it.
*/
WorkbenchPlugin
.log("Cannot set the global action handler for a null action id"); //$NON-NLS-1$
return;
} if (handler instanceof CommandLegacyActionWrapper) {
// this is a registration of a fake action for an already
// registered handler
WorkbenchPlugin
.log("Cannot feed a CommandLegacyActionWrapper back into the system"); //$NON-NLS-1$
return;
} if (handler instanceof CommandAction) {
// we unfortunately had to allow these out into the wild, but they
// still must not feed back into the system
return;
} if (handler != null) {
// Update the action handlers.
if (actionHandlers == null) {
actionHandlers = new HashMap(11);
}
actionHandlers.put(actionID, handler); // Add a mapping from this action id to the command id.
if (serviceLocator != null) {
String commandId = null;
final IActionCommandMappingService mappingService = (IActionCommandMappingService) serviceLocator
.getService(IActionCommandMappingService.class);
if (mappingService != null) {
commandId = mappingService.getCommandId(actionID);
}
if (commandId == null) {
commandId = handler.getActionDefinitionId();
} // Update the handler activations.
final IHandlerService service = (IHandlerService) serviceLocator
.getService(IHandlerService.class);
Map activationsByActionId = null;
if (activationsByActionIdByServiceLocator == null) {
activationsByActionIdByServiceLocator = new WeakHashMap();
activationsByActionId = new HashMap();
activationsByActionIdByServiceLocator.put(serviceLocator,
activationsByActionId);
} else {
activationsByActionId = (Map) activationsByActionIdByServiceLocator
.get(serviceLocator);
if (activationsByActionId == null) {
activationsByActionId = new HashMap();
activationsByActionIdByServiceLocator.put(
serviceLocator, activationsByActionId);
} else if (activationsByActionId.containsKey(actionID)) {
final Object value = activationsByActionId
.remove(actionID);
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
actionIdByCommandId.remove(activation.getCommandId());
if (service != null) {
service.deactivateHandler(activation);
}
activation.getHandler().dispose();
}
} else if (commandId != null
&& actionIdByCommandId.containsKey(commandId)) {
final Object value = activationsByActionId
.remove(actionIdByCommandId.remove(commandId));
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
if (service != null) {
service.deactivateHandler(activation);
}
activation.getHandler().dispose();
}
}
} if (commandId != null) {
actionIdByCommandId.put(commandId, actionID);
// Register this as a handler with the given definition id.
// the expression gives the setGlobalActionHandler() a
// priority.
final IHandler actionHandler = new ActionHandler(handler);
Expression handlerExpression = EXPRESSION;
//XXX add new API in next release to avoid down-casting (bug 137091)
if (this instanceof EditorActionBars) {
handlerExpression = ((EditorActionBars)this).getHandlerExpression();
}
if (service != null) {
final IHandlerActivation activation = service
.activateHandler(commandId, actionHandler,
handlerExpression);
activationsByActionId.put(actionID, activation);
}
}
} } else {
if (actionHandlers != null) {
actionHandlers.remove(actionID);
} // Remove the handler activation.
if (serviceLocator != null) {
final IHandlerService service = (IHandlerService) serviceLocator
.getService(IHandlerService.class);
if (activationsByActionIdByServiceLocator != null) {
final Map activationsByActionId = (Map) activationsByActionIdByServiceLocator
.get(serviceLocator);
if ((activationsByActionId != null)
&& (activationsByActionId.containsKey(actionID))) {
final Object value = activationsByActionId
.remove(actionID);
if (value instanceof IHandlerActivation) {
final IHandlerActivation activation = (IHandlerActivation) value;
actionIdByCommandId.remove(activation.getCommandId());
service.deactivateHandler(activation);
activation.getHandler().dispose();
}
}
}
}
}
actionHandlersChanged = true;
}

可以看到,commandId的来源,一是从IActionCommandMappingService,再是从action的getActionDefinitionId()。

如果都不行,再是其他处理(暂不表)。

该方式代码示例如下:

IActionCommandMappingService acms = (IActionCommandMappingService) getViewSite()
.getWorkbenchWindow().getService(
IActionCommandMappingService.class);
String deleteId = acms.getCommandId("delete");
if (deleteId == null)
acms.map("delete", "org.eclipse.ui.edit.delete");

RCP:解决Navigator快捷键不生效的问题的更多相关文章

  1. 解决eclipse快捷键Ctrl+Alt+Down冲突问题办法

    解决eclipse快捷键Ctrl+Alt+Down冲突问题办法 时间:2016-01-18 21:11:08      阅读:376      评论:0      收藏:0      [点我收藏+] ...

  2. linux下jdk环境变量配置深度分析----解决环境变量不生效的问题

    1.linux下jdk环境变量配置 是否需要配置环境变量,主要看java -version 显示的版本是否为你期望的版本 1.1 不需要配置环境变量的情况 使用java -version查看,版本显示 ...

  3. 解决IDEA快捷键 Alt+Insert 失效的问题

    现象 IDEA快捷键 Alt+Inser 失效,单击右键也不出现[Generate]. 这个问题经常出现在重新安装IDEA后. 原因 缺少2个插件 解决办法 在setting中启用这2个插件即可.这2 ...

  4. 【ssh信任关系】解决信任关系不生效问题

    配置的时候遇见点问题,发现即便将id_rsa.pub拷贝到了另一台机器上,信任也没有建立起来. 原因是另外一台机器上目录权限不对,可以通过su root后观察/var/log/message里的日志信 ...

  5. 解决Eclipse快捷键被其他软件占用

    做为一个java攻城狮,eclipse是我最常用的攻城设备,eclipse快捷键 极大的提高了我的开发效率!!!! 前段时间升级了一下我的战斗装备——给电脑的系统盘换成了一个固态硬盘,因此需要重装系统 ...

  6. 19.3.20 解决pycharm快捷键无法使用问题和熟悉git与码云操作流程

    problem:由于Vim插件导致快捷键无法使用: answer:settings→Plugins→搜索到ideaVim→取消选中→apply→重启pycharm: git:创建仓库→生成公钥(ssh ...

  7. Javascript复制内容到剪贴板,解决navigator.clipboard Cannot read property 'writeText' of undefined

    起因 最近帮同事实现了一个小功能--复制文本到剪贴板,主要参考了前端大神阮一峰的博客,根据 navigator.clipboard 返回的 Clipboard 对象的方法 writeText() 写文 ...

  8. php5.6解决curl扩展不生效的问题

    最近在本机安装PHP环境,遇到一个奇粑问题,本地安装的php5.2.php5.3.php5.4都需要做常规设置,即可正常使用.安装php5.5.php5.6时php_curl按各种方法进行配制,都无法 ...

  9. 解决sublime快捷键回车换行问题

    鼠标右键sublime 以管理员身份运行 打开首选项里面的按键绑定用户 将下面的代码粘贴复制 { "keys": ["enter"], "comman ...

随机推荐

  1. coredump

    COREDUMP调试的使用 一,什么是coredump 跑程序的时候经常碰到SIGNAL 或者 call trace的问题,需要定位解决,这里说的大部分是指对应程序由于各种异常或者bug导致在运行过程 ...

  2. <停车卫> 产品需求说明书 version 2.0

    <停车卫> 产品需求说明书 文档版本号: Version 2.0 文档编号: xxxx 文档密级: 归属部门/项目: 产品名: 停车卫 子系统名: 编写人: kina 编写日期: 2015 ...

  3. .net 控件

    DevComponents.DotNetBar11.8.0.0:百度网盘里 DevExpress13.2.8:百度网盘里

  4. iOS开发-automaticallyAdjustsScrollViewInsets属性

    iOS开发-automaticallyAdjustsScrollViewInsets属性 Available in iOS 7.0 and later. 简单点说就是automaticallyAdju ...

  5. PBX 评测二

    //由于诸事繁多,结果评测一拖再拖. 博客园的优化还行啊,PBX220搜索, 第一页,第四个就是上一篇的评测文章. 配置没有什么说的(按照说明/还有这篇无线迷你IPPBX-PBX220). 以下是在公 ...

  6. c# GridView有关RowClick事件,可单击显示选中的row

    //当前选定行 int i = this.gridView1.FocusedRowHandle; //选中行,列名为name的值 gridView1.GetRowCellDisplayText(i, ...

  7. IT小喇叭-企业品牌宣传、产品营销推广的首选

    IT小喇叭-企业品牌宣传.产品营销推广的首选 IT小喇叭,成立于2015年6月初,成都芮嘉科技有限公司旗下产品,主要进行媒体资源整合.宣传报道:使移动互联网等相关企业的产品宣传.品牌营销变得更加方便. ...

  8. 浅谈城市规划在移动GIS方面的应用发展

    1.概述 城市建设进程加快,城市规划管理工作日趋繁重,各种来源的数据产生各种层出不穷的问题,严重影响城市规划时的准确性,为此全面合理的掌握好各方面的城市规划资料才能做出更加科学的决策.移动端的兴起为规 ...

  9. 谁动了我的特征?——sklearn特征转换行为全记录

    目录 1 为什么要记录特征转换行为?2 有哪些特征转换的方式?3 特征转换的组合4 sklearn源码分析 4.1 一对一映射 4.2 一对多映射 4.3 多对多映射5 实践6 总结7 参考资料 1 ...

  10. [UCSD白板题] Sorting: 3-Way Partition

    Problem Introduction The goal in this problem is to redesign a given implementation of the randomize ...