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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 436fa6f1ed fix grid height on previews in linux, fixes #8155 (#8171)
436fa6f1ed is described below

commit 436fa6f1ed8a1f036f44223a78346088c9991cba
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Sat Aug 29 20:27:35 2026 +0200

    fix grid height on previews in linux, fixes #8155 (#8171)
---
 .../org/apache/hop/ui/testing/EditRowsDialog.java  |   5 +-
 .../transforms/datagrid/DataGridDialog.java        |   5 +-
 .../dialog/PreviewRowsDialogRowHeightTest.java     | 137 ++++++++++++++++++++
 .../dialog/ShowRowsDialogCellSelectionTest.java    |  27 +++-
 .../widget/TableViewDataGridRowHeightTest.java     | 142 +++++++++++++++++++++
 .../apache/hop/ui/core/dialog/EditRowsDialog.java  |   5 +-
 .../hop/ui/core/dialog/PreviewRowsDialog.java      |   6 +-
 .../apache/hop/ui/core/dialog/ShowRowsDialog.java  |   6 +-
 .../org/apache/hop/ui/core/widget/TableView.java   | 113 +++++++++++++---
 .../execution/PipelineExecutionViewer.java         |   6 +-
 .../execution/WorkflowExecutionViewer.java         |   6 +-
 11 files changed, 418 insertions(+), 40 deletions(-)

diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/EditRowsDialog.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/EditRowsDialog.java
index 9c08df6e2c..e13ef20e67 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/EditRowsDialog.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/EditRowsDialog.java
@@ -279,7 +279,7 @@ public class EditRowsDialog {
       }
 
       if (show != null) {
-        item.setText(c + 1, show);
+        wFields.setCellValue(item, c + 1, show);
         item.setForeground(c + 1, GuiResource.getInstance().getColorBlack());
       } else {
         // Set null value
@@ -302,7 +302,8 @@ public class EditRowsDialog {
         if 
(GuiResource.getInstance().getColorBlue().equals(item.getForeground(colnr))) {
           row[i] = null; // <null> value
         } else {
-          String string = item.getText(colnr);
+          // The cell is drawn shortened; the value itself comes back out of 
the grid.
+          String string = TableView.getCellValue(item, colnr);
           row[i] =
               valueMeta.convertDataFromString(
                   string, stringValueMeta, null, null, 
IValueMeta.TRIM_TYPE_NONE);
diff --git 
a/plugins/transforms/datagrid/src/main/java/org/apache/hop/pipeline/transforms/datagrid/DataGridDialog.java
 
b/plugins/transforms/datagrid/src/main/java/org/apache/hop/pipeline/transforms/datagrid/DataGridDialog.java
index 5a9fedf5cf..d45b84e511 100644
--- 
a/plugins/transforms/datagrid/src/main/java/org/apache/hop/pipeline/transforms/datagrid/DataGridDialog.java
+++ 
b/plugins/transforms/datagrid/src/main/java/org/apache/hop/pipeline/transforms/datagrid/DataGridDialog.java
@@ -271,7 +271,7 @@ public class DataGridDialog extends BaseTransformDialog {
       TableItem item = wData.table.getItem(i);
 
       for (int f = 0; f < line.getDatalines().size(); f++) {
-        item.setText(f + 1, Const.NVL(line.getDatalines().get(f), ""));
+        wData.setCellValue(item, f + 1, Const.NVL(line.getDatalines().get(f), 
""));
       }
     }
 
@@ -413,7 +413,8 @@ public class DataGridDialog extends BaseTransformDialog {
       DataGridDataMeta line = new DataGridDataMeta();
       TableItem item = wData.table.getItem(i);
       for (int f = 0; f < nrFields; f++) {
-        line.getDatalines().add(item.getText(f + 1));
+        // The cell is drawn shortened; the value itself comes back out of the 
grid.
+        line.getDatalines().add(TableView.getCellValue(item, f + 1));
       }
       data.add(line);
     }
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/core/dialog/PreviewRowsDialogRowHeightTest.java
 
b/rcp/src/test/java/org/apache/hop/ui/core/dialog/PreviewRowsDialogRowHeightTest.java
new file mode 100644
index 0000000000..23cf9b645c
--- /dev/null
+++ 
b/rcp/src/test/java/org/apache/hop/ui/core/dialog/PreviewRowsDialogRowHeightTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.core.dialog;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.testing.SwtBotTestBase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swtbot.swt.finder.SWTBot;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Guards the row height of the transform preview grid.
+ *
+ * <p>Row geometry is not uniform on every platform: GTK measures each row 
from the text its cells
+ * hold, so a line break that reaches the cell grows that row to as many lines 
as the value has
+ * (issue #8155). macOS gives every row the same height, so this can only ever 
fail on GTK — run it
+ * there, e.g. through {@code tools/with-isolated-display.sh}.
+ */
+@Tag("uitest")
+class PreviewRowsDialogRowHeightTest extends SwtBotTestBase {
+
+  private static final String MULTI_LINE = "first line\nsecond line\nthird 
line\nfourth line";
+
+  @Test
+  void aMultiLineValueDoesNotGrowItsRow() {
+    IRowMeta rowMeta = new RowMeta();
+    rowMeta.addValueMeta(new ValueMetaString("text"));
+    List<Object[]> rows = new ArrayList<>();
+    rows.add(new Object[] {"single line"});
+    rows.add(new Object[] {MULTI_LINE});
+    rows.add(new Object[] {"another single line"});
+
+    withDialog(
+        parent ->
+            new PreviewRowsDialog(parent, new Variables(), SWT.NONE, 
"transform", rowMeta, rows)
+                .open(),
+        bot -> {
+          Table table = awaitTable(bot);
+          assertNotNull(table, "the PreviewRowsDialog table should open");
+
+          int singleLineHeight = onUi(() -> 
table.getItem(0).getBounds(1).height);
+          int multiLineHeight = onUi(() -> 
table.getItem(1).getBounds(1).height);
+          assertEquals(
+              singleLineHeight,
+              multiLineHeight,
+              "a multi-line value must not make its row taller than a 
single-line one");
+          assertEquals(
+              MULTI_LINE,
+              onUi(() -> TableView.getCellValue(table.getItem(1), 1)),
+              "the grid must still hand back the value with all of its lines");
+        });
+  }
+
+  private Table awaitTable(SWTBot bot) {
+    for (int i = 0; i < 50; i++) {
+      Table found = onUi(PreviewRowsDialogRowHeightTest::findTableOnUi);
+      if (found != null && onUi(() -> found.getItemCount() >= 3)) {
+        return found;
+      }
+      bot.sleep(100);
+    }
+    return null;
+  }
+
+  private static Table findTableOnUi() {
+    for (Shell shell : display.getShells()) {
+      Table table = findTable(shell);
+      if (table != null) {
+        return table;
+      }
+    }
+    return null;
+  }
+
+  private static Table findTable(Composite composite) {
+    for (Control child : composite.getChildren()) {
+      if (child instanceof Table foundTable) {
+        return foundTable;
+      }
+      if (child instanceof Composite childComposite) {
+        Table found = findTable(childComposite);
+        if (found != null) {
+          return found;
+        }
+      }
+    }
+    return null;
+  }
+
+  private static <T> T onUi(Supplier<T> supplier) {
+    AtomicReference<T> result = new AtomicReference<>();
+    AtomicReference<RuntimeException> failure = new AtomicReference<>();
+    display.syncExec(
+        () -> {
+          try {
+            result.set(supplier.get());
+          } catch (RuntimeException e) {
+            failure.set(e);
+          }
+        });
+    if (failure.get() != null) {
+      throw failure.get();
+    }
+    return result.get();
+  }
+}
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/core/dialog/ShowRowsDialogCellSelectionTest.java
 
b/rcp/src/test/java/org/apache/hop/ui/core/dialog/ShowRowsDialogCellSelectionTest.java
index 9ed71ba4bb..11715a5bec 100644
--- 
a/rcp/src/test/java/org/apache/hop/ui/core/dialog/ShowRowsDialogCellSelectionTest.java
+++ 
b/rcp/src/test/java/org/apache/hop/ui/core/dialog/ShowRowsDialogCellSelectionTest.java
@@ -18,6 +18,7 @@
 package org.apache.hop.ui.core.dialog;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNotSame;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -30,6 +31,7 @@ import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.RowMeta;
 import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.variables.Variables;
+import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.testing.SwtBotTestBase;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.dnd.Clipboard;
@@ -102,11 +104,15 @@ class ShowRowsDialogCellSelectionTest extends 
SwtBotTestBase {
   }
 
   /**
-   * Copy to clipboard, export to Excel/CSV and save-to-data-set all read the 
cell straight off the
-   * table item, so the item has to hold the complete value: the grid only 
shortens it when drawing.
+   * A data grid puts the shortened, single-line text in the cell and keeps 
the value itself aside.
+   * Native tables take their geometry from the text a cell holds, and GTK 
grows a row to as many
+   * lines as its value has, so a stored line break makes the whole row tall 
no matter how short we
+   * draw it (issue #8155; invisible on macOS, whose rows are all the same 
height). The complete
+   * value stays available through {@link TableView#getCellValue(TableItem, 
int)}, which is what the
+   * in-place editor and the clipboard read.
    */
   @Test
-  void gridCellsHoldTheFullValueEvenThoughTheyAreDrawnShortened() {
+  void gridCellsHoldTheShortenedTextWhileTheGridKeepsTheFullValue() {
     String multiLine = "first line\nsecond line\nthird line";
 
     IRowMeta rowMeta = new RowMeta();
@@ -123,14 +129,21 @@ class ShowRowsDialogCellSelectionTest extends 
SwtBotTestBase {
           Table table = awaitTable(bot);
           assertNotNull(table, "the ShowRowsDialog table should open");
 
+          assertTrue(
+              onUi(() -> table.getItem(0).getText(1)).length() < 
LONG_VALUE.length(),
+              "a long value must be shortened in the cell, not stored at its 
full length");
+          assertFalse(
+              onUi(() -> table.getItem(0).getText(2)).contains("\n"),
+              "a multi-line value must be single-lined in the cell, or the row 
grows on GTK");
+
           assertEquals(
               LONG_VALUE,
-              onUi(() -> table.getItem(0).getText(1)),
-              "a long cell value must be stored in full, not truncated with an 
ellipsis");
+              onUi(() -> TableView.getCellValue(table.getItem(0), 1)),
+              "the grid must still hand back the long value in full");
           assertEquals(
               multiLine,
-              onUi(() -> table.getItem(0).getText(2)),
-              "a multi-line cell value must keep all of its lines");
+              onUi(() -> TableView.getCellValue(table.getItem(0), 2)),
+              "the grid must still hand back the multi-line value with all of 
its lines");
         });
   }
 
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/core/widget/TableViewDataGridRowHeightTest.java
 
b/rcp/src/test/java/org/apache/hop/ui/core/widget/TableViewDataGridRowHeightTest.java
new file mode 100644
index 0000000000..08f3b6ea02
--- /dev/null
+++ 
b/rcp/src/test/java/org/apache/hop/ui/core/widget/TableViewDataGridRowHeightTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.widget;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import org.apache.hop.core.variables.Variables;
+import org.apache.hop.ui.core.PropsUi;
+import org.apache.hop.ui.testing.SwtBotTestBase;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.TableItem;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Guards the row height of an <em>editable</em> data grid — the Data tab of 
the Data grid
+ * transform, and the edit-rows dialogs.
+ *
+ * <p>Row geometry is not uniform on every platform: GTK measures each row 
from the text its cells
+ * hold, so a line break that reaches the cell grows that row to as many lines 
as the value has
+ * (issue #8155). macOS gives every row the same height, so these can only 
fail on GTK.
+ */
+@Tag("uitest")
+class TableViewDataGridRowHeightTest extends SwtBotTestBase {
+
+  private static final String MULTI_LINE = "first line\nsecond line\nthird 
line\nfourth line";
+  private static final int VALUE_COLUMN = 1;
+
+  @Test
+  void aMultiLineValueDoesNotGrowItsRow() {
+    AtomicReference<TableView> gridRef = new AtomicReference<>();
+    withScene(
+        shell -> gridRef.set(buildGrid(shell)),
+        bot -> {
+          TableView grid = gridRef.get();
+          int singleLine = onUi(() -> 
grid.table.getItem(0).getBounds(VALUE_COLUMN).height);
+          int multiLine = onUi(() -> 
grid.table.getItem(1).getBounds(VALUE_COLUMN).height);
+
+          assertEquals(
+              singleLine,
+              multiLine,
+              "a multi-line value must not make its row taller than a 
single-line one");
+        });
+  }
+
+  @Test
+  void theGridHandsBackTheCompleteValue() {
+    AtomicReference<TableView> gridRef = new AtomicReference<>();
+    withScene(
+        shell -> gridRef.set(buildGrid(shell)),
+        bot ->
+            assertEquals(
+                MULTI_LINE,
+                onUi(() -> 
TableView.getCellValue(gridRef.get().table.getItem(1), VALUE_COLUMN)),
+                "the value must survive being drawn shortened, every line of 
it"));
+  }
+
+  /**
+   * The value kept aside is only good while the cell still shows the text it 
was derived from. A
+   * write that goes straight to the item — any path that does not know about 
the grid's own value
+   * accessors — has to win, so that the worst such a path can cause is a 
value drawn in full again,
+   * never a stale one handed back and saved.
+   */
+  @Test
+  void aValueWrittenStraightToTheCellTakesOver() {
+    AtomicReference<TableView> gridRef = new AtomicReference<>();
+    withScene(
+        shell -> gridRef.set(buildGrid(shell)),
+        bot -> {
+          TableItem item = onUi(() -> gridRef.get().table.getItem(1));
+          onUi(
+              () -> {
+                item.setText(VALUE_COLUMN, "written around the grid");
+                return null;
+              });
+
+          assertEquals(
+              "written around the grid",
+              onUi(() -> TableView.getCellValue(item, VALUE_COLUMN)),
+              "a cell written directly must be read back as it stands, not as 
it was");
+        });
+  }
+
+  private TableView buildGrid(org.eclipse.swt.widgets.Shell shell) {
+    shell.setLayout(new FillLayout());
+    shell.setSize(900, 320);
+    ColumnInfo[] columns = {
+      new ColumnInfo("Value", ColumnInfo.COLUMN_TYPE_TEXT, false, false),
+    };
+    TableView grid =
+        new TableView(
+            new Variables(),
+            shell,
+            SWT.BORDER | SWT.FULL_SELECTION,
+            columns,
+            3,
+            null,
+            PropsUi.getInstance());
+    // What the Data grid's Data tab and the edit-rows dialogs do: data rows, 
drawn shortened.
+    grid.setShortenDisplayedValues(true);
+    grid.setCellValue(grid.table.getItem(0), VALUE_COLUMN, "single line");
+    grid.setCellValue(grid.table.getItem(1), VALUE_COLUMN, MULTI_LINE);
+    grid.setCellValue(grid.table.getItem(2), VALUE_COLUMN, "another single 
line");
+    grid.optWidth(true);
+    return grid;
+  }
+
+  private static <T> T onUi(Supplier<T> supplier) {
+    AtomicReference<T> result = new AtomicReference<>();
+    AtomicReference<RuntimeException> failure = new AtomicReference<>();
+    display.syncExec(
+        () -> {
+          try {
+            result.set(supplier.get());
+          } catch (RuntimeException e) {
+            failure.set(e);
+          }
+        });
+    if (failure.get() != null) {
+      throw failure.get();
+    }
+    return result.get();
+  }
+}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/EditRowsDialog.java 
b/ui/src/main/java/org/apache/hop/ui/core/dialog/EditRowsDialog.java
index 70d202d09c..0cf01cb0b7 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/EditRowsDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/EditRowsDialog.java
@@ -279,7 +279,7 @@ public class EditRowsDialog {
       }
 
       if (show != null) {
-        item.setText(c + 1, show);
+        wFields.setCellValue(item, c + 1, show);
         item.setForeground(c + 1, GuiResource.getInstance().getColorBlack());
       } else {
         // Set null value
@@ -303,7 +303,8 @@ public class EditRowsDialog {
         if (isDisplayingNullValue(item, colnr)) {
           row[i] = null; // <null> value
         } else {
-          String string = item.getText(colnr);
+          // The cell is drawn shortened; the value itself comes back out of 
the grid.
+          String string = TableView.getCellValue(item, colnr);
           if (stringValueMeta.isNull(string)) {
             string = null;
           }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java 
b/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
index 4e5db37a60..1e06a314f6 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/PreviewRowsDialog.java
@@ -411,9 +411,9 @@ public class PreviewRowsDialog {
       }
 
       if (show != null) {
-        // Store the full value: the grid shortens long / multi-line text at 
paint time, so what is
-        // copied, exported or read back out of the table stays complete.
-        item.setText(c + 1, show);
+        // The cell holds the shortened, single-line text while the grid keeps 
the full value
+        // aside, so what is copied, expanded or read back out of the table 
stays complete.
+        wFields.setCellValue(item, c + 1, show);
         item.setForeground(c + 1, GuiResource.getInstance().getColorBlack());
       } else {
         // Set null value
diff --git a/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowRowsDialog.java 
b/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowRowsDialog.java
index 455d42d0b4..b0a80ff4ac 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowRowsDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/dialog/ShowRowsDialog.java
@@ -228,9 +228,9 @@ public final class ShowRowsDialog {
       }
 
       if (displayValue != null) {
-        // Store the full value: the grid shortens long / multi-line text at 
paint time, so what is
-        // copied, exported or read back out of the table stays complete.
-        item.setText(column + 1, displayValue);
+        // The cell holds the shortened, single-line text while the grid keeps 
the full value
+        // aside, so what is copied, expanded or read back out of the table 
stays complete.
+        tableView.setCellValue(item, column + 1, displayValue);
         item.setForeground(column + 1, 
GuiResource.getInstance().getColorBlack());
       } else {
         item.setText(column + 1, "<null>");
diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java 
b/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
index 05cfadb5e3..9acd521206 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/widget/TableView.java
@@ -116,6 +116,17 @@ public class TableView extends Composite {
   private static final int EXTRA_COLUMN_WIDTH_MARGIN =
       
Const.toInt(HopConfig.readStringVariable(Const.HOP_TABLE_VIEW_EXTRA_COLUMN_MARGIN,
 ""), 0);
 
+  /**
+   * Key of the {@link TableItem} data holding a row's values as {@code 
[full][displayed]}, both
+   * indexed by column. See {@link #setCellValue(TableItem, int, String)}.
+   */
+  private static final String CELL_VALUES_KEY = "TableView.CellValues";
+
+  /** Index of the complete value, and of the shortened text put in the cell, 
in that data. */
+  private static final int FULL = 0;
+
+  private static final int DISPLAYED = 1;
+
   /** Default minimum height hint in pixels for all TableView instances. */
   public static final int HEIGHT_HINT_PX = 200;
 
@@ -859,7 +870,7 @@ public class TableView extends Composite {
       final String[] fBeforeEdit = beforeEdit;
       String[] afterEdit = getItemText(row);
       checkChanged(new String[][] {fBeforeEdit}, new String[][] {afterEdit}, 
new int[] {rowNr});
-      row.setText(colNr, value);
+      setCellValue(row, colNr, value);
     };
   }
 
@@ -1745,8 +1756,12 @@ public class TableView extends Composite {
       // Keep TableItem#getData() across the rebuild so callers that tag their 
rows can still map a
       // visual row back to their own data after the user sorts a column.
       final Object[] preservedData = new Object[items.length];
+      final String[][][] preservedCellValues = new String[items.length][][];
       for (int i = 0; i < items.length; i++) {
         preservedData[i] = items[i].getData();
+        // A sort rebuilds every item from its text, so the values kept aside 
by setCellValue have
+        // to travel with the row or the grid would be left holding only the 
shortened text.
+        preservedCellValues[i] = (String[][]) 
items[i].getData(CELL_VALUES_KEY);
       }
 
       final int[] sortIndex = new int[] {sortField + 2};
@@ -1802,6 +1817,9 @@ public class TableView extends Composite {
         if (preservedData[origIdx] != null) {
           item.setData(preservedData[origIdx]);
         }
+        if (preservedCellValues[origIdx] != null) {
+          item.setData(CELL_VALUES_KEY, preservedCellValues[origIdx]);
+        }
       }
       table.setSortColumn(table.getColumn(this.sortField));
       table.setSortDirection(sortingDescending ? SWT.DOWN : SWT.UP);
@@ -1972,7 +1990,7 @@ public class TableView extends Composite {
     }
     String textData = getTextWidgetValue(colNr);
 
-    row.setText(colNr, textData);
+    setCellValue(row, colNr, textData);
     disposeInlineEditor();
     table.setFocus();
 
@@ -2118,7 +2136,7 @@ public class TableView extends Composite {
       seed = getTextWidgetValue(colNr);
       disposeInlineEditor();
     } else {
-      seed = row.getText(colNr);
+      seed = getCellValue(row, colNr);
     }
 
     setPosition(rowNr, colNr);
@@ -2127,15 +2145,19 @@ public class TableView extends Composite {
     if (!viewOnly) {
       beforeEdit = getItemText(row);
       // An edit is already in progress when the carried-over text differs 
from the stored value.
-      fieldChanged = !seed.equals(row.getText(colNr));
+      fieldChanged = !seed.equals(getCellValue(row, colNr));
     }
 
     Rectangle cellBounds = row.getBounds(colNr);
     Point location = table.toDisplay(cellBounds.x, cellBounds.y);
 
     // Resizable (but title-less) floating shell so the user can drag its 
edges to make a long value
-    // bigger, matching the read-only value viewer.
-    final Shell popup = new Shell(getShell(), SWT.RESIZE);
+    // bigger, matching the read-only value viewer. SWT.RESIZE on its own is 
only title-less on
+    // macOS: it is part of SWT.SHELL_TRIM, so GTK leaves the window decorated 
and the window
+    // manager puts a full title bar with minimize and maximize on a pop-out 
that has no use for
+    // either. Adding SWT.ON_TOP makes a child shell a GTK popup window, which 
SWT undecorates,
+    // and keeps the drag-to-resize border by way of SWT's own custom resize.
+    final Shell popup = new Shell(getShell(), SWT.ON_TOP | SWT.RESIZE);
     multilineShell = popup;
     popup.addListener(
         SWT.Dispose,
@@ -2196,8 +2218,8 @@ public class TableView extends Composite {
             // A value viewer only closes: nothing to store, nothing changed.
             return;
           }
-          if (!newValue.equals(row.getText(colNr))) {
-            row.setText(colNr, newValue);
+          if (!newValue.equals(getCellValue(row, colNr))) {
+            setCellValue(row, colNr, newValue);
           }
           String[] afterEdit = getItemText(row);
           checkChanged(new String[][] {beforeEdit}, new String[][] 
{afterEdit}, new int[] {rowNr});
@@ -2370,10 +2392,64 @@ public class TableView extends Composite {
     return "\n".equals(Text.DELIMITER) ? unix : unix.replace("\n", 
Text.DELIMITER);
   }
 
+  /**
+   * Fill a data-grid cell: the untruncated value is kept on the item and the 
cell itself only gets
+   * the shortened, single-line text.
+   *
+   * <p>Native tables take their geometry from the text a cell holds, and not 
every platform stops
+   * at the first line: GTK measures every row from its cell renderers, so a 
stored line break makes
+   * the whole row grow to as many lines as the value has (macOS keeps a 
uniform row height, which
+   * is why this stays invisible there). Drawing the value shortened is 
therefore not enough — the
+   * text we do not want drawn must not be in the cell to begin with.
+   *
+   * <p>Use it for the grids that show data rather than configuration. 
Everything in this widget
+   * that needs the real value reads it with {@link #getCellValue(TableItem, 
int)}; a grid that
+   * hands its rows out to a caller has to do the same.
+   */
+  public void setCellValue(TableItem item, int colNr, String value) {
+    if (!shortenDisplayedValues) {
+      item.setText(colNr, value);
+      return;
+    }
+    String[][] cells = (String[][]) item.getData(CELL_VALUES_KEY);
+    if (cells == null) {
+      int columnCount = table.getColumnCount();
+      cells = new String[][] {new String[columnCount], new 
String[columnCount]};
+      item.setData(CELL_VALUES_KEY, cells);
+    }
+    String display = formatCellValueForDisplay(value);
+    display = display == null ? "" : display;
+    if (colNr < cells[FULL].length) {
+      cells[FULL][colNr] = value;
+      cells[DISPLAYED][colNr] = display;
+    }
+    item.setText(colNr, display);
+  }
+
+  /**
+   * The complete value of a cell: the one kept aside by {@link 
#setCellValue(TableItem, int,
+   * String)}, or the cell text itself for the grids that hold their values in 
full.
+   *
+   * <p>The value is only used while the cell still shows the text it was 
derived from. Anything
+   * that writes the cell some other way therefore takes over cleanly: the 
worst a write path that
+   * does not know about this can cause is a value drawn in full again, never 
a stale one saved.
+   */
+  public static String getCellValue(TableItem item, int colNr) {
+    String[][] cells = (String[][]) item.getData(CELL_VALUES_KEY);
+    if (cells != null
+        && colNr < cells[FULL].length
+        && cells[FULL][colNr] != null
+        && cells[DISPLAYED][colNr].equals(item.getText(colNr))) {
+      return cells[FULL][colNr];
+    }
+    return item.getText(colNr);
+  }
+
   /**
    * The shortened display string for a text cell whose stored value is longer 
/ multi-line, or null
-   * when the cell should be drawn natively (non-text column, or nothing to 
shorten). Used by the
-   * desktop owner-draw so {@link TableItem#getText(int)} keeps returning the 
full, saved value.
+   * when the cell should be drawn natively (nothing to shorten, or the cell 
already holds the
+   * shortened text because the grid stores its values with {@link 
#setCellValue(TableItem, int,
+   * String)}).
    */
   private String customCellText(TableItem item, int columnIndex) {
     if (!shortenDisplayedValues
@@ -2388,9 +2464,8 @@ public class TableView extends Composite {
             && colinfo.getType() != ColumnInfo.COLUMN_TYPE_TEXT_BUTTON)) {
       return null;
     }
-    String full = item.getText(columnIndex);
-    String display = formatCellValueForDisplay(full);
-    return display != null && !display.equals(full) ? display : null;
+    String display = formatCellValueForDisplay(getCellValue(item, 
columnIndex));
+    return display != null && !display.equals(item.getText(columnIndex)) ? 
display : null;
   }
 
   private void eraseCell(Event event) {
@@ -2765,11 +2840,11 @@ public class TableView extends Composite {
         if (c > 1) {
           selection.append(CLIPBOARD_DELIMITER);
         }
-        String value = ti.getText(c);
+        String value = getCellValue(ti, c);
         if (StringUtils.isNotEmpty(value)) {
           Color textColor = ti.getForeground(c);
           if (!nullTextColor.equals(textColor) || !"<null>".equals(value)) {
-            selection.append(ti.getText(c));
+            selection.append(value);
           }
         }
       }
@@ -3086,7 +3161,7 @@ public class TableView extends Composite {
 
     String[] retval = new String[table.getColumnCount() - 1];
     for (int i = 0; i < retval.length; i++) {
-      retval[i] = row.getText(i + 1);
+      retval[i] = getCellValue(row, i + 1);
     }
 
     return retval;
@@ -3137,12 +3212,12 @@ public class TableView extends Composite {
     // edit values that contain a line break in the multi-line pop-out editor 
instead.
     if ((colinfo.getType() == ColumnInfo.COLUMN_TYPE_TEXT
             || colinfo.getType() == ColumnInfo.COLUMN_TYPE_TEXT_BUTTON)
-        && indexOfLineBreak(row.getText(colNr)) >= 0) {
+        && indexOfLineBreak(getCellValue(row, colNr)) >= 0) {
       editMultiline(row, rowNr, colNr, colinfo);
       return;
     }
 
-    String content = row.getText(colNr) + (!viewOnly && extra != 0 ? "" + 
extra : "");
+    String content = getCellValue(row, colNr) + (!viewOnly && extra != 0 ? "" 
+ extra : "");
     String tooltip = columns[colNr - 1].getToolTip();
 
     final boolean useVariables = !viewOnly && columns[colNr - 
1].isUsingVariables();
@@ -4680,7 +4755,7 @@ public class TableView extends Composite {
     String[] retval = new String[table.getItemCount()];
     for (int i = 0; i < retval.length; i++) {
       TableItem item = table.getItem(i);
-      retval[i] = item.getText(colNr + 1);
+      retval[i] = getCellValue(item, colNr + 1);
     }
     return retval;
   }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
index d5e915dd1d..4ccb36ff59 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
@@ -629,6 +629,10 @@ public class PipelineExecutionViewer extends 
BaseExecutionViewer
                     null,
                     props);
 
+            // Data rows, not configuration: draw long / multi-line values 
shortened. The value
+            // itself stays on the item, out of the cell, so the row keeps to 
a single line.
+            dataView.setShortenDisplayedValues(true);
+
             for (int r = 0; r < rowBuffer.size(); r++) {
               Object[] row = rowBuffer.getBuffer().get(r);
               TableItem item = dataView.table.getItem(r);
@@ -638,7 +642,7 @@ public class PipelineExecutionViewer extends 
BaseExecutionViewer
                 if (value == null) {
                   value = "";
                 }
-                item.setText(c + 1, value);
+                dataView.setCellValue(item, c + 1, value);
               }
             }
             dataView.optWidth(true);
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
index 5e2b41cb79..3ab912c293 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
@@ -441,6 +441,10 @@ public class WorkflowExecutionViewer extends 
BaseExecutionViewer
                   null,
                   props);
 
+          // Data rows, not configuration: draw long / multi-line values 
shortened. The value
+          // itself stays on the item, out of the cell, so the row keeps to a 
single line.
+          dataView.setShortenDisplayedValues(true);
+
           for (int r = 0; r < rowBuffer.size(); r++) {
             Object[] row = rowBuffer.getBuffer().get(r);
             TableItem item = dataView.table.getItem(r);
@@ -450,7 +454,7 @@ public class WorkflowExecutionViewer extends 
BaseExecutionViewer
               if (value == null) {
                 value = "";
               }
-              item.setText(c + 1, value);
+              dataView.setCellValue(item, c + 1, value);
             }
           }
           dataView.optWidth(true);

Reply via email to