This is an automated email from the ASF dual-hosted git repository.

mattcasters 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 0d1c875eaf Polishing the database exploration dialog #5376 (#8353)
0d1c875eaf is described below

commit 0d1c875eaffbf89f2c680c410335c38d4df6763a
Author: Nicolas Adment <[email protected]>
AuthorDate: Tue Sep 15 19:03:57 2026 +0200

    Polishing the database exploration dialog #5376 (#8353)
    
    Co-authored-by: mattcasters <[email protected]>
---
 .../hop/core/database/DatabaseMetaInformation.java |   35 +-
 .../hop/core/database/DatabasePluginType.java      |    2 +-
 .../database/DatabaseTreeNode.java                 |   32 +-
 .../hop/ui/core/database/DatabaseTreeUtil.java     |  100 ++
 .../database/dialog/DatabaseExplorerDialog.java    | 1516 +++++++++-----------
 .../dialog/GetDatabaseInfoProgressDialog.java      |    4 +-
 .../hop/ui/core/database/dialog/SqlEditor.java     |   96 +-
 .../org/apache/hop/ui/core/gui/GuiResource.java    |    4 +
 .../perspective/database/DatabaseTableInfoTab.java |    1 +
 .../perspective/database/DatabaseWorkbench.java    |   59 +-
 .../dialog/messages/messages_de_DE.properties      |   14 +-
 .../dialog/messages/messages_en_US.properties      |   23 +-
 .../dialog/messages/messages_es_AR.properties      |   14 +-
 .../dialog/messages/messages_es_ES.properties      |   14 +-
 .../dialog/messages/messages_fr_FR.properties      |   14 +-
 .../dialog/messages/messages_it_IT.properties      |   14 +-
 .../dialog/messages/messages_ja_JP.properties      |    2 +-
 .../dialog/messages/messages_ko_KR.properties      |    4 +-
 .../dialog/messages/messages_pt_BR.properties      |    8 +-
 .../dialog/messages/messages_zh_CN.properties      |    2 +-
 ui/src/main/resources/ui/images/layout.svg         |    3 +
 ui/src/main/resources/ui/images/regex-disabled.svg |    3 +
 ui/src/main/resources/ui/images/regex.svg          |   23 +-
 ui/src/main/resources/ui/images/run-all.svg        |    8 +-
 .../perspective/database/DatabaseTreeNodeTest.java |   22 +-
 25 files changed, 941 insertions(+), 1076 deletions(-)

diff --git 
a/core/src/main/java/org/apache/hop/core/database/DatabaseMetaInformation.java 
b/core/src/main/java/org/apache/hop/core/database/DatabaseMetaInformation.java
index 2845372ee6..9c758caec1 100644
--- 
a/core/src/main/java/org/apache/hop/core/database/DatabaseMetaInformation.java
+++ 
b/core/src/main/java/org/apache/hop/core/database/DatabaseMetaInformation.java
@@ -23,6 +23,7 @@ import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import lombok.Getter;
 import lombok.Setter;
@@ -70,9 +71,8 @@ public class DatabaseMetaInformation {
     }
 
     monitor.beginTask(BaseMessages.getString(PKG, 
"DatabaseMeta.Info.GettingInfoFromDb"), 8);
-    Database db = new Database(parentLoggingObject, variables, databaseMeta);
 
-    try {
+    try (Database db = new Database(parentLoggingObject, variables, 
databaseMeta)) {
       monitor.subTask(BaseMessages.getString(PKG, 
"DatabaseMeta.Info.ConnectingDb"));
       db.connect();
       monitor.worked(1);
@@ -93,18 +93,17 @@ public class DatabaseMetaInformation {
       monitor.subTask(BaseMessages.getString(PKG, 
"DatabaseMeta.Info.GettingInfo"));
       Map<String, String> connectionExtraOptions = 
databaseMeta.getExtraOptions();
       if (databaseMeta.supportsCatalogs() && 
dbmd.supportsCatalogsInTableDefinitions()) {
-        ArrayList<Catalog> catalogList = new ArrayList<>();
+        List<Catalog> catalogs = new ArrayList<>();
 
         String catalogFilterKey = databaseMeta.getPluginId() + "." + 
FILTER_CATALOG_LIST;
         if ((connectionExtraOptions != null)
             && connectionExtraOptions.containsKey(catalogFilterKey)) {
-          String catsFilterCommaList = 
connectionExtraOptions.get(catalogFilterKey);
-          String[] catsFilterArray = catsFilterCommaList.split(",");
-          for (String s : catsFilterArray) {
-            catalogList.add(new Catalog(s.trim()));
+          String catalogsFilterCommaList = 
connectionExtraOptions.get(catalogFilterKey);
+          for (String name : catalogsFilterCommaList.split(",")) {
+            catalogs.add(new Catalog(name.trim()));
           }
         }
-        if (catalogList.isEmpty()) {
+        if (catalogs.isEmpty()) {
           ResultSet catalogResultSet = dbmd.getCatalogs();
 
           // Grab all the catalog names and put them in an array list
@@ -113,7 +112,7 @@ public class DatabaseMetaInformation {
           //
           while (catalogResultSet != null && catalogResultSet.next()) {
             String catalogName = catalogResultSet.getString(1);
-            catalogList.add(new Catalog(catalogName));
+            catalogs.add(new Catalog(catalogName));
           }
 
           // Close the catalogs resultset immediately
@@ -123,7 +122,7 @@ public class DatabaseMetaInformation {
 
         // Now loop over the catalogs...
         //
-        for (Catalog catalog : catalogList) {
+        for (Catalog catalog : catalogs) {
           ArrayList<String> catalogTables = new ArrayList<>();
 
           try {
@@ -149,11 +148,11 @@ public class DatabaseMetaInformation {
 
           // Save the list of tables in the catalog (can be empty)
           //
-          catalog.setItems(catalogTables.toArray(new 
String[catalogTables.size()]));
+          catalog.setItems(catalogTables.toArray(new String[0]));
         }
 
         // Save for later...
-        setCatalogs(catalogList.toArray(new Catalog[catalogList.size()]));
+        setCatalogs(catalogs.toArray(new Catalog[0]));
       }
       monitor.worked(1);
       if (monitor.isCanceled()) {
@@ -164,7 +163,7 @@ public class DatabaseMetaInformation {
       //
       monitor.subTask(BaseMessages.getString(PKG, 
"DatabaseMeta.Info.GettingSchemaInfo"));
       if (databaseMeta.supportsSchemas() && 
dbmd.supportsSchemasInTableDefinitions()) {
-        ArrayList<Schema> schemaList = new ArrayList<>();
+        List<Schema> schemaList = new ArrayList<>();
         try {
           String schemaFilterKey = databaseMeta.getPluginId() + "." + 
FILTER_SCHEMA_LIST;
           if ((connectionExtraOptions != null)
@@ -204,7 +203,7 @@ public class DatabaseMetaInformation {
             }
           }
           for (Schema schema : schemaList) {
-            ArrayList<String> schemaTables = new ArrayList<>();
+            List<String> schemaTables = new ArrayList<>();
 
             try {
               ResultSet schemaTablesResultSet = null;
@@ -237,7 +236,7 @@ public class DatabaseMetaInformation {
               // Just ignore it!
             }
 
-            schema.setItems(schemaTables.toArray(new 
String[schemaTables.size()]));
+            schema.setItems(schemaTables.toArray(new String[0]));
 
             if (monitor.isCanceled()) {
               return;
@@ -249,8 +248,7 @@ public class DatabaseMetaInformation {
         }
 
         // Sort the schemas by names
-        Collections.sort(
-            schemaList,
+        schemaList.sort(
             (s1, s2) -> {
               return s1.getSchemaName() == null
                   ? -1
@@ -258,7 +256,7 @@ public class DatabaseMetaInformation {
             });
 
         // Save for later...
-        setSchemas(schemaList.toArray(new Schema[schemaList.size()]));
+        setSchemas(schemaList.toArray(new Schema[0]));
       }
       monitor.worked(1);
       if (monitor.isCanceled()) {
@@ -309,7 +307,6 @@ public class DatabaseMetaInformation {
           BaseMessages.getString(PKG, 
"DatabaseMeta.Error.UnableRetrieveDbInfo"), e);
     } finally {
       monitor.subTask(BaseMessages.getString(PKG, 
"DatabaseMeta.Info.ClosingDbConnection"));
-      db.disconnect();
       monitor.done();
     }
   }
diff --git 
a/core/src/main/java/org/apache/hop/core/database/DatabasePluginType.java 
b/core/src/main/java/org/apache/hop/core/database/DatabasePluginType.java
index 49c736bb19..c8903182ba 100644
--- a/core/src/main/java/org/apache/hop/core/database/DatabasePluginType.java
+++ b/core/src/main/java/org/apache/hop/core/database/DatabasePluginType.java
@@ -22,7 +22,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.plugins.BasePluginType;
 
-/** This class represents the transform plugin type. */
+/** This class represents the database plugin type. */
 public class DatabasePluginType extends BasePluginType<DatabaseMetaPlugin> {
   private static DatabasePluginType pluginType;
 
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNode.java
 b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeNode.java
similarity index 73%
rename from 
ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNode.java
rename to ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeNode.java
index 8a936c9f82..46d100fcd3 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNode.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeNode.java
@@ -15,9 +15,8 @@
  * limitations under the License.
  */
 
-package org.apache.hop.ui.hopgui.perspective.database;
+package org.apache.hop.ui.core.database;
 
-import java.util.Collection;
 import lombok.Getter;
 
 /** Data stored on a connection-tree item. */
@@ -73,33 +72,4 @@ public class DatabaseTreeNode {
   public boolean isTableLike() {
     return kind == Kind.TABLE || kind == Kind.VIEW || kind == Kind.SYNONYM;
   }
-
-  /**
-   * Classify a schema object so views (and synonyms) can use a distinct tree 
icon.
-   *
-   * @param name object name
-   * @param views view names in the same schema (any case)
-   * @param synonyms synonym names in the same schema (any case)
-   */
-  public static Kind kindOf(String name, Collection<String> views, 
Collection<String> synonyms) {
-    if (containsIgnoreCase(views, name)) {
-      return Kind.VIEW;
-    }
-    if (containsIgnoreCase(synonyms, name)) {
-      return Kind.SYNONYM;
-    }
-    return Kind.TABLE;
-  }
-
-  static boolean containsIgnoreCase(Collection<String> names, String name) {
-    if (names == null || name == null) {
-      return false;
-    }
-    for (String candidate : names) {
-      if (name.equalsIgnoreCase(candidate)) {
-        return true;
-      }
-    }
-    return false;
-  }
 }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeUtil.java 
b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeUtil.java
new file mode 100644
index 0000000000..69cde818d7
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/core/database/DatabaseTreeUtil.java
@@ -0,0 +1,100 @@
+/*
+ * 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.database;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.apache.hop.ui.core.database.DatabaseTreeNode.Kind;
+import org.apache.hop.ui.core.gui.GuiResource;
+import org.eclipse.swt.graphics.Image;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Utility class for sharing functions between the Database perspective and 
the Database Explorer
+ * dialog.
+ */
+public class DatabaseTreeUtil {
+
+  private DatabaseTreeUtil() {
+    // Utility class
+  }
+
+  /**
+   * Classify a schema object so views (and synonyms) can use a distinct tree 
icon.
+   *
+   * @param name object name
+   * @param views view names in the same schema (any case)
+   * @param synonyms synonym names in the same schema (any case)
+   */
+  public static Kind kindOf(String name, Collection<String> views, 
Collection<String> synonyms) {
+    if (containsIgnoreCase(views, name)) {
+      return Kind.VIEW;
+    }
+    if (containsIgnoreCase(synonyms, name)) {
+      return Kind.SYNONYM;
+    }
+    return Kind.TABLE;
+  }
+
+  public static boolean containsIgnoreCase(
+      @Nullable Collection<String> names, @Nullable String name) {
+    if (names == null || name == null) {
+      return false;
+    }
+    for (String candidate : names) {
+      if (name.equalsIgnoreCase(candidate)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static Collection<String> namesForSchema(
+      Map<String, Collection<String>> map, String schemaName) {
+    if (map == null || map.isEmpty()) {
+      return List.of();
+    }
+    if (schemaName != null) {
+      Collection<String> exact = map.get(schemaName);
+      if (exact != null) {
+        return exact;
+      }
+      for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
+        if (schemaName.equalsIgnoreCase(entry.getKey()) && entry.getValue() != 
null) {
+          return entry.getValue();
+        }
+      }
+    }
+    Collection<String> empty = map.get("");
+    if (empty != null) {
+      return empty;
+    }
+    Collection<String> missing = map.get(null);
+    return missing != null ? missing : List.of();
+  }
+
+  public static Image imageFor(DatabaseTreeNode.Kind kind) {
+    GuiResource resources = GuiResource.getInstance();
+    return switch (kind) {
+      case VIEW -> resources.getImageView();
+      case SYNONYM -> resources.getImageSynonym();
+      default -> resources.getImageTable();
+    };
+  }
+}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
 
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
index 60827710af..76ad966459 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/DatabaseExplorerDialog.java
@@ -18,12 +18,13 @@
 package org.apache.hop.ui.core.database.dialog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
-import java.util.Objects;
 import lombok.Getter;
 import lombok.Setter;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.hop.core.Const;
+import org.apache.hop.core.DbCache;
 import org.apache.hop.core.Props;
 import org.apache.hop.core.database.Catalog;
 import org.apache.hop.core.database.Database;
@@ -32,6 +33,9 @@ import org.apache.hop.core.database.DatabaseMetaInformation;
 import org.apache.hop.core.database.Schema;
 import org.apache.hop.core.exception.HopDatabaseException;
 import org.apache.hop.core.gui.plugin.GuiPlugin;
+import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
+import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
+import org.apache.hop.core.gui.plugin.menu.GuiMenuElement;
 import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElement;
 import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElementType;
 import org.apache.hop.core.logging.ILogChannel;
@@ -39,43 +43,45 @@ import org.apache.hop.core.logging.ILoggingObject;
 import org.apache.hop.core.logging.LogChannel;
 import org.apache.hop.core.logging.LoggingObject;
 import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.search.SearchMatcher;
+import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
-import org.apache.hop.ui.core.ConstUi;
+import org.apache.hop.ui.core.FormDataBuilder;
 import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.database.DatabaseTreeNode;
+import org.apache.hop.ui.core.database.DatabaseTreeUtil;
+import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.dialog.ShowRowsDialog;
 import org.apache.hop.ui.core.dialog.TransformFieldsDialog;
+import org.apache.hop.ui.core.gui.GuiMenuWidgets;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
 import org.apache.hop.ui.core.gui.IToolbarContainer;
 import org.apache.hop.ui.core.gui.WindowProperty;
 import org.apache.hop.ui.core.widget.HopTree;
 import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.HopGuiKeyHandler;
 import org.apache.hop.ui.hopgui.ToolbarFacade;
-import org.apache.hop.ui.hopgui.perspective.database.DatabaseWorkbenchDialog;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FormAttachment;
-import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Dialog;
-import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Menu;
-import org.eclipse.swt.widgets.MenuItem;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.swt.widgets.Tree;
 import org.eclipse.swt.widgets.TreeItem;
+import org.jspecify.annotations.Nullable;
 
 /**
  * This dialog represents an explorer type of interface on a given database 
connection. It shows the
@@ -85,31 +91,39 @@ import org.eclipse.swt.widgets.TreeItem;
 @GuiPlugin
 public class DatabaseExplorerDialog extends Dialog {
   private static final Class<?> PKG = DatabaseExplorerDialog.class;
-
+  public static final String GUI_PLUGIN_CONTEXT_MENU_PARENT_ID =
+      "DatabaseExplorerDialog-ContextMenu";
+  public static final String GUI_PLUGIN_SEARCHBAR_PARENT_ID = 
"DatabaseExplorerDialog-SearchBar";
   public static final String GUI_PLUGIN_TOOLBAR_PARENT_ID = 
"DatabaseExplorerDialog-Toolbar";
-  public static final String TOOLBAR_ITEM_EXPAND_ALL = 
"DatabaseExplorer-ToolBar-10100-ExpandAll";
-  public static final String TOOLBAR_ITEM_COLLAPSE_ALL =
-      "DatabaseExplorer-ToolBar-10200-CollapseAll";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100 =
-      "DatabaseExplorerDialog.Menu.Preview100";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N =
-      "DatabaseExplorerDialog.Menu.PreviewN";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE =
-      "DatabaseExplorerDialog.Menu.ShowSize";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT =
-      "DatabaseExplorerDialog.Menu.ShowLayout";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL =
-      "DatabaseExplorerDialog.Menu.OpenSQL";
-  public static final String CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL =
-      "DatabaseExplorerDialog.Menu.GenDDL";
-  public static final String 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN =
-      "DatabaseExplorerDialog.Menu.GenDDLOtherConn";
 
-  private final ILogChannel log;
-  private final PropsUi props;
-  private DatabaseMeta dbMeta;
-  private final IVariables variables;
-  private final ILoggingObject loggingObject;
+  public static final String TOOLBAR_ITEM_REGEX = 
"10000-DatabaseExplorerDialog-ToolBar-Regex";
+  public static final String TOOLBAR_ITEM_REFRESH = 
"DatabaseExplorerDialog-ToolBar-10000-Refresh";
+  public static final String TOOLBAR_ITEM_EXPAND_ALL =
+      "DatabaseExplorerDialog-ToolBar-10010-ExpandAll";
+  public static final String TOOLBAR_ITEM_COLLAPSE_ALL =
+      "DatabaseExplorerDialog-ToolBar-10020-CollapseAll";
+  public static final String TOOLBAR_ITEM_PREVIEW = 
"DatabaseExplorerDialog-Toolbar-10050-Preview";
+  public static final String TOOLBAR_ITEM_SHOW_LAYOUT =
+      "DatabaseExplorerDialog-Toolbar-10060-ShowLayout";
+  public static final String TOOLBAR_ITEM_SQL_SELECT =
+      "DatabaseExplorerDialog-Toolbar-10070-SqlSelect";
+
+  public static final String CONTEXT_MENU_PREVIEW_100 =
+      "DatabaseExplorerDialog-ContextMenu-10010-Preview100";
+  public static final String CONTEXT_MENU_PREVIEW_N =
+      "DatabaseExplorerDialog-ContextMenu-10020-PreviewN";
+  public static final String CONTEXT_MENU_SHOW_SIZE =
+      "DatabaseExplorerDialog-ContextMenu-10030-ShowSize";
+  public static final String CONTEXT_MENU_SHOW_LAYOUT =
+      "DatabaseExplorerDialog-ContextMenu-10040-ShowLayout";
+  public static final String CONTEXT_MENU_DDL =
+      "DatabaseExplorerDialog-ContextMenu-10050-GenerateDdl";
+  public static final String CONTEXT_MENU_DDL_OTHER =
+      "DatabaseExplorerDialog-ContextMenu-10060-GenerateDdlOther";
+  public static final String CONTEXT_MENU_SQL_SELECT =
+      "DatabaseExplorerDialog-ContextMenu-10070-SqlSelect";
+  public static final String CONTEXT_MENU_SQL_TRUNCATE =
+      "DatabaseExplorerDialog-ContextMenu-10080-SqlTruncate";
 
   private static final String STRING_CATALOG =
       BaseMessages.getString(PKG, "DatabaseExplorerDialog.Catalogs.Label");
@@ -122,107 +136,97 @@ public class DatabaseExplorerDialog extends Dialog {
   private static final String STRING_SYNONYMS =
       BaseMessages.getString(PKG, "DatabaseExplorerDialog.Synonyms.Label");
 
-  private final Shell parentShell;
-  private Shell shell;
-  private Tree wTree;
-  private TreeItem tiTree;
-
-  /** Updated on SWT.Expand/Collapse; used to detect native ">" handling vs 
DPI miss. */
-  private long lastTreeExpandCollapseNanos;
+  private static final int FILTER_DEBOUNCE_MS = 250;
 
-  @Getter @Setter private String tableName;
+  /** Debounced search action so we don't rebuild the tree on every keystroke. 
*/
+  private final Runnable filterRunnable = this::updateTree;
 
-  private final boolean justLook;
-  @Getter @Setter private String selectedSchema;
-  @Setter private String selectedTable;
+  private final ILogChannel log;
+  private final DatabaseMeta databaseMeta;
+  private final IVariables variables;
+  private final DbCache dbCache;
+  private final ILoggingObject loggingObject;
   private final List<DatabaseMeta> databases;
-  @Getter @Setter private boolean splitSchemaAndTable;
-  @Getter @Setter private String schemaName;
-  private Composite buttonsComposite;
-  private Button wOk;
-  private Button bPrev;
-  private Button bPrevN;
-  private Button bCount;
-  private Button bShow;
-  private Button bDDL;
-  private Button bDDL2;
-  private Button bSql;
-  private String activeSchemaTable;
-  private Button bTruncate;
-
-  private Control toolBar;
+  private final boolean justLook;
+
+  private Shell shell;
+  private Tree wTree;
+  private TreeItem connectionItem;
+  private Text wSearch;
+  private GuiToolbarWidgets searchBarWidgets;
+  private GuiToolbarWidgets toolBarWidgets;
+  private GuiMenuWidgets menuWidgets;
+  private SearchMatcher filter;
+  private boolean filterUseRegEx;
+  private String selectedSchemaName;
+  private String selectedTableName;
+  @Setter @Getter private boolean splitSchemaAndTable;
+  private DatabaseMetaInformation databaseMetaInformation;
 
   public DatabaseExplorerDialog(
       Shell parentShell,
       int style,
       IVariables variables,
-      DatabaseMeta conn,
+      DatabaseMeta databaseMeta,
       List<DatabaseMeta> databases) {
-    this(parentShell, style, variables, conn, databases, false, true);
+    this(parentShell, style, variables, databaseMeta, databases, false, true);
   }
 
   public DatabaseExplorerDialog(
-      Shell parentShell,
+      Shell parent,
       int style,
       IVariables variables,
-      DatabaseMeta conn,
+      DatabaseMeta databaseMeta,
       List<DatabaseMeta> databases,
       boolean look,
       boolean splitSchemaAndTable) {
-    super(parentShell, style);
-    this.parentShell = parentShell;
-    this.dbMeta = conn;
+    super(parent, style);
+    this.databaseMeta = databaseMeta;
     this.variables = variables;
     this.databases = databases;
     this.justLook = look;
     this.splitSchemaAndTable = splitSchemaAndTable;
     this.loggingObject = new LoggingObject("Database Explorer");
 
-    selectedSchema = null;
-    selectedTable = null;
-
-    props = PropsUi.getInstance();
+    filterUseRegEx = false;
+    selectedSchemaName = null;
+    selectedTableName = null;
     log = new LogChannel("DBExplorer");
+    dbCache = DbCache.getInstance();
   }
 
   public boolean open() {
-    tableName = null;
-
     if (Const.isLinux()) {
       shell =
           new Shell(
-              parentShell,
+              getParent(),
               SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | 
SWT.MIN);
     } else {
-      shell = new Shell(parentShell, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | 
SWT.MIN);
+      shell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | 
SWT.MIN);
     }
     PropsUi.setLook(shell);
     shell.setImage(GuiResource.getInstance().getImageDatabase());
-
-    shell.setText(BaseMessages.getString(PKG, "DatabaseExplorerDialog.Title", 
dbMeta.toString()));
+    // Do not include the connection name in the title (to save/restore the 
dialog box dimensions
+    // once)
+    shell.setText(BaseMessages.getString(PKG, "DatabaseExplorerDialog.Title"));
 
     FormLayout formLayout = new FormLayout();
     formLayout.marginWidth = PropsUi.getFormMargin();
     formLayout.marginHeight = PropsUi.getFormMargin();
-
     shell.setLayout(formLayout);
+    shell.setMinimumSize(200, 400);
 
     int margin = PropsUi.getMargin();
 
     // Main buttons at the bottom
     //
     List<Button> buttons = new ArrayList<>();
-    wOk = new Button(shell, SWT.PUSH);
+    Button wOk = new Button(shell, SWT.PUSH);
     wOk.setText(BaseMessages.getString(PKG, "System.Button.OK"));
     wOk.addListener(SWT.Selection, e -> ok());
     buttons.add(wOk);
     shell.setDefaultButton(wOk);
 
-    Button wRefresh = new Button(shell, SWT.PUSH);
-    wRefresh.setText(BaseMessages.getString(PKG, "System.Button.Refresh"));
-    wRefresh.addListener(SWT.Selection, e -> getData());
-    buttons.add(wRefresh);
-
     if (!justLook) {
       Button wCancel = new Button(shell, SWT.PUSH);
       wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
@@ -231,257 +235,125 @@ public class DatabaseExplorerDialog extends Dialog {
     }
     BaseTransformDialog.positionBottomButtons(shell, buttons.toArray(new 
Button[0]), margin, null);
 
-    // Add a toolbar
-    //
-    IToolbarContainer toolBarContainer =
+    // Create toolbar for search options at the top right
+    IToolbarContainer searchBarContainer =
         ToolbarFacade.createToolbarContainer(shell, SWT.WRAP | SWT.LEFT | 
SWT.HORIZONTAL);
-    toolBar = toolBarContainer.getControl();
-    GuiToolbarWidgets toolBarWidgets = new GuiToolbarWidgets();
+    searchBarWidgets = new GuiToolbarWidgets();
+    searchBarWidgets.registerGuiPluginObject(this);
+    searchBarWidgets.createToolbarWidgets(searchBarContainer, 
GUI_PLUGIN_SEARCHBAR_PARENT_ID);
+    Control searchBar = searchBarContainer.getControl();
+    searchBar.setLayoutData(FormDataBuilder.builder().top().right().build());
+    PropsUi.setLook(searchBar, Props.WIDGET_STYLE_TOOLBAR);
+
+    // Create search/filter text box at the top left
+    wSearch = new Text(shell, SWT.SEARCH | SWT.ICON_SEARCH | SWT.ICON_CANCEL | 
SWT.BORDER);
+    wSearch.setMessage(BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.Search.Placeholder"));
+    
wSearch.setLayoutData(FormDataBuilder.builder().top().left().right(searchBar, 
-margin).build());
+    wSearch.setData(BaseDialog.NO_DEFAULT_HANDLER, "Nop");
+    wSearch.addListener(SWT.Modify, e -> filterTree());
+    wSearch.addListener(SWT.DefaultSelection, e -> updateTree());
+    PropsUi.setLook(wSearch, Props.WIDGET_STYLE_TOOLBAR);
+
+    // Create composite
+    Composite composite = new Composite(shell, SWT.BORDER);
+    composite.setLayout(new FormLayout());
+    composite.setLayoutData(
+        FormDataBuilder.builder()
+            .top(searchBar, margin)
+            .bottom(wOk, -2 * margin)
+            .fullWidth()
+            .build());
+    PropsUi.setLook(composite);
+
+    // Create toolbar for tree actions
+    IToolbarContainer toolBarContainer =
+        ToolbarFacade.createToolbarContainer(composite, SWT.WRAP | SWT.LEFT | 
SWT.HORIZONTAL);
+    toolBarWidgets = new GuiToolbarWidgets();
     toolBarWidgets.registerGuiPluginObject(this);
     toolBarWidgets.createToolbarWidgets(toolBarContainer, 
GUI_PLUGIN_TOOLBAR_PARENT_ID);
-    FormData layoutData = new FormData();
-    layoutData.top = new FormAttachment(0, 0);
-    layoutData.left = new FormAttachment(0, 0);
-    layoutData.right = new FormAttachment(100, 0);
-    toolBar.setLayoutData(layoutData);
-    toolBar.pack();
+    Control toolBar = toolBarContainer.getControl();
+    toolBar.setLayoutData(FormDataBuilder.builder().top().fullWidth().build());
     PropsUi.setLook(toolBar, Props.WIDGET_STYLE_TOOLBAR);
 
-    addRightButtons();
-    refreshButtons(null);
-
-    // Tree /*| (multiple?SWT.CHECK:SWT.NONE)*/
-    wTree = new HopTree(shell, SWT.SINGLE | SWT.BORDER);
+    // Create tree
+    wTree = new HopTree(composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
+    wTree.setLayoutData(
+        FormDataBuilder.builder().top(toolBar, 
margin).bottom().fullWidth().build());
+    wTree.addListener(SWT.DefaultSelection, this::openTreeItem);
+    wTree.addListener(SWT.Selection, e -> updateGui());
     PropsUi.setLook(wTree);
-    FormData fdTree = new FormData();
-    // To the right of the label
-    fdTree.left = new FormAttachment(0, 0);
-    fdTree.top = new FormAttachment(toolBar, margin);
-    fdTree.right = new FormAttachment(buttonsComposite, -margin);
-    fdTree.bottom = new FormAttachment(wOk, -2 * margin);
-    wTree.setLayoutData(fdTree);
-
-    if (!getData()) {
-      if (shell != null && !shell.isDisposed()) {
-        shell.dispose();
-      }
-      return false;
-    }
 
-    wTree.addListener(SWT.Selection, e -> refreshButtons(getSchemaTable()));
-    wTree.addListener(SWT.DefaultSelection, this::openSchema);
-    wTree.addListener(SWT.Expand, e -> lastTreeExpandCollapseNanos = 
System.nanoTime());
-    wTree.addListener(SWT.Collapse, e -> lastTreeExpandCollapseNanos = 
System.nanoTime());
-    wTree.addListener(SWT.MouseDown, this::handleTreeMouseDown);
-    shell.addListener(SWT.Close, e -> cancel());
+    // Create context menu
+    Menu menu = new Menu(wTree);
+    menuWidgets = new GuiMenuWidgets();
+    menuWidgets.registerGuiPluginObject(this);
+    menuWidgets.createMenuWidgets(GUI_PLUGIN_CONTEXT_MENU_PARENT_ID, shell, 
menu);
+    wTree.setMenu(menu);
+    wTree.addListener(SWT.MenuDetect, e -> updateGui());
 
-    // Prevent resizing below the complete control layout (notably right-side 
action buttons).
-    Point minSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-    shell.setMinimumSize(minSize);
-    BaseTransformDialog.setSize(shell);
+    // So shortcut work and we're tried before the active perspective
+    HopGuiKeyHandler keyHandler = HopGuiKeyHandler.getInstance();
+    keyHandler.addParentObjectToHandle(this, shell);
+    HopGui.getInstance().replaceKeyboardShortcutListeners(shell, keyHandler);
 
-    shell.open();
+    refresh();
 
-    // Handle the event loop until we're done with this shell...
-    //
-    Display display = shell.getDisplay();
-    while (!shell.isDisposed()) {
-      if (!display.readAndDispatch()) {
-        display.sleep();
-      }
-    }
+    updateGui();
+
+    wSearch.setFocus();
 
-    return tableName != null;
+    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+
+    return selectedTableName != null;
   }
 
-  private void cancel() {
-    log.logBasic("SelectTableDialog", "CANCEL SelectTableDialog", null);
-    dbMeta = null;
+  public void cancel() {
+    selectedSchemaName = null;
+    selectedTableName = null;
     dispose();
   }
 
-  private void addRightButtons() {
-    buttonsComposite = new Composite(shell, SWT.NONE);
-    PropsUi.setLook(buttonsComposite);
-    buttonsComposite.setLayout(new FormLayout());
-
-    activeSchemaTable = null;
-
-    bPrev = new Button(buttonsComposite, SWT.PUSH);
-    bPrev.setText(
-        BaseMessages.getString(
-            PKG,
-            CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100,
-            Const.NVL(activeSchemaTable, "?")));
-    bPrev.setEnabled(activeSchemaTable != null);
-    bPrev.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            previewTable(activeSchemaTable);
-          }
-        });
-    FormData prevData = new FormData();
-    prevData.left = new FormAttachment(0, 0);
-    prevData.right = new FormAttachment(100, 0);
-    prevData.top = new FormAttachment(0, 0);
-    bPrev.setLayoutData(prevData);
-
-    bPrevN = new Button(buttonsComposite, SWT.PUSH);
-    bPrevN.setText(
-        BaseMessages.getString(
-            PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N, 
Const.NVL(activeSchemaTable, "?")));
-    bPrevN.setEnabled(activeSchemaTable != null);
-    bPrevN.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            previewTable(activeSchemaTable);
-          }
-        });
-    FormData prevNData = new FormData();
-    prevNData.left = new FormAttachment(0, 0);
-    prevNData.right = new FormAttachment(100, 0);
-    prevNData.top = new FormAttachment(bPrev, PropsUi.getMargin());
-    bPrevN.setLayoutData(prevNData);
-
-    bCount = new Button(buttonsComposite, SWT.PUSH);
-    bCount.setText(
-        BaseMessages.getString(
-            PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE, 
Const.NVL(activeSchemaTable, "?")));
-    bCount.setEnabled(activeSchemaTable != null);
-    bCount.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            showCount(activeSchemaTable);
-          }
-        });
-    FormData countData = new FormData();
-    countData.left = new FormAttachment(0, 0);
-    countData.right = new FormAttachment(100, 0);
-    countData.top = new FormAttachment(bPrevN, PropsUi.getMargin());
-    bCount.setLayoutData(countData);
-
-    bShow = new Button(buttonsComposite, SWT.PUSH);
-    bShow.setText(
-        BaseMessages.getString(
-            PKG,
-            CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT,
-            Const.NVL(activeSchemaTable, "?")));
-    bShow.setEnabled(activeSchemaTable != null);
-    bShow.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            showTable(activeSchemaTable);
-          }
-        });
-    FormData showData = new FormData();
-    showData.left = new FormAttachment(0, 0);
-    showData.right = new FormAttachment(100, 0);
-    showData.top = new FormAttachment(bCount, PropsUi.getMargin() * 7);
-    bShow.setLayoutData(showData);
-
-    bDDL = new Button(buttonsComposite, SWT.PUSH);
-    bDDL.setText(BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL));
-    bDDL.setEnabled(activeSchemaTable != null);
-    bDDL.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getDDL(activeSchemaTable);
-          }
-        });
-    FormData ddlData = new FormData();
-    ddlData.left = new FormAttachment(0, 0);
-    ddlData.right = new FormAttachment(100, 0);
-    ddlData.top = new FormAttachment(bShow, PropsUi.getMargin());
-    bDDL.setLayoutData(ddlData);
-
-    bDDL2 = new Button(buttonsComposite, SWT.PUSH);
-    bDDL2.setText(
-        BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
-    bDDL2.setEnabled(activeSchemaTable != null && databases != null);
-    bDDL2.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getDDLForOther(activeSchemaTable);
-          }
-        });
-    FormData ddl2Data = new FormData();
-    ddl2Data.left = new FormAttachment(0, 0);
-    ddl2Data.right = new FormAttachment(100, 0);
-    ddl2Data.top = new FormAttachment(bDDL, PropsUi.getMargin());
-    bDDL2.setLayoutData(ddl2Data);
-
-    bSql = new Button(buttonsComposite, SWT.PUSH);
-    bSql.setText(
-        BaseMessages.getString(
-            PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL, 
Const.NVL(activeSchemaTable, "?")));
-    bSql.setEnabled(activeSchemaTable != null);
-    bSql.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getSql(activeSchemaTable);
-          }
-        });
-    FormData sqlData = new FormData();
-    sqlData.left = new FormAttachment(0, 0);
-    sqlData.right = new FormAttachment(100, 0);
-    sqlData.top = new FormAttachment(bDDL2, PropsUi.getMargin());
-    bSql.setLayoutData(sqlData);
-
-    bTruncate = new Button(buttonsComposite, SWT.PUSH);
-    bTruncate.setText(
-        BaseMessages.getString(
-            PKG, "DatabaseExplorerDialog.Menu.Truncate", 
Const.NVL(activeSchemaTable, "?")));
-    bTruncate.setEnabled(activeSchemaTable != null);
-    bTruncate.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            getTruncate(activeSchemaTable);
-          }
-        });
-    FormData truncateData = new FormData();
-    truncateData.left = new FormAttachment(0, 0);
-    truncateData.right = new FormAttachment(100, 0);
-    truncateData.top = new FormAttachment(bSql, PropsUi.getMargin());
-    bTruncate.setLayoutData(truncateData);
-
-    FormData fdComposite = new FormData();
-    fdComposite.right = new FormAttachment(100, 0);
-    // Attach to the toolbar control (not a pixel snapshot of its height at 
creation time).
-    fdComposite.top = new FormAttachment(toolBar, 0);
-    fdComposite.bottom = new FormAttachment(wOk, -2 * PropsUi.getMargin());
-    buttonsComposite.setLayoutData(fdComposite);
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_SEARCHBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_REGEX,
+      toolTip = "i18n::DatabaseExplorerDialog.RegEx.Tooltip",
+      type = GuiToolbarElementType.BUTTON,
+      image = "ui/images/regex.svg")
+  public void filterUseRegEx() {
+    this.filterUseRegEx = !this.filterUseRegEx;
+    // Update the button icon
+    updateGui();
+    // Apply the filter
+    updateTree();
   }
 
   @GuiToolbarElement(
       root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
       id = TOOLBAR_ITEM_EXPAND_ALL,
-      toolTip = "i18n::DatabaseExplorerDialog.Toolbar.ExpandAll.Tooltip",
+      toolTip = "i18n::System.Tooltip.ExpandAll",
       type = GuiToolbarElementType.BUTTON,
-      image = "ui/images/expand-all.svg")
+      image = "ui/images/expand-all.svg",
+      separator = true)
+  @GuiKeyboardShortcut(control = true, key = '+')
+  @GuiOsxKeyboardShortcut(command = true, key = '+')
   public void expandAll() {
-    expandAllItems(wTree.getItems(), true);
+    expandAllItems(connectionItem.getItems(), true);
   }
 
   @GuiToolbarElement(
       root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
       id = TOOLBAR_ITEM_COLLAPSE_ALL,
-      toolTip = "i18n::DatabaseExplorerDialog.Toolbar.CollapseAll.Tooltip",
+      toolTip = "i18n::System.Tooltip.CollapseALl",
       type = GuiToolbarElementType.BUTTON,
       image = "ui/images/collapse-all.svg")
+  @GuiKeyboardShortcut(control = true, key = '-')
+  @GuiOsxKeyboardShortcut(command = true, key = '-')
   public void collapseAll() {
-    expandAllItems(wTree.getItems(), false);
+    expandAllItems(connectionItem.getItems(), false);
   }
 
-  private void expandAllItems(TreeItem[] treeitems, boolean expand) {
-    for (TreeItem item : treeitems) {
+  private void expandAllItems(TreeItem[] items, boolean expand) {
+    for (TreeItem item : items) {
       item.setExpanded(expand);
       if (item.getItemCount() > 0) {
         expandAllItems(item.getItems(), expand);
@@ -489,368 +361,374 @@ public class DatabaseExplorerDialog extends Dialog {
     }
   }
 
-  private void refreshButtons(String table) {
-    // Avoid setText/layout while expanding folders: Windows DPI often 
misaligns the native
-    // expander hit target, and synchronous label updates during Selection can 
make clicks feel
-    // dead unless they land in the tiny gap between ">" and the folder icon.
-    boolean tableChanged = !Objects.equals(activeSchemaTable, table);
-    activeSchemaTable = table;
-    boolean hasTable = table != null;
-    boolean canGenerateDdlForOther = hasTable && databases != null;
-
-    if (tableChanged) {
-      bPrev.setText(
-          BaseMessages.getString(
-              PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100, 
Const.NVL(table, "?")));
-      bPrevN.setText(
-          BaseMessages.getString(
-              PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N, 
Const.NVL(table, "?")));
-      bCount.setText(
-          BaseMessages.getString(
-              PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE, 
Const.NVL(table, "?")));
-      bShow.setText(
-          BaseMessages.getString(
-              PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT, 
Const.NVL(table, "?")));
-      bDDL.setText(BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL));
-      bDDL2.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
-      bSql.setText(
-          BaseMessages.getString(
-              PKG, CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL, 
Const.NVL(table, "?")));
-      bTruncate.setText(
-          BaseMessages.getString(
-              PKG, "DatabaseExplorerDialog.Menu.Truncate", Const.NVL(table, 
"?")));
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_REFRESH,
+      toolTip = "i18n::System.Button.Refresh",
+      type = GuiToolbarElementType.BUTTON,
+      image = "ui/images/refresh.svg")
+  @GuiKeyboardShortcut(key = SWT.F5)
+  @GuiOsxKeyboardShortcut(key = SWT.F5)
+  public void refresh() {
+    GetDatabaseInfoProgressDialog dialog =
+        new GetDatabaseInfoProgressDialog(shell, variables, databaseMeta);
+    this.databaseMetaInformation = dialog.open();
+    updateTree();
+  }
+
+  public void updateGui() {
+    // Update the regex filter icons in the search bar
+    ToolItem item = searchBarWidgets.findToolItem(TOOLBAR_ITEM_REGEX);
+    if (item != null && !item.isDisposed()) {
+      if (filterUseRegEx) {
+        item.setImage(GuiResource.getInstance().getImageRegex());
+      } else {
+        item.setImage(GuiResource.getInstance().getImageRegexDisabled());
+      }
     }
 
-    bPrev.setEnabled(hasTable);
-    bPrevN.setEnabled(hasTable);
-    bCount.setEnabled(hasTable);
-    bShow.setEnabled(hasTable);
-    bDDL.setEnabled(hasTable);
-    bDDL2.setEnabled(canGenerateDdlForOther);
-    bSql.setEnabled(hasTable);
-    bTruncate.setEnabled(hasTable);
+    DatabaseTreeNode node = getSelectedNode();
+    boolean isTable = node != null && node.isTableLike();
+    toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_PREVIEW, isTable);
+    toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_SHOW_LAYOUT, isTable);
+    toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_SQL_SELECT, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_PREVIEW_100, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_PREVIEW_N, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_SHOW_SIZE, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_SHOW_LAYOUT, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_DDL, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_DDL_OTHER, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_SQL_SELECT, isTable);
+    menuWidgets.enableMenuItem(CONTEXT_MENU_SQL_TRUNCATE, isTable);
+  }
+
+  private SearchMatcher createSearchMatcher() {
+    if (wSearch != null && !wSearch.isDisposed()) {
+      String search = wSearch.getText();
+      if (!Utils.isEmpty(search)) {
+        return new SearchMatcher(search, false, filterUseRegEx, false);
+      }
+    }
+    return null; // new SearchMatcher("", false, false, false);
+  }
 
-    if (!tableChanged || shell == null || shell.isDisposed()) {
+  /** Filter the tree based on search text, debounced so we don't rebuild on 
every keystroke. */
+  protected void filterTree() {
+    if (shell == null || shell.isDisposed()) {
       return;
     }
-    Display display = shell.getDisplay();
-    display.asyncExec(
-        () -> {
-          if (shell == null || shell.isDisposed()) {
-            return;
-          }
-          buttonsComposite.layout(true, true);
-          shell.layout(true, false);
-        });
+    shell.getDisplay().timerExec(FILTER_DEBOUNCE_MS, filterRunnable);
   }
 
-  private boolean getData() {
-    GetDatabaseInfoProgressDialog gdipd =
-        new GetDatabaseInfoProgressDialog(shell, variables, dbMeta);
-    DatabaseMetaInformation dmi = gdipd.open();
-    if (dmi == null) {
-      return false;
+  /** Update tree when refresh or filter changed */
+  private void updateTree() {
+    if (wTree.isDisposed()) {
+      return;
     }
-    disposeOldTreeRootIfPresent();
-    createDatabaseRootTreeItem();
-    addCatalogBranches(dmi);
-    addSchemaBranches(dmi);
-    TreeItem tiTab = addTablesBranch(dmi.getTables());
-    TreeItem tiView = addViewsBranch(dmi.getViews());
-    TreeItem tiSyn = addSynonymsBranch(dmi.getSynonyms());
-    selectInitiallySelectedTable(tiTab, tiView, tiSyn);
-    tiTree.setExpanded(true);
-    return true;
-  }
 
-  private void disposeOldTreeRootIfPresent() {
-    if (tiTree != null && !tiTree.isDisposed()) {
-      tiTree.dispose();
+    shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
+    wTree.setRedraw(false);
+
+    // Remove all previous items
+    wTree.removeAll();
+
+    // Create connection tree item
+    String connectionName = "";
+    Image image = GuiResource.getInstance().getImageDatabase();
+    if (databaseMeta != null) {
+      connectionName = databaseMeta.getName();
+      image = GuiResource.getInstance().getImage(databaseMeta.getIDatabase());
+    }
+    connectionItem = new TreeItem(wTree, SWT.NONE);
+    connectionItem.setImage(image);
+    connectionItem.setText(connectionName);
+    connectionItem.setData(DatabaseTreeNode.connection(connectionName, true));
+
+    if (databaseMetaInformation != null) {
+
+      // Create search filter
+      this.filter = createSearchMatcher();
+
+      if (!updateTreeSchemas()) {
+        if (!updateTreeCatalogs()) {
+          addFolder(
+              connectionItem,
+              connectionName,
+              STRING_TABLES,
+              databaseMetaInformation.getTables(),
+              DatabaseTreeNode.Kind.TABLE);
+          addFolder(
+              connectionItem,
+              connectionName,
+              STRING_VIEWS,
+              databaseMetaInformation.getViews(),
+              DatabaseTreeNode.Kind.VIEW);
+          addFolder(
+              connectionItem,
+              connectionName,
+              STRING_SYNONYMS,
+              databaseMetaInformation.getSynonyms(),
+              DatabaseTreeNode.Kind.SYNONYM);
+        }
+      }
     }
-  }
 
-  private void createDatabaseRootTreeItem() {
-    tiTree = new TreeItem(wTree, SWT.NONE);
-    tiTree.setImage(GuiResource.getInstance().getImageDatabase());
-    tiTree.setText(dbMeta == null ? "" : dbMeta.getName());
+    // Always expand the root item
+    connectionItem.setExpanded(true);
+
+    wTree.setRedraw(true);
+    shell.setCursor(null);
+  }
+
+  // Database support catalogs
+  private boolean updateTreeCatalogs() {
+    Catalog[] catalogs = databaseMetaInformation.getCatalogs();
+    if (catalogs != null && catalogs.length > 0) {
+      String connectionName = databaseMeta.getName();
+      for (Catalog catalog : catalogs) {
+        TreeItem catalogItem = new TreeItem(connectionItem, SWT.NONE);
+        catalogItem.setText(Const.NVL(catalog.getCatalogName(), ""));
+        catalogItem.setImage(GuiResource.getInstance().getImageFolder());
+        catalogItem.setData(DatabaseTreeNode.catalog(connectionName, 
catalog.getCatalogName()));
+        addSchemaObjects(
+            catalogItem,
+            connectionName,
+            catalog.getCatalogName(),
+            catalog.getItems(),
+            databaseMetaInformation);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  // Database support schemas
+  private boolean updateTreeSchemas() {
+    Schema[] schemas = databaseMetaInformation.getSchemas();
+    if (schemas != null && schemas.length > 0) {
+      String connectionName = databaseMeta.getName();
+      for (Schema schema : schemas) {
+        if (schemaOrChildMatches(schema, databaseMetaInformation)) {
+          TreeItem schemaItem = new TreeItem(connectionItem, SWT.NONE);
+          schemaItem.setText(Const.NVL(schema.getSchemaName(), ""));
+          schemaItem.setImage(GuiResource.getInstance().getImageSchema());
+          schemaItem.setData(DatabaseTreeNode.schema(connectionName, 
schema.getSchemaName()));
+          addSchemaObjects(
+              schemaItem,
+              connectionName,
+              schema.getSchemaName(),
+              schema.getItems(),
+              databaseMetaInformation);
+        }
+      }
+      return true;
+    }
+    return false;
   }
 
-  private void addCatalogBranches(DatabaseMetaInformation dmi) {
-    Catalog[] catalogs = dmi.getCatalogs();
-    if (catalogs == null) {
+  private void addFolder(
+      TreeItem parent,
+      String connectionName,
+      String folderName,
+      String[] names,
+      DatabaseTreeNode.Kind kind) {
+    if (names == null || names.length == 0) {
       return;
     }
-    TreeItem tiCat = new TreeItem(tiTree, SWT.NONE);
-    tiCat.setImage(GuiResource.getInstance().getImageFolder());
-    tiCat.setText(STRING_CATALOG);
-    for (Catalog catalog : catalogs) {
-      TreeItem newCat = new TreeItem(tiCat, SWT.NONE);
-      newCat.setImage(GuiResource.getInstance().getImageFolder());
-      newCat.setText(catalog.getCatalogName());
-      for (String catalogItemName : catalog.getItems()) {
-        TreeItem ti = new TreeItem(newCat, SWT.NONE);
-        ti.setImage(GuiResource.getInstance().getImageTable());
-        ti.setText(catalogItemName);
-      }
-    }
+    TreeItem item = new TreeItem(parent, SWT.NONE);
+    item.setText(folderName);
+    item.setImage(GuiResource.getInstance().getImageFolder());
+    item.setData(DatabaseTreeNode.folder(connectionName, folderName));
+    addTables(item, connectionName, names, kind);
   }
 
-  private void addSchemaBranches(DatabaseMetaInformation dmi) {
-    Schema[] schemas = dmi.getSchemas();
-    if (schemas == null) {
+  private void addTables(
+      TreeItem parent, String connectionName, String[] names, 
DatabaseTreeNode.Kind kind) {
+    if (names == null) {
       return;
     }
-    TreeItem tiSch = new TreeItem(tiTree, SWT.NONE);
-    tiSch.setImage(GuiResource.getInstance().getImageFolder());
-    tiSch.setText(STRING_SCHEMAS);
-    for (Schema schema : schemas) {
-      TreeItem newSch = new TreeItem(tiSch, SWT.NONE);
-      newSch.setImage(GuiResource.getInstance().getImageSchema());
-      newSch.setText(schema.getSchemaName());
-      for (String schemaItemName : schema.getItems()) {
-        TreeItem ti = new TreeItem(newSch, SWT.NONE);
-        ti.setImage(GuiResource.getInstance().getImageTable());
-        ti.setText(schemaItemName);
+    boolean expanded = false;
+    for (String name : names) {
+      if (matchesFilter(name, null)) {
+        TreeItem item = new TreeItem(parent, SWT.NONE);
+        item.setText(name);
+        item.setImage(DatabaseTreeUtil.imageFor(kind));
+        item.setData(DatabaseTreeNode.table(kind, connectionName, null, name));
+
+        // Highlight selected item
+        if (name.equalsIgnoreCase(selectedTableName)) {
+          item.setFont(GuiResource.getInstance().getFontBold());
+          wTree.setSelection(item);
+          wTree.showItem(item);
+          expanded = true;
+        }
       }
     }
-  }
-
-  private TreeItem addTablesBranch(String[] tabnames) {
-    if (tabnames == null) {
-      return null;
-    }
-    TreeItem tiTab = new TreeItem(tiTree, SWT.NONE);
-    tiTab.setImage(GuiResource.getInstance().getImageFolder());
-    tiTab.setText(STRING_TABLES);
-    for (String tabname : tabnames) {
-      TreeItem newTab = new TreeItem(tiTab, SWT.NONE);
-      newTab.setImage(GuiResource.getInstance().getImageTable());
-      newTab.setText(tabname);
+    parent.setExpanded(expanded);
+  }
+
+  /**
+   * Tables, views and synonyms under a schema (or catalog). Views get {@code 
view.svg} via {@link
+   * DatabaseTreeUtil#kindOf}.
+   */
+  private void addSchemaObjects(
+      TreeItem parent,
+      String connectionName,
+      String schemaName,
+      String[] items,
+      DatabaseMetaInformation info) {
+    Collection<String> views = 
DatabaseTreeUtil.namesForSchema(info.getViewMap(), schemaName);
+    Collection<String> synonyms = 
DatabaseTreeUtil.namesForSchema(info.getSynonymMap(), schemaName);
+    List<String> names = new ArrayList<>();
+    if (items != null) {
+      names.addAll(Arrays.asList(items));
     }
-    tiTab.setExpanded(true);
-    return tiTab;
-  }
-
-  private TreeItem addViewsBranch(String[] views) {
-    if (views == null) {
-      return null;
-    }
-    TreeItem tiView = new TreeItem(tiTree, SWT.NONE);
-    tiView.setImage(GuiResource.getInstance().getImageFolder());
-    tiView.setText(STRING_VIEWS);
     for (String view : views) {
-      TreeItem newView = new TreeItem(tiView, SWT.NONE);
-      newView.setImage(GuiResource.getInstance().getImageView());
-      newView.setText(view);
-    }
-    return tiView;
-  }
-
-  private TreeItem addSynonymsBranch(String[] syn) {
-    if (syn == null) {
-      return null;
+      if (!DatabaseTreeUtil.containsIgnoreCase(names, view)) {
+        names.add(view);
+      }
     }
-    TreeItem tiSyn = new TreeItem(tiTree, SWT.NONE);
-    tiSyn.setImage(GuiResource.getInstance().getImageFolder());
-    tiSyn.setText(STRING_SYNONYMS);
-    for (String s : syn) {
-      TreeItem newSyn = new TreeItem(tiSyn, SWT.NONE);
-      newSyn.setImage(GuiResource.getInstance().getImageSynonym());
-      newSyn.setText(s);
+    for (String synonym : synonyms) {
+      if (!DatabaseTreeUtil.containsIgnoreCase(names, synonym)) {
+        names.add(synonym);
+      }
     }
-    return tiSyn;
-  }
+    names.sort(String.CASE_INSENSITIVE_ORDER);
+    boolean expanded = false;
+    for (String name : names) {
+      if (matchesFilter(name, schemaName)) {
+        DatabaseTreeNode.Kind kind = DatabaseTreeUtil.kindOf(name, views, 
synonyms);
+        TreeItem item = new TreeItem(parent, SWT.NONE);
+        item.setText(name);
+        item.setImage(DatabaseTreeUtil.imageFor(kind));
+        item.setData(DatabaseTreeNode.table(kind, connectionName, schemaName, 
name));
+
+        // Highlight selected item
+        if (schemaName.equalsIgnoreCase(selectedSchemaName)
+            && name.equalsIgnoreCase(selectedTableName)) {
+          parent.setFont(GuiResource.getInstance().getFontBold());
+          item.setFont(GuiResource.getInstance().getFontBold());
+          wTree.setSelection(item);
+          wTree.showItem(item);
+          expanded = true;
+        }
 
-  private void selectInitiallySelectedTable(TreeItem tiTab, TreeItem tiView, 
TreeItem tiSyn) {
-    if (StringUtils.isEmpty(selectedTable)) {
-      return;
-    }
-    TreeItem ti = findTreeItemForInitialSelection(tiTab, tiView, tiSyn);
-    if (ti != null) {
-      wTree.setSelection(new TreeItem[] {ti});
-      wTree.showSelection();
-      if (dbMeta != null) {
-        refreshButtons(
-            dbMeta.getQuotedSchemaTableCombination(variables, selectedSchema, 
selectedTable));
+        // If is filtered and an item match then expand parent
+        if (filter != null) {
+          expanded = true;
+        }
       }
     }
-    selectedTable = null;
+    parent.setExpanded(expanded);
   }
 
-  private TreeItem findTreeItemForInitialSelection(
-      TreeItem tiTab, TreeItem tiView, TreeItem tiSyn) {
-    TreeItem ti = null;
-    if (tiTab != null) {
-      ti = ConstUi.findTreeItem(tiTab, selectedSchema, selectedTable);
+  private boolean matchesFilter(String name, String schemaName) {
+    if (filter == null) {
+      return true;
     }
-    if (ti == null && tiView != null) {
-      ti = ConstUi.findTreeItem(tiView, selectedSchema, selectedTable);
-    }
-    if (ti == null) {
-      ti = ConstUi.findTreeItem(tiTree, selectedSchema, selectedTable);
-    }
-    if (ti == null && tiSyn != null) {
-      ti = ConstUi.findTreeItem(tiSyn, selectedSchema, selectedTable);
-    }
-    return ti;
+    return filter.matches(name) || filter.matches(schemaName);
   }
 
-  private String getSchemaTable() {
-    TreeItem[] ti = wTree.getSelection();
-    if (ti.length != 1) {
-      return null;
+  private boolean schemaOrChildMatches(Schema schema, DatabaseMetaInformation 
info) {
+    if (filter == null) {
+      return true;
     }
-    TreeItem parentItem = ti[0].getParentItem();
-    if (parentItem == null) {
-      return null;
+    if (filter.matches(schema.getSchemaName())) {
+      return true;
     }
-    String parentLabel = parentItem.getText();
-    String itemText = ti[0].getText();
-    if (ti[0].getItemCount() != 0) {
-      return null;
-    }
-    if (isRootTableViewSynonymOrEmptyParent(parentLabel)) {
-      return itemText;
+    if (schema.getItems() != null) {
+      for (String table : schema.getItems()) {
+        if (filter.matches(table)) {
+          return true;
+        }
+      }
     }
-    if (dbMeta == null) {
-      return null;
+    for (String view : DatabaseTreeUtil.namesForSchema(info.getViewMap(), 
schema.getSchemaName())) {
+      if (filter.matches(view)) {
+        return true;
+      }
     }
-    return dbMeta.getQuotedSchemaTableCombination(variables, parentLabel, 
itemText);
+    return false;
   }
 
-  private static boolean isRootTableViewSynonymOrEmptyParent(String 
parentLabel) {
-    return parentLabel.equalsIgnoreCase(STRING_TABLES)
-        || parentLabel.equalsIgnoreCase(STRING_VIEWS)
-        || parentLabel.equalsIgnoreCase(STRING_SYNONYMS)
-        || parentLabel.isEmpty();
-  }
-
-  public void setTreeMenu() {
-    final String table = getSchemaTable();
-    if (table != null) {
-      Menu mTree = new Menu(shell, SWT.POP_UP);
-
-      MenuItem miPrev = new MenuItem(mTree, SWT.PUSH);
-      miPrev.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_100, table));
-      miPrev.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              previewTable(table);
-            }
-          });
-      MenuItem miPrevN = new MenuItem(mTree, SWT.PUSH);
-      miPrevN.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_PREVIEW_N, table));
-      miPrevN.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              previewTable(table);
-            }
-          });
-      MenuItem miCount = new MenuItem(mTree, SWT.PUSH);
-      miCount.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_SIZE, table));
-      miCount.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              showCount(table);
-            }
-          });
-
-      new MenuItem(mTree, SWT.SEPARATOR);
-
-      MenuItem miShow = new MenuItem(mTree, SWT.PUSH);
-      miShow.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_SHOW_LAYOUT, table));
-      miShow.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              showTable(table);
-            }
-          });
-      MenuItem miDDL = new MenuItem(mTree, SWT.PUSH);
-      miDDL.setText(BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDL));
-      miDDL.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              getDDL(table);
-            }
-          });
-      MenuItem miDDL2 = new MenuItem(mTree, SWT.PUSH);
-      miDDL2.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_GEN_DDLOTHER_CONN));
-      miDDL2.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              getDDLForOther(table);
-            }
-          });
-      miDDL2.setEnabled(databases != null);
-      MenuItem miSql = new MenuItem(mTree, SWT.PUSH);
-      miSql.setText(
-          BaseMessages.getString(PKG, 
CONST_DATABASE_EXPLORER_DIALOG_MENU_OPEN_SQL, table));
-      miSql.addSelectionListener(
-          new SelectionAdapter() {
-            @Override
-            public void widgetSelected(SelectionEvent e) {
-              getSql(table);
-            }
-          });
-
-      wTree.setMenu(mTree);
-    } else {
-      wTree.setMenu(null);
+  private @Nullable DatabaseTreeNode getSelectedNode() {
+    TreeItem[] selection = wTree.getSelection();
+    if (selection.length != 1) {
+      return null;
     }
+    Object data = selection[0].getData();
+    return data instanceof DatabaseTreeNode node ? node : null;
   }
 
-  public void previewTable(String qualifiedTableName) {
-    PreviewTableSettingsDialog settingsDialog =
-        new PreviewTableSettingsDialog(shell, 100, variables, false);
-    PreviewTableSettingsDialog.Settings settings = settingsDialog.open();
-    if (settings == null) {
-      return;
-    }
-    int limit = settings.rowLimit;
-    int queryTimeoutSeconds = settings.queryTimeoutSeconds;
-
-    String[] tableNameParts = qualifiedTableName.split("\\.");
-
-    GetPreviewTableProgressDialog pd = null;
-    if (schemaName == null && tableNameParts.length == 2) {
-      // Table name contains both schema name and table name concatenated
-      pd =
-          new GetPreviewTableProgressDialog(
-              shell,
-              variables,
-              dbMeta,
-              tableNameParts[0],
-              tableNameParts[1],
-              limit,
-              queryTimeoutSeconds);
-    } else {
-      pd =
-          new GetPreviewTableProgressDialog(
-              shell, variables, dbMeta, null, qualifiedTableName, limit, 
queryTimeoutSeconds);
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_PREVIEW,
+      type = GuiToolbarElementType.BUTTON,
+      toolTip = "i18n::DatabaseExplorerDialog.Toolbar.Preview.Tooltip",
+      image = "ui/images/preview.svg",
+      separator = true)
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_PREVIEW_100,
+      label = "i18n::DatabaseExplorerDialog.Menu.Preview100",
+      image = "ui/images/preview.svg")
+  @GuiKeyboardShortcut(key = SWT.F4)
+  @GuiOsxKeyboardShortcut(key = SWT.F4)
+  public void showTablePreview100() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      showTablePreview(node, false);
+    }
+  }
+
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_PREVIEW_N,
+      label = "i18n::DatabaseExplorerDialog.Menu.PreviewN",
+      image = "ui/images/preview.svg")
+  public void showTablePreviewN() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      showTablePreview(node, true);
+    }
+  }
+
+  public void showTablePreview(DatabaseTreeNode node, boolean askSetting) {
+    int limit = 100;
+    int queryTimeoutSeconds = 0;
+
+    if (askSetting) {
+      PreviewTableSettingsDialog settingsDialog =
+          new PreviewTableSettingsDialog(shell, 100, variables, false);
+      PreviewTableSettingsDialog.Settings settings = settingsDialog.open();
+      if (settings == null) {
+        return;
+      }
+      limit = settings.rowLimit;
+      queryTimeoutSeconds = settings.queryTimeoutSeconds;
     }
 
-    List<Object[]> rows = pd.open();
-    if (pd.isPreviewSucceeded()) {
+    GetPreviewTableProgressDialog dialog =
+        new GetPreviewTableProgressDialog(
+            shell,
+            variables,
+            databaseMeta,
+            node.getSchemaName(),
+            node.getObjectName(),
+            limit,
+            queryTimeoutSeconds);
+    List<Object[]> rows = dialog.open();
+    if (dialog.isPreviewSucceeded()) {
       if (!rows.isEmpty()) {
         new ShowRowsDialog(
                 shell,
                 variables,
                 BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.ShowRows.Title"),
                 BaseMessages.getString(
-                    PKG, "DatabaseExplorerDialog.ShowRows.Message", 
qualifiedTableName),
-                pd.getRowMeta(),
+                    PKG, "DatabaseExplorerDialog.ShowRows.Message", 
node.getObjectName()),
+                dialog.getRowMeta(),
                 rows)
             .open();
       } else {
@@ -862,103 +740,133 @@ public class DatabaseExplorerDialog extends Dialog {
     }
   }
 
-  public void showTable(String qualifiedTableName) {
-    String sql = dbMeta.getSqlQueryFields(qualifiedTableName);
-    IRowMeta result = null;
-    try (Database db = new Database(HopGui.getInstance().getLoggingObject(), 
variables, dbMeta)) {
-      db.connect();
-      result = db.getQueryFields(sql, false);
-    } catch (Exception e) {
-      // Do Nothing
-    }
-    if (result != null) {
-      TransformFieldsDialog sfd =
-          new TransformFieldsDialog(shell, variables, SWT.NONE, 
qualifiedTableName, result);
-      sfd.open();
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SHOW_LAYOUT,
+      type = GuiToolbarElementType.BUTTON,
+      toolTip = "i18n::DatabaseExplorerDialog.Toolbar.ShowLayout.Tooltip",
+      image = "ui/images/layout.svg")
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_SHOW_LAYOUT,
+      label = "i18n::DatabaseExplorerDialog.Menu.ShowLayout",
+      image = "ui/images/layout.svg",
+      separator = true)
+  public void showTableLayout() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      String table =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
+      String sql = databaseMeta.getSqlQueryFields(table);
+      IRowMeta result = null;
+      try (Database db =
+          new Database(HopGui.getInstance().getLoggingObject(), variables, 
databaseMeta)) {
+        db.connect();
+        result = db.getQueryFields(sql, false);
+      } catch (Exception e) {
+        // Do Nothing
+      }
+      if (result != null) {
+        TransformFieldsDialog sfd =
+            new TransformFieldsDialog(shell, variables, SWT.NONE, table, 
result);
+        sfd.open();
+      }
     }
   }
 
-  public void showCount(String qualifiedTableName) {
-    String realTableName =
-        (qualifiedTableName.contains(".")
-            ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") + 1)
-            : qualifiedTableName);
-    GetTableSizeProgressDialog pd =
-        new GetTableSizeProgressDialog(shell, variables, dbMeta, 
realTableName);
-    Long size = pd.open();
-    if (size != null) {
-      MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
-      mb.setMessage(
-          BaseMessages.getString(
-              PKG,
-              "DatabaseExplorerDialog.TableSize.Message",
-              qualifiedTableName,
-              size.toString()));
-      mb.setText(BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.TableSize.Title"));
-      mb.open();
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_SHOW_SIZE,
+      label = "i18n::DatabaseExplorerDialog.Menu.ShowSize")
+  public void showTableSize() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      GetTableSizeProgressDialog dialog =
+          new GetTableSizeProgressDialog(
+              shell, variables, databaseMeta, node.getObjectName(), 
node.getSchemaName());
+      Long size = dialog.open();
+      if (size != null) {
+        String tableName =
+            databaseMeta.getQuotedSchemaTableCombination(
+                variables, node.getSchemaName(), node.getObjectName());
+
+        MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
+        mb.setMessage(
+            BaseMessages.getString(
+                PKG, "DatabaseExplorerDialog.TableSize.Message", tableName, 
size.toString()));
+        mb.setText(BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.TableSize.Title"));
+        mb.open();
+      }
     }
   }
 
-  public void getDDL(String qualifiedTableName) {
-    try (Database db = new Database(loggingObject, variables, dbMeta)) {
-      db.connect();
-      IRowMeta r = db.getTableFields(qualifiedTableName);
-      String realTableName =
-          (qualifiedTableName.contains(".")
-              ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") + 
1)
-              : qualifiedTableName);
-
-      String sql = db.getCreateTableStatement(realTableName, r, null, false, 
null, true);
-      DatabaseWorkbenchDialog.openSql(dbMeta, sql);
-    } catch (HopDatabaseException dbe) {
-      new ErrorDialog(
-          shell,
-          BaseMessages.getString(PKG, "Dialog.Error.Header"),
-          BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.Error.RetrieveLayout"),
-          dbe);
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_DDL,
+      label = "i18n::DatabaseExplorerDialog.Menu.GenDDL")
+  public void showDDL() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      String table =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
+
+      try (Database db = new Database(loggingObject, variables, databaseMeta)) 
{
+        db.connect();
+        IRowMeta rowMeta = db.getTableFields(table);
+        String sql = db.getCreateTableStatement(table, rowMeta, null, false, 
null, true);
+        openSqlEditor(sql);
+      } catch (HopDatabaseException dbe) {
+        new ErrorDialog(
+            shell,
+            BaseMessages.getString(PKG, "Dialog.Error.Header"),
+            BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.Error.RetrieveLayout"),
+            dbe);
+      }
     }
   }
 
-  public void getDDLForOther(String qualifiedTableName) {
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_DDL_OTHER,
+      label = "i18n::DatabaseExplorerDialog.Menu.GenDDLOtherConn")
+  public void showDDLForOther() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null && databases != null) {
+      String table =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
 
-    if (databases != null) {
-      try (Database database = new Database(loggingObject, variables, dbMeta)) 
{
+      try (Database database = new Database(loggingObject, variables, 
databaseMeta)) {
         database.connect();
 
-        String realTableName =
-            (qualifiedTableName.contains(".")
-                ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") 
+ 1)
-                : qualifiedTableName);
-        IRowMeta rowMeta = database.getTableFields(realTableName);
+        IRowMeta rowMeta = database.getTableFields(table);
 
         // Now select the other connection...
-
-        // Only take non-SAP ERP connections....
-        List<DatabaseMeta> databaseMetaList = new ArrayList<>();
-        for (DatabaseMeta databaseMeta : databases) {
-          databaseMetaList.add(databaseMeta);
-        }
-
-        String[] connectionNames = new String[databaseMetaList.size()];
+        String[] connectionNames = new String[databases.size()];
         for (int i = 0; i < connectionNames.length; i++) {
-          connectionNames[i] = (databaseMetaList.get(i)).getName();
+          connectionNames[i] = (databases.get(i)).getName();
         }
 
-        EnterSelectionDialog enterSelectionDialog =
+        EnterSelectionDialog dialog =
             new EnterSelectionDialog(
                 shell,
                 connectionNames,
                 BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.TargetDatabase.Title"),
                 BaseMessages.getString(PKG, 
"DatabaseExplorerDialog.TargetDatabase.Message"));
-        String target = enterSelectionDialog.open();
+        String target = dialog.open();
         if (target != null) {
-          DatabaseMeta targetDatabaseMeta = 
DatabaseMeta.findDatabase(databaseMetaList, target);
+          DatabaseMeta targetDatabaseMeta = 
DatabaseMeta.findDatabase(databases, target);
           try (Database targetDatabase =
               new Database(loggingObject, variables, targetDatabaseMeta)) {
             String sql =
-                targetDatabase.getCreateTableStatement(
-                    qualifiedTableName, rowMeta, null, false, null, true);
-            DatabaseWorkbenchDialog.openSql(targetDatabaseMeta, sql);
+                targetDatabase.getCreateTableStatement(table, rowMeta, null, 
false, null, true);
+            openSqlEditor(sql);
           }
         }
       } catch (HopDatabaseException dbe) {
@@ -977,20 +885,51 @@ public class DatabaseExplorerDialog extends Dialog {
     }
   }
 
-  public void getSql(String qualifiedTableName) {
-    String realTableName =
-        (qualifiedTableName.contains(".")
-            ? qualifiedTableName.substring(qualifiedTableName.indexOf(".") + 1)
-            : qualifiedTableName);
-    DatabaseWorkbenchDialog.openSql(dbMeta, "SELECT * FROM " + realTableName);
-  }
-
-  public void getTruncate(String truncateTableReference) {
-    DatabaseWorkbenchDialog.openSql(dbMeta, "-- TRUNCATE TABLE " + 
truncateTableReference);
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SQL_SELECT,
+      type = GuiToolbarElementType.BUTTON,
+      toolTip = "i18n::DatabaseExplorerDialog.Toolbar.SQLSelect.Tooltip",
+      image = "ui/images/script.svg")
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_SQL_SELECT,
+      label = "i18n::DatabaseExplorerDialog.Menu.SQLSelect",
+      image = "ui/images/script.svg",
+      separator = true)
+  public void showSqlSelect() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      String table =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
+      openSqlEditor("SELECT * FROM " + table);
+    }
+  }
+
+  @GuiMenuElement(
+      root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+      id = CONTEXT_MENU_SQL_TRUNCATE,
+      label = "i18n::DatabaseExplorerDialog.Menu.Truncate")
+  public void showSqlTruncate() {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node != null) {
+      String table =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
+      openSqlEditor("-- TRUNCATE TABLE " + table);
+    }
+  }
+
+  protected void openSqlEditor(String sql) {
+    SqlEditor dialog = new SqlEditor(shell, SWT.NONE, variables, databaseMeta, 
dbCache, sql);
+    dialog.open();
   }
 
   public void dispose() {
-    props.setScreen(new WindowProperty(shell));
+    PropsUi.getInstance().setScreen(new WindowProperty(shell));
     shell.dispose();
   }
 
@@ -999,181 +938,56 @@ public class DatabaseExplorerDialog extends Dialog {
       dispose();
       return;
     }
-    TreeItem[] selection = wTree.getSelection();
-    if (selection.length != 1) {
-      return;
-    }
-    TreeItem selected = selection[0];
-    if (completeOkFromThreePartPath(selected)) {
-      return;
-    }
-    completeOkFromFourPartPath(selected);
-  }
-
-  private boolean completeOkFromThreePartPath(TreeItem selected) {
-    String[] path = ConstUi.getTreeStrings(selected);
-    if (path.length != 3 || !isTablesViewsOrSynonymsPathSegment(path[1])) {
-      return false;
-    }
-    applyOkSelectionForFlatTableFolder(selected.getText());
-    dispose();
-    return true;
-  }
-
-  private void applyOkSelectionForFlatTableFolder(String selectedItemText) {
-    schemaName = null;
-    tableName = selectedItemText;
-    String[] st = tableName.split("\\.", 2);
-    if (st.length > 1) {
-      schemaName = st[0];
-      tableName = st[1];
-    }
-  }
 
-  private void completeOkFromFourPartPath(TreeItem selected) {
-    String[] path = ConstUi.getTreeStrings(selected);
-    if (path.length != 4 || !isCatalogOrSchemasPathSegment(path[1])) {
+    DatabaseTreeNode node = getSelectedNode();
+    if (node == null || !node.isTableLike()) {
       return;
     }
+
     if (splitSchemaAndTable) {
-      schemaName = path[2];
-      tableName = path[3];
+      selectedSchemaName = node.getSchemaName();
+      selectedTableName = node.getObjectName();
     } else {
-      if (dbMeta == null) {
-        return;
-      }
-      schemaName = null;
-      tableName = dbMeta.getQuotedSchemaTableCombination(variables, path[2], 
path[3]);
-    }
-    dispose();
-  }
-
-  private static boolean isTablesViewsOrSynonymsPathSegment(String 
pathSegment) {
-    return STRING_TABLES.equalsIgnoreCase(pathSegment)
-        || STRING_VIEWS.equalsIgnoreCase(pathSegment)
-        || STRING_SYNONYMS.equalsIgnoreCase(pathSegment);
-  }
-
-  private static boolean isCatalogOrSchemasPathSegment(String pathSegment) {
-    return STRING_SCHEMAS.equals(pathSegment) || 
STRING_CATALOG.equals(pathSegment);
-  }
-
-  public void openSchema(Event e) {
-    TreeItem sel = (TreeItem) e.item;
-    if (sel == null) {
-      return;
+      selectedSchemaName = null;
+      selectedTableName =
+          databaseMeta.getQuotedSchemaTableCombination(
+              variables, node.getSchemaName(), node.getObjectName());
     }
 
-    TreeItem up1 = sel.getParentItem();
-    if (up1 != null) {
-      TreeItem up2 = up1.getParentItem();
-      if (up2 != null) {
-        TreeItem up3 = up2.getParentItem();
-        if (up3 != null) {
-          tableName = sel.getText();
-          if (!justLook) {
-            ok();
-          } else {
-            previewTable(tableName);
-          }
-        }
-      }
-    }
+    dispose();
   }
 
-  private void handleTreeMouseDown(Event e) {
-    if (e.button == 3) {
-      setTreeMenu();
-      return;
-    }
-    if (e.button != 1) {
-      return;
-    }
-
-    TreeItem item = wTree.getItem(new Point(e.x, e.y));
-    if (item == null) {
-      // Expander ">" is often outside getItem()'s hit box; resolve the row by 
Y.
-      item = findTreeItemAtY(e.y);
-    }
-    if (item == null || item.getItemCount() == 0) {
-      return;
-    }
-
-    Rectangle textBounds = item.getBounds(0);
-    Rectangle imageBounds = item.getImageBounds(0);
-    boolean inGutter = e.x < textBounds.x;
-    boolean onImage =
-        imageBounds != null && !imageBounds.isEmpty() && 
imageBounds.contains(e.x, e.y);
-    if (!inGutter && !onImage) {
+  public void openTreeItem(Event e) {
+    TreeItem[] selection = wTree.getSelection();
+    if (selection == null || selection.length == 0) {
       return;
     }
+    TreeItem item = selection[0];
 
-    if (onImage) {
-      // Folder icon is outside the native expander hit target.
+    DatabaseTreeNode node = (DatabaseTreeNode) item.getData();
+    if (!node.isTableLike()) {
+      // Expand/Collapse hierarchy
       item.setExpanded(!item.getExpanded());
       return;
     }
 
-    // Gutter / ">": native may or may not handle the click (works at 100%, 
often misses at
-    // high DPI). Wait one display turn; only complement when Expand/Collapse 
did not fire
-    // around this click — avoids the expand-then-collapse race at 100%.
-    final TreeItem target = item;
-    final boolean expandedBefore = item.getExpanded();
-    final long mouseDownNanos = System.nanoTime();
-    wTree
-        .getDisplay()
-        .asyncExec(
-            () -> {
-              if (target.isDisposed()) {
-                return;
-              }
-              long delta = lastTreeExpandCollapseNanos - mouseDownNanos;
-              // Native handled if Expand/Collapse fired just before or after 
MouseDown.
-              if (Math.abs(delta) < 100_000_000L) {
-                return;
-              }
-              if (target.getExpanded() != expandedBefore) {
-                return;
-              }
-              target.setExpanded(!expandedBefore);
-            });
+    if (justLook) {
+      showTablePreview(node, false);
+    } else {
+      ok();
+    }
   }
 
-  private TreeItem findTreeItemAtY(int y) {
-    for (TreeItem root : wTree.getItems()) {
-      TreeItem found = findTreeItemAtY(root, y);
-      if (found != null) {
-        return found;
-      }
-    }
-    return null;
+  public void setSelectedSchemaAndTable(String schema, String table) {
+    this.selectedSchemaName = schema;
+    this.selectedTableName = table;
   }
 
-  private static TreeItem findTreeItemAtY(TreeItem item, int y) {
-    Rectangle textBounds = item.getBounds(0);
-    Rectangle imageBounds = item.getImageBounds(0);
-    int top = textBounds.y;
-    int bottom = textBounds.y + textBounds.height;
-    if (imageBounds != null && !imageBounds.isEmpty()) {
-      top = Math.min(top, imageBounds.y);
-      bottom = Math.max(bottom, imageBounds.y + imageBounds.height);
-    }
-    if (y >= top && y < bottom) {
-      return item;
-    }
-    if (item.getExpanded()) {
-      for (TreeItem child : item.getItems()) {
-        TreeItem found = findTreeItemAtY(child, y);
-        if (found != null) {
-          return found;
-        }
-      }
-    }
-    return null;
+  public String getSchemaName() {
+    return selectedSchemaName;
   }
 
-  public void setSelectedSchemaAndTable(String schema, String table) {
-    this.selectedSchema = schema;
-    this.selectedTable = table;
+  public String getTableName() {
+    return selectedTableName;
   }
 }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
 
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
index f60e87d5b6..66cb4137e5 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/GetDatabaseInfoProgressDialog.java
@@ -40,9 +40,9 @@ import org.eclipse.swt.widgets.Shell;
 public class GetDatabaseInfoProgressDialog {
   private static final Class<?> PKG = GetDatabaseInfoProgressDialog.class;
 
-  private Shell shell;
+  private final Shell shell;
   private final IVariables variables;
-  private DatabaseMeta databaseMeta;
+  private final DatabaseMeta databaseMeta;
 
   /**
    * Creates a new dialog that will handle the wait while we're finding out 
what tables, views etc.
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java 
b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
index 774e72331e..4d9cf3f9ed 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/database/dialog/SqlEditor.java
@@ -125,15 +125,8 @@ public class SqlEditor {
 
     Button wExec = new Button(shell, SWT.PUSH);
     wExec.setText(BaseMessages.getString(PKG, "SQLEditor.Button.Execute"));
-    wExec.addListener(
-        SWT.Selection,
-        e -> {
-          try {
-            exec();
-          } catch (Exception ge) {
-            // Ignore errors
-          }
-        });
+    wExec.addListener(SWT.Selection, e -> exec());
+
     Button wClear = new Button(shell, SWT.PUSH);
     wClear.setText(BaseMessages.getString(PKG, "SQLEditor.Button.ClearCache"));
     wClear.setToolTipText(BaseMessages.getString(PKG, 
"SQLEditor.Button.ClearCache.Tooltip"));
@@ -298,47 +291,38 @@ public class SqlEditor {
   }
 
   private void exec() {
-    DatabaseMeta databaseMeta = connection;
-    if (databaseMeta == null) {
-      return;
-    }
-
-    String sqlScript =
-        Utils.isEmpty(wScript.getSelectionText()) ? wScript.getText() : 
wScript.getSelectionText();
-    StringBuilder message = new StringBuilder();
-    ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell);
+    try {
+      DatabaseMeta databaseMeta = connection;
+      if (databaseMeta == null) {
+        return;
+      }
 
-    DatabaseProgressCancelWatcher.startIfDesktop(pmd, activeDb::get, 
"Hop-SqlEditor-CancelWatcher");
+      String sqlScript =
+          Utils.isEmpty(wScript.getSelectionText())
+              ? wScript.getText()
+              : wScript.getSelectionText();
 
-    try {
-      pmd.run(true, monitor -> runSqlScriptWithMonitor(monitor, databaseMeta, 
sqlScript, message));
+      ProgressMonitorDialog pmd = new ProgressMonitorDialog(shell);
+      DatabaseProgressCancelWatcher.startIfDesktop(
+          pmd, activeDb::get, "Hop-SqlEditor-CancelWatcher");
+      pmd.run(true, monitor -> runSqlScriptWithMonitor(monitor, databaseMeta, 
sqlScript));
     } catch (InterruptedException | InvocationTargetException e) {
       if (e instanceof InterruptedException) {
         Thread.currentThread().interrupt();
       }
     }
-
-    EnterTextDialog dialog =
-        new EnterTextDialog(
-            shell,
-            BaseMessages.getString(PKG, "SQLEditor.Result.Title"),
-            BaseMessages.getString(PKG, "SQLEditor.Result.Message"),
-            message.toString(),
-            true);
-    dialog.open();
   }
 
   private void runSqlScriptWithMonitor(
-      IProgressMonitor monitor,
-      DatabaseMeta databaseMeta,
-      String sqlScript,
-      StringBuilder message) {
+      IProgressMonitor monitor, DatabaseMeta databaseMeta, String sqlScript) {
     monitor.beginTask(BaseMessages.getString(PKG, 
"SQLEditor.Monitor.Execute"), 100);
     String raw =
         variables.getVariable(
             Const.HOP_QUERY_PREVIEW_TIMEOUT,
             EnvUtil.getSystemProperty(Const.HOP_QUERY_PREVIEW_TIMEOUT, "0"));
     int timeoutSeconds = Math.max(0, Const.toInt(variables.resolve(raw), 0));
+    StringBuilder message = new StringBuilder();
+
     try (Database db = new Database(loggingObject, variables, databaseMeta)) {
       activeDb.set(db);
       if (timeoutSeconds > 0) {
@@ -348,7 +332,9 @@ public class SqlEditor {
       db.connect();
 
       int[] nrStats = {0};
+
       runScriptStatementsLoop(db, databaseMeta, sqlScript, message, monitor, 
nrStats);
+
       message.append(
           BaseMessages.getString(PKG, "SQLEditor.Log.StatsExecuted", 
Integer.toString(nrStats[0])));
       message.append(Const.CR);
@@ -356,8 +342,8 @@ public class SqlEditor {
       handleConnectFailure(dbe, message);
     } finally {
       activeDb.set(null);
-      shell.getDisplay().asyncExec(this::refreshExecutionResults);
       monitor.done();
+      shell.getDisplay().asyncExec(this::refreshExecutionResults);
     }
   }
 
@@ -371,26 +357,55 @@ public class SqlEditor {
     List<SqlScriptStatement> statements =
         databaseMeta.getIDatabase().getSqlScriptStatements(sqlScript + 
Const.CR);
     boolean aborted = false;
-    for (SqlScriptStatement sql : statements) {
+    boolean isQuery = false;
+    for (SqlScriptStatement statement : statements) {
       if (aborted || monitor.isCanceled()) {
         break;
       }
-      if (sql.isQuery()) {
+      if (statement.isQuery()) {
         nrStats[0]++;
-        executeSelectStatement(db, sql, nrStats[0], message);
+        isQuery = true;
+        executeSelectStatement(db, statement, nrStats[0], message, monitor);
       } else {
         nrStats[0]++;
-        aborted = executeDdlStatement(db, sql, databaseMeta, message);
+        aborted = executeDdlStatement(db, statement, databaseMeta, message);
       }
     }
+
+    // Close progress monitor before displaying result
+    monitor.done();
+
+    if (!isQuery) {
+      shell
+          .getDisplay()
+          .syncExec(
+              () -> {
+                EnterTextDialog dialog =
+                    new EnterTextDialog(
+                        shell,
+                        BaseMessages.getString(PKG, "SQLEditor.Result.Title"),
+                        BaseMessages.getString(PKG, 
"SQLEditor.Result.Message"),
+                        message.toString(),
+                        true);
+                dialog.open();
+              });
+    }
   }
 
   private void executeSelectStatement(
-      Database db, SqlScriptStatement sql, int statNr, StringBuilder message) {
+      Database db,
+      SqlScriptStatement sql,
+      int statNr,
+      StringBuilder message,
+      IProgressMonitor monitor) {
     log.logDetailed("launch SELECT statement: " + Const.CR + sql);
     try {
       List<Object[]> rows = db.getRows(sql.getStatement(), 1000);
       IRowMeta rowMeta = db.getReturnRowMeta();
+
+      // Close progress monitor before displaying result
+      monitor.done();
+
       if (!rows.isEmpty()) {
         shell
             .getDisplay()
@@ -417,6 +432,7 @@ public class SqlEditor {
                 });
       }
     } catch (HopDatabaseException dbe) {
+      monitor.done();
       handleSelectExecutionFailure(dbe, sql, message);
     }
   }
diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java 
b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
index 6d4af46be6..5e5405b65b 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
@@ -248,6 +248,7 @@ public class GuiResource {
   @Getter private Image imageRedo;
   @Getter private Image imageRefresh;
   @Getter private Image imageRegex;
+  @Getter private Image imageRegexDisabled;
   @Getter private Image imageRemoveAll;
   @Getter private Image imageRemoveSingle;
   @Getter private Image imageRename;
@@ -488,6 +489,7 @@ public class GuiResource {
     imageExpandAll.dispose();
     imageSearch.dispose();
     imageRegex.dispose();
+    imageRegexDisabled.dispose();
     imageNew.dispose();
     imageEdit.dispose();
     imageLocked.dispose();
@@ -801,6 +803,8 @@ public class GuiResource {
     imagePrint = loadAsResource(display, "ui/images/print.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageRefresh = loadAsResource(display, "ui/images/refresh.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageRegex = loadAsResource(display, "ui/images/regex.svg", 
ConstUi.SMALL_ICON_SIZE);
+    imageRegexDisabled =
+        loadAsResource(display, "ui/images/regex-disabled.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageRemoveAll = loadAsResource(display, "ui/images/remove_all.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageRemoveSingle =
         loadAsResource(display, "ui/images/remove_single.svg", 
ConstUi.SMALL_ICON_SIZE);
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTableInfoTab.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTableInfoTab.java
index 90ffb43cdc..3d4365a0ad 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTableInfoTab.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTableInfoTab.java
@@ -40,6 +40,7 @@ import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.ui.core.FormDataBuilder;
 import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.core.database.DatabaseTreeNode;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
index 0e9f94a03d..aa3c101b89 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseWorkbench.java
@@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import lombok.Getter;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.database.Catalog;
 import org.apache.hop.core.database.Database;
@@ -44,6 +45,8 @@ import org.apache.hop.ui.core.ConstUi;
 import org.apache.hop.ui.core.FormDataBuilder;
 import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.bus.HopGuiEvents;
+import org.apache.hop.ui.core.database.DatabaseTreeNode;
+import org.apache.hop.ui.core.database.DatabaseTreeUtil;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.gui.GuiMenuWidgets;
@@ -116,7 +119,7 @@ public class DatabaseWorkbench extends Composite implements 
TabClosable {
 
   private final IDatabaseWorkbenchHost host;
   private final Map<String, DatabaseConnectionState> connections = new 
LinkedHashMap<>();
-  private final List<TabItemHandler> items = new ArrayList<>();
+  @Getter private final List<TabItemHandler> items = new ArrayList<>();
 
   private final SashForm horizontalSash;
   private final Composite rightComposite;
@@ -650,7 +653,7 @@ public class DatabaseWorkbench extends Composite implements 
TabClosable {
         }
       }
     }
-    for (String view : namesForSchema(info.getViewMap(), 
schema.getSchemaName())) {
+    for (String view : DatabaseTreeUtil.namesForSchema(info.getViewMap(), 
schema.getSchemaName())) {
       if (filterMatcher.matches(view)) {
         return true;
       }
@@ -660,7 +663,7 @@ public class DatabaseWorkbench extends Composite implements 
TabClosable {
 
   /**
    * Tables, views and synonyms under a schema (or catalog). Views get {@code 
view.svg} via {@link
-   * DatabaseTreeNode#kindOf}.
+   * DatabaseTreeUtil#kindOf}.
    */
   private void addSchemaObjects(
       TreeItem parent,
@@ -668,19 +671,19 @@ public class DatabaseWorkbench extends Composite 
implements TabClosable {
       String schemaName,
       String[] items,
       DatabaseMetaInformation info) {
-    Collection<String> views = namesForSchema(info.getViewMap(), schemaName);
-    Collection<String> synonyms = namesForSchema(info.getSynonymMap(), 
schemaName);
+    Collection<String> views = 
DatabaseTreeUtil.namesForSchema(info.getViewMap(), schemaName);
+    Collection<String> synonyms = 
DatabaseTreeUtil.namesForSchema(info.getSynonymMap(), schemaName);
     List<String> names = new ArrayList<>();
     if (items != null) {
       names.addAll(Arrays.asList(items));
     }
     for (String view : views) {
-      if (!DatabaseTreeNode.containsIgnoreCase(names, view)) {
+      if (!DatabaseTreeUtil.containsIgnoreCase(names, view)) {
         names.add(view);
       }
     }
     for (String synonym : synonyms) {
-      if (!DatabaseTreeNode.containsIgnoreCase(names, synonym)) {
+      if (!DatabaseTreeUtil.containsIgnoreCase(names, synonym)) {
         names.add(synonym);
       }
     }
@@ -689,37 +692,14 @@ public class DatabaseWorkbench extends Composite 
implements TabClosable {
       if (!matchesFilter(name, schemaName, connectionName)) {
         continue;
       }
-      DatabaseTreeNode.Kind kind = DatabaseTreeNode.kindOf(name, views, 
synonyms);
+      DatabaseTreeNode.Kind kind = DatabaseTreeUtil.kindOf(name, views, 
synonyms);
       TreeItem item = new TreeItem(parent, SWT.NONE);
       item.setText(name);
-      item.setImage(imageFor(kind));
+      item.setImage(DatabaseTreeUtil.imageFor(kind));
       item.setData(DatabaseTreeNode.table(kind, connectionName, schemaName, 
name));
     }
   }
 
-  static Collection<String> namesForSchema(Map<String, Collection<String>> 
map, String schemaName) {
-    if (map == null || map.isEmpty()) {
-      return List.of();
-    }
-    if (schemaName != null) {
-      Collection<String> exact = map.get(schemaName);
-      if (exact != null) {
-        return exact;
-      }
-      for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
-        if (schemaName.equalsIgnoreCase(entry.getKey()) && entry.getValue() != 
null) {
-          return entry.getValue();
-        }
-      }
-    }
-    Collection<String> empty = map.get("");
-    if (empty != null) {
-      return empty;
-    }
-    Collection<String> missing = map.get(null);
-    return missing != null ? missing : List.of();
-  }
-
   private boolean matchesFilter(String name, String schemaName, String 
connectionName) {
     if (Utils.isEmpty(filterText)) {
       return true;
@@ -764,20 +744,11 @@ public class DatabaseWorkbench extends Composite 
implements TabClosable {
       }
       TreeItem item = new TreeItem(parent, SWT.NONE);
       item.setText(name);
-      item.setImage(imageFor(kind));
+      item.setImage(DatabaseTreeUtil.imageFor(kind));
       item.setData(DatabaseTreeNode.table(kind, connectionName, schemaName, 
name));
     }
   }
 
-  private org.eclipse.swt.graphics.Image imageFor(DatabaseTreeNode.Kind kind) {
-    GuiResource resources = GuiResource.getInstance();
-    return switch (kind) {
-      case VIEW -> resources.getImageView();
-      case SYNONYM -> resources.getImageSynonym();
-      default -> resources.getImageTable();
-    };
-  }
-
   private DatabaseTreeNode selectedNode() {
     TreeItem[] selection = tree.getSelection();
     if (selection.length != 1) {
@@ -1489,10 +1460,6 @@ public class DatabaseWorkbench extends Composite 
implements TabClosable {
     }
   }
 
-  public List<TabItemHandler> getItems() {
-    return items;
-  }
-
   public boolean remove(IHopFileTypeHandler handler) {
     if (handler == null || !handler.isCloseable()) {
       return false;
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_de_DE.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_de_DE.properties
index cdf6ad123b..a63dd82fdc 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_de_DE.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_de_DE.properties
@@ -23,12 +23,12 @@ DatabaseExplorerDialog.Error.GenDDL=Konnte die DDL nicht 
erzeugen
 DatabaseExplorerDialog.Error.RetrieveLayout=Konnte Tabellenstruktur nicht 
ermitteln.
 DatabaseExplorerDialog.Menu.GenDDL=DDL erzeugen
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=DDL f\u00FCr andere Verbindung 
generieren
-DatabaseExplorerDialog.Menu.OpenSQL=SQL-Abfrage auf [{0}] erzeugen
-DatabaseExplorerDialog.Menu.Preview100=&Voransicht der ersten 100 Zeilen von 
[{0}]
-DatabaseExplorerDialog.Menu.PreviewN=Voransicht der &ersten ... Zeilen von 
[{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Struktur der Tabelle [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=Anzahl der Zeilen in [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Tabelle [{0}] leeren
+DatabaseExplorerDialog.Menu.SQLSelect=SQL-Abfrage auf [{0}] erzeugen
+DatabaseExplorerDialog.Menu.Preview100=&Voransicht der ersten 100 Zeilen
+DatabaseExplorerDialog.Menu.PreviewN=Voransicht der &ersten ... Zeilen
+DatabaseExplorerDialog.Menu.ShowLayout=Struktur der Tabelle
+DatabaseExplorerDialog.Menu.ShowSize=Anzahl der Zeilen
+DatabaseExplorerDialog.Menu.Truncate=Tabelle leeren
 DatabaseExplorerDialog.NoConnectionsKnown.Message=Zu diesem Zeitpunkt sind 
keine weiteren Verbindungen verf\u00FCgbar, f\u00FCr die die DDL generiert 
werden k\u00F6nnte.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=Keine Verbindungen bekannt
 DatabaseExplorerDialog.NoRows.Message=Die Tabelle enth\u00E4lt keine Zeilen
@@ -42,7 +42,7 @@ DatabaseExplorerDialog.TableSize.Message=Tabelle ''{0}'' 
enth\u00E4lt {1} Zeilen
 DatabaseExplorerDialog.TableSize.Title=Zeilenanzahl
 DatabaseExplorerDialog.TargetDatabase.Message=W\u00E4hlen Sie die 
Zieldatenbank aus
 DatabaseExplorerDialog.TargetDatabase.Title=Zieldatenbank
-DatabaseExplorerDialog.Title=Datenbankexplorer auf Verbindung [{0}]
+DatabaseExplorerDialog.Title=Datenbankexplorer
 DatabaseExplorerDialog.Views.Label=Sichten
 Dialog.Error.Header=Fehler
 GetDatabaseInfoProgressDialog.Error.GettingInfoTable=Es ist ein Problem beim 
Abrufen von Informationen aus der Datenbank aufgetreten: {0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
index 8315c8e5c3..72bbf3d143 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_en_US.properties
@@ -18,22 +18,27 @@
 DatabaseExplorerDialog.Catalogs.Label=Catalogs
 DatabaseExplorerDialog.Error.GenDDL=Couldn''t generate the DDL
 DatabaseExplorerDialog.Error.RetrieveLayout=Couldn''t retrieve the table 
layout.
-DatabaseExplorerDialog.Menu.GenDDL=Generate DDL
-DatabaseExplorerDialog.Menu.GenDDLOtherConn=Generate DDL for other connection
-DatabaseExplorerDialog.Menu.OpenSQL=Open SQL for [{0}]
-DatabaseExplorerDialog.Menu.Preview100=&Preview first 100 rows of [{0}]
-DatabaseExplorerDialog.Menu.PreviewN=&Preview first ... rows of [{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Show layout of [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=Number of rows of [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Truncate table [{0}]
+DatabaseExplorerDialog.Menu.GenDDL=Generate DDL...
+DatabaseExplorerDialog.Menu.GenDDLOtherConn=Generate DDL for other 
connection...
+DatabaseExplorerDialog.Menu.SQLSelect=Generate SQL Select...
+DatabaseExplorerDialog.Menu.Preview100=&Preview first 100 rows...
+DatabaseExplorerDialog.Menu.PreviewN=&Preview first … rows...
+DatabaseExplorerDialog.Menu.ShowLayout=Show layout...
+DatabaseExplorerDialog.Menu.ShowSize=Number of rows...
+DatabaseExplorerDialog.Menu.Truncate=Truncate table...
+DatabaseExplorerDialog.Toolbar.SQLSelect.Tooltip=Generate SQL Select...
+DatabaseExplorerDialog.Toolbar.Preview.Tooltip=Preview first 100 rows...
+DatabaseExplorerDialog.Toolbar.ShowLayout.Tooltip=Show layout...
 DatabaseExplorerDialog.NoConnectionsKnown.Message=At this point no other 
connections are available to generate the DDL for.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=No connections known
 DatabaseExplorerDialog.NoRows.Message=The table contains no rows
 DatabaseExplorerDialog.NoRows.Title=No rows
 DatabaseExplorerDialog.PreviewTable.Message=Number of lines to preview (0 
means all lines)
 DatabaseExplorerDialog.PreviewTable.Title=Preview limit
+DatabaseExplorerDialog.RegEx.Tooltip=Use wildcard (regular expressions)
 DatabaseExplorerDialog.ShowRows.Title=Table data
 DatabaseExplorerDialog.ShowRows.Message=Rows of table {0}
+DatabaseExplorerDialog.Search.Placeholder=Search
 PreviewTableSettingsDialog.Error.InvalidNumber=Enter valid whole numbers for 
rows and timeout.
 PreviewTableSettingsDialog.Error.Title=Invalid input
 PreviewTableSettingsDialog.Rows.Hint=0 means no row limit on the query (fetch 
all rows).
@@ -48,7 +53,7 @@ DatabaseExplorerDialog.TableSize.Message=Table ''{0}'' 
contains {1} rows
 DatabaseExplorerDialog.TableSize.Title=Number of rows
 DatabaseExplorerDialog.TargetDatabase.Message=Select the target database
 DatabaseExplorerDialog.TargetDatabase.Title=Target database
-DatabaseExplorerDialog.Title=Database explorer on connection [{0}]
+DatabaseExplorerDialog.Title=Database explorer
 DatabaseExplorerDialog.Views.Label=Views
 Dialog.Error.Header=Error
 GetDatabaseInfoProgressDialog.Error.GettingInfoTable=Problem encountered 
getting information from the database\: {0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_AR.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_AR.properties
index 61e7e5edae..c7eb5bce64 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_AR.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_AR.properties
@@ -23,12 +23,12 @@ DatabaseExplorerDialog.Error.GenDDL=No pudo generarse el DDL
 DatabaseExplorerDialog.Error.RetrieveLayout=No pudo obtenerse el diagrama de 
tablas.
 DatabaseExplorerDialog.Menu.GenDDL=Generar DDL
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=Generar DDL para otra conexi\u00F3n
-DatabaseExplorerDialog.Menu.OpenSQL=Abrir SQL para [{0}]
-DatabaseExplorerDialog.Menu.Preview100=&Prever las primeros 100 filas de [{0}]
-DatabaseExplorerDialog.Menu.PreviewN=&Prever primeras ... filas de [{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Mostrar diagrama de [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=N\u00FAmero de filas de [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Truncar tablas [{0}]
+DatabaseExplorerDialog.Menu.SQLSelect=Abrir SQL para [{0}]
+DatabaseExplorerDialog.Menu.Preview100=&Prever las primeros 100 filas
+DatabaseExplorerDialog.Menu.PreviewN=&Prever primeras ... filas
+DatabaseExplorerDialog.Menu.ShowLayout=Mostrar diagrama
+DatabaseExplorerDialog.Menu.ShowSize=N\u00FAmero de filas
+DatabaseExplorerDialog.Menu.Truncate=Truncar tablas
 DatabaseExplorerDialog.NoConnectionsKnown.Message=En este momento no hay otras 
conexiones disponibles para generar DDL.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=No se conocen conexiones
 DatabaseExplorerDialog.NoRows.Message=La tabla no contiene filas
@@ -42,7 +42,7 @@ DatabaseExplorerDialog.TableSize.Message=La tabla "{0}" 
contiene {1} filas
 DatabaseExplorerDialog.TableSize.Title=N\u00FAmero de filas
 DatabaseExplorerDialog.TargetDatabase.Message=Seleccionar la base de datos 
destino
 DatabaseExplorerDialog.TargetDatabase.Title=Base de datos destino
-DatabaseExplorerDialog.Title=Explorador de base de datso en la conexi\u00F3n 
[{0}]
+DatabaseExplorerDialog.Title=Explorador de base de datso
 DatabaseExplorerDialog.Views.Label=Vistas
 Dialog.Error.Header=Error
 GetDatabaseInfoProgressDialog.Error.Message=\u00A1Ocurri\u00F3 un error al 
obtener informaci\u00F3n de la base de datos\!
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_ES.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_ES.properties
index 3545367eaf..07f73aa353 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_ES.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_es_ES.properties
@@ -21,12 +21,12 @@ DatabaseExplorerDialog.Error.GenDDL=No se ha podido generar 
el DDL
 DatabaseExplorerDialog.Error.RetrieveLayout=No se ha podido recuperar el 
dise\u00F1o de la tabla.
 DatabaseExplorerDialog.Menu.GenDDL=Generar DDL
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=Generar DDL para otra conexi\u00F3n
-DatabaseExplorerDialog.Menu.OpenSQL=Abrir SQL para [{0}]
-DatabaseExplorerDialog.Menu.Preview100=&Previsualizar las 100 primeras filas 
de [{0}]
-DatabaseExplorerDialog.Menu.PreviewN=&Previsualizar primeras ... filas de [{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Mostrar disposici\u00F3n de [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=N\u00FAmero de filas de [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Truncar tabla [{0}]
+DatabaseExplorerDialog.Menu.SQLSelect=Abrir SQL para [{0}]
+DatabaseExplorerDialog.Menu.Preview100=&Previsualizar las 100 primeras filas
+DatabaseExplorerDialog.Menu.PreviewN=&Previsualizar primeras ... filas
+DatabaseExplorerDialog.Menu.ShowLayout=Mostrar disposici\u00F3n
+DatabaseExplorerDialog.Menu.ShowSize=N\u00FAmero de filas
+DatabaseExplorerDialog.Menu.Truncate=Truncar tabla
 DatabaseExplorerDialog.NoConnectionsKnown.Message=En este punto no hay otras 
conexiones disponibles para generar el DDL.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=Sin conexiones conocidas
 DatabaseExplorerDialog.NoRows.Message=La tabla no contiene filas
@@ -40,7 +40,7 @@ DatabaseExplorerDialog.TableSize.Message=La tabla ''{0}'' 
contiene {1} filas
 DatabaseExplorerDialog.TableSize.Title=N\u00FAmero de filas
 DatabaseExplorerDialog.TargetDatabase.Message=Seleccione la base de datos de 
destino
 DatabaseExplorerDialog.TargetDatabase.Title=Base de datos de destino
-DatabaseExplorerDialog.Title=Explorador de base de datos en conexi\u00F3n [{0}]
+DatabaseExplorerDialog.Title=Explorador de base de datos
 DatabaseExplorerDialog.Views.Label=Vistas
 Dialog.Error.Header=Error
 GetDatabaseInfoProgressDialog.Error.GettingInfoTable=Problema al obtener 
informaci\u00F3n de la base de datos\: {0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_fr_FR.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_fr_FR.properties
index 2913f289b1..f67c489fca 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_fr_FR.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_fr_FR.properties
@@ -23,12 +23,12 @@ DatabaseExplorerDialog.Error.GenDDL=Impossible de 
g\u00E9n\u00E9rer les instruct
 DatabaseExplorerDialog.Error.RetrieveLayout=Impossible de r\u00E9cup\u00E9rer 
les informations depuis la table.
 DatabaseExplorerDialog.Menu.GenDDL=G\u00E9n\u00E9rer instructions DDL
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=G\u00E9n\u00E9rer instructions DDL 
pour autre connexion
-DatabaseExplorerDialog.Menu.OpenSQL=Ouvrir La fen\u00EAtre ex\u00E9cution SQL 
pour [{0}]
-DatabaseExplorerDialog.Menu.Preview100=&Pr\u00E9visualiser les 100 
premi\u00E8res lignes de [{0}]
-DatabaseExplorerDialog.Menu.PreviewN=&Pr\u00E9visualiser les ... 
premi\u00E8res lignes de [{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Afficher structure sortie de [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=Nombre de lignes de [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Tronquer la table [{0}]
+DatabaseExplorerDialog.Menu.SQLSelect=Ouvrir La fen\u00EAtre ex\u00E9cution SQL
+DatabaseExplorerDialog.Menu.Preview100=&Pr\u00E9visualiser les 100 
premi\u00E8res lignes
+DatabaseExplorerDialog.Menu.PreviewN=&Pr\u00E9visualiser les ... 
premi\u00E8res lignes
+DatabaseExplorerDialog.Menu.ShowLayout=Afficher structure sortie
+DatabaseExplorerDialog.Menu.ShowSize=Nombre de lignes
+DatabaseExplorerDialog.Menu.Truncate=Tronquer la table
 DatabaseExplorerDialog.NoConnectionsKnown.Message=Aucune autre connexion 
n''est disponible.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=Aucune connexion
 DatabaseExplorerDialog.NoRows.Message=La table est vide
@@ -42,7 +42,7 @@ DatabaseExplorerDialog.TableSize.Message=La table ''{0}'' 
contient {1} ligne(s)
 DatabaseExplorerDialog.TableSize.Title=Nombre de lignes
 DatabaseExplorerDialog.TargetDatabase.Message=S\u00E9lectionner la base de 
donn\u00E9es cible\:
 DatabaseExplorerDialog.TargetDatabase.Title=Base de donn\u00E9es cible
-DatabaseExplorerDialog.Title=Explorateur base de donn\u00E9es sur la connexion 
[{0}]
+DatabaseExplorerDialog.Title=Explorateur base de donn\u00E9es
 DatabaseExplorerDialog.Views.Label=Vues
 Dialog.Error.Header=Erreur
 GetDatabaseInfoProgressDialog.Error.GettingInfoTable=Apache Hop a 
rencontr\u00E9 une erreur lors de la tentative de r\u00E9cup\u00E9ration des 
informations depuis la base de donn\u00E9es\: {0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_it_IT.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_it_IT.properties
index c1aa7b6e9e..389e9ddfc2 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_it_IT.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_it_IT.properties
@@ -20,12 +20,12 @@ DatabaseExplorerDialog.Error.GenDDL=Impossibile generare il 
DDL
 DatabaseExplorerDialog.Error.RetrieveLayout=Impossibile recuperare il layout 
della tabella.
 DatabaseExplorerDialog.Menu.GenDDL=Genera DDL
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=Genera DDL per altra connessione
-DatabaseExplorerDialog.Menu.OpenSQL=Apri SQL per [{0}]
-DatabaseExplorerDialog.Menu.Preview100=&Anteprima delle prime 100 righe di 
[{0}]
-DatabaseExplorerDialog.Menu.PreviewN=&Anteprima delle prime ... righe di [{0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Mostra layout di [{0}]
-DatabaseExplorerDialog.Menu.ShowSize=Numero di righe di [{0}]
-DatabaseExplorerDialog.Menu.Truncate=Tronca tabella [{0}]
+DatabaseExplorerDialog.Menu.SQLSelect=Apri SQL per [{0}]
+DatabaseExplorerDialog.Menu.Preview100=&Anteprima delle prime 100 righe
+DatabaseExplorerDialog.Menu.PreviewN=&Anteprima delle prime ... righe
+DatabaseExplorerDialog.Menu.ShowLayout=Mostra layout
+DatabaseExplorerDialog.Menu.ShowSize=Numero di righe
+DatabaseExplorerDialog.Menu.Truncate=Tronca tabella
 DatabaseExplorerDialog.NoConnectionsKnown.Message=A questo punto nessuna 
connessione \u00E8 disponibile per generare DDL.
 DatabaseExplorerDialog.NoConnectionsKnown.Title=Nessuna connessione conosciuta
 DatabaseExplorerDialog.NoRows.Message=La tabella non contiene righe
@@ -39,7 +39,7 @@ DatabaseExplorerDialog.TableSize.Message=La tabella ''{0}'' 
contiene {1} righe
 DatabaseExplorerDialog.TableSize.Title=Numero di righe
 DatabaseExplorerDialog.TargetDatabase.Message=Seleziona il database di 
destinazione\:
 DatabaseExplorerDialog.TargetDatabase.Title=Database di destinazione
-DatabaseExplorerDialog.Title=Database explorer per la connessione [{0}]
+DatabaseExplorerDialog.Title=Database explorer
 DatabaseExplorerDialog.Views.Label=Viste
 Dialog.Error.Header=Errore
 GetDatabaseInfoProgressDialog.Error.GettingInfoTable=E'' stato rilevato un 
problema nel recuperare le informazioni dal database\: {0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ja_JP.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ja_JP.properties
index dbc5e00cb7..5899374409 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ja_JP.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ja_JP.properties
@@ -23,7 +23,7 @@ 
DatabaseExplorerDialog.Error.GenDDL=DDL\u3092\u751F\u6210\u3067\u304D\u307E\u305
 
DatabaseExplorerDialog.Error.RetrieveLayout=\u30C6\u30FC\u30D6\u30EB\u30EC\u30A4\u30A2\u30A6\u30C8\u3092\u8AAD\u307F\u51FA\u305B\u307E\u305B\u3093\u3067\u3057\u305F\u3002
 DatabaseExplorerDialog.Menu.GenDDL=Generate DDL
 
DatabaseExplorerDialog.Menu.GenDDLOtherConn=\u4ED6\u306E\u63A5\u7D9A\u306EDDL\u751F\u6210
-DatabaseExplorerDialog.Menu.OpenSQL=[{0}] \u306ESQL\u3092\u958B\u304F
+DatabaseExplorerDialog.Menu.SQLSelect=[{0}] \u306ESQL\u3092\u958B\u304F
 DatabaseExplorerDialog.Menu.Preview100=[{0}] 
\u306E\u6700\u521D\u306E100\u884C\u3092\u30D7\u30EC\u30D3\u30E5\u30FC(&P)
 DatabaseExplorerDialog.Menu.PreviewN=[{0}] \u306E\u6700\u521D\u306E ... 
\u884C\u3092\u30D7\u30EC\u30D3\u30E5\u30FC(&P)
 DatabaseExplorerDialog.Menu.ShowLayout=[{0}] 
\u306E\u30EC\u30A4\u30A2\u30A6\u30C8\u3092\u8868\u793A
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ko_KR.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ko_KR.properties
index 6dee1a3825..5450b05bee 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ko_KR.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_ko_KR.properties
@@ -23,7 +23,7 @@ DatabaseExplorerDialog.Error.GenDDL=DDL\uC744 
\uC0DD\uC131\uD560 \uC218 \uC5C6\u
 DatabaseExplorerDialog.Error.RetrieveLayout=\uD14C\uC774\uBE14 
\uB808\uC774\uC544\uC6C3\uC744 \uAC00\uC838\uC62C \uC218 
\uC5C6\uC2B5\uB2C8\uB2E4.
 DatabaseExplorerDialog.Menu.GenDDL=DDL \uC0DD\uC131
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=\uB2E4\uB978 \uC5F0\uACB0\uC758 
DDL \uC0DD\uC131
-DatabaseExplorerDialog.Menu.OpenSQL=[{0}] \uC758 SQL \uC5F4\uAE30
+DatabaseExplorerDialog.Menu.SQLSelect=[{0}] \uC758 SQL \uC5F4\uAE30
 DatabaseExplorerDialog.Menu.Preview100=[{0}]\uC758 \uCC98\uC74C 100 
\uB85C\uC6B0 \uBBF8\uB9AC\uBCF4\uAE30
 DatabaseExplorerDialog.Menu.PreviewN=[{0}] \uC758 \uCC98\uC74C ... 
\uB85C\uC6B0 \uBBF8\uB9AC\uBCF4\uAE30(&P)
 DatabaseExplorerDialog.Menu.ShowLayout=[{0}]\uC758 \uB808\uC774\uC544\uC6C3 
\uBCF4\uAE30
@@ -42,7 +42,7 @@ DatabaseExplorerDialog.TableSize.Message=\uD14C\uC774\uBE14 
''{0}'' \uB294 {1} \
 DatabaseExplorerDialog.TableSize.Title=\uB85C\uC6B0 \uC218
 DatabaseExplorerDialog.TargetDatabase.Message=\uB300\uC0C1 
\uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC120\uD0DD:
 DatabaseExplorerDialog.TargetDatabase.Title=\uB300\uC0C1 
\uB370\uC774\uD130\uBCA0\uC774\uC2A4
-DatabaseExplorerDialog.Title=[{0}] \uC5F0\uACB0\uC758 
\uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uD0D0\uC0C9
+DatabaseExplorerDialog.Title=\uC5F0\uACB0\uC758 
\uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uD0D0\uC0C9
 DatabaseExplorerDialog.Views.Label=\uBDF0
 Dialog.Error.Header=\uC624\uB958
 
GetDatabaseInfoProgressDialog.Error.GettingInfoTable=\uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC5D0\uC11C
 \uC815\uBCF4\uB97C \uAC00\uC838\uC624\uB294 \uC911 \uBB38\uC81C \uBC1C\uC0DD: 
{0}
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_pt_BR.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_pt_BR.properties
index eaef50a596..4baf2fcea0 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_pt_BR.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_pt_BR.properties
@@ -53,10 +53,10 @@ SQLEditor.Position.Label=Linha {0} Coluna {1}
 DatabaseExplorerDialog.Error.GenDDL=Não consegui gerar o DDL.
 DatabaseExplorerDialog.Error.RetrieveLayout=Não consegui recuperar o layout da 
tabela.
 DatabaseExplorerDialog.Menu.GenDDLOtherConn=Gere DDL para outra conexão.
-DatabaseExplorerDialog.Menu.OpenSQL=Abrir SQL para [{0}]
-DatabaseExplorerDialog.Menu.Preview100=Antever as primeiras 100 fileiras de
-DatabaseExplorerDialog.Menu.PreviewN=&Preview first ... linhas de [ {0}]
-DatabaseExplorerDialog.Menu.ShowLayout=Mostrar layout de [ {0}]
+DatabaseExplorerDialog.Menu.SQLSelect=Abrir SQL
+DatabaseExplorerDialog.Menu.Preview100=Antever as primeiras 100 fileiras
+DatabaseExplorerDialog.Menu.PreviewN=&Preview first ... linhas
+DatabaseExplorerDialog.Menu.ShowLayout=Mostrar layout
 DatabaseExplorerDialog.NoConnectionsKnown.Message=Neste ponto, nenhuma outra 
conexão está disponível para gerar o DDL.
 DatabaseExplorerDialog.PreviewTable.Message=Número de linhas para visualização 
(0 significa todas as linhas)
 DatabaseExplorerDialog.TargetDatabase.Message=Selecione o banco de dados alvo
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_zh_CN.properties
 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_zh_CN.properties
index f825ace114..a7986ec9af 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_zh_CN.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/core/database/dialog/messages/messages_zh_CN.properties
@@ -22,7 +22,7 @@ DatabaseExplorerDialog.Error.GenDDL=\u4E0D\u80FD\u751F\u6210 
DDL \u8BED\u53E5
 
DatabaseExplorerDialog.Error.RetrieveLayout=\u4E0D\u80FD\u62BD\u53D6\u8868\u7ED3\u6784
 DatabaseExplorerDialog.Menu.GenDDL=\u751F\u6210 DDL
 
DatabaseExplorerDialog.Menu.GenDDLOtherConn=\u4E3A\u5176\u5B83\u8FDE\u63A5\u751F\u6210
 DDL
-DatabaseExplorerDialog.Menu.OpenSQL=\u6253\u5F00 {{0}} \u4E0A\u7684 SQL 
+DatabaseExplorerDialog.Menu.SQLSelect=\u6253\u5F00 {{0}} \u4E0A\u7684 SQL 
 DatabaseExplorerDialog.Menu.Preview100=\u9884\u89C8 [{0}] \u7684\u524D 100 
\u884C(&P)
 DatabaseExplorerDialog.Menu.PreviewN=\u9884\u89C8 [{0}] \u7684\u524D ... 
\u884C(&P)
 DatabaseExplorerDialog.Menu.ShowLayout=\u67E5\u770B [{0}] 
\u7684\u8868\u7ED3\u6784
diff --git a/ui/src/main/resources/ui/images/layout.svg 
b/ui/src/main/resources/ui/images/layout.svg
new file mode 100644
index 0000000000..6eb3d76211
--- /dev/null
+++ b/ui/src/main/resources/ui/images/layout.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="24" height="24" viewBox="0 0 24 
24">
+    <path fill="none" stroke="#0F3B5A" stroke-linecap="round" 
stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 
1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1zm6-1v18m6-18v18"/>
+</svg>
\ No newline at end of file
diff --git a/ui/src/main/resources/ui/images/regex-disabled.svg 
b/ui/src/main/resources/ui/images/regex-disabled.svg
new file mode 100644
index 0000000000..10bad68b5f
--- /dev/null
+++ b/ui/src/main/resources/ui/images/regex-disabled.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 24 24">
+    <path fill="#D7D7D7" d="M16,16.92C15.67,16.97 15.34,17 15,17C14.66,17 
14.33,16.97 14,16.92V13.41L11.5,15.89C11,15.5 10.5,15 
10.11,14.5L12.59,12H9.08C9.03,11.67 9,11.34 9,11C9,10.66 9.03,10.33 
9.08,10H12.59L10.11,7.5C10.3,7.25 10.5,7 10.76,6.76V6.76C11,6.5 11.25,6.3 
11.5,6.11L14,8.59V5.08C14.33,5.03 14.66,5 15,5C15.34,5 15.67,5.03 
16,5.08V8.59L18.5,6.11C19,6.5 19.5,7 19.89,7.5L17.41,10H20.92C20.97,10.33 
21,10.66 21,11C21,11.34 20.97,11.67 20.92,12H17.41L19.89,14.5C19.7,14.75 
19.5,15 1 [...]
+</svg>
\ No newline at end of file
diff --git a/ui/src/main/resources/ui/images/regex.svg 
b/ui/src/main/resources/ui/images/regex.svg
index 85e589eb0e..469cf27c7c 100644
--- a/ui/src/main/resources/ui/images/regex.svg
+++ b/ui/src/main/resources/ui/images/regex.svg
@@ -1,20 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg"; width="16" height="16">
-    <g>
-        <g>
-            <rect fill="none" width="16" height="16"/>
-        </g>
-    </g>
-    <g>
-        <g>
-            <polygon fill="#0E3A5A" stroke="#0E3A5A" stroke-miterlimit="10" 
points="1.9,1.5 4,1.5 4,0.5 0.6,0.5 0.6,15.5 4,15.5 4,14.5
-                       1.9,14.5                "/>
-            <polygon fill="#0E3A5A" stroke="#0E3A5A" stroke-miterlimit="10" 
points="12,0.5 12,1.5 14.1,1.5 14.1,14.5 12,14.5 12,15.5
-                       15.4,15.5 15.4,0.5              "/>
-            <polygon fill="#0E3A5A" stroke="#0E3A5A" stroke-width="0.25" 
stroke-miterlimit="10" points="3.4,3.5 8.7,12.8 9.6,12.8 4.2,3.5
-                                       "/>
-            <polygon fill="#0E3A5A" points="10.7,6 12.6,6.2 12.6,5.3 10.7,5.6 
10.7,5.6 11.9,4 11.2,3.5 10.4,5.3 10.3,5.3 9.5,3.5 8.8,4
-                       10,5.6 10,5.6 8.1,5.3 8.1,6.2 10,6 10,6 8.8,7.6 9.5,8 
10.3,6.2 10.4,6.2 11.1,8 11.9,7.5 10.7,6          "/>
-        </g>
-    </g>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 24 24">
+    <path fill="#0F3B5A" d="M16,16.92C15.67,16.97 15.34,17 15,17C14.66,17 
14.33,16.97 14,16.92V13.41L11.5,15.89C11,15.5 10.5,15 
10.11,14.5L12.59,12H9.08C9.03,11.67 9,11.34 9,11C9,10.66 9.03,10.33 
9.08,10H12.59L10.11,7.5C10.3,7.25 10.5,7 10.76,6.76V6.76C11,6.5 11.25,6.3 
11.5,6.11L14,8.59V5.08C14.33,5.03 14.66,5 15,5C15.34,5 15.67,5.03 
16,5.08V8.59L18.5,6.11C19,6.5 19.5,7 19.89,7.5L17.41,10H20.92C20.97,10.33 
21,10.66 21,11C21,11.34 20.97,11.67 20.92,12H17.41L19.89,14.5C19.7,14.75 
19.5,15 1 [...]
+</svg>
\ No newline at end of file
diff --git a/ui/src/main/resources/ui/images/run-all.svg 
b/ui/src/main/resources/ui/images/run-all.svg
index 076840be38..1839ad3d78 100644
--- a/ui/src/main/resources/ui/images/run-all.svg
+++ b/ui/src/main/resources/ui/images/run-all.svg
@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg";>
     <path fill="#0e3a5a"
           d="m 8.8514669,8.6850151 -6.363,3.6919999 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 V 4.2960151 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.363,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
-          style="fill:#4da2e3;fill-opacity:.4"/>
+          style="fill:#0e3a5a;fill-opacity:.4"/>
     <path fill="#0e3a5a"
           d="M 11.553752,8.7145888 5.1907514,12.406589 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 V 4.3255888 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.3630006,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
-          style="fill:#1b74b4;fill-opacity:.7"/>
+          style="fill:#0e3a5a;fill-opacity:.7"/>
     <path fill="#0e3a5a"
           d="M 14.274315,8.6187097 7.9113144,12.31071 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 V 4.2297097 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.3630006,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
-          style="fill:#1b74b4;fill-opacity:1"/>
+          style="fill:#0e3a5a;fill-opacity:1"/>
 </svg>
diff --git 
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNodeTest.java
 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNodeTest.java
index 87ebb25b6b..b97ffb3643 100644
--- 
a/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNodeTest.java
+++ 
b/ui/src/test/java/org/apache/hop/ui/hopgui/perspective/database/DatabaseTreeNodeTest.java
@@ -24,6 +24,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.apache.hop.ui.core.database.DatabaseTreeNode;
+import org.apache.hop.ui.core.database.DatabaseTreeUtil;
 import org.junit.jupiter.api.Test;
 
 class DatabaseTreeNodeTest {
@@ -32,25 +34,25 @@ class DatabaseTreeNodeTest {
   void kindOfPrefersViewThenSynonymThenTable() {
     assertEquals(
         DatabaseTreeNode.Kind.VIEW,
-        DatabaseTreeNode.kindOf("orders_v", Set.of("orders_v"), Set.of()));
+        DatabaseTreeUtil.kindOf("orders_v", Set.of("orders_v"), Set.of()));
     assertEquals(
         DatabaseTreeNode.Kind.VIEW,
-        DatabaseTreeNode.kindOf("ORDERS_V", Set.of("orders_v"), Set.of()));
+        DatabaseTreeUtil.kindOf("ORDERS_V", Set.of("orders_v"), Set.of()));
     assertEquals(
         DatabaseTreeNode.Kind.SYNONYM,
-        DatabaseTreeNode.kindOf("orders_s", Set.of(), Set.of("orders_s")));
+        DatabaseTreeUtil.kindOf("orders_s", Set.of(), Set.of("orders_s")));
     assertEquals(
         DatabaseTreeNode.Kind.TABLE,
-        DatabaseTreeNode.kindOf("orders", Set.of("orders_v"), Set.of()));
+        DatabaseTreeUtil.kindOf("orders", Set.of("orders_v"), Set.of()));
   }
 
   @Test
   void namesForSchemaMatchesIgnoreCaseAndEmptyKeys() {
     Map<String, java.util.Collection<String>> map =
         Map.of("Public", List.of("v1"), "", List.of("root_view"));
-    assertEquals(List.of("v1"), DatabaseWorkbench.namesForSchema(map, 
"public"));
-    assertEquals(List.of("root_view"), DatabaseWorkbench.namesForSchema(map, 
null));
-    assertTrue(DatabaseWorkbench.namesForSchema(Map.of(), "public").isEmpty());
+    assertEquals(List.of("v1"), DatabaseTreeUtil.namesForSchema(map, 
"public"));
+    assertEquals(List.of("root_view"), DatabaseTreeUtil.namesForSchema(map, 
null));
+    assertTrue(DatabaseTreeUtil.namesForSchema(Map.of(), "public").isEmpty());
   }
 
   @Test
@@ -64,8 +66,8 @@ class DatabaseTreeNodeTest {
 
   @Test
   void containsIgnoreCase() {
-    assertTrue(DatabaseTreeNode.containsIgnoreCase(List.of("Alpha"), "alpha"));
-    assertFalse(DatabaseTreeNode.containsIgnoreCase(List.of("Alpha"), "beta"));
-    assertFalse(DatabaseTreeNode.containsIgnoreCase(null, "alpha"));
+    assertTrue(DatabaseTreeUtil.containsIgnoreCase(List.of("Alpha"), "alpha"));
+    assertFalse(DatabaseTreeUtil.containsIgnoreCase(List.of("Alpha"), "beta"));
+    assertFalse(DatabaseTreeUtil.containsIgnoreCase(null, "alpha"));
   }
 }

Reply via email to