RCP:解决Navigator快捷键不生效的问题

简介: 自己扩展CNF之后,导航栏的删除、复制、黏贴等快捷键失效了,在网上搜索了半天,结果最终不如自己看源码。 本篇文章的主要目的不止于解决快捷键失效,更在于如何处理类似的问题,如何利用debug快速定位。这种解决问题的思路能帮助我们更加理解他人的代码,快速熟悉一套陌生的框架。

自己扩展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. 
View Code

看第一段,就能明白,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");

 

目录
相关文章
|
SQL XML 关系型数据库
Mybatis-Plus通过SQL注入器实现真正的批量插入
Mybatis-Plus通过SQL注入器实现真正的批量插入
6242 0
Mybatis-Plus通过SQL注入器实现真正的批量插入
|
监控 安全 C#
attempt to write a readonly database错误的解决(C#,SQLite)
今天打包WPF程序,安装后总是打不开,查看监控日志原来是SQLite的问题,报错如图     当向SQLite数据库中存入新纪录时总是显示attempt to write a readonly a database。
3546 0
|
10月前
|
SQL 弹性计算 安全
在云上轻松部署达梦数据库
达梦数据库(DM Database)是达梦数据库有限公司开发的关系型数据库管理系统,广泛应用于政府、金融、能源等行业。它具备高性能、高安全、兼容性强、易管理等特点,支持多种操作系统,适用于关键业务系统、政务系统及大数据处理等场景。在阿里云上,可通过一键部署快速使用达梦数据库DM8。
|
11月前
|
关系型数据库 MySQL OLAP
快速入门:搭建你的第一个AnalyticDB实例
【10月更文挑战第25天】在大数据时代,高效的在线分析处理(OLAP)成为企业决策的关键。AnalyticDB是阿里云推出的一款完全托管的实时数据仓库服务,它能够支持PB级的数据量和高并发的查询需求。作为一名数据工程师,我有幸在工作中使用了AnalyticDB,并积累了丰富的实践经验。本文将从个人角度出发,详细介绍如何快速搭建你的第一个AnalyticDB实例,包括创建实例、连接数据库、导入数据和执行简单查询等步骤。
444 0
|
监控 网络协议 JavaScript
【公告】淘宝 npm 域名即将切换 && npmmirror 重构升级
淘宝NPM 镜像站喊你切换新域名啦。新的Web 站点:https://npmmirror.com,Registry Endpoint:https://registry.npmmirror.com。 http://npm.taobao.org 和 http://registry.npm.taobao.org 将在 2022.06.30 号正式下线和停止 DNS 解析。
3176 0
|
存储 分布式计算 NoSQL
大数据编程技术基础实验七:HBase实验——部署HBase
大数据技术基础实验七,.掌握HBase集群安装部署及HBase Shell的一些常用命令的使用。
916 0
大数据编程技术基础实验七:HBase实验——部署HBase
|
前端开发 JavaScript 数据库
vue 使用 async 和 await 实现异步 axios 同步化(实战案例:数据异步校验通过后,再执行保存)
vue 使用 async 和 await 实现异步 axios 同步化(实战案例:数据异步校验通过后,再执行保存)
583 1
|
前端开发 Java API
WebSocket vs SSE: 实时数据推送到前端的选择与实现(详细)
WebSocket vs SSE: 实时数据推送到前端的选择与实现(详细)
2219 0
|
Kubernetes 容器
使用kubeadm部署k8s报错:The kubelet is not running或者level=error msg="Handler for POST /v1.43/images/create returned error: Head \"https://us-west2-dock
使用kubeadm部署k8s报错:The kubelet is not running或者level=error msg="Handler for POST /v1.43/images/create returned error: Head \"https://us-west2-dock
|
JSON Java 数据格式
万字长文讲解调用第三方接口,RestTemplate,urlConnection使用详解,java代码模拟postman发送请求
万字长文讲解调用第三方接口,RestTemplate,urlConnection使用详解,java代码模拟postman发送请求
261 0
万字长文讲解调用第三方接口,RestTemplate,urlConnection使用详解,java代码模拟postman发送请求