如何在 Eclipse3/Eclipse4 混合编辑器 (DIEditorPart) 中填充 undo/redo?

How to populate undo/redo in an Eclipse3/Eclipse4 hybrid editor (DIEditorPart)?

我的应用程序是混合的 E3/E4 因为我想使用 Eclipse 的一部分 Workbench,但也让我的东西(在某种程度上)为以后纯基于 E4 的应用程序做好准备。

在这个应用程序中,我有一个使用自定义编辑器输入的编辑器(不是基于文件,而是基于从数据库中提取的数据;当用户保存更改时,它们将被写回数据库)。我的本地内存中数据表示由 EMF / Xcore 管理,编辑器使用手动设计的 GUI,该 GUI 通过 EMF 编辑数据绑定工作,即我使用的是 EditingDomain(即基本的 AdapterFactoryEditingDomainBasicCommandStack) 跟踪所有更改。

为了将我的 E4 编辑器插入 E3 编辑器,我使用了兼容层,这里特别是 DIEditorPart

虽然到目前为止一切正常,但我还没有能够 undo/redo 工作。

我的代码看起来像这样(我正在使用 Scala 和 Scala-IDE):

E3编辑桥:

final class CustomEditorPartE3Bridge extends DIEditorPart(classOf[CustomEditorPart])

和 "real" 部分:

final class CustomEditorPart {

  @Inject private var _ctx: IEclipseContext = _
  private var _view: Option[MyCustomEditorView] = None
  @Inject private var _dirty: MDirtyable = _
  @Inject @Optional private var _dirtyE3: IDirtyProviderService = _
  private var doPersist: () => Unit = () => {}

  @PostConstruct
  def init(input: IEditorInput): Unit = input match {
    case i: MyCustomEditorInput => initPart(i)
    case _ =>
      throw new IllegalStateException("Required a %s but got a %s".
        format(classOf[MyCustomEditorInput].getName, input.getClass.getName))
  }

  private def initPart(input: MyCustomEditorInput): Unit = {
    val cc = _ctx.createChild()
    // Now we need an adapter factory and a respective editing domain
    // to enable Undo and Redo
    val adapterFactory = new ModelAdapterFactory // generated with Xcore
    val cs = new BasicCommandStack
    val domain = new AdapterFactoryEditingDomain(adapterFactory, cs)
    // We need the editing domain in the control for Databinding
    cc.set(classOf[EditingDomain], domain)
    // Now we setup the view
    _view = Some(ContextInjectionFactory.make(classOf[MyCustomEditorView], cc))
    // And we handle dirtying of our part
    object csl extends CommandStackListener {
      def commandStackChanged(eo: EventObject): Unit = {
        val dirty = cs.isSaveNeeded()
        if (_dirtyE3 == null) {
          _dirty.setDirty(dirty)
        } else {
          Display.getDefault.asyncExec(() => _dirtyE3.setDirtyState(dirty))
        }
      }
    }
   cs.addCommandStackListener(csl)
    // Finally, we setup our saving routine.
    doPersist = () => { /* not relevant here */ }
  }

  @Focus
  def setFocus(): Unit = _view.foreach(_.setFocus)

  @PersistState
  def persistState(): Unit = {}

  @Persist
  def commit(): Unit = doPersist()
}

那么,我如何贡献 undo/redo 以便 E3 的 undo/redo 机制启动?我是否必须以某种方式将我的 EditingDomain 传播回我的 E3 桥并设置一些操作栏贡献者,或者我可以注入一些东西来为我设置 undo/redo 吗?

我将此作为答案发布,以便将其标记为解决方案。

在摆弄和搜索 EHandlerService 用法的各种示例后,我想出了这个:

当我使用 DIEditorPart 时,我可以注入一个 IWorkbenchPart。如果稍后在基于纯 E4 的应用程序中使用该部件,这可能不存在,我将其作为 @Optional 注入并稍后测试 null

所以除了E4方式,我还有以下方式:

  @Inject @Optional private var _workbenchPart: IWorkbenchPart = _

  // In my method
  val cc: IEclipseContext = ...
  val domain: EditingDomain = ...
  cc.set(classOf[EditingDomain], domain)
    if (_workbenchPart != null) {
      val undoAction = ContextInjectionFactory.make(classOf[UndoAction], cc)
      val redoAction = ContextInjectionFactory.make(classOf[RedoAction], cc)
      val site = _workbenchPart.getSite.asInstanceOf[{
        def getActionBars(): org.eclipse.ui.IActionBars
      }]
      val targetActionBars = site.getActionBars
      if (targetActionBars != null) {
        targetActionBars.setGlobalActionHandler(ActionFactory.UNDO.getId, undoAction)
        targetActionBars.setGlobalActionHandler(ActionFactory.REDO.getId, redoAction)
      }
    }

我的 UndoActionRedoAction 看起来像这样:

abstract class UndoRedoAction(canExecute: CommandStack => Boolean,
                              execute: CommandStack => Unit) extends Action {

  @Inject private var _domain: EditingDomain = _

  @PostConstruct
  private def init(): Unit = {
    setEnabled(canExecute(_domain.getCommandStack))
    object csl extends CommandStackListener {
      def commandStackChanged(eo: EventObject): Unit = setEnabled(canExecute(_domain.getCommandStack))
    }
    _domain.getCommandStack.addCommandStackListener(csl)
  }

  override final def run(): Unit = execute(_domain.getCommandStack)
}

final class UndoAction extends UndoRedoAction(_.canUndo, _.undo)
final class RedoAction extends UndoRedoAction(_.canRedo, _.redo)

这有点乱七八糟,但它确实有效。