This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new fd32042019 Issue #8098 : Allow help files to be opened in a dialog
window (#8100)
fd32042019 is described below
commit fd3204201934ebf7ee8f31dbe462534778892849
Author: Matt Casters <[email protected]>
AuthorDate: Wed Aug 26 14:30:18 2026 +0200
Issue #8098 : Allow help files to be opened in a dialog window (#8100)
Add a third destination for Hop GUI help (external browser, Explorer tab,
or a modeless dialog on top of the current context). Route Help buttons
and F1 through the same setting, migrate the old openingHelpFiles checkbox,
and document the option with a screenshot.
---
...onfiguration-perspective-open-help-pages-in.png | Bin 0 -> 10762 bytes
.../pages/hop-gui/perspective-configuration.adoc | 3 +
.../pages/hop-gui/perspective-file-explorer.adoc | 3 +
.../apache/hop/ui/core/dialog/ShowHelpDialog.java | 172 +++++++++++++++------
.../main/java/org/apache/hop/ui/hopgui/HopGui.java | 7 +-
.../explorer/config/ExplorerPerspectiveConfig.java | 68 ++++----
.../config/ExplorerPerspectiveConfigPlugin.java | 77 +++++++--
.../java/org/apache/hop/ui/util/HelpOpenMode.java | 85 ++++++++++
.../java/org/apache/hop/ui/util/HelpUtils.java | 104 ++++++++++---
.../ui/hopgui/messages/messages_en_US.properties | 1 +
.../config/messages/messages_en_US.properties | 4 +-
.../config/messages/messages_fr_FR.properties | 4 +-
.../config/messages/messages_pt_BR.properties | 4 +-
.../hop/ui/util/messages/messages_en_US.properties | 20 +++
.../hop/ui/util/messages/messages_fr_FR.properties | 20 +++
.../hop/ui/util/messages/messages_pt_BR.properties | 20 +++
.../hop/ui/core/dialog/ShowHelpDialogTest.java | 35 +++++
.../config/ExplorerPerspectiveConfigTest.java | 87 +++++++++++
.../org/apache/hop/ui/util/HelpOpenModeTest.java | 52 +++++++
.../java/org/apache/hop/ui/util/HelpUtilsTest.java | 49 ++++++
20 files changed, 688 insertions(+), 127 deletions(-)
diff --git
a/docs/hop-user-manual/modules/ROOT/assets/images/hop-gui/configuration-perspective-open-help-pages-in.png
b/docs/hop-user-manual/modules/ROOT/assets/images/hop-gui/configuration-perspective-open-help-pages-in.png
new file mode 100644
index 0000000000..6bd33d6cff
Binary files /dev/null and
b/docs/hop-user-manual/modules/ROOT/assets/images/hop-gui/configuration-perspective-open-help-pages-in.png
differ
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
index 8cf2194d54..b9c2504fe6 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc
@@ -131,6 +131,9 @@ The following plugins are available by default:
** The initial depth to load not lazily: Controls how many folder levels are
loaded immediately when opening folders.
** The maximum file size to load: Sets the maximum file size (in MB) that
will be loaded when opening files.
** Show file explorer panel by default: When enabled, the file explorer
panel (project tree) is shown by default when opening the explorer perspective.
When disabled, the panel starts hidden.
+ ** Open help pages in: Where Hop GUI opens documentation from Help buttons
and F1. *External browser* (default) uses the system browser. *Hop GUI tab*
opens an HTML tab in the File Explorer perspective. *Dialog window* opens a
modeless help window on top of the current dialog or main window so you can
keep editing.
++
+image::hop-gui/configuration-perspective-open-help-pages-in.png[Open help
pages in,width="90%"]
* xref:technology/google/index.adoc[Google Cloud] configuration options
(service account JSON key file).
* xref:vfs/google-drive-vfs.adoc#_configuration[Google Drive] VFS
configuration options.
* xref:projects/index.adoc[Project] configuration options
diff --git
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-file-explorer.adoc
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-file-explorer.adoc
index 9887829321..feaa000341 100644
---
a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-file-explorer.adoc
+++
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-file-explorer.adoc
@@ -64,6 +64,9 @@ The File Explorer perspective can be configured through the
xref:hop-gui/perspec
* **The initial depth to load not lazily**: Controls how many folder levels
are loaded immediately when opening folders in the file explorer tree.
* **The maximum file size to load**: Sets the maximum file size (in MB) that
will be loaded when opening files in the explorer.
* **Show file explorer panel by default**: When enabled, the file explorer
panel (project tree) is shown by default when opening the explorer perspective.
When disabled, the panel starts hidden, giving you more workspace for editing
files.
+* **Open help pages in**: Where Help buttons and F1 open documentation: the
system browser, a tab in this perspective, or a dialog on top of the current
window.
++
+image::hop-gui/configuration-perspective-open-help-pages-in.png[Open help
pages in,width="90%"]
== Git integration
diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowHelpDialog.java
b/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowHelpDialog.java
index 3f6f7eff0a..ee0ca6747b 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowHelpDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowHelpDialog.java
@@ -17,14 +17,14 @@
package org.apache.hop.ui.core.dialog;
-import java.net.MalformedURLException;
-import java.net.URL;
import org.apache.hop.core.Const;
+import org.apache.hop.core.util.Utils;
import org.apache.hop.i18n.BaseMessages;
-import org.apache.hop.laf.BasePropertyHandler;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.gui.GuiResource;
+import org.apache.hop.ui.core.gui.WindowProperty;
import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
import org.apache.hop.ui.util.EnvironmentUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
@@ -44,21 +44,23 @@ import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
+/**
+ * Modeless documentation viewer parented to the current context shell
(transform/action dialog or
+ * the main Hop GUI window).
+ */
public class ShowHelpDialog extends Dialog {
+ public static final String SHELL_DATA_KEY = "hop.help.dialog";
+
private static final Class<?> PKG = HopGui.class;
- private static final String DOC_URL =
- Const.getDocUrl(BasePropertyHandler.getProperty("documentationUrl"));
- private static final String PREFIX = "https://help";
- private static final String PRINT_PREFIX = "https://f1.help";
private static final String PRINT_SCRIPT = "javascript:window.print();";
private static final int MARGIN = 5;
+ private static final int DEFAULT_WIDTH = 900;
+ private static final int DEFAULT_HEIGHT = 700;
- private boolean fromPrint;
-
- private String dialogTitle;
+ private final String dialogTitle;
private String url;
- private String homeURL;
+ private final String homeURL;
private Browser wBrowser;
@@ -69,22 +71,29 @@ public class ShowHelpDialog extends Dialog {
private Shell shell;
- public ShowHelpDialog(Shell parent, String dialogTitle, String url, String
header) {
- super(parent, SWT.NONE);
- this.dialogTitle = BaseMessages.getString(PKG,
"HopGui.Documentation.Hop.Title");
- this.url = url;
- try {
- this.homeURL = new URL(DOC_URL).toString();
- } catch (MalformedURLException e) {
- }
+ public ShowHelpDialog(Shell parent, String url) {
+ this(parent, BaseMessages.getString(PKG,
"HopGui.Documentation.Hop.Title"), url);
}
public ShowHelpDialog(Shell parent, String dialogTitle, String url) {
- this(parent, dialogTitle, url, "");
+ super(parent, SWT.NONE);
+ this.dialogTitle =
+ Utils.isEmpty(dialogTitle)
+ ? BaseMessages.getString(PKG, "HopGui.Documentation.Hop.Title")
+ : dialogTitle;
+ this.url = url;
+ this.homeURL = Const.getDocUrl("");
}
protected Shell createShell(Shell parent) {
- return new Shell(parent, BaseDialog.getDefaultDialogStyle());
+ return new Shell(parent, helpDialogStyle());
+ }
+
+ static int helpDialogStyle() {
+ if (EnvironmentUtils.getInstance().isWeb()) {
+ return SWT.DIALOG_TRIM | SWT.RESIZE;
+ }
+ return SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN;
}
public void open() {
@@ -93,12 +102,26 @@ public class ShowHelpDialog extends Dialog {
PropsUi props = PropsUi.getInstance();
shell = createShell(parent);
- shell.setImage(GuiResource.getInstance().getImageHopUi());
+ shell.setImage(GuiResource.getInstance().getImageHelp());
shell.setLayout(new FormLayout());
shell.setText(dialogTitle);
PropsUi.setLook(shell);
Cursor cursorHand = new Cursor(display, SWT.CURSOR_HAND);
+ Color urlColor = new Color(display, props.contrastColor(101, 101, 101));
+ shell.addListener(SWT.Close, e -> PropsUi.getInstance().setScreen(new
WindowProperty(shell)));
+ shell.addDisposeListener(
+ e -> {
+ if (cursorHand != null && !cursorHand.isDisposed()) {
+ cursorHand.dispose();
+ }
+ if (urlColor != null && !urlColor.isDisposed()) {
+ urlColor.dispose();
+ }
+ if (parent != null && !parent.isDisposed() &&
parent.getData(SHELL_DATA_KEY) == this) {
+ parent.setData(SHELL_DATA_KEY, null);
+ }
+ });
ToolBar navigateToolBar = new ToolBar(shell, SWT.FLAT);
FormData fdtoolBarBack = new FormData();
@@ -149,16 +172,35 @@ public class ShowHelpDialog extends Dialog {
tltmPrint.setEnabled(true);
tltmPrint.addListener(SWT.Selection, e -> print());
+ ToolItem tltmExternal = new ToolItem(printToolBar, SWT.NONE);
+
tltmExternal.setImage(GuiResource.getInstance().getImage("ui/images/html.svg"));
+ tltmExternal.setToolTipText(
+ BaseMessages.getString(PKG,
"HopGui.Documentation.Tooltip.OpenExternal"));
+ tltmExternal.addListener(SWT.Selection, e -> openExternal());
+
textURL = new Text(shell, SWT.BORDER);
FormData fdtext = new FormData();
fdtext.top = new FormAttachment(0, MARGIN);
fdtext.right = new FormAttachment(printToolBar, -MARGIN);
fdtext.left = new FormAttachment(navigateToolBar, MARGIN);
textURL.setLayoutData(fdtext);
- textURL.setForeground(new Color(display, props.contrastColor(101, 101,
101)));
+ textURL.setForeground(urlColor);
+ textURL.setText(Const.NVL(url, ""));
+ textURL.addListener(
+ SWT.DefaultSelection,
+ e -> {
+ String location = textURL.getText();
+ if (!Utils.isEmpty(location) && wBrowser != null &&
!wBrowser.isDisposed()) {
+ wBrowser.setUrl(location);
+ }
+ });
- // Browser
- wBrowser = new Browser(shell, SWT.NONE);
+ try {
+ wBrowser = new Browser(shell, SWT.NONE);
+ } catch (RuntimeException e) {
+ dispose();
+ throw e;
+ }
FormData fdBrowser = new FormData();
fdBrowser.top = new FormAttachment(textURL, MARGIN);
fdBrowser.right = new FormAttachment(100, 0);
@@ -170,9 +212,18 @@ public class ShowHelpDialog extends Dialog {
addProgressAndLocationListener();
- textURL.setFocus();
+ shell.addListener(
+ SWT.Traverse,
+ e -> {
+ if (e.detail == SWT.TRAVERSE_ESCAPE) {
+ e.doit = false;
+ dispose();
+ }
+ });
- BaseDialog.defaultShellHandling(shell, c -> ok(), c -> ok());
+ BaseTransformDialog.setSize(shell, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ textURL.setFocus();
+ shell.open();
}
private void addProgressAndLocationListener() {
@@ -185,11 +236,7 @@ public class ShowHelpDialog extends Dialog {
@Override
public void completed(ProgressEvent event) {
- if (fromPrint) {
- wBrowser.execute(PRINT_SCRIPT);
- fromPrint = false;
- }
- if (!EnvironmentUtils.getInstance().isWeb()) {
+ if (!EnvironmentUtils.getInstance().isWeb() && tltmBack != null) {
// Browser in RAP does not implement back() and forward()
setBackEnable(wBrowser.isBackEnabled());
setForwardEnable(wBrowser.isForwardEnabled());
@@ -201,7 +248,7 @@ public class ShowHelpDialog extends Dialog {
new LocationListener() {
@Override
public void changing(LocationEvent event) {
- if (event.location.endsWith(".pdf")) {
+ if (event.location != null && event.location.endsWith(".pdf")) {
try {
EnvironmentUtils.getInstance().openUrl(event.location);
} catch (Exception e) {
@@ -213,7 +260,9 @@ public class ShowHelpDialog extends Dialog {
@Override
public void changed(LocationEvent event) {
- textURL.setText(event.location);
+ if (event.location != null && textURL != null &&
!textURL.isDisposed()) {
+ textURL.setText(event.location);
+ }
}
};
wBrowser.addProgressListener(progressListener);
@@ -237,29 +286,58 @@ public class ShowHelpDialog extends Dialog {
}
private void print() {
- String printURL = wBrowser.getUrl();
- if (printURL.startsWith(PREFIX)) {
- printURL = printURL.replace(PREFIX, PRINT_PREFIX);
- fromPrint = true;
- wBrowser.setUrl(printURL);
- } else {
- wBrowser.execute(PRINT_SCRIPT);
+ wBrowser.execute(PRINT_SCRIPT);
+ }
+
+ private void openExternal() {
+ String location = wBrowser.getUrl();
+ if (Utils.isEmpty(location)) {
+ location = url;
+ }
+ try {
+ EnvironmentUtils.getInstance().openUrl(location);
+ } catch (Exception e) {
+ new ErrorDialog(shell, "Error", "Error opening URL", e);
}
}
private void setBackEnable(boolean enable) {
- tltmBack.setEnabled(enable);
+ if (tltmBack != null && !tltmBack.isDisposed()) {
+ tltmBack.setEnabled(enable);
+ }
}
private void setForwardEnable(boolean enable) {
- tltmForward.setEnabled(enable);
+ if (tltmForward != null && !tltmForward.isDisposed()) {
+ tltmForward.setEnabled(enable);
+ }
}
- public void dispose() {
- shell.dispose();
+ public void setUrl(String url) {
+ this.url = url;
+ if (wBrowser != null && !wBrowser.isDisposed()) {
+ wBrowser.setUrl(url);
+ }
+ if (textURL != null && !textURL.isDisposed() && url != null) {
+ textURL.setText(url);
+ }
+ }
+
+ public boolean isDisposed() {
+ return shell == null || shell.isDisposed();
}
- private void ok() {
- dispose();
+ public void forceActive() {
+ if (shell != null && !shell.isDisposed()) {
+ shell.setMinimized(false);
+ shell.setActive();
+ shell.forceActive();
+ }
+ }
+
+ public void dispose() {
+ if (shell != null && !shell.isDisposed()) {
+ shell.dispose();
+ }
}
}
diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
index 4aa4225097..4d37882f2e 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
@@ -135,6 +135,7 @@ import
org.apache.hop.ui.hopgui.search.SearchEverywhereDialog;
import org.apache.hop.ui.hopgui.welcome.WelcomeDialog;
import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
import org.apache.hop.ui.util.EnvironmentUtils;
+import org.apache.hop.ui.util.HelpUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.ShellAdapter;
@@ -2576,11 +2577,7 @@ public class HopGui
HopPerspectivePlugin plugin =
activePerspective.getClass().getAnnotation(HopPerspectivePlugin.class);
if (plugin != null) {
- try {
-
EnvironmentUtils.getInstance().openUrl(getDocUrl(plugin.documentationUrl()));
- } catch (Exception e) {
- new ErrorDialog(shell, "Error", "Error opening URL", e);
- }
+ HelpUtils.openHelp(shell, getDocUrl(plugin.documentationUrl()));
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfig.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfig.java
index a14fd63aa9..b55c11d6f2 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfig.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfig.java
@@ -6,7 +6,7 @@
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,6 +17,14 @@
package org.apache.hop.ui.hopgui.perspective.explorer.config;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.hop.ui.util.HelpOpenMode;
+
+@Getter
+@Setter
public class ExplorerPerspectiveConfig {
public static final String HOP_CONFIG_EXPLORER_PERSPECTIVE_CONFIG_KEY =
"explorer-perspective";
@@ -24,14 +32,14 @@ public class ExplorerPerspectiveConfig {
private String lazyLoadingDepth;
private String fileLoadingMaxSize;
private Boolean fileExplorerVisibleByDefault;
- private Boolean openingHelpFiles;
+ private HelpOpenMode helpOpenMode;
private Boolean activeFileSelection;
public ExplorerPerspectiveConfig() {
this.lazyLoadingDepth = "0";
this.fileLoadingMaxSize = "16";
this.fileExplorerVisibleByDefault = true;
- this.openingHelpFiles = false;
+ this.helpOpenMode = HelpOpenMode.BROWSER;
this.activeFileSelection = true;
}
@@ -40,47 +48,33 @@ public class ExplorerPerspectiveConfig {
this.lazyLoadingDepth = config.lazyLoadingDepth;
this.fileLoadingMaxSize = config.fileLoadingMaxSize;
this.fileExplorerVisibleByDefault = config.fileExplorerVisibleByDefault;
- this.openingHelpFiles = config.openingHelpFiles;
+ this.helpOpenMode = config.getHelpOpenMode();
this.activeFileSelection = config.activeFileSelection;
}
- public String getLazyLoadingDepth() {
- return lazyLoadingDepth;
- }
-
- public void setLazyLoadingDepth(String lazyLoadingDepth) {
- this.lazyLoadingDepth = lazyLoadingDepth;
- }
-
- public String getFileLoadingMaxSize() {
- return fileLoadingMaxSize;
- }
-
- public void setFileLoadingMaxSize(String fileLoadingMaxSize) {
- this.fileLoadingMaxSize = fileLoadingMaxSize;
+ public HelpOpenMode getHelpOpenMode() {
+ return helpOpenMode != null ? helpOpenMode : HelpOpenMode.BROWSER;
}
- public Boolean getFileExplorerVisibleByDefault() {
- return fileExplorerVisibleByDefault;
- }
-
- public void setFileExplorerVisibleByDefault(Boolean
fileExplorerVisibleByDefault) {
- this.fileExplorerVisibleByDefault = fileExplorerVisibleByDefault;
+ /**
+ * Legacy hop-config key {@code openingHelpFiles}. True used to mean "open
help in Explorer tabs".
+ *
+ * @param openingHelpFiles previous boolean flag
+ */
+ @JsonSetter("openingHelpFiles")
+ public void migrateOpeningHelpFiles(Boolean openingHelpFiles) {
+ if (Boolean.TRUE.equals(openingHelpFiles) && this.helpOpenMode ==
HelpOpenMode.BROWSER) {
+ this.helpOpenMode = HelpOpenMode.TAB;
+ }
}
+ /**
+ * @return true when help should open as an Explorer tab (legacy checkbox
semantics)
+ * @deprecated use {@link #getHelpOpenMode()}
+ */
+ @Deprecated(since = "2.20")
+ @JsonIgnore
public Boolean isOpeningHelpFiles() {
- return openingHelpFiles != null ? openingHelpFiles : false;
- }
-
- public void setOpeningHelpFiles(Boolean openingHelpFiles) {
- this.openingHelpFiles = openingHelpFiles;
- }
-
- public Boolean getActiveFileSelection() {
- return activeFileSelection != null ? activeFileSelection : true;
- }
-
- public void setActiveFileSelection(Boolean activeFileSelection) {
- this.activeFileSelection = activeFileSelection;
+ return getHelpOpenMode() == HelpOpenMode.TAB;
}
}
diff --git
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigPlugin.java
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigPlugin.java
index 9c6218dcf4..699b3382f9 100644
---
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigPlugin.java
+++
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigPlugin.java
@@ -17,6 +17,8 @@
package org.apache.hop.ui.hopgui.perspective.explorer.config;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.hop.core.Const;
import org.apache.hop.core.config.plugin.ConfigPlugin;
import org.apache.hop.core.config.plugin.IConfigOptions;
@@ -27,13 +29,17 @@ import org.apache.hop.core.gui.plugin.GuiWidgetElement;
import org.apache.hop.core.logging.ILogChannel;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.metadata.api.IHasHopMetadataProvider;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.gui.GuiCompositeWidgets;
import org.apache.hop.ui.core.gui.IGuiPluginCompositeWidgetsListener;
+import org.apache.hop.ui.core.widget.ComboVar;
import org.apache.hop.ui.core.widget.TextVar;
import org.apache.hop.ui.hopgui.HopGui;
import
org.apache.hop.ui.hopgui.perspective.configuration.tabs.ConfigPluginOptionsTab;
+import org.apache.hop.ui.util.HelpOpenMode;
import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
import picocli.CommandLine;
@@ -93,12 +99,20 @@ public class ExplorerPerspectiveConfigPlugin
@GuiWidgetElement(
id = WIDGET_ID_OPEN_HELP_FILES,
parentId = ConfigPluginOptionsTab.GUI_WIDGETS_PARENT_ID,
- type = GuiElementType.CHECKBOX,
- label = "i18n::ExplorerPerspectiveConfig.OpenHelpFiles.Label",
- toolTip = "i18n::ExplorerPerspectiveConfig.OpenHelpFiles.Tooltip")
+ type = GuiElementType.COMBO,
+ variables = false,
+ comboValuesMethod = "getHelpOpenModeLabels",
+ label = "i18n::ExplorerPerspectiveConfig.HelpOpenMode.Label",
+ toolTip = "i18n::ExplorerPerspectiveConfig.HelpOpenMode.Tooltip")
+ @CommandLine.Option(
+ names = {"--open-help-mode"},
+ description = "Where to open help pages: BROWSER, TAB or DIALOG")
+ private String helpOpenMode;
+
@CommandLine.Option(
names = {"-oh", "--open-help-in-tabs"},
- description = "Open help files in Hop GUI tabs instead of external
browser")
+ description =
+ "Deprecated: open help files in Hop GUI tabs instead of the external
browser. Prefer --open-help-mode=TAB")
private Boolean openingHelpFiles;
@GuiWidgetElement(
@@ -137,7 +151,7 @@ public class ExplorerPerspectiveConfigPlugin
instance.fileLoadingMaxSize = config.getFileLoadingMaxSize();
Boolean visibleByDefault = config.getFileExplorerVisibleByDefault();
instance.fileExplorerVisibleByDefault = visibleByDefault != null ?
visibleByDefault : true;
- instance.openingHelpFiles = config.isOpeningHelpFiles();
+ instance.helpOpenMode = config.getHelpOpenMode().getLabel();
instance.activeFileSelection = config.getActiveFileSelection();
instance.maxUndo =
Integer.toString(org.apache.hop.ui.core.PropsUi.getInstance().getMaxUndo());
@@ -177,10 +191,16 @@ public class ExplorerPerspectiveConfigPlugin
changed = true;
}
- if (openingHelpFiles != null) {
- config.setOpeningHelpFiles(openingHelpFiles);
- log.logBasic(
- "Explorer perspective: open help files in tabs is set to '" +
openingHelpFiles + "'");
+ if (Boolean.TRUE.equals(openingHelpFiles)) {
+ config.setHelpOpenMode(HelpOpenMode.TAB);
+ log.logBasic("Explorer perspective: open help mode is set to '" +
HelpOpenMode.TAB + "'");
+ changed = true;
+ }
+
+ if (helpOpenMode != null) {
+ HelpOpenMode mode = HelpOpenMode.fromConfigValue(helpOpenMode);
+ config.setHelpOpenMode(mode);
+ log.logBasic("Explorer perspective: open help mode is set to '" + mode
+ "'");
changed = true;
}
@@ -243,8 +263,9 @@ public class ExplorerPerspectiveConfigPlugin
.setFileExplorerVisibleByDefault(fileExplorerVisibleByDefault);
break;
case WIDGET_ID_OPEN_HELP_FILES:
- openingHelpFiles = ((Button) control).getSelection();
-
ExplorerPerspectiveConfigSingleton.getConfig().setOpeningHelpFiles(openingHelpFiles);
+ helpOpenMode = readComboText(control);
+ ExplorerPerspectiveConfigSingleton.getConfig()
+ .setHelpOpenMode(HelpOpenMode.fromLabel(helpOpenMode));
break;
case WIDGET_ID_ACTIVE_FILE_SELECTION:
activeFileSelection = ((Button) control).getSelection();
@@ -296,6 +317,14 @@ public class ExplorerPerspectiveConfigPlugin
this.fileExplorerVisibleByDefault = fileExplorerVisibleByDefault;
}
+ public String getHelpOpenMode() {
+ return helpOpenMode;
+ }
+
+ public void setHelpOpenMode(String helpOpenMode) {
+ this.helpOpenMode = helpOpenMode;
+ }
+
public Boolean isOpeningHelpFiles() {
return openingHelpFiles != null ? openingHelpFiles : false;
}
@@ -304,6 +333,32 @@ public class ExplorerPerspectiveConfigPlugin
this.openingHelpFiles = openingHelpFiles;
}
+ /**
+ * Combo values for {@link HelpOpenMode} shown in the Configuration
perspective.
+ *
+ * @param log unused (required by GuiCompositeWidgets)
+ * @param metadataProvider unused (required by GuiCompositeWidgets)
+ * @return translated labels in enum order
+ */
+ public List<String> getHelpOpenModeLabels(
+ ILogChannel log, IHopMetadataProvider metadataProvider) {
+ List<String> labels = new ArrayList<>();
+ for (HelpOpenMode mode : HelpOpenMode.values()) {
+ labels.add(mode.getLabel());
+ }
+ return labels;
+ }
+
+ private static String readComboText(Control control) {
+ if (control instanceof Combo combo) {
+ return combo.getText();
+ }
+ if (control instanceof ComboVar comboVar) {
+ return comboVar.getText();
+ }
+ return "";
+ }
+
public Boolean getActiveFileSelection() {
return activeFileSelection != null ? activeFileSelection : true;
}
diff --git a/ui/src/main/java/org/apache/hop/ui/util/HelpOpenMode.java
b/ui/src/main/java/org/apache/hop/ui/util/HelpOpenMode.java
new file mode 100644
index 0000000000..3915dd9595
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/util/HelpOpenMode.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.util;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import org.apache.hop.core.util.Utils;
+import org.apache.hop.i18n.BaseMessages;
+
+/** Where Hop GUI should open documentation / help URLs. */
+public enum HelpOpenMode {
+ /** System (or RAP) browser. */
+ BROWSER,
+ /** HTML tab in the File Explorer perspective. */
+ TAB,
+ /** Modeless dialog parented to the current context shell. */
+ DIALOG;
+
+ private static final Class<?> PKG = HelpOpenMode.class;
+
+ public String getLabel() {
+ return BaseMessages.getString(PKG, "HelpOpenMode." + name());
+ }
+
+ @JsonValue
+ public String toConfigValue() {
+ return name();
+ }
+
+ /**
+ * Resolve a combo label or enum name. Unknown values fall back to {@link
#BROWSER}.
+ *
+ * @param label translated combo text, enum name, or {@code null}
+ * @return matching mode, never {@code null}
+ */
+ public static HelpOpenMode fromLabel(String label) {
+ if (Utils.isEmpty(label)) {
+ return BROWSER;
+ }
+ String trimmed = label.trim();
+ for (HelpOpenMode mode : values()) {
+ if (mode.name().equalsIgnoreCase(trimmed) ||
mode.getLabel().equalsIgnoreCase(trimmed)) {
+ return mode;
+ }
+ }
+ return BROWSER;
+ }
+
+ /**
+ * Parse a hop-config / CLI value. Accepts enum names and the legacy boolean
{@code true} (tab
+ * mode). Unknown values fall back to {@link #BROWSER}.
+ *
+ * @param value stored string or {@code null}
+ * @return matching mode, never {@code null}
+ */
+ @JsonCreator
+ public static HelpOpenMode fromConfigValue(String value) {
+ if (Utils.isEmpty(value)) {
+ return BROWSER;
+ }
+ String trimmed = value.trim();
+ if ("true".equalsIgnoreCase(trimmed)) {
+ return TAB;
+ }
+ if ("false".equalsIgnoreCase(trimmed)) {
+ return BROWSER;
+ }
+ return fromLabel(trimmed);
+ }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/util/HelpUtils.java
b/ui/src/main/java/org/apache/hop/ui/util/HelpUtils.java
index 72cacfe5d0..8c5e1f0ee2 100644
--- a/ui/src/main/java/org/apache/hop/ui/util/HelpUtils.java
+++ b/ui/src/main/java/org/apache/hop/ui/util/HelpUtils.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.core.dialog.ErrorDialog;
import org.apache.hop.ui.core.dialog.MessageBox;
+import org.apache.hop.ui.core.dialog.ShowHelpDialog;
import org.apache.hop.ui.core.gui.GuiResource;
import org.apache.hop.ui.hopgui.HopGui;
import org.apache.hop.ui.hopgui.file.HopFileTypeRegistry;
@@ -57,15 +58,7 @@ public class HelpUtils {
public static Button createHelpButton(final Composite parent, final String
url) {
Button button = newButton(parent);
- button.addListener(
- SWT.Selection,
- e -> {
- try {
- EnvironmentUtils.getInstance().openUrl(url);
- } catch (Exception ex) {
- new ErrorDialog(parent.getShell(), "Error", "Error opening URL",
ex);
- }
- });
+ button.addListener(SWT.Selection, e -> openHelp(parent.getShell(), url));
return button;
}
@@ -96,17 +89,7 @@ public class HelpUtils {
return;
}
if (isPluginDocumented(plugin)) {
- try {
- String originalUrl = getDocUrl(plugin.getDocumentationUrl());
- String trackedUrl = appendUtmParameters(originalUrl);
- if
(ExplorerPerspectiveConfigSingleton.getConfig().isOpeningHelpFiles()) {
- openHelpInTab(trackedUrl);
- } else {
- EnvironmentUtils.getInstance().openUrl(trackedUrl);
- }
- } catch (Exception ex) {
- new ErrorDialog(shell, "Error", "Error opening URL", ex);
- }
+ openHelp(shell, getDocUrl(plugin.getDocumentationUrl()));
} else {
MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
String msg = "";
@@ -127,12 +110,54 @@ public class HelpUtils {
}
}
+ /**
+ * Open a documentation URL using the configured {@link HelpOpenMode}.
+ *
+ * @param shell context shell (transform/action dialog or main window)
+ * @param url documentation URL
+ */
+ public static void openHelp(Shell shell, String url) {
+ if (Utils.isEmpty(url)) {
+ return;
+ }
+ try {
+ openTrackedUrl(shell, appendUtmParameters(url));
+ } catch (Exception ex) {
+ Shell errorShell = shell != null ? shell :
HopGui.getInstance().getShell();
+ new ErrorDialog(errorShell, "Error", "Error opening URL", ex);
+ }
+ }
+
+ static void openTrackedUrl(Shell shell, String trackedUrl) throws
HopException {
+ HelpOpenMode mode = currentOpenMode();
+ switch (mode) {
+ case TAB:
+ openHelpInTab(trackedUrl);
+ break;
+ case DIALOG:
+ openHelpInDialog(shell, trackedUrl);
+ break;
+ case BROWSER:
+ default:
+ EnvironmentUtils.getInstance().openUrl(trackedUrl);
+ break;
+ }
+ }
+
+ static HelpOpenMode currentOpenMode() {
+ try {
+ return ExplorerPerspectiveConfigSingleton.getConfig().getHelpOpenMode();
+ } catch (Exception e) {
+ return HelpOpenMode.BROWSER;
+ }
+ }
+
/**
* Add analytics tracking parameters for help-button <code>
* mtm_campaign=hopgui&mtm_source=help_btn&mtm_kwd=write to log
* </code>
*/
- private static String appendUtmParameters(String url) {
+ static String appendUtmParameters(String url) {
if (url == null || url.isEmpty()) {
return url;
}
@@ -168,4 +193,41 @@ public class HelpUtils {
// Fallback
EnvironmentUtils.getInstance().openUrl(url);
}
+
+ private static void openHelpInDialog(Shell shell, String url) throws
HopException {
+ Shell parent = resolveParentShell(shell);
+ if (parent == null) {
+ EnvironmentUtils.getInstance().openUrl(url);
+ return;
+ }
+
+ Object existing = parent.getData(ShowHelpDialog.SHELL_DATA_KEY);
+ if (existing instanceof ShowHelpDialog dialog && !dialog.isDisposed()) {
+ dialog.setUrl(url);
+ dialog.forceActive();
+ return;
+ }
+
+ try {
+ ShowHelpDialog dialog = new ShowHelpDialog(parent, url);
+ dialog.open();
+ parent.setData(ShowHelpDialog.SHELL_DATA_KEY, dialog);
+ parent.addDisposeListener(e ->
parent.setData(ShowHelpDialog.SHELL_DATA_KEY, null));
+ } catch (Exception ex) {
+ parent.setData(ShowHelpDialog.SHELL_DATA_KEY, null);
+ new ErrorDialog(parent, "Error", "Error opening help dialog", ex);
+ EnvironmentUtils.getInstance().openUrl(url);
+ }
+ }
+
+ private static Shell resolveParentShell(Shell shell) {
+ if (shell != null && !shell.isDisposed()) {
+ return shell;
+ }
+ HopGui hopGui = HopGui.getInstance();
+ if (hopGui != null && hopGui.getShell() != null &&
!hopGui.getShell().isDisposed()) {
+ return hopGui.getShell();
+ }
+ return null;
+ }
}
diff --git
a/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
b/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
index a9fa08b436..30c6e7ab85 100644
---
a/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
+++
b/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
@@ -55,6 +55,7 @@ HopGui.Documentation.Hop.Title=Hop Documentation
HopGui.Documentation.Tooltip.Back=Show previous page
HopGui.Documentation.Tooltip.Forward=Show next page
HopGui.Documentation.Tooltip.Home=Go to Hop Help home page
+HopGui.Documentation.Tooltip.OpenExternal=Open this page in the system browser
HopGui.Documentation.Tooltip.Print=Print page
HopGui.Documentation.Tooltip.Refresh=Refresh page
HopGui.ErrorDialog.ErrorEditingAction.Message=Error editing action
diff --git
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_en_US.properties
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_en_US.properties
index 8147d0c3a2..4f2f67e69a 100644
---
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_en_US.properties
+++
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_en_US.properties
@@ -22,8 +22,8 @@ ExplorerPerspectiveConfig.LazyLoading.Tooltip=The initial
depth to load not lazi
ExplorerPerspectiveConfig.FileExplorerVisible.Label=Show file explorer panel
by default
ExplorerPerspectiveConfig.FileExplorerVisible.Tooltip=When enabled, the file
explorer panel (project tree) is shown by default when opening the explorer
perspective
-ExplorerPerspectiveConfig.OpenHelpFiles.Label=Open help files in Hop GUI tabs
instead of external browser
-ExplorerPerspectiveConfig.OpenHelpFiles.Tooltip=When checked, help links will
open as a new tab within the Hop GUI using the internal HTML viewer. Unchecked
will open the system default browser.
+ExplorerPerspectiveConfig.HelpOpenMode.Label=Open help pages in
+ExplorerPerspectiveConfig.HelpOpenMode.Tooltip=Choose where documentation
opens: the system browser, a tab in the File Explorer perspective, or a dialog
on top of the current window.
ExplorerPerspectiveConfig.ActiveFileSelection.Label=Select active file in tree
automatically
ExplorerPerspectiveConfig.ActiveFileSelection.Tooltip=Automatically select the
active tab file in the file explorer tree on the left hand side when it is
shown in a tab.
diff --git
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_fr_FR.properties
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_fr_FR.properties
index c0feb83e1d..6de04f0bb0 100644
---
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_fr_FR.properties
+++
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_fr_FR.properties
@@ -20,9 +20,9 @@
ExplorerPerspectiveConfig.FileSize.Label=Taille maximale des fichiers \u00E0
charger
ExplorerPerspectiveConfig.FileExplorerVisible.Label=Afficher le panneau de
l''explorateur de fichiers par d\u00E9faut
ExplorerPerspectiveConfig.FileExplorerVisible.Tooltip=Lorsque cette option est
activ\u00E9e, le panneau de l'explorateur de fichiers (arborescence du projet)
s'affiche par d\u00E9faut \u00E0 l'ouverture de la perspective
-ExplorerPerspectiveConfig.OpenHelpFiles.Label=Ouvrir l''aide dans Hop
plut\u00F4t que dans un navigateur externe
+ExplorerPerspectiveConfig.HelpOpenMode.Label=Ouvrir l''aide dans
ExplorerPerspectiveConfig.FileSize.Tooltip=Taille maximale des fichiers \u00E0
charger
-ExplorerPerspectiveConfig.OpenHelpFiles.Tooltip=Lorsque cette option est
coch\u00E9e, l''aide s''ouvrent dans un nouvel onglet de Hop \u00E0 l''aide de
la visionneuse HTML interne., sinon le navigateur par d\u00E9faut du
syst\u00E8me est utilis\u00E9.
+ExplorerPerspectiveConfig.HelpOpenMode.Tooltip=Choisissez o\u00F9 s''ouvre la
documentation : le navigateur du syst\u00E8me, un onglet de l''explorateur de
fichiers, ou une fen\u00EAtre au-dessus du contexte actuel.
ExplorerPerspectiveConfig.ActiveFileSelection.Label=S\u00E9lectionner
automatiquement le fichier actif dans l''arborescence
ExplorerPerspectiveConfig.ActiveFileSelection.Tooltip=S\u00E9lectionner
automatiquement le fichier de l''onglet actif dans l''explorateur de fichiers
de gauche lorsqu''il est affich\u00E9.
diff --git
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_pt_BR.properties
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_pt_BR.properties
index 2fe4eef0ca..c6a437d037 100644
---
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_pt_BR.properties
+++
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/explorer/config/messages/messages_pt_BR.properties
@@ -23,8 +23,8 @@ ExplorerPerspectiveConfig.LazyLoading.Label=A profundidade
inicial para carregar
ExplorerPerspectiveConfig.LazyLoading.Tooltip=A profundidade inicial para
carregar
ExplorerPerspectiveConfig.FileExplorerVisible.Label=Mostrar painel explorador
de arquivos por padrão
ExplorerPerspectiveConfig.FileExplorerVisible.Tooltip=Quando habilitado, o
painel explorador de arquivos (árvore do projeto) é mostrado por padrão ao
abrir a perspectiva exploradora
-ExplorerPerspectiveConfig.OpenHelpFiles.Label=Abra arquivos de ajuda nas abas
do Hop GUI em vez de navegador externo
-ExplorerPerspectiveConfig.OpenHelpFiles.Tooltip=Quando marcada, links de ajuda
abrirão como uma nova guia dentro da GUI Hop usando o visualizador HTML
interno. Sem controle, abrirá o navegador padrão do sistema.
+ExplorerPerspectiveConfig.HelpOpenMode.Label=Abrir p\u00E1ginas de ajuda em
+ExplorerPerspectiveConfig.HelpOpenMode.Tooltip=Escolha onde a
documenta\u00E7\u00E3o abre: o navegador do sistema, uma aba na perspectiva
Explorador de arquivos, ou uma janela sobre o contexto atual.
ExplorerPerspectiveConfig.ActiveFileSelection.Label=Selecionar automaticamente
arquivo ativo na \u00E1rvore
ExplorerPerspectiveConfig.ActiveFileSelection.Tooltip=Selecionar
automaticamente o arquivo da guia ativa na \u00E1rvore do explorador de
arquivos no lado esquerdo.
diff --git
a/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_en_US.properties
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_en_US.properties
new file mode 100644
index 0000000000..d0c4cbf365
--- /dev/null
+++
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_en_US.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+HelpOpenMode.BROWSER=External browser
+HelpOpenMode.TAB=Hop GUI tab
+HelpOpenMode.DIALOG=Dialog window
diff --git
a/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_fr_FR.properties
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_fr_FR.properties
new file mode 100644
index 0000000000..bac4e1bf2d
--- /dev/null
+++
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_fr_FR.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+HelpOpenMode.BROWSER=Navigateur externe
+HelpOpenMode.TAB=Onglet Hop GUI
+HelpOpenMode.DIALOG=Fen\u00EAtre de dialogue
diff --git
a/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_pt_BR.properties
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_pt_BR.properties
new file mode 100644
index 0000000000..4dd24e1ce7
--- /dev/null
+++
b/ui/src/main/resources/org/apache/hop/ui/util/messages/messages_pt_BR.properties
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+HelpOpenMode.BROWSER=Navegador externo
+HelpOpenMode.TAB=Aba do Hop GUI
+HelpOpenMode.DIALOG=Janela de di\u00E1logo
diff --git
a/ui/src/test/java/org/apache/hop/ui/core/dialog/ShowHelpDialogTest.java
b/ui/src/test/java/org/apache/hop/ui/core/dialog/ShowHelpDialogTest.java
new file mode 100644
index 0000000000..5b526ce409
--- /dev/null
+++ b/ui/src/test/java/org/apache/hop/ui/core/dialog/ShowHelpDialogTest.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.core.dialog;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.eclipse.swt.SWT;
+import org.junit.jupiter.api.Test;
+
+class ShowHelpDialogTest {
+
+ @Test
+ void helpDialogStyleIsModelessAndResizable() {
+ int style = ShowHelpDialog.helpDialogStyle();
+ assertEquals(0, style & SWT.APPLICATION_MODAL);
+ assertEquals(0, style & SWT.PRIMARY_MODAL);
+ assertNotEquals(0, style & SWT.RESIZE);
+ }
+}
diff --git
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigTest.java
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigTest.java
new file mode 100644
index 0000000000..b091ffdbfe
--- /dev/null
+++
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/explorer/config/ExplorerPerspectiveConfigTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui.perspective.explorer.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.hop.core.json.HopJson;
+import org.apache.hop.ui.util.HelpOpenMode;
+import org.junit.jupiter.api.Test;
+
+class ExplorerPerspectiveConfigTest {
+
+ private final ObjectMapper mapper = HopJson.newMapper();
+
+ @Test
+ void defaultModeIsBrowser() {
+ assertEquals(HelpOpenMode.BROWSER, new
ExplorerPerspectiveConfig().getHelpOpenMode());
+ }
+
+ @Test
+ void migratesLegacyOpeningHelpFilesTrueToTab() throws Exception {
+ ExplorerPerspectiveConfig config =
+ mapper.readValue(
+ "{\"lazyLoadingDepth\":\"0\",\"openingHelpFiles\":true}",
+ ExplorerPerspectiveConfig.class);
+ assertEquals(HelpOpenMode.TAB, config.getHelpOpenMode());
+ }
+
+ @Test
+ void migratesLegacyOpeningHelpFilesFalseToBrowser() throws Exception {
+ ExplorerPerspectiveConfig config =
+ mapper.readValue("{\"openingHelpFiles\":false}",
ExplorerPerspectiveConfig.class);
+ assertEquals(HelpOpenMode.BROWSER, config.getHelpOpenMode());
+ }
+
+ @Test
+ void missingLegacyFlagDefaultsToBrowser() throws Exception {
+ ExplorerPerspectiveConfig config =
+ mapper.readValue("{\"lazyLoadingDepth\":\"2\"}",
ExplorerPerspectiveConfig.class);
+ assertEquals(HelpOpenMode.BROWSER, config.getHelpOpenMode());
+ }
+
+ @Test
+ void newHelpOpenModeWinsOverLegacyBoolean() throws Exception {
+ ExplorerPerspectiveConfig config =
+ mapper.readValue(
+ "{\"helpOpenMode\":\"DIALOG\",\"openingHelpFiles\":true}",
+ ExplorerPerspectiveConfig.class);
+ assertEquals(HelpOpenMode.DIALOG, config.getHelpOpenMode());
+ }
+
+ @Test
+ void serializesHelpOpenModeAndOmitsLegacyBoolean() throws Exception {
+ ExplorerPerspectiveConfig config = new ExplorerPerspectiveConfig();
+ config.setHelpOpenMode(HelpOpenMode.DIALOG);
+ String json = mapper.writeValueAsString(config);
+ assertEquals(
+ HelpOpenMode.DIALOG,
+ mapper.readValue(json,
ExplorerPerspectiveConfig.class).getHelpOpenMode());
+ assertFalse(json.contains("openingHelpFiles"));
+ assertFalse(json.contains("\"helpOpenMode\":\"true\""));
+ }
+
+ @Test
+ void copyConstructorCopiesHelpOpenMode() {
+ ExplorerPerspectiveConfig original = new ExplorerPerspectiveConfig();
+ original.setHelpOpenMode(HelpOpenMode.TAB);
+ assertEquals(HelpOpenMode.TAB, new
ExplorerPerspectiveConfig(original).getHelpOpenMode());
+ }
+}
diff --git a/ui/src/test/java/org/apache/hop/ui/util/HelpOpenModeTest.java
b/ui/src/test/java/org/apache/hop/ui/util/HelpOpenModeTest.java
new file mode 100644
index 0000000000..74c9aba0b6
--- /dev/null
+++ b/ui/src/test/java/org/apache/hop/ui/util/HelpOpenModeTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class HelpOpenModeTest {
+
+ @Test
+ void fromConfigValueAcceptsEnumNamesAndLegacyBoolean() {
+ assertEquals(HelpOpenMode.BROWSER, HelpOpenMode.fromConfigValue(null));
+ assertEquals(HelpOpenMode.BROWSER, HelpOpenMode.fromConfigValue(""));
+ assertEquals(HelpOpenMode.BROWSER,
HelpOpenMode.fromConfigValue("BROWSER"));
+ assertEquals(HelpOpenMode.TAB, HelpOpenMode.fromConfigValue("tab"));
+ assertEquals(HelpOpenMode.DIALOG, HelpOpenMode.fromConfigValue("Dialog"));
+ assertEquals(HelpOpenMode.TAB, HelpOpenMode.fromConfigValue("true"));
+ assertEquals(HelpOpenMode.BROWSER, HelpOpenMode.fromConfigValue("false"));
+ assertEquals(HelpOpenMode.BROWSER,
HelpOpenMode.fromConfigValue("not-a-mode"));
+ }
+
+ @Test
+ void fromLabelAcceptsEnumNameAndTranslatedLabel() {
+ assertEquals(HelpOpenMode.BROWSER, HelpOpenMode.fromLabel(null));
+ assertEquals(HelpOpenMode.DIALOG, HelpOpenMode.fromLabel("DIALOG"));
+ assertEquals(HelpOpenMode.TAB,
HelpOpenMode.fromLabel(HelpOpenMode.TAB.getLabel()));
+ assertEquals(HelpOpenMode.DIALOG,
HelpOpenMode.fromLabel(HelpOpenMode.DIALOG.getLabel()));
+ }
+
+ @Test
+ void toConfigValueIsEnumName() {
+ assertEquals("BROWSER", HelpOpenMode.BROWSER.toConfigValue());
+ assertEquals("TAB", HelpOpenMode.TAB.toConfigValue());
+ assertEquals("DIALOG", HelpOpenMode.DIALOG.toConfigValue());
+ }
+}
diff --git a/ui/src/test/java/org/apache/hop/ui/util/HelpUtilsTest.java
b/ui/src/test/java/org/apache/hop/ui/util/HelpUtilsTest.java
new file mode 100644
index 0000000000..c175c67dd4
--- /dev/null
+++ b/ui/src/test/java/org/apache/hop/ui/util/HelpUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class HelpUtilsTest {
+
+ @Test
+ void appendUtmParametersAddsQueryWhenMissing() {
+ String tracked =
HelpUtils.appendUtmParameters("https://hop.apache.org/manual/latest/x.html");
+
assertTrue(tracked.startsWith("https://hop.apache.org/manual/latest/x.html?"));
+ assertTrue(tracked.contains("mtm_campaign="));
+ assertTrue(tracked.contains("mtm_source="));
+ assertFalse(tracked.contains("?mtm_campaign") &&
tracked.contains("&mtm_campaign="));
+ }
+
+ @Test
+ void appendUtmParametersUsesAmpersandWhenQueryExists() {
+ String tracked =
+
HelpUtils.appendUtmParameters("https://hop.apache.org/manual/latest/x.html?foo=bar");
+ assertTrue(tracked.contains("?foo=bar&mtm_campaign="));
+ }
+
+ @Test
+ void appendUtmParametersPassesThroughBlank() {
+ assertEquals("", HelpUtils.appendUtmParameters(""));
+ assertEquals(null, HelpUtils.appendUtmParameters(null));
+ }
+}