Java 将SWT.OpenDocument SWT Listener添加到RCP应用程序的e4应用程序

Java 将SWT.OpenDocument SWT Listener添加到RCP应用程序的e4应用程序,java,eclipse,eclipse-rcp,e4,Java,Eclipse,Eclipse Rcp,E4,我想使用eclipse的launcher.openfile特性。因此,我阅读了一些文档(例如) 我正确地实现了一个自定义应用程序类,但是我猜这个类的内容缺少了一些东西,因为在我的生命周期类中再也找不到主窗口,这通常可以在我使用标准应用程序时找到 如何使用E4Application类的通用功能,只添加SWT侦听器SWT.OpenDocument 这里是我的申请代码: package de.port.dsntool.ui.services; import org.eclipse.core.runt

我想使用eclipse的launcher.openfile特性。因此,我阅读了一些文档(例如) 我正确地实现了一个自定义应用程序类,但是我猜这个类的内容缺少了一些东西,因为在我的生命周期类中再也找不到主窗口,这通常可以在我使用标准应用程序时找到

如何使用E4Application类的通用功能,只添加SWT侦听器SWT.OpenDocument

这里是我的申请代码:

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

    }

}
ApplicationWorkbenchHadVisor.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
        }
    }
}
编辑

我在我的生命周期和EventloopAdvisor中使用PostContextCreate函数实现了您的解决方案@greg-449。 但我发现了一个奇怪的bug:只有在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);
}
LicenseAgreementDialog
在RCP应用程序启动之前(在splashscreen加载时打开)仅打开一次对话框,在应用程序启动之后,SWT.OpenDocument事件由其他双击的项目文件正确触发。但是,当我关闭rcp应用程序并再次启动它时,
LicenseAgreementDialog
不会再次正确打开,因此不会再触发任何SWT.OpenDocument事件。 我测试了这个bug,找到了一个解决方案,我总是必须在
@PostContextCreateFunction
中打开一个对话框,否则不会触发SWT.OpenDocument事件。我还使用了一个普通的
MessageDialog
(->
MessageDialog.openInformation(newshell(Display.getCurrent()),“Opening”,“Now”);
)来测试它,而不是
LicenseAgreementDialog
,它在每次启动时都会打开,但之前没有任何对话框

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

最终编辑

经过大量的尝试和错误,我终于找到了一个可以接受的解决方案,以避免在开始时出现这个虚拟对话框:我使用了添加readAndDispatch循环的提示,直到它为false,但是这个循环本身并没有什么区别。我不得不添加第二个循环,等待readAndDispatch返回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事件一起使用时,该属性必须与独立的RCP应用程序主窗口标签相匹配(对于我来说,我使用产品名称作为窗口标签):

当您的主窗口标签为例如: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,因此找不到您的窗口

我认为对于纯e4 RCP,您可能只需在生命周期中设置侦听器,并使用
IEventLoopAdvisor

因此,删除MyE4Application并使用标准的E4Application。还应删除ApplicationWorkbenchHadVisor

在生命周期中设置内容
@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
  }
}

我不会真的称之为e4 RCP,它是一个3.x兼容模式RCP,使用一些底层e4功能。我想如果你使用
PlatformUI.createAndRunWorkbench
它将使用
LegacyIDE.e4xmi
而不是你的应用程序。e4xmi请看我的编辑,在那里我解释了我的解决方案@greg-449I我想我帮不了多少忙。我使用macOS,甚至在Eclipse本身上也无法让launcher.openFile工作。一个猜测是循环调用Display.readAndDispatch,直到返回false代替对话框。我需要添加两个循环。第一个循环是您的猜测:调用Display.readAndDispatch直到返回false。第二个循环必须调用Display.readAndDispatch,直到返回true!(见我的最终编辑)
@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
  }
}