Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2025 Eclipse Platform and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eclipse Platform - initial API and implementation
******************************************************************************/

package org.eclipse.e4.core.commands;

import org.eclipse.e4.core.contexts.IEclipseContext;

/**
* A marker interface for handlers that have enabledWhen expressions that need to be
* evaluated before @CanExecute.
*
* @since 1.1
* @noimplement This interface is not intended to be implemented by clients outside of the
* workbench.
*/
public interface IHandlerWithExpression {
/**
* Evaluates the enabledWhen expression if present.
*
* @param context the eclipse context for evaluation
* @return true if no expression is defined or if the expression evaluates to true
*/
boolean evaluateEnabledWhen(IEclipseContext context);

/**
* Returns the actual handler object that should be used for execution.
*
* @return the handler object
*/
Object getHandler();
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.core.commands.State;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.e4.core.commands.ExpressionContext;
import org.eclipse.e4.core.commands.IHandlerWithExpression;
import org.eclipse.e4.core.commands.internal.HandlerServiceImpl.ExecutionContexts;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
Expand Down Expand Up @@ -64,8 +65,20 @@ public boolean isEnabled() {
setBaseEnabled(false);
return super.isEnabled();
}

// Check for enabledWhen expression first (takes precedence over @CanExecute)
Object actualHandler = handler;
if (handler instanceof IHandlerWithExpression) {
IHandlerWithExpression handlerWithExpr = (IHandlerWithExpression) handler;
if (!handlerWithExpr.evaluateEnabledWhen(executionContext)) {
setBaseEnabled(false);
return super.isEnabled();
}
actualHandler = handlerWithExpr.getHandler();
}

IEclipseContext staticContext = contexts.staticContext; // getStaticContext(contexts);
Boolean result = (Boolean) ContextInjectionFactory.invoke(handler, CanExecute.class,
Boolean result = (Boolean) ContextInjectionFactory.invoke(actualHandler, CanExecute.class,
executionContext, staticContext, Boolean.TRUE);
setBaseEnabled(result.booleanValue());
return super.isEnabled();
Expand All @@ -83,12 +96,19 @@ public void setEnabled(Object evaluationContext) {
if (handler == null) {
return;
}

// Unwrap handler if needed
Object actualHandler = handler;
if (handler instanceof IHandlerWithExpression) {
actualHandler = ((IHandlerWithExpression) handler).getHandler();
}

IEclipseContext staticContext = getStaticContext(executionContext);
if (staticContext == null) {
staticContext = EclipseContextFactory.create();
createContext = true;
}
ContextInjectionFactory.invoke(handler, SetEnabled.class, executionContext, staticContext,
ContextInjectionFactory.invoke(actualHandler, SetEnabled.class, executionContext, staticContext,
Boolean.TRUE);
if (createContext) {
staticContext.dispose();
Expand Down Expand Up @@ -132,10 +152,17 @@ public boolean isHandled() {
if (contexts != null) {
Object handler = HandlerServiceImpl.lookUpHandler(contexts.context, commandId);
switchHandler(handler);
if (handler instanceof IHandler) {
return ((IHandler) handler).isHandled();

// Unwrap handler if needed
Object actualHandler = handler;
if (handler instanceof IHandlerWithExpression) {
actualHandler = ((IHandlerWithExpression) handler).getHandler();
}
return handler != null;

if (actualHandler instanceof IHandler) {
return ((IHandler) actualHandler).isHandled();
}
return actualHandler != null;

}
return false;
Expand All @@ -154,6 +181,13 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
if (handler == null) {
return null;
}

// Unwrap handler if needed
Object actualHandler = handler;
if (handler instanceof IHandlerWithExpression) {
actualHandler = ((IHandlerWithExpression) handler).getHandler();
}

IEclipseContext staticContext = getStaticContext(executionContext);
IEclipseContext localStaticContext = null;
try {
Expand All @@ -162,11 +196,11 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
.create(HandlerServiceImpl.TMP_STATIC_CONTEXT);
staticContext.set(HandlerServiceImpl.PARM_MAP, event.getParameters());
}
Object result = ContextInjectionFactory.invoke(handler, Execute.class,
Object result = ContextInjectionFactory.invoke(actualHandler, Execute.class,
executionContext,
staticContext, missingExecute);
if (result == missingExecute) {
throw new ExecutionException(handler.getClass().getName() + HANDLER_MISSING_EXECUTE_ANNOTATION,
throw new ExecutionException(actualHandler.getClass().getName() + HANDLER_MISSING_EXECUTE_ANNOTATION,
new NotHandledException(getClass().getName()));
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@
<details key="documentation" value="&lt;p>&#xD;&#xA;This is a reference to the Command for which this is an execution candidate.&#xD;&#xA;&lt;/p>"/>
</eAnnotations>
</eStructuralFeatures>
<eStructuralFeatures xsi:type="ecore:EReference" name="enabledWhen" eType="#//ui/Expression"
containment="true">
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
<details key="documentation" value="&lt;p>&#xD;&#xA;An optional core expression to control handler enablement. When specified, this takes precedence over the @CanExecute annotation.&#xD;&#xA;&lt;/p>&#xD;&#xA;@since 1.4"/>
</eAnnotations>
</eStructuralFeatures>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="HandlerContainer" abstract="true"
interface="true">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package org.eclipse.e4.ui.model.application.commands;

import org.eclipse.e4.ui.model.application.MContribution;
import org.eclipse.e4.ui.model.application.ui.MExpression;

/**
* <!-- begin-user-doc -->
Expand All @@ -34,6 +35,7 @@
* </p>
* <ul>
* <li>{@link org.eclipse.e4.ui.model.application.commands.MHandler#getCommand <em>Command</em>}</li>
* <li>{@link org.eclipse.e4.ui.model.application.commands.MHandler#getEnabledWhen <em>Enabled When</em>}</li>
* </ul>
*
* @model
Expand Down Expand Up @@ -66,4 +68,32 @@ public interface MHandler extends MContribution {
*/
void setCommand(MCommand value);

/**
* Returns the value of the '<em><b>Enabled When</b></em>' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* <!-- begin-model-doc -->
* <p>
* An optional core expression to control handler enablement. When specified, this takes precedence over the @CanExecute annotation.
* </p>
* @since 1.4
* <!-- end-model-doc -->
* @return the value of the '<em>Enabled When</em>' containment reference.
* @see #setEnabledWhen(MExpression)
* @model containment="true"
* @generated
*/
MExpression getEnabledWhen();

/**
* Sets the value of the '{@link org.eclipse.e4.ui.model.application.commands.MHandler#getEnabledWhen <em>Enabled When</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @param value the new value of the '<em>Enabled When</em>' containment reference.
* @see #getEnabledWhen()
* @since 1.4
* @generated
*/
void setEnabledWhen(MExpression value);

} // MHandler
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,16 @@ public class CommandsPackageImpl extends EPackageImpl {
*/
public static final int HANDLER__COMMAND = ApplicationPackageImpl.CONTRIBUTION_FEATURE_COUNT + 0;

/**
* The feature id for the '<em><b>Enabled When</b></em>' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @since 1.4
* @generated
* @ordered
*/
public static final int HANDLER__ENABLED_WHEN = ApplicationPackageImpl.CONTRIBUTION_FEATURE_COUNT + 1;

/**
* The number of structural features of the '<em>Handler</em>' class.
* <!-- begin-user-doc -->
Expand All @@ -775,7 +785,7 @@ public class CommandsPackageImpl extends EPackageImpl {
* @generated
* @ordered
*/
public static final int HANDLER_FEATURE_COUNT = ApplicationPackageImpl.CONTRIBUTION_FEATURE_COUNT + 1;
public static final int HANDLER_FEATURE_COUNT = ApplicationPackageImpl.CONTRIBUTION_FEATURE_COUNT + 2;

/**
* The number of operations of the '<em>Handler</em>' class.
Expand Down Expand Up @@ -1801,6 +1811,20 @@ public EReference getHandler_Command() {
return (EReference) handlerEClass.getEStructuralFeatures().get(0);
}

/**
* Returns the meta object for the containment reference '{@link org.eclipse.e4.ui.model.application.commands.MHandler#getEnabledWhen <em>Enabled When</em>}'.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @return the meta object for the containment reference '<em>Enabled When</em>'.
* @see org.eclipse.e4.ui.model.application.commands.MHandler#getEnabledWhen()
* @see #getHandler()
* @since 1.4
* @generated
*/
public EReference getHandler_EnabledWhen() {
return (EReference) handlerEClass.getEStructuralFeatures().get(1);
}

/**
* Returns the meta object for class '{@link org.eclipse.e4.ui.model.application.commands.MHandlerContainer <em>Handler Container</em>}'.
* <!-- begin-user-doc -->
Expand Down Expand Up @@ -2097,6 +2121,7 @@ public void createPackageContents() {

handlerEClass = createEClass(HANDLER);
createEReference(handlerEClass, HANDLER__COMMAND);
createEReference(handlerEClass, HANDLER__ENABLED_WHEN);

handlerContainerEClass = createEClass(HANDLER_CONTAINER);
createEReference(handlerContainerEClass, HANDLER_CONTAINER__HANDLERS);
Expand Down Expand Up @@ -2244,6 +2269,9 @@ public void initializePackageContents() {
initEReference(getHandler_Command(), this.getCommand(), null, "command", null, 1, 1, MHandler.class, //$NON-NLS-1$
!IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, !IS_COMPOSITE, IS_RESOLVE_PROXIES, !IS_UNSETTABLE,
IS_UNIQUE, !IS_DERIVED, IS_ORDERED);
initEReference(getHandler_EnabledWhen(), theUiPackageImpl.getExpression(), null, "enabledWhen", null, 0, 1, //$NON-NLS-1$
MHandler.class, !IS_TRANSIENT, !IS_VOLATILE, IS_CHANGEABLE, IS_COMPOSITE, !IS_RESOLVE_PROXIES,
!IS_UNSETTABLE, IS_UNIQUE, !IS_DERIVED, IS_ORDERED);

initEClass(handlerContainerEClass, MHandlerContainer.class, "HandlerContainer", IS_ABSTRACT, IS_INTERFACE, //$NON-NLS-1$
IS_GENERATED_INSTANCE_CLASS);
Expand Down Expand Up @@ -2566,6 +2594,15 @@ public interface Literals {
*/
public static final EReference HANDLER__COMMAND = eINSTANCE.getHandler_Command();

/**
* The meta object literal for the '<em><b>Enabled When</b></em>' containment reference feature.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @since 1.4
* @generated
*/
public static final EReference HANDLER__ENABLED_WHEN = eINSTANCE.getHandler_EnabledWhen();

/**
* The meta object literal for the '{@link org.eclipse.e4.ui.model.application.commands.MHandlerContainer <em>Handler Container</em>}' class.
* <!-- begin-user-doc -->
Expand Down
Loading
Loading