DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Frameworks
  4. Key Bindings in Eclipse Editors

Key Bindings in Eclipse Editors

David Green user avatar by
David Green
·
Oct. 28, 09 · Interview
Like (0)
Save
Tweet
Share
8.03K Views

Join the DZone community and get the full member experience.

Join For Free

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/

Eclipse

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Choose the Right Streaming Database
  • Assessment of Scalability Constraints (and Solutions)
  • Orchestration Pattern: Managing Distributed Transactions
  • What Is Advertised Kafka Address?

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: