=== modified file 'src/com/goldencode/p2j/ui/chui/ThinClient.java'
--- src/com/goldencode/p2j/ui/chui/ThinClient.java	2022-03-10 21:22:31 +0000
+++ src/com/goldencode/p2j/ui/chui/ThinClient.java	2022-03-13 18:36:16 +0000
@@ -2784,6 +2784,8 @@
 **     HC  20220228          Prevented client abend when error in loading image data.
 **     CA  20220304          In putBrowseColumnValues, log if attempting to use a BROWSE column with an invalid
 **                           ID (set to -1).
+**     VVT 20220306          Method processProgressEvent: results of calls to event.id(), and event.source() extracted
+**                           to local variables (otherwise these calls are performed >40 times in the method code).
 **     SBI 20220311          openMimeResource should accept spaces and escaped spaces for local file resources.
 */
 
@@ -19582,11 +19584,13 @@
     */
    public void processProgressEvent(Event event)
    {
+      int evtId = event.id();
+      Widget eventSource = event.source();
+      
       if (event instanceof ResourceEvent)
       {
          ResourceEvent revt = (ResourceEvent) event;
          
-         int evtId = revt.id();
          long resourceId = revt.resourceId();
          boolean foundGo = false;
          if (checkBreak(resourceId, evtId))
@@ -19599,7 +19603,7 @@
          boolean consume = false;
          try
          {
-            TriggerResult tr = invokeTriggers(event.source(), resourceId, evtId, false);
+            TriggerResult tr = invokeTriggers(eventSource, resourceId, evtId, false);
             consume = tr.consume;
          }
          finally
@@ -19626,7 +19630,7 @@
          }
 
          boolean comEvent = srv.getName().equals("COM-EVENT");
-         Runnable serverCode = () -> invokeOnServer(() -> server.invokeServerEvent(srv.id()));
+         Runnable serverCode = () -> invokeOnServer(() -> server.invokeServerEvent(evtId));
          
          if (comEvent)
          {
@@ -19644,17 +19648,18 @@
       {
          MouseEvt evt = (MouseEvt) event;
          
+         MouseEvent awtEvent = evt.getEvent();
+         int eventType = awtEvent.getID();
          if (consumeNextMouseClick)
          {
             // this flag is set only on a mouse released event.  only the next clicked event must be consumed.
-            int evtID = evt.getEvent().getID();
-            if (evtID == MouseEvt.MOUSE_PRESSED  || 
-                evtID == MouseEvt.MOUSE_RELEASED ||
-                evtID == MouseEvt.MOUSE_CLICKED)
+            if (eventType == MouseEvt.MOUSE_PRESSED  || 
+                eventType == MouseEvt.MOUSE_RELEASED ||
+                eventType == MouseEvt.MOUSE_CLICKED)
             {
                consumeNextMouseClick = false;
 
-               if (evtID == MouseEvt.MOUSE_CLICKED)
+               if (eventType == MouseEvt.MOUSE_CLICKED)
                {
                   // consume this - it will be raised explicitly
                   evt.consume();
@@ -19663,7 +19668,10 @@
             }
          }
          
-         Function<Widget, Boolean> hasWindow = (w) ->
+         // apply the event
+         TopLevelWindow window = (TopLevelWindow) eventSource;
+         
+         Function<Widget, Boolean> hasWindow = w ->
          {
             if (w != null)
             {
@@ -19678,22 +19686,17 @@
             return true;
          };
          
-         // apply the event
-         TopLevelWindow window = (TopLevelWindow) evt.source();
-         
          if (!hasWindow.apply(window))
          {
             // the window has been destroyed
             return;
          }
 
-         Widget<?> mouseSource = evt.getExplicitSource();
-
-         if (mouseSource == null)
-         {
-            mouseSource = window.findMouseSource(evt);
-         }
-
+         // Note: we effectively ignore the value provided by the driver here,
+         // to restore using driver events, replace window.findMouseSource(evt) back
+         // to evt.getExplicitSource()
+         Widget<?> mouseSource = window.findMouseSource(evt);
+       
          if (mouseSource == null)
          {
             mouseSource = window;
@@ -19750,18 +19753,16 @@
          boolean maySwitchFocusDown = false;
          boolean maySwitchFocusUp = false;
          
-         // get the mouse event type
-         int evtID = evt.getEvent().getID();
          // exclude mouse move events from consideration
-         if (evtID == MouseEvt.MOUSE_PRESSED  ||
-             evtID == MouseEvt.MOUSE_RELEASED ||
-             evtID == MouseEvt.MOUSE_CLICKED)
+         if (eventType == MouseEvt.MOUSE_PRESSED  ||
+             eventType == MouseEvt.MOUSE_RELEASED ||
+             eventType == MouseEvt.MOUSE_CLICKED)
          {
             maySwitchFocusDown = mouseSource != null &&
-                                 evtID == MouseEvt.MOUSE_PRESSED &&
+                                 eventType == MouseEvt.MOUSE_PRESSED &&
                                  mouseSource.maySwitchFocus();
             maySwitchFocusUp   = mouseSource != null &&
-                                 evtID == MouseEvt.MOUSE_RELEASED &&
+                                 eventType == MouseEvt.MOUSE_RELEASED &&
                                  mouseSource.maySwitchFocus();
             
             if (mouseSource != null && !mouseSource.isRMBEntrySupported())
@@ -19776,16 +19777,16 @@
          // from here on, we need to work with the browse
          if (legacyMouseSource != null && legacyMouseSource.parent(Browse.class) != null)
          {
-            if (!isChui() && (evtID == MouseEvt.MOUSE_CLICKED ||
-                              evtID == MouseEvt.MOUSE_PRESSED ||
-                              evtID == MouseEvt.MOUSE_RELEASED))
+            if (!isChui() && (eventType == MouseEvt.MOUSE_CLICKED ||
+                              eventType == MouseEvt.MOUSE_PRESSED ||
+                              eventType == MouseEvt.MOUSE_RELEASED))
             {
                // GUI browse click case.
                if (legacyMouseSource instanceof FillIn ||
                    legacyMouseSource instanceof ComboBox)
                {
                   // BrowseColumn is the legacyMouseSource
-                  legacyMouseSource = legacyMouseSource.getTriggerWidget(evtID, evt.isRealKey());
+                  legacyMouseSource = legacyMouseSource.getTriggerWidget(eventType, evt.isRealKey());
                }
                else if (legacyMouseSource instanceof ToggleBox)
                {
@@ -19800,7 +19801,7 @@
                   else
                   {
                      // BrowseColumn is the legacyMouseSource
-                     legacyMouseSource = legacyMouseSource.getTriggerWidget(evtID, evt.isRealKey());
+                     legacyMouseSource = legacyMouseSource.getTriggerWidget(eventType, evt.isRealKey());
                   }
                }
                else if (!mouseSource.raiseLegacyMouseEvents())
@@ -19810,11 +19811,11 @@
                else if (legacyMouseSource instanceof BrowseColumn)
                {
                   BrowseGuiImpl browse = (BrowseGuiImpl) legacyMouseSource.parent(Browse.class);
-                  if (evtID == MouseEvt.MOUSE_PRESSED ||
-                      evtID == MouseEvt.MOUSE_RELEASED ||
+                  if (eventType == MouseEvt.MOUSE_PRESSED ||
+                      eventType == MouseEvt.MOUSE_RELEASED ||
                       !evt.isLeftButton() ||
                       ((BrowseColumn) legacyMouseSource).config().readOnly ||
-                      browse.isBelowLastRow(evt.getEvent()))
+                      browse.isBelowLastRow(awtEvent))
                   {
                      legacyMouseSource = browse;
                   }
@@ -19830,7 +19831,7 @@
                legacyMouseSource = legacyMouseSource.parent(Browse.class);
             }
          }
-         else if (legacyMouseSource instanceof BrowseGuiImpl && evtID == MouseEvt.MOUSE_CLICKED)
+         else if (legacyMouseSource instanceof BrowseGuiImpl && eventType == MouseEvt.MOUSE_CLICKED)
          {
             // If we click on the empty area to the right of the rightmost column which is an editing column,
             // editing is started and the trigger for this rightmost column is invoked.
@@ -19940,12 +19941,12 @@
          
          if (evt.isLeftButton())
          {
-            if (evtID == MouseEvt.MOUSE_PRESSED)
+            if (eventType == MouseEvt.MOUSE_PRESSED)
             {
                downMouseButtonNewFocus = newFocus;
                downMouseButtonOldFocus = oldFocus;
             }
-            else if (evtID == MouseEvt.MOUSE_RELEASED)
+            else if (eventType == MouseEvt.MOUSE_RELEASED)
             {
                if (downMouseButtonNewFocus != null && !downMouseButtonNewFocus.hasFocus())
                {
@@ -20106,15 +20107,13 @@
       if (event instanceof PortableMouseEvent || event instanceof WindowEvent)
       {
          // set the event details
-         int evtId = event.id();
-         
          functionKey = event instanceof WindowEvent ? ((WindowEvent) event).getAction()
                                                     : ((PortableMouseEvent) event).getAction();
          
-         Widget<?> src = event.source();
+         Widget<?> src = eventSource;
          
          WidgetId id = src.getId();
-         if (src != null && id != null && WidgetId.virtualWidget(id))
+         if (id != null && WidgetId.virtualWidget(id))
          {
             // virtual widgets do not call mouse triggers
          }
@@ -20147,11 +20146,11 @@
             
             lastKeyState = 0;
 
-            TriggerResult tr = invokeTriggers(event.source(), evtId, false, eventX, eventY);
+            TriggerResult tr = invokeTriggers(eventSource, evtId, false, eventX, eventY);
             boolean consumed = tr.consume;
             if (functionKey != -1 && functionKey != evtId)
             {
-               tr = invokeTriggers(event.source(), functionKey, false, eventX, eventY);
+               tr = invokeTriggers(eventSource, functionKey, false, eventX, eventY);
                consumed = consumed || tr.consume;
             }
             
@@ -20162,8 +20161,8 @@
             }
 
             // check if we have mouse event attached exit conditions
-            if (checkBreak(event.source(), evtId, false) ||
-                (functionKey != -1 && checkBreak(event.source(), functionKey, false)))
+            if (checkBreak(eventSource, evtId, false) ||
+                (functionKey != -1 && checkBreak(eventSource, functionKey, false)))
             {
                addGoPending();
             }
@@ -20180,27 +20179,27 @@
       
       if (event instanceof WindowActivated)
       {
-         event.source().processEvent(event);
+         eventSource.processEvent(event);
          return;
       }
 
       // direct manipulation events calls the handler from source
       if (event instanceof DirectManipulationEvent)
       {
-         boolean invokeBefore = event.id() == Keyboard.SE_END_MOVE;
+         boolean invokeBefore = evtId == Keyboard.SE_END_MOVE;
          if (invokeBefore)
          {
             // for buttons invoke immediately
-            event.source().processDirectManipulationEvent((DirectManipulationEvent)event);
+            eventSource.processDirectManipulationEvent((DirectManipulationEvent)event);
          }
          
          // execute registered triggers first
-         TriggerResult tr = invokeTriggers(event.source(), event.id(), false);
+         TriggerResult tr = invokeTriggers(eventSource, evtId, false);
          
          Integer aliasId = ((DirectManipulationEvent) event).getAlias();
          if (!tr.executed && aliasId != null)
          {
-            tr = invokeTriggers(event.source(), aliasId, false);
+            tr = invokeTriggers(eventSource, aliasId, false);
          }
          
          if (!invokeBefore)
@@ -20208,19 +20207,19 @@
             // if not consumed then invoke event handler
             if (!tr.consume)
             {
-               event.source().processDirectManipulationEvent((DirectManipulationEvent)event);
+               eventSource.processDirectManipulationEvent((DirectManipulationEvent)event);
             }
             // if consumed(NO-APPLY defined) inform handler about consumed event
             else if (((DirectManipulationEvent)event).hasHandler())
             {
-               ((DirectManipulationEvent)event).getHandler().setNoApplyEvent(event.id());
+               ((DirectManipulationEvent)event).getHandler().setNoApplyEvent(evtId);
             }
          }
          return;
       }
       
       KeyInput evt    = (event instanceof KeyInput ? (KeyInput) event : null);
-      Widget   src    = event.source().getTriggerWidget();
+      Widget   src    = eventSource.getTriggerWidget();
       int      key      = (evt == null ? KeyInput.CHAR_UNDEFINED : evt.keyCode());
       int      keyState = (evt == null ? 0 : evt.getKeyState());
       int      action   = (evt == null ? KeyInput.CHAR_UNDEFINED  : evt.actionCode());

=== modified file 'src/com/goldencode/p2j/ui/client/TopLevelWindow.java'
--- src/com/goldencode/p2j/ui/client/TopLevelWindow.java	2022-02-15 12:19:45 +0000
+++ src/com/goldencode/p2j/ui/client/TopLevelWindow.java	2022-03-09 23:24:54 +0000
@@ -65,6 +65,11 @@
 **                  See #6013.
 **     VVT 20220215 Change 13474 for #6013 temporary reverted (caused multiple regressions,
 **                  need to be completed).
+**     VVT 20220306 A new private field mousePressedSource was introduced. This field remembers the widget
+**                  the mouse was pressed upon, and the value is used as the mouse event target during drag
+**                  operation. findMouseSource(MouseEvt) implementation added. It uses
+**                  mousePressedSource for drag operations, otherwise calls
+**                  findMouseSource(evtID, mousePoint). See #6013.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -127,7 +132,6 @@
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.event.*;
-import com.goldencode.p2j.ui.client.event.FocusEvent;
 import com.goldencode.p2j.ui.client.gui.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
 import com.goldencode.p2j.ui.client.gui.theme.*;
@@ -215,16 +219,33 @@
    }
    
    /**
-    * Get the title bar widget associated witn this window.
+    * Get the title bar widget associated with this window.
     * 
     * @return   See above.
     */
+   @SuppressWarnings("static-method")
    public WindowTitleBar getTitleBar()
    {
       return null;
    }
 
    /**
+    * Find the widget to which the given mouse event can be applied.
+    * 
+    * @param    evt
+    *           The mouse event.
+    * 
+    * @return   A widget over which the mouse is positioned or {@code null} if none found.
+    */
+   public final Widget<O> findMouseSource(MouseEvt evt)
+   {
+      int evtID = evt.getEvent().getID();
+      java.awt.Point p = evt.getEvent().getPoint();
+      Widget<O> mouseSource = findMouseSource(evtID, new NativePoint(p.x, p.y));
+      return (mouseSource == null ? this : mouseSource);
+   }
+
+   /**
     * Sets the default window icon for all new windows on the client side.
     * 
     * @param    type
@@ -652,6 +673,7 @@
     *
     * @return   <code>TRUE</code> if operation successful <code>FALSE</code> otherwise.
     */
+   @SuppressWarnings("static-method")
    public boolean setWindowIcon(byte[] newIcon)
    {
       // this is generic and CHUI version returning false
@@ -668,6 +690,7 @@
     *
     * @return   <code>TRUE</code> if operation successful <code>FALSE</code> otherwise.
     */
+   @SuppressWarnings("static-method")
    public boolean setWindowIcon(boolean small, byte[] newIcon)
    {
       // this is generic and CHUI version returning false
@@ -684,6 +707,7 @@
     *
     * @return   <code>TRUE</code> if operation successful <code>FALSE</code> otherwise.
     */
+   @SuppressWarnings("static-method")
    public boolean loadWindowIcon(boolean small, String name)
    {
       // this is generic and CHUI version returning false
@@ -705,6 +729,7 @@
     * 
     * @return   This implementation always returns <code>false</code>.
     */
+   @SuppressWarnings("static-method")
    public boolean isMaximized()
    {
       return false;
@@ -715,6 +740,7 @@
     *
     * @return   If minimized returns <code>true</code>, <code>false</code> otherwise.
     */
+   @SuppressWarnings("static-method")
    public boolean isMinimized()
    {
       return false;

=== modified file 'src/com/goldencode/p2j/ui/client/UiUtils.java'
--- src/com/goldencode/p2j/ui/client/UiUtils.java	2022-03-10 21:22:31 +0000
+++ src/com/goldencode/p2j/ui/client/UiUtils.java	2022-03-13 18:35:35 +0000
@@ -425,7 +425,7 @@
     * @param    w
     *           Component to for which ID is requested.
     * 
-    * @return   The widget ID or {@code null} if the widget doesn't have a config.
+    * @return   The widget ID or {@code null}.
     */
    public static WidgetId getWidgetId(Widget<?> w)
    {
@@ -438,7 +438,7 @@
     * @param    widget
     *           Component to for which ID is requested.
     * 
-    * @return   The widget ID or -1 if the widget doesn't have a config.
+    * @return   The widget ID or -1 if widget is {@code null}
     */
    static public int getWidgetIdAsInt(Widget<?> widget)
    {

=== modified file 'src/com/goldencode/p2j/ui/client/WindowManager.java'
--- src/com/goldencode/p2j/ui/client/WindowManager.java	2022-03-10 01:16:48 +0000
+++ src/com/goldencode/p2j/ui/client/WindowManager.java	2022-03-13 18:36:38 +0000
@@ -2,7 +2,7 @@
 ** Module   : WindowManager.java
 ** Abstract :
 **
-** Copyright (c) 2010-2021, Golden Code Development Corporation.
+** Copyright (c) 2010-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 SIY 20100629 Created initial version
@@ -127,6 +127,14 @@
 **                  have a caret, be highlighted, etc.
 **     SVL 20220209 Generate frame LEAVE event when another window receives the focus.
 **     VVT 20220218 Javadocs and other cosmetic fixes.
+**     VVT 20220306 getNextActiveWindow code refactored, so compilator complains of "Unlikely argument type"
+**                  no more. processWindowEvent: the mouse source stored with the event by the driver
+**                  used instead of running window.findMouseSource(evt) second time.  Note: after this
+**                  change, the call to findMouseSource is executed only once by the driver (previously
+**                  findMouseSource was called multiple times during the event dispatching and processing,
+**                  which is waste of CPU time, and probably may result in incorrect returned value).
+**                  Local variable parent renamed to mouseSourceWithId, since the old name was misleading.
+**                  See #6013.
 **     EVL 20220309 Dialog box window should ensure the focus to be requested for activation case.
 */
 /*
@@ -215,6 +223,7 @@
    private static final ContextLocal<WorkArea> work = 
    new ContextLocal<WorkArea>()
    {
+      @Override
       protected synchronized WorkArea initialValue()
       {
          return new WorkArea();
@@ -1259,7 +1268,7 @@
          }
          
          Map<GuiWindow, Set<TopLevelWindow>> delayed = wa.delayedMinimizeWindows;
-         delayed.remove(window);
+         delayed.remove((Object)window);
          for (Set<TopLevelWindow> v : delayed.values())
          {
             v.remove(window);
@@ -1812,17 +1821,18 @@
    {
       Predicate<TitledWindow<?>> checkNextWindow = tw ->
       {
-         if (!tw.isVisible()                               ||
-             !(tw instanceof GuiWindow)                    ||
-             ((GuiWindow) tw).isShareActivationWithOwner() ||
-             ((GuiWindow) tw).isMinimized()                ||
-             tw.equals(hidingWindow))
+         if (!tw.isVisible() || !(tw instanceof GuiWindow))
          {
             return false;
          }
          
-         return true;
+         GuiWindow guiWindow = (GuiWindow) tw;
+         
+         return !guiWindow.isShareActivationWithOwner() &&
+                !guiWindow.isMinimized()                &&
+                !guiWindow.equals(hidingWindow);
       };
+      
       if (!isIconifying)
       {
          ArrayList<Window<?>> list = work.get().currentWindowsStack;
@@ -1915,26 +1925,22 @@
       {
          MouseEvt evt = (MouseEvt) event;
          
-         Widget<?> mouseSource = (Widget<?>) evt.getExplicitSource();
-         if (mouseSource == null)
-         {
-            mouseSource = window.findMouseSource(evt);
-         }
+         Widget<?> mouseSource = evt.getExplicitSource();
          
          if (mouseSource != null && mouseSource.isDisplayed())
          {
             // search for a parent with a valid id
-            Widget<?> parent = WidgetRegistry.findAncestor(mouseSource,
+            Widget<?> mouseSourceWithId = WidgetRegistry.findAncestor(mouseSource,
                                                            t -> UiUtils.getWidgetId(t) != null);
             
-            WidgetId id = (parent != null) ? parent.getId() : null;
+            WidgetId id = (mouseSourceWithId != null) ? mouseSourceWithId.getId() : null;
             
-            if (id != null && parent.ancestor() != null)
+            if (id != null && mouseSourceWithId.ancestor() != null)
             {
                // not all OS-widgets have an ID, skip those
                GuiDriver gd = (GuiDriver) OutputManager.getDriver();
                
-               gd.selectWindow(parent.topLevelWindow().getId().asInt());
+               gd.selectWindow(mouseSourceWithId.topLevelWindow().getId().asInt());
                try
                {
                   gd.handleMouseEvent(id.asInt(), evt.getEvent());

=== modified file 'src/com/goldencode/p2j/ui/client/gui/BrowseGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/BrowseGuiImpl.java	2022-03-07 13:06:46 +0000
+++ src/com/goldencode/p2j/ui/client/gui/BrowseGuiImpl.java	2022-03-10 10:33:31 +0000
@@ -203,6 +203,9 @@
 **     VVT 20220218 Missing @Override annotations added and other cosmetic fixes.
 **     HC  20220304 OFF-END is triggered for additional events: arrow down, pg down, mouse wheel,
 **                  scroll.
+**     VVT 20220307 findMouseSource(w.findMouseSource(eventID, NativePoint)) overridden to assure the mouse
+**                  pressed and mouse release events are sent to the same widget. See #6013-152. Also
+**                  multiple cosmetic fixes.
 */
 
 /*
@@ -262,7 +265,6 @@
 
 import java.awt.event.*;
 import java.util.*;
-import java.util.logging.*;
 
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.chui.*;
@@ -367,9 +369,6 @@
    /** Template of a sorting number image name. */
    private static final String SORT_NUMBER_IMAGE_NAME_TEMPLATE = "sort-num-%d";
 
-   /** Logger. */
-   private static final Logger LOG = LogHelper.getLogger(BrowseGuiImpl.class);
-
    /**
     * When the current row is out of the view below, then if we press UP key, the new current row
     * becomes <code>max(1, DOWN - UP_KEY_BOTTOM_OFFSET_ROWS)</code>th row in the viewport
@@ -909,7 +908,7 @@
     *
     * @return attribute container for the given cell.
     */
-   public GuiCellAttributes getCellAttr(BrowseRow[] rowCache, int viewportIndex, int columnOrdinal)
+   public static GuiCellAttributes getCellAttr(BrowseRow[] rowCache, int viewportIndex, int columnOrdinal)
    {
       if (rowCache == null || viewportIndex >= rowCache.length)
       {
@@ -1338,7 +1337,7 @@
    @Override
    public void drawCaret()
    {
-
+      // no-op
    }
 
    /**
@@ -1586,6 +1585,7 @@
       // row resize messages first
       if (id == Keyboard.SE_START_ROW_RESIZE)
       {
+         // no-op
       }
       else if (id == Keyboard.SE_END_ROW_RESIZE)
       {
@@ -1701,7 +1701,6 @@
                break;
             }
 
-            BrowseColumnConfig c = column.config();
             int newWidth = (int) (availableWidth * ((double) dataWidth / totalDataWidth));
             column.setWidth(Math.max(cc.widthFromNative(newWidth), minWidth));
             totalWidth += column.physicalDimension().width;
@@ -1737,7 +1736,7 @@
             {
                BrowseColumnGuiImpl column = getColumn(i);
                column.config().selectable = brc.selectable;
-               ((GuiDriver) screen().getDriver()).registerDirectManipulation(column);
+               ((GuiDriver) OutputManager.getDriver()).registerDirectManipulation(column);
             }
          }
       }
@@ -1806,10 +1805,8 @@
       {
          return -1;
       }
-      else
-      {
-         return y / (getRowHeight() + BORDER_THICKNESS);
-      }
+      
+      return y / (getRowHeight() + BORDER_THICKNESS);
    }
 
    /**
@@ -2643,17 +2640,13 @@
          {
             return gd.getSysColor(SysColor.COLOR_3DFACE);
          }
-         else
-         {
-            GuiColorResolver browseResolver = getBrowseColorResolver();
-            return fg ? browseResolver.fgColor : browseResolver.bgColor;
-         }
-      }
-      else
-      {
-         return fg ? ColorManager.getFgColor(window, color) :
-               ColorManager.getBgColor(window, color);
-      }
+         
+         GuiColorResolver browseResolver = getBrowseColorResolver();
+         return fg ? browseResolver.fgColor : browseResolver.bgColor;
+      }
+      
+      return fg ? ColorManager.getFgColor(window, color) :
+            ColorManager.getBgColor(window, color);
    }
 
    /**
@@ -2688,6 +2681,7 @@
    {
       draggedColumn = column;
       this.leftDragColumnMouseOffset = leftDragColumnMouseOffset;
+      MouseHandler.startDragMode();
    }
 
    /**
@@ -2766,6 +2760,7 @@
                               (visibleColumnIndex < config.numLockedColumns ? 0 :
                                  resizedColumn.parent().physicalLocation().x);
       leftResizeColumnOffset = Math.max(columnLeftEdge, browseLeftEdge);
+      MouseHandler.startDragMode();
    }
 
    /**
@@ -2959,10 +2954,8 @@
       {
          return FontManager.resolveFont(window(), labelFont, gd);
       }
-      else
-      {
-         return getBrowseFont();
-      }
+      
+      return getBrowseFont();
    }
 
    /**
@@ -3380,8 +3373,8 @@
                FillIn fillIn = (FillIn) editor;
                if (mouseFocusGained)
                {
-                  fillIn.config().selectionStart = FillInConfig.INVALID_SELECTION;
-                  fillIn.config().selectionEnd = FillInConfig.INVALID_SELECTION;
+                  fillIn.config().selectionStart = ControlConfig.INVALID_SELECTION;
+                  fillIn.config().selectionEnd = ControlConfig.INVALID_SELECTION;
                   fillIn.setCursorOffset(0);
                   fillIn.setZapMode(false);
                }
@@ -3821,31 +3814,6 @@
    }
 
    /**
-    * Checks if the browse scrollbar with the given orientation is visible.
-    *
-    * @param orientation
-    *        Scrollbar orientation: vertical or horizontal.
-    *
-    * @return <code>true</code> the browse scrollbar with the given orientation is visible.
-    */
-   private boolean isScrollBarVisible(ScrollBar.Orientation orientation)
-   {
-      ScrollBarGuiImpl bar;
-      boolean visible = false;
-
-      if (scrollPane != null)
-      {
-         bar = scrollPane.getScrollBar(orientation);
-         if (bar != null)
-         {
-            visible = bar.isVisible();
-         }
-      }
-
-      return visible;
-   }
-
-   /**
     * This functions contains the common code for showing/hiding the line of the column filter
     * entry fields.
     */
@@ -4613,10 +4581,8 @@
       {
          return calcImplicitRowHeight();
       }
-      else
-      {
-         return calcExplicitRowHeight();
-      }
+      
+      return calcExplicitRowHeight();
    }
 
    /**
@@ -4971,10 +4937,10 @@
     *
     * @return <code>true</code> if CTRL key was held down when a mouse button was pressed.
     */
-   boolean isCtrlModifier(MouseEvent e)
+   static boolean isCtrlModifier(MouseEvent e)
    {
-      return (e.getModifiers()   & MouseEvent.CTRL_MASK)      != 0 ||
-             (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0;
+      return (e.getModifiers()   & InputEvent.CTRL_MASK)      != 0 ||
+             (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
    }
    
    /**
@@ -5385,10 +5351,10 @@
     *
     * @return <code>true</code> if SHIFT key was held down when a mouse button was pressed.
     */
-   private boolean isShiftModifier(MouseEvent e)
+   private static boolean isShiftModifier(MouseEvent e)
    {
-      return (e.getModifiers()   & MouseEvent.SHIFT_MASK)      != 0 ||
-             (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;
+      return (e.getModifiers()   & InputEvent.SHIFT_MASK)      != 0 ||
+             (e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0;
    }
 
    /**
@@ -6366,7 +6332,7 @@
     * @param destIndex
     *        0-based position to which the element is moved.
     */
-   private void moveColumnInArray(Object[] array, int srcIndex, int destIndex)
+   private static void moveColumnInArray(Object[] array, int srcIndex, int destIndex)
    {
       if (array == null)
       {
@@ -6638,6 +6604,7 @@
       }
       else if (isEditorToggleBox())
       {
+         // no-op
       }
       else
       {
@@ -6808,7 +6775,6 @@
     */
    private class HorizontalColumnScroller
    extends ScrollBarControllerGuiImpl
-   implements ScrollBarController
    {
       /**
        * Get maximum value for the scroll bar.
@@ -6881,10 +6847,8 @@
 
             return 0;
          }
-         else
-         {
-            return offset;
-         }
+         
+         return offset;
       }
 
       /**
@@ -6919,7 +6883,7 @@
 
          int availSize = scrollBar.getTravelSize() - getThumbSize();
          int tPos = Coordinate.toInt(Coordinate.scale(
-               (double) availSize * (double) getPosition() / (double) getMax()));
+               (double) availSize * (double) getPosition() / getMax()));
          tPos = Math.max(0, tPos);
          tPos = Math.min(tPos, availSize);
          return tPos;
@@ -7040,7 +7004,7 @@
 
             int position = availSize > 0 ?
                               Coordinate.toInt(Coordinate.scale(
-                                    (double) dragPos * (double) getMax() / (double) availSize)) :
+                                    (double) dragPos * (double) getMax() / availSize)) :
                               0;
 
             positionUpdated(position, ScrollStep.CUSTOM, true);
@@ -7148,24 +7112,22 @@
             {
                return;
             }
-            else
+            
+            int pageSize = getPageSize();
+            if (ScrollStep.LONG.equals(scrollStep))
             {
-               int pageSize = getPageSize();
-               if (ScrollStep.LONG.equals(scrollStep))
-               {
-                  if (position < oldPosition)
-                  {
-                     position = oldPosition - pageSize;
-                  }
-                  else
-                  {
-                     position = oldPosition + pageSize;
-                  }
-               }
+               if (position < oldPosition)
+               {
+                  position = oldPosition - pageSize;
+               }
+               else
+               {
+                  position = oldPosition + pageSize;
+               }
+            }
 
-               position += pageSize;
-               scrollToRow(position, false, false, drag, true, true, pageSize, false);
-            }
+            position += pageSize;
+            scrollToRow(position, false, false, drag, true, true, pageSize, false);
          }
          else
          {
@@ -7246,10 +7208,8 @@
             int firstExistingRow = getFirstExistingRow();
             return firstExistingRow == -1 ? 0 : firstExistingRow;
          }
-         else
-         {
-            return Math.max(0, config.maxDataGuess - getDown());
-         }
+         
+         return Math.max(0, config.maxDataGuess - getDown());
       }
 
       /**
@@ -7300,7 +7260,7 @@
             int scrollSize = getAvailableTravelSize() - getThumbSize();
             int maxVal = getMax();
             int position = Coordinate.toInt(Coordinate.scale(
-                              (double)dragPos * (double)maxVal / (double)scrollSize));
+                              (double)dragPos * (double)maxVal / scrollSize));
             position = Math.max(0, position);
             position = Math.min(position, maxVal);
 
@@ -7336,12 +7296,12 @@
             {
                lastExistingRow += 1; // 1-based row number
                dHeight = (double) availSize *
-                     (double) lastExistingRow / (double) (lastExistingRow + 1);
+                     (double) lastExistingRow / (lastExistingRow + 1);
             }
          }
          else
          {
-            dHeight = (double) availSize * (double) getDown() / (double) config.maxDataGuess;
+            dHeight = (double) availSize * (double) getDown() / config.maxDataGuess;
          }
          int tHeight = Coordinate.toInt(Coordinate.scale(dHeight));
 
@@ -7376,7 +7336,7 @@
          {
             pos = Coordinate.toInt(Coordinate.scale(
                   (double) availSize * (double) getPosition() /
-                        (double) (config.maxDataGuess - getDown())));
+                        (config.maxDataGuess - getDown())));
             pos = Math.min(pos, availSize);
          }
 

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ButtonListGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/ButtonListGuiImpl.java	2021-07-26 22:23:19 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ButtonListGuiImpl.java	2022-03-08 22:06:23 +0000
@@ -2,7 +2,7 @@
 ** Module   : ButtonListGuiImpl.java
 ** Abstract : ButtonListGuiImpl implements the button list widget.
 **
-** Copyright (c) 2020-2021, Golden Code Development Corporation.
+** Copyright (c) 2020-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description-----------------------------------
 ** 001 SBI 20200305 Created initial version.
@@ -18,6 +18,10 @@
 **     SBI 20210617 Changed to display its first button with expanded items list and fixed its accordion-like style.
 **     RFB 20210726 Protect afterConfigUpdate from indexing into the GroupButtons when it is invalid. 
 **                  Ref #5562.
+**     VVT 20220306 processKeyEvent: piece of code doing nothing removed.
+**                  findMouseSource(NativePoint) fixed.
+**                  findMouseSource(eventId, NativePoint) was overriden. See #6013.
+**                  setSelected: missing Javadocs added.
 */
 
 /*
@@ -841,20 +845,6 @@
             return;
          }
          
-         
-         if (key == Keyboard.KA_CURSOR_LEFT)
-         {
-         }
-         else if (key == Keyboard.KA_CURSOR_RIGHT)
-         {
-         }
-         else if (key == Keyboard.KA_CURSOR_UP || key == Keyboard.KA_CURSOR_DOWN)
-         {
-         }
-         else if (key == Keyboard.KA_RETURN)
-         {
-         }
-         
          repaint();
       }
    }
@@ -1380,10 +1370,10 @@
       private Viewport<GuiOutputManager> itemsView;
       
       /** The scroll up button */
-      private ButtonGuiImpl scrollUpButton;
+      private final ButtonGuiImpl scrollUpButton;
       
       /** The scroll down button */
-      private ButtonGuiImpl scrollDownButton;
+      private final ButtonGuiImpl scrollDownButton;
       
       /** 
        * Creates the items control for the given items list.
@@ -1474,6 +1464,7 @@
       /**
        * Prepares its components.
        */
+      @Override
       public void doLayout()
       {
          changeControlState();
@@ -1581,46 +1572,75 @@
        * 
        * @return   The item under the mouse pointer.
        */
+      @Override
       public Widget<GuiOutputManager> findMouseSource(NativePoint p)
       {
          Widget<GuiOutputManager> w = super.findMouseSource(p);
-         
-         if (w == scrollDownButton || w == scrollUpButton || w instanceof Item)
-         {
-            return w;
-         }
-         
-         if (w == null)
-         {
-            //adjust x-coordinate
-            NativeRectangle nd = this.itemsView.physicalBounds();
-            
-            int x = p.x;
-            
-            int y = p.y;
-            
-            if (nd.left() > x)
-            {
-               x = nd.left() + 1;
-            }
-            
-            if (nd.right() < x)
-            {
-               x = nd.right() - 1;
-            }
-            
-            w = super.findMouseSource(new NativePoint(x, y));
-            
-            if (w == scrollDownButton || w == scrollUpButton || w instanceof Item)
-            {
-               return w;
-            }
-         }
-         
-         return w;
+         if (w != null)
+         {
+            return w;
+         }
+
+         //adjust x-coordinate
+         NativeRectangle nd = this.itemsView.physicalBounds();
+
+         int x = p.x;
+
+         int y = p.y;
+
+         if (nd.left() > x)
+         {
+            x = nd.left() + 1;
+         }
+
+         if (nd.right() < x)
+         {
+            x = nd.right() - 1;
+         }
+
+         return super.findMouseSource(new NativePoint(x, y));
+      }
+
+      /**
+       * Find the source for mouse event.
+       * 
+       * @param eventId
+       *        the event ID to match 
+       * @param p
+       *        the mouse position relative to this widget
+       *        
+       * @return the mouse event source or {@code null} if no source found.
+       */
+      @Override
+      public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+      {
+         Widget<GuiOutputManager> w = super.findMouseSource(eventId, p);
+         if (w != null)
+         {
+            return w;
+         }
+
+         //adjust x-coordinate
+         NativeRectangle nd = this.itemsView.physicalBounds();
+
+         int x = p.x;
+
+         int y = p.y;
+
+         if (nd.left() > x)
+         {
+            x = nd.left() + 1;
+         }
+
+         if (nd.right() < x)
+         {
+            x = nd.right() - 1;
+         }
+
+         return super.findMouseSource(eventId, new NativePoint(x, y));
       }
    }
-   
+
    /**
     * Represents a scrollable items list.
     */
@@ -2042,6 +2062,7 @@
        * 
        * @return   The item under the mouse pointer.
        */
+      @Override
       public Widget<GuiOutputManager> findMouseSource(NativePoint p)
       {
          NativePoint shift = new NativePoint(0, itemIndexOffset * itemHeightInPixels);
@@ -2073,6 +2094,49 @@
          
          return super.findMouseSource(p);
       }
+
+      /**
+       * Find the source for mouse event.
+       * 
+       * @param eventId
+       *        the event ID to match 
+       * @param p
+       *        the mouse position relative to this widget
+       *        
+       * @return the mouse event source or {@code null} if no source found.
+       */
+      @Override
+      public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+      {
+         NativePoint shift = new NativePoint(0, itemIndexOffset * itemHeightInPixels);
+         
+         NativePoint loc = p.minus(shift);
+         
+         NativeDimension size = getVisibleDimension();
+         
+         if (loc.x >= 0 && loc.x < size.width && loc.y >= 0 && loc.y <= size.height)
+         {
+            for (int i = 0, h = 0; i < numberOfVisibleItems; i++, h += itemHeightInPixels)
+            {
+               if (h <= loc.y && loc.y <= (h + itemHeightInPixels))
+               {
+                  return get(itemIndexOffset + i);
+               }
+            }
+            
+            int limitIndex = itemIndexOffset + numberOfVisibleItems;
+            
+            // calculate the index of a partly visible or the last item
+            if (limitIndex == numberOfAllItems)
+            {
+               limitIndex--;
+            }
+            
+            return get(limitIndex);
+         }
+         
+         return super.findMouseSource(eventId, p);
+      }
    }
    
    /**
@@ -2135,6 +2199,7 @@
        * 
        * @return   The item's dimension
        */
+      @Override
       public Dimension dimension()
       {
          return new Dimension(this.config.widthChars, this.config.heightChars);
@@ -2145,6 +2210,7 @@
        * 
        * @return   The item's width
        */
+      @Override
       public double width()
       {
          return dimension().width;
@@ -2155,6 +2221,7 @@
        * 
        * @return   The item's height
        */
+      @Override
       public double height()
       {
          return dimension().height;
@@ -2205,6 +2272,12 @@
          doLayout();
       }
       
+      /**
+       * Select or deselect this item.
+       * 
+       * @param isSelected
+       *        new selection status
+       */
       public void setSelected(boolean isSelected)
       {
          config.selected = isSelected;
@@ -2271,6 +2344,7 @@
                 * @param    e
                 *           The mouse event.
                 */
+               @Override
                public void mouseClicked(MouseEvent e)
                {
                   ButtonListGuiImpl.this.mouseClicked(e);
@@ -2500,9 +2574,26 @@
        * 
        * @return   The item under the mouse pointer.
        */
+      @Override
       public Widget<GuiOutputManager> findMouseSource(NativePoint p)
       {
          return this;
       }
+
+      /**
+       * Find the source for mouse event.
+       * 
+       * @param eventId
+       *        the event ID to match 
+       * @param p
+       *        the mouse position relative to this widget
+       *        
+       * @return the mouse event source or {@code null} if no source found.
+       */
+      @Override
+      public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+      {
+         return this;
+      }
    }
 }

=== modified file 'src/com/goldencode/p2j/ui/client/gui/CaptionButton.java'
--- src/com/goldencode/p2j/ui/client/gui/CaptionButton.java	2019-09-08 09:25:29 +0000
+++ src/com/goldencode/p2j/ui/client/gui/CaptionButton.java	2022-03-06 21:49:25 +0000
@@ -2,7 +2,7 @@
 ** Module   : CaptionButton.java
 ** Abstract : Define caption button associated with a window.
 **
-** Copyright (c) 2014-2017, Golden Code Development Corporation.
+** Copyright (c) 2014-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20140805 Created initial version.
@@ -33,6 +33,7 @@
 ** 019 OM  20170407 Fixed list of handled mouseActions.
 ** 020 HC  20170823 Removed unused code of the window-state runtime logic.
 ** 021 SBI 20190908 Changed to send the window close command directly to the OS queue.
+** 022 VVT 20220306 mouseActions: MouseEvent.MOUSE_MOVED was added to the list of actions. See #6013.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -308,6 +309,7 @@
          MouseEvent.MOUSE_ENTERED,
          MouseEvent.MOUSE_EXITED,
          MouseEvent.MOUSE_DRAGGED,
+         MouseEvent.MOUSE_MOVED
       };
 
       return res;

=== modified file 'src/com/goldencode/p2j/ui/client/gui/EditorGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/EditorGuiImpl.java	2021-12-06 15:35:23 +0000
+++ src/com/goldencode/p2j/ui/client/gui/EditorGuiImpl.java	2022-03-06 21:49:25 +0000
@@ -2,7 +2,7 @@
 ** Module   : EditorGuiImpl.java
 ** Abstract : EDITOR widget implementation. GUI-specific version.
 **
-** Copyright (c) 2015-2021, Golden Code Development Corporation.
+** Copyright (c) 2015-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ----------------------------------------Description---------------------------------------
 ** 001 CA  20150509 Created initial version.
@@ -100,6 +100,7 @@
 **     CA  20211206 isPrintableKey() must know if the key is real or not, as its code can be used only when 
 **                  the key is originating from a real key press; otherwise, its label must be checked, as the 
 **                  key code may be for a legacy event name, which can collide with a real UTF-8 key code.
+**     VVT 20220306 findMouseSource(eventId, NativePoint) was overriden. See #6013.
 */
 
 /*
@@ -1811,6 +1812,24 @@
    }
 
    /**
+    * Find the source for mouse event.
+    * 
+    * @param eventId
+    *        the event ID to match 
+    * @param p
+    *        the mouse position relative to this widget
+    *        
+    * @return the mouse event source or {@code null} if no source found.
+    */
+   @Override
+   public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+   {
+      Widget<GuiOutputManager> src = super.findMouseSource(eventId, p);
+      
+      return (src == editor || src == contentPane) ? this : src;
+   }
+
+   /**
     * Get the widget's actual bounds
     * 
     * @return widget's actual bounds

=== modified file 'src/com/goldencode/p2j/ui/client/gui/FrameGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/FrameGuiImpl.java	2022-02-18 17:35:06 +0000
+++ src/com/goldencode/p2j/ui/client/gui/FrameGuiImpl.java	2022-03-06 21:49:25 +0000
@@ -2,7 +2,7 @@
 ** Module   : FrameGuiImpl.java
 ** Abstract : Frame implementation for GUI
 **
-** Copyright (c) 2014-2021, Golden Code Development Corporation.
+** Copyright (c) 2014-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20141010 Created initial version.
@@ -157,6 +157,7 @@
 **     EVL 20220112 Fix for missed mouse drag event in web client.
 **     AL2 20220127 Register all realized frames as empty selectors: can deselect all widgets from a window.
 **     VVT 20220218 Javadocs and other cosmetic fixes.
+**     VVT 20220306 findMouseSource(eventId, NativePoint) was overriden. See #6013.
 */
 
 /*
@@ -1107,6 +1108,37 @@
       return foundWidget;
    }
 
+   @Override
+   public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+   {
+      // the point incoming is related to frame origin, we need to substract the frame title
+      NativePoint pCheck = hasTitle() ? p.minus(frameScroll.physicalLocation()) : p;
+      
+      Widget<GuiOutputManager> foundWidget = frameScroll.findMouseSource(eventId, pCheck);
+      
+      if (foundWidget == null)
+      {
+         foundWidget = (this.physicalBounds().contains(p) ? this : null);
+      }
+      
+      // if the widget is a label, the "source" will be the frame scroll in order to prevent
+      // exit and enter mouse events for the frame if the mouse pointer is over the label.
+      if (foundWidget instanceof LabelGuiImpl)
+      {
+         return frameScroll;
+      }
+      
+      // if the widget is not enabled, then the source is the frame, too
+      if (foundWidget != null                              &&
+          foundWidget.config() != null                     &&
+          !WidgetId.virtualWidget(foundWidget.config().id) &&
+          !foundWidget.isEnabled()) // TODO: tooltips work with disabled widgets
+      {
+         return this;
+      }
+      
+      return foundWidget;
+   }
    /**
     * Get a point to represent the LAST-EVENT:X and LAST-EVENT:Y coordinates.  For frames, these
     * represents a point relative to the frame's top-left corner (excluding title).

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ModalWindow.java'
--- src/com/goldencode/p2j/ui/client/gui/ModalWindow.java	2022-02-15 12:19:45 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ModalWindow.java	2022-03-09 17:35:49 +0000
@@ -74,6 +74,7 @@
 **                  method added. See #6013.
 **     VVT 20220215 Change 13474 for #6013 temporary reverted (caused multiple regressions,
 **                  need to be completed).
+**     VVT 20220306 findMouseSource(eventId, NativePoint) was overriden. See #6013.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -165,9 +166,6 @@
    /** Cached GUI driver reference. */  
    protected GuiDriver<?,?> gd;
 
-   /** The last mouse pressed determined for this window. */
-   private Widget<GuiOutputManager> mousePressedSource = null;
-   
    /** Destroy flag. */
    protected boolean destroyInProgress;
    
@@ -640,7 +638,7 @@
    }
    
    /**
-    * Get the title bar widget associated witn this window.
+    * Get the title bar widget associated with this window.
     * 
     * @return   See above.
     */
@@ -747,50 +745,35 @@
    }
    
    /**
-    * Find the widget to which the given mouse event can be applied.
-    * 
-    * @param    evt
-    *           The mouse event.
-    * 
-    * @return   A widget over which the mouse is positioned or {@code null} if none found.
-    */
-   @Override
-   public Widget<GuiOutputManager> findMouseSource(MouseEvt evt)
-   {
-      Widget<GuiOutputManager> widget = null;
-      
-      int evtID = evt.getEvent().getID();
-      
-      if (evtID == MouseEvent.MOUSE_DRAGGED  || 
-          evtID == MouseEvent.MOUSE_RELEASED ||
-          evtID == MouseEvent.MOUSE_CLICKED)
-      {
-         return mousePressedSource;
-      }
-      
-      try
-      {
-         java.awt.Point p = evt.getEvent().getPoint();
-         
-         // search the other widgets
-         widget = windowPane.findMouseSource(new NativePoint(p.x, p.y));
-         
-         widget = (widget == null ? this : widget);
-      }
-      finally
-      {
-         if (widget != null && evtID == MouseEvent.MOUSE_PRESSED)
-         {
-            mousePressedSource = widget;
-         }
-         
-         if (evtID == MouseEvent.MOUSE_CLICKED)
-         {
-            mousePressedSource = null;
-         }
-      }
-      
-      return widget;
+    * Find the source for mouse event.
+    * 
+    * @param eventId
+    *        the event ID to match 
+    * @param p
+    *        the mouse position relative to this widget
+    *        
+    * @return the mouse event source or {@code null} if no source found.
+    */
+   @Override
+   public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+   {
+      return windowPane.findMouseSource(eventId, p);
+   }
+
+   /**
+    * Find the widget positioned just bellow the specified mouse position (in physical units).
+    * Each container should search for own widgets. Because widgets are relative positioned 
+    * inside containers the container physical position is adjusted before search.  
+    * 
+    * @param    p
+    *           The mouse physical location.
+    * 
+    * @return   A widget over which the mouse is positioned or <code>null</code> if none found.
+    */
+   @Override
+   public Widget<GuiOutputManager> findMouseSource(NativePoint p)
+   {
+      return windowPane.findMouseSource(p);
    }
 
    /**

=== modified file 'src/com/goldencode/p2j/ui/client/gui/OverlayWindow.java'
--- src/com/goldencode/p2j/ui/client/gui/OverlayWindow.java	2022-02-15 12:19:45 +0000
+++ src/com/goldencode/p2j/ui/client/gui/OverlayWindow.java	2022-03-09 22:24:45 +0000
@@ -542,48 +542,6 @@
    }
    
    /**
-    * Find the widget to which the given mouse event can be applied.
-    * 
-    * @param    evt
-    *           The mouse event.
-    * 
-    * @return   A widget over which the mouse is positioned or {@code null} if none found.
-    */
-   @Override
-   public Widget<GuiOutputManager> findMouseSource(MouseEvt evt)
-   {
-      Widget<GuiOutputManager> widget = null;
-      
-      int evtID = evt.getEvent().getID();
-      
-      if (evtID == MouseEvent.MOUSE_DRAGGED  || 
-          evtID == MouseEvent.MOUSE_RELEASED ||
-          evtID == MouseEvent.MOUSE_CLICKED)
-      {
-         return mousePressedSource;
-      }
-      
-      try
-      {
-         java.awt.Point p = evt.getEvent().getPoint();
-         
-         // search the other widgets
-         widget = findMouseSource(new NativePoint(p.x, p.y));
-         
-         widget = (widget == null ? this : widget);
-      }
-      finally
-      {
-         if (widget != null && evtID == MouseEvent.MOUSE_PRESSED)
-         {
-            mousePressedSource = widget;
-         }
-      }
-      
-      return widget;
-   }
-
-   /**
     * Get a point to represent the LAST-EVENT:X and LAST-EVENT:Y coordinates.  For overlay windows,
     * these returns always <code>null</code>.
     * 

=== modified file 'src/com/goldencode/p2j/ui/client/gui/TreeListCaptionItem.java'
--- src/com/goldencode/p2j/ui/client/gui/TreeListCaptionItem.java	2021-07-13 20:19:47 +0000
+++ src/com/goldencode/p2j/ui/client/gui/TreeListCaptionItem.java	2022-03-10 16:03:19 +0000
@@ -299,6 +299,9 @@
                return;
             }
 
+            // Start drag mode            
+            MouseHandler.startDragMode();
+            
             final TreeListCaptionItem ci = new TreeListCaptionItem(tree,
                      new TreeListGuiImpl.ColumnCache(column));
 
@@ -409,6 +412,7 @@
          {
             gd.setCursor(MousePtrWrapper.E_RESIZE);
             atResizeEdge = true;
+            MouseHandler.startDragMode();
          }
          else
          {

=== modified file 'src/com/goldencode/p2j/ui/client/gui/WindowGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/WindowGuiImpl.java	2022-02-15 12:19:45 +0000
+++ src/com/goldencode/p2j/ui/client/gui/WindowGuiImpl.java	2022-03-06 21:49:25 +0000
@@ -202,6 +202,9 @@
 **     VVT 20220202 findMouseSource(MouseEvt) removed. See #6013.
 **     VVT 20220215 Change 13474 for #6013 temporary reverted (caused multiple regressions,
 **                  need to be completed).
+**     VVT 20220306 1) findMouseSource(MouseEvent) moved to TopLevelWindow. 2) init(): now that true
+**                  mouse source is WindowTitle calculated instead of WindowTitleBar, WindowTitle
+**                  is registered with gd.registerMoveableWidget instead of WindowTitleBar.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -309,7 +312,7 @@
    /** The physical window height, after resize. */
    private int resizedHeight;
 
-   /** The last mouse pressed determined for this window. */
+   /** The last mouse pressed widget in this window. This field is set when mouse is pressed. */
    private Widget<GuiOutputManager> mousePressedSource = null;
    
    /** The location before the window is maximized or minimized. */
@@ -873,53 +876,6 @@
    }
    
    /**
-    * Find the widget to which the given mouse event can be applied.
-    * 
-    * @param    evt
-    *           The mouse event.
-    * 
-    * @return   A widget over which the mouse is positioned or {@code null} if none found.
-    */
-   @Override
-   public Widget<GuiOutputManager> findMouseSource(MouseEvt evt)
-   {
-      Widget<GuiOutputManager> widget = null;
-      
-      int evtID = evt.getEvent().getID();
-      
-      if (evtID == MouseEvent.MOUSE_DRAGGED  || 
-          evtID == MouseEvent.MOUSE_RELEASED || 
-          evtID == MouseEvent.MOUSE_CLICKED)
-      {
-         return mousePressedSource;
-      }
-      
-      try
-      {
-         java.awt.Point p = evt.getEvent().getPoint();
-         
-         // search the other widgets
-         widget = findMouseSource(new NativePoint(p.x, p.y));
-         
-         widget = (widget == null ? this : widget);
-      }
-      finally
-      {
-         if (widget != null && evtID == MouseEvent.MOUSE_PRESSED)
-         {
-            mousePressedSource = widget;
-         }
-         
-         if (evtID == MouseEvent.MOUSE_CLICKED)
-         {
-            mousePressedSource = null;
-         }
-      }
-      
-      return widget;
-   }
-   
-   /**
     * Iconify the window.
     * 
     * @param   postEvent
@@ -2089,7 +2045,7 @@
       // can be missing if there are no decorations
       if (hasTitlebar())
       {
-         gd.registerMoveableWidget(titleBar.getId().asInt());
+         gd.registerMoveableWidget(titleBar.title.getId().asInt());
       }
       gd.registerResizeableWidget(contentPane.getId().asInt());
       

=== modified file 'src/com/goldencode/p2j/ui/client/gui/WindowTitleBar.java'
--- src/com/goldencode/p2j/ui/client/gui/WindowTitleBar.java	2021-08-10 18:30:38 +0000
+++ src/com/goldencode/p2j/ui/client/gui/WindowTitleBar.java	2022-03-06 21:49:25 +0000
@@ -2,7 +2,7 @@
 ** Module   : WindowTitleBar.java
 ** Abstract : GUI window title bar implementation
 **
-** Copyright (c) 2014-2021, Golden Code Development Corporation.
+** Copyright (c) 2014-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20140805 Created initial version.
@@ -78,6 +78,17 @@
 **     CA  20201117 Fixed a leak for the caption buttons, which need to be deleted explicitly. 
 **     VVT 20210810 AbstractGuiDriver.createIcon(..) method made static: the method does not depend on
 **                  the kind of the driver.
+**     VVT 20220306 1) unused import statemet removed
+**                  2) duplicate interfaces removed from the class definition
+**                  3) The allButtons field made constant (final).
+**                  4) The class fixed in static method call: TopLevelWindow.getWindowDefaultIcon() (was Window).
+**                  5) Missing @Override annotations were added
+**                  6) toString 8 lines of code refactored to 1.
+**                  7) loadIcon method: access to a closable resource surrounded by try/catch block.
+**                  8) Internal WindowTitle class access restriction weakened to allow access from outside
+**                  of this class
+**                  9) Overridden findMouseSource(int eventId, NativePoint p) introduced in the previous
+**                  release was removed: special treatment is no more needed for this container widget children.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -143,7 +154,6 @@
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.*;
-import com.goldencode.p2j.ui.client.ClientDrivenLabel;
 import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
 import com.goldencode.p2j.ui.client.gui.theme.*;
@@ -160,9 +170,7 @@
  */
 public class WindowTitleBar
 extends AbstractContainer<GuiOutputManager>
-implements WidgetWithConfig<BaseConfig>,
-           MouseListener,
-           MouseMotionListener
+implements WidgetWithConfig<BaseConfig>
 {
    /** The window's icon. */
    WindowIcon icon;
@@ -174,7 +182,7 @@
    private GuiDriver gd;
 
    /** The full set of window caption buttons */
-   private CaptionButton[] allButtons;
+   private final CaptionButton[] allButtons;
 
    /** The displayed caption buttons. */
    private CaptionButton[] buttons;
@@ -239,7 +247,7 @@
       icon = new WindowIcon(WidgetId.nextID());
       icon.setHidden(!showWindowIcon);
 
-      byte[] appIcon = Window.getWindowDefaultIcon();
+      byte[] appIcon = TopLevelWindow.getWindowDefaultIcon();
 
       // found the application icon defined on the server side
       if (appIcon != null)
@@ -313,6 +321,7 @@
    /**
     * Layout the elements of this title.   Everything is done in pixel units.
     */
+   @Override
    public void doLayout()
    {
       double height = ThemeManager.getCurrentTheme().layoutTitleBar(
@@ -650,14 +659,7 @@
       String parent = super.toString();
       // topLevelWindow() will throw exception instead of returning null 
       TopLevelWindow tlw = parentOrSelf(TopLevelWindow.class);
-      if (tlw != null)
-      {
-         return parent + " [window=" + tlw + "]";
-      }
-      else
-      {
-         return parent;
-      }
+      return (tlw != null) ? parent + " [window=" + tlw + "]" : parent;
    }
 
    /**
@@ -678,7 +680,6 @@
     */
    class WindowIcon
    extends ImageGuiImpl
-   implements MouseListener
    {
       /** Flag indicating the icon was loaded by LOAD-SMALL-ICON(). */
       private boolean useSmallIconSize = false;
@@ -796,36 +797,27 @@
        * @return   <code>TRUE</code> if operation successful <code>FALSE</code> otherwise.
        */
       public boolean loadIcon(boolean small, String name)
-      {
-         InputStream is = null;
-         byte[] icon = null;
-         
+      {         
          // find the icon on local file system
-         try
-         {
-            is = new FileInputStream(name);
-         }
-         catch (FileNotFoundException ex)
-         {
-            is = null;
-         }
-         
-         // the file is found, try to read
-         if (is != null)
-         {
-            icon = UiUtils.getData(is);
-         }
-         
-         // if having new icon data - set it
-         if (icon != null)
-         {
-            useSmallIconSize = small;
-            return setIconData(icon);
-         }
-         else
-         {
-            return false;
-         }
+         try(InputStream is = new FileInputStream(name))
+         {
+            final byte[] icon = UiUtils.getData(is);
+
+            // if having new icon data - set it
+            if (icon != null)
+            {
+               useSmallIconSize = small;
+               return setIconData(icon);
+            }
+            
+         }
+         catch (IOException e)
+         {
+            LOG.warning("Cannot load icon: " + name + ": " + e.getMessage());
+         }
+         
+         return false;
+         
       }
 
       /**
@@ -888,7 +880,7 @@
    /**
     * The window's title.
     */
-   private class WindowTitle
+   class WindowTitle
    extends ClientDrivenLabel<GuiOutputManager>
    {
       /** The cached window title */
@@ -1003,6 +995,23 @@
       }
 
       /**
+       * Find the source for mouse event.
+       * 
+       * @param eventId
+       *        the event ID to match 
+       * @param p
+       *        the mouse position relative to this widget
+       *        
+       * @return the mouse event source or {@code null} if no source found.
+       */
+      @Override
+      public Widget<GuiOutputManager> findMouseSource(int eventId, NativePoint p)
+      {
+         Widget<GuiOutputManager> w = super.findMouseSource(eventId, p);
+         return (w != null ? w : this);
+      }
+
+      /**
        * Retrieve the widget's width in native units.
        * 
        * @return   See above.
@@ -1023,5 +1032,7 @@
       {
          return screen().coordinates().heightToNative(height());
       }
+
    }
+
 }

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/MouseDirectManipulation.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/MouseDirectManipulation.java	2022-02-15 17:19:54 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/MouseDirectManipulation.java	2022-03-06 21:49:25 +0000
@@ -26,6 +26,21 @@
 **                  has custom pointer defined.
 **     VVT 20220215 Wrong Javascript comments fixed and some other minor changes. 3 unused private
 **                  methods commented as such.
+**     VVT 20220306 1) processDrag(MouseEvent, boolean): unused second argument removed.
+**                  2) Unused selectSingleWidget(false) removed
+**                  3) selectSingleWidget(boolean): method argument inlined and removed (always true),
+**                  assertion added to check this widget is selected at the moment of the call.
+**                  4) Unused private method removed: deselectEverythingExcept(Widget)
+**                  5) Methods deselectFrameWidgets(Frame) deselectFrameWidgetsExcept(Frame, Widget)
+**                  refactored
+**                  into one deselectFrameWidgets(Frame) (the second "except" parameter is not used anymore).
+**                  6) Unused private method deselectEverythingExcept(Widget) removed.
+**                  7) Private method deselectEverything() inlined (only single use).
+**                  8) Unused private methods deselectNestedFrames(Frame), deselectNestedFramesExcept(Frame)
+**                  removed.
+**                  9) mousePressed refactored: if (!widgetResizing) replaced by more meaningful if
+**                  (rowResizing); if/else replaced by early return; two conditional block with
+**                  !isExtended(e) as part of condition replaced by one block.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -196,6 +211,9 @@
    /** The widget's custom mouse pointer (if any). */
    private MousePtrWrapper mousePtr = null;
    
+   /** The widget mouse was pressed upon. */
+   private Widget mousePressedSource;
+   
    /**
     * Constructor.
     * 
@@ -264,7 +282,7 @@
 
       if (isDirectManipulationValid())
       {
-         processDrag(e, !dragInProgress);
+         processDrag(e);
       }
    }
 
@@ -364,7 +382,7 @@
             // post the event to target widget if not already selected
             if (!((BaseConfig)widgetEff.config()).selected)
             {
-               selectSingleWidget(true);
+               selectSingleWidget();
             }
          }
       }
@@ -384,58 +402,61 @@
          return;
       }
       
+      mousePressedSource = widgetEff;
+      
       mousePtr = widget.getCustomMousePointer().orElse(null);
 
       // reset NO-APPLY cache
       eventNoApply = -1;
       startPoint = ((AbstractWidget)widget).translate(e);
-      // prepare mouse coord to resize operation check 
+      // prepare mouse coordinates to resize operation check 
       NativePoint resizePoint = ((Frame)parentContainer).getContentPane().translate(e);
       widgetResizing = isInsideFrameResizeHandle(resizePoint) ||
                        isInsideResizeHandle(resizePoint);
       // widget resizing has higher priority comparing to widgets moving
-      if (!widgetResizing)
-      {
-         rowResizing = isInsideRowResizable() && isInsideRowResizeArea(e);
-         
-         if (rowResizing)
-         {
-            // start browse widget row resize mode
-            EventManager.postEvent(new DirectManipulationEvent(
-                                                     ((BrowseColumnGuiImpl)widgetEff).getBrowse(),
-                                                                   Keyboard.SE_START_ROW_RESIZE));
-         }
-         else
-         {
-            // defer start moving to mouseDrag() first event
-            if (!((BaseConfig)widgetEff.config()).selected && !isExtended(e))
-            {
-               if (isFrame)
-               {
-                  // empty selection event before every child widget is deselected
-                  // ant the target frame is selected
-                  EventManager.postEvent(new DirectManipulationEvent(widgetEff,
-                                                                     Keyboard.SE_EMPTY_SELECTION));
-               }
-               selectSingleWidget(true);
-            }
-            else if (isFrame && !isExtended(e))
-            {               
-               // the target widget is an already selected frame: do not deselect child widgets
-               
-               if (((PaneConfig)widgetEff.config()).boxSelectable)
-               {
-                  // we will enter box selecting mode if drag next
-                  boxSelecting = true;
-               }
-            }
-         }
-      }
-      else
+      if (widgetResizing)
       {
          // start widget resizing mode
          EventManager.postEvent(new DirectManipulationEvent(this, widgetToResize,
                                                             Keyboard.SE_START_RESIZE));
+         return;
+      }
+      
+      rowResizing = isInsideRowResizable() && isInsideRowResizeArea(e);
+
+      if (rowResizing)
+      {
+         // start browse widget row resize mode
+         EventManager.postEvent(new DirectManipulationEvent(
+                  ((BrowseColumnGuiImpl) widgetEff).getBrowse(), Keyboard.SE_START_ROW_RESIZE));
+         return;
+      }
+      
+      if (!isExtended(e))
+      {
+         // defer start moving to mouseDrag() first event
+         WidgetConfig config = widgetEff.config();
+         if (!((BaseConfig) config).selected)
+         {
+            if (isFrame)
+            {
+               // empty selection event before every child widget is deselected
+               // and the target frame is selected
+               EventManager.postEvent(
+                        new DirectManipulationEvent(widgetEff, Keyboard.SE_EMPTY_SELECTION));
+            }
+
+            selectSingleWidget();
+            
+            return;
+         }
+
+         // the target widget is an already selected frame: do not deselect child widgets
+         if (isFrame && (((PaneConfig) config).boxSelectable))
+         {
+            // we will enter box selecting mode if drag next
+            boxSelecting = true;
+         }
       }
    }
 
@@ -638,9 +659,9 @@
                                                                           Keyboard.SE_END_MOVE);
 
             // set up move shift taking into account possible grid snap flag
-            NativePoint newP = null;
             NativePoint currLoc = widMove[i].physicalLocation();
 
+            NativePoint newP;
             if (isGridSnapOn())
             {
                newP = new NativePoint(alignToGrid(currLoc.x + xMove, false),
@@ -667,9 +688,13 @@
          // clean up previous drawings
          drawMovableWidgetsBounds(xOld, yOld, true);
       }
+      
+      // FIXME vvt check if the following still make sense:
+      
       // for some reasons web client driver unlike swing one generates click even for
-      // pres-drag-release sequence so we disable click handling here for drag operations
+      // press-drag-release sequence so we disable click handling here for drag operations
       clickExpected = !dragInProgress;
+      
       // clean up all mode flags
       startPoint = null;
       boxSelecting = false;
@@ -677,6 +702,8 @@
       widgetResizing = false;
       rowResizing = false;
       widgetToResize = null;
+      mousePressedSource = null;
+
       if (dragInProgress)
       {
          // finish any selection box paintings
@@ -706,10 +733,8 @@
     * 
     * @param    evt
     *           The mouse event details.
-    * @param    firstCall
-    *           Flag indicating first call in a current drag session.
     */
-   public void processDrag(MouseEvent evt, boolean firstCall)
+   protected void processDrag(MouseEvent evt)
    {
       // possible issue here, drag was started somewhere else or other error
       if (startPoint == null)
@@ -854,9 +879,12 @@
          yOld = y;
          wOld = w;
          hOld = h;
+         
+         return;
       }
+      
       // then row resizing
-      else if (rowResizing && eventNoApply != Keyboard.SE_START_ROW_RESIZE)
+      if (rowResizing && eventNoApply != Keyboard.SE_START_ROW_RESIZE)
       {
          // get the main browse widget
          BrowseGuiImpl br = ((BrowseColumnGuiImpl) widgetEff).getBrowse();
@@ -897,29 +925,36 @@
          // save parameters for resizing complete
          yOld = y;
          hOld = h;
+         
+         return;
       }
+      
       // then widgets moving
-      else if (widgetsMoving && eventNoApply != Keyboard.SE_START_MOVE)
+      if (widgetsMoving && eventNoApply != Keyboard.SE_START_MOVE)
       {
+         // clean up previous drawings
+         if (dragInProgress)
+         {
+            drawMovableWidgetsBounds(xOld, yOld, true);
+         }
+         else
+         {
+            dragInProgress = true;
+         }
+
          // new move distance
          int xShift = n.x - startPoint.x;
          int yShift = n.y - startPoint.y;
-         // clean up previous drawings
-         if (dragInProgress)
-         {
-            drawMovableWidgetsBounds(xOld, yOld, true);
-         }
-         else
-         {
-            dragInProgress = true;
-         }
          // draw new bounds
          drawMovableWidgetsBounds(xShift, yShift, false);
          xOld = xShift;
          yOld = yShift;
+         
+         return;
       }
+      
       // drag in a frame enters box selection mode
-      else if (boxSelecting && eventNoApply != Keyboard.SE_START_BOX_SELECTION)
+      if (boxSelecting && eventNoApply != Keyboard.SE_START_BOX_SELECTION)
       {
          // calculate rectangle parameters
          int x = n.x < startPoint.x ? n.x : startPoint.x;
@@ -1027,7 +1062,7 @@
       // get rectangles for movable widgets
       NativeRectangle [] nrMovable = getMovableWidgetsBounds();
       // draw rectangles for movable widgets
-      if (nrMovable != null && nrMovable.length > 0)
+      if (nrMovable != null)
       {
          for (NativeRectangle nr : nrMovable)
          {
@@ -1077,7 +1112,7 @@
          return point;
       }
 
-      Container parent = null;
+      Container parent;
 
       if (isFrame)
       {
@@ -1089,6 +1124,7 @@
       }
 
       NativeDimension parentDim = parent.physicalDimension();
+      
       int newx = Math.min(parentDim.width - widgetDim.width, point.x);
       int newy = Math.min(parentDim.height - widgetDim.height, point.y);
       newx = Math.max(0, newx);
@@ -1193,10 +1229,6 @@
     */
    private boolean isInsideFrameResizeHandle(NativePoint np)
    {
-      // need a little fix to take into account frame title height
-      NativePoint npCheck = new NativePoint(np);
-      npCheck.y += ((FrameGuiImpl)parentContainer).getTitleHeight();
-      
       // check the frame itself first
       BaseConfig fc = parentContainer.config();
       if (fc.selected && fc.resizable)
@@ -1204,6 +1236,11 @@
          // generate resize handles coordinates for current widget
          NativeRectangle[] frResizeHandles = getFrameResizeHandles(parentContainer);
          // check if we are inside one of them
+
+         // need a little fix to take into account frame title height
+         NativePoint npCheck = new NativePoint(np);
+         npCheck.y += ((FrameGuiImpl)parentContainer).getTitleHeight();
+         
          for(int i = 0; i < frResizeHandles.length; i++)
          {
             if (frResizeHandles[i].contains(npCheck))
@@ -1252,11 +1289,9 @@
                                                                                       rowNum;
          return true;
       }
-      else
-      {
-         resizeDirection = RESIZE_DIRECTION_UNDEF;
-         return false;
-      }
+      
+      resizeDirection = RESIZE_DIRECTION_UNDEF;
+      return false;
    }
 
    /**
@@ -1274,145 +1309,66 @@
    /**
     * Selects single widget the current handler is attached to.  All other selected widget inside
     * current container will be de-selected, including container itself.
-    *        
-    * FIXME: this private method is never used and should probably be removed.
+    * 
+    * The widget is supposed to be unselected at the moment of the call.
     */
    private void selectSingleWidget()
    {
-      selectSingleWidget(false);
-   }
-   
-   /**
-    * Selects single widget the current handler is attached to.  All other selected widget inside
-    * current container will be de-selected, including container itself.
-    * 
-    * @param    repaintParent
-    *           If <code>TRUE</code> the parent repaint should be performed after message
-    *           processing.
-    */
-   private void selectSingleWidget(boolean repaintParent)
-   {
+      assert !((BaseConfig) widgetEff.config()).selected;
+
       // deselect everything selected
-      deselectEverything();
-      // select single widget
-      EventManager.postEvent(new DirectManipulationEvent(widgetEff, parentContainer,
-                                                         repaintParent, Keyboard.SE_SELECTION));
-   }
-   
-   /**
-    * Deselect all selected widget inside the current container, including container itself.
-    * If nested frames deselect parent frame widgets as well. 
-    * 
-    * @param   except
-    *          A widget to be skipped by the deselection. This can be {@code null}.
-    */
-   private void deselectEverythingExcept(Widget<?> except)
-   {
       if (parentContainer != null)
       {
-         // first deselect the parent frame if nested;
-         deselectNestedFramesExcept((Frame<?>) parentContainer, except);
-         // then the frame itself
-         BaseConfig fc = parentContainer.config();
-         if (fc.selectable && fc.selected && parentContainer != except)
-         {
-            EventManager.postEvent(new DirectManipulationEvent(parentContainer, parentContainer,
-                                                               Keyboard.SE_DESELECTION));
-         }
-         MouseDirectManipulation.deselectFrameWidgetsExcept((Frame<?>) parentContainer, except);
+         List<Frame<?>> parentFrames = UiUtils.getParentFrames(parentContainer);
+         if (parentFrames.size() > 1)
+         {
+            // deselect the parent frame if nested, it will deselect parentContainer as well.
+            deselectFrameWidgets(parentFrames.get(1));
+         }
+         else
+         {
+            if (parentContainer != widgetEff)
+            {
+               // then the frame itself
+               BaseConfig fc = parentContainer.config();
+               if (fc.selectable && fc.selected)
+               {
+                  EventManager.postEvent(new DirectManipulationEvent(parentContainer,
+                           parentContainer, Keyboard.SE_DESELECTION));
+               }
+            }
+         }
+
+         deselectFrameWidgets((Frame<?>) parentContainer);
       }
-   }
-   
-   /**
-    * Deselect all selected widget inside the current container, including container itself.
-    * If nested frames deselect parent frame widgets as well. 
-    */
-   private void deselectEverything()
-   {
-      deselectEverythingExcept(null);
-   }
-   
-   /**
-    * Deselect all <p>selectable</p> widgets already <p>selected</p> within a frame.
-    * 
-    * @param frame
-    *        Frame widget.
-    * @param except
-    *        A widget to be skipped by the deselection. This can be {@code null}
-    */
-   private static void deselectFrameWidgetsExcept(Frame<?> frame, Widget<?> except)
-   {
-      Widget<?>[] widLst = frame.getContentPane().widgets();
-
-      for (Widget<?> wid : widLst)
-      {
-         // skip widgets must be excluded
-         if (!wid.isDirectManipulable() || wid == except)
-         { 
-            continue;
-         }
-         
-         BaseConfig bc = wid.config();
-         if (bc.selectable && bc.selected)
-         {
-            EventManager.postEvent(new DirectManipulationEvent(wid, frame,
-                                                               Keyboard.SE_DESELECTION));
-         }
-      }      
-   }
-
-   /**
-    * Deselect all <p>selectable</p> widgets already <p>selected</p> within a frame.
-    * 
-    * @param frame
-    *        Frame widget.
-    *        
-    * FIXME: this private method is never used and should probably be removed.
+
+      EventManager.postEvent(new DirectManipulationEvent(widgetEff, parentContainer, true,
+               Keyboard.SE_SELECTION));
+   }
+   
+   /**
+    * Deselect all <p>selectable</p> widgets already <p>selected</p> within a frame.
+    * 
+    * @param frame
+    *        Frame widget must not be {@code null}
     */
    private static void deselectFrameWidgets(Frame<?> frame)
    {
-      deselectFrameWidgetsExcept(frame, null);
-   }
-   
-   /**
-    * If widget is a nested frame (has a parent frame) deselect all widgets within parent frame.
-    * <p>
-    * Get a list of all parent frames of this frame.
-    * The first element in the list, at index 0, is the frame it self.
-    * If frame is nested it parent frame is located at index 1.
-    * 
-    * @param frame
-    *        Frame widget
-    * @param except
-    *        A widget to be skipped by the deselection. This can be {@code null}
-    */
-   private static void deselectNestedFramesExcept(Frame<?> frame, Widget<?> except)
-   {      
-      List<Frame<?>> parentFrames = UiUtils.getParentFrames(frame);
-      if (parentFrames.size() > 1)
+      for (Widget<?> wid : frame.getContentPane().widgets())
       {
-         MouseDirectManipulation.deselectFrameWidgetsExcept(parentFrames.get(1), except);
+         if (wid.isDirectManipulable())
+         {
+            BaseConfig bc = wid.config();
+            if (bc.selectable && bc.selected)
+            {
+               EventManager.postEvent(
+                        new DirectManipulationEvent(wid, frame, Keyboard.SE_DESELECTION));
+            }
+         }
       }
    }
 
    /**
-    * If widget is a nested frame (has a parent frame) deselect all widgets within parent frame.
-    * <p>
-    * Get a list of all parent frames of this frame.
-    * The first element in the list, at index 0, is the frame it self.
-    * If frame is nested it parent frame is located at index 1.
-    * 
-    * @param frame
-    *        Frame widget
-    *        
-    * FIXME: this private method is never used and should probably be removed.
-    */
-   private static void deselectNestedFrames(Frame<?> frame)
-   {     
-      deselectNestedFramesExcept(frame, null);
-   }
-   
-   /**
     * Collects the widget resize handles bounds for selected resizable widget.
     * 
     * @param    widResize
@@ -1544,45 +1500,46 @@
     * 
     * @return   Array of the {@link Widget} elements to be moved.
     */
-   private Widget<?>[] getWidgetsToMove()
+   private final Widget<?>[] getWidgetsToMove()
    {
+      if (parentContainer == null)
+      {
+         return new Widget<?>[0];
+      }
+
       ArrayList<Widget<?>> res = new ArrayList<Widget<?>>();
-      if (parentContainer != null)
-      {
-         // check first the frame itself if we are looking from window widget perspective
-         if (isWindow || isFrame)
-         {
-            BaseConfig fc = parentContainer.config();
-            if (fc.selected && fc.movable)
-            {
-               // add frame widget to move
-               res.add(parentContainer);
-            }
-         }
-
-         // when moving frame or window do not include the child widgets
-         if (!isWindow && !isFrame)
-         {
-            // enumerate all other widgets
-            Widget<?>[] widLst = ((Frame) parentContainer).getContentPane().widgets();
-            for (Widget<?> wid : widLst)
-            {
-               // skip widgets must be excluded
-               if (!wid.isDirectManipulable())
-               {
-                  continue;
-               }
-
-               BaseConfig bc = wid.config();
-               if (bc.selected && bc.movable)
-               {
-                  // add another widget to move
-                  res.add(wid);
-               }
-            }
-         }
-      }
-      
+      // check first the frame itself if we are looking from window widget perspective
+      if (isWindow || isFrame)
+      {
+         BaseConfig fc = parentContainer.config();
+         if (fc.selected && fc.movable)
+         {
+            // add frame widget to move
+            res.add(parentContainer);
+         }
+      }
+
+      // when moving frame or window do not include the child widgets
+      if (!isWindow && !isFrame)
+      {
+         // enumerate all other widgets
+         for (Widget<?> wid : ((Frame) parentContainer).getContentPane().widgets())
+         {
+            // skip widgets must be excluded
+            if (!wid.isDirectManipulable())
+            {
+               continue;
+            }
+
+            BaseConfig bc = wid.config();
+            if (bc.selected && bc.movable)
+            {
+               // add another widget to move
+               res.add(wid);
+            }
+         }
+      }
+
       return res.toArray(new Widget<?>[res.size()]);
    }
    

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/MouseHandler.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/MouseHandler.java	2022-02-18 17:35:06 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/MouseHandler.java	2022-03-09 23:18:39 +0000
@@ -33,6 +33,15 @@
 ** 021 AL2 20220126 Don't allow selectable widgets suppress menu button (editors, frames etc.).
 **     VVT 20220128 Missing @Override annotation added, import section cleaned.
 **     VVT 20220218 Javadocs fixes.
+**     VVT 20220222 Method findMouseSource(MouseEvent) pulled up from SwingMouseHandler to make it accessible
+**                  also from WebMouseHandler.
+**     VVT 20220306 1) Method findMouseSource(MouseEvent) pulled up from SwingMouseHandler to make it accessible
+**                  also from WebMouseHandler.  2) Methods processWidgetActions,
+**                  handleMouseEventIfMovable, handleMouseEventIfSelectable inlined 3) Method
+**                  applyMouseEvent(Object mouseAdapter, MouseEvent evt) now accepts all suitably types of
+**                  mouse handlers as the first argument.  4) Method canProcessMouse declared static 5)
+**                  Field mousePressedSourceID added to local context to track the last mouse pressed
+**                  widget.
 */
 
 /*
@@ -102,6 +111,8 @@
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.*;
+import com.goldencode.p2j.ui.client.event.*;
+import com.goldencode.p2j.ui.client.gui.*;
 import com.goldencode.p2j.ui.client.widget.*;
 
 /**
@@ -192,6 +203,15 @@
    }
    
    /**
+    * Set dragging mode ON.
+    * 
+    */
+   public static void startDragMode()
+   {
+      local.get().dragMode = true;
+   }
+   
+   /**
     * Deregister the specified widget for mouse events.
     * 
     * @param    wid
@@ -286,6 +306,45 @@
    }
    
    /**
+    * Apply the specified mouse event to the specified source.
+    * 
+    * @param    mouseAdapter
+    *           The mouse event adapter.
+    * @param    evt
+    *           The mouse event.
+    */
+   private static void applyMouseEvent(Object mouseAdapter, MouseEvent evt)
+   {
+      switch (evt.getID())
+      {
+         case MouseEvent.MOUSE_CLICKED:
+            ((MouseListener)mouseAdapter).mouseClicked(evt);
+            break;
+         case MouseEvent.MOUSE_PRESSED:
+            ((MouseListener)mouseAdapter).mousePressed(evt);
+            break;
+         case MouseEvent.MOUSE_RELEASED:
+            ((MouseListener)mouseAdapter).mouseReleased(evt);
+            break;
+         case MouseEvent.MOUSE_ENTERED:
+            ((MouseListener)mouseAdapter).mouseEntered(evt);
+            break;
+         case MouseEvent.MOUSE_EXITED:
+            ((MouseListener)mouseAdapter).mouseExited(evt);
+            break;
+         case MouseEvent.MOUSE_DRAGGED:
+            ((MouseMotionListener)mouseAdapter).mouseDragged(evt);
+            break;
+         case MouseEvent.MOUSE_MOVED:
+            ((MouseMotionListener)mouseAdapter).mouseMoved(evt);
+            break;
+         case MouseEvent.MOUSE_WHEEL:
+            ((MouseWheelListener)mouseAdapter).mouseWheelMoved((MouseWheelEvent) evt);
+            break;
+      }
+   }
+
+   /**
     * Handle the specified event, in the context of the specified widget.
     * 
     * @param    sourceID
@@ -293,28 +352,129 @@
     * @param    evt
     *           The mouse event.
     */
-   public static void handleMouseEvent(int sourceID, final MouseEvent evt)
+   public static void handleMouseEvent(int sourceID, MouseEvent evt)
    {
-      @SuppressWarnings("unchecked")
-      Widget<GuiOutputManager> source = 
-         (Widget<GuiOutputManager>) OutputManager.resolveWidget(sourceID);
+      Widget<GuiOutputManager> source = (Widget<GuiOutputManager>) OutputManager
+               .resolveWidget(sourceID);
+    
+      WorkArea wa = local.get();
+      Widget<GuiOutputManager> sourceForActions = source;
+      
+      int mousePressedSourceID = wa.mousePressedSourceID;
+
+      switch (evt.getID())
+      {
+         case MouseEvent.MOUSE_DRAGGED:
+            sourceID = mousePressedSourceID;
+            break;
+            
+         case MouseEvent.MOUSE_RELEASED:
+
+            if (mousePressedSourceID != sourceID)
+            {
+               Widget<GuiOutputManager> mousePressedSource = (Widget<GuiOutputManager>) OutputManager
+                        .resolveWidget(mousePressedSourceID);
+               
+               if (mousePressedSource != null)
+               {
+                  if (wa.dragMode)
+                  {
+                     source = mousePressedSource;
+                  }
+                  
+                  if (mousePressedSource.isDirectManipulable())
+                  {
+                     sourceForActions = mousePressedSource;
+                  }
+               }
+            }
+            
+            wa.mousePressedSourceID = 0;
+            wa.dragMode = false;
+            
+            break;
+
+         case MouseEvent.MOUSE_PRESSED:            
+            wa.mousePressedSourceID = sourceID;
+      }
       
       if (source == null)
       {
+         // FIXME: use logging instead of just printing
          System.err.println("Received mouse event for zombie source " + sourceID + ": " +
                             evt.toString());
          return;
       }
       
-      // handle mouse events for selectable widgets.
-      if (source.isDirectManipulable())
+      // Handle direct manipulation actions
+      if (sourceForActions.isDirectManipulable())
       {
-         boolean consume = false;
-         consume = handleMouseEventIfSelectable(source, evt) || consume;
-         consume = handleMouseEventIfMovable(source, evt) || consume;
-         
+         // 1. Handle direct manipulation for selectable widgets.
+         
+         WidgetConfig config = sourceForActions.config();
+         boolean consumeAndExit = false;
+         
+         // Call mouse clicked if left button is clicked.
+         if (config != null && config.selectable && evt.getButton() != sourceForActions.getMenuMouse())
+         {         
+            if (isMouseClickEvent(evt))
+            {
+               // select / deselect
+               if (evt.getID() == MouseEvent.MOUSE_CLICKED)
+               {
+                  performAction(config.id, evt, (action1) -> {
+                     action1.mouseClicked(evt);
+                  });
+               }
+               evt.consume();
+               consumeAndExit = true;
+            }
+         }
+         
+         // 2. Handle direct manipulation for movable widgets.
+         if (config instanceof BaseConfig)
+         {
+            BaseConfig baseConfig = (BaseConfig) config;
+
+            // for movable widgets handle only drag events
+            if (baseConfig != null && baseConfig.movable && evt.getButton() != sourceForActions.getMenuMouse())
+            {
+               Consumer<MouseDirectManipulation> consumer = null;
+               switch (evt.getID())
+               {
+                  case MouseEvent.MOUSE_PRESSED:
+                  {
+                     consumer = (action) -> {
+                        action.mousePressed(evt);
+                     };
+                     break;
+                  }
+                  case MouseEvent.MOUSE_DRAGGED:
+                  {
+                     consumer = (action) -> {
+                        action.mouseDragged(evt);
+                     };
+                     break;
+                  }
+                  case MouseEvent.MOUSE_RELEASED:
+                  {
+                     consumer = (action) -> {
+                        action.mouseReleased(evt);
+                     };
+                     break;
+                  }
+               }
+               
+               if (consumer != null)
+               {
+                  performAction(baseConfig.id, evt, consumer);
+                  consumeAndExit = true;
+               }
+            }
+         }
+
          // Suppress any default behavior on the widget
-         if (consume)
+         if (consumeAndExit)
          {
             evt.consume();
             return;
@@ -322,110 +482,63 @@
       }
       
       applyMouseEvent(source, evt);
+
+      // Process actions
       
       // at this point the source may be destroyed (for example window closed on button click),
       // do not process the event further in this case
       // TODO: this condition is fragile, find a better way to detect this condition
-      if (!(source instanceof TopLevelWindow) && source.ancestor() == null)
+      if (!(sourceForActions instanceof TopLevelWindow)
+               && sourceForActions.ancestor() == null)
       {
          return;
       }
 
-      processWidgetActions(sourceID, evt);
-   }
-   
-   /**
-    * Handle mouse events for movable widgets. 
-    * If the widgets support mouse direct manipulation, call mouse dragged if left button is dragged.
-    * 
-    * @param source  Widget
-    * @param evt     Mouse event
-    * 
-    * @return <code>true</code> iv event has been consumed <code>false</code> if not.
-    */
-   private static boolean handleMouseEventIfMovable(Widget<GuiOutputManager> source, final MouseEvent evt)
-   {
-      WidgetConfig cfg = source.config();
-      if (!(cfg instanceof BaseConfig))
-      {
-         return false;
-      }
-      BaseConfig config = (BaseConfig) cfg;
-      // return code that can differs from evt.isConsumed() result
-      boolean resConsumed = false;
+      List<MouseWidgetAction> actions = wa.actions.get(sourceForActions.getId().asInt());
       
-      // for movable widgets handle only drag events
-      if (config != null && config.movable && evt.getButton() != source.getMenuMouse())
-      {         
-         Consumer<MouseDirectManipulation> consumer = null;
+      if (actions == null)
+      {
+         return;
+      }
+
+      for (MouseWidgetAction action : actions)
+      {
+         applyMouseEvent(action, evt);
          switch (evt.getID())
          {
+            case MouseEvent.MOUSE_CLICKED:
+               action.mouseClicked(evt);
+               break;
+
             case MouseEvent.MOUSE_PRESSED:
-            {
-               consumer = (action) -> {
-                  action.mousePressed(evt);
-               };
-               break;
-            }
+               action.mousePressed(evt);
+               break;
+
+            case MouseEvent.MOUSE_RELEASED:
+               action.mouseReleased(evt);
+               break;
+
+            case MouseEvent.MOUSE_ENTERED:
+               action.mouseEntered(evt);
+               break;
+
+            case MouseEvent.MOUSE_EXITED:
+               action.mouseExited(evt);
+               break;
+
             case MouseEvent.MOUSE_DRAGGED:
-            {
-               consumer = (action) -> {
-                  action.mouseDragged(evt);
-               };
-               break;
-            }
-            case MouseEvent.MOUSE_RELEASED:
-            {
-               consumer = (action) -> {
-                  action.mouseReleased(evt);
-               };
-               break;
-            }
-         }
-         if (consumer != null)
-         {
-            performAction(config.id, evt, consumer);
-            evt.consume();
-            resConsumed = true;
-         }
-      }
-      
-      return resConsumed;
-   }
-   
-   /**
-    * Handle mouse events for selectable widgets. 
-    * If the widgets support mouse direct manipulation call mouse clicked if left button is clicked.
-    * 
-    * @param source  Widget
-    * @param evt     Mouse event
-    * 
-    * @return <code>true</code> iv event has been consumed <code>false</code> if not.
-    */
-   private static boolean handleMouseEventIfSelectable(Widget<GuiOutputManager> source, final MouseEvent evt)
-   {
-      WidgetConfig config = source.config();
-      // return code that can differs from evt.isConsumed() result
-      boolean resConsumed = false;
-      
-      // for selectable widgets handle only mouse clicked events
-      if (config != null && config.selectable && evt.getButton() != source.getMenuMouse())
-      {         
-         if (isMouseClickEvent(evt))
-         {
-            // select / deselect
-            if (evt.getID() == MouseEvent.MOUSE_CLICKED)
-            {
-               performAction(config.id, evt, (action) -> {
-                  action.mouseClicked(evt);
-               });
-            }
-            evt.consume();
-            resConsumed = true;
-         }
-      }
-      
-      return resConsumed;
+               action.mouseDragged(evt);
+               break;
+
+            case MouseEvent.MOUSE_MOVED:
+               action.mouseMoved(evt);
+               break;
+
+            case MouseEvent.MOUSE_WHEEL:
+               action.mouseWheelMoved((MouseWheelEvent) evt);
+               break;
+         }
+      }
    }
    
    /**
@@ -439,16 +552,16 @@
     *          The callback which should be fired.
     */
    private static void performAction(WidgetId id, 
-                                     final MouseEvent evt, 
+                                     MouseEvent evt, 
                                      Consumer<MouseDirectManipulation> mouseAction)
    {
       WorkArea wa = local.get();         
       // get the list of the actions for this widget
-      List<MouseWidgetAction> handlers = wa.actions.get(id.asInt());
-      if (handlers != null)
+      List<MouseWidgetAction> actions = wa.actions.get(id.asInt());
+      if (actions != null)
       {
          // walk through handlers
-         Iterator<MouseWidgetAction> iterAction = handlers.iterator();
+         Iterator<MouseWidgetAction> iterAction = actions.iterator();
          while (iterAction.hasNext())
          {
             MouseWidgetAction action = iterAction.next(); 
@@ -471,7 +584,7 @@
     * 
     * @return <code>true</code> if is a mouse click <code>false</code> if not.
     */
-   private static boolean isMouseClickEvent(final MouseEvent evt)
+   private static boolean isMouseClickEvent(MouseEvent evt)
    {
       int id = evt.getID();
       return id == MouseEvent.MOUSE_CLICKED || id == MouseEvent.MOUSE_PRESSED
@@ -505,101 +618,6 @@
    }
 
    /**
-    * Apply the specified mouse event to the specified source.
-    * 
-    * @param    mouseSource
-    *           The mouse source.
-    * @param    evt
-    *           The mouse event.
-    */
-   protected static void applyMouseEvent(Widget<GuiOutputManager> mouseSource, MouseEvent evt)
-   {
-      switch (evt.getID())
-      {
-         case MouseEvent.MOUSE_CLICKED:
-            mouseSource.mouseClicked(evt);
-            break;
-         case MouseEvent.MOUSE_PRESSED:
-            mouseSource.mousePressed(evt);
-            break;
-         case MouseEvent.MOUSE_RELEASED:
-            mouseSource.mouseReleased(evt);
-            break;
-         case MouseEvent.MOUSE_ENTERED:
-            mouseSource.mouseEntered(evt);
-            break;
-         case MouseEvent.MOUSE_EXITED:
-            mouseSource.mouseExited(evt);
-            break;
-         case MouseEvent.MOUSE_DRAGGED:
-            mouseSource.mouseDragged(evt);
-            break;
-         case MouseEvent.MOUSE_MOVED:
-            mouseSource.mouseMoved(evt);
-            break;
-         case MouseEvent.MOUSE_WHEEL:
-            mouseSource.mouseWheelMoved((MouseWheelEvent) evt);
-            break;
-      }
-   }
-
-   /**
-    * Process the mouse actions registered for the specified source.
-    * 
-    * @param    sourceID
-    *           The source widget ID.
-    * @param    evt
-    *           The mouse event.
-    */
-   protected static void processWidgetActions(int sourceID, MouseEvent evt)
-   {
-      WorkArea wa = local.get();
-
-      List<MouseWidgetAction> actions = wa.actions.get(sourceID);
-      
-      if (actions != null)
-      {
-         for (MouseWidgetAction action : actions)
-         {
-            switch (evt.getID())
-            {
-               case MouseEvent.MOUSE_CLICKED:
-                  action.mouseClicked(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_PRESSED:
-                  action.mousePressed(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_RELEASED:
-                  action.mouseReleased(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_ENTERED:
-                  action.mouseEntered(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_EXITED:
-                  action.mouseExited(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_DRAGGED:
-                  action.mouseDragged(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_MOVED:
-                  action.mouseMoved(evt);
-                  break;
-               
-               case MouseEvent.MOUSE_WHEEL:
-                  action.mouseWheelMoved((MouseWheelEvent) evt);
-                  break;
-            }
-         }
-      }
-   }
-   
-   /**
     * Set the window on which this mouse handler acts.
     * 
     * @param    ews
@@ -739,11 +757,49 @@
    }
    
    /**
+    * Find the mouse source on which the specified event will act, based on coordinates.
+    * 
+    * @param    e
+    *           The event.
+    *           
+    * @return   The mouse source, as returned by {@link Window#findMouseSource}.
+    */
+   protected final Widget<?> findMouseSource(MouseEvent e)
+   {
+      TopLevelWindow<?> window = (TopLevelWindow<?>) WindowManager.findWindow(ews.getWindowId());
+      if (window == null)
+      {
+         // the window may have been gone when the event was generated
+         // during window destroy
+         return null;
+      }
+      
+      Widget<?> mouseSource = window.findMouseSource(new MouseEvt(e, window));
+   
+      // If mouse source has no ID, find the nearest ancestor with ID 
+      Widget mouseSourceWithID = WidgetRegistry.findAncestor(mouseSource, true, t -> {
+         // TODO: to improve/simplify the mouse pointer is over a widget work around to skip labels
+         return UiUtils.getWidgetId(t) != null && !(LabelGuiImpl.class.equals(t.getClass()));
+      });
+   
+      // If the mouse source is ScrollPaneGuiImpl in EditorGuiImpl, return EditorGuiImpl
+      
+      // TODO: move this widget-specific logic into methods that are implemented in the widget
+      // hierarchy so that we do not have direct references to widget classes in this common code
+      if (mouseSourceWithID instanceof ScrollPaneGuiImpl && mouseSourceWithID.parent() instanceof EditorGuiImpl)
+      {
+         return mouseSourceWithID.parent();
+      }
+   
+      return mouseSourceWithID;
+   }
+
+   /**
     * Determine if mouse events can be processed.
     * 
     * @return   See above.
     */
-   protected static boolean canProcessMouse()
+   protected final static boolean canProcessMouse()
    {
       WorkArea wa = local.get();
       synchronized (wa)
@@ -765,6 +821,13 @@
       
       /** Cache of widget's mouse actions. */
       private Map<Integer, RoaringBitmap> cachedActions = new HashMap<>();
+      
+      /** The source ID of the last mouse press event */ 
+      private int mousePressedSourceID;
+
+      /** The "drag" mode flag */ 
+      private boolean dragMode;
+
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingMouseHandler.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingMouseHandler.java	2022-02-18 17:35:06 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingMouseHandler.java	2022-03-09 23:13:09 +0000
@@ -36,6 +36,30 @@
 ** 016 HC  20190806 Implemented SetParent Windows native function and the system-level widget
 **                  hierarchy.
 ** 017 VVT 20220218 Missing @Override annotations added, Javadocs and other cosmetic fixes.
+**     VVT 20220306 1) mouseEntered made no-op: all necessary actions are taken in the first mouse move
+**                     event. The reason is that when a mouse pointer enters a widget, *two* events
+**                     are generated simultaneously: the ENTER and the MOVE events.
+**                  2) mouseExited: call to findMouseSource eliminated, the correct mouse source is in
+**                     lastHoveredWidget field already.
+**                  3) mouseDragged, mouseWheelMoved: the mouse source widget is explicitly passed to
+**                     postMouseEvent.
+**                  4) mouseMoved:
+**                     * where is no need to execute code as a Runnable exit lambda, so the lambda body
+**                       was inlined.
+**                     * the complex method logic made simple:
+**                        * if the widget under mouse was not changed, then do nothing in this method (just
+**                          call super method) and exit.
+**                        * otherwise if previous widget (lastHoveredWidget) is not null, then send
+**                          EXIT event to it.
+**                        * send ENTER event to the new widget.
+**                  5) findMouseSource(MouseEvent): the local variable parent renamed to mouseSourceWithId,
+**                     since the old name was misleading.
+**                  6) processAction(MouseEvent): the value returned by findMouseSource(e) used instead of
+**                     null.
+**                  7) processAction: the argument is asserted never be null; method made static and renamed to
+**                     processActions.
+**                  8) Method findMouseSource(MouseEvent) pulled up to MouseHandler to make it accessible from
+**                     WebMouseHandler.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -92,20 +116,14 @@
 
 package com.goldencode.p2j.ui.client.gui.driver.swing;
 
-import java.awt.Component;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
 import java.util.List;
-import java.util.Map;
+
 import com.goldencode.p2j.security.*;
-import com.goldencode.p2j.ui.WidgetId;
+import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.client.*;
-import com.goldencode.p2j.ui.client.event.*;
-import com.goldencode.p2j.ui.client.gui.EditorGuiImpl;
-import com.goldencode.p2j.ui.client.gui.LabelGuiImpl;
-import com.goldencode.p2j.ui.client.gui.ScrollPaneGuiImpl;
 import com.goldencode.p2j.ui.client.gui.driver.*;
 import com.goldencode.p2j.ui.client.widget.*;
 
@@ -152,10 +170,7 @@
       // serialize this call on the OS event queue to prevent race condition
       tc.invokeLaterOS(() ->
       {
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseClicked(e);
-         }
+         defaultProcessEvent(e, findMouseSource(e));
       });
    }
 
@@ -182,10 +197,7 @@
          pressed = true;
          delayedMouseExited = null;
          
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mousePressed(e);
-         }
+         defaultProcessEvent(e, findMouseSource(e));
       });
    }
 
@@ -215,10 +227,7 @@
             delayedMouseExited = null;
          }
          
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseReleased(e);
-         }
+         defaultProcessEvent(e, findMouseSource(e));
       });
    }
 
@@ -229,31 +238,11 @@
     * @param    e
     *           The mouse event.
     */
+   @Override
    public void mouseEntered(MouseEvent e)
    {
-      if (!canProcessMouse())
-      {
-         return;
-      }
-      
-      // serialize this call on the OS event queue to prevent race condition
-      tc.invokeLaterOS(() ->
-      {
-         Widget<?> mouseSource = findMouseSource(e);
-         lastHoveredWidget = mouseSource;
-         
-         if (mouseSource != null)
-         {
-            processAction(mouseSource, e); // calls mouseSource.mouseEntered(e);
-            // call this unconditionally, as the widget needs to be notified that it was exited...
-            tc.postMouseEvent(e, ews.getWindowId(), UiUtils.getWidgetIdAsInt(mouseSource));
-         }
-         
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseEntered(e);
-         }
-      });
+      // Ignore this event, since it duplicates the MOUSE_MOVED, and all necessary actions
+      // are taken in mouseMoved
    }
    
    /**
@@ -264,7 +253,7 @@
     *           The mouse event.
     */
    @Override
-   public void mouseExited(final MouseEvent e)
+   public void mouseExited(MouseEvent e)
    {
       if (!canProcessMouse())
       {
@@ -280,23 +269,16 @@
             delayedMouseExited = e;
             return;
          }
-         
-         Widget<?> mouseSource = findMouseSource(e);
-         if (mouseSource != null)
+
+         // This can be null on window close.
+         if (lastHoveredWidget != null)
          {
-            processAction(mouseSource, e); // calls mouseSource.mouseExited(e);
-               
+            processActions(lastHoveredWidget, e);
+
             // call this unconditionally, as the widget needs to be notified that it was exited...
-            tc.postMouseEvent(e, ews.getWindowId(),
-                              UiUtils.getWidgetIdAsInt(lastHoveredWidget != null
-                                                        ? lastHoveredWidget : mouseSource));
-         }
-         
-         lastHoveredWidget = null;
-         
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseExited(e);
+            tc.postMouseEvent(e, ews.getWindowId(), UiUtils.getWidgetIdAsInt(lastHoveredWidget));
+
+            lastHoveredWidget = null;
          }
       });
    }
@@ -319,14 +301,11 @@
       // serialize this call on the OS event queue to prevent race condition
       tc.invokeLaterOS(() ->
       {
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseWheelMoved(e);
-         }
+         defaultProcessEvent(e, findMouseSource(e));
       });
    }
    
-   /**
+  /**
     * Notification of a mouse dragged event.  If the mouse event can't be processed by the driver
     * directly, it will post it to the event manager.
     * 
@@ -344,10 +323,7 @@
       // serialize this call on the OS event queue to prevent race condition
       tc.invokeLaterOS(() ->
       {
-         if (!processAction(e))
-         {
-            SwingMouseHandler.super.mouseDragged(e);
-         }
+         defaultProcessEvent(e, lastHoveredWidget);
       });
    }
 
@@ -362,7 +338,7 @@
     *           The mouse event.
     */
    @Override
-   public void mouseMoved(final MouseEvent e)
+   public void mouseMoved(MouseEvent e)
    {
       if (!canProcessMouse())
       {
@@ -373,70 +349,47 @@
       tc.invokeLaterOS(() ->
       {
          Widget<?> mouseSource = findMouseSource(e);
-         
-         Runnable exit = () ->
-         {
-            Component src = (Component) e.getSource();
-            
-            if (src.isDisplayable())
-            {
-               MouseEvent exite = new MouseEvent(src,
-                                                 MouseEvent.MOUSE_EXITED,
-                                                 e.getWhen(),
-                                                 e.getModifiers(),
-                                                 e.getX(),
-                                                 e.getY(),
-                                                 e.getClickCount(),
-                                                 false,
-                                                 e.getButton());
-               // lastHoveredWidget.mouseExited(exite);
-               processAction(lastHoveredWidget, exite);
-               // call this unconditionally, as the widget needs to be notified that it was exit...
-               tc.postMouseEvent(exite, ews.getWindowId(), UiUtils.getWidgetIdAsInt(lastHoveredWidget));
-            }
-            lastHoveredWidget = null;
-         };
-         
-         // if the widget has changed, send EXITED/ENTERED events
-         if (lastHoveredWidget != null            &&
-             lastHoveredWidget.ancestor() != null && 
-             (!lastHoveredWidget.isDisplayed() ||
-              lastHoveredWidget.getActualBounds() == null ||
-              !lastHoveredWidget.getActualBounds().contains(e.getX(), e.getY())))
-         {
-            exit.run();
-         }
-         
-         if (lastHoveredWidget != mouseSource &&
-             mouseSource!= null               &&
-             mouseSource.getActualBounds().contains(e.getX(), e.getY()))
-         {
-            if (lastHoveredWidget != null)
-            {
-               exit.run();
-            }
-            
-            MouseEvent entere = new MouseEvent((Component) e.getSource(),
-                                               MouseEvent.MOUSE_ENTERED,
-                                               e.getWhen(),
-                                               e.getModifiers(),
-                                               e.getX(),
-                                               e.getY(),
-                                               e.getClickCount(),
-                                               false,
-                                               e.getButton());
-            // mouseSource.mouseEntered(entere);
-            processAction(mouseSource, entere);
-            // call this unconditionally, as the widget needs to be notified that it was entered...
-            tc.postMouseEvent(entere, ews.getWindowId(), UiUtils.getWidgetIdAsInt(mouseSource));
-            
+
+         int windowId = ews.getWindowId();
+         Component src = (Component) e.getSource();
+         int newWidgetId = UiUtils.getWidgetIdAsInt(mouseSource);
+
+         if (lastHoveredWidget != mouseSource)
+         {
+            // Test if we have to send EXIT
+            if (lastHoveredWidget != null && lastHoveredWidget.ancestor() != null)
+            {
+               MouseEvent exitEvent = new MouseEvent(src, MouseEvent.MOUSE_EXITED,
+                        e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getClickCount(),
+                        false, e.getButton());
+               processActions(lastHoveredWidget, exitEvent);
+
+               // call this unconditionally (regardless of the process action result),
+               // as the widget needs to be always notified that it was exited...
+               tc.postMouseEvent(exitEvent, windowId,
+                        UiUtils.getWidgetIdAsInt(lastHoveredWidget));
+
+            }
+
+            if (mouseSource != null)
+            {
+               MouseEvent enterEvent = new MouseEvent(src, MouseEvent.MOUSE_ENTERED,
+                        e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getClickCount(),
+                        false, e.getButton());
+               processActions(mouseSource, enterEvent);
+               // call this unconditionally (regardless of the process action result),
+               // as the widget needs to be always notified that it was entered...
+               tc.postMouseEvent(enterEvent, windowId, newWidgetId);
+
+            }
+
             lastHoveredWidget = mouseSource;
          }
          else
          {
-            if (!processAction(e))
+            if (mouseSource != null && !processActions(mouseSource, e))
             {
-               SwingMouseHandler.super.mouseMoved(e);
+               tc.postMouseEvent(e, windowId, newWidgetId);
             }
          }
       });
@@ -491,88 +444,50 @@
    }
 
    /**
-    * Find the mouse source on which the specified event will act, based on coordinates.
-    * 
-    * @param    e
-    *           The event.
-    *           
-    * @return   The mouse source, as returned by {@link Window#findMouseSource}.
-    */
-   private Widget<?> findMouseSource(MouseEvent e)
-   {
-      TopLevelWindow<?> window = (TopLevelWindow<?>) WindowManager.findWindow(ews.getWindowId());
-      if (window == null)
-      {
-         // the window may have been gone when the event was generated
-         // during window destroy
-         return null;
-      }
-      
-      Widget<?> mouseSource = window.findMouseSource(new MouseEvt(e, window));
-      
-      // If mouse source has no ID, find the nearest ancestor with ID 
-      Widget parent = WidgetRegistry.findAncestor(mouseSource, true, t ->
-      {
-         // TODO: to improve/simplify the mouse pointer is over a widget work around to skip labels
-         return UiUtils.getWidgetId(t) != null && !(LabelGuiImpl.class.equals(t.getClass()));
-      });
-      
-      // TODO: move this widget-specific logic into methods that are implemented in the widget
-      // hierarchy so that we do not have direct references to widget classes in this common code
-      if (parent instanceof ScrollPaneGuiImpl && parent.parent() instanceof EditorGuiImpl)
-      {
-         parent = parent.parent();
-      }
-
-      return parent;
-   }
-
-   /**
-    * Determine the widget which will react on the specified event and process any actions
-    * associated with it.
-    * 
-    * @param    e
-    *           The mouse event.
-    *           
-    * @return   See above.
-    */
-   private boolean processAction(MouseEvent e)
-   {
-      return processAction(null, e);
-   }
-
+    * Process action and post mouse event using {@link #lastHoveredWidget} as the mouse source.
+    * 
+    * @param e
+    *        the mouse event.
+    * @param mouseSource
+    *        the widget to post event to, can be {@code null} if no mouse source matches the event
+    */
+   private final void defaultProcessEvent(MouseEvent e, Widget mouseSource)
+   {
+      if (!processActions(mouseSource, e))
+      {
+         // Note: we have to post an event even if null was passed as mouse source,
+         // since for dragging events the effective mouse source may be computed later
+         // as the non-null mouse press event source.
+         tc.postMouseEvent(e, ews.getWindowId(), UiUtils.getWidgetIdAsInt(mouseSource));
+      }
+   }
+   
    /**
     * Process any actions for the given widget and event.
     * 
     * @param    widget
-    *           The widget.  If <code>null</code>, it will be determined by 
-    *           {@link #findMouseSource}.
+    *           The widget. May be {@code null}.
     * @param    e
     *           The mouse event.
     * 
     * @return   {@code true} if at least one action was executed
     */
-   private boolean processAction(Widget<?> widget, final MouseEvent e)
+   private static boolean processActions(Widget<?> widget, MouseEvent e)
    {
-      if (widget == null)
-      {
-         widget = findMouseSource(e);
-      }
-
-      if (widget == null || !(widget.isDisplayed() && widget.isVisible()))
+      if (widget == null || !widget.isDisplayed() || !widget.isVisible())
       {
          return false;
       }
-      
-      WidgetId wid = UiUtils.getWidgetId(widget);
-      
+
+      WidgetId wid = widget.getId();
+
       if (wid == null)
       {
          return false;
       }
-      
+
       List<MouseWidgetAction> actionList = local.get().actions.get(wid.asInt());
-         
+
       if (actionList == null)
       {
          return false;

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java	2022-02-23 20:38:07 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java	2022-03-06 22:06:22 +0000
@@ -184,6 +184,14 @@
 **     SBI 20220131 Changed resolveResourceId by adding ContentDisposition parameter and changed
 **                  deliverDocumentOutput to use ContentDisposition.INLINE for printed documents.
 **     SBI 20220223 Changed chooseFiles to process accepted files filters for the web client.
+**     VVT 20220224 raiseMouseEvent(): an additional widgetId parameter is passed to window raiseMouseEvent()
+**                  method. Multiple cosmetic fixes: missing @Override annotations added, methods made static
+**                  when possible, closable resource access wrapped with try() statement, unneeded Java casts
+**                  removed.
+**     VVT 20220306 1) raiseMouseEvent: an additional widgetId parameter is passed to window raiseMouseEvent.
+**                  2) Multiple cosmetic fixes: missing @Override annotations added, methods made static when
+**                  possible, closable resource access wrapped with try() statement, unneeded Java casts
+**                  removed.
 */
 
 /*
@@ -314,6 +322,7 @@
    /** A bogus AWT component used to raise mouse events, on notification from the JS code. */
    private static final Component BOGUS_AWT_COMPONENT = new Component()
    {
+      // no-op
    };
    
    /** Actions for hoverable widgets */
@@ -334,7 +343,7 @@
    /** Text sample to calculate scaling factor for variable size fonts. */
    private static final String TEXT_SAMPLE = "QqWwEeRrTtYyUuIiOoPp[{}]|AaSsDdFfGgHhJjKkLl:;'ZzXxCcVvBbNnMn<,>.?/ 1234567890!@#$%^&*()_-+=~`";
    
-   /** Text sample to calculate scaling factor for monospace fonts. */
+   /** Text sample to calculate scaling factor for monospaced fonts. */
    private static final String TEXT_SAMPLE_MONO = "0.0";
    
    /** Delgated implementation for the embedded web server. */
@@ -367,9 +376,6 @@
    /** Owned images is a mapping of a window to its owned images seals. */
    private final Map<Integer, Set<List<Integer>> > ownedImages;
    
-   /** A cache of font metrics objects, by their remote font ID. */
-   private Map<Integer, FontMetricsHelper> fontMetricsCache = new HashMap<>();
-   
    /** A counter for font IDs to be mapped on the js-side. */
    private int nextFontId = 0;
    
@@ -488,6 +494,7 @@
       updateDesktopDimensions(width, height);
 
       // calculate our resource base
+      @SuppressWarnings("resource")
       Resource cpres = EmbeddedWebServerImpl.calcResourceBase(GuiWebDriver.class, "res");
      
       // calculate our welcome page
@@ -600,6 +607,7 @@
    @Override
    public void initRemoteClient()
    {
+      // no-op
    }
 
    /**
@@ -731,6 +739,7 @@
    @Override
    public void injectVT100Key(int keyCode, char charCode)
    {
+      // no-op
    }
    
    /**
@@ -1345,7 +1354,7 @@
     * @param    widgetId
     *           The widget ID.
     * @param    fileNameList
-    *           The oredered file name list to be dropped.
+    *           The ordered file name list to be dropped.
     */
    @Override
    public void startDropTarget(int windowId, int widgetId, List<String> fileNameList)
@@ -1526,7 +1535,7 @@
     * 
     * @return   The key's modifiers bit mask for this mouse event.
     */
-   private int getMouseModifiers(int button, int modifiersMask)
+   private static int getMouseModifiers(int button, int modifiersMask)
    {
       int modifiers = 0;
       // button codes from JS (same as from AWT) 
@@ -1547,21 +1556,21 @@
             break;
          default:
       }
-      if ((modifiersMask & MouseEvent.SHIFT_MASK) != 0)
-      {
-         modifiers |= MouseEvent.SHIFT_DOWN_MASK; 
-      }
-      if ((modifiersMask & MouseEvent.CTRL_MASK) != 0)
-      {
-         modifiers |= MouseEvent.CTRL_DOWN_MASK; 
-      }
-      if ((modifiersMask & MouseEvent.META_MASK) != 0)
-      {
-         modifiers |= MouseEvent.META_DOWN_MASK; 
-      }
-      if ((modifiersMask & MouseEvent.ALT_MASK) != 0)
-      {
-         modifiers |= MouseEvent.ALT_DOWN_MASK; 
+      if ((modifiersMask & InputEvent.SHIFT_MASK) != 0)
+      {
+         modifiers |= InputEvent.SHIFT_DOWN_MASK; 
+      }
+      if ((modifiersMask & InputEvent.CTRL_MASK) != 0)
+      {
+         modifiers |= InputEvent.CTRL_DOWN_MASK; 
+      }
+      if ((modifiersMask & InputEvent.META_MASK) != 0)
+      {
+         modifiers |= InputEvent.META_DOWN_MASK; 
+      }
+      if ((modifiersMask & InputEvent.ALT_MASK) != 0)
+      {
+         modifiers |= InputEvent.ALT_DOWN_MASK; 
       }
       
       return modifiers;
@@ -2041,7 +2050,7 @@
     *           The collected mouse widget.
     * @return   The web actions that are handled by the given widget.
     */
-   private RoaringBitmap getWebActions(Widget widget)
+   private static RoaringBitmap getWebActions(Widget widget)
    {
       return MouseHandler.getCachedActions(widget.getId().asInt(), () ->
       {
@@ -2128,6 +2137,7 @@
     * @param isWaitState
     *        wait state
     */
+   @Override
    public void propagateWaitState(boolean isWaitState)
    {
       websock.propagateWaitState(isWaitState);
@@ -2359,7 +2369,7 @@
     * @return  <code>TRUE</code> if the widget is inside visible frame or standalone,
     *          <code>FALSE</code> otherwise.
     */
-   private boolean isInsideVisibleFrameOrStandalone(Widget wid)
+   private static boolean isInsideVisibleFrameOrStandalone(Widget wid)
    {
       // get the enclosing frame
       Frame fr = UiUtils.locateFrame(wid);
@@ -2493,14 +2503,14 @@
     * @param    icon
     *           The name of custom cursor.
     */
+   @Override
    public void setCursor(String icon)
    {
       String mimeType = mimeType(icon.toLowerCase());
       if (mimeType != null)
       {
-         try
-         {
-            InputStream resource = ThemeManager.getCurrentTheme().getBinaryResource(icon);
+         try(InputStream resource = ThemeManager.getCurrentTheme().getBinaryResource(icon))
+         {            
             if (resource != null)
             {
                byte[] image = ByteStreams.toByteArray(resource);            
@@ -2544,6 +2554,7 @@
     * @param ptr
     *           mouse pointer
     */
+   @Override
    public void propagate(int windowId, int wid, MousePtrWrapper ptr)
    {
       websock.setMousePtr(windowId, wid, ptr); 
@@ -2733,12 +2744,12 @@
          // special processing for monospaced fonts
          if (scaleX <= 1.1)
          {
-            wRes = (int)Math.ceil((double)wRes * scaleX);
+            wRes = (int)Math.ceil(wRes * scaleX);
          }
       }
       else
       {
-         wRes = (int)Math.ceil((double)super.getTextWidth(text, fd) * scaleX);
+         wRes = (int)Math.ceil(super.getTextWidth(text, fd) * scaleX);
       }
       
       return wRes;
@@ -3177,11 +3188,8 @@
     * 
     * @param    uriString
     *           The target resource.
-    * 
-    * @throws   IOException
-    *           For IO errors during file operations.
     */
-   private String writeLauncherWrapper(String uriString)
+   private static String writeLauncherWrapper(String uriString)
    {
       Path wrapperFile;
       
@@ -3215,7 +3223,7 @@
     *          
     * @return  MIME type if image <code>null</code> if not.
     */
-   private String mimeType(String icon)
+   private static String mimeType(String icon)
    {
       if (icon == null)
       {

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseDirectManipulation.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseDirectManipulation.java	2022-02-18 17:35:06 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseDirectManipulation.java	2022-03-06 21:49:25 +0000
@@ -10,6 +10,7 @@
 ** 002 HC  20200403 Improved web gui widget direct manipulation to allow widget move, and to
 **                  limit the bounds of widget move and resize.
 ** 003 VVT 20220218 Javadocs and other cosmetic fixes.
+**     VVT 20220306 Javadocs and other cosmetic fixes.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -65,14 +66,13 @@
 */
 package com.goldencode.p2j.ui.client.gui.driver.web;
 
-import java.awt.event.MouseEvent;
+import java.awt.event.*;
 
 import com.goldencode.p2j.ui.*;
-import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.*;
+import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.gui.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
-import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.widget.*;
 
 /**
@@ -207,11 +207,9 @@
     * 
     * @param    evt
     *           The mouse event details.
-    * @param    firstCall
-    *           Flag indicating first call in a current drag session.
     */
    @Override
-   public void processDrag(MouseEvent evt, boolean firstCall)
+   protected void processDrag(MouseEvent evt)
    {
       // box selection mode needs the event to be sent on actual start drag instead of press
       if (boxSelecting)

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseHandler.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseHandler.java	2022-01-04 11:25:39 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseHandler.java	2022-03-06 21:49:25 +0000
@@ -21,6 +21,10 @@
 ** 011 HC  20190806 Implemented SetParent Windows native function and the system-level widget
 **                  hierarchy.
 ** 012 AL2 20220104 Resolve dragging editors with the initial widget when no resolved target.
+**     VVT 20220306 Javadocs and other cosmetic fixes. raiseMouseEvent:
+**                  * Method MouseHandler.findMouseSource(MouseEvent) is used instead of
+**                    findMouseSource(NativePoint).
+**                  * The unused widgetId arguments eliminated.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -77,9 +81,9 @@
 
 package com.goldencode.p2j.ui.client.gui.driver.web;
 
-import java.awt.event.MouseEvent;
-import com.goldencode.p2j.ui.client.*;
-import com.goldencode.p2j.ui.client.gui.*;
+import java.awt.event.*;
+
+import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
 import com.goldencode.p2j.ui.client.widget.*;
 
@@ -97,7 +101,7 @@
     * @param    windowId
     *           Target window id.
     * @param    widgetId
-    *           Target widget id.
+    *           the widget ID or -1 if not known
     */
    public void raiseMouseEvent(MouseEvent event, int windowId, int widgetId)
    {
@@ -105,49 +109,21 @@
       {
          return;
       }
-      tc.invokeLaterOS(() ->
-      {
-         int effectiveWidgetId = widgetId;
-         if (widgetId == -1)
+
+      tc.invokeLaterOS(() -> {
+         Widget effMouseSource = ThinClient.getInstance().getWidget(widgetId);
+         if (effMouseSource == null)
+         {
+            effMouseSource = findMouseSource(event);
+         }
+
+         if (effMouseSource == null)
          {
             tc.postMouseEvent(event, windowId);
          }
          else
          {
-            Widget w = tc.getWidget(widgetId);
-            if (w == null || w.ancestor() == null)
-            {
-               return;
-            }
-
-            if (w instanceof AbstractContainer)
-            {
-               // it needs to get the widget's location with respect to the parent window
-               NativePoint origin = w.screenPhysicalLocation();
-               Widget target = w.findMouseSource(
-                        new NativePoint(event.getX() - origin.x, event.getY() - origin.y));
-               if (target != null && target.getId() != null)
-               {
-                  effectiveWidgetId = target.getId().asInt();
-               }
-               else
-               {
-                  // TODO: move this widget-specific logic into methods that are implemented in
-                  // the widget hierarchy so that we do not have direct references to widget
-                  // classes in this common code
-             
-                  // in a dragging process, the target may be null and still the dragged
-                  // widget to be an editor; therefore ensure that the editor is resolved
-                  // using the actual widgetId (w)
-                  Widget editor = WidgetRegistry.findAncestor((target == null ? w : target), true,
-                                                t -> EditorGuiImpl.class.equals(t.getClass()));
-                  if (editor != null && editor.getId() != null)
-                  {
-                     effectiveWidgetId = editor.getId().asInt();
-                  }
-               }
-            }
-            tc.postMouseEvent(event, windowId, effectiveWidgetId);
+            tc.postMouseEvent(event, windowId, effMouseSource.getId().asInt());
          }
       });
    }

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.mouse.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.mouse.js	2021-10-08 19:29:40 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.mouse.js	2022-03-06 21:49:25 +0000
@@ -2,7 +2,7 @@
 ** Module   : p2j.mouse.js
 ** Abstract : web-specific mouse management
 **
-** Copyright (c) 2015-2021, Golden Code Development Corporation.
+** Copyright (c) 2015-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ------------------------------Description----------------------------------
 ** 001 CA  20150826 First version.
@@ -49,10 +49,14 @@
 ** 020 HC  20210720 Fixed multiple mouse pointer issues (cursor not changing when it
 **                  should or changing when it should not).
 **     SBI 20210914 Reused the common code by creating new method p2j.screen.activateWindow.
-**     EVL 20211004 Optomization for widnow resize drag handler to fix secondary window processing.
+**     EVL 20211004 Optimization for widnow resize drag handler to fix secondary window processing.
 **     SBI 20211008 Renamed p2j.createDialog to p2j.createClipboardDialog and p2j.closeDialog to
 **                  p2j.closeClipboardDialog.
-
+**     VVT 20220306 1) Event listeners removed for "mouseenter" and "mouseleave": they were duplicating
+**                  "mouseout" and "mouseover" listeners in wrong order and resulted in duplicate events sent
+**                  to Java.  2) Function inlined: sendMouseExit, sendMouseEnter 3) sendMouseEvent renamed to
+**                  sendMouseOrOSEvent, Javadocs fixed 4) processMouseMove simplified and fixed: See #6013-94
+**                  for the description.
 */
 
 /*
@@ -133,10 +137,8 @@
    
    // mouse move: if the mouse cursor hasn't exited the last hovered area, cancel
    win.canvas.addEventListener("mousemove",  processMouseMove(this, "mousemove"));
-   win.canvas.addEventListener("mouseenter", processMouseMove(this, "mouseenter"));
-   win.canvas.addEventListener("mouseleave", processMouseMove(this, "mouseleave"));
-   win.canvas.addEventListener("mouseout",   processMouseMove(this, "mouseout"));
    win.canvas.addEventListener("mouseover",  processMouseMove(this, "mouseover"));
+   win.canvas.addEventListener("mouseout", processMouseMove(this, "mouseleave"));
 
    /**
     * Registers event handler for the supplied driver widget.
@@ -226,54 +228,19 @@
    }
    
    /**
-    * Send mouse leave event to a widget
-    * 
-    *  @param wid 
-    *         widget Id
-    *  @param evt
-    *         source mouse event
-    */
-   function sendMouseExit(wid, evt)
-   {
-      if (wid != undefined)
-      {
-         sendMouseEvent(wid, evt, "mouseleave", "mouseExit");
-      }
-   };
-
-   /**
-    * Send mouse leave enter to a widget
-    * 
-    *  @param wid 
-    *         widget Id
-    *  @param evt
-    *         source mouse event
-    */
-   function sendMouseEnter(wid, evt)
-   {
-      if (wid != undefined)
-      {
-         sendMouseEvent(wid, evt, "mouseenter", "mouseEnter");
-      }
-   };
-
-   /**
-    * Send mouse leave to a widget
-    * 
-    *  @param wid 
-    *         widget Id
-    *  @param evt
-    *         source mouse event
+    * Send mouse event to a widget
+    * 
+    *  @param wid 
+    *         widget Id
+    *  @param evt
+    *         mouse event source
     *  @param mouseOp
     *         operation name
     *  @param handler
-    *         the name of the handler method
+    *         the name of the OS event handler method
     */
-   function sendMouseEvent(wid, evt, mouseOp, handler)
+   function sendMouseOrOSEvent(wid, evt, mouseOp, handler)
    {
-      // send an exit event to last widget
-      var exitOpCode = p2j.screen.mouseOps[mouseOp] & 0xff;
-      
       var osEvent = p2j.screen.getOsEvent(evt, 
                                           win, 
                                           mouseOp, 
@@ -285,27 +252,28 @@
       }
       else
       {
-         win.sendExplicitMouseEvent(
-               wid, 
+         win.sendMouseEvent(
                evt, 
                win.getMousePos(evt), 
                win, 
-               exitOpCode);
+               p2j.screen.mouseOps[mouseOp] & 0xff,
+               wid);
       }
    };
 
    /**
-    * Listen for mouse move events.   This listener is intended to discard any mouse movement
+    * Listen for mouse "mousemove", "mouseout" and "mouseover"
+    * events. This listener is intended to discard any mouse movement
     * inside the same widget.
     *
     * TODO: CA: test how it interacts with drag (i.e. in EDITOR).
     *
     * @param    {Object} mThis
     *           The <code>MouseHandler</code> instance.
-    * @param    {String} mop
+    * @param    {String} mouseOp
     *           The mouse event associated with this callback.
     */
-   function processMouseMove(mThis, mop)
+   function processMouseMove(mThis, mouseOp)
    {
       return function(evt)
       {
@@ -314,64 +282,50 @@
             return;
          }
 
-         var mouseOp = mop;
-
-         var merged = mouseOp == "mouseenter" ? [win.mouseAwareWidgets["mouseenter"]]
-                                              : [win.mouseAwareWidgets[mouseOp], win.mouseAwareWidgets["mouseenter"]];
-         var wid = win.findMouseSource(evt, merged);
-         var enterWid = undefined;
          var mousePos = mThis.getMousePos(evt);
-         
-         // intercept early events and send them to MsgBlaster. Use "mouseenter" list as the
-         // events will be filtered and sent to MsgBlasterPeer only the minimum required
-         p2j.screen.checkMessageBlasterEvent(
-                  win.findMouseSource(evt, win.mouseAwareWidgets["mouseenter"]), mousePos, evt, mop);
-         
-         if ((mouseOp == "mousemove" || mouseOp == "mouseover")) 
-         {
-            enterWid = win.findMouseSource(evt, win.mouseAwareWidgets["mouseenter"]);
-            if (enterWid != undefined && wid != lastMouseWidget)
-            {
-               mouseOp = "mouseenter";
-            }
-         }
-
-         var opCode = p2j.screen.mouseOps[mouseOp] & 0xff;
-
-         // determine if the widget has changed
-         if (lastMouseWidget != undefined && lastMouseWidget == wid)
-         {
-            // if the widget has not changed but has a "mousemove" op, then let it process
-            // on the server-side
-            if (mouseOp == "mousemove" || mouseOp == "mouseover")
-            {
-               return;
-            }
-            
-            // otherwise, if widget has not changed, do nothing
+          
+         var newMouseSource
+          
+         if (mouseOp == "mousemove")
+         {
+            var mouseMoveAwareWidgets = win.mouseAwareWidgets["mousemove"]
+            if (mouseMoveAwareWidgets != null)
+            {
+                // intercept early mouse move events and send them to MsgBlaster.
+                p2j.screen.checkMessageBlasterEvent(mouseMoveAwareWidgets, mousePos, evt);
+                
+                newMouseSource = win.findMouseSource(evt, mouseMoveAwareWidgets)
+            }
          }
          else
          {
-            if (mouseOp == "mouseenter")
-            {
-               // send an enter event to the previous widget
-               sendMouseExit(lastMouseWidget, evt);
-               // send an enter event to this widget
-               sendMouseEnter(enterWid, evt);
-            }
-            else if (mouseOp == "mouseleave" || mouseOp == "mouseout")
-            {
-               // exiting the window
-               sendMouseExit(lastMouseWidget, evt);
-            }
-            else if (mouseOp == "mousemove" || mouseOp == "mouseover")
-            {
-               win.sendMouseEvent(evt, mousePos, win, opCode, wid);
-            }
-         }
-
-         // save the last widget the mouse was over
-         lastMouseWidget = wid;
+            // "mouseover" or "mouseout"
+            // we do not need to test widget for "mouseexit" here, since to be "mouseexit" aware,
+            // a widget should be "mouseenter" also aware
+            newMouseSource = win.findMouseSource(evt, win.mouseAwareWidgets["mouseenter"])
+         }
+         
+         if (lastMouseWidget != newMouseSource)
+         {
+            if (lastMouseWidget != undefined)
+            {
+               sendMouseOrOSEvent(lastMouseWidget, evt, "mouseleave", "mouseExit");
+            }
+
+            if (newMouseSource != undefined)
+            {
+               sendMouseOrOSEvent(newMouseSource, evt, "mouseenter", "mouseEnter");
+            }
+
+            lastMouseWidget = newMouseSource;
+         }
+         
+         if (mouseOp == "mousemove")
+         {
+             var opCode = p2j.screen.mouseOps[mouseOp] & 0xff;
+             // FIXME: this way of event sending ignores OS events, is this correct?
+             win.sendMouseEvent(evt, mousePos, win, opCode, newMouseSource);
+         }
 
          evt.preventDefault();
       };
@@ -402,12 +356,12 @@
    }
    
    // dragging operations
+   document.addEventListener("mousedown",   processMouseDragStart("mousedown"));
+   document.addEventListener("mousemove",   processMouseDrag     ("mousemove"));
    document.addEventListener("click",       processMouseDragStop ("click"));
    document.addEventListener("contextmenu", processMouseDragStop ("contextmenu"));
    document.addEventListener("dblclick",    processMouseDragStop ("dblclick"));
-   document.addEventListener("mousedown",   processMouseDragStart("mousedown"));
    document.addEventListener("mouseup",     processMouseDragStop ("mouseup"));
-   document.addEventListener("mousemove",   processMouseDrag     ("mousemove"));
    
    /**
     * Resizes the peer canvas according to given area.
@@ -898,12 +852,8 @@
       }
       
       var exitOpCode = p2j.screen.mouseOps["mouseleave"] & 0xff;
-      win.sendExplicitMouseEvent(wid, 
-            evt, 
-            win.getMousePos(evt), 
-            win, 
-            exitOpCode);
-//      console.log("mouseExit(%s); parent: %s", wid, pid);
+      // console.debug("mouseExit(%s); parent: %s", wid, pid);
+      win.sendMouseEvent(evt, win.getMousePos(evt), win, exitOpCode, wid);
       evt.preventDefault();
    };
 
@@ -913,14 +863,9 @@
       win.setHovered(this);
       
       p2j.screen.setActiveHoverWindow(win);
-//      console.log("mouseEnter(%s:%s): %s; %s", win.id, wid, this.ptr, this.forceDefault);
       win.setCursorStyle(this.effectivePtr(), this.wid == p2j.screen.getActiveDropDown());
-      var exitOpCode = p2j.screen.mouseOps["mouseenter"] & 0xff;
-      win.sendExplicitMouseEvent(wid, 
-            evt, 
-            win.getMousePos(evt), 
-            win, 
-            exitOpCode);
+      // console.debug("mouseEnter(%s:%s): %s; %s", win.id, wid, this.ptr, this.forceDefault);
+      win.sendMouseEvent(evt, win.getMousePos(evt), win, p2j.screen.mouseOps["mouseenter"] & 0xff, wid);
       evt.preventDefault();
    };
 
@@ -1000,11 +945,13 @@
    // c. mouse up: disables the drag
    var targets = [win, win, win, win, win, win.canvas.ownerDocument, win.canvas.ownerDocument];
    var mops = ["click", "contextmenu", "dblclick", "mousedown", "mouseup", "mousemove", "mouseleave"];
-   var listeners = [processMouseDragStop (this, "click"), processMouseDragStop (this, "contextmenu"),
-                    processMouseDragStop (this, "dblclick"), processMouseDragStart(this, "mousedown"),
-                    processMouseDragStop (this, "mouseup"), processMouseDrag     (this, "mousemove"),
+   var listeners = [processMouseDragStop (this, "click"),
+                    processMouseDragStop (this, "contextmenu"),
+                    processMouseDragStop (this, "dblclick"),
+                    processMouseDragStart(this, "mousedown"),
+                    processMouseDragStop (this, "mouseup"),
+                    processMouseDrag     (this, "mousemove"),
                     processMouseDragStop (this, "mouseleave")];
-   // win.canvas.addEventListener("drag", processMouseDrag(this, "drag"));
    addListeners(targets, mops, listeners);
    
    this.deregisterListeners = function()

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2022-02-25 14:46:27 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2022-03-06 21:49:25 +0000
@@ -148,6 +148,13 @@
 **                  need to be completed).
 **     SBI 20220223 Added "filters" parameter to chooseFiles. Changed to select a unique child of input of file type
 **                  for Uploader dom node.
+**     VVT 20220306 1) The mouseButtonDownStack variable added to keep last mouse button pressed/released
+**                  state stack.  2) evt.preventSendToApplication is no more used to prevent some event
+**                  handlers. evt.preventDefault() is used instead.  3) event listeners are now not registered
+**                  for "mouseenter", "mouseleave", "mouseover" and "mouselout" events.  4) trivial
+**                  sendExplicitMouseEvent function inlined.  5) sendMouseEvent: a correct button field of
+**                  output event is now calculated for *all* events (not only press/release/click events).  6)
+**                  getModifiersBitMask inlined 7) checkMessageBlasterEvent: unused mop argument removed.
 */
 
 /*
@@ -420,6 +427,16 @@
    var totalFilesLimit = 100 * 1024 * 1024;
    
    /**
+    * Last mouse button pressed/released state stack.
+    *
+    * The value is used to get the 'button' value to pass along with events, for which the value is not
+    * reliable directly provided by browsers.
+    *
+    * The value is computed based on mouse down/up event data (fields 'buttons' and 'button').
+    */
+   var mouseButtonDownStack = [];
+    
+   /**
     * Dispatch mouse events to the registered widgets. 
     */
    function MouseEventDispatcher()
@@ -573,7 +590,7 @@
       };
 
       /**
-       * Handles a document widen "mousedown" event.
+       * Handles a document-wide "mousedown" event.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -613,7 +630,7 @@
       }
       
       /**
-       * Handles a document widen "mouseup" event.
+       * Handles a document-wide "mouseup" event.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -651,7 +668,7 @@
       }
 
       /**
-       * Handles a document widen "click" event.
+       * Handles a document-wide "click" event.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -675,7 +692,7 @@
       }
 
       /**
-       * Handles a document widen "contextmenu" event. Forces contextmenu to be postponed until
+       * Handles a document-wide "contextmenu" event. Forces contextmenu to be postponed until
        * mouseup is not raised.
        * 
        * @param    {Event} evt
@@ -705,7 +722,7 @@
       }
 
       /**
-       * Handles a document widen "mousemove" event and dispatches artificial "drag" events.
+       * Handles a document-wide "mousemove" event and dispatches artificial "drag" events.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -748,25 +765,21 @@
             var dx = Math.abs(mouseEventDispatcher.mouseDownEvent.x - evt.x);
             var dy = Math.abs(mouseEventDispatcher.mouseDownEvent.y - evt.y);
             
-            // only start dragging when the distance since mouse down is equal or greater 8 pixels
+            // only start dragging when mouse was moved 8 or more pixels from the mouse down point
             dragging = dx >= 8 || dy >= 8;
-            if(dragging)
-            {
-               //console.log("mouseMoveHandler: drag ON: " + evt.x + " " + evt.y);
-            }
          }
 
          if(dragging)
          {
             //console.log("mouseMoveHandler: dispatch drag event: " + evt.x + " " + evt.y);
             dispatchEvent("drag", evt);
+            // Disable MOVE event generation is other mouse event handlers
+            evt.preventDefault();
          }
-
-         evt.preventSendToApplication = true;
       }
       
       /**
-       * Handles a document widen "dragover" event.
+       * Handles a document-wide "dragover" event.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -782,7 +795,7 @@
       }
       
       /**
-       * Handles a document widen "drop" event.
+       * Handles a document-wide "drop" event.
        * 
        * @param    {Event} evt
        *           The raised mouse event.
@@ -1587,8 +1600,13 @@
          {
             // cannot use an array here because some indexes are negative numbers
             this.mouseAwareWidgets[mouseOp] = {};
+            // FIXME: replace the expression by switch 
             if (mouseOp !== "drag" && mouseOp !== "click" && mouseOp !== "dblclick" && 
-                mouseOp !== "mouseup" && mouseOp !== "mousedown" && mouseOp !== "contextmenu")
+                mouseOp !== "mouseup" && mouseOp !== "mousedown" && mouseOp !== "contextmenu"
+                // These enter/leave are generated by mousemove, we do not need these events
+                && mouseOp !== "mouseenter" && mouseOp !== "mouseleave"
+                && mouseOp !== "mouseover" && mouseOp !== "mouselout"
+               )
             {
                this.canvas.addEventListener(mouseOp, this.raiseMouseEvent(this, mouseOp));
             }
@@ -1909,11 +1927,6 @@
       
       return function(evt)
       {
-         if(evt.preventSendToApplication)
-         {
-            return;
-         }
-         
          var mousePos = wThis.getMousePos(evt);
 
          if (!me.canProcessOsEvent(evt, wThis))
@@ -3425,11 +3438,6 @@
       return this.drawCache[md5];
    }
 
-   Window.prototype.sendExplicitMouseEvent = function(wid, evt, mousePos, wThis, opCode)
-   {
-      this.sendMouseEvent(evt, mousePos, wThis, opCode, wid);
-   }
-   
    /**
     * Send a mouse event to be processed by the Java side.
     *
@@ -3441,18 +3449,77 @@
     *          The window reference.
     * @param   {Number} opCode
     *          The ID of this mouse event.
+    * @param   {Number} wid
+    *          Widget ID
     */
    Window.prototype.sendMouseEvent = function(evt, mousePos, wThis, opCode, wid)
    {
-      var mouseEvent = {
-                           windowId   : wThis.id,
-                           opCode     : opCode,
-                           wid        : wid,
-                           x          : mousePos.x,
-                           y          : mousePos.y,
-                           button     : evt.which,
-                           clicks     : evt.detail
+      const mouseEvent = {
+                          windowId   : wThis.id,
+                          opCode     : opCode,
+                          wid        : wid,
+                          x          : mousePos.x,
+                          y          : mousePos.y,
+                          clicks     : evt.detail
                         };
+
+      // Set the output event button field:
+      // 
+      // For mouse press/release/click browser provides correct button values, so we get it from event and
+      // store in a field.
+      //
+      // In all other cases the browser does not provide a value, so we compute the value based on the
+      // previous button press stack.
+
+      const button = evt.button;
+
+      switch (opCode) {
+      case 4:   // mousedown
+      case 10:  // mouseup
+          // Collect all currently pressed mouse buttons to a set to facilitate further access.
+          const buttons = evt.buttons;
+          const buttonsDown = new Set();
+          for (let i = 0, mask = 1; i < 10; i++, mask <<= 1)
+          {
+             if (buttons & mask)
+             {
+                 // Note: button numeration in 'buttons' and 'button' is slightly different.
+                 buttonsDown.add(i == 1 ? 2 : i == 2 ? 1 : i);
+             }
+          }
+          
+          // Sync the stack with actual buttons (our button press info may be incorrect if the user managed to
+          // press or release buttons outside the workspace area) and remove this event button from the stack.
+          const stack = mouseButtonDownStack.filter(function(e) { return e !== button && buttonsDown.has(e) })
+          if (opCode == 4)
+          {
+              stack.push(button);
+          }
+          
+          // console.debug("mousedown/mouseup: opCode: " + opCode
+          //               + " evt.buttons: " + evt.buttons
+          //               + " evt.button: " + evt.button
+          //               + " stack before: " + mouseButtonDownStack
+          //               + " stack after: " + stack
+          //              );
+          mouseButtonDownStack = stack;
+          mouseEvent.button = button + 1;
+          break;
+      case 1:   // mouseclick
+          mouseEvent.button = button + 1;
+          break;
+      default: // events other than press/release/click
+          const len = mouseButtonDownStack.length;
+          mouseEvent.button = len > 0 ? (mouseButtonDownStack[len - 1] + 1) : 0;
+      }
+
+      console.debug("sendMouseEvent: opCode: " + opCode
+                    + " evt.buttons: " + evt.buttons
+                    + " evt.button: " + evt.button
+                    + " mouseEvent.button: " + mouseEvent.button
+                    + " stack: " + mouseButtonDownStack
+                    + " wid: " + wid
+                   );
       
       // fix mouse click count
       if (p2j.isIe && (opCode == mouseOps["click"]) && (evt.detail == 0))
@@ -3460,7 +3527,28 @@
          mouseEvent.clicks = 1;
       }
       
-      mouseEvent.bitmask = getModifiersBitMask(evt);
+      // Get modifiers: The keys modifier mask that represents which
+      // of the modifier keys are down. If 0, 1, 2, or 3-bit is set,
+      // then respectivelly the shift, the ctrl, the meta or the alt
+      // key is pressed.
+      var modifiers = 0;
+      if (evt.shiftKey !== undefined && evt.shiftKey)
+      {
+         modifiers |= 1;
+      }
+      if (evt.ctrlKey !== undefined && evt.ctrlKey)
+      {
+         modifiers |= 2;
+      }
+      if (evt.metaKey !== undefined && evt.metaKey)
+      {
+         modifiers |= 4;
+      }
+      if (evt.altKey !== undefined && evt.altKey)
+      {
+         modifiers |= 8;
+      }
+       mouseEvent.bitmask = modifiers;
       
       var wheelEvent = (evt.deltaMode !== undefined);
       
@@ -3471,8 +3559,9 @@
          mouseEvent.rotation = getScrollAmountDelta(evt);
          mouseEvent.unit     = getScrollAmountUnit(evt);
       }
-      //console.debug("sendMouseEvent " + JSON.stringify(mouseEvent));
-      //console.debug("event name: " + getMouseEventName(mouseEvent));
+
+      // console.debug("sendMouseEvent: " + JSON.stringify(mouseEvent));
+      // console.debug("event name: " + getMouseEventName(mouseEvent));
       // send the mouse event
       p2j.socket.sendMouseEvent(mouseEvent);
       
@@ -3482,40 +3571,6 @@
    }
    
    /**
-    * Returns the bit mask that represents the current modifier keys are pressed during this
-    * mouse event occurred.
-    * 
-    * @param    {Event} evt
-    *           The mouse event.
-    * 
-    * @return   The keys modifier mask that represents which of the modifier keys are down.
-    *           If 0, 1, 2, or 3-bit is set, then respectivelly the shift, the ctrl, the meta or
-    *           the alt key is pressed.
-    */
-   function getModifiersBitMask(evt)
-   {
-      var modifiers = 0;
-      if (evt.shiftKey !== undefined && evt.shiftKey)
-      {
-         modifiers |= 1;
-      }
-      if (evt.ctrlKey !== undefined && evt.ctrlKey)
-      {
-         modifiers |= 2;
-      }
-      if (evt.metaKey !== undefined && evt.metaKey)
-      {
-         modifiers |= 4;
-      }
-      if (evt.altKey !== undefined && evt.altKey)
-      {
-         modifiers |= 8;
-      }
-      
-      return modifiers;
-   }
-   
-   /**
     * Gets the mouse event name for the given event. It has a debug purpose only.
     * 
     * @param    evt
@@ -5793,18 +5848,18 @@
    var WM_MOUSEMOVE = 512;
    
    /**
-    * Handles mouse move on the screen and preserves the last event.
+    * If the widget with ID provided as argument is registered with MessageBlaster, send WM_MOUSEMOVE
+    * event to the widget.
     *
+    * @param   wid
+    *          A valid widget ID
+    * @param   mousePos
+    *          Mouse position
     * @param   evt
     *          The mouse move event.
     */
-   me.checkMessageBlasterEvent = function(wid, mousePos, evt, mop)
+   me.checkMessageBlasterEvent = function(wid, mousePos, evt)
    {
-      if (wid == null)
-      {
-         return;
-      }
-      
       // low-level os notifications
       var winId = me.wgListMB.get(wid);
       if (winId != null)

=== modified file 'src/com/goldencode/p2j/ui/client/widget/AbstractContainer.java'
--- src/com/goldencode/p2j/ui/client/widget/AbstractContainer.java	2022-03-11 20:58:47 +0000
+++ src/com/goldencode/p2j/ui/client/widget/AbstractContainer.java	2022-03-13 18:37:02 +0000
@@ -122,6 +122,7 @@
 **     ME  20211209 Fixed focus lost issue when current focus is the last entry (direct) or first (reverse). 
 **     AL2 20220127 Added representation for all selected widgets in this container.
 **     VVT 20220218 Missing @Override annotations added.
+**     VVT 20220306 findMouseSource(eventID, NativePoint) method is overriden.
 **     EVL 20220311 Fix for invalid initial current focus for non-modal windows.
 */
 
@@ -2294,6 +2295,76 @@
    }
    
    /**
+    * Find the source for mouse event.
+    * 
+    * @param eventId
+    *        the event ID to match 
+    * @param p
+    *        the mouse position relative to this widget
+    *        
+    * @return the mouse event source or {@code null} if no source found.
+    */
+   @Override
+   public Widget<O> findMouseSource(int eventId, NativePoint p)
+   {
+      // If this widget is not visible or not enabled return null
+      if (!isVisible() || !isEnabled())
+      {
+         return null;
+      }
+      
+      // offset insets if any
+      Insets insets = insets();
+      NativePoint pbody; 
+      if (insets != null && insets != Insets.ZERO_INSETS)
+      {
+         NativeInsets ninsets = screen().coordinates().insetsToNative(insets);
+         pbody = p.offset(-ninsets.left, -ninsets.top);
+      }
+      else
+      {
+         pbody = p;
+      }
+
+      List<Widget<O>> widgets = widgetsAsList(true);
+      
+      // to allow z-order awareness, process the widgets in reverse order: the top-most widgets
+      // are always last in the widget list
+      int maxIndex = widgets.size() - 1;
+      for (int i = maxIndex; i >= 0; i--)
+      {
+         Widget<O> w = widgets.get(i);
+         if (!w.isVisible()
+             || !w.isEnabled()
+             || !w.physicalBounds().contains(p))
+         {
+            continue;
+         }
+
+         Widget<O> found = w.findMouseSource(eventId, pbody.minus(w.physicalLocation()));
+         if (found != null)
+         {
+            return found;
+         }
+         
+         // FIXME vvt check how rewrite the following properly
+         
+         // MENUBAR can allocate more place than just MENUBAR string itself.
+         // If some frame is allocated in the window right under the MENUBAR,
+         // than we should properly search mouse event in the controversial area.
+         // MENUBAR string and frame cannot intersects, so the algorithm will be the following:
+         // MENUBAR will be searched earlier than frames and if it will not find the source child
+         // owning the event, than we should search the frames.
+         //         if (w instanceof Menu && !((Menu<?>)w).config().popupOnly)
+         //         {
+         //            continue;
+         //         }
+      }
+
+      return super.findMouseSource(eventId, p);
+   }
+
+   /**
     * Helper method for traversing widgets in the container.
     *
     * @param   iter

=== modified file 'src/com/goldencode/p2j/ui/client/widget/TitledWindow.java'
--- src/com/goldencode/p2j/ui/client/widget/TitledWindow.java	2022-02-15 12:19:45 +0000
+++ src/com/goldencode/p2j/ui/client/widget/TitledWindow.java	2022-03-09 16:17:30 +0000
@@ -281,21 +281,6 @@
    }
 
    /**
-    * Find the widget to which the given mouse event can be applied.
-    * 
-    * @param    evt
-    *           The mouse event.
-    * 
-    * @return   A widget over which the mouse is positioned or {@code null} if none found.
-    *           By default, this method returns {@code null}.
-    */
-   public Widget<O> findMouseSource(MouseEvt evt)
-   {
-      // no-op by default
-      return null;
-   }
-   
-   /**
     * Process key event.
     * 
     * @param   event

=== modified file 'src/com/goldencode/p2j/ui/client/widget/Widget.java'
--- src/com/goldencode/p2j/ui/client/widget/Widget.java	2022-02-18 17:35:06 +0000
+++ src/com/goldencode/p2j/ui/client/widget/Widget.java	2022-03-06 21:49:25 +0000
@@ -99,6 +99,11 @@
 **     EVL 20210820 Adding methods to get initial widget width, height and whether to use initial values on
 **                  layout.
 **     VVT 20220218 Javadocs fixes.
+**     VVT 20220306 findMouseSource(eventID, NativePoint) method is overriden.
+**     VVT 20220306 new findMouseSource(w.findMouseSource(eventID, NativePoint)) method introduced instead
+**                  of obsolete findMouseSource(w.findMouseSource(eventID)) along with default
+**                  implementation.  This method returns 'this' iff widget is visible and enabled and mouse
+**                  event type matches mouse actions defined in this widget. Otherwise null is returned.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -1247,4 +1252,28 @@
    {
       return false;
    }
+
+   /**
+    * Find the source for mouse event.
+    * 
+    * @param eventId
+    *        the event ID to match 
+    * @param p
+    *        the mouse position relative to this widget
+    *        
+    * @return the mouse event source or {@code null} if no source found.
+    */
+   default Widget<O> findMouseSource(int eventId, NativePoint p)
+   {
+      // By default this method returns either this object or null.
+      // 
+      // This method returns 'this' iff all these conditions are true:
+      // 1. this widget is visible and enabled
+      // 2. mouse event type matches mouse actions defined in this widget
+      return (isVisible()
+               && isEnabled()
+               && getMouseActions().contains(eventId)) ? this : null;
+   }
+
+   
 }

