=== modified file 'src/com/goldencode/p2j/ui/EventDefinition.java'
--- old/src/com/goldencode/p2j/ui/EventDefinition.java	2021-10-25 09:43:25 +0000
+++ new/src/com/goldencode/p2j/ui/EventDefinition.java	2022-10-11 10:02:22 +0000
@@ -63,6 +63,8 @@
 **     CA  20210917          Javadoc fixes.
 **     CA  20211005          Set the matchedWidgetId when lookup is performed.
 **     SBI 20211024          Added containsWidget(int) method.
+**     ME  20220928          Rewrite trigger lookup, move some of the logic in EventList.
+**         20221003          Remove 'global anywhere' (derived), use scope self/anywhere on lookup.
 **/
 
 /*
@@ -125,13 +127,10 @@
 import java.util.*;
 import java.util.function.Consumer;
 
-import com.goldencode.p2j.ui.chui.*;
 import org.roaringbitmap.*;
 import org.roaringbitmap.longlong.*;
 
 import com.goldencode.p2j.util.*;
-import com.goldencode.p2j.ui.client.*;
-import com.goldencode.p2j.ui.client.widget.*;
 import com.goldencode.p2j.util.Utils;
 
 /**
@@ -177,6 +176,12 @@
 public class EventDefinition
 implements Externalizable
 {
+   /** SELF event scope */
+   public static final int SCOPE_SELF = 1;
+
+   /** ANYWHERE event scope */
+   public static final int SCOPE_ANYWHERE = 2;
+   
    /** Bitwise mask to mark if there are events in the stream.  */
    private static final byte MASK_HAS_EVENT = (byte) 0x01;
    
@@ -198,9 +203,6 @@
    /** Bitwise mask for the anywhere flag.  */
    private static final byte MASK_ANYWHERE  = (byte) 0x40;
    
-   /** Bitwise mask for the global anywhere flag.  */
-   private static final byte MASK_GLOBAL_ANYWHERE = (byte) 0x80;
-   
    /** The event ID associated with this instance IF and ONLY IF there is only 1 event. */
    private Integer event = null;   
    
@@ -222,9 +224,6 @@
    /** Widget-specific ANYWHERE option, if given. */
    private boolean anywhere = false;
 
-   /** Global ANYWHERE option, if given. */
-   private boolean globalAnywhere = false;
-   
    /** Trigger assigned to the event definition or -1 if this is an exit condition. */
    private int triggerId = -1;
 
@@ -253,7 +252,6 @@
       this.widgets        = widgets;
       this.resources      = resources;
       this.anywhere       = anywhere;
-      this.globalAnywhere = (widgets == null && resources == null);
       
       events.forEach(evt -> this.events.add(EventList.eventCode(evt).intValue()));
    }
@@ -276,7 +274,6 @@
       this.widgets        = widgets;
       this.resources      = resources;
       this.anywhere       = anywhere;
-      this.globalAnywhere = (widgets == null && resources == null);
    }
    
    /**
@@ -305,8 +302,6 @@
       
       this.event          = event;
       this.anywhere       = anywhere;
-      this.globalAnywhere = ((widget == null || widget == WidgetId.INVALID_WIDGET_ID) &&
-                             (resource == null || resource == ResourceIdHelper.INVALID_RESOURCE));
    }
    
    /**
@@ -335,8 +330,6 @@
       
       this.events         = events;
       this.anywhere       = anywhere;
-      this.globalAnywhere = ((widget == null || widget == WidgetId.INVALID_WIDGET_ID) &&
-                             (resource == null || resource == ResourceIdHelper.INVALID_RESOURCE));
    }
    
    /**
@@ -357,7 +350,6 @@
       this.widgets        = widgets;
       this.resources      = resources;
       this.anywhere       = anywhere;
-      this.globalAnywhere = (widgets == null && resources == null);
    }
    
    /**
@@ -373,7 +365,6 @@
       copyResources(ed);
       
       this.anywhere       = ed.anywhere;
-      this.globalAnywhere = ed.globalAnywhere;
       this.triggerId      = ed.triggerId;
    }
    
@@ -390,7 +381,10 @@
    EventDefinition(boolean anywhere, Integer widgetId, Integer event)
    {
       this.event   = event;
-      this.widget  = widgetId;
+      if (widgetId != null)
+      {
+         this.widget = widgetId;
+      }
       this.anywhere = anywhere;
    }
 
@@ -407,7 +401,10 @@
    EventDefinition(boolean anywhere, Long resId, Integer event)
    {
       this.event    = event;
-      this.resource = resId;
+      if (resId != null)
+      {
+         this.resource = resId;
+      }
       this.anywhere = anywhere;
    }
 
@@ -419,8 +416,8 @@
     */
    EventDefinition(Integer event)
    {
-      this.event          = event;
-      this.globalAnywhere = true;
+      this.event    = event;
+      this.anywhere = true;
    }
    
    /**
@@ -537,9 +534,6 @@
       if (anywhere != other.anywhere)
          return false;
          
-      if (globalAnywhere != other.globalAnywhere)
-         return false;
-         
       return hasSameEvents(other) && hasSameWidgets(other) && hasSameResources(other);
    }
    
@@ -689,7 +683,7 @@
     */
    public boolean isAnywhereGlobally()
    {
-      return globalAnywhere;
+      return isAnywhere() && !hasResource() && !hasWidget();
    }
 
    /**
@@ -706,20 +700,20 @@
    }
 
    /**
-    * Check if the given event name is part of this instance.
+    * Check if the given event id is part of this instance.
     *
-    * @param    name
+    * @param    id
     *           The event ID to test.
     *
-    * @return   <code>true</code> if the event has been defined for particular event name.
+    * @return   <code>true</code> if the event has been defined for particular event id.
     */
-   public boolean isEventMatch(Integer name)
+   public boolean isEventMatch(Integer id)
    {
-      if (name == null)
+      if (id == null)
          return false;
       
-      return (events != null && !events.isEmpty()) ? events.contains(name)
-                                                   : event.equals(name);
+      return (events != null && !events.isEmpty()) ? events.contains(id)
+                                                   : event.equals(id);
    }
 
    /**
@@ -742,7 +736,7 @@
     */
    public boolean hasEvent()
    {
-      return (events != null && !events.isEmpty()) || event != null;
+      return event != null || (events != null && !events.isEmpty());
    }
 
    /**
@@ -752,7 +746,7 @@
     */
    public boolean hasWidget()
    {
-      return (widgets != null && !widgets.isEmpty()) || widget != WidgetId.INVALID_WIDGET_ID;
+      return widget != WidgetId.INVALID_WIDGET_ID || (widgets != null && !widgets.isEmpty());
    }
 
    /**
@@ -762,7 +756,7 @@
     */
    public boolean hasResource()
    {
-      return (resources != null && !resources.isEmpty()) || resource != ResourceIdHelper.INVALID_RESOURCE;
+      return resource != ResourceIdHelper.INVALID_RESOURCE || (resources != null && !resources.isEmpty());
    }
 
    /**
@@ -855,8 +849,6 @@
     * Checks whether this event matches any event defined in this <code>EventDefinition</code>
     * instance.
     *
-    * @param    frameId
-    *           Frame ID, can be <code>-1</code>.
     * @param    widgetId
     *           Widget ID to check or -1 if this is a global anywhere event.
     * @param    event
@@ -864,149 +856,57 @@
     * @param    isTrigger
     *           <code>true</code> if the search is for a trigger or <code>false</code> to 
     *           seach for an exit condition.
-    * @param    specific
-    *           <code>true</code> if an exact match is to be returned.
-    * @param    universal
-    *           <code>true</code> if the event is of the universal class.
-    * @param    result
-    *           On output both trigger ID and matched widget ID may be modified.  If no match
-    *           was found, then the trigger ID will be set to -1.
+    * @param    scope
+    *           include only triggers without ANYWHERE (SELF), only ANYWHERE or both.
+    * @return   returns trigger ID or -1 if no match was found.
     */
-   public void lookup(int     frameId,
-                      int     widgetId,
+   public int lookup(int     widgetId,
                       Integer event,
                       boolean isTrigger,
-                      boolean specific,
-                      boolean universal,
-                      TriggerMatch result)
+                      int     scope)
    {
-      result.matchedWidgetId = -1;
-
       // check the search type
       if (isTrigger && triggerId == -1)
       {
          // no match; not a trigger
-         result.triggerId = -1;
-         return;
+         return -1;
       }
 
       if (!isTrigger && triggerId != -1)
       {
          // no match; not an exit condition
-         result.triggerId = -1;
-         return;
+         return -1;
       }
 
       // check if the event is listed
       if (!isEventMatch(event))
       {
-         result.triggerId = -1;
-         return;
+         return -1;
+      }
+      
+      // check if anywhere scope is valid
+      if ((scope & SCOPE_ANYWHERE) != SCOPE_ANYWHERE && anywhere) {
+         return -1;
+      }
+      
+      // check if widget only scope is valid
+      if ((scope & SCOPE_SELF) != SCOPE_SELF && !anywhere) {
+         return -1;
       }
 
       // check global anywhere case
-      if (globalAnywhere && widgetId == -1)
-      {
-         result.triggerId = isTrigger ? triggerId : 0;
-         return;
-      }
-
-      if (!hasWidget() && widgetId != -1)
-      {
-         // no match; only global anywhere
-         result.triggerId = -1;
-         return;
-      }
-
-      if (widgetId == -1)
-      {
-         // no match; only specific widgets
-         result.triggerId = -1;
-         return;
+      if (widgetId == -1 && isAnywhereGlobally())
+      {
+         return isTrigger ? triggerId : 0;
       }
 
       // check specific widget
-      if (isWidgetMatch(widgetId))
-      {
-         result.triggerId = isTrigger ? triggerId : 0;
-         result.matchedWidgetId = widgetId;
-         return;
-      }
-
-      if (specific && !anywhere) // exact match but if not anywhere
-      {
-         result.triggerId = -1;
-         return;
-      }
-
-      // check anywhere within the frame
-      if (!anywhere && !universal)
-      {
-         result.triggerId = -1;
-         return;
-      }
-
-      // WARNING: This code can currently only run on the client due to direct references to ThinClient,
-      //          Browse, TitledWindow, Window and Widget.
-      Widget<?> w = ThinClient.getInstance().registry().getComponent(widgetId);
-      Browse<?> parentBrowse = w != null ? (Browse<?>) w.parent(Browse.class) : null;
-      if (parentBrowse != null)
-      {
-         // If an event occurs for an in-browse editor, and there is a trigger for the browse column, it
-         // should be fired. But if there is not column trigger and there is an OF BROWSE br ANYWHERE trigger,
-         // it should be fired instead. This case is handled here.
-         int browseId = parentBrowse.getId().asInt();
-         if (isWidgetMatch(browseId))
-         {
-            result.triggerId = isTrigger ? triggerId : 0;
-            result.matchedWidgetId = browseId;
-            return;
-         }
-      }
-
-      if (frameId != -1)
-      {
-         if (isWidgetMatch(frameId))
-         {
-            result.triggerId = isTrigger ? triggerId : 0;
-            result.matchedWidgetId = frameId;
-            return;
-         }
-      }
-      else
-      {
-         TitledWindow<?> winInFocus = UiUtils.getCurrentFocusWindow();
-         if (winInFocus instanceof Window && winInFocus.getId().asInt() == widgetId)
-         {
-            // added events propagation from window to its frames
-            Widget<?>[] topComps = UiUtils.getTopLevelComponents((Window<?>) winInFocus);
-            
-            for (Widget<?> widget : topComps)
-            {
-               if (widget.getId() != null && isWidgetMatch(widget.getId().asInt()))
-               {
-                  result.triggerId = isTrigger ? triggerId : 0;
-                  result.matchedWidgetId = widget.getId().asInt();
-                  return;
-               }
-            }
-         }
-         else
-         {
-            // determine the frame ID based on given widget ID, consider parent frames as well
-            for (Integer parentFrameId : UiUtils.getParentFrames(widgetId))
-            {
-               if (isWidgetMatch(parentFrameId))
-               {
-                  result.triggerId = isTrigger ? triggerId : 0;
-                  result.matchedWidgetId = parentFrameId;
-                  return;
-               }
-            }
-         }
-      }
-
-      result.triggerId = -1;
+      if (widgetId != -1 && isWidgetMatch(widgetId))
+      {
+         return isTrigger ? triggerId : 0;
+      }
+
+      return -1;
    }
 
    /**
@@ -1065,12 +965,6 @@
          return false;                      // not an exit condition
       }
 
-      // check global anywhere case
-      if (globalAnywhere)
-      {
-         return false;                      // not an exit condition
-      }
-
       // check specific widget
       return isWidgetMatch(widgetId);
    }
@@ -1385,7 +1279,6 @@
       EventDefinition ned = new EventDefinition();
       ned.anywhere        = anywhere;
       ned.triggerId       = triggerId;
-      ned.globalAnywhere  = globalAnywhere;
       
       eventsIntersection(ed, ned);
       widgetsIntersection(ed, ned);
@@ -1606,7 +1499,6 @@
       if (ned1.hasEvent())
       {
          ned1.anywhere       = anywhere;
-         ned1.globalAnywhere = globalAnywhere;
          ned1.triggerId      = triggerId;
       
          widgetsIntersection(ed, ned1);
@@ -1622,7 +1514,6 @@
       if (ned2.hasWidget() || ned2.hasResource())
       {
          ned2.anywhere       = anywhere;
-         ned2.globalAnywhere = globalAnywhere;
          ned2.triggerId      = triggerId;
       
          ned2.copyEvents(this);
@@ -1723,7 +1614,6 @@
       boolean resourceSet  = (state & MASK_RESOURCE_SET) == MASK_RESOURCE_SET;
       
       anywhere             = (state & MASK_ANYWHERE) == MASK_ANYWHERE;
-      globalAnywhere       = (state & MASK_GLOBAL_ANYWHERE) == MASK_GLOBAL_ANYWHERE;
       
       triggerId = in.readInt();
       
@@ -1796,7 +1686,6 @@
       if (hasResources)   state |= MASK_HAS_RESOURCE;
       if (resourceSet)    state |= MASK_RESOURCE_SET;
       if (anywhere)       state |= MASK_ANYWHERE;
-      if (globalAnywhere) state |= MASK_GLOBAL_ANYWHERE;
       
       out.writeByte(state);
       out.writeInt(triggerId);
@@ -1847,7 +1736,7 @@
    public String toString()
    {
       return String.format("EventDefinition { events %s; widgets %s; resources %s; anywhere %b;" +
-                           " globalAnywhere %b; trigger id %d }",
+                           " trigger id %d }",
                            events != null && !events.isEmpty() ? toString(events) : "[" + toString(event) + "]",
                            widgets != null && !widgets.isEmpty()
                               ? widgets.toString()
@@ -1860,7 +1749,6 @@
                                  ? "[]" 
                                  : "[" + Long.toString(resource) + "]",
                            anywhere,
-                           globalAnywhere,
                            triggerId);
    }
    
@@ -2040,7 +1928,7 @@
          }
       }
          
-      if (globalAnywhere)
+      if (isAnywhereGlobally())
       {
          atoms.add(new EventDefinition(name));
       }

=== modified file 'src/com/goldencode/p2j/ui/EventList.java'
--- old/src/com/goldencode/p2j/ui/EventList.java	2022-09-08 16:08:25 +0000
+++ new/src/com/goldencode/p2j/ui/EventList.java	2022-10-11 10:02:22 +0000
@@ -165,6 +165,9 @@
 **     ME  20220907          Rework lookupEventHelper to consider function key, any-printable, any-key for key
 **                           events (in that order), first for specific widget then anywhere and last the frame.
 **     CA  20220907          Added isEventMatchForWidget.
+**     ME  20220928          Clean-up lookupEventHandler, reduce recursive calls.
+**         20221003          Build events bitmap and resolve function key code in lookupWorker, avoid looping 
+**                           through widget's parents chain if there is no event registered for the code.
 */
 
 /*
@@ -233,7 +236,6 @@
 
 import com.goldencode.p2j.ui.chui.ThinClient;
 import com.goldencode.p2j.ui.client.*;
-import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.widget.*;
 import com.goldencode.p2j.util.*;
 
@@ -291,9 +293,6 @@
    /** List of event definitions */
    private List<EventDefinition> events = new ArrayList<>();
 
-   /** The new order of events according to EventsPriorityComparator built on the client side*/
-   private EventIndex[] order;
-   
    /** Chain of event lists (not transferred via network).  */
    private EventList chain = null;
    
@@ -739,46 +738,6 @@
    }
    
    /**
-    * Calculates new order of events according to EventsPriorityComparator.
-    * 
-    * @param    events
-    *           The original list of event definition.
-    * 
-    * @return   The new order of events.
-    */
-   public static EventIndex[] calculatesEventsPriority(List<EventDefinition> events)
-   {
-      // The first commented method approximately performs 2*events.size()*log(events.size()) comparisons
-      
-//      PriorityQueue<EventIndex> copy = new PriorityQueue<EventList.EventIndex>(new EventsPriorityComparator(events));
-//      for (int i = 0; i < events.size(); i++)
-//      {
-//         EventIndex eventIndex = new EventIndex();
-//         eventIndex.id = i;
-//         copy.add(eventIndex);
-//      }
-//      
-//      EventIndex[] order = new EventIndex[copy.size()];
-//      int i = 0;
-//      while (!copy.isEmpty())
-//      {
-//         order[i++] = copy.poll();
-//      }
-      // The second method can be faster according to the java doc because it takes advantages of the almost sorted array.
-      EventIndex[] order = new EventIndex[events.size()];
-      for (int k = 0; k < events.size(); k++)
-      {
-         EventIndex eventIndex = new EventIndex();
-         eventIndex.id = k;
-         order[k] = eventIndex;
-      }
-     
-      Arrays.sort(order, new EventsPriorityComparator(events));
-      
-      return order;
-   }
-
-   /**
     * Calculate the event or key code for this event name.
     * 
     * @param    name
@@ -1859,8 +1818,8 @@
     * {@link #lookup(int, int, boolean, boolean, boolean, TriggerMatch)} with
     * <code>specificWidget</code> set to <code>false</code>.
     *
-    * @param    fid
-    *           Frame ID, can be <code>-1</code>
+    * @param    cid
+    *           Parent container ID, can be <code>-1</code>
     * @param    wid
     *           widget ID, can't be <code>-1</code>, but can be <code>SESSION_WINDOW_ID</code>.
     * @param    trigger
@@ -1874,9 +1833,9 @@
     *           For more information see
     *           {@link #lookup(int, int, boolean, boolean, boolean, TriggerMatch)}.
     */
-   public void lookup(int fid, int wid, boolean trigger, boolean isKey, TriggerMatch result)
+   public void lookup(int cid, int wid, boolean trigger, boolean isKey, TriggerMatch result)
    {
-      lookup(fid, wid, trigger, isKey, false, result);
+      lookup(cid, wid, trigger, isKey, false, result);
    }
 
    /**
@@ -1924,8 +1883,8 @@
     * <br>
     * If the widget is the session window, only the first step is taken.
     *
-    * @param    fid
-    *           Frame ID, can be <code>-1</code>
+    * @param    cid
+    *           Parent container ID, can be <code>-1</code>
     * @param    wid
     *           widget ID, can't be <code>-1</code>, but can be <code>SESSION_WINDOW_ID</code>.
     * @param    trigger
@@ -1934,7 +1893,7 @@
     * @param    isKey
     *           Marks real key pressed by user.
     * @param    specificWidget
-    *           When <code>true</code> only the specific widget events are lsearched, i.e.
+    *           When <code>true</code> only the specific widget events are searched, i.e.
     *           anywhere, session window, etc. are ignored.
     * @param    result
     *           On input this contains the numeric event code that is the subject of the search.
@@ -1944,14 +1903,14 @@
     *           given event code in the case of ANY-KEY or ANY-PRINTABLE).  For exit conditions,
     *           the trigger ID will be the found event index when it is found.
     */
-   public void lookup(int fid, int wid, boolean trigger, boolean isKey,
+   public void lookup(int cid, int wid, boolean trigger, boolean isKey,
                       boolean specificWidget, TriggerMatch result)
    {
-      lookupWorker(fid, wid, trigger, isKey, specificWidget, result);
+      lookupWorker(cid, wid, trigger, isKey, specificWidget, result);
 
       if (result.triggerId == -1 && chain != null)
       {
-         chain.lookup(fid, wid, trigger, isKey, result);
+         chain.lookup(cid, wid, trigger, isKey, result);
       }
    }
 
@@ -2576,8 +2535,8 @@
    /**
     * Worker routine for {@link #lookup(int, int, boolean, boolean, TriggerMatch)}.
     * 
-    * @param    fid
-    *           Frame ID, can be <code>-1</code>.
+    * @param    cid
+    *           Parent container ID, can be <code>-1</code>.
     * @param    wid
     *           Widget ID, can't be <code>-1</code>, but can be <code>SESSION_WINDOW_ID</code>.
     * @param    trig
@@ -2596,10 +2555,10 @@
     *           given event code in the case of ANY-KEY or ANY-PRINTABLE).  For exit conditions,
     *           the trigger ID will be the found event index when it is found.
     */
-   private void lookupWorker(int fid, int wid, boolean trig, boolean isKey,
+   private void lookupWorker(int cid, int wid, boolean trig, boolean isKey,
                              boolean specificWidget, TriggerMatch result)
    {
-      LookupWidgetKey key = new LookupWidgetKey(fid, wid, trig, isKey, specificWidget, result.eventId);
+      LookupWidgetKey key = new LookupWidgetKey(cid, wid, trig, isKey, specificWidget, result.eventId);
       if (lookupWidgets == null)
       {
          lookupWidgets = new HashMap<>();
@@ -2616,18 +2575,14 @@
       {
          // obtain the event name and class
          String event = Keyboard.eventName(result.eventId);
-         boolean universal = EventDefinition.isEventUniversal(result.eventId);
          
          // convert the event name to the proper case
          String ev = setCase(event);
          // convert the label to a key code, alternate labels are honored here 
          int code = Keyboard.keyCode(ev);
          
-         //calculates new priority order of events
-         order = calculatesEventsPriority(events);
-         
          // is this a valid label?
-         if (code != -1)
+         if (code != -1 && code != result.eventId)
          {
             // yes, it was a label so now convert the common code to the primary label name
             String primary = Keyboard.keyLabel(code); 
@@ -2641,42 +2596,38 @@
                                            " could not be resolved!");
          }
          
-         // lookup event for a specific widget and honor any-printable/any-key if needed
-         lookupEventHelper(-1, wid, evCode, trig, true, false, isKey, result);
-         
-         if (result.triggerId != -1)
-         {
-            return;
-         }
-   
-         if (specificWidget)
-         {
-            return;
-         }
-   
-         // lookup event for a anywhere in the frame and honor any-printable/any-key if needed
-         lookupEventHelper(fid, wid, evCode, trig, false, universal, isKey, result);
-         
-         if (result.triggerId != -1)
+         int functionCode = isKey ? Keyboard.eventCode(Keyboard.keyFunction(evCode)) : -1;
+         boolean isPrintable = isKey && Keyboard.isPrintable(ev);
+         
+         if (eventCodes == null)
+         {
+            eventCodes = new RoaringBitmap();
+            events.forEach(evt -> evt.gatherEventCodes(eventCodes));
+         }
+
+         // avoid looking-up the same event for all parent's chain if none defined
+         if (!eventCodes.contains(evCode)
+                  && (!isKey || !hasEventForKey(evCode, functionCode, isPrintable)))
+         {
+            return;
+         }
+
+         // lookup event for the widget and honor any-printable/any-key if needed (specific or anywhere)
+         if (lookupEventHelper(wid, evCode, functionCode, trig,
+                  EventDefinition.SCOPE_SELF | EventDefinition.SCOPE_ANYWHERE, isKey, isPrintable,
+                  result) != -1 || specificWidget)
+         {
+            return;
+         }
+
+         // lookup event for the parent container and honor any-printable/any-key if needed (anywhere only)
+         if (cid != -1 && lookupEventHelper(cid, evCode, functionCode, trig,
+                  EventDefinition.SCOPE_ANYWHERE, isKey, isPrintable, result) != -1)
          {
             result.matchedWidgetId = wid;
             return;
          }
          
-         // look the list up for the specified event and the containing frame
-         if (!trig || universal)
-         {
-            for (EventDefinition ed : events)
-            {
-               ed.lookup(-1, fid, evCode, trig, true, false, result);
-               if (result.triggerId != -1)
-               {
-                  result.matchedWidgetId = fid;
-                  return;
-               }
-            }
-         }
-
          ThinClient tc = ThinClient.getInstance();
          Widget<?> widget = tc.getWidget(wid);
          
@@ -2685,26 +2636,30 @@
             List<Frame<?>> parentFrames = UiUtils.getParentFrames(widget);
             
             // search the target trigger through parent frames
-            for (Frame<?> frame  : parentFrames)
+            Frame<?> rootFrame = null;
+            for (Frame<?> frame : parentFrames)
             {
                int id = frame.getId().asInt();
-               
-               if (id != wid && id != fid && frame.isVisible() && !frame.hidden())
-               {
-                  lookupEventHelper(id, wid, evCode, trig, true, false, isKey, result);
-               }
-               
-               if (result.triggerId != -1)
+
+               if (id != wid && id != cid && frame.isVisible() && !frame.hidden()
+                        && lookupEventHelper(id, evCode, functionCode, trig,
+                                 EventDefinition.SCOPE_ANYWHERE, isKey, isPrintable,
+                                 result) != -1)
                {
                   result.matchedWidgetId = wid;
                   return;
                }
+               
+               if (frame.isRootFrame()) 
+               {
+                  rootFrame = frame;
+               }
             }
-            
-            TopLevelWindow<?> win = null;
-            Frame<?> rootFrame =  UiUtils.locateRootFrame(widget);
+
             if (rootFrame != null)
             {
+               TopLevelWindow<?> win = null;
+               
                if (rootFrame.isDialog())
                {
                   // for dialog-box, consider parent windows only if its top-level window is not visible
@@ -2726,41 +2681,30 @@
                {
                   win = rootFrame.topLevelWindow();
                }
-            }
-            else if (widget instanceof TopLevelWindow)
-            {
-               win = (TopLevelWindow<?>) widget;
-            }
-
-            // search the target trigger in the window parent hierarchy up recursively
-            while (win != null && win.config() != null)
-            {
-               // note that the target window doesn't need to be visible (except dialog-box, but visible
-               // dialog-box should not reach this code block)
-               int winid = win.getId().asInt();
-               // lookup event for a specific widget and honor any-printable/any-key if needed
-               lookupEventHelper(rootFrame == null ? -1 : rootFrame.getId().asInt(),
-                                 winid, evCode, trig, true, false, isKey, result);
-
-               if (result.triggerId != -1)
+               
+               // search the target trigger in the window parent hierarchy up recursively
+               while (win != null && win.config() != null)
                {
-                  result.matchedWidgetId = winid;
-                  break;
+                  // note that the target window doesn't need to be visible (except dialog-box, but visible
+                  // dialog-box should not reach this code block)
+                  int id = win.getId().asInt();
+                  if (id != wid && id != cid
+                           && lookupEventHelper(win.getId().asInt(), evCode, functionCode, trig,
+                                    EventDefinition.SCOPE_ANYWHERE, isKey, isPrintable,
+                                    result) != -1)
+                  {
+                     return;
+                  }
+
+                  BaseConfig c = win.config();
+                  win = (TopLevelWindow<?>) tc.getWidget(c.parentId);
                }
-
-               BaseConfig c = win.config();
-               win = (TopLevelWindow<?>) tc.getWidget(c.parentId);
             }
+            
          }
-         
-         if (result.triggerId != -1)
-            return;
-         
          // lookup event for a anywhere globally and honor any-printable/any-key if needed
-         lookupEventHelper(-1, -1, evCode, trig, false, false, isKey, result);
-         
-         if (result.triggerId != -1)
-            return;
+         lookupEventHelper(-1, evCode, functionCode, trig, EventDefinition.SCOPE_ANYWHERE, isKey,
+                  isPrintable, result);
       }
       finally
       {
@@ -2862,8 +2806,6 @@
     * and then upon an ANY-PRINTABLE event and finally upon an ANY-KEY event. The first
     * match aborts further checking.
     *
-    * @param    fid
-    *           Frame ID, can be <code>-1</code>.
     * @param    wid
     *           Widget ID to check or -1 if this is a global anywhere event.
     * @param    evCode
@@ -2871,13 +2813,14 @@
     * @param    trigger  
     *           <code>true</code> if the search is for a trigger or <code>false</code> to 
     *           search for an exit condition.
-    * @param    specific
-    *           <code>true</code> if an exact match is to be returned.
-    * @param    universal
-    *           <code>true</code> if the event is of the universal class.
+    * @param    scope
+    *           include only triggers without ANYWHERE (SELF), only ANYWHERE or both.
     * @param    isKey
     *           <code>true</code> if the event was generated from the keyboard, which will
-    *           cause the ANY-PRINTABLE/ANY-KEY events to be checked.
+    *           cause the ANY-KEY events to be checked.
+    *  @param   isPrintable
+    *           <code>true</code> if the event was generated by a printable keyboard, which will
+    *           cause the ANY-PRINTABLE events to be checked.
     * @param    result
     *           On input this contains the numeric event code that is the subject of the search.
     *           On output both the event code and the trigger id may be modified.  If no match
@@ -2886,60 +2829,106 @@
     *           given event code in the case of ANY-KEY or ANY-PRINTABLE).  For exit conditions,
     *           the trigger ID will be the found event index when it is found.
     */
-   private void lookupEventHelper(int          fid,
-                                  int          wid,
-                                  Integer      evCode,
-                                  boolean      trigger,
-                                  boolean      specific,
-                                  boolean      universal,
-                                  boolean      isKey,
-                                  TriggerMatch result)
+   private int lookupEventHelper(int            wid,
+                                 int            evCode,
+                                 int            functionCode,
+                                 boolean        trigger,
+                                 int            scope,
+                                 boolean        isKey,
+                                 boolean        isPrintable,
+                                 TriggerMatch   result)
    {
-      lookupEventHelper(fid, wid, evCode, trigger, specific, universal, isKey, false, result);
+      // array for trigger ids matching in order: 
+      // 0 - key function
+      // 1 - any-printable
+      // 2 - any-key
+      // 3 - key label anywhere
+      // 4 - key function anywhere
+      // 5 - any-printable anywhere
+      // 6 - any-key anywhere
+      int[] matches = new int[] { -1, -1, -1, -1, -1, -1, -1};
+      
+      // look the list up for the specified event and widget 
+      for (EventDefinition ed: events)
+      {
+         int triggerId = ed.lookup(wid, evCode, trigger, scope);
+         
+         if (triggerId != -1) {
+            if (!ed.isAnywhere()) 
+            {
+               // exact match
+               result.triggerId = triggerId;
+               
+               return triggerId;
+            }
+            else 
+            {
+               matches[3] = triggerId; // key label anywhere
+            }
+         }
+         else if (isKey)
+         {
+            // key function: 0 - widget, 4 - anywhere
+            // if already have a match for widget no point on testing further
+            if (functionCode != -1 && functionCode != evCode && matches[0] == -1)
+            {
+               triggerId = ed.lookup(wid, functionCode, trigger, scope);
+               
+               if (triggerId != -1) {
+                  matches[ed.isAnywhere() ? 4 : 0] = triggerId;
+               }
+            }
+
+            // any-printable: 1 - widget, 5 - anywhere
+            // if already have a match for widget no point on testing further
+            if (isPrintable && evCode != Keyboard.SE_ANY_PRINTABLE && matches[0] == -1
+                     && matches[1] == -1 && (matches[5] == -1 || !ed.isAnywhere()))
+            {
+               triggerId = ed.lookup(wid, Keyboard.SE_ANY_PRINTABLE, trigger, scope);
+
+               if (triggerId != -1)
+               {
+                  matches[ed.isAnywhere() ? 5 : 1] = triggerId;
+               }
+            }
+
+            // any-key: 2 - widget, 6 - anywhere
+            // if already have a match for widget no point on testing further
+            if (evCode != Keyboard.SE_ANY_KEY && matches[0] == -1 && matches[1] == -1
+                     && matches[2] == -1 && (matches[6] == -1 || !ed.isAnywhere()))
+            {
+               triggerId = ed.lookup(wid, Keyboard.SE_ANY_KEY, trigger, scope);
+
+               if (triggerId != -1)
+               {
+                  matches[ed.isAnywhere() ? 6 : 2] = triggerId;
+               }
+            }
+         }
+      }
+               
+      
+      // no exact match
+      for (int i = 0; i < matches.length; i++)
+      {
+         if (matches[i] != -1) {
+            result.triggerId = matches[i];
+            return matches[i];
+         }
+      }
+      
+      return -1;
    }
    
    /**
-    * Handles an event definition lookup for all known events and then it may also try the
-    * same lookup for ANY-PRINTABLE and ANY-KEY events, depending on the properties of the
-    * original event.  This will match first on a specific event match (e.g. the letter "A"
-    * and then upon an ANY-PRINTABLE event and finally upon an ANY-KEY event. The first
-    * match aborts further checking.
-    *
-    * @param    fid
-    *           Frame ID, can be <code>-1</code>.
-    * @param    wid
-    *           Widget ID to check or -1 if this is a global anywhere event.
-    * @param    evCode
-    *           The event ID.
-    * @param    trigger  
-    *           <code>true</code> if the search is for a trigger or <code>false</code> to 
-    *           search for an exit condition.
-    * @param    specific
-    *           <code>true</code> if an exact match is to be returned.
-    * @param    universal
-    *           <code>true</code> if the event is of the universal class.
-    * @param    isKey
-    *           <code>true</code> if the event was generated from the keyboard, which will
-    *           cause the ANY-PRINTABLE/ANY-KEY events to be checked.
-    * @param    isKeyFunction
-    *           <code>true</code> if the event is for a key function, as opposed to key label.
-    * @param    result
-    *           On input this contains the numeric event code that is the subject of the search.
-    *           On output both the event code and the trigger id may be modified.  If no match
-    *           was found, then the trigger ID will be set to -1.  For trigger searches, this is
-    *           the matching trigger ID and matched event code (which may be different from the
-    *           given event code in the case of ANY-KEY or ANY-PRINTABLE).  For exit conditions,
-    *           the trigger ID will be the found event index when it is found.
+    * Check if there is any event definition for a specific keyboard key.
+    * 
+    * @param evCode  the key label code
+    * @param functionCode the key function code
+    * @param isPrintable flag for printable key
+    * @return <code>true</code> if an event for the keyboard key is registered
     */
-   private void lookupEventHelper(int          fid,
-                                  int          wid,
-                                  Integer      evCode,
-                                  boolean      trigger,
-                                  boolean      specific,
-                                  boolean      universal,
-                                  boolean      isKey,
-                                  boolean      isKeyFunction,
-                                  TriggerMatch result)
+   private boolean hasEventForKey(int evCode, int functionCode, boolean isPrintable)
    {
       if (eventCodes == null)
       {
@@ -2947,100 +2936,10 @@
          events.forEach(evt -> evt.gatherEventCodes(eventCodes));
       }
 
-      if (!eventCodes.contains(evCode)
-               && (!isKey || (!eventCodes.contains(Keyboard.SE_ANY_PRINTABLE)
-                        && !eventCodes.contains(Keyboard.SE_ANY_KEY))))
-      {
-         return;
-      }
-
-      // look the list up for the specified event and widget 
-      for (EventDefinition ed: events)
-      {
-         // do not use parent frame here, instead use the same widget id
-         ed.lookup(wid, wid, evCode, trigger, specific, universal, result);
-         
-         // if found check for widget match or default trigger
-         if (result.triggerId != -1 && (!specific || !ed.isAnywhere())
-                  && (wid == -1 || ed.isWidgetMatch(wid)))
-         {
-            return;
-         }
-      }
-               
-      
-      // keyboard only, look for "function key" then "any printable" and "any key" as needed
-      if (isKey && evCode != Keyboard.SE_ANY_KEY) {
-         // honor "function key" event (must be before ANY-PRINTABLE)
-         if (!isKeyFunction && evCode != Keyboard.SE_ANY_PRINTABLE)
-         {
-            int functionCode = Keyboard.eventCode(Keyboard.keyFunction(evCode));
-            
-            if (functionCode != -1 && !evCode.equals(functionCode))
-            {
-               lookupEventHelper(fid, wid, functionCode, trigger, specific, universal, isKey, true, result);
-               
-               if (result.triggerId != -1)
-               {
-                  result.eventId = functionCode;
-                  
-                  return;
-               }
-            }
-         }
-
-         // honor ANY-PRINTABLE event (must be before ANY-KEY)
-         if (evCode != Keyboard.SE_ANY_PRINTABLE)
-         {
-            if (Keyboard.isPrintable(Keyboard.eventName(evCode)))
-            {
-               lookupEventHelper(fid, wid, Keyboard.SE_ANY_PRINTABLE, trigger, specific, universal, isKey, true, result);
-               
-               if (result.triggerId != -1)
-               {
-                  result.eventId = Keyboard.SE_ANY_PRINTABLE;
-                  
-                  return;
-               }
-            }
-            
-            // look for ANY-KEY
-            if (result.eventId != Key.VK_ALT && result.eventId != Key.VK_CTRL
-                     && result.eventId != Key.VK_SHIFT)
-            {
-               
-               lookupEventHelper(fid, wid, Keyboard.SE_ANY_KEY, trigger, specific, universal, isKey, true, result);
-               
-               if (result.triggerId != -1)
-               {
-                  result.eventId = Keyboard.SE_ANY_KEY;
-                  
-                  return;
-               }
-            }
-         }
-      }
-      
-      // if specific trigger wasn't found fallback to anywhere for the widget
-      if (specific)
-      {
-         lookupEventHelper(fid, wid, evCode, trigger, false /* anywhere */, universal, isKey, isKeyFunction, result);
-      }
-      else
-      {
-         // lookup for parent frame triggers
-         if (fid != -1 && fid != wid)
-         {
-            lookupEventHelper(fid, fid, evCode, trigger, false /* anywhere */, universal, isKey, isKeyFunction, result);
-            
-            // reset back the widget id
-            if (result.triggerId != -1)
-            {
-               result.matchedWidgetId = wid;
-            }
-         }
-      }
-      
+      return eventCodes.contains(evCode)
+               || (functionCode != -1 && eventCodes.contains(functionCode))
+               || (isPrintable && eventCodes.contains(Keyboard.SE_ANY_PRINTABLE))
+               || eventCodes.contains(Keyboard.SE_ANY_KEY);
    }
    
    /**
@@ -3053,129 +2952,7 @@
       this.lookupWidgets = null;
       this.eventCodes = null;
    }
-   
-   /**
-    * Defines new priority event order based on the natural linear order of events. EventList 1 and EventList 2 are
-    * always comparable. If they have the different events, then their order positions are not changed.
-    * If they have the same events, then their order positions are swapped if each associated widget from the first list
-    * have at least one of its parents in the second event list. This new order is also a linear order. 
-    */
-   private static class EventsPriorityComparator
-   implements Comparator<EventIndex>
-   {
-      private final List<EventDefinition> events;
-      
-      public EventsPriorityComparator(List<EventDefinition> events)
-      {
-         this.events = events;
-      }
-      
-      @Override
-      public int compare(EventIndex o1, EventIndex o2)
-      {
-         if (o1.id != o2.id)
-         {
-            EventDefinition o1Event = events.get(o1.id);
-            EventDefinition o2Event = events.get(o2.id);
-            if (o1Event != null && !o1Event.isSame(o2Event) && o1Event.hasSameEvents(o2Event))
-            {
-               final boolean[] collector = new boolean[] {true, false}; 
-               o1Event.processWidgets((Integer wid) ->
-               {
-                  if (o2Event != null && collector[0])
-                  {
-                     Iterator<Integer> parentFramesIter = new ClientFrameIterator(wid.intValue());
-                     boolean contains = false;
-                     while(parentFramesIter.hasNext())
-                     {
-                        Integer pfid = parentFramesIter.next();
-                        if (o2Event.containsWidget(pfid))
-                        {
-                           contains = true;
-                           break;
-                        }
-                     };
-                     collector[0] = collector[0] && contains;
-                     collector[1] = collector[0];
-                  }
-               });
-               if (collector[1])
-               {
-                  if (o2.id <= o1.id) // swap if o2.id before o1.id 
-                  {
-                     return o2.id - o1.id;
-                  }
-               }
-            }
-         }
-         
-         return o1.id - o2.id;
-      }
-   }
-   
-   /**
-    * Defines the client side frame iterator.
-    */
-   public static class ClientFrameIterator
-   implements Iterator<Integer>
-   {     
-      private Widget curr;
-      private Frame frame;
-      
-      /**
-       * Creates this iterator over the parent's hierarchy of the given widget.
-       * 
-       * @param    wid
-       *           The given widget id
-       */
-      public ClientFrameIterator(int wid)
-      {
-         curr = ThinClient.getInstance().getWidget(wid);
-      }
-      
-      /**
-       * Returns true if the iteration has more elements.
-       *
-       * @return   True if the iteration has more elements, otherwise false.
-       */
-      @Override
-      public boolean hasNext()
-      {
-         frame = UiUtils.locateFrame(curr);
-         
-         return (frame != null);
-      }
-
-      /**
-       * Returns the next element in the iteration.
-       *
-       * @return   The next element in the iteration, or WidgetId.INVALID_WIDGET_ID if there is no 
-       *           such element.
-       */
-      @Override
-      public Integer next()
-      {
-         if (frame == null)
-         {
-            frame = UiUtils.locateFrame(curr);
-         }
-         
-         if (curr != null)
-         {
-            curr = curr.parent();
-         }
-         
-         if (frame != null)
-         {
-            return frame.getId().asInt();
-         }
-         else
-         {
-            return WidgetId.INVALID_WIDGET_ID;
-         }
-      }
-   }
-   
+      
    /**
     * Defines the index of the original event definition within its events list.
     */

=== modified file 'src/com/goldencode/p2j/ui/chui/ThinClient.java'
--- old/src/com/goldencode/p2j/ui/chui/ThinClient.java	2022-09-18 16:13:54 +0000
+++ new/src/com/goldencode/p2j/ui/chui/ThinClient.java	2022-10-11 10:02:22 +0000
@@ -2856,6 +2856,7 @@
 **     EVL 20220913          Fix for regression in non date fill-ins with screen value getting.
 **      HC 20220915          Performance: Eliminated integer auto-boxing when enqueuing widget attributes.
 **      CA 20220918          Allow legacy client sockets to be ran on FWD server instead of FWD client.
+**      ME 20220920          Use widget parent 'container' when looking up triggers.
 */
 
 /*
@@ -17253,14 +17254,14 @@
          // assume no trigger is found
          moreKeys = true;
 
-         Frame frame = UiUtils.locateFrame(focused);
+         Widget container = UiUtils.locateContainer(focused);
          int focusId = UiUtils.lookupWidgetIdAsInt(focused);
-         int frameId = UiUtils.lookupWidgetIdAsInt(frame);
+         int containerId = UiUtils.lookupWidgetIdAsInt(container);
          
          TriggerMatch result = new TriggerMatch(key.keyCode());
 
          // check if there is a trigger for this specific key
-         currentEventList.lookup(frameId, focusId, true, true, result);
+         currentEventList.lookup(containerId, focusId, true, true, result);
          
          if (result.triggerId == -1)
          {
@@ -17272,7 +17273,7 @@
                result = new TriggerMatch(defaultEvent);
                
                // check if there is a trigger for this default event
-               currentEventList.lookup(frameId, focusId, true, true, result);
+               currentEventList.lookup(containerId, focusId, true, true, result);
                
                // only if there is no other more specific match AND there is a match to 
                // a system event, do we call invoke
@@ -20606,7 +20607,7 @@
       // in the event list for an active user interface trigger
       if (key > 0 && key != KeyInput.CHAR_UNDEFINED && evt.id() != EventType.KEY_RELEASED)
       {
-         tr = invokeTriggers(src, key, false);
+         tr = invokeTriggers(src, key, evt.isRealKey());
 
          if (tr.executed)
          {
@@ -21626,7 +21627,9 @@
       }
       else if (!WidgetId.virtualWidget(widgetId))
       {
-         currentEventList.lookup(frameId, widgetId, true, isKey, result);
+         Widget container = UiUtils.locateContainer(widget);
+         
+         currentEventList.lookup(container != null && container != frame ? UiUtils.getWidgetIdAsInt(container) : frameId, widgetId, true, isKey, result);
       }
 
       // activate trigger
@@ -26153,7 +26156,7 @@
    /**
     * Applies widget attributes in the widget tree.
     *
-    * @param   widgetAttrs
+    * @param   widgetAttrsIds
     *          Flat array of widget attribute ids.
     * @param   widgetAttrValues
     *          Flat array of widget attribute values.

=== modified file 'src/com/goldencode/p2j/ui/client/UiUtils.java'
--- old/src/com/goldencode/p2j/ui/client/UiUtils.java	2022-08-10 02:18:10 +0000
+++ new/src/com/goldencode/p2j/ui/client/UiUtils.java	2022-10-11 10:02:22 +0000
@@ -110,6 +110,7 @@
 **         20220527 Improved error logging.
 **     IAS 20220803 Fixed loop condition in 'resolveLocalFileResource'.
 **     EVL 20220809 Fixed issue with buildCanonicalUrl() on Windows server installation.
+**     ME  20220928 Added two methods to get widget container and top level window.
 */
 
 /*
@@ -807,6 +808,38 @@
    }
 
    /**
+    * Return the widget which contains the specified widget. Most of the time this going to be the 
+    * frame containing the widget except for the browse column where the container is the browse. 
+    *
+    * @param    widget
+    *           Widget for which to find the container.
+    *
+    * @return   The container widget which contains specified widget, or <code>null</code>
+    *           if <code>null</code> is passed as the parameter or the widget 
+    *           is not placed in a container. 
+    */
+   public static Widget<?> locateContainer(Widget<?> widget)
+   {
+      if (widget == null)
+      {
+         return null;
+      }
+
+      if (!(widget instanceof Frame))
+      {
+         // check if parent is a browse (browse column)
+         Browse<?> browse = widget.parent(Browse.class);
+         
+         if (browse != null)
+         {
+            return browse;
+         }
+      }
+      
+      return locateFrame(widget);
+   }
+   
+   /**
     * Return the root frame which contains the specified widget. If the given 
     * widget is itself a root frame, then it will be returned.
     * 

