[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[转帖]Key Bindings in Eclipse Editors

上一篇:[整理]RCP开发,笔记整理
下一篇:[整理]JQuery小笔记~~

添加日期:2012/11/23 11:05:26 快速返回   返回列表 阅读3419次
http://eclipse.dzone.com/articles/key-bindings-eclipse-editors

Key Bindings in Eclipse Editors
Submitted by David Green on Wed, 2009/10/28 - 3:02am


For tool writers, the Eclipse platform provides great facilities for binding keys to commands so that specific actions can be invoked with the right key combination. In most cases hooking into these facilities is a simple matter of activating the right context via IContextService and using a few extension points in your plugin.xml. For more complex editors, such as those with multiple Text or StyledText controls, it's not so easy. This article examines the mechanisms that the Eclipse platform uses to determine how commands are handled, and outlines an approach for implementing editors that wish to alter command handling depending on the focus control.

Suppose we are building an editor that has more than one StyledText control and uses IOperationHistory to support undo/redo. We want CTRL+Z and CTRL+Y (Command-Z and Command-Y on a Mac) to cause undo/redo in the editor. In this editor what we want is for these keys to manipulate the IOperationHistory, however when a StyledText has focus in our editor we want to invoke undo/redo on the StyledText. In other words, the user will expect undo/redo to behave differently depending on what they're doing. This is consistent with how undo/redo work throughout the rest of the platform.

Eclipse uses the following abstractions to represent the concept of keys invoking commands:

KeyBinding - binds a sequence of keys to a logical command
Command - an abstract representation for some semantic behaviour
IHandler - an implementation of the behaviour for a specific command
Let's take (for example) CTRL+Z. When key presses are made, they are matched with a KeyBinding. Eclipse knows which Command to invoke based on the KeyBinding (in this case the 'undo' command). Eclipse then determines which IHandler to use by looking up the handler using the logical command in IHandlerService. (There are many layers of indirection in the actual implementation -- this example is simplified for ease of understanding).

In our editor we want the operation history to be associated with undo/redo. We hook it up as follows:


OperationHistoryActionHandler undoAction= new UndoActionHandler(site, moduleEditor.getOperationContext());
PlatformUI.getWorkbench().getHelpSystem().setHelp(undoAction, IAbstractTextEditorHelpContextIds.UNDO_ACTION);
undoAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.UNDO);
undoAction.setId(ITextEditorActionConstants.UNDO);

IHandlerService handlerService = (IHandlerService) site.getService(IHandlerService.class);
handlerService.activateHandler(undoAction.getActionDefinitionId(), new ActionHandler(undoAction));



When we test our editor this works as expected: we can undo changes in the editor by pressing CTRL+Z. After focusing on a StyledText in our editor however, we expect CTRL+Z to undo text that we type. In our editor these text changes aren't in our operation history until after the text control loses focus. So how do we set that up? Here's how:

TextViewer textViewer = // create the text viewer
TextViewerSupport support = new TextViewerSupport(textViewer);


Most of the work is done by this helper class:



protected class TextViewerSupport implements FocusListener, DisposeListener {

 private final TextViewer textViewer;
 private List handlerActivations = new ArrayList();
 

 public TextViewerSupport(TextViewer textViewer) {
  this.textViewer = textViewer;
  StyledText textWidget = textViewer.getTextWidget();
  textWidget.addFocusListener(this);
  textWidget.addDisposeListener(this);

  if (textViewer.getTextWidget().isFocusControl()) {
   activateContext();
  }
 }
 public void focusLost(FocusEvent e) {
  deactivateContext();
 }
 public void focusGained(FocusEvent e) {
  activateContext();
 }
 public void widgetDisposed(DisposeEvent e) {
  deactivateContext();
 }

 protected void activateContext() {
  if (handlerActivations.isEmpty()) {
   activateHandler(ISourceViewer.QUICK_ASSIST,ITextEditorActionDefinitionIds.QUICK_ASSIST);
   activateHandler(ISourceViewer.CONTENTASSIST_PROPOSALS,ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
   activateHandler(ITextOperationTarget.CUT, ITextEditorActionDefinitionIds.CUT);
   activateHandler(ITextOperationTarget.COPY, ITextEditorActionDefinitionIds.COPY);
   activateHandler(ITextOperationTarget.PASTE, ITextEditorActionDefinitionIds.PASTE);
   activateHandler(ITextOperationTarget.DELETE, ITextEditorActionDefinitionIds.DELETE);
   activateHandler(ITextOperationTarget.UNDO, ITextEditorActionDefinitionIds.UNDO);
   activateHandler(ITextOperationTarget.REDO, ITextEditorActionDefinitionIds.REDO);
  }
 }
 

 protected void activateHandler(int operation, String actionDefinitionId) {
  StyledText textWidget = textViewer.getTextWidget();
  IHandler actionHandler = createActionHandler(operation, actionDefinitionId);
  IHandlerActivation handlerActivation = handlerService.activateHandler(actionDefinitionId, actionHandler,new ActiveFocusControlExpression(textWidget));
  
  handlerActivations.add(handlerActivation);
 }

 private IHandler createActionHandler(final int operation, String actionDefinitionId) {
  Action action = new Action() {
   @Override
   public void run() {
    if (textViewer.canDoOperation(operation)) {
     textViewer.doOperation(operation);
    }
   }
  };
  action.setActionDefinitionId(actionDefinitionId);
  return new ActionHandler(action);
 }
 
 protected void deactivateContext() {
  if (!handlerActivations.isEmpty()) {
   for (IHandlerActivation activation: handlerActivations) {
    handlerService.deactivateHandler(activation);
    activation.getHandler().dispose();
   }
   handlerActivations.clear();
  }
 }  
}


Though TextViewerSupport looks complicated, it's really just activating different command handlers whenever the textViewer gains focus.

When the text control has focus the editor now has two handlers for the undo command. So how does Eclipse know which one to use? Digging into the internals of Eclipse reveals that it uses the handler with highest priority. But we don't set a priority no these handlers! Eclipse calculates the priority of handlers automatically based on the enablement expression of the handler. The priority of the enablement expression is based in part on the variables that the expression references. This leads us to the last part of our solution, ActiveFocusControlExpression:


/**
 * An expression that evaluates to true if and only if the current focus control is the one provided.
 * Has a very high priority in order to ensure proper conflict resolution.
 */
public class ActiveFocusControlExpression extends Expression {

 private Control focusControl;

 public ActiveFocusControlExpression(Control control) {
  focusControl = control;
 }

 @Override
 public void collectExpressionInfo(ExpressionInfo info) {
  info.markDefaultVariableAccessed(); // give it a very high priority
  info.addVariableNameAccess(ISources.ACTIVE_SHELL_NAME);
  info.addVariableNameAccess(ISources.ACTIVE_WORKBENCH_WINDOW_NAME);
 }

 @Override
 public EvaluationResult evaluate(IEvaluationContext context)
   throws CoreException {
  if (Display.getCurrent() != null && focusControl.isFocusControl()) {
   return EvaluationResult.TRUE;
  }
  return EvaluationResult.FALSE;
 }
}
In collectExpressionInfo we ensure that the expression indicates that it uses the default variable. This gives the expression a very high priority. Since the expression is only enabled when our control has focus, we've ensured that undo is directed to our text control at the right time.

Credit: The approach described in this article is inspired by a class CommonTextSupport (authored by Steffen Pingel) from the Mylyn project.

From http://greensopinion.blogspot.com/
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved