将 SWT.OpenDocument SWT 侦听器添加到 RCP 应用程序的 E4Application

Add SWT.OpenDocument SWT Listener to E4Application of RCP app

我想使用 eclipse 的 launcher.openfile 功能。因此我阅读了一些文档(例如 https://help.eclipse.org/2020-03/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2Fproduct_open_file.htm) 我正确地实现了一个自定义应用程序 class 但是这个 class 的内容丢失了一些我猜的东西,因为在我的 LifeCycle Class 中再也找不到主要的 window,这当我使用标准 E4Application 时通常可以找到。

如何仅添加 SWT 侦听器 SWT.OpenDocument.

来使用 E4Application class 的通用功能

这是我的申请代码:

package de.port.dsntool.ui.services;

import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;

public class MyE4Application implements IApplication{

    //Application with Listener to SWT.OpenDocument
    private Display display = null;

    public Display getApplicationDisplay() {
        if (display == null) {
            display = Display.getDefault();
        }
        return display;
    }

    @Override
    public Object start(IApplicationContext context) throws Exception {
        System.out.println("START My Application");
        OpenDocumentEventProcessor openDocProcessor = 
                new OpenDocumentEventProcessor();

        IProduct product = Platform.getProduct();
        if (product != null && product.getName() != null) {
            Display.setAppName(product.getName());
        }
        Display display = getApplicationDisplay();
        display.addListener(SWT.OpenDocument, openDocProcessor);

        try {
            int returnCode = PlatformUI.createAndRunWorkbench(display, new 
                    ApplicationWorkbenchAdvisor(openDocProcessor));

            if (returnCode == PlatformUI.RETURN_RESTART) {
                return IApplication.EXIT_RESTART;
            }
            return IApplication.EXIT_OK;
        } finally {
            if (display != null)
                display.dispose();
        }
    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub

    }

}

ApplicationWorkbenchAdvisor.java:

package my.package;

import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.application.WorkbenchAdvisor;

public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
    private OpenDocumentEventProcessor openDocProcessor;

    public ApplicationWorkbenchAdvisor(
            OpenDocumentEventProcessor openDocProcessor) {
        this.openDocProcessor = openDocProcessor;
    }


    @Override
    public void eventLoopIdle(Display display) {
        openDocProcessor.openFiles();
        super.eventLoopIdle(display);
    }


    @Override
    public String getInitialWindowPerspectiveId() {
        // TODO Auto-generated method stub
        return null;
    }
}

OpenDocumentEventProcessor.java:

package my.package;

import java.util.ArrayList;

import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

public class OpenDocumentEventProcessor implements Listener {
    private ArrayList<String> filesToOpen = new ArrayList<String>(1);

    @Override
    public void handleEvent(Event event) {
        if (event.text != null)
            filesToOpen.add(event.text);
    }

    public void openFiles() {
        if (filesToOpen.isEmpty())
            return;

        String[] filePaths = filesToOpen.toArray(
            new String[filesToOpen.size()]);
        filesToOpen.clear();

        for (String path : filePaths) {
            // open the file path
        }
    }
}

LifeCycle.java ProcessAdditions 片段:

/**
 * Method to be invoked on ProcessAdditions life-cycle moment.
 * 
 * @param context Eclipse context.
 */
@ProcessAdditions
public void processAdditons(MApplication app, EModelService modelService,
        final IEclipseContext context,
        final IBrandingInfo branding) {
    /*obtain logger from context and publish it
     * to objects that require it*/
    final Logger logger = context.get(Logger.class);
    if (logger != null) {
        ProblemRegistry.INSTANCE.setLogger(logger);
    }

    /*obtain extension registry from context and publish
     * it to objects that require it*/
    final IExtensionRegistry registry = context
            .get(IExtensionRegistry.class);
    if (registry != null) {
        ProjectRegistry.INSTANCE.setExtensionRegistry(registry);
    }

    /* Push help service into context. */
    context.set(HelpService.class, new HelpServiceImpl(registry));


    MWindow window = (MWindow)modelService.find("my.package2.app.trimmedwindow.0", app);
    System.out.println("app: " + app);
    System.out.println("modelService: " + modelService);
    System.out.println("window: " + window);
    //ERROR: window is null here which is normally not when using standard E4Application
    window.setLabel(branding.getWindowTitle());


    ...
}

编辑

我在我的生命周期和 EventloopAdvisor 中使用 PostContextCreate 函数实现了您的解决方案 @greg-449。 但是我发现了一个奇怪的错误:只有在 PostContextCreate 中的侦听器实现之前或之后打开对话框时,此解决方案才有效:

这是我生命周期中的实际代码片段:

@PostContextCreate
public void postContextCreate(final IEclipseContext context) {
    final Shell shell = new Shell(Display.getCurrent());
    new LicenseAgreementDialog(shell).open();

    if(!shell.isDisposed())
        shell.dispose();

    OpenDocumentEventProcessor openDocProcessor = new OpenDocumentEventProcessor();
    Display display = Display.getCurrent();
    display.addListener(SWT.OpenDocument, openDocProcessor);
    IEventLoopAdvisor eventLoopAdvisor = new EventLoopAdvisor(openDocProcessor);
    context.set(IEventLoopAdvisor.class, eventLoopAdvisor);
}

Class LicenseAgreementDialog 在 RCP 应用程序启动之前只打开一次对话框(在启动画面加载时打开)并且在应用程序启动之后 SWT.OpenDocument 事件是正确的由其他双击项目文件触发。但是当我关闭 rcp 应用程序并再次启动它时,LicenseAgreementDialog 正确地没有再次打开,然后不再有 SWT.OpenDocument 事件被触发。 我测试了这个错误并找到了解决方案,我总是必须在 @PostContextCreateFunction 中打开一个对话框,否则不会触发 SWT.OpenDocument 事件。我还使用普通 MessageDialog (--> MessageDialog.openInformation(new Shell(Display.getCurrent()), "Opening", "Now");) 而不是 LicenseAgreementDialog 对其进行了测试,它每次都在开始时打开,但在它之前没有任何对话框。

是否有机会避免总是打开一个虚拟对话框来触发事件?

最终编辑

经过大量试验和错误后,我终于找到了一个可以接受的解决方案来避免在开始时出现这个虚拟对话框:我使用提示添加一个 readAndDispatch 循环直到它为假,但是这个循环本身没有任何区别。我不得不添加第二个循环等待 readAndDispatch return true。我以不同的顺序测试了这两个循环等等,但这是唯一可行的解​​决方案:

@PostContextCreate
    public void postContextCreate(final IEclipseContext context,
            final IEventBroker eventBroker) {
        final Shell shell = new Shell(Display.getCurrent());
        new LicenseAgreementDialog(shell).open();

        /*check for clicked project file or only launcher.exe
         * when only launcher.exe is clicked there are no cmd arguments
         * when project file is double clicked and gets opened by file handler
         * the one and only cmd arg is the filepath from the clicked project file */
        if(Platform.getCommandLineArgs().length != 0) {
            while(Display.getCurrent().readAndDispatch()) { /* wait for false */ }
            while(!Display.getCurrent().readAndDispatch()) { /* wait for true */ }
        }

        if(!shell.isDisposed())
            shell.dispose();

        OpenDocumentEventProcessor openDocProcessor = new OpenDocumentEventProcessor(eventBroker);
        Display display = Display.getCurrent();
        display.addListener(SWT.OpenDocument, openDocProcessor);
        IEventLoopAdvisor eventLoopAdvisor = new EventLoopAdvisor(openDocProcessor);
        context.set(IEventLoopAdvisor.class, eventLoopAdvisor);
    }

有了这两个循环,即使我之前没有出现对话框,SWT.OpenDocument 事件也总是会被正确触发。感谢@greg-449 的帮助。

这里有一个我已经知道的小提示:.ini 文件必须具有 -name 属性,当您将 openfile 功能与 SWT.OpenDocument 事件(对我来说,我使用产品名称作为 window 标签):

当您的主要 window 标签是例如:My Rcp App

然后您的 launcher.ini 文件必须有 -name 属性 和相同的字符串:

--launcher.defaultAction
openFile
-name
My Rcp App

或者您使用变量作为您的 rcp 应用程序的产品名称:

--launcher.defaultAction
openFile
-name
%product.name

使用 PlatformUI.createAndRunWorkbench 使您的 RCP 成为 3.x 兼容模式 RCP,它使用 LegacyIDE.e4xmi,因此找不到您的 window。

我认为对于纯 e4 RCP,您可以只在 LifeCycle 中设置侦听器并使用 IEventLoopAdvisor

所以删除MyE4Application,使用标准的E4Application。还应删除 ApplicationWorkbenchAdvisor。

在生命周期中进行设置 @PostContextCreate

@PostContextCreate
public void postContextCreate(final IEclipseContext context)
{
  OpenDocumentEventProcessor openDocProcessor = new OpenDocumentEventProcessor();

  Display display = Display.getCurrent();

  display.addListener(SWT.OpenDocument, openDocProcessor);

  IEventLoopAdvisor eventLoopAdvisor = new EventLoopAdvisor(openDocProcessor);

  context.set(IEventLoopAdvisor.class, eventLoopAdvisor);
}

并使用这样的事件循环顾问:

@SuppressWarnings("restriction")
class EventLoopAdvisor implements IEventLoopAdvisor
{
  private final OpenDocumentEventProcessor openDoc;

  EventLoopAdvisor(OpenDocumentEventProcessor openDoc)
  {
    this.openDoc = openDoc;
  }


  @Override
  public void eventLoopIdle(final Display display)
  {
    openDoc.openFiles();

    display.sleep();
  }


  @Override
  public void eventLoopException(final Throwable exception)
  {
    // TODO handle errors
  }
}