diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/NatExporter.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/NatExporter.java
index 7d2df152..388af2c4 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/NatExporter.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/NatExporter.java
@@ -40,6 +40,7 @@
import org.eclipse.nebula.widgets.nattable.util.PlatformHelper;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.slf4j.Logger;
@@ -107,6 +108,16 @@ public class NatExporter {
*/
private boolean useProgressDialog = false;
+ /**
+ * The {@link Runnable} that should be executed after the export finished
+ * successfully. Useful in case {@link #openResult} is set to
+ * false so an alternative for reporting the export success can
+ * be configured.
+ *
+ * @since 2.6
+ */
+ private Runnable successRunnable;
+
/**
* Create a new {@link NatExporter}.
*
@@ -160,13 +171,56 @@ public NatExporter(Shell shell, boolean executeSynchronously) {
* is made based on whether a {@link Shell} is set or not. If a
* {@link Shell} is set and this flag is set to true
* the execution is performed synchronously.
+ * @param useProgressDialog
+ * Configure whether the progress should be reported via
+ * {@link ProgressMonitorDialog}. If set to false a
+ * custom shell with a {@link ProgressBar} will be shown if the
+ * shell parameter is not null.
*
* @since 2.3
*/
public NatExporter(Shell shell, boolean executeSynchronously, boolean useProgressDialog) {
+ this(shell, executeSynchronously, useProgressDialog, true, null);
+ }
+
+ /**
+ * Create a new {@link NatExporter}.
+ *
+ * @param shell
+ * The {@link Shell} that should be used to open sub-dialogs and
+ * perform export operations in a background thread. Can be
+ * null but could lead to
+ * {@link NullPointerException}s if {@link IExporter} are
+ * configured, that use a {@link FileOutputStreamProvider}.
+ * @param executeSynchronously
+ * Configure whether the export should be performed
+ * asynchronously or synchronously. By default the decision
+ * whether the execution should be performed synchronously or not
+ * is made based on whether a {@link Shell} is set or not. If a
+ * {@link Shell} is set and this flag is set to true
+ * the execution is performed synchronously.
+ * @param useProgressDialog
+ * Configure whether the progress should be reported via
+ * {@link ProgressMonitorDialog}. If set to false a
+ * custom shell with a {@link ProgressBar} will be shown if the
+ * shell parameter is not null.
+ * @param openResult
+ * Configure if the created export result should be opened after
+ * the export is finished.
+ * @param successRunnable
+ * The {@link Runnable} that should be executed after the export
+ * finished successfully. Useful in case {@link #openResult} is
+ * set to false so an alternative for reporting the
+ * export success can be configured.
+ *
+ * @since 2.6
+ */
+ public NatExporter(Shell shell, boolean executeSynchronously, boolean useProgressDialog, boolean openResult, Runnable successRunnable) {
this.shell = shell;
this.runAsynchronously = !executeSynchronously;
this.useProgressDialog = useProgressDialog;
+ this.openResult = openResult;
+ this.successRunnable = successRunnable;
}
/**
@@ -946,17 +1000,29 @@ protected void setClientAreaToMaximum(ILayer layer) {
* @since 1.5
*/
protected void openExport(IExporter exporter) {
- if (this.exportSucceeded
- && this.openResult
- && exporter.getResult() != null
- && exporter.getResult() instanceof File) {
+ if (this.exportSucceeded) {
- try {
- Class> program = Class.forName("org.eclipse.swt.program.Program"); //$NON-NLS-1$
- Method launch = program.getMethod("launch", String.class); //$NON-NLS-1$
- launch.invoke(null, ((File) exporter.getResult()).getAbsolutePath());
- } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
- LOG.info("Could not open the export because org.eclipse.swt.program.Program, you are probably running a RAP application."); //$NON-NLS-1$
+ if (this.successRunnable != null) {
+ if (this.shell != null) {
+ this.shell.getDisplay().syncExec(() -> {
+ this.successRunnable.run();
+ });
+ } else {
+ this.successRunnable.run();
+ }
+ }
+
+ if (this.openResult
+ && exporter.getResult() != null
+ && exporter.getResult() instanceof File) {
+
+ try {
+ Class> program = Class.forName("org.eclipse.swt.program.Program"); //$NON-NLS-1$
+ Method launch = program.getMethod("launch", String.class); //$NON-NLS-1$
+ launch.invoke(null, ((File) exporter.getResult()).getAbsolutePath());
+ } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ LOG.info("Could not open the export because org.eclipse.swt.program.Program, you are probably running a RAP application."); //$NON-NLS-1$
+ }
}
}
}
@@ -1061,4 +1127,20 @@ public boolean isUseProgressDialog() {
public void setUseProgressDialog(boolean useProgressDialog) {
this.useProgressDialog = useProgressDialog;
}
+
+ /**
+ * Configure a {@link Runnable} that should be executed after a successful
+ * export operation. If a {@link #shell} is set, the {@link Runnable} is
+ * executed using {@link Display#syncExec(Runnable)}.
+ *
+ * @param successRunnable
+ * The {@link Runnable} that should be executed after the export
+ * finished successfully. Useful in case {@link #openResult} is
+ * set to false so an alternative for reporting the
+ * export success can be configured.
+ * @since 2.6
+ */
+ public void setSuccessRunnable(Runnable successRunnable) {
+ this.successRunnable = successRunnable;
+ }
}
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommand.java
index 6cebaf55..ef245ed9 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommand.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2023 Original authors and others.
+ * Copyright (c) 2012, 2025 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -31,6 +31,8 @@ public class ExportCommand extends AbstractContextFreeCommand {
private final boolean executeSynchronously;
private final boolean useProgressDialog;
private final ILayerExporter exporter;
+ private final boolean openResult;
+ private final Runnable successRunnable;
/**
* Creates a new {@link ExportCommand}.
@@ -124,11 +126,59 @@ public ExportCommand(IConfigRegistry configRegistry, Shell shell, boolean execut
* @since 2.3
*/
public ExportCommand(IConfigRegistry configRegistry, Shell shell, boolean executeSynchronously, boolean useProgressDialog, ILayerExporter exporter) {
+ this(configRegistry, shell, executeSynchronously, useProgressDialog, exporter, true, null);
+ }
+
+ /**
+ * Creates a new {@link ExportCommand}.
+ *
+ * @param configRegistry
+ * The {@link IConfigRegistry} that contains the necessary export
+ * configurations.
+ * @param shell
+ * The {@link Shell} that should be used to open sub-dialogs and
+ * perform export operations in a background thread. Can be
+ * null which definitely leads to synchronous
+ * execution but could cause errors in case sub-dialogs should be
+ * opened before exporting.
+ * @param executeSynchronously
+ * Configure if the export should be performed synchronously even
+ * if a {@link Shell} is set.
+ * @param useProgressDialog
+ * Configure whether the progress should be reported via
+ * {@link ProgressMonitorDialog}. If set to false a
+ * custom shell with a {@link ProgressBar} will be shown if the
+ * shell parameter is not null.
+ * @param exporter
+ * The {@link ILayerExporter} that should be used. Can be
+ * null, which causes the usage of the exporter
+ * registered in the {@link IConfigRegistry}.
+ * @param openResult
+ * Configure if the created export result should be opened after
+ * the export is finished.
+ * @param successRunnable
+ * The {@link Runnable} that should be executed after the export
+ * finished successfully. Useful in case {@link #openResult} is
+ * set to false so an alternative for reporting the
+ * export success can be configured.
+ * @since 2.6
+ */
+ public ExportCommand(
+ IConfigRegistry configRegistry,
+ Shell shell,
+ boolean executeSynchronously,
+ boolean useProgressDialog,
+ ILayerExporter exporter,
+ boolean openResult,
+ Runnable successRunnable) {
+
this.configRegistry = configRegistry;
this.shell = shell;
this.executeSynchronously = executeSynchronously;
this.useProgressDialog = useProgressDialog;
this.exporter = exporter;
+ this.openResult = openResult;
+ this.successRunnable = successRunnable;
}
/**
@@ -185,4 +235,26 @@ public boolean isUseProgressDialog() {
public ILayerExporter getExporter() {
return this.exporter;
}
+
+ /**
+ *
+ * @return true if the created export file should be opened
+ * after the export finished successfully, false if not
+ * @since 2.6
+ */
+ public boolean isOpenResult() {
+ return this.openResult;
+ }
+
+ /**
+ *
+ * @return The {@link Runnable} that should be executed after the export
+ * finished successfully. Useful in case {@link #openResult} is set
+ * to false so an alternative for reporting the export
+ * success can be configured.
+ * @since 2.6
+ */
+ public Runnable getSuccessRunnable() {
+ return this.successRunnable;
+ }
}
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommandHandler.java
index c0508b87..cc0e2256 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/export/command/ExportCommandHandler.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2012, 2023 Original authors and others.
+ * Copyright (c) 2012, 2025 Original authors and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
@@ -37,18 +37,17 @@ public ExportCommandHandler(ILayer layer) {
@Override
public boolean doCommand(final ExportCommand command) {
+ NatExporter natExporter = new NatExporter(
+ command.getShell(),
+ command.isExecuteSynchronously(),
+ command.isUseProgressDialog(),
+ command.isOpenResult(),
+ command.getSuccessRunnable());
+
if (command.getExporter() == null) {
- new NatExporter(
- command.getShell(),
- command.isExecuteSynchronously(),
- command.isUseProgressDialog())
- .exportSingleLayer(this.layer, command.getConfigRegistry());
+ natExporter.exportSingleLayer(this.layer, command.getConfigRegistry());
} else {
- new NatExporter(
- command.getShell(),
- command.isExecuteSynchronously(),
- command.isUseProgressDialog())
- .exportSingleLayer(command.getExporter(), this.layer, command.getConfigRegistry());
+ natExporter.exportSingleLayer(command.getExporter(), this.layer, command.getConfigRegistry());
}
return true;
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupby/GroupByHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupby/GroupByHeaderLayerTest.java
index 40d43611..748c0ba7 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupby/GroupByHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/groupby/GroupByHeaderLayerTest.java
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
+import org.eclipse.nebula.widgets.nattable.command.DisposeResourcesCommand;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
@@ -60,6 +61,7 @@
import org.eclipse.nebula.widgets.nattable.sort.config.DefaultSortConfiguration;
import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -176,6 +178,11 @@ public void setup() {
this.natTable.configure();
}
+ @AfterEach
+ public void tearDown() {
+ this.natTable.doCommand(new DisposeResourcesCommand());
+ }
+
@Test
public void shouldUpdateGroupByModelViaGroupByCommand() {
this.natTable.doCommand(new GroupByCommand(GroupByAction.ADD, 1));