PreviousUpNext

15.4.1440  src/lib/x-kit/widget/gui/guiboss-event-dispatch.pkg

## guiboss-event-dispatch.pkg
#
# A support library for 
#
#     src/lib/x-kit/widget/gui/guiboss-imp.pkg
#
# This code used to live in it, but guiboss-imp.pkg was getting too big,
# so event-dispatch moved into this dedicated support package.
#
# Our primary responsibilities here are:
#
#  *  Dispatching incoming X events to the appropriate gadget,
#     determined primarily by which visible gadget is positioned
#     under the click coordinate.
#
#  *  Tracking which gadget has the mouse focus and synthesizing
#     enter/leave events when the focus moves from one gadget to
#     another gadget.

# Compiled by:
#     src/lib/x-kit/widget/xkit-widget.sublib


stipulate
    include package   threadkit;                                # threadkit                             is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
#   package ap  =  client_to_atom;                              # client_to_atom                        is from   src/lib/x-kit/xclient/src/iccc/client-to-atom.pkg
#   package au  =  authentication;                              # authentication                        is from   src/lib/x-kit/xclient/src/stuff/authentication.pkg
#   package cpm =  cs_pixmap;                                   # cs_pixmap                             is from   src/lib/x-kit/xclient/src/window/cs-pixmap.pkg
#   package cpt =  cs_pixmat;                                   # cs_pixmat                             is from   src/lib/x-kit/xclient/src/window/cs-pixmat.pkg
#   package dy  =  display;                                     # display                               is from   src/lib/x-kit/xclient/src/wire/display.pkg
#   package fil =  file__premicrothread;                        # file__premicrothread                  is from   src/lib/std/src/posix/file--premicrothread.pkg
#   package fti =  font_index;                                  # font_index                            is from   src/lib/x-kit/xclient/src/window/font-index.pkg
#   package r2k =  xevent_router_to_keymap;                     # xevent_router_to_keymap               is from   src/lib/x-kit/xclient/src/window/xevent-router-to-keymap.pkg
#   package mtx =  rw_matrix;                                   # rw_matrix                             is from   src/lib/std/src/rw-matrix.pkg
#   package rop =  ro_pixmap;                                   # ro_pixmap                             is from   src/lib/x-kit/xclient/src/window/ro-pixmap.pkg
#   package rw  =  root_window;                                 # root_window                           is from   src/lib/x-kit/widget/lib/root-window.pkg
#   package rwv =  rw_vector;                                   # rw_vector                             is from   src/lib/std/src/rw-vector.pkg
#   package sep =  client_to_selection;                         # client_to_selection                   is from   src/lib/x-kit/xclient/src/window/client-to-selection.pkg
#   package shp =  shade;                                       # shade                                 is from   src/lib/x-kit/widget/lib/shade.pkg
#   package sj  =  socket_junk;                                 # socket_junk                           is from   src/lib/internet/socket-junk.pkg
#   package x2s =  xclient_to_sequencer;                        # xclient_to_sequencer                  is from   src/lib/x-kit/xclient/src/wire/xclient-to-sequencer.pkg
#   package tr  =  logger;                                      # logger                                is from   src/lib/src/lib/thread-kit/src/lib/logger.pkg
#   package tsr =  thread_scheduler_is_running;                 # thread_scheduler_is_running           is from   src/lib/src/lib/thread-kit/src/core-thread-kit/thread-scheduler-is-running.pkg
#   package u1  =  one_byte_unt;                                # one_byte_unt                          is from   src/lib/std/one-byte-unt.pkg
#   package v1u =  vector_of_one_byte_unts;                     # vector_of_one_byte_unts               is from   src/lib/std/src/vector-of-one-byte-unts.pkg
#   package v2w =  value_to_wire;                               # value_to_wire                         is from   src/lib/x-kit/xclient/src/wire/value-to-wire.pkg
#   package wg  =  widget;                                      # widget                                is from   src/lib/x-kit/widget/old/basic/widget.pkg
#   package wi  =  window;                                      # window                                is from   src/lib/x-kit/xclient/src/window/window.pkg
#   package wme =  window_map_event_sink;                       # window_map_event_sink                 is from   src/lib/x-kit/xclient/src/window/window-map-event-sink.pkg
#   package wpp =  client_to_window_watcher;                    # client_to_window_watcher              is from   src/lib/x-kit/xclient/src/window/client-to-window-watcher.pkg
#   package wy  =  widget_style;                                # widget_style                          is from   src/lib/x-kit/widget/lib/widget-style.pkg
#   package xc  =  xclient;                                     # xclient                               is from   src/lib/x-kit/xclient/xclient.pkg
#   package xj  =  xsession_junk;                               # xsession_junk                         is from   src/lib/x-kit/xclient/src/window/xsession-junk.pkg
#   package xtr =  xlogger;                                     # xlogger                               is from   src/lib/x-kit/xclient/src/stuff/xlogger.pkg
    #

    #
    package evt =  gui_event_types;                             # gui_event_types                       is from   src/lib/x-kit/widget/gui/gui-event-types.pkg
    package gts =  gui_event_to_string;                         # gui_event_to_string                   is from   src/lib/x-kit/widget/gui/gui-event-to-string.pkg
    package gt  =  guiboss_types;                               # guiboss_types                         is from   src/lib/x-kit/widget/gui/guiboss-types.pkg
    package gtj =  guiboss_types_junk;                          # guiboss_types_junk                    is from   src/lib/x-kit/widget/gui/guiboss-types-junk.pkg

    package a2r =  windowsystem_to_xevent_router;               # windowsystem_to_xevent_router         is from   src/lib/x-kit/xclient/src/window/windowsystem-to-xevent-router.pkg

    package gd  =  gui_displaylist;                             # gui_displaylist                       is from   src/lib/x-kit/widget/theme/gui-displaylist.pkg

    package pp  =  standard_prettyprinter;                      # standard_prettyprinter                is from   src/lib/prettyprint/big/src/standard-prettyprinter.pkg

    package err =  compiler::error_message;                     # compiler                              is from   src/lib/core/compiler/compiler.pkg
                                                                # error_message                         is from   src/lib/compiler/front/basics/errormsg/error-message.pkg

    package bt  =  gui_to_sprite_theme;                         # gui_to_sprite_theme                   is from   src/lib/x-kit/widget/theme/sprite/gui-to-sprite-theme.pkg
    package ct  =  gui_to_object_theme;                         # gui_to_object_theme                   is from   src/lib/x-kit/widget/theme/object/gui-to-object-theme.pkg
    package wt  =  widget_theme;                                # widget_theme                          is from   src/lib/x-kit/widget/theme/widget/widget-theme.pkg

    package boi =  spritespace_imp;                             # spritespace_imp                       is from   src/lib/x-kit/widget/space/sprite/spritespace-imp.pkg
    package cai =  objectspace_imp;                             # objectspace_imp                       is from   src/lib/x-kit/widget/space/object/objectspace-imp.pkg
    package pai =  widgetspace_imp;                             # widgetspace_imp                       is from   src/lib/x-kit/widget/space/widget/widgetspace-imp.pkg

    #    
    package gtg =  guiboss_to_guishim;                          # guiboss_to_guishim                    is from   src/lib/x-kit/widget/theme/guiboss-to-guishim.pkg

    package b2s =  spritespace_to_sprite;                       # spritespace_to_sprite                 is from   src/lib/x-kit/widget/space/sprite/spritespace-to-sprite.pkg
    package c2o =  objectspace_to_object;                       # objectspace_to_object                 is from   src/lib/x-kit/widget/space/object/objectspace-to-object.pkg

    package s2s =  sprite_to_spritespace;                       # sprite_to_spritespace                 is from   src/lib/x-kit/widget/space/sprite/sprite-to-spritespace.pkg
    package o2o =  object_to_objectspace;                       # object_to_objectspace                 is from   src/lib/x-kit/widget/space/object/object-to-objectspace.pkg

    package g2p =  gadget_to_pixmap;                            # gadget_to_pixmap                      is from   src/lib/x-kit/widget/theme/gadget-to-pixmap.pkg

#   package frm =  frame;                                       # frame                                 is from   src/lib/x-kit/widget/leaf/frame.pkg

    package idm =  id_map;                                      # id_map                                is from   src/lib/src/id-map.pkg
    package im  =  int_red_black_map;                           # int_red_black_map                     is from   src/lib/src/int-red-black-map.pkg
#   package is  =  int_red_black_set;                           # int_red_black_set                     is from   src/lib/src/int-red-black-set.pkg

    package r8  =  rgb8;                                        # rgb8                                  is from   src/lib/x-kit/xclient/src/color/rgb8.pkg
    package r64 =  rgb;                                         # rgb                                   is from   src/lib/x-kit/xclient/src/color/rgb.pkg
    package g2d =  geometry2d;                                  # geometry2d                            is from   src/lib/std/2d/geometry2d.pkg
    package g2j =  geometry2d_junk;                             # geometry2d_junk                       is from   src/lib/std/2d/geometry2d-junk.pkg

    package ebi =  millboss_imp;                                # millboss_imp                          is from   src/lib/x-kit/widget/edit/millboss-imp.pkg
    package e2g =  millboss_to_guiboss;                         # millboss_to_guiboss                   is from   src/lib/x-kit/widget/edit/millboss-to-guiboss.pkg

    tracefile   =  "widget-unit-test.trace.log";

    nb = log::note_on_stderr;                                   # log                                   is from   src/lib/std/src/log.pkg

Dummy1 = ebi::Millboss_Option;                          # XXX SUCKO DELETEME. This is a quick hack to make sure the package compiles during early development of it.

herein

    package guiboss_event_dispatch
    :       Guiboss_Event_Dispatch                                                                                              # Guiboss_Event_Dispatch        is from   src/lib/x-kit/widget/gui/guiboss-event-dispatch.api
    {
        Dummy = Int;

        #################################################################################
        # guiboss interface fns::
        #
        #
        stipulate
            Appropriate_Gadget_Imp_Info                                                                                         # Return value type for    find_appropriate_gadget_imp_info().
              #
              =    APPROPRIATE_GADGET (gt::Gadget_Imp_Info, g2d::Point)                                                         # The g2d::Point is the query point transformed into the appropriate local coordinate system for the gadget.
              | NO_APPROPRIATE_GADGET                       g2d::Point
              ;

            fun find_appropriate_gadget_imp_info                                                                                # Which widget did the user click on?  This is usually easy but it might be in a scrollable view in a tabbed view in another scrollable view, say.
                  (
                    me:                         gt::Guiboss_State,                                                              # 
                    hostwindow_info:            gt::Hostwindow_Info,
                    event_point
                  )
                =
                {
                    subwindow_infos                                                                                             #
                        =
                        gtj::return_all_subwindow_infos_in_descending_stacking_order
                            #
                            *hostwindow_info.subwindow_info;

                    search_subwindow_infos_in_descending_stacking_order  subwindow_infos
                    where
                        fun search_subwindow_infos_in_descending_stacking_order  []
                                =>
                                NO_APPROPRIATE_GADGET event_point;                                                              # Tried everything, no hits, give up.

                            search_subwindow_infos_in_descending_stacking_order  (subwindow_info ! rest)                        # Try topmost remaining popup first.
                                =>
                                {   site   =  gtj::subwindow_info_site_in_basewindow_coordinates  subwindow_info;
                                    #
                                    if (g2d::box::point_in_box (event_point, site))
                                        #
                                        find_appropriate_gadget_imp_info_for_guipane  subwindow_info;                           # Found the right popup (or base) running gui, see if we hit any widgets within it.
                                    else
                                        search_subwindow_infos_in_descending_stacking_order  rest;                              # event_point is not within this running gui, so try next one.
                                    fi;
                                };
                        end;
                    end;                
                }
                where
                    fun find_appropriate_gadget_imp_info_for_guipane                                                            # Which widget did the user click on?  This is usually easy but it might be in a scrollable view in a tabbed view in another scrollable view, say.
                          (
                            subwindow_info:             gt::Subwindow_Info                                                      # This will be the subwindow_info for either the topllevel running gui on the hostwindow, or else the running gui for one of its popups.
                          )
                        =
                        case (*subwindow_info.guipane)
                            #
                            THE (guipane:       gt::Guipane)
                                =>
                                {   event_point
                                        =
                                        event_point
                                        -
                                        (gtj::subwindow_info_upperleft_in_base_window_coordinates subwindow_info);                      #
                                    #
                                    find_gadget_imp_info (guipane.rg_widget, event_point);
                                }
                                where
                                    fun try_all_widgets ([]: List(gt::Rg_Widget_Type),  point: g2d::Point)
                                            =>
                                            NO_APPROPRIATE_GADGET point;

                                        try_all_widgets (row_or_col_widget ! rest,  point)
                                            =>
                                            case (find_gadget_imp_info  (row_or_col_widget, point))
                                                #
                                                NO_APPROPRIATE_GADGET _ =>  try_all_widgets (rest,  point);                     # Try the remaining widgets in ROW/COL/FRAME/whatever.
                                                info =>  info;                                                                  # Got it.
                                            esac;
                                    end

                                    also
                                    fun try_all_rows ([]: List(List(gt::Rg_Widget_Type)),  point: g2d::Point)
                                            =>
                                            NO_APPROPRIATE_GADGET point;

                                        try_all_rows (row ! rest,  point)
                                            =>
                                            case (try_all_widgets (row, point))
                                                #
                                                NO_APPROPRIATE_GADGET _ =>  try_all_rows (rest,  point);                        # Try the remaining widgets in ROW/COL/FRAME/whatever.
                                                info =>  info;                                                                  # Got it.
                                            esac;
                                    end

                                    also        
                                    fun find_gadget_imp_info
                                          (
                                            rg_widget:          gt::Rg_Widget_Type,
                                            point:              g2d::Point
                                          )
                                        =
                                        if (g2d::box::point_in_box  (point,  gtj::rg_widget_site rg_widget))
                                            #
                                            case rg_widget
                                                #
                                                gt::RG_ROW r =>   try_all_widgets ( r.widgets,  point);
                                                gt::RG_COL r =>   try_all_widgets ( r.widgets,  point);

                                                gt::RG_GRID r =>  try_all_rows    ( r.widgets,  point);
                                                gt::RG_MARK r =>  try_all_widgets ([r.widget],  point);


                                                gt::RG_SCROLLPORT r
                                                    =>
                                                    {   point = point - g2d::box::upperleft(*r.site);                           # Transform mouseclick point into          view coordinate system.
                                                        point = point - *r.upperleft;                                           # Transform mouseclick point into scrolled view coordinate system.
                                                        #
                                                        find_gadget_imp_info (*r.rg_widget, point);                             # Recursively search for target widget of mouseclick among widgets in scrollable view.
                                                    };  

                                                gt::RG_TABPORT r
                                                    =>
                                                    {   point = point - g2d::box::upperleft(*r.site);                           # Transform mouseclick point into view coordinate system.
                                                        #
                                                        this_tab = list::nth (r.tabs, *r.visible_tab);

                                                        find_gadget_imp_info (this_tab.rg_widget, point);                       # Recursively search for target widget of mouseclick among widgets on visible tab.
                                                    };

                                                gt::RG_FRAME r
                                                    =>
                                                    try_all_widgets ([ r.widget, r.frame_widget ], point);

                                                gt::RG_WIDGET (r as { guiboss_to_widget, ... })
                                                    =>
                                                    {   imps = *me.gadget_imps;
                                                        #
                                                        id   = guiboss_to_widget.id;

                                                        case (idm::get (imps, id))
                                                            #
                                                            NULL =>  NO_APPROPRIATE_GADGET point ;
                                                            #
                                                            THE gadget_imp_info                                                 # Return 'point' transformed into correct coordinate system for widget.
                                                                =>
                                                                case (*gadget_imp_info.point_in_gadget)
                                                                    #
                                                                    NULL   =>                 APPROPRIATE_GADGET (gadget_imp_info, point);      # No   point_in_gadget fn supplied by gadget, so count mouseclick as having hit the gadget.
                                                                    THE fn =>   if (fn point) APPROPRIATE_GADGET (gadget_imp_info, point);      # Have point_in_gadget fn supplied by the gadget decide whether the mouseclick was close enough to count.
                                                                                else       NO_APPROPRIATE_GADGET (                 point);
                                                                                fi;
                                                                esac;
                                                        esac;
                                                    };

                                                gt::RG_OBJECTSPACE r
                                                    =>
                                                    NO_APPROPRIATE_GADGET point; # TBD

                                                gt::RG_SPRITESPACE r
                                                    =>
                                                    NO_APPROPRIATE_GADGET point; # TBD

                                                gt::RG_NULL_WIDGET
                                                    =>
                                                    NO_APPROPRIATE_GADGET point;
                                            esac;
                                        else
                                            NO_APPROPRIATE_GADGET point;
                                        fi;
                                end;
                            #    
                            NULL => NO_APPROPRIATE_GADGET event_point;                                                          # Maybe we should do:   log::fatal "find_appropriate_gadget_imp_info' called while gui not running!";
                        esac;
                end;
        herein                                                                                                                  # 

            fun do_motion_notify                                                                                                # Mouse has moved. Inform any relevant gadgets.
                  (
                    me:                         gt::Guiboss_State,
                    theme:                      wt::Widget_Theme,
                    hostwindow_info:            gt::Hostwindow_Info,
                    motion_xevtinfo:            evt::Motion_Xevtinfo
                  )
                =
                {   mouse_is = me.mouse_is;
                    #
                    case *mouse_is
                        #
                        gt::CROSSING_NONGADGET                                                                                  # Here the mouse is crossing from non-gadget pixels into gadget pixels.
                            =>
                            case (find_appropriate_gadget_imp_info (me, hostwindow_info, motion_xevtinfo.event_point))
                                #
                                APPROPRIATE_GADGET (gadget_imp_info, event_point)                                               # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                                    =>
                                    {   gadget_imp_info -> { guiboss_to_gadget,
                                                             gadget_mode,
                                                             ...
                                                           };

                                        # Remember gadget now has mouse focus:
                                        #
                                      (*gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                        gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };

                                        guiboss_to_gadget.note_mouse_transit                                                    # Notify gadget that mouse has entered its space.
                                          {
                                            transit             => gt::CAME,
                                            modifier_keys_state => motion_xevtinfo.modifier_keys_state,
                                            event_point,
                                            site                => *gadget_imp_info.site,
                                            theme
                                          };
                                        guiboss_to_gadget.note_mouse_transit                                                    # Notify gadget that mouse has entered its space.
                                          {
                                            transit             => gt::MOVE,                                                    # We send a MOVE after every CAME, for the convenience of handlers interested only in coordinates -- they can process all MOVE events and ignore CAME and LEFT events.
                                            modifier_keys_state => motion_xevtinfo.modifier_keys_state,
                                            event_point,
                                            site                => *gadget_imp_info.site,
                                            theme
                                          };

                                        mouse_is := gt::CROSSING_GADGET { gadget_imp_info };                                    # Remember which widget mouse is in.

                                    };  

                                NO_APPROPRIATE_GADGET event_point
                                    => 
                                    ();                                                                                         # Nothing to do in this case.
                            esac;

                        gt::CROSSING_GADGET  { gadget_imp_info => (last_gadget_imp_info as { guiboss_to_gadget => last_guiboss_to_gadget, ... }) }
                            =>
                            case (find_appropriate_gadget_imp_info (me, hostwindow_info, motion_xevtinfo.event_point))
                                #
                                APPROPRIATE_GADGET (gadget_imp_info, event_point)                                               # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                                    =>
                                    {   gadget_imp_info -> { guiboss_to_gadget,
                                                             gadget_mode,
                                                             ...
                                                           };

                                        if (gtj::same_gadget_imp_info (gadget_imp_info, last_gadget_imp_info))
                                            #                                                                                   # Here mouse is continuing to move on same gadget it was on at last report.
                                            guiboss_to_gadget.note_mouse_transit                                                # Notify gadget that mouse has entered its space.
                                              {
                                                transit                 => gt::MOVE,                                            # 
                                                modifier_keys_state     => motion_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                    => *gadget_imp_info.site,
                                                theme
                                              };

                                        else                                                                                    # Here mouse has crossed from one gadget to another.

                                            # Remember that last gadget no longer has mousefocus:
                                            #
                                          (*last_gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                            last_gadget_imp_info.gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                            # Remember that new gadget now has mousefocus:
                                            #
                                          (*gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                            gadget_imp_info.gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };


                                            last_guiboss_to_gadget.note_mouse_transit                                           # Notify last gadget that mouse has left its space.
                                              {
                                                transit                 => gt::LEFT,
                                                modifier_keys_state     => motion_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                    => *last_gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::CROSSING_GADGET { gadget_imp_info };                                # Remember that we're now on the new gadget.
                                            #
                                            guiboss_to_gadget.note_mouse_transit                                                # Notify new gadget that mouse has entered its space.
                                              {
                                                transit                 => gt::CAME,
                                                modifier_keys_state     => motion_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                    => *gadget_imp_info.site,
                                                theme
                                              };
                                            guiboss_to_gadget.note_mouse_transit                                                # Notify gadget that mouse has entered its space.
                                              {
                                                transit                 => gt::MOVE,                                            # We send a MOVE after every CAME, for the convenience of handlers interested only in coordinates -- they can process all MOVE events and ignore CAME and LEFT events.
                                                modifier_keys_state     => motion_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                    => *gadget_imp_info.site,
                                                theme
                                              };
                                        fi;
                                    };

                                NO_APPROPRIATE_GADGET event_point
                                    => 
                                    {   last_gadget_imp_info -> { guiboss_to_gadget,
                                                                  gadget_mode,
                                                                  ...
                                                                };

                                        # Remember gadget no longer has mousefocus:
                                        #
                                      (*gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                        gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };


                                        guiboss_to_gadget.note_mouse_transit                                                    # Notify the gadget that we were on that mouse has left its space.
                                          {
                                            transit             => gt::LEFT,
                                            modifier_keys_state => motion_xevtinfo.modifier_keys_state,
                                            event_point,
                                            site                => *last_gadget_imp_info.site,
                                            theme
                                          };

                                        mouse_is := gt::CROSSING_NONGADGET;
                                    };

                            esac;

                        gt::DRAGGING                                                                                            # Mouse is being dragged -- drag started on this gadget.
                            {
                              gadget_imp_info,                                                                                  # This is the gadget on which the drag started.  It gets all the MOVE events until drag terminates, even if mouse leaves the window area owned by the gadget. (But we only send DRAGs while cursor is in the dragged gadget.)
                              start_point,                                                                                      # This is the window coordinate of the downclick which started this drag.
                              last_point,                                                                                       # This is the window coordinate of the last motion event for this drag.
                              guipane_offset                                                                                    # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                            }
                            =>
                            {   event_point = motion_xevtinfo.event_point + guipane_offset;
                                #
                                gadget_imp_info ->    { guiboss_to_gadget,
                                                        ...
                                                      };

                                guiboss_to_gadget.note_mouse_drag_event                                                         # 
                                  {                                                                                             # 
                                    phase               => gt::DRAG,
                                    button              => *me.last_button_changed,
                                    modifier_keys_state => motion_xevtinfo.modifier_keys_state,
                                    mousebuttons_state  => motion_xevtinfo.mousebuttons_state,
                                    event_point,
                                    start_point,
                                    last_point,
                                    site                => *gadget_imp_info.site,
                                    theme
                                  };

                                mouse_is := gt::DRAGGING                                                                        # Remember location of last DRAG event.
                                              {
                                                gadget_imp_info,                                                                # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
                                                start_point,                                                                    # This is the window coordinate of the downclick which started this drag.
                                                last_point => event_point,                                                      # This is the window coordinate of the last DRAG event for this drag.
                                                guipane_offset                                                                  # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                                              };

                            };

# This is the old code, which only generates DRAG events when the mouse
# is within the gadget in which the drag started.  I'm keeping it for now
# in case we turn out to have a need for it.
#
#                           case (find_appropriate_gadget_imp_info (me, hostwindow_info, motion_xevtinfo.event_point))
#                               #
#                               APPROPRIATE_GADGET (gadget_imp_info, event_point)                                               # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
#                                   =>
#                                   {   gadget_imp_info -> { guiboss_to_gadget,
#                                                            gadget_mode,
#                                                            ...
#                                                          };
#
#                                       if (gtj::same_gadget_imp_info (gadget_imp_info, dragged_gadget_imp_info))               # If cursor is outside gadget in which drag started, we do absolutely nothing.
#                                           #
#                                           guiboss_to_gadget.note_mouse_drag_event                                             # 
#                                             {                                                                                 # 
#                                               phase           => gt::DRAG,
#                                               button          => *me.last_button_changed,
#                                               modifier_keys_state     => motion_xevtinfo.modifier_keys_state,
#                                               mousebuttons_state      => motion_xevtinfo.mousebuttons_state,
#                                               event_point,
#                                               start_point,
#                                               last_point,
#                                               site            => *gadget_imp_info.site,
#                                               theme
#                                             };
#
#                                           mouse_is := gt::DRAGGING                                                            # Remember location of last DRAG event.
#                                                         {
#                                                           gadget_imp_info,                                                    # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
#                                                           start_point,                                                        # This is the window coordinate of the downclick which started this drag.
#                                                           last_point => event_point,                                          # This is the window coordinate of the last DRAG event for this drag.
#                                                           guipane_offset                                                      # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
#                                                         };
#                                       fi;
#                                   };
#
#                               NO_APPROPRIATE_GADGET event_point
#                                   => 
#                                   ();                                                                                         # Mouse is not in the gadget that was downclicked so we completely ignore the motion event.
#                           esac;

                    esac;
                };


            fun do_button_press                                                                                                 # 
                  (
                    me:                         gt::Guiboss_State,
                    theme:                      wt::Widget_Theme,
                    hostwindow_info:            gt::Hostwindow_Info,
                    button_xevtinfo:            evt::Button_Xevtinfo
                  )
                =
                {   me.last_button_changed  := button_xevtinfo.mouse_button;                                                    # Remember this for drag_fn client functions.
                    #
                    case (find_appropriate_gadget_imp_info (me, hostwindow_info, button_xevtinfo.event_point))
                        #
                        APPROPRIATE_GADGET (gadget_imp_info, event_point)                                                       # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                            =>
                            {
                                mouse_is = me.mouse_is;
                                #
                                gadget_imp_info -> { guiboss_to_gadget,
                                                     gadget_mode,
                                                     ...
                                                   };

                                case *mouse_is
                                    #
                                    gt::CROSSING_NONGADGET
                                        =>
                                        {
                                            # Remember gadget now has mousefocus:
                                            #
                                          (*gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                            gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };

                                            guiboss_to_gadget.note_mouse_transit                                                # Tell gadget that mouse has entered its space.
                                              {
                                                transit             => gt::CAME,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            guiboss_to_gadget.note_mouse_drag_event                                             # Tell gadget that user just started a drag operation on it.
                                              {                                                                                 # 
                                                phase               => gt::OPEN,
                                                button              => *me.last_button_changed,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                event_point,
                                                start_point         => event_point,
                                                last_point          => event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            guiboss_to_gadget.note_mouse_drag_event                                             # We send a DRAG after every OPEN, for the convenience of handlers interested only in coordinates -- they can process all DRAG events and ignore OPEN and DONE events.
                                              {                                                                                 # 
                                                phase               => gt::DRAG,
                                                button              => *me.last_button_changed,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                event_point,
                                                start_point         => event_point,
                                                last_point          => event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::DRAGGING                                                            # Remember which widget mouse is in. SHOULD GENERATE AN 'ENTER' HERE.
                                                          {
                                                            gadget_imp_info,
                                                            start_point    => event_point,
                                                            last_point     => event_point,
                                                            guipane_offset => event_point - button_xevtinfo.event_point
                                                          };
                                            #
                                        };      

                                    gt::CROSSING_GADGET  { gadget_imp_info => (last_gadget_imp_info as { guiboss_to_gadget => last_guiboss_to_gadget, ... }) }
                                        =>
                                        {
                                            if (not (gtj::same_gadget_imp_info (gadget_imp_info, last_gadget_imp_info)))        # If we just left a gadget, tell it so.
                                                #

                                                # Remember that last gadget no longer has mousefocus:
                                                #
                                              (*last_gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                                last_gadget_imp_info.gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                                # Remember that new gadget now has mousefocus:
                                                #
                                              (*gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                                gadget_imp_info.gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };

                                                last_guiboss_to_gadget.note_mouse_transit                                       # Notify last gadget that we were on that mouse has left its space.
                                                  {
                                                    transit             => gt::LEFT,
                                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                    event_point,
                                                    site                => *last_gadget_imp_info.site,
                                                    theme
                                                  };

                                                guiboss_to_gadget.note_mouse_transit                                            # Notify new gadget that mouse has entered its space.
                                                  {
                                                    transit             => gt::CAME,
                                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                    event_point,
                                                    site                => *gadget_imp_info.site,
                                                    theme
                                                  };
                                            fi;

                                            guiboss_to_gadget.note_mouse_drag_event                                             # Tell gadget that user just started a drag operation on it.
                                              {                                                                                 # 
                                                phase               => gt::OPEN,
                                                button              => *me.last_button_changed,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                event_point,
                                                start_point         => event_point,
                                                last_point          => event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            guiboss_to_gadget.note_mouse_drag_event                                             # We send a DRAG after every OPEN, for the convenience of handlers interested only in coordinates -- they can process all DRAG events and ignore OPEN and DONE events.
                                              {                                                                                 # 
                                                phase               => gt::DRAG,
                                                button              => *me.last_button_changed,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                event_point,
                                                start_point         => event_point,
                                                last_point          => event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::DRAGGING                                                            # Remember which widget mouse is in.
                                                          {
                                                            gadget_imp_info,
                                                            start_point    => event_point,
                                                            last_point     => event_point,
                                                            guipane_offset => event_point - button_xevtinfo.event_point
                                                          };
                                            #
                                        };      

                                    gt::DRAGGING                                                                                # Mouse is being dragged -- drag started on this gadget.
                                        {
                                          gadget_imp_info => draggee,                                                           # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
                                          start_point,                                                                          # This is the window coordinate of the downclick which started this drag.
                                          last_point,                                                                           # This is the window coordinate of the last motion event for this drag.
                                          guipane_offset                                                                        # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                                        }
                                        =>
                                        if (gtj::same_gadget_imp_info (gadget_imp_info, draggee))                               # If we are still on the gadget being dragged...
                                            #
                                            guiboss_to_gadget.note_mouse_drag_event                                             # Tell the gadget where the mouse now is.
                                              {                                                                                 # 
                                                phase               => gt::DRAG,
                                                button              => *me.last_button_changed,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                event_point,
                                                start_point,
                                                last_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::DRAGGING                                                            # Remember new 'last_point' for dragged gadget.
                                                              {
                                                                gadget_imp_info,
                                                                start_point,
                                                                last_point  => event_point,
                                                                guipane_offset
                                                              };
                                        fi;
                                esac;
                                #       

                                # Following note_mousebutton_event stuff is completely independent of drag/motion stuff.

                                guiboss_to_gadget.note_mousebutton_event
                                  {
                                    mousebutton_event   => gt::MOUSEBUTTON_PRESS,
                                    mouse_button        => button_xevtinfo.mouse_button,
                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                    mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                    event_point,
                                    site                => *gadget_imp_info.site,
                                    theme
                                  };
                            };

                        NO_APPROPRIATE_GADGET event_point
                            =>
                            {
                                mouse_is = me.mouse_is;
                                #
                                case *mouse_is
                                    #
                                    gt::CROSSING_NONGADGET
                                        =>
                                        ();

                                    gt::CROSSING_GADGET  { gadget_imp_info }
                                        =>
                                        {   gadget_imp_info -> { guiboss_to_gadget, gadget_mode, ... };
                                            #

                                            # Remember that gadget no longer has mousefocus:
                                            #
                                          (*gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                            gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                            guiboss_to_gadget.note_mouse_transit                                                # Notify last gadget that we were on that mouse has left its space.
                                              {
                                                transit             => gt::LEFT,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::CROSSING_NONGADGET;
                                        };      

                                    gt::DRAGGING                                                                                # Mouse is being dragged on this gadget.
                                        {
                                          gadget_imp_info => dragged_gadget_imp_info,                                           # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
                                          start_point,                                                                          # This is the window coordinate of the downclick which started this drag.
                                          last_point,                                                                           # This is the window coordinate of the last motion event for this drag.
                                          guipane_offset                                                                        # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                                        }
                                        =>
                                        ();                                                                                     # When a drag is in progress, we do nothing if we are not on the dragged gadget.
                                esac;
                            };
                    esac;
                };

            fun do_button_release                                                                                               # Private.
                  (
                    me:                         gt::Guiboss_State,
                    theme:                      wt::Widget_Theme,
                    hostwindow_info:            gt::Hostwindow_Info,
                    button_xevtinfo:            evt::Button_Xevtinfo
                  )
                =
                {   me.last_button_changed              := button_xevtinfo.mouse_button;                                        # Remember this for drag_fn client functions.
                    #
                    case (find_appropriate_gadget_imp_info (me, hostwindow_info, button_xevtinfo.event_point))
                        #
                        APPROPRIATE_GADGET (gadget_imp_info, event_point)                                                       # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                            =>
                            {   gadget_imp_info -> { guiboss_to_gadget, ... };
                                #
                                mouse_is = me.mouse_is;

                                case *mouse_is
                                    #
                                    gt::CROSSING_NONGADGET
                                        =>
                                        {   gadget_imp_info -> { guiboss_to_gadget,
                                                                 gadget_mode,
                                                                 ...
                                                               };
                                            #

                                            # Remember gadget now has mousefocus:
                                            #
                                          (*gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                            gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };

                                            guiboss_to_gadget.note_mouse_transit                                                # Notify new gadget that mouse has entered its space.
                                              {
                                                transit             => gt::CAME,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site                => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::CROSSING_GADGET { gadget_imp_info };
                                            #
                                        };      

                                    gt::CROSSING_GADGET  { gadget_imp_info => gadget_imp_info2 }                                # This case should not actually be possible...?  How can we be in gt::CROSSING_GADGET mode with a mousebutton down?
                                        =>
                                        if (not (gtj::same_gadget_imp_info (gadget_imp_info, gadget_imp_info2)))                # If we just left a gadget, tell it so.
                                            #
                                            gadget_imp_info2 -> { guiboss_to_gadget => guiboss_to_gadget2, ... };

                                            # Remember that last gadget no longer has mousefocus:
                                            #
                                          (*gadget_imp_info2.gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                            gadget_imp_info2.gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                            # Remember that new gadget now has mousefocus:
                                            #
                                          (*gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                            gadget_imp_info.gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };


                                            guiboss_to_gadget2.note_mouse_transit                                               # Notify last gadget that we were on that mouse has left its space.
                                              {
                                                transit         => gt::LEFT,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site            => *gadget_imp_info2.site,
                                                theme
                                              };

                                            guiboss_to_gadget.note_mouse_transit                                                # Notify new gadget that mouse has entered its space.
                                              {
                                                transit         => gt::CAME,
                                                modifier_keys_state     => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site            => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::CROSSING_GADGET { gadget_imp_info };
                                        fi;

                                    gt::DRAGGING                                                                                # Mouse is being dragged -- drag started on this gadget.
                                        {
                                          gadget_imp_info => gadget_imp_info2,                                                  # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
                                          start_point,                                                                          # This is the window coordinate of the downclick which started this drag.
                                          last_point,                                                                           # This is the window coordinate of the last motion event for this drag.
                                          guipane_offset                                                                        # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                                        }
                                        =>
                                        {
                                            buttons = button_xevtinfo.mousebuttons_state;
                                            #
                                            if (evt::pressed_mousebutton_count buttons == 1)                                    # 'buttons' is state BEFORE the release, so if pressed-button count is 1, all buttons are now released.
                                                #                                                                               # All mouse buttons are now released, so drag operation is over
                                                gadget_imp_info2 -> { guiboss_to_gadget => guiboss_to_gadget2, ... };

                                                guiboss_to_gadget2.note_mouse_drag_event                                        # Tell the drag gadget that drag operation is complete.
                                                  {                                                                             # 
                                                    phase               => gt::DONE,
                                                    button              => *me.last_button_changed,
                                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                    mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                    event_point,
                                                    start_point,
                                                    last_point,
                                                    site                => *gadget_imp_info2.site,
                                                    theme
                                                  };

                                                if (not (gtj::same_gadget_imp_info (gadget_imp_info, gadget_imp_info2)))        # If we just left a gadget, tell it so.
                                                    #

                                                    # Remember that last gadget no longer has mousefocus:
                                                    #
                                                  (*gadget_imp_info2.gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                                    gadget_imp_info2.gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                                    # Remember that new gadget now has mousefocus:
                                                    #
                                                  (*gadget_imp_info.gadget_mode) -> { has_mouse_focus => _,    is_active, has_keyboard_focus };
                                                    gadget_imp_info.gadget_mode  := { has_mouse_focus => TRUE, is_active, has_keyboard_focus };

                                                    guiboss_to_gadget2.note_mouse_transit                                       # Notify drag gadget that we were on that mouse has left its space.
                                                      {
                                                        transit             => gt::LEFT,
                                                        modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                        event_point,
                                                        site                => *gadget_imp_info2.site,
                                                        theme
                                                      };

                                                    guiboss_to_gadget.note_mouse_transit                                        # Notify new gadget that mouse has entered its space.
                                                      {
                                                        transit             => gt::CAME,
                                                        modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                        event_point,
                                                        site                => *gadget_imp_info.site,
                                                        theme
                                                      };
                                                fi;

                                                mouse_is := gt::CROSSING_GADGET { gadget_imp_info };                            # Remember that drag operation is complete.

                                            else                                                                                # We still have some mouse buttons down.

                                                if (gtj::same_gadget_imp_info (gadget_imp_info, gadget_imp_info2))              # If we are still on the drag gadget
                                                    #
                                                    guiboss_to_gadget.note_mouse_drag_event                                     # Update the drag gadget with mouse location and buttonstate.
                                                      {                                                                         # 
                                                        phase               => gt::DRAG,
                                                        button              => *me.last_button_changed,
                                                        modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                        mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                        event_point,
                                                        start_point,
                                                        last_point,
                                                        site                => *gadget_imp_info.site,
                                                        theme
                                                      };
                                                fi;

                                                mouse_is := gt::DRAGGING                                                        # Remember new 'last_point' for dragged gadget.
                                                                  {
                                                                    gadget_imp_info,
                                                                    start_point,
                                                                    last_point  => event_point,
                                                                    guipane_offset
                                                                  };
                                            fi;
                                        };      
                                esac;
                                #       
                                guiboss_to_gadget.note_mousebutton_event                                                        # note_mousebutton_event is completely independent of drag/motion stuff.
                                  {
                                    mousebutton_event           => gt::MOUSEBUTTON_RELEASE,
                                    mouse_button                => button_xevtinfo.mouse_button,
                                    modifier_keys_state         => button_xevtinfo.modifier_keys_state,
                                    mousebuttons_state          => button_xevtinfo.mousebuttons_state,
                                    event_point,
                                    site                        => *gadget_imp_info.site,
                                    theme
                                  };

                            };

                        NO_APPROPRIATE_GADGET event_point
                            =>
                            {
                                mouse_is = me.mouse_is;
                                #
                                case *mouse_is
                                    #
                                    gt::CROSSING_NONGADGET
                                        =>
                                        ();

                                    gt::CROSSING_GADGET  { gadget_imp_info }
                                        =>
                                        {   gadget_imp_info -> { guiboss_to_gadget, gadget_mode, ... };
                                            #

                                            # Remember that gadget no longer has mousefocus:
                                            #
                                          (*gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                            gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                            guiboss_to_gadget.note_mouse_transit                                                # Notify last gadget that we were on that mouse has left its space.
                                              {
                                                transit         => gt::LEFT,
                                                modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                event_point,
                                                site            => *gadget_imp_info.site,
                                                theme
                                              };

                                            mouse_is := gt::CROSSING_NONGADGET;
                                        };      

                                    gt::DRAGGING                                                                                # Mouse is being dragged on this gadget.
                                        {
                                          gadget_imp_info,                                                                      # This is the gadget on which the drag started.  It gets all the motion events until drag terminates, even if mouse leaves the window area owned by the gadget.
                                          start_point,                                                                          # This is the window coordinate of the downclick which started this drag.
                                          last_point,                                                                           # This is the window coordinate of the last motion event for this drag.
                                          guipane_offset                                                                        # Add this to points in basewindow coordinate system to convert them to guipane coordinate system that the gadget expects.
                                        }
                                        =>
                                        {   buttons = button_xevtinfo.mousebuttons_state;
                                            #
                                            gadget_imp_info -> { guiboss_to_gadget, gadget_mode, ... };

                                            if (evt::pressed_mousebutton_count buttons == 1)                                    # Tell the drag gadget that drag operation is complete.
                                                #

                                                # Remember that gadget no longer has mousefocus:
                                                #
                                              (*gadget_mode) -> { has_mouse_focus => _,     is_active, has_keyboard_focus };
                                                gadget_mode  := { has_mouse_focus => FALSE, is_active, has_keyboard_focus };

                                                guiboss_to_gadget.note_mouse_drag_event                                         # Notify the drag gadget that the drag operation is now complete.
                                                  {                                                                             # 
                                                    phase               => gt::DONE,
                                                    button              => *me.last_button_changed,
                                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                    mousebuttons_state  => button_xevtinfo.mousebuttons_state,
                                                    event_point         => last_point,                                          # event_point is not within drag gadget, so re-use old point here. Apps probably should not use this value, but some will likely ignore the OPEN/DRAG/DONE flag and blindly process all event_points.
                                                    start_point,
                                                    last_point,
                                                    site                => *gadget_imp_info.site,
                                                    theme
                                                  };

                                                guiboss_to_gadget.note_mouse_transit                                            # Notify drag gadget that mouse has left its space.
                                                  {
                                                    transit             => gt::LEFT,
                                                    modifier_keys_state => button_xevtinfo.modifier_keys_state,
                                                    event_point,
                                                    site                => *gadget_imp_info.site,
                                                    theme
                                                  };

                                                mouse_is := gt::CROSSING_NONGADGET;
                                            fi;                                                                                 # No 'else' here because as long as we're dragging we generate no Gadget_Transit events.
                                        };
                                esac;
                            };                                                                                                  # NO_APPROPRIATE_GADGET case._
                    esac;
                };

            fun do_key_press                                                                                                    # Private.
                  (
                    me:                         gt::Guiboss_State,
                    theme:                      wt::Widget_Theme,
                    hostwindow_info:            gt::Hostwindow_Info,
                    key_xevtinfo:               evt::Key_Xevtinfo
                  )
                =
                {
                    if TRUE
                        case *me.keyboard_focus                                                                                 # This code implements click-to-type keystroke handling.  Click-to-type works much better with the emacs tradition of using 'C-x o' etc to move keyboard focus around,
                            #                                                                                                   # since it removes the presumption that keystrokes always go to the widget under the mouse cursor.
                            THE gadget_imp_info
                                =>
                                {
                                    gadget_imp_info -> { guiboss_to_gadget, ... };
                                    #
                                    keychar = (string::length_in_bytes key_xevtinfo.ascii == 1)
                                                ?? string::get_byte_as_char(key_xevtinfo.ascii, 0)
                                                :: '\0';

                                    guiboss_to_gadget.note_key_event
                                      {
                                        keystroke
                                          =>
                                          { key_event           => gt::KEY_PRESS,
                                            keycode             => key_xevtinfo.keycode,
                                            keysym              => key_xevtinfo.keysym,
                                            keystring           => key_xevtinfo.ascii,
                                            keychar,
                                            modifier_keys_state => key_xevtinfo.modifier_keys_state,
                                            mousebuttons_state  => key_xevtinfo.mousebuttons_state
                                        },
                                        site                    => *gadget_imp_info.site,
                                        theme
                                      };        
                                };

                            NULL => ();
                        esac;
                    else                                                                                                        # Using 'if TRUE' is better than commenting the following out because it helps prevent bitrot -- the below code gets typechecked before being removed as dead code.
                        case (find_appropriate_gadget_imp_info (me, hostwindow_info, key_xevtinfo.event_point))                 # This code implements keyboard-focus-follows-mouse keystroke handling, which was phased out in favor of the above.
                            #                                                                                                   # I'm leaving this code in place because we may want to make this optionally selectable behavior at some point.
                            APPROPRIATE_GADGET (gadget_imp_info, event_point)                                                   # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                                =>
                                {
                                    gadget_imp_info -> { guiboss_to_gadget, ... };
                                    #
                                    keychar = (string::length_in_bytes key_xevtinfo.ascii == 1)
                                                ?? string::get_byte_as_char(key_xevtinfo.ascii, 0)
                                                :: '\0';

                                    guiboss_to_gadget.note_key_event
                                      {
                                        keystroke
                                          =>
                                          { key_event           => gt::KEY_PRESS,
                                            keycode             => key_xevtinfo.keycode,
                                            keysym              => key_xevtinfo.keysym,
                                            keystring           => key_xevtinfo.ascii,
                                            keychar,
                                            modifier_keys_state => key_xevtinfo.modifier_keys_state,
                                            mousebuttons_state  => key_xevtinfo.mousebuttons_state
                                         },
                                        site                    => *gadget_imp_info.site,
                                        theme
                                      };        
                                };

                            NO_APPROPRIATE_GADGET _
                                =>
                                {
                                };
                        esac;
                    fi;
                };

            fun do_key_release                                                                                                  # Private.
                  (
                    me:                         gt::Guiboss_State,
                    theme:                      wt::Widget_Theme,
                    hostwindow_info:            gt::Hostwindow_Info,
                    key_xevtinfo:               evt::Key_Xevtinfo
                  )
                =
                {
                    if TRUE
                        case *me.keyboard_focus                                                                                 # This code implements click-to-type keystroke handling.  Click-to-type works much better with the emacs tradition of using 'C-x o' etc to move keyboard focus around,
                            #                                                                                                   # since it removes the presumption that keystrokes always go to the widget under the mouse cursor.
                            THE gadget_imp_info                                                                                 # I'm leaving this code in place because we may want to make this optionally selectable behavior at some point.
                                =>
                                {
                                    gadget_imp_info -> { guiboss_to_gadget, ... };
                                    #
                                    keychar = (string::length_in_bytes key_xevtinfo.ascii == 1)
                                                ?? string::get_byte_as_char(key_xevtinfo.ascii, 0)
                                                :: '\0';

                                    guiboss_to_gadget.note_key_event
                                      {
                                        keystroke
                                          =>
                                          { key_event           => gt::KEY_RELEASE,
                                            keycode             => key_xevtinfo.keycode,
                                            keysym              => key_xevtinfo.keysym,
                                            keystring           => key_xevtinfo.ascii,
                                            keychar,
                                            modifier_keys_state => key_xevtinfo.modifier_keys_state,
                                            mousebuttons_state  => key_xevtinfo.mousebuttons_state
                                        },
                                        site                    => *gadget_imp_info.site,
                                        theme
                                      };        
                                };

                            NULL => ();
                        esac;
                    else                                                                                                        # Using 'if TRUE' is better than commenting the following out because it helps prevent bitrot -- the below code gets typechecked before being removed as dead code.
                        case (find_appropriate_gadget_imp_info (me, hostwindow_info, key_xevtinfo.event_point))                 # This code implements keyboard-focus-follows-mouse keystroke handling, which was phased out in favor of the above.
                            #
                            APPROPRIATE_GADGET (gadget_imp_info, event_point)                                                   # 'event_point' is button_xevtinfo.event_point transformed into correct coordinate system for gadget (null transform if no scrollports or popups are involved).
                                =>
                                {   gadget_imp_info -> { guiboss_to_gadget, ... };
                                    #
                                    keychar = (string::length_in_bytes key_xevtinfo.ascii == 1)
                                                ?? string::get_byte_as_char(key_xevtinfo.ascii, 0)
                                                :: '\0';

                                    guiboss_to_gadget.note_key_event
                                      {
                                        keystroke
                                          =>
                                          { key_event           => gt::KEY_RELEASE,
                                            keycode             => key_xevtinfo.keycode,
                                            keysym              => key_xevtinfo.keysym,
                                            keystring           => key_xevtinfo.ascii,
                                            keychar,
                                            modifier_keys_state => key_xevtinfo.modifier_keys_state,
                                            mousebuttons_state  => key_xevtinfo.mousebuttons_state
                                        },
                                        site                    => *gadget_imp_info.site,
                                        theme
                                      };        
                                };

                            NO_APPROPRIATE_GADGET _
                                =>
                                {
                                };
                        esac;
                    fi;
                };
        end;



        fun dispatch_event
              (
                arg
                as
                  ( route:              a2r::Envelope_Route,
                    event:              evt::x::Event
                  ),
                me:                     gt::Guiboss_State,
                theme:                  wt::Widget_Theme,
                hostwindow_info:                gt::Hostwindow_Info
              )
            =
            case event
                #
                evt::x::KEY_PRESS       (key_xevtinfo:    evt::Key_Xevtinfo   ) =>  do_key_press      (me, theme, hostwindow_info, key_xevtinfo   );
                evt::x::KEY_RELEASE     (key_xevtinfo:    evt::Key_Xevtinfo   ) =>  do_key_release    (me, theme, hostwindow_info, key_xevtinfo   );
                evt::x::BUTTON_PRESS    (button_xevtinfo: evt::Button_Xevtinfo) =>  do_button_press   (me, theme, hostwindow_info, button_xevtinfo);
                evt::x::BUTTON_RELEASE  (button_xevtinfo: evt::Button_Xevtinfo) =>  do_button_release (me, theme, hostwindow_info, button_xevtinfo);
                evt::x::MOTION_NOTIFY   (motion_xevtinfo: evt::Motion_Xevtinfo) =>  do_motion_notify  (me, theme, hostwindow_info, motion_xevtinfo);

                evt::x::ENTER_NOTIFY    (inout_xevtinfo: evt::Inout_Xevtinfo  )
                    =>
                    {
#                       printf "guievent_sink()/ENTER_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);
                    };

                evt::x::LEAVE_NOTIFY    (inout_xevtinfo: evt::Inout_Xevtinfo)
                    =>
                    {
#                       printf "guievent_sink()/LEAVE_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);
                    };

                evt::x::FOCUS_IN        (focus_xevtinfo: evt::Focus_Xevtinfo)
                    =>
                    printf "guievent_sink()/FOCUS_IN: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::FOCUS_OUT       (focus_xevtinfo:  evt::Focus_Xevtinfo)
                    =>
                    printf "guievent_sink()/FOCUS_OUT: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::KEYMAP_NOTIFY    { }
                    =>
                    printf "guievent_sink()/KEYMAP_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::EXPOSE           (expose_record:         evt::x::Expose_Record)
                    =>
                    printf "guievent_sink()/EXPOSE: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::GRAPHICS_EXPOSE  (graphics_expose_record: evt::x::Graphics_Expose_Record)
                    =>
                    printf "guievent_sink()/GRAPHICS_EXPOSE: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::NO_EXPOSE
                  { drawable:           evt::Drawable_Id,
                    major_opcode:               Unt,                                            # The graphics operation code.
                    minor_opcode:               Unt                                             # Always 0 for core protocol.
                  }
                    =>
                    printf "guievent_sink()/NO_EXPOSE: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::VISIBILITY_NOTIFY
                  { changed_window_id:  evt::Window_Id,                                         # The window with changed visibility state.
                    state:                      evt::Visibility                                 # The new visibility state.
                  }
                    =>
                    printf "guievent_sink()/VISIBILITY_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::CREATE_NOTIFY
                  { parent_window_id:   evt::Window_Id,                                         # The created window's parent.
                    created_window_id:  evt::Window_Id,                                         # The created window.
                    box:                        g2d::Box,                                       # The window's rectangle.
                    border_wid:         Int,                                                    # The width of the border.
                    override_redirect:  Bool                                                    #  
                  }
                    =>
                    printf "guievent_sink()/CREATE_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::DESTROY_NOTIFY
                  { event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    destroyed_window_id:        evt::Window_Id                                  # The destroyed window.
                  }
                    =>
                    printf "guievent_sink()/DESTROY_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::UNMAP_NOTIFY
                  { event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    unmapped_window_id: evt::Window_Id,                                         # The window being unmapped.
                    from_config:                Bool                                            # TRUE if parent was resized.
                  }
                    =>
                    printf "guievent_sink()/UNMAP_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::MAP_NOTIFY
                  { event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    mapped_window_id:   evt::Window_Id,                                         # The window being mapped.
                    override_redirect:  Bool                                                    #  
                  }
                    =>
                    printf "guievent_sink()/MAP_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::MAP_REQUEST
                  { parent_window_id:   evt::Window_Id,                                         # The parent.
                    mapped_window_id:   evt::Window_Id                                          # The mapped window.
                  }
                    =>
                    printf "guievent_sink()/MAP_REQUEST: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::REPARENT_NOTIFY
                  { event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    parent_window_id:           evt::Window_Id,                                 # The new parent.
                    rerooted_window_id:         evt::Window_Id,                                 # The re-rooted window.
                    upperleft_corner:   g2d::Point,                                             # The upper-left corner.
                    override_redirect:  Bool                                                    #  
                  }
                    =>
                    printf "guievent_sink()/REPARENT_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::CONFIGURE_NOTIFY
                  { event_window_id:            evt::Window_Id,                                 # The window on which this was generated.
                    configured_window_id:       evt::Window_Id,                                 # The reconfigured window.
                    sibling_window_id:  Null_Or(evt::Window_Id),                                # The sibling that window is above (if any).
                    box:                        g2d::Box,                                       # The window's rectangle.
                    border_wid:                 Int,                                            # The width of the border.
                    override_redirect:  Bool                                                    #  
                  }
                    =>
                    {
                        printf "guievent_sink()/CONFIGURE_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);
                    };

                evt::x::CONFIGURE_REQUEST
                  { parent_window_id:   evt::Window_Id,                                         # The parent.
                    configure_window_id:        evt::Window_Id,                                 # The window to reconfigure.
                    sibling_window_id:          Null_Or(evt::Window_Id),                        # The new sibling (if any).
                    x:                          Null_Or(Int),                                   # The window's rectangle.
                    y:                          Null_Or(Int),
                    wide:                       Null_Or(Int),
                    high:                       Null_Or(Int),
                    border_wid:                 Null_Or(Int),                                   # The width of the border.
                    stack_mode:                 Null_Or(evt::Stack_Mode)                        # The mode for stacking windows.
                  }
                    =>
                    printf "guievent_sink()/CONFIGURE_REQUEST: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::GRAVITY_NOTIFY
                  {
                    event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    moved_window_id:    evt::Window_Id,                                         # The window being moved.
                    upperleft_corner:   g2d::Point                                              # Upper-left corner of window.
                  }             
                    =>
                    printf "guievent_sink()/GRAVITY_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::RESIZE_REQUEST
                  {
                    resize_window_id:   evt::Window_Id,                                         # The window to resize.
                    req_size:           g2d::Size                                               # The requested new size.
                  }
                    =>
                    printf "guievent_sink()/RESIZE_REQUEST: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::CIRCULATE_NOTIFY
                  {
                    event_window_id:    evt::Window_Id,                                         # The window on which this was generated.
                    circulated_window_id:       evt::Window_Id,                                 # The window being circulated.
                    parent_window_id:   evt::Window_Id,                                         # The parent.
                    place:                      evt::Stack_Pos                                  # The new place.
                  }
                    =>
                    printf "guievent_sink()/CIRCULATE_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::CIRCULATE_REQUEST
                  {
                    parent_window_id:   evt::Window_Id,                                         # The parent.
                    circulate_window_id:        evt::Window_Id,                                 # The window to circulate.
                    place:                      evt::Stack_Pos                                  # The place to circulate the window to.
                  }
                    =>
                    printf "guievent_sink()/CIRCULATE_REQUEST: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::PROPERTY_NOTIFY
                  {
                    changed_window_id:  evt::Window_Id,                                         # The window with the changed property.
                    atom:                       evt::Atom,                                      # The affected property.
                    timestamp:                  evt::t::Xserver_Timestamp,                      # When the property was changed.
                    deleted:                    Bool                                            # TRUE if the property was deleted.
                  }
                    =>
                    printf "guievent_sink()/PROPERTY_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::SELECTION_CLEAR
                  {
                    owning_window_id:   evt::Window_Id,                                         # The current owner of the selection.
                    selection:          evt::Atom,                                              # The selection.
                    timestamp:          evt::t::Xserver_Timestamp                               # The last-change time.
                  }             
                    =>
                    printf "guievent_sink()/SELECTION_CLEAR: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::SELECTION_REQUEST
                  {
                    owning_window_id:   evt::Window_Id,                                         # The owner of the selection.
                    selection:          evt::Atom,                                              # The selection.
                    target:                     evt::Atom,                                      # The requested type for the selection.
                    requesting_window_id:       evt::Window_Id,                                 # The requesting window.
                    property:           Null_Or( evt::Atom ),                                   # The property to store the selection in. 
                    timestamp:          evt::Timestamp                                          #  
                  }
                    =>
                    printf "guievent_sink()/SELECTION_REQUEST: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::SELECTION_NOTIFY
                  {
                    requesting_window_id:       evt::Window_Id,                                 # The requestor of the selection.
                    selection:          evt::Atom,                                              # The selection.
                    target:                     evt::Atom,                                      # The requested type of the selection.
                    property:           Null_Or( evt::Atom ),                                   # The property to store the selection in.
                    timestamp:          evt::Timestamp                                          #  
                  }
                    =>
                    printf "guievent_sink()/SELECTION_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::COLORMAP_NOTIFY
                  {
                    window_id:          evt::Window_Id,                                         # The affected window.
                    cmap:                       Null_Or( evt::Colormap_Id ),                    # The colormap.
                    new:                        Bool,                                           # TRUE, if the colormap attribute is changed.
                    installed:          Bool                                                    # TRUE, if the colormap is installed.
                  }
                    =>
                    printf "guievent_sink()/COLORMAP_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::CLIENT_MESSAGE
                  {
                    window_id:          evt::Window_Id,                                         #  
                    type:                       evt::Atom,                                      # The type of the message.
                    value:                      evt::Raw_Data                                   # The message value.
                  }
                    =>
                    printf "guievent_sink()/CLIENT_MESSAGE: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::MODIFIER_MAPPING_NOTIFY                                                 # Really a MappingNotify event.
                    =>
                    printf "guievent_sink()/MODIFIER_MAPPING_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::KEYBOARD_MAPPING_NOTIFY                                                 # Really a MappingNotify event.
                  {
                    first_keycode:      evt::Keycode,
                    count:              Int
                  }
                    =>
                    printf "guievent_sink()/KEYBOARD_MAPPING_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

                evt::x::POINTER_MAPPING_NOTIFY                                                  # Really a MappingNotify event.
                    =>
                    printf "guievent_sink()/POINTER_MAPPING_NOTIFY: ignoring '%s' Gui_Event    -- guiboss-imp.pkg\n" (gts::gui_event_to_string event);

            esac;                                                                               # NB: We avoid a '_' case here because if an event is added to evt::x::Event we want a compile error as a reminder to handle it.
    };
end;





Comments and suggestions to: bugs@mythryl.org

PreviousUpNext