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 2e399c63f9 Prevent the creation of invalid hops in a visual manner 
#6281 (#6293)
2e399c63f9 is described below

commit 2e399c63f97c5d064901fa313a47086882bbc610
Author: Nicolas Adment <[email protected]>
AuthorDate: Tue Jan 6 11:20:04 2026 +0100

    Prevent the creation of invalid hops in a visual manner #6281 (#6293)
---
 .../java/org/apache/hop/pipeline/PipelineMeta.java |  51 +++---
 .../org/apache/hop/pipeline/PipelinePainter.java   |  16 +-
 .../java/org/apache/hop/workflow/WorkflowMeta.java |  73 ++++-----
 .../org/apache/hop/workflow/WorkflowPainter.java   |  21 +--
 .../hopgui/file/pipeline/HopGuiPipelineGraph.java  | 160 ++++++++++--------
 .../hopgui/file/workflow/HopGuiWorkflowGraph.java  | 179 ++++++++++++---------
 .../delegates/HopGuiWorkflowHopDelegate.java       |  10 +-
 .../workflow/messages/messages_en_US.properties    |   5 +-
 .../workflow/messages/messages_fr_FR.properties    |   2 +-
 .../ui/hopgui/messages/messages_en_US.properties   |   2 +-
 10 files changed, 287 insertions(+), 232 deletions(-)

diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java 
b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
index 02923524d5..b940234ce1 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
@@ -581,10 +581,10 @@ public class PipelineMeta extends AbstractMeta
    * metadata at the specified index to the specified meta-data object.
    *
    * @param i The index into the hops list
-   * @param hi The hop meta-data to set
+   * @param hop The hop meta-data to set
    */
-  public void setPipelineHop(int i, PipelineHopMeta hi) {
-    hops.set(i, hi);
+  public void setPipelineHop(int i, PipelineHopMeta hop) {
+    hops.set(i, hop);
     clearCaches();
   }
 
@@ -649,15 +649,12 @@ public class PipelineMeta extends AbstractMeta
    * Searches the list of hops for a hop with a certain name.
    *
    * @param name The name of the hop to look for
-   * @return The hop information or null if nothing was found.
+   * @return The hop found or null if nothing was found.
    */
   public PipelineHopMeta findPipelineHop(String name) {
-    int i;
-
-    for (i = 0; i < nrPipelineHops(); i++) {
-      PipelineHopMeta hi = getPipelineHop(i);
-      if (hi.toString().equalsIgnoreCase(name)) {
-        return hi;
+    for (PipelineHopMeta hop : hops) {
+      if (hop.toString().equalsIgnoreCase(name)) {
+        return hop;
       }
     }
     return null;
@@ -667,13 +664,14 @@ public class PipelineMeta extends AbstractMeta
    * Search all hops for a hop where a certain transform is at the start.
    *
    * @param fromTransform The transform at the start of the hop.
-   * @return The hop or null if no hop was found.
+   * @return The first hop found or null if nothing was found.
    */
   public PipelineHopMeta findPipelineHopFrom(TransformMeta fromTransform) {
-    for (PipelineHopMeta hop : hops) {
-      if (hop.getFromTransform() != null
-          && hop.getFromTransform().equals(fromTransform)) { // return the 
first
-        return hop;
+    if (fromTransform != null) {
+      for (PipelineHopMeta hop : hops) {
+        if (fromTransform.equals(hop.getFromTransform())) { // return the first
+          return hop;
+        }
       }
     }
     return null;
@@ -689,11 +687,11 @@ public class PipelineMeta extends AbstractMeta
   /**
    * Find a certain hop in the pipeline.
    *
-   * @param hi The hop information to look for.
+   * @param hop The hop information to look for.
    * @return The hop or null if no hop was found.
    */
-  public PipelineHopMeta findPipelineHop(PipelineHopMeta hi) {
-    return findPipelineHop(hi.getFromTransform(), hi.getToTransform());
+  public PipelineHopMeta findPipelineHop(PipelineHopMeta hop) {
+    return findPipelineHop(hop.getFromTransform(), hop.getToTransform());
   }
 
   /**
@@ -712,13 +710,13 @@ public class PipelineMeta extends AbstractMeta
    *
    * @param from The transform at the start of the hop.
    * @param to The transform at the end of the hop.
-   * @param disabledToo the disabled too
+   * @param includeDisabled include disabled hop
    * @return The hop or null if no hop was found.
    */
   public PipelineHopMeta findPipelineHop(
-      TransformMeta from, TransformMeta to, boolean disabledToo) {
+      TransformMeta from, TransformMeta to, boolean includeDisabled) {
     for (PipelineHopMeta hop : hops) {
-      if ((hop.isEnabled() || disabledToo)
+      if ((hop.isEnabled() || includeDisabled)
           && hop.getFromTransform() != null
           && hop.getToTransform() != null
           && hop.getFromTransform().equals(from)
@@ -733,13 +731,14 @@ public class PipelineMeta extends AbstractMeta
    * Search all hops for a hop where a certain transform is at the end.
    *
    * @param toTransform The transform at the end of the hop.
-   * @return The hop or null if no hop was found.
+   * @return The hop or null if nothing was found.
    */
   public PipelineHopMeta findPipelineHopTo(TransformMeta toTransform) {
-    for (PipelineHopMeta hop : hops) {
-      if (hop.getToTransform() != null
-          && hop.getToTransform().equals(toTransform)) { // Return the first!
-        return hop;
+    if (toTransform != null) {
+      for (PipelineHopMeta hop : hops) {
+        if (toTransform.equals(hop.getToTransform())) { // Return the first!
+          return hop;
+        }
       }
     }
     return null;
diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java 
b/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
index ebf66406c1..80dd852873 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelinePainter.java
@@ -323,15 +323,23 @@ public class PipelinePainter extends 
BasePainter<PipelineHopMeta, TransformMeta>
       drawTransformPerformanceTable(transformMeta);
     }
 
-    // Display an icon on the indicated location signaling to the user that 
the transform in
-    // question does not accept input
+    // Display a red cross on the indicated location signaling to the user 
that the transform in
+    // question does not accept input or is not a good candidate for a hop 
(duplicate hop or loop)
     //
     if (noInputTransform != null) {
       gc.setLineWidth(2);
       gc.setForeground(EColor.RED);
       Point n = noInputTransform.getLocation();
-      gc.drawLine(n.x - 5, n.y - 5, n.x + iconSize + 10, n.y + iconSize + 10);
-      gc.drawLine(n.x - 5, n.y + iconSize + 5, n.x + iconSize + 5, n.y - 5);
+      gc.drawLine(
+          round(offset.x + n.x - 1),
+          round(offset.y + n.y - 1),
+          round(offset.x + n.x + iconSize + 1),
+          round(offset.y + n.y + iconSize + 1));
+      gc.drawLine(
+          round(offset.x + n.x - 1),
+          round(offset.y + n.y + iconSize + 1),
+          round(offset.x + n.x + iconSize + 1),
+          round(offset.y + n.y - 1));
     }
 
     try {
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java 
b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
index a8cbe208a9..de0404aabd 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
@@ -662,7 +662,7 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Adds the action.
    *
-   * @param action the je
+   * @param action the action meta to add
    */
   public void addAction(ActionMeta action) {
     workflowActions.add(action);
@@ -673,7 +673,7 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Adds the workflow hop.
    *
-   * @param hop the hi
+   * @param hop the workflow hop meta to add
    */
   public void addWorkflowHop(WorkflowHopMeta hop) {
     workflowHops.add(hop);
@@ -683,23 +683,23 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Adds the action.
    *
-   * @param p the p
-   * @param action the si
+   * @param index index at which the specified action is to be inserted
+   * @param action the action meta to add
    */
-  public void addAction(int p, ActionMeta action) {
-    workflowActions.add(p, action);
+  public void addAction(int index, ActionMeta action) {
+    workflowActions.add(index, action);
     changedActions = true;
   }
 
   /**
    * Adds the workflow hop.
    *
-   * @param p the p
-   * @param hop the hi
+   * @param index index at which the specified hop is to be inserted
+   * @param hop the workflow hop meta to add
    */
-  public void addWorkflowHop(int p, WorkflowHopMeta hop) {
+  public void addWorkflowHop(int index, WorkflowHopMeta hop) {
     try {
-      workflowHops.add(p, hop);
+      workflowHops.add(index, hop);
     } catch (IndexOutOfBoundsException e) {
       workflowHops.add(hop);
     }
@@ -709,10 +709,10 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Removes the action.
    *
-   * @param i the i
+   * @param index the index of the action to be removed
    */
-  public void removeAction(int i) {
-    ActionMeta deleted = workflowActions.remove(i);
+  public void removeAction(int index) {
+    ActionMeta deleted = workflowActions.remove(index);
     if (deleted != null) {
       // give transform a chance to cleanup
       deleted.setParentWorkflowMeta(null);
@@ -791,8 +791,8 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Find workflow hop.
    *
-   * @param name the name
-   * @return the workflow hop meta
+   * @param name the name of the hop to look for
+   * @return the workflow hop meta or null if nothing was found.
    */
   public WorkflowHopMeta findWorkflowHop(String name) {
     for (WorkflowHopMeta hop : workflowHops) {
@@ -806,18 +806,15 @@ public class WorkflowMeta extends AbstractMeta
   }
 
   /**
-   * Find workflow hop from.
+   * Find the first workflow hop from.
    *
-   * @param action the action meta
-   * @return the workflow hop meta
+   * @param action the action meta at the start of the hop.
+   * @return the first hop found or null if nothing was found.
    */
   public WorkflowHopMeta findWorkflowHopFrom(ActionMeta action) {
     if (action != null) {
       for (WorkflowHopMeta hop : workflowHops) {
-
-        // Return the first we find!
-        //
-        if (hop != null && (hop.getFromAction() != null) && 
hop.getFromAction().equals(action)) {
+        if (action.equals(hop.getFromAction())) {
           return hop;
         }
       }
@@ -828,9 +825,9 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Find workflow hop.
    *
-   * @param from the from
-   * @param to the to
-   * @return the workflow hop meta
+   * @param from the action meta at the start of the hop
+   * @param to the to action meta at the end of the hop.
+   * @return the workflow hop meta or null if nothing was found.
    */
   public WorkflowHopMeta findWorkflowHop(ActionMeta from, ActionMeta to) {
     return findWorkflowHop(from, to, false);
@@ -839,15 +836,14 @@ public class WorkflowMeta extends AbstractMeta
   /**
    * Find workflow hop.
    *
-   * @param from the from
-   * @param to the to
-   * @param includeDisabled the include disabled
-   * @return the workflow hop meta
+   * @param from the action meta at the start of the hop
+   * @param to the to action meta at the end of the hop.
+   * @param includeDisabled include disabled hop
+   * @return the workflow hop meta or null if nothing was found.
    */
   public WorkflowHopMeta findWorkflowHop(ActionMeta from, ActionMeta to, 
boolean includeDisabled) {
     for (WorkflowHopMeta hop : workflowHops) {
       if ((hop.isEnabled() || includeDisabled)
-          && hop != null
           && hop.getFromAction() != null
           && hop.getToAction() != null
           && hop.getFromAction().equals(from)
@@ -859,16 +855,17 @@ public class WorkflowMeta extends AbstractMeta
   }
 
   /**
-   * Find workflow hop to.
+   * Find the first workflow hop to.
    *
-   * @param actionMeta the action meta
-   * @return the workflow hop meta
+   * @param action the to action meta at the end of the hop.
+   * @return the first workflow hop meta or null if nothing was found.
    */
-  public WorkflowHopMeta findWorkflowHopTo(ActionMeta actionMeta) {
-    for (WorkflowHopMeta hop : workflowHops) {
-      if (hop != null && hop.getToAction() != null && 
hop.getToAction().equals(actionMeta)) {
-        // Return the first!
-        return hop;
+  public WorkflowHopMeta findWorkflowHopTo(ActionMeta action) {
+    if (action != null) {
+      for (WorkflowHopMeta hop : workflowHops) {
+        if (action.equals(hop.getToAction())) {
+          return hop;
+        }
       }
     }
     return null;
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java 
b/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
index 60ea1d2977..3cfce6792d 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowPainter.java
@@ -194,23 +194,24 @@ public class WorkflowPainter extends 
BasePainter<WorkflowHopMeta, ActionMeta> {
       drawAction(actionMeta);
     }
 
-    // Display an icon on the indicated location signaling to the user that 
the action in
-    // question does not accept input
+    // Display a red cross on the indicated location signaling to the user 
that the action in
+    // question does not accept input or is not a good candidate for a hop 
(duplicate hop or
+    // workflow loop)
     //
     if (noInputAction != null) {
       gc.setLineWidth(2);
       gc.setForeground(EColor.RED);
       Point n = noInputAction.getLocation();
       gc.drawLine(
-          round(offset.x + n.x - 5),
-          round(offset.y + n.y - 5),
-          round(offset.x + n.x + iconSize + 5),
-          round(offset.y + n.y + iconSize + 5));
+          round(offset.x + n.x - 1),
+          round(offset.y + n.y - 1),
+          round(offset.x + n.x + iconSize + 1),
+          round(offset.y + n.y + iconSize + 1));
       gc.drawLine(
-          round(offset.x + n.x - 5),
-          round(offset.y + n.y + iconSize + 5),
-          round(offset.x + n.x + iconSize + 5),
-          round(offset.y + n.y - 5));
+          round(offset.x + n.x - 1),
+          round(offset.y + n.y + iconSize + 1),
+          round(offset.x + n.x + iconSize + 1),
+          round(offset.y + n.y - 1));
     }
 
     try {
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
index 1372f23149..659afb1431 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
@@ -145,7 +145,6 @@ import org.apache.hop.ui.core.dialog.TransformFieldsDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
 import org.apache.hop.ui.core.gui.HopNamespace;
-import org.apache.hop.ui.core.widget.OsHelper;
 import org.apache.hop.ui.hopgui.CanvasFacade;
 import org.apache.hop.ui.hopgui.CanvasListener;
 import org.apache.hop.ui.hopgui.HopGui;
@@ -383,7 +382,7 @@ public class HopGuiPipelineGraph extends HopGuiAbstractGraph
   private Point endHopLocation;
   private boolean startErrorHopTransform;
 
-  private TransformMeta noInputTransform;
+  private TransformMeta forbiddenTransform;
 
   private TransformMeta endHopTransform;
 
@@ -735,26 +734,21 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
             return;
           }
 
-          if (candidate != null && !OsHelper.isMac()) {
-            // Avoid duplicate pop-up for hop handling as candidate is never 
null? */
-            addCandidateAsHop(event.x, event.y);
-          }
-
           currentTransform = (TransformMeta) areaOwner.getOwner();
 
-          for (ITransformSelectionListener listener : 
currentTransformListeners) {
-            listener.onUpdateSelection(currentTransform);
-          }
-
-          // ALT-Click: edit error handling
-          //
-          if (event.button == 1 && alt && 
currentTransform.supportsErrorHandling()) {
+          if (startHopTransform != null) {
+            // If we click on the start hop transform or a forbidden 
transform, then we don't have a
+            // candidate hop, but we need to ignore this click to not start a 
drag operation.
+            if (candidate != null) {
+              addCandidateAsHop(event.x, event.y);
+            }
+          } else if (event.button == 1 && alt && 
currentTransform.supportsErrorHandling()) {
+            // ALT-Click: edit error handling
+            //
             pipelineTransformDelegate.editTransformErrorHandling(pipelineMeta, 
currentTransform);
             return;
-          } else if (event.button == 1 && startHopTransform != null && 
endHopTransform == null) {
-            candidate = new PipelineHopMeta(startHopTransform, 
currentTransform);
           } else if (event.button == 2 || (event.button == 1 && shift)) {
-            // SHIFT CLICK is start of drag to create a new hop
+            // SHIFT CLICK: start drawing a new hop
             //
             canvas.setData("mode", "hop");
             startHopTransform = currentTransform;
@@ -763,6 +757,11 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
             dragSelection = true;
             selectedTransforms = pipelineMeta.getSelectedTransforms();
             selectedTransform = currentTransform;
+
+            for (ITransformSelectionListener listener : 
currentTransformListeners) {
+              listener.onUpdateSelection(currentTransform);
+            }
+
             //
             // When an icon is moved that is not selected, it gets
             // selected too late.
@@ -792,8 +791,8 @@ public class HopGuiPipelineGraph extends HopGuiAbstractGraph
       }
     }
 
-    // Layer 2: click on hop links between transforms
-    if (!done) {
+    // Layer 2: click on hop links between transforms and not drawing a hop
+    if (!done && startHopTransform == null) {
       // hop links between transforms are found searching by (x,y) coordinates.
       PipelineHopMeta hop = findPipelineHop(real.x, real.y);
       if (hop != null) {
@@ -818,8 +817,11 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       }
     }
 
-    // Layer 3: click on a note
-    if (!done && areaOwner != null && areaOwner.getAreaType() == 
AreaOwner.AreaType.NOTE) {
+    // Layer 3: click on a note and not drawing a hop
+    if (!done
+        && startHopTransform == null
+        && areaOwner != null
+        && areaOwner.getAreaType() == AreaOwner.AreaType.NOTE) {
       currentNotePad = (NotePadMeta) areaOwner.getOwner();
       selectedNotes = pipelineMeta.getSelectedNotes();
       selectedNote = currentNotePad;
@@ -844,11 +846,12 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
 
     // Layer 4: Click on the background of the graph
     if (!done) {
-      // If we're dragging a candidate hop around and click on the background 
it simply needs to
+      // If we're drawing a candidate hop and click on the background it 
simply needs to
       // go away.
       //
       if (startHopTransform != null) {
         startHopTransform = null;
+        endHopLocation = null;
         candidate = null;
         lastClick = null;
         avoidContextDialog = true;
@@ -890,6 +893,7 @@ public class HopGuiPipelineGraph extends HopGuiAbstractGraph
   @Override
   public void mouseUp(MouseEvent e) {
     resize = null;
+    forbiddenTransform = null;
 
     // canvas.setData("mode", null); does not work.
     canvas.setData("mode", "null");
@@ -904,6 +908,7 @@ public class HopGuiPipelineGraph extends HopGuiAbstractGraph
 
     if (viewPortNavigation || viewDrag) {
       viewDrag = false;
+      viewDragStart = null;
       viewPortNavigation = false;
       viewPortStart = null;
       return;
@@ -914,8 +919,6 @@ public class HopGuiPipelineGraph extends HopGuiAbstractGraph
     updateErrorMetaForHop(selectedHop);
     boolean singleClick = false;
     mouseOverName = null;
-    viewDrag = false;
-    viewDragStart = null;
     SingleClickType singleClickType = null;
     TransformMeta singleClickTransform = null;
     NotePadMeta singleClickNote = null;
@@ -971,10 +974,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
           break;
         case TRANSFORM_ICON:
           if (startHopTransform != null) {
-            // Mouse up while dragging around a hop candidate
-            //
-            currentTransform = (TransformMeta) areaOwner.getOwner();
-            candidate = new PipelineHopMeta(startHopTransform, 
currentTransform);
+            // Mouse up while drawing a hop candidate
             addCandidateAsHop(e.x, e.y);
             redraw();
             return;
@@ -987,14 +987,10 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
               && selectedNotes == null) {
             // This is available only in single click mode...
             //
-            startHopTransform = null;
-            selectionRegion = null;
-
             TransformMeta transformMeta = (TransformMeta) 
areaOwner.getParent();
             editTransform(transformMeta);
           }
           return;
-
         default:
           break;
       }
@@ -1057,10 +1053,10 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       // OK, we moved the transform, did we move it across a hop?
       // If so, ask to split the hop!
       if (splitHop) {
-        PipelineHopMeta hi =
+        PipelineHopMeta hop =
             findPipelineHop(icon.x + iconSize / 2, icon.y + iconSize / 2, 
selectedTransform);
-        if (hi != null) {
-          splitHop(hi);
+        if (hop != null) {
+          splitHop(hop);
         }
         splitHop = false;
       }
@@ -1409,7 +1405,6 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
   @Override
   public void mouseMove(MouseEvent event) {
     boolean shift = (event.stateMask & SWT.SHIFT) != 0;
-    noInputTransform = null;
     mouseMovedSinceClick = true;
     boolean doRedraw = false;
 
@@ -1417,6 +1412,16 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
     //
     toolTip.setVisible(false);
 
+    // First, check for operations that have been started, such as move 
selection, dragging the
+    // view, creating a hop or resizing a note.
+
+    // Drag the view around with middle button on the background?
+    //
+    if (viewDrag && lastClick != null) {
+      dragView(viewDragStart, new Point(event.x, event.y));
+      return;
+    }
+
     // Check to see if we're navigating with the view port
     //
     if (viewPortNavigation) {
@@ -1563,53 +1568,66 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       doRedraw = true;
     } else if ((startHopTransform != null && endHopTransform == null)
         || (endHopTransform != null && startHopTransform == null)) {
+
       // Are we creating a new hop with the middle button or pressing SHIFT?
       //
-
       TransformMeta transformMeta = pipelineMeta.getTransform(real.x, real.y, 
iconSize);
       endHopLocation = new Point(real.x, real.y);
-      if (transformMeta != null
-          && ((startHopTransform != null && 
!startHopTransform.equals(transformMeta))
-              || (endHopTransform != null && 
!endHopTransform.equals(transformMeta)))) {
+      if (transformMeta != null) {
         ITransformIOMeta ioMeta = 
transformMeta.getTransform().getTransformIOMeta();
-        if (candidate == null) {
-          // See if the transform accepts input. If not, we can't create a new 
hop...
-          //
+
+        // Checks if mouse over another transformn
+        if ((startHopTransform != null && 
!startHopTransform.equals(transformMeta))
+            || (endHopTransform != null && 
!endHopTransform.equals(transformMeta))) {
+
           if (startHopTransform != null) {
-            if (ioMeta.isInputAcceptor()) {
-              candidate = new PipelineHopMeta(startHopTransform, 
transformMeta);
-              endHopLocation = null;
-            } else {
-              noInputTransform = transformMeta;
+
+            // Check if the transform accepts input. If not, we can't create a 
new hop...
+            //
+            if (!ioMeta.isInputAcceptor()) {
+              forbiddenTransform = transformMeta;
               toolTip.setText("This transform does not accept any input from 
other transforms");
-              showToolTip(new org.eclipse.swt.graphics.Point(real.x, real.y));
+              showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
+            }
+            // Check if the hop already exists
+            else if (pipelineMeta.findPipelineHop(startHopTransform, 
transformMeta, true) != null) {
+              forbiddenTransform = transformMeta;
+              toolTip.setText(BaseMessages.getString(PKG, 
"HopGui.Dialog.HopExists.Message"));
+              showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
+            }
+            // Check if the hop candidate creates a loop
+            else {
+              candidate = new PipelineHopMeta(startHopTransform, 
transformMeta);
+              pipelineMeta.addPipelineHop(candidate);
+              boolean hasLoop = pipelineMeta.hasLoop(transformMeta);
+              pipelineMeta.removePipelineHop(candidate);
+
+              if (hasLoop) {
+                candidate = null;
+                forbiddenTransform = transformMeta;
+                toolTip.setText(
+                    BaseMessages.getString(PKG, 
"PipelineGraph.Dialog.HopCausesLoop.Message"));
+                showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
+              }
             }
           } else if (endHopTransform != null) {
             if (ioMeta.isOutputProducer()) {
               candidate = new PipelineHopMeta(transformMeta, endHopTransform);
               endHopLocation = null;
             } else {
-              noInputTransform = transformMeta;
+              forbiddenTransform = transformMeta;
               toolTip.setText(
                   "This transform doesn't pass any output to other transforms. 
(except perhaps for targeted output)");
-              showToolTip(new org.eclipse.swt.graphics.Point(real.x, real.y));
+              showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
             }
           }
         }
       } else {
-        if (candidate != null) {
-          candidate = null;
-          doRedraw = true;
-        }
+        candidate = null;
+        forbiddenTransform = null;
       }
 
       doRedraw = true;
-    } else {
-      // Drag the view around with middle button on the background?
-      //
-      if (viewDrag && lastClick != null) {
-        dragView(viewDragStart, new Point(event.x, event.y));
-      }
     }
 
     // Move around notes and transforms
@@ -1710,11 +1728,17 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
 
   private void addCandidateAsHop(int mouseX, int mouseY) {
 
-    boolean forward = startHopTransform != null;
+    if (candidate == null) {
+      return;
+    }
 
+    boolean forward = startHopTransform != null;
     TransformMeta fromTransform = candidate.getFromTransform();
     TransformMeta toTransform = candidate.getToTransform();
-    if (fromTransform.equals(toTransform)) {
+
+    // A couple of sanity checks...
+    //
+    if (fromTransform == null || toTransform == null || 
fromTransform.equals(toTransform)) {
       return; // Don't add
     }
 
@@ -1815,7 +1839,6 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       return;
     }
 
-    candidate = null;
     selectedTransforms = null;
     startHopTransform = null;
     endHopLocation = null;
@@ -1833,7 +1856,10 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
     if (candidate == null) {
       return;
     }
-    this.startHopTransform = null;
+
+    startHopTransform = null;
+    endHopLocation = null;
+
     switch (stream.getStreamType()) {
       case ERROR:
         addErrorHop();
@@ -2102,7 +2128,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
 
   public void clearSettings() {
     selectedTransform = null;
-    noInputTransform = null;
+    forbiddenTransform = null;
     selectedNote = null;
     selectedTransforms = null;
     selectionRegion = null;
@@ -3435,7 +3461,7 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
       pipelinePainter.setTransformLogMap(transformLogMap);
       pipelinePainter.setStartHopTransform(startHopTransform);
       pipelinePainter.setEndHopLocation(endHopLocation);
-      pipelinePainter.setNoInputTransform(noInputTransform);
+      pipelinePainter.setNoInputTransform(forbiddenTransform);
       pipelinePainter.setEndHopTransform(endHopTransform);
       pipelinePainter.setCandidateHopType(candidateHopType);
       pipelinePainter.setStartErrorHopTransform(startErrorHopTransform);
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
index 89d04f10bf..303e340d40 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
@@ -325,7 +325,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
   private Point endHopLocation;
 
   private ActionMeta endHopAction;
-  private ActionMeta noInputAction;
+  private ActionMeta forbiddenAction;
   private Point[] previousActionLocations;
   private Point[] previousNoteLocations;
   private ActionMeta currentAction;
@@ -584,9 +584,12 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
 
           currentAction = (ActionMeta) areaOwner.getOwner();
 
-          if (hopCandidate != null) {
-            addCandidateAsHop();
-
+          if (startHopAction != null) {
+            // If we click on the start hop action or a forbidden action, then 
we don't have a
+            // candidate hop, but we need to ignore this click to not start a 
drag operation
+            if (hopCandidate != null) {
+              addCandidateAsHop();
+            }
           } else if (event.button == 2 || (event.button == 1 && shift)) {
             // SHIFT CLICK is start of drag to create a new hop
             //
@@ -654,7 +657,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
     }
 
     // Layer 2: click on hop links between actions
-    if (!done) {
+    if (!done && startHopAction == null) {
       // Hop links between actions are found searching by (x,y) coordinates.
       WorkflowHopMeta hop = findWorkflowHop(real.x, real.y);
       if (hop != null) {
@@ -681,7 +684,10 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
     }
 
     // Layer 3: click on a note
-    if (!done && areaOwner != null && areaOwner.getAreaType() == 
AreaOwner.AreaType.NOTE) {
+    if (!done
+        && startHopAction == null
+        && areaOwner != null
+        && areaOwner.getAreaType() == AreaOwner.AreaType.NOTE) {
       currentNotePad = (NotePadMeta) areaOwner.getOwner();
       selectedNotes = workflowMeta.getSelectedNotes();
       selectedNote = currentNotePad;
@@ -719,6 +725,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
       if (startHopAction != null) {
         startHopAction = null;
         hopCandidate = null;
+        endHopLocation = null;
         lastClick = null;
         redraw();
         return;
@@ -758,6 +765,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
   public void mouseUp(MouseEvent event) {
     resize = null;
     dragSelection = false;
+    forbiddenAction = null;
 
     // canvas.setData("mode", null); does not work.
     canvas.setData("mode", "null");
@@ -771,6 +779,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
 
     if (viewPortNavigation || viewDrag) {
       viewDrag = false;
+      viewDragStart = null;
       viewPortNavigation = false;
       viewPortStart = null;
       return;
@@ -783,8 +792,6 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
     ActionMeta singleClickAction = null;
     NotePadMeta singleClickNote = null;
     WorkflowHopMeta singleClickHop = null;
-    viewDrag = false;
-    viewDragStart = null;
     mouseOverName = null;
 
     if (iconOffset == null) {
@@ -832,8 +839,7 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
       switch (areaOwner.getAreaType()) {
         case ACTION_ICON:
           if (startHopAction != null) {
-            currentAction = (ActionMeta) areaOwner.getOwner();
-            hopCandidate = new WorkflowHopMeta(startHopAction, currentAction);
+            // Mouse up while drawing a hop candidate
             addCandidateAsHop();
             redraw();
           }
@@ -844,9 +850,6 @@ public class HopGuiWorkflowGraph extends HopGuiAbstractGraph
               && selectedActions == null
               && selectedNotes == null) {
             // This is available only in single click mode...
-            //
-            startHopAction = null;
-            selectionRegion = null;
             ActionMeta actionMeta = (ActionMeta) areaOwner.getParent();
             editAction(actionMeta);
             return;
@@ -1153,13 +1156,22 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
   @Override
   public void mouseMove(MouseEvent event) {
     boolean shift = (event.stateMask & SWT.SHIFT) != 0;
-    noInputAction = null;
     boolean doRedraw = false;
 
     // disable the tooltip
     //
     hideToolTips();
 
+    // First, check for operations that have been started, such as move 
selection, dragging the
+    // view, creating a hop or resizing a note.
+
+    // Drag the view around with middle button on the background?
+    //
+    if (viewDrag && lastClick != null) {
+      dragView(viewDragStart, new Point(event.x, event.y));
+      return;
+    }
+
     // Check to see if we're navigating with the view port
     //
     if (viewPortNavigation) {
@@ -1173,6 +1185,12 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
     //
     lastMove = real;
 
+    // Resizing the current note
+    if (resize != null) {
+      resizeNote(selectedNote, real);
+      return;
+    }
+
     if (iconOffset == null) {
       iconOffset = new Point(0, 0);
     }
@@ -1183,23 +1201,6 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
     }
     Point note = new Point(real.x - noteOffset.x, real.y - noteOffset.y);
 
-    // First, check for operations that have been started, such as move 
selection, dragging the
-    // view, creating a hop or
-    // resizing a note.
-
-    // Drag the view around with middle button on the background?
-    //
-    if (viewDrag && lastClick != null) {
-      dragView(viewDragStart, new Point(event.x, event.y));
-      return;
-    }
-
-    // Resizing the current note
-    if (resize != null) {
-      resizeNote(selectedNote, real);
-      return;
-    }
-
     // Moved over an area?
     //
     AreaOwner areaOwner = getVisibleAreaOwner(real.x, real.y);
@@ -1300,36 +1301,54 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
       doRedraw = true;
     } else if ((startHopAction != null && endHopAction == null)
         || (endHopAction != null && startHopAction == null)) {
+
       // Are we creating a new hop with the middle button or pressing SHIFT?
       //
-
       ActionMeta actionMeta = workflowMeta.getAction(real.x, real.y, iconSize);
       endHopLocation = new Point(real.x, real.y);
-      if (actionMeta != null
-          && ((startHopAction != null && !startHopAction.equals(actionMeta))
-              || (endHopAction != null && !endHopAction.equals(actionMeta)))) {
-        if (hopCandidate == null) {
-          // See if the transform accepts input. If not, we can't create a new 
hop...
-          //
+      if (actionMeta != null) {
+
+        // Checks if mouse over another action
+        if ((startHopAction != null && !startHopAction.equals(actionMeta))
+            || (endHopAction != null && !endHopAction.equals(actionMeta))) {
+
           if (startHopAction != null) {
-            if (!actionMeta.isStart()) {
-              hopCandidate = new WorkflowHopMeta(startHopAction, actionMeta);
-              endHopLocation = null;
-            } else {
-              noInputAction = actionMeta;
-              toolTip.setText("The start action can only be used at the start 
of a Workflow");
+            // Check if the start action
+            if (actionMeta.isStart()) {
+              forbiddenAction = actionMeta;
+              toolTip.setText(
+                  BaseMessages.getString(PKG, 
"WorkflowGraph.Hop.CreateHopToStartAction.Tooltip"));
+              showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
+            }
+            // Check if the hop already exists
+            else if (workflowMeta.findWorkflowHop(startHopAction, actionMeta, 
true) != null) {
+              forbiddenAction = actionMeta;
+              toolTip.setText(
+                  BaseMessages.getString(PKG, 
"WorkflowGraph.Dialog.HopExists.Message"));
               showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
             }
+            // Check if the hop candidate creates a loop
+            else {
+              hopCandidate = new WorkflowHopMeta(startHopAction, actionMeta);
+              workflowMeta.addWorkflowHop(hopCandidate);
+              boolean hasLoop = workflowMeta.hasLoop(actionMeta);
+              workflowMeta.removeWorkflowHop(hopCandidate);
+              if (hasLoop) {
+                hopCandidate = null;
+                forbiddenAction = actionMeta;
+                toolTip.setText(
+                    BaseMessages.getString(PKG, 
"WorkflowGraph.Dialog.HopCausesLoop.Message"));
+                showToolTip(new org.eclipse.swt.graphics.Point(event.x, 
event.y));
+              }
+            }
           } else if (endHopAction != null) {
             hopCandidate = new WorkflowHopMeta(actionMeta, endHopAction);
             endHopLocation = null;
           }
         }
       } else {
-        if (hopCandidate != null) {
-          hopCandidate = null;
-          doRedraw = true;
-        }
+        hopCandidate = null;
+        forbiddenAction = null;
       }
 
       doRedraw = true;
@@ -1398,42 +1417,44 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
   }
 
   private void addCandidateAsHop() {
-    if (hopCandidate != null) {
 
-      // A couple of sanity checks...
-      //
-      if (hopCandidate.getFromAction() == null || hopCandidate.getToAction() 
== null) {
-        return;
-      }
-      if (hopCandidate.getFromAction().equals(hopCandidate.getToAction())) {
-        return;
-      }
+    if (hopCandidate == null) {
+      return;
+    }
 
-      if (!hopCandidate.getFromAction().isEvaluation()
-          && hopCandidate.getFromAction().isUnconditional()) {
-        hopCandidate.setUnconditional();
-      } else {
-        hopCandidate.setConditional();
-        int nr = workflowMeta.findNrNextActions(hopCandidate.getFromAction());
-
-        // If there is one green link: make this one red! (or
-        // vice-versa)
-        if (nr == 1) {
-          ActionMeta actionMeta = 
workflowMeta.findNextAction(hopCandidate.getFromAction(), 0);
-          WorkflowHopMeta other =
-              workflowMeta.findWorkflowHop(hopCandidate.getFromAction(), 
actionMeta);
-          if (other != null) {
-            hopCandidate.setEvaluation(!other.isEvaluation());
-          }
+    // A couple of sanity checks...
+    //
+    if (hopCandidate.getFromAction() == null || hopCandidate.getToAction() == 
null) {
+      return;
+    }
+    if (hopCandidate.getFromAction().equals(hopCandidate.getToAction())) {
+      return;
+    }
+
+    if (!hopCandidate.getFromAction().isEvaluation()
+        && hopCandidate.getFromAction().isUnconditional()) {
+      hopCandidate.setUnconditional();
+    } else {
+      hopCandidate.setConditional();
+      int nr = workflowMeta.findNrNextActions(hopCandidate.getFromAction());
+
+      // If there is one green link: make this one red! (or
+      // vice-versa)
+      if (nr == 1) {
+        ActionMeta actionMeta = 
workflowMeta.findNextAction(hopCandidate.getFromAction(), 0);
+        WorkflowHopMeta other =
+            workflowMeta.findWorkflowHop(hopCandidate.getFromAction(), 
actionMeta);
+        if (other != null) {
+          hopCandidate.setEvaluation(!other.isEvaluation());
         }
       }
+    }
 
-      // Stop drawing the hop candidate
-      startHopAction = null;
+    // Stop drawing the hop candidate
+    startHopAction = null;
 
-      workflowHopDelegate.newHop(workflowMeta, hopCandidate);
-      clearSettings();
-    }
+    workflowHopDelegate.newHop(workflowMeta, hopCandidate);
+    clearSettings();
   }
 
   public boolean checkIfHopAlreadyExists(WorkflowMeta workflowMeta, 
WorkflowHopMeta newHop) {
@@ -2993,7 +3014,7 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
       workflowPainter.setStartHopAction(startHopAction);
       workflowPainter.setEndHopLocation(endHopLocation);
       workflowPainter.setEndHopAction(endHopAction);
-      workflowPainter.setNoInputAction(noInputAction);
+      workflowPainter.setNoInputAction(forbiddenAction);
       if (workflow != null) {
         workflowPainter.setActionResults(workflow.getActionResults());
       } else {
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowHopDelegate.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowHopDelegate.java
index f67c3675fc..a24aa21e0f 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowHopDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowHopDelegate.java
@@ -29,7 +29,7 @@ import org.eclipse.swt.SWT;
 
 public class HopGuiWorkflowHopDelegate {
 
-  private static final Class<?> PKG = HopGui.class;
+  private static final Class<?> PKG = HopGuiWorkflowGraph.class;
 
   private HopGui hopGui;
   private HopGuiWorkflowGraph workflowGraph;
@@ -42,8 +42,8 @@ public class HopGuiWorkflowHopDelegate {
   }
 
   public void newHop(WorkflowMeta workflowMeta, ActionMeta fr, ActionMeta to) {
-    WorkflowHopMeta hi = new WorkflowHopMeta(fr, to);
-    newHop(workflowMeta, hi);
+    WorkflowHopMeta hop = new WorkflowHopMeta(fr, to);
+    newHop(workflowMeta, hop);
   }
 
   public void newHop(WorkflowMeta workflowMeta, WorkflowHopMeta hopMeta) {
@@ -78,8 +78,8 @@ public class HopGuiWorkflowHopDelegate {
       MessageBox mb = new MessageBox(hopGui.getShell(), SWT.OK | 
SWT.ICON_ERROR);
       mb.setMessage(
           BaseMessages.getString(
-              PKG, "HopGui.Dialog.HopExists.Message")); // "This hop already 
exists!"
-      mb.setText(BaseMessages.getString(PKG, 
"HopGui.Dialog.HopExists.Title")); // Error!
+              PKG, "WorkflowGraph.Dialog.HopExists.Message")); // "This hop 
already exists!"
+      mb.setText(BaseMessages.getString(PKG, 
"WorkflowGraph.Dialog.HopExists.Title")); // Error!
       mb.open();
       ok = false;
     }
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_en_US.properties
index 220e466963..e265a65009 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_en_US.properties
@@ -119,8 +119,10 @@ WorkflowGraph.DeprecatedEntry.Tooltip.Title=Deprecated
 WorkflowGraph.Dialog.EditDescription.Message=Edit description
 WorkflowGraph.Dialog.EditDescription.Title=Description
 WorkflowGraph.Dialog.EditNote.Title=Edit note
-WorkflowGraph.Dialog.HopCausesLoop.Message=This hop causes a loop.  This is 
not allowed.
+WorkflowGraph.Dialog.HopCausesLoop.Message=This hop causes a loop in the 
workflow.
 WorkflowGraph.Dialog.HopCausesLoop.Title=Loop\!
+WorkflowGraph.Dialog.HopExists.Message=This hop already exists\!
+WorkflowGraph.Dialog.HopExists.Title=Error
 WorkflowGraph.Dialog.HopInfo=Hop information\: 
 WorkflowGraph.Dialog.HopInfo.Disable=Disabled
 WorkflowGraph.Dialog.HopInfo.Enable=Enabled
@@ -137,6 +139,7 @@ 
WorkflowGraph.ExecutionResultsPanel.CloseButton.Tooltip=Close the execution resu
 WorkflowGraph.ExecutionResultsPanel.MaxButton.Tooltip=Maximize the execution 
results panel
 WorkflowGraph.ExecutionResultsPanel.MinButton.Tooltip=Minimize the execution 
results panel
 WorkflowGraph.ExecutionResultsPanel.RotateButton.Tooltip=Rotate the execution 
results panel
+WorkflowGraph.Hop.CreateHopToStartAction.Tooltip=The start action can only be 
used at the start of a Workflow
 WorkflowGraph.Hop.Tooltip.EvaluatingFalse=After the execution of {0}, the 
result of the execution will be evaluated.{1}If the result is false (with 
errors) this path will be taken.
 WorkflowGraph.Hop.Tooltip.EvaluatingTrue=After the execution of {0}, the 
result of the execution will be evaluated.{1}If the result is true (without 
errors) this path will be taken.
 WorkflowGraph.Hop.Tooltip.Parallel=Parallel execution.{1}All actions after {0} 
will be executed in parallel.{1}That is ALL actions, not just the next 
ones.{1}If you want to resume serial processing, put the parallel actions in a 
sub-workflow.
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_fr_FR.properties
 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_fr_FR.properties
index c90c605992..d610f1aa60 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_fr_FR.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/file/workflow/messages/messages_fr_FR.properties
@@ -68,7 +68,7 @@ HopWorkflowFileType.New.Text=Nouveau workflow
 WorkflowGraph.Dialog.EditDescription.Message=Editer la description
 WorkflowGraph.Dialog.EditDescription.Title=Description
 WorkflowGraph.Dialog.EditNote.Title=Editer la note
-WorkflowGraph.Dialog.HopCausesLoop.Message=Ce lien provoque une boucle.  Ceci 
n''est pas autoris\u00E9.
+WorkflowGraph.Dialog.HopCausesLoop.Message=Ce lien provoque une boucle dans le 
workflow. Ceci n''est pas autoris\u00E9.
 WorkflowGraph.Dialog.HopCausesLoop.Title=Boucle !
 WorkflowGraph.Dialog.HopInfo.Disable=D\u00E9sactiv\u00E9
 WorkflowGraph.Dialog.HopInfo.Enable=Activ\u00E9
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
index 9c6f2700fc..c7a7081a4f 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/messages/messages_en_US.properties
@@ -39,7 +39,7 @@ HopGui.Dialog.HopExists.Message=This hop already exists\!
 HopGui.Dialog.HopExists.Title=Error
 HopGui.Dialog.OnlyUseStartOnce.Message=You can only use the start icon once in 
a workflow.
 HopGui.Dialog.OnlyUseStartOnce.Title=ERROR
-HopGui.Dialog.TransformnameExists.Message=This transform name already exists.  
HopGui changed the transform name to [{0}]
+HopGui.Dialog.TransformnameExists.Message=This transform name already exists. 
HopGui changed the transform name to [{0}]
 HopGui.Dialog.TransformnameExists.Title=Note
 HopGui.Dialog.UnableCreateNewTransform.Message=I was unable to create a new 
transform
 HopGui.Dialog.UnableCreateNewTransform.Title=Error creating transform


Reply via email to