=== modified file 'src/com/goldencode/p2j/ui/BaseEntity.java'
--- src/com/goldencode/p2j/ui/BaseEntity.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/BaseEntity.java	2022-05-06 20:47:54 +0000
@@ -1799,32 +1803,18 @@
     *           Value of the attribute.
     */
    @Override
-   public void setTabStop(boolean value)
+   public void setTabStop(final boolean value)
    {
       if (config.tabStop == value)
       {
          return;
       }
       
-      boolean oldValue = config.tabStop;
-      
       config.tabStop = value;
       
-      if (this.group instanceof FieldGroup)
+      if (group instanceof FieldGroup)
       {
-         FieldGroup fg = (FieldGroup) this.group;
-         // add widget to the end of NEXT-TAB-ITEM list iff the widget is not in this list
-         if (value)
-         {
-            fg.updateTabItemsOnTabStop(this, !oldValue);
-         }
-         
-         GenericFrame ownerFrame = fg.getFrame();
-         
-         if (_isRealized() && ownerFrame != null && ownerFrame.getFrameWidget()._isRealized())
-         {
-            LogicalTerminal.updateTabItemList(fg);
-         }
+         ((FieldGroup) this.group).updateTabStopForWidget(this, value);
       }
 
       pushWidgetAttr("tabStop", config.tabStop);

=== modified file 'src/com/goldencode/p2j/ui/FieldGroup.java'
--- src/com/goldencode/p2j/ui/FieldGroup.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/FieldGroup.java	2022-05-06 20:09:33 +0000
@@ -158,14 +158,9 @@
     */ 
    private Consumer<GenericWidget<?>> widgetAdder = null; 
    
-   /** The first tab item */
-   private GenericWidget<?> firstTabItem;
-   
-   /** The last tab item */
-   private GenericWidget<?> lastTabItem;
-   
-   /** Tab item list */ 
-   private List<GenericWidget<?>> tabItems;
+   /** The list of widgets in this field group with the TAP-STOP attribute value set */ 
+   private final List<GenericWidget<?>> tabItems = new LinkedList<GenericWidget<?>>();
+
    
    /**
     * Creates an instance to groups related widgets in a frame.
@@ -191,22 +186,38 @@
     */
    public void setWidgets(List<GenericWidget<?>> widgets)
    {
-      this.widgets  = widgets;
-      this.tabItems = new LinkedList<GenericWidget<?>>();
-      if (widgets != null)
+      this.widgets = widgets;
+
+      resetTabItems();
+
+      for (GenericWidget<?> next : widgets)
       {
-         for (GenericWidget<?> next : widgets)
+         if (next instanceof BaseEntity)
          {
-            this.tabItems.add(next);
-            if (next instanceof BaseEntity)
-            {
-               BaseEntity<?> be = (BaseEntity<?>) next;
-               be.setGroup(this);
-            }
+            ((BaseEntity<?>) next).setGroup(this);
          }
       }
       
+   }
+
+   /**
+    * Re-calculate tab items from current widget list and widget TAB-STOP attribute value.
+    * Reset previous changes made with TAB-INDEX setter if any. 
+    */
+   final void resetTabItems()
+   {
+      tabItems.clear();
+      for (GenericWidget<?> w : widgets)
+      {
+         if (w.isTabStop().booleanValue())
+         {
+            assert !tabItems.contains(w);
+            tabItems.add(w);
+         }
+      }
    }
    
    /**
@@ -318,9 +330,8 @@
          BaseEntity<?> be = (BaseEntity<?>) widget;
          be.setGroup(null);
       }
-      this.tabItems.remove(widget);
-      this.firstTabItem = null;
-      this.lastTabItem  = null;
+      tabItems.remove(widget);
+      assert !tabItems.contains(widget);
       
       frame.removeWidget(widget);
    }
@@ -331,10 +342,11 @@
     * @param    widget
     *           The widget to be added to this field group.
     */
+   @Override
    public void addWidget(GenericWidget<?> widget)
    {
-      this.widgets.remove(widget);
-      this.widgets.add(widget);
+      widgets.remove(widget);
+      widgets.add(widget);
       
       Utils.normalizeZOrder(widgets,  w -> { return w.getZOrderClass(); });
       
@@ -350,31 +362,62 @@
          be.setGroup(this);
       }
    }
-   
+      
    /**
-    * Adds a new tab item to the end of the tab item list if the target widget is not in this list
+    * Adds a new tab-stoppable widget to the end of the tab item list if the target widget is not in this list
     * and notifies the client about the changes. The widget can be added during addWidget calls.
     * 
     * @param    widget
-    *           The widget to be added to the tab item list.
+    *           The widget to be added to the tab item list with the attribute TAB-STOP set.
+    *           It is assumed the widget was not in the list before this call.
     */
-   public void addNewTabItem(GenericWidget<?> widget)
+   final void addNewTabItem(GenericWidget<?> widget)
    {
-      if (!widget.isTabWidget())
-      {
-         return;
-      }
-
-      // add widget to the tab item list
-      if (!this.tabItems.contains(widget))
-      {
-         this.tabItems.add(widget);
-      }
-
+      assert !tabItems.contains(widget);
+      
+      tabItems.add(widget);
       LogicalTerminal.updateTabItemList(this);
    }
    
    /**
+    * Update tab stop list when the widget TAB-STOP attribute changes.
+    * 
+    * @param widget
+    *        the widget whose TAB-STOP attribute might changed
+    * @param isTabStop
+    *        the widget TAB-STOP value
+    */
+   final void updateTabStopForWidget(final GenericWidget<?> widget, boolean isTabStop)
+   {
+      if (isTabStop)
+      {
+         // assure the widget is in the list, if missing,
+         // append to the end of the list
+         if (tabItems.contains(widget))
+         {
+            return;
+         }
+         
+         tabItems.add(widget);
+      }
+      else
+      {
+         if (!tabItems.remove(widget))
+         {
+            return;
+         }
+         
+         assert !tabItems.contains(widget);
+      }
+
+      final GenericFrame ownerFrame = getFrame();
+      if (_isRealized() && ownerFrame != null && ownerFrame.getFrameWidget()._isRealized())
+      {
+         LogicalTerminal.updateTabItemList(this);
+      }
+   }
+   
+   /**
     * Removes the target widget from the tab item list and add it to the end of the tab item list.
     * 
     * @param    widget
@@ -382,24 +425,10 @@
     */
    private void moveTabItemToEnd(GenericWidget<?> widget)
    {
-      moveTabItem(widget, () -> -1);
+      moveTabItem(widget, tabItems.size() - 1);
    }
    
    /**
-    * Removes the target widget from the tab item list, add it at the top of the tab item
-    * list at the given position and shifts the other tab items below this target widget. 
-    * 
-    * @param    widget
-    *           The target widget
-    * @param    pos
-    *           The position to place new widget.
-    */
-   private void moveTabItemAtTop(GenericWidget<?> widget, int pos)
-   {
-      moveTabItem(widget, () -> pos);
-   }
-
-   /**
     * Places the given widget in the current tab item list.
     * 
     * @param    widget
@@ -411,74 +440,34 @@
     */
    private void placeWidgetInTabList(GenericWidget<?> widget, UpdatesOp op, int pos)
    {
-      if (widget != null)
-      {
-               switch(op)
-               {
-                  case UPDATE:
-                  case PROMPT_FOR:
-                  case SET:
-                     moveTabItemAtTop(widget, pos);
-                     break;
-                  case ENABLE:
-                  default:
-                     moveTabItemToEnd(widget);
-               }
-      }
-   }
-
-   /**
-    * Updates tab items on tab stop.
-    * 
-    * @param    widget
-    *           The given widget
-    * @param    addToEnd
-    *           The true value indicates that the given widget is placed at the end of the tab
-    *           item list, otherwise its tab position is not changed.
-    */
-   public void updateTabItemsOnTabStop(GenericWidget<?> widget, boolean addToEnd)
-   {
-      if (addToEnd)
-      {
-         moveTabItemToEnd(widget);
-      }
-      updateExtremeTabItems();
-   }
-
-   /**
-    * Move the target widget after the provided widget in this field-group. 
-    * 
-    * @param    widget
-    *           The target widget to be moved.
-    * @param    after
-    *           The provided widget.
-    */
-   public void moveWidgetAfter(GenericWidget<?> widget, GenericWidget<?> after)
-   {
-      if (!tabItems.contains(widget) || !tabItems.contains(after))
-      {
-         throw new RuntimeException("The widgets must be in specified field-group");
-      }
-      
-      setTabItemAfter(widget, after);
-   }
-   
+      switch (op)
+      {
+         case UPDATE:
+         case PROMPT_FOR:
+         case SET:
+            moveTabItem(widget, pos);
+            break;
+         case ENABLE:
+            moveTabItemToEnd(widget);
+      }
+   }
+
    /**
     * Move the target widget before the provided widget in this field-group. 
     * 
     * @param    widget
     *           The target widget to be moved.
-    * @param    before
+    * @param    anchor
     *           The provided widget.
     */
-   public void moveWidgetBefore(GenericWidget<?> widget, GenericWidget<?> before)
+   public void moveWidgetBefore(GenericWidget<?> widget, GenericWidget<?> anchor)
    {
-      if (!tabItems.contains(widget) || !tabItems.contains(before))
+      if (!tabItems.contains(widget))
       {
          throw new RuntimeException("The widgets must be in specified field-group");
       }
       
-      setTabItemBefore(widget, before);
+      setTabItemRelative(widget, anchor, false);
    }
    
    /**
@@ -504,6 +493,7 @@
     * @param name
     *        The handle type to be set.
     */
+   @Override
    public void name(character name)
    {
       if (name == null || name.isUnknown())
@@ -525,6 +515,7 @@
     *
     * @return   the parent widget for this widget
     */
+   @Override
    public CommonWidget getParent()
    {
       // explicitly set field group
@@ -579,6 +570,7 @@
     *
     * @return   The next widget or a <code>null</code> if there is no subsequent widget.
     */
+   @Override
    public GenericWidget<?> getNextChild(GenericWidget<?> child)
    {
       if (widgets == null || child == null)
@@ -599,6 +591,7 @@
     *
     * @return   The previous widget or <code>null</code> if there is no preceding widget.
     */
+   @Override
    public GenericWidget<?> getPrevChild(GenericWidget<?> child)
    {
       if (widgets == null || child == null)
@@ -619,18 +612,7 @@
    @Override
    public handle getFirstTabItem()
    {
-      if (firstTabItem == null)
-      {
-         firstTabItem = getExtremeTabItem(true);
-      }
-      
-      if (firstTabItem == null)
-      {
-         // when no item is found, unknown handle must be returned
-         return new handle();
-      }
-      
-      return firstTabItem.asWidgetHandle();
+      return getExtremeTabItem(true);
    }
    
    /**
@@ -641,18 +623,7 @@
    @Override
    public handle getLastTabItem()
    {
-      if (lastTabItem == null)
-      {
-         lastTabItem = getExtremeTabItem(false);
-      }
-      
-      if (lastTabItem == null)
-      {
-         // when no item is found, unknown handle must be returned
-         return new handle();
-      }
-      
-      return lastTabItem.asWidgetHandle();
+      return getExtremeTabItem(false);
    }
 
    /**
@@ -664,36 +635,7 @@
    @Override
    public void setFirstTabItem(handle wHandle)
    {
-      if (wHandle == null || !wHandle._isValid() || wHandle.isUnknown())
-      {
-         String ms = 
-            "**Unable to assign UNKNOWN value to attribute FIRST-TAB-ITEM on FIELD-GROUP widget";
-         ErrorManager.recordOrThrowError(4083, ms, false);
-         return;
-      }
-      
-      CommonWidget widget = wHandle.unwrapWidget();
-      
-      if (!tabItems.contains(widget))
-      {
-         String ms = "**Unable to set attribute FIRST-TAB-ITEM for FIELD-GROUP widget";
-         ErrorManager.recordOrThrowError(4078, ms, false);
-         return;
-      }
-      GenericWidget<?> anchor;
-      if (firstTabItem != null)
-      {
-         anchor = firstTabItem;
-      }
-      else
-      {
-         anchor = tabItems.get(0);
-      }
-      firstTabItem = (GenericWidget<?>) widget;
-      
-      setTabItemBefore((GenericWidget<?>) widget, anchor);
-      
-      LogicalTerminal.updateTabItemList(this);
+      setFirstLastTabItem(wHandle, true);
    }
    
    /**
@@ -705,36 +647,35 @@
    @Override
    public void setLastTabItem(handle wHandle)
    {
+      setFirstLastTabItem(wHandle, false);
+   }
+   
+   /**
+    * Setting the FIRST-TAB-ITEM or LAST-TAB-ITEM attribute.
+    * 
+    * @param   wHandle
+    *          The handle of widget in this field-group to assign as last in the tab order.
+    */
+   private final void setFirstLastTabItem(final handle wHandle, final boolean isFirst)
+   {
+      final String attName = isFirst ? "FIRST-TAB-ITEM" : "LAST-TAB-ITEM";
       if (wHandle == null || !wHandle._isValid() || wHandle.isUnknown())
       {
-         String ms = 
-            "**Unable to assign UNKNOWN value to attribute LAST-TAB-ITEM on FIELD-GROUP widget";
-         ErrorManager.recordOrThrowError(4083, ms, false);
+         ErrorManager.recordOrShowError(4083, "Unable to assign UNKNOWN value to attribute "
+                  + attName + " on FIELD-GROUP widget", false);
          return;
       }
       
-      CommonWidget widget = wHandle.unwrapWidget();
+      final CommonWidget widget = wHandle.unwrapWidget();
       
       if (!tabItems.contains(widget))
       {
-         String ms = "**Unable to set attribute LAST-TAB-ITEM for FIELD-GROUP widget";
-         ErrorManager.recordOrThrowError(4078, ms, false);
+         ErrorManager.recordOrShowError(4078,
+                  "Unable to set attribute " + attName + " for FIELD-GROUP widget", false);
          return;
       }
-      GenericWidget<?> anchor;
-      if (lastTabItem != null)
-      {
-         anchor = lastTabItem;
-      }
-      else
-      {
-         anchor = tabItems.get(tabItems.size() - 1);
-      }
-      
-      lastTabItem = (GenericWidget<?>) widget;
-      
-      setTabItemAfter((GenericWidget<?>) widget, anchor);
-      
+      
+      moveTabItem((GenericWidget<?>) widget, isFirst ? 0 : tabItems.size() - 1);
       LogicalTerminal.updateTabItemList(this);
    }
    
@@ -774,19 +715,17 @@
       {
          throw new UnsupportedOperationException("The FieldGroup is not expandable");
       }
-      else
-      {
-         if (widgets.contains(dynWidget))
-         {
-            return;
-         }
-         
-         addWidget(dynWidget);
-         
-         if (notifyOwnerFrame)
-         {
-            widgetAdder.accept(dynWidget);
-         }
+      
+      if (widgets.contains(dynWidget))
+      {
+         return;
+      }
+      
+      addWidget(dynWidget);
+      
+      if (notifyOwnerFrame)
+      {
+         widgetAdder.accept(dynWidget);
       }
    }
    
@@ -802,19 +741,13 @@
    }
    
    /**
-    * Return the current tab item list.
+    * Return the current tab item list as an array of integers.
     * 
     * @return   The tab item list
     */
    public int[] getTabItemList()
    {
-      if (tabItems != null && !tabItems.isEmpty())
-      {
-         return tabItems.stream().filter(x -> (x.isTabStop().booleanValue()))
-                  .mapToInt(x -> x.getId()).toArray();
-      }
-      
-      return new int[0];
+      return tabItems.stream().mapToInt(x -> x.getId()).toArray();
    }
    
    /**
@@ -836,12 +769,11 @@
          {
             int id = widgetIds[i];
             GenericWidget<?> widget = LogicalTerminal.getWidgetForId(id);
-            placeWidgetInTabList(widget, op, i);
+            if (widget != null)
+               placeWidgetInTabList(widget, op, i);
          }
       }
       
-      updateExtremeTabItems();
-      
       return getTabItemList();
    }
    
@@ -867,8 +799,6 @@
          }
       }
       
-      updateExtremeTabItems();
-      
       return getTabItemList();
    }
    
@@ -882,7 +812,7 @@
     */
    public handle nextTabItem(GenericWidget<?> widget)
    {
-      return nextTabItem(widget, false);
+      return nextOrPreviousTabItem(widget, false);
    }
    
    /**
@@ -895,7 +825,7 @@
     */
    public handle previousTabItem(GenericWidget<?> widget)
    {
-      return nextTabItem(widget, true);
+      return nextOrPreviousTabItem(widget, true);
    }
    
    /**
@@ -953,51 +883,25 @@
     * 
     * @return   An instance of <code>GenericWidget</code> or <code>null</code>.
     */
-   private handle nextTabItem(GenericWidget<?> widget, boolean reverse)
+   private handle nextOrPreviousTabItem(GenericWidget<?> widget, boolean reverse)
    {
-      Iterator<GenericWidget<?>> widgetsIter;
-      
-      if (reverse)
-      {
-         widgetsIter = Iterables.reverse(tabItems).iterator();
+      final int anchorIndex = tabItems.indexOf(widget);
+      final int index;
+      if (anchorIndex < 0)
+      {
+         // widget is no tab-stoppable
+         index = -1;
+      }
+      else if (reverse)
+      {
+         index = anchorIndex > 0 ? anchorIndex - 1 : -1;
       }
       else
       {
-         widgetsIter = tabItems.iterator();
-      }
-      
-      boolean notFound = true;
-      while(notFound && widgetsIter.hasNext())
-      {
-         GenericWidget<?> testableWidget = widgetsIter.next();
-         if (widget.equals(testableWidget))
-         {
-            notFound = false;
-         }
-      }
-      
-      if (notFound || !widget.isTabStop().booleanValue())
-      {
-         return new handle();
-      }
-      
-      notFound = true;
-      GenericWidget<?> nextWidget = null;
-      while(notFound && widgetsIter.hasNext())
-      {
-         nextWidget = widgetsIter.next();
-         if (nextWidget._isRealized() && nextWidget.isTabStop().booleanValue())
-         {
-            notFound = false;
-         }
-      }
-      
-      if (notFound)
-      {
-         return new handle();
-      }
-      
-      return nextWidget.asWidgetHandle();
+         index = anchorIndex < tabItems.size() - 1 ? anchorIndex + 1 : -1;         
+      }
+      
+      return index == -1 ? new handle() : new handle(tabItems.get(index));
    }
    
    /**
@@ -1006,6 +910,7 @@
     * @return   TRUE  - if the logic of parent class {@link HandleChain} is used,
     *           FALSE - if own siblings implementation logic is used. 
     */
+   @Override
    protected boolean useHandleChainSiblings()
    {
       return true;
@@ -1046,99 +951,54 @@
    }
    
    /**
-    * Accesses the the first and the last field-level widget in the given field group.
+    * Find the first or the last tab-stoppable widget in this field group.
     *
     * @param    first
     *           The true value indicates that the first will be accessed, otherwise
     *           the last widget.
     * 
-    * @return   The extreme possible field-level widget in this group or
-    *           <code>null</code> if there are no field level widgets.
-    */
-   private GenericWidget<?> getExtremeTabItem(boolean first)
-   {
-      if (tabItems == null)
-      {
-         return null;
-      }
-      
-      Iterator<GenericWidget<?>> iter;
-      
-      if (first)
-      {
-         iter = tabItems.iterator();
-      }
-      else
-      {
-         iter = Iterables.reverse(tabItems).iterator();
-      }
-      
-      // scan first available field-level widget
-      
-      while (iter.hasNext())
-      {
-         GenericWidget<?> widget = iter.next();
-         if (widget._isRealized() && widget.isTabStop().booleanValue())
-         {
-            return widget;
-         }
-      }
-      
-      return null;
-   }
-   
-   /**
-    * Set the tab item after the provided widget in this field-group. 
-    * 
-    * @param    widget
-    *           The tab item.
-    * @param    after
-    *           The provided widget.
-    */
-   private void setTabItemAfter(GenericWidget<?> widget, GenericWidget<?> after)
-   {
-      if (widget != after)
-      {
-         moveTabItem(widget, () -> tabItems.indexOf(after) + 1);
-      }
-      
-      updateExtremeTabItems();
-   }
-   
-   /**
-    * Set the tab item before the provided widget in this field-group. 
-    * 
-    * @param    widget
-    *           The tab item.
-    * @param    before
-    *           The provided widget.
-    */
-   private void setTabItemBefore(GenericWidget<?> widget, GenericWidget<?> before)
-   {
-      if (widget != before)
-      {
-         moveTabItem(widget, () -> tabItems.indexOf(before));
-      }
-      
-      updateExtremeTabItems();
-   }
-   
-   /**
-    * Updates the first and last tab items if there are initialized.
-    */
-   private void updateExtremeTabItems()
-   {
-      if (firstTabItem != null)
-      {
-         firstTabItem = getExtremeTabItem(true);
-      }
-      
-      if (lastTabItem != null)
-      {
-         lastTabItem = getExtremeTabItem(false);
-      }
-   }
+    * @return   the handle of the widget found or unknown if there is no tab stoppable widgets
+    *           in this group
+    */
+   private final handle getExtremeTabItem(boolean first)
+   {
+      final int size = tabItems.size();
+      return size == 0 ? new handle() : new handle(tabItems.get(first ? 0 : size - 1));
+   }
+   
+   /**
+    * Set widget relative to the provided anchor widget in this field-group. 
+    * 
+    * @param    widget
+    *           The tab item.
+    * @param    anchor
+    *           The provided widget.
+    * @param    isAfter
+    *           {@code true} if place the widget after the anchor widget, otherwise place widget before
+    *           the anchor widget.
+    */
+   final void setTabItemRelative(final GenericWidget<?> widget, GenericWidget<?> anchor,
+            boolean isAfter)
+   {
+      if (!tabItems.contains(anchor))
+      {
+         throw new RuntimeException(
+                  "The anchor widget must be in specified field-group and tab-stoppable");
+      }
 
+      if (widget != anchor)
+      {
+         assert tabItems.contains(widget);
+         
+         tabItems.remove(widget);
+         
+         assert !tabItems.contains(widget);
+         
+         final int anchorIndex = tabItems.indexOf(anchor);
+         tabItems.add(isAfter ? anchorIndex + 1 : anchorIndex, widget);
+      }
+   }
+   
    /**
     * Get the NUM-TABS attribute, which indicates the number of tab widgets inside this
     * field-group container.
@@ -1148,20 +1008,7 @@
    @Override
    public integer getNumTabs()
    {
-      Iterator<GenericWidget<?>> widgetsIter = tabItems.iterator();
-      
-      int count = 0;
-      
-      while(widgetsIter.hasNext())
-      {
-         GenericWidget<?> nextWidget = widgetsIter.next();
-         if (nextWidget._isRealized() && nextWidget.isTabStop().booleanValue())
-         {
-            count++;
-         }
-      }
-      
-      return new integer(count);
+      return new integer(tabItems.size());
    }
 
    /**
@@ -1189,34 +1036,15 @@
     * @param    tabPosition
     *           The tab position
     * 
-    * @return   The handle of the tab item by the given tab position.
+    * @return   The handle of the tab item by the given tab position or
+    *           UNKNOWN if tap position is out of valid position range.
     */
    @Override
    public handle getTabItem(int tabPosition)
    {
-      if (tabPosition <= 0)
-      {
-         return new handle();
-      }
-      
-      Iterator<GenericWidget<?>> widgetsIter = tabItems.iterator();
-      
-      int count = 0;
-      
-      while(widgetsIter.hasNext())
-      {
-         GenericWidget<?> nextWidget = widgetsIter.next();
-         if (nextWidget._isRealized() && nextWidget.isTabStop().booleanValue())
-         {
-            count++;
-            if (count == tabPosition)
-            {
-               return nextWidget.asWidgetHandle();
-            }
-         }
-      }
-      
-      return new handle();
+      return tabPosition > 0 && tabPosition <= tabItems.size()
+         ? new handle(tabItems.get(tabPosition - 1))
+         : new handle();
    }
    
    /**
@@ -1235,53 +1063,8 @@
          return new integer();
       }
       
-      Iterator<GenericWidget<?>> widgetsIter = tabItems.iterator();
-      
-      int tabPosition = 0;
-      
-      while(widgetsIter.hasNext())
-      {
-         GenericWidget<?> nextWidget = widgetsIter.next();
-         
-         if (nextWidget._isRealized() && nextWidget.isTabStop().booleanValue())
-         {
-            tabPosition++;
-            
-            if (widget.equals(nextWidget))
-            {
-               return new integer(tabPosition);
-            }
-         }
-      }
-      
-      return new integer();
-   }
-
-   /**
-    * Remove from the list of tab items the items that became non-tab items.
-    */
-   public void cleanNonTabItems()
-   {
-      if (tabItems != null)
-      {
-         boolean removed = false;
-         Iterator<GenericWidget<?>> iter = tabItems.iterator();
-         while (iter.hasNext())
-         {
-            GenericWidget<?> widget = iter.next();
-            if (!widget.isTabWidget())
-            {
-               iter.remove();
-               removed = true;
-            }
-         }
-
-         if (removed)
-         {
-            firstTabItem = null;
-            lastTabItem = null;
-         }
-      }
+      final int index = tabItems.indexOf(widget);
+      return index >= 0 ? new integer(index + 1) : new integer();
    }
 
    /**
@@ -1314,27 +1097,32 @@
     *          it will call {@linkplain LogicalTerminal#updateTabItemList(FieldGroup)} to reflect the
     *          addition.
     * @param   index
-    *          The index supplier of the new widget position in the tab item list. Provide -1 to move the
+    *          The index of the new widget position in the tab item list. Provide -1 to move the
     *          widget at the end of the list. The index is fetched after the widget is removed from the list.
     */
-   private void moveTabItem(GenericWidget<?> widget, Supplier<Integer> index)
+   private final void moveTabItem(GenericWidget<?> widget, final int index)
    {
-      boolean contained = tabItems.remove(widget);
+      // final boolean contained = 
+               tabItems.remove(widget);
 
-      int i = index.get();
-      if (i == -1)
+      assert !tabItems.contains(widget);
+      
+      if (index == -1)
       {
          tabItems.add(widget);
       }
       else
       {
-         tabItems.add(i, widget);
+         tabItems.add(index, widget);
       }
 
-      if (!contained)
-      {
-         LogicalTerminal.updateTabItemList(this);
-      }
+      // FIXME: do we need to call LogicalTerminal.updateTabItemList only
+      // when a new item was added? 
+//      if (!contained)
+//      {
+//         LogicalTerminal.updateTabItemList(this);
+//      }
+      LogicalTerminal.updateTabItemList(this);
    }
 
    /**

=== modified file 'src/com/goldencode/p2j/ui/GenericFrame.java'
--- src/com/goldencode/p2j/ui/GenericFrame.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/GenericFrame.java	2022-05-06 22:46:18 +0000
@@ -1589,7 +1592,7 @@
     * @return   <code>TRUE</code> - if handle is valid, 
     *           <code>FALSE</code> - otherwise
     */
-   private boolean isAbleToDisplayWindow(handle hWin)
+   private static boolean isAbleToDisplayWindow(handle hWin)
    {
       // current-window is used (not the frame's window)
       if (hWin == null || !hWin._isValid() || hWin.isUnknown())
@@ -1661,7 +1664,6 @@
       frame.setInsideSetup(true);
       ((Settable) frame).setup(frame);
       frame.setInsideSetup(false);
-      frame.cleanNonTabItems();
       frame.batch(false);
    }
    
@@ -3456,7 +3458,6 @@
             
             // hmmm, at this point we should have had a valid widget that was initialized
             // so encountering null here is unexpected. See below how we deal with unknown value.
-            String wt = widget.getDataType().toStringMessage();
             // return new character(wt.startsWith("date") ? "" : "?");
             return new character("");
          }
@@ -6645,7 +6646,7 @@
     * 
     * @see      #checkFieldGroup(GenericWidget) for limitations.
     */
-   private void setFieldGroupWidgets(FieldGroup fg, GenericWidget<?>[] widgets)
+   private static void setFieldGroupWidgets(FieldGroup fg, GenericWidget<?>[] widgets)
    {
       LinkedList<GenericWidget<?>> list = new LinkedList<>();
       
@@ -6673,7 +6674,7 @@
     * @param    widget
     *           The widget to check.
     */
-   private void checkFieldGroup(GenericWidget<?> widget)
+   private static void checkFieldGroup(GenericWidget<?> widget)
    {
       GenericFrame frame = widget.getFrame();
       if (!widget._dynamic() && frame.frame.config.labels && !frame.frame.config.sideLabels)
@@ -7955,15 +7956,6 @@
    }
 
    /**
-    * Remove from the list of tab items the items that became non-tab items.
-    */
-   @Override
-   public void cleanNonTabItems()
-   {
-      getFieldGroup().cleanNonTabItems();
-   }
-
-   /**
     * Marks the end of the editing block when triggered by any abnormal exit
     * from the block on the server.  This method does NOT need to be called
     * if the exit is naturally triggered via {@link #continueEditing}. This
@@ -8222,7 +8214,7 @@
     *          if specified widget does not exists or does not support such a
     *          request.
     */
-   public int[] getSelection(GenericWidget<?> widget)
+   final static int[] getSelection(GenericWidget<?> widget)
    {
       return LogicalTerminal.getSelection(widget.getId());
    }
@@ -8365,7 +8357,7 @@
     *
     * @return  Converted value.
     */
-   public Object convertScreenValue(Class<?> dataType, Object value, String format)
+   static Object convertScreenValue(Class<?> dataType, Object value, String format)
    {
       if (value != null)
       {
@@ -8713,7 +8705,7 @@
     * @return   The numeric ID assigned to the widget or -1 if no such widget
     *           exists.
     */
-   int getWidgetId(GenericWidget<?> widget)
+   private final static int getWidgetId(GenericWidget<?> widget)
    {
       if (widget != null)
       {
@@ -8748,7 +8740,7 @@
     * @return   The associated widget instance or <code>null</code> if a
     *           widget by that ID does not exist in this frame.
     */
-   GenericWidget<?> getWidgetForId(int id)
+   final static GenericWidget<?> getWidgetForId(int id)
    {
       return LogicalTerminal.getWidgetForId(id);
    }
@@ -9182,7 +9174,7 @@
     *
     * @return   The String for dynamic widget name in string form of "ID".
     */
-   private String getDynamicWidgetName(GenericWidget<?> dynWidget)
+   private static String getDynamicWidgetName(GenericWidget<?> dynWidget)
    {
       return Integer.toString(dynWidget.getId());
    }
@@ -9871,7 +9863,7 @@
     *
     * @return   <code>true</code> if the unnamed stream is redirected
     */
-   boolean isRedirected()
+   static boolean isRedirected()
    {
       return UnnamedStreams.isOut();
    }
@@ -10178,6 +10170,7 @@
    {
       LiteralWidget lit = new LiteralWidget()
       {
+         @Override
          public character getScreenValue() 
          {
             return widget == null ? new character(txt) : new character(widget.getLabelStr());
@@ -10468,46 +10461,6 @@
    /**
     * Returns the current screen value of the specified widget. 
     * 
-    * @param    widgetName
-    *           The widget name. 
-    * @param    dataType
-    *           The widget data type; a class representing one of the
-    *           following widget data types:
-    *           <ul>
-    *             <li><code>character</code>;
-    *             <li><code>integer</code>;
-    *             <li><code>decimal</code>;
-    *             <li><code>logical</code>;
-    *             <li><code>date</code>.
-    *           </ul>
-    *
-    * @return    <code>Object</code> currently in the screen buffer as the
-    *            value of the widget or one of these defaults, if no value
-    *            has been set:
-    *           <ul>
-    *             <li><code>character</code> - "";
-    *             <li><code>integer</code> - 0;
-    *             <li><code>decimal</code> - 0;
-    *             <li><code>logical</code> - no (false);
-    *             <li><code>date</code> - ? (unknown).
-    *           </ul>
-    */
-   @SuppressWarnings("unused")
-   private Object getter(String widgetName, Class<?> dataType)
-   {
-      int index = getWidgetId(widgetName);
-      
-      if (index < 0)
-      {
-         throw new RuntimeException("Invalid widget name " + widgetName);
-      }
-      
-      return getter(index, dataType, true, false);
-   }
-   
-   /**
-    * Returns the current screen value of the specified widget. 
-    * 
     * @param    widgetId
     *           The widget ID. 
     * @param    dataType
@@ -10631,33 +10584,6 @@
    /**
     * Sets a new value for the specified widget in the screen buffer.
     * 
-    * @param   widgetName
-    *          widget name
-    * @param   value
-    *          An object representing the widget value. Since this code is
-    *          called from the Java Reflection API, any time a primitive
-    *          (e.g. <code>int</code>) is passed, Reflection automatically
-    *          wraps it in an <code>Object</code> of the correct type (e.g.
-    *          <code>Integer</code>).  Thus it is safe to always expect the
-    *          value to be an <code>Object</code>.
-    * @return  <code>null</code> to comply with <code>void</code>
-    */
-   @SuppressWarnings("unused")
-   private Object setter(String widgetName, Object value)
-   {
-      int index = getWidgetId(widgetName);
-
-      if (index < 0)
-      {
-         throw new RuntimeException("Invalid widget name " + widgetName);
-      }      
-      
-      return setter(index, value);
-   }
-      
-   /**
-    * Sets a new value for the specified widget in the screen buffer.
-    * 
     * @param   index
     *          The widget ID.
     * @param   value
@@ -10748,7 +10674,7 @@
     * @throws   RuntimeException
     *           If the index object is not a supported numeric object.
     */
-   private String generateWidgetName(String base, int op, Object index)
+   private static String generateWidgetName(String base, int op, Object index)
    throws RuntimeException
    {
       String result = indexAsString(index);
@@ -10783,7 +10709,7 @@
     * @return   The resulting string or <code>null</code> if the type is not
     *           one of the supported numeric types.
     */
-   private String indexAsString(Object index)
+   private static String indexAsString(Object index)
    {
       String result = null;
       
@@ -10828,7 +10754,7 @@
     *           an instance of the proper type (set as 
     *           <code>unknown value</code>) otherwise.
     */
-   private BaseDataType translateUnknown(BaseDataType  value, ControlEntity<?> widget)
+   private static BaseDataType translateUnknown(BaseDataType  value, ControlEntity<?> widget)
    {
       if (value instanceof unknown)
       {
@@ -11343,7 +11269,7 @@
     * @return   The list of remaining ids or <code>null</code> if no ids
     *           were included.
     */
-   public int[] validIds(GenericWidget<?>[] list)  
+   private final static int[] validIds(GenericWidget<?>[] list)  
    {
       if (list == null || list.length == 0)
          return null;
@@ -11638,7 +11564,7 @@
     * @return   The list of remaining ids or <code>null</code> if all ids
     *           were excluded.
     */
-   private int[] remainingIds(GenericWidget<?>[] all, GenericWidget<?>[] xcpt)
+   private static int[] remainingIds(GenericWidget<?>[] all, GenericWidget<?>[] xcpt)
    {
       Map<Integer, GenericWidget<?>> widgetIds = new LinkedHashMap<>();
       
@@ -11677,7 +11603,7 @@
     * @return   The <code>int[]</code> or <code>null</code> if there are no
     *           ids in the set.
     */
-   private int[] idSetToArray(Set<Integer> widgetIds)
+   private static int[] idSetToArray(Set<Integer> widgetIds)
    {  
       // calculate the widgets
       int size = widgetIds.size();
@@ -11706,7 +11632,7 @@
     *
     * @return   The array of widget IDs.
     */
-   private int[] idsFromWidgets(GenericWidget<?>[] widgets)
+   private static int[] idsFromWidgets(GenericWidget<?>[] widgets)
    {
       if (widgets == null)
          return null;
@@ -11731,7 +11657,7 @@
     *
     * @return   The array of widgets.
     */
-   private GenericWidget<?>[] widgetListFromFrameElements(FrameElement[] widgets)
+   private static GenericWidget<?>[] widgetListFromFrameElements(FrameElement[] widgets)
    {
       GenericWidget<?>   current = null;
       GenericWidget<?>[] wids    = new GenericWidget[widgets.length];
@@ -11771,7 +11697,7 @@
     *           If the unnamed input is redirected and silent error mode is
     *           off.
     */
-   private boolean errorOnRedirectedInput(int num, String txt)
+   private static boolean errorOnRedirectedInput(int num, String txt)
    {
       if (UnnamedStreams.input() != null)
       {
@@ -11802,7 +11728,7 @@
     *           If the unnamed input is redirected and silent error mode is
     *           off.
     */
-   private boolean errorOnBatchModeEnable(int num, String txt)
+   private static boolean errorOnBatchModeEnable(int num, String txt)
    {
       // It depends on underlying OS and|or background mode.
       if (LogicalTerminal.isInBatchMode() &&
@@ -11845,7 +11771,7 @@
     *           If the unnamed input is redirected and silent error mode is
     *           off.
     */
-   private boolean errorOnBatchModeSetPrompt(int num, String txt)
+   private static boolean errorOnBatchModeSetPrompt(int num, String txt)
    {
       // It depends on internal I/O redirection.
       if (LogicalTerminal.isInBatchMode() && UnnamedStreams.input() == null)
@@ -12297,7 +12223,7 @@
     *
     * @return   The remote stream ID.
     */
-   int getRemoteStreamId(Stream out)
+   final static int getRemoteStreamId(Stream out)
    {
       RemoteStream rs = null;
       
@@ -12357,7 +12283,7 @@
          for (int i = 0; i < widgets.length; i++)
          {
             int wid = widgets[i].getId();
-            GenericWidget<?> widget = this.getWidgetForId(wid);
+            GenericWidget<?> widget = getWidgetForId(wid);
             
             if (widget instanceof ControlTextWidget)
             {
@@ -12389,7 +12315,7 @@
     *
     * @return   The array of the GenericWidget&lt;?&gt; values with HIDDEN attrubute is FALSE.
     */
-   private GenericWidget<?>[] removeHiddenWidgets(GenericWidget<?>[] inputList)
+   private static GenericWidget<?>[] removeHiddenWidgets(GenericWidget<?>[] inputList)
    {
       List<GenericWidget<?>> tmpList = new ArrayList<>();
       
@@ -12428,36 +12354,6 @@
    }
    
    /**
-    * Restores the widget registry for static content of this frame based on header and
-    * non-header widget info.
-    */
-   private void restoreWidgetRegistry()
-   {
-      // non-header widgets first
-      for (int i = 0; i < widgets.length; i++)
-      {
-         if (!widgets[i]._dynamic())
-         {
-            LogicalTerminal.registerWidget(widgets[i], widgets[i].getId());
-         }
-      }
-      
-      // enumerate all the header widgets in the order of their definition
-      Iterator<String> iter = n2h.keySet().iterator();
-
-      while (iter.hasNext())
-      {
-         // get the widget
-         GenericWidget<?> gw = n2h.get(iter.next());
-            
-         if (!gw._dynamic())
-         {
-            LogicalTerminal.registerWidget(gw, gw.getId());
-         }
-      }
-   }
-   
-   /**
     * Provides a cleanup mechanism for editing blocks that exits abnormally
     * (without a natural exit via {@link #continueEditing}).
     */
@@ -13359,73 +13255,6 @@
    }
    
    /**
-    * This method prints field-groups of the frame and is needed only for debugging purposes.
-    * 
-    * Here is a sample output (each triplet group is for a distinct call):
-    * <pre>
-    * 3
-    *  gr1 [null, gr2]     gr2 [gr1, gr3]     gr3 [gr2, gr4]     gr4 [gr3, gr5]     gr5 [gr4, UN]
-    * 1 -&gt; gr5, 
-    * 
-    * 3
-    *  gr1 [null, gr2]     gr2 [gr1, gr3]     gr3 [gr2, gr4]     gr4 [gr3, gr5]     gr5 [gr4, UN]
-    * 1 -&gt; gr5, 2 -&gt; gr1, 
-    * 
-    * 3
-    *  gr1 [null, gr2]     gr2 [gr1, gr3]     gr3 [gr2, gr4]     gr4 [gr3, gr5]     gr5 [gr4, UN]
-    * 1 -&gt; gr5, 2 -&gt; gr1, 3 -&gt; gr2, 
-    * </pre>
-    * 
-    * Here are groups of several triplets of strings.
-    * The first line in a triplet is a number of lines to step. Can be &lt; 0.
-    * The second row displays field groups, each with its neighbors in square brackets:
-    * [left, right].
-    * The third row is a mapping from the row number to field-group, only for rows with an
-    * associated field-group.
-    * 
-    * @param   lines
-    *          Number of lines to step. Can be &lt;0 (UP statement) or &gt;0 (DOWN statement).
-    */
-   private void printFieldGroups(int lines)
-   {
-      System.out.println(lines);
-      String result = "";
-      for (FieldGroup fg = fieldGroup; 
-           fg != null; 
-           fg = (FieldGroup) fg.getNextSibling().getResource())
-      {
-         result += " " + (fg.name().getValue() == null ? "UN" : fg.name().getValue()) + " [";
-         if(fg.getPrevSibling() == null || fg.getPrevSibling().isUnknown()) 
-            result += null;
-         else
-         {
-            String fgName = ((FieldGroup)fg.getPrevSibling().getResource()).name().getValue();
-            result += fgName == null ? "UN" : fgName; 
-         }
-         result += ", ";
-         if(fg.getNextSibling() == null || fg.getNextSibling().isUnknown()) 
-            result += null;
-         else
-         {
-            String fgName = ((FieldGroup)fg.getNextSibling().getResource()).name().getValue();
-            result += fgName == null ? "UN" : fgName;
-         }
-         result += "]     ";
-         if (fg.getNextSibling() == null || fg.getNextSibling().isUnknown())
-            break;
-      }
-      System.out.println(result);
-      
-      result = "";
-      for(Entry<Integer, FieldGroup> entry : rows2fg.entrySet())
-      {
-         result += entry.getKey() + " -> " + entry.getValue().name().getValue() + ", ";
-      }
-      System.out.println(result);
-      System.out.println();
-   }
-
-   /**
     * This method actualizes field-groups of this frame in correspondance with 
     * the current iteration (DOWN/UP step). For the cases DOWN-SIZE != 1 this method will
     * iterate steps one by one as it would be several DOWN/UP 1 statements.
@@ -13498,20 +13327,6 @@
    }
 
    /**
-    * This method checks if the specified field-group is used for some row in this frame.
-    * 
-    * @param    groupHandle
-    *           Handle of specified field-group.
-    * 
-    * @return   true - if field-group is associated with some row in this frame
-    *           false - otherwise.
-    */
-   private boolean isFGAssigned(handle groupHandle)
-   {
-      return getFGRow(groupHandle) != null;
-   }
-   
-   /**
     * This method searches the frame row for specified field-group.
     * TODO: use bidirectional map
     * 

=== modified file 'src/com/goldencode/p2j/ui/GenericWidget.java'
--- src/com/goldencode/p2j/ui/GenericWidget.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/GenericWidget.java	2022-05-16 21:47:21 +0000
@@ -397,6 +397,9 @@
 package com.goldencode.p2j.ui;
 
 import com.goldencode.util.*;
+
+import net.sf.ehcache.constructs.nonstop.concurrency.*;
+
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.util.*;
 
@@ -634,48 +637,52 @@
       this.frame = frame;
       this.config.frameId = frame.getFrameId();
       
-      if (this instanceof BaseEntity && !(this instanceof FieldGroup))
+      final FieldGroup fg = frame.getFieldGroup();
+      if (!(this instanceof FieldGroup))
       {
-         FieldGroup fg = frame.getFieldGroup();
-
-         // fg == null when this frame widget is being initialized,
-         // during the initialization this method may be called to link
-         // this widget with the corresponding GenericFrame part and we don't
-         // want it to end up in the field group
-         if (fg != null)
+         if (this instanceof BaseEntity && fg != null)
          {
+            // fg == null when this frame widget is being initialized,
+            // during the initialization this method may be called to link
+            // this widget with the corresponding GenericFrame part and we don't
+            // want it to end up in the field group
             ((BaseEntity) this).group = fg;
             if (dynamic)
             {
                fg.addWidget(this);
             }
          }
-      }
 
-      // dor dynamic widgets this is the place where we need to initialize all frame internals
-      // related to the widget integration into the frame functionality
-      if (dynamic && !(this instanceof FieldGroup))
-      {
-         if (!(this instanceof FrameWidget))
+         // for dynamic widgets this is the place where we need to initialize all frame internals
+         // related to the widget integration into the frame functionality
+         if (dynamic)
          {
-            if (frameLock)
-            {
-               // continue collecting changes if frame lock was set
-               frame.batch(true);
-            }
-            
-            frame.addDynamicWidget(this);
-            
-            // added this dynamic widget to its field group if it hasn't been included yet.
-            FieldGroup fg = frame.getFieldGroup();
-            if (fg != null)
-            {
-               fg.addDynamicWidget(this, false);
-               fg.addNewTabItem(this);
+            if (!(this instanceof FrameWidget))
+            {
+               if (frameLock)
+               {
+                  // continue collecting changes if frame lock was set
+                  frame.batch(true);
+               }
+
+               frame.addDynamicWidget(this);
+
+               // added this dynamic widget to its field group if it hasn't been included yet.
+               if (fg != null)
+               {
+                  fg.addDynamicWidget(this, false);
+                  if (isTabStop().booleanValue()
+                           // We might add this to tab item list in the call fg.addWidget(this)
+                           // above. Assure we do not add twice.
+                           && fg.getTabPosition(this).isUnknown())
+                  {
+                     fg.addNewTabItem(this);
+                  }
+               }
             }
          }
       }
-
+      
       if (pendingScreenValue != null)
       {
          setScreenValue(pendingScreenValue);
@@ -3464,6 +3471,8 @@
 
    /**
     * This method implements MOVE-AFTER-TAB function.
+    * 
+    * Assigns the current widget to the tab position after the specified in the same field group.
     *
     * @param   widget
     *          Widget reference.
@@ -3471,44 +3480,103 @@
     * @return  <code>true</code> if operation was successful.
     */
    @Override
-   public logical moveAfterTab(CommonWidget widget)
-   {
-      if (!getParent().equals(widget.getParent()))
-      {
-         String ms = "**Unable to query attribute MOVE-AFTER-TAB-ITEM for %s %s";
-
-         ErrorManager.recordOrThrowError(4077, String.format(ms, type(), name().getValue()), false);
-         
-         return new logical(false);
-      }
-      
-      // Is the parent always field-group here?
-      FieldGroup fieldGroup = (FieldGroup) getParent();
-      
-      return LogicalTerminal.moveAfterTab(fieldGroup, this, widget);
+   public logical moveAfterTab(CommonWidget widget)
+   {
+      return moveTabImpl(widget, true);
+   }
+   
+   /**
+    * Assigns the widget to the tab position before or after the specified anchor widget
+    * in the same field group.
+    *
+    * @param   anchor
+    *          Anchor widget reference.
+    * @param   isAfter
+    *          if {@code true} then place widget to the tab position after the specified anchor in
+    *          the same field group, otherwise place before anchor.
+    *
+    * @return  <code>true</code> if operation was successful.
+    */
+   private final logical moveTabImpl(final CommonWidget anchor, final boolean isAfter)
+   {
+      if (!anchor.isTabStop().booleanValue())
+      {
+         return error4062(isAfter);
+      }
+      
+      final String methodName = isAfter ? "MOVE-AFTER-TAB-ITEM" : "MOVE-BEFORE-TAB-ITEM";
+      final CommonWidget parent = getParent();
+      if (parent == null || !parent.equals(anchor.getParent()))
+      {
+         ErrorManager.recordOrShowError(4077,
+                  String.format("Unable to query attribute %s for %s %s", methodName, type(),
+                           name().getValue()),
+                  true);
+
+         return new logical(false);
+      }
+
+      if (!(parent instanceof FieldGroup))
+      {
+         ErrorManager.recordOrShowError(4052, String.format(
+                  "%s is not a queryable attribute for %s widget", methodName, type()), true);
+
+         return new logical(false);
+      }
+
+      LogicalTerminal.moveTabItem((FieldGroup) parent, this, anchor, isAfter);
+      
+      return new logical(true);
+   }
+
+   private logical error4062(final boolean isAfter)
+   {
+      String ms = "Attribute " + (isAfter ? "MOVE-AFTER-TAB-ITEM" : "MOVE-BEFORE-TAB-ITEM")
+               + " for the %s %s was passed an invalid widget";
+
+      ErrorManager.recordOrShowError(4062, String.format(ms, type(), name().getValue()), false);
+
+      return new logical(false);
    }
    
    /**
     * This method implements MOVE-AFTER-TAB function.
     *
+    * Assigns the current widget to the tab position after the specified in the same field group.
+    * 
     * @param   widget
     *          The handle of the widget after whose tab position this will be moved.
     *
     * @return  <code>true</code> if operation was successful.
     */
    @Override
-   public logical moveAfterTab(handle widget)
-   {
-      if (widget == null || !widget._isValid() || widget.isUnknown())
+   public logical moveAfterTab(handle widget)
+   {
+      return moveTabImpl(widget, true);
+   }
+
+   /**
+    * Assigns the current widget to the tab position before or after the specified in the same field group.
+    * 
+    * @param   widget
+    *          The handle of the widget after whose tab position this will be moved.
+    * @param   isAfter
+    *          {@code true} if new tab position is after the anchor.
+    *
+    * @return  <code>true</code> if operation was successful.
+    */
+   private final logical moveTabImpl(final handle widget, final boolean isAfter)
+   {
+      if (widget != null && widget._isValid() && !widget.isUnknown())
       {
-         String ms = "**Attribute MOVE-BEFORE-TAB-ITEM for the %s %s was passed an invalid widget";
-
-         ErrorManager.recordOrThrowError(4062, String.format(ms, type(), name().getValue()), false);
-         
-         return new logical(false);
+         final CommonWidget commonWidget = widget.unwrapWidget();
+         if (commonWidget.isTabStop().booleanValue())
+         {
+            return moveTabImpl(commonWidget, isAfter);
+         }
       }
-      
-      return moveAfterTab(widget.unwrapWidget());
+
+      return error4062(isAfter);
    }
 
    /**
@@ -3566,6 +3634,8 @@
    }
    
    /**
+    * This method implements MOVE-BEFORE-TAB function.
+    * 
     * Assigns the current widget to the tab position before the specified in the same field group.
     *
     * @param   widget
@@ -3576,22 +3646,12 @@
    @Override
    public logical moveBeforeTabItem(CommonWidget widget)
    {
-      if (!getParent().equals(widget.getParent()))
-      {
-         String ms = "**Unable to query attribute MOVE-BEFORE-TAB-ITEM for %s %s";
-
-         ErrorManager.recordOrThrowError(4077, String.format(ms, type(), name().getValue()), false);
-         
-         return new logical(false);
-      }
-      
-      // Is the parent always field-group here?
-      FieldGroup fieldGroup = (FieldGroup) getParent();
-      
-      return LogicalTerminal.moveBeforeTabItem(fieldGroup, this, widget);
+      return moveTabImpl(widget, false);
    }
 
    /**
+    * This method implements MOVE-BEFORE-TAB function.
+    * 
     * Assigns the current widget to the tab position before the specified in the same field group.
     *
     * @param   widget
@@ -3602,16 +3662,7 @@
    @Override
    public logical moveBeforeTabItem(handle widget)
    {
-      if (widget == null || !widget._isValid() || widget.isUnknown())
-      {
-         String ms = "**Attribute MOVE-BEFORE-TAB-ITEM for the %s %s was passed an invalid widget";
-
-         ErrorManager.recordOrThrowError(4062, String.format(ms, type(), name().getValue()), false);
-         
-         return new logical(false);
-      }
-      
-      return moveBeforeTabItem(widget.unwrapWidget());
+      return moveTabImpl(widget, false);
    }
    
    /**
=== modified file 'src/com/goldencode/p2j/ui/LogicalTerminal.java'
--- src/com/goldencode/p2j/ui/LogicalTerminal.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/LogicalTerminal.java	2022-05-16 21:47:21 +0000
@@ -7037,20 +7037,15 @@
     *          Containing field-group.
     * @param   widget
     *          The widget to move.
-    * @param   before            
+    * @param   anchor            
     *          The widget before which the widget should be moved.
     *          
-    * @return  TRUE - the widget was moved successfully, FALSE - otherwise.         
     */
-   public static logical moveBeforeTabItem(FieldGroup    fieldGroup, 
-                                           CommonWidget  widget,
-                                           CommonWidget  before)
+   public static void moveBeforeTabItem(FieldGroup    fieldGroup, 
+                                        CommonWidget  widget,
+                                        CommonWidget  anchor)
    {
-      fieldGroup.moveWidgetBefore((GenericWidget<?>)widget, (GenericWidget<?>)before);
-      
-      updateTabItemList(fieldGroup);
-      
-      return new logical(true);
+      moveTabItem(fieldGroup, widget, anchor, false);
    }
 
    /**
@@ -7098,20 +7093,36 @@
     *          Containing field-group.
     * @param   widget
     *          The widget to move.
-    * @param   after            
-    *          The widget after which the widget should be placed.
-    *          
-    * @return  TRUE - the widget was moved successfully, FALSE - otherwise.
-    */
-   public static logical moveAfterTab(FieldGroup    fieldGroup, 
-                                          CommonWidget  widget,
-                                          CommonWidget  after)
-   {
-      fieldGroup.moveWidgetAfter((GenericWidget<?>)widget, (GenericWidget<?>)after);
+    * @param   anchor            
+    *          The widget after which the widget should be placed.
+    */
+   public static void moveAfterTab(FieldGroup    fieldGroup, 
+                                       CommonWidget  widget,
+                                       CommonWidget  anchor)
+   {
+      moveTabItem(fieldGroup, widget, anchor, true);
+   }
+
+   /**
+    * Moves specified widget before or after the given widget in the tab order.
+    * 
+    * @param   fieldGroup
+    *          Containing field-group.
+    * @param   widget
+    *          The widget to move.
+    * @param   anchor            
+    *          The widget after which the widget should be placed.
+    * @param   isAfter
+    *          {@code true} if place widget after the given widget
+    */
+   public final static void moveTabItem(FieldGroup    fieldGroup, 
+                                        CommonWidget  widget,
+                                        CommonWidget  anchor,
+                                        boolean isAfter)
+   {
+      fieldGroup.setTabItemRelative((GenericWidget<?>)widget, (GenericWidget<?>)anchor, isAfter);
       
       updateTabItemList(fieldGroup);
-      
-      return new logical(true);
    }
 
    /**

=== modified file 'src/com/goldencode/p2j/ui/client/Frame.java'
--- src/com/goldencode/p2j/ui/client/Frame.java	2022-05-16 08:32:14 +0000
+++ src/com/goldencode/p2j/ui/client/Frame.java	2022-05-16 21:49:27 +0000
@@ -799,11 +799,12 @@
 
 /**
  * A <code>Frame</code> is a top-level window with a title and a border. 
  */
 public abstract class Frame<O extends OutputManager<?>>
 extends AbstractContainer<O>
-implements ConfigOwner<FrameConfig>,
-           WidgetWithConfig<FrameConfig>,
+implements WidgetWithConfig<FrameConfig>,
            PageBreakListener,
            CloseListener
 {
@@ -2344,6 +2347,7 @@
          ThinClient tc = ThinClient.getInstance();
          List<Widget<O>> tabItems = Arrays.stream(nextTabItemList)
                   .mapToObj(id -> (Widget<O>) tc.getWidget(id))
+                  .filter(w -> w != null)
                   .<Widget<O>>map(
                      w ->
                         {
@@ -2799,8 +2803,6 @@
             }
          }
          
-         config().realized = true;
-
          FrameConfig config = config();
          // 4GL bug, row/column affects the parent inner size
          Dimension dim = getFrameRegion();
@@ -2829,14 +2831,9 @@
          // down-body will be rebuilt inside doLayout
          doLayout();
          
-         // save the calculated outer size and scrollable state in config 
-         CoordinatesConversion cc = screen().coordinates();
-         Dimension visibleSize = cc.dimensionFromNative(contentPane.getVisibleDimension());
-         Dimension scrollableSize = cc.dimensionFromNative(contentPane.getScrollDimension());
-         
          // z-order is normalized after layout is done; layout needs to use the original widget order
          // regardless of their z-class
-         this.getContentPane().normalizeZOrder();
+         getContentPane().normalizeZOrder();
       }
    }
    
@@ -7128,16 +7129,6 @@
    protected abstract double adjustMinSize(double minWidthOrHeight, boolean isHeight);
     
    /**
-    * Check prerequisite conditions for potential size change errors.
-    * 
-    * @return  <code>true</code> if conditions are met.
-    */
-   private boolean sizeErrorCondition()
-   {
-      return isConfigUpdateActive() && sizeChanged;
-   }
-   
-   /**
     * Determine if the <code>ROW</code> attribute can be set to the specified 
     * value.
     * 
