PreviousUpNext

15.4.1442  src/lib/x-kit/widget/gui/guiboss-popup-junk.pkg

## guiboss-popup-junk.pkg
#
# A support library for 
#
#     src/lib/x-kit/widget/gui/guiboss-imp.pkg
#
# Random code related to maintaining guiboss popups, moved here
# because guiboss-imp.pkg was getting way too big for comfort.

# 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

#   package tbi =  textmill;                                    # textmill                              is from   src/lib/x-kit/widget/edit/textmill.pkg

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

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

herein

    package guiboss_popup_junk
    :       Guiboss_Popup_Junk                                                                                                  # Guiboss_Popup_Junk    is from   src/lib/x-kit/widget/gui/guiboss-popup-junk.api
    {
        Dummy = Int;

        fun clear_box_in_pixmap                                                                                         # Clear a box to black, mostly to avoid undefined values etc.
              (
                pixmap:                 gt::Subwindow_Or_View,                                                          # pixmap holding the scrollport.
                box:                    g2d::Box                                                                        # Box in view coordinates.
              )
            =
            case pixmap
                #
                gt::SUBWINDOW_INFO  { pixmap:   Ref(g2p::Gadget_To_Rw_Pixmap), ... } => (*pixmap).draw_displaylist [ gd::COLOR (r64::black,  [ gd::FILLED_BOXES [ box ]]) ];
                gt::SCROLLABLE_INFO { pixmap:       g2p::Gadget_To_Rw_Pixmap,  ... } =>    pixmap.draw_displaylist [ gd::COLOR (r64::black,  [ gd::FILLED_BOXES [ box ]]) ];
                gt::TABBABLE_INFO   { pixmap:       g2p::Gadget_To_Rw_Pixmap,  ... } =>    pixmap.draw_displaylist [ gd::COLOR (r64::black,  [ gd::FILLED_BOXES [ box ]]) ];
            esac;

                                                                                                                        # The high-level architectural ideas here are:
                                                                                                                        #  o  Every running gui has an associated offscreen subwindow_or_view containing a complete image of its window.
                                                                                                                        #     We have one running gui for the main visible window plus one for each visible popup.
                                                                                                                        #     (Popups do not have separate X windows; they are implemented entirely client-side, drawing into the main window.)
                                                                                                                        #     The popups cover less screen space than the main visible window, so they have smaller offscreen subwindow_or_views.
                                                                                                                        #  o  Gadgets always draw to the offscreen subwindow_or_view for the running gui with which they are associated.
                                                                                                                        #  o  We update the visible window by copying rectangles from the subwindow_or_views to the visible window.
                                                                                                                        #     These copies need to respect the popup hierarchy, e.g. the main guipane must not write over screenspace belonging to any popup.
                                                                                                                        #     We never do text drawing ops or such directly onto the visible window.
                                                                                                                        #  o  Scrollable views introduce an additional level of indirection:
                                                                                                                        #      *  Each scrollable view has its own offscreen subwindow_or_view.
                                                                                                                        #      *  Widgets located on a scrollable view draw to its subwindow_or_view.
                                                                                                                        #      *  We copy rectangles from the scrollable-view subwindow_or_view to its parent subwindow_or_view.
                                                                                                                        #         (We never copy directly from the scrollable-view subwindow_or_view to the visible window.)
                                                                                                                        #      *  Scrollable-view stuff then becomes visible when the parent subwindow_or_view is copied to the visible window.
                                                                                                                        #      *  It is reasonable for a scrollable view to contain another scrollable view, so the above should be understood recursively.
                                                                                                                        #
                                                                                                                        # Nomenclature: "blit" is from "BitBLT" == "bit-boundary block transfer" -- originally a Xerox Alto graphics op, now informal jargon term. For more info:   http://en.wikipedia.org/wiki/Bit_blit
                                                                                                                        # Because we allow nested scrollable subwindows of main hostwindow for app,
                                                                                                                        # it is nontrivial to figure out where on the hostwindow a given gadget is
                                                                                                                        # visible -- if it is visible at all! -- and how much is visible, and what
                                                                                                                        # pixel rectangle to copy from where to where to update the gadget's on-
                                                                                                                        # screen image.  That's our job here.
        fun update_offscreen_parent_pixmaps_and_then_hostwindow
              (
                pixmap:                         gt::Subwindow_Or_View,
                from_box:                       g2d::Box,                                                               # From-box in source pixmap coordinates.
                hostwindow_for_gui:             gtg::Guiboss_To_Hostwindow
              )
            =
            {
                propagate_frombox_changes_to_all_parents_in_which_they_are_visible
                    #
                    (pixmap, from_box);                                                                                 # Copy visible part of gadget to the backing pixmap for the scrollport containing it, the backing pixmap for the scrollport containing that scrollport etc and finally the visible hostwindow itself.
            }                                                                                                           # Typically there will be no scrollports involved, so this will be just a blit to the hostwindow.
            where
                fun find_frombox_parts_not_hidden_by_popups
                      (
                        subwindow_or_view:              gt::Subwindow_Or_View,
                        from_box:                       g2d::Box                                                        # We assume this is in local subwindow_or_view coordinates (not basewindow coordinates).
                      )
                    =
                    {
                                                                                                                        # The redraw logic expects the from_box info to be in 'subwindow_or_view' coordinates, not basewindow coords.
                                                                                                                        # But to compare from_box with the possibly shading running-gui pixmaps, they must all be in the samme coordinate system.
                                                                                                                        # Consequently we transform the possibly_shading_pixmaps into our local 'subwindow_or_view' coordinates.
                        # Now we figure out which parts of 'from_box'
                        # are not hidden by popup windows:

                        bp = gtj::subwindow_info_of_subwindow_or_view  subwindow_or_view;                               # This returns 'r' from   subwindow_or_view as (SUBWINDOW_INFO r).   "bp" == "subwindow_or_view".

                        possibly_shadowing_pixmaps                                                                      # Get the popup windows which are maybe shadowing us (that is, which we might clobber if we drew all of from_box)
                            =
                            gtj::find_all_subwindow_infos_above_given_subwindow_or_view_in_stacking_order
                                #
                                subwindow_or_view;

                        possibly_shadowing_sites                                                                        # Convert the possibly_shadowing_pixmaps to boxes in basewindow coordinates.
                            =
                            (map gtj::subwindow_info_site_in_basewindow_coordinates                                     # This adds in the 'upperleft' fields of all our parents.
                                #
                                possibly_shadowing_pixmaps
                            )
                            :   List( g2d::Box );

                        bp_upperleft_in_basewindow_coordinates
                            =
                            g2d::box::upperleft
                              (
                                gtj::subwindow_info_site_in_basewindow_coordinates  bp
                              );

                        possibly_shadowing_sites                                                                        # Convert shadowing sites from basewindow coords to 'subwindow_or_view' coords.
                            =
                            (map translate_site_to_our_coordinate_system
                                #
                                possibly_shadowing_sites                                                                # 
                            )
                            where
                                fun translate_site_to_our_coordinate_system (box: g2d::Box)
                                    =
                                    {   (g2d::box::upperleft_and_size  box)
                                            ->
                                            (box_upperleft, size);

                                        g2d::box::make  (box_upperleft - bp_upperleft_in_basewindow_coordinates,  size);
                                    };
                            end;

                        visible_parts_of_frombox                                                                        # Subtract the boxes from from_box to see what parts of from_box it is actually safe to draw (if any).
                            =
                            g2d::box::subtract_boxes_b_from_boxes_a
                              {
                                a => [ from_box ],
                                b => possibly_shadowing_sites
                              };

                        visible_parts_of_frombox;                                                                       # 
                    };                                                                                                  # fun find_frombox_parts_not_hidden_by_popups

                fun propagate_frombox_changes_to_all_parents_in_which_they_are_visible                                  # Caller has just changed contents of from_box in subwindow_or_view:  We need to propagate these changes to all parents in which it is visible.
                      (
                        subwindow_or_view                                                                               # A gt::SUBWINDOW_INFO contains the complete offscreen image of one running gui, either the main window one or a popup.
                        as                                                                                              # Consequently in the gt::SUBWINDOW_INFO case we are always copying from the offscreen subwindow_or_view to the visible screen,
                        gt::SUBWINDOW_INFO bp,                                                                          # never from one offscreen subwindow_or_view to another.  When copying to the visible screen we do need to be careful not to
                                                                                                                        # overwrite screen space belonging to any popup which is above us in the stacking order.
                        from_box:       g2d::Box                                                                        # From-box in source pixmap coordinates.
                      )
                        =>
                        {
                            frombox_fragments_to_draw                                                                   # These are in local pixmap coordinates (not basewindow coordinates).
                                =
                                find_frombox_parts_not_hidden_by_popups (subwindow_or_view,  from_box);

                            pixmap_site
                                =
                                gtj::subwindow_info_site_in_basewindow_coordinates   bp;

                            pixmap_upperleft = g2d::box::upperleft  pixmap_site;

                            from_id   =  gtj::subwindow_or_view_id_of  subwindow_or_view;                               # This is the pixmap holding the source pixels for the blit.
                            #
                            apply  draw_fragment  frombox_fragments_to_draw                                             # For each part of from_box which is not hidden by overlying popups....
                                where
                                    fun draw_fragment (from_box: g2d::Box)
                                        =
                                        {
                                            to_point  =  (g2d::box::upperleft  from_box) + pixmap_upperleft;            # Where should we copy pixels to, on the visible hostwindow?   If we are a popup, we need to add in the popup upperleft (pixmap_upperleft) in basewindow coordinates.

                                            hostwindow_for_gui.draw_displaylist                                         # ... blit that part to on-screen window for user to see.
                                              [
                                                gd::COPY_FROM_RW_PIXMAP { from_box, to_point, from_id }
                                              ];
                                        };
                                end;
                        };

                    propagate_frombox_changes_to_all_parents_in_which_they_are_visible                                  # This gt::SCROLLABLE_INFO case will happen when we have a scrollport located on a scrollport.
                      (
                        subwindow_or_view                                                                               # A gt::SCROLLABLE_INFO contains the offscreen image of one scrollable scrollport embedded within a parent subwindow_or_view.
                        as                                                                                              # Consequently in the gt::SCROLLABLE_INFO case we are always copying from one offscreen subwindow_or_view to another.
                        gt::SCROLLABLE_INFO { id:                       Id,
                                              upperleft:                Ref(g2d::Point),                                # Upperleft of view's subwindow_or_view in scrollport coordinates, used for scrolling pixmap in scrollport.
                                              scroller:                 Ref(gt::Scroller),                              # Client-code interface for controlling view_upperleft.
                                              callback:                 gt::Scroller_Callback,                          # This is how we pass our Scroller to app client code, which basically lets it set 'pixmap_upperleft' above.
                                              site:                     Ref(g2d::Box),                                  # Current assigned site on pixmap.  Set by  assign_sites_to_all_widgets()     in   src/lib/x-kit/widget/space/widget/widgetspace-imp.pkg
                                              rg_widget:                Ref( gt::Rg_Widget_Type ),
                                              pixmap:                   g2p::Gadget_To_Rw_Pixmap,
                                              parent_subwindow_or_view: gt::Subwindow_Or_View                           # This can be a SCROLLABLE_INFO if we have a scrollport located on a scrollport.
                                            },

                        from_box:       g2d::Box                                                                        # From-box in source pixmap.
                      )
                        =>
                        {
                            from_id   =  gtj::subwindow_or_view_id_of  subwindow_or_view;                               # The Id for the source pixmap for the copy.

                                                scrollport_upperleft_in_view                                            # If the from_box is not entirely visible in the scrollport
                                                    =                                                                   # then we need to clip it to the scrollport.  Since we want
                                                    g2d::point::zero - *upperleft;                                      # the clipped from_box to be in the gadget's home pixmap,
                                                                                                                        # the most straightforward approach is to transform the
                                                                                                                        # scrollport into home pixmap space.  If *upperleft is
                            scrollport' = g2d::box::clone_box_at (*site, scrollport_upperleft_in_view);                 # 0,0 then scrollport is at (0,0) in home pixmap, otherwise
                                                                                                                        # it is offset by -*upperleft, so in general we want
                                                                                                                        # the port cloned at -*upperleft.



                            to_point  = g2d::box::upperleft  from_box;                                                  # Where should we copy pixels to, on our parent pixmap?   We initialize this to the null transform -- we'll add in appropriate offsets to this momentarily.

                            case (g2d::box::intersection (scrollport', from_box))
                                #
                                NULL => ();                                                                             # No intersection between from_box and scrollport' means no part of gadget is visible, so just return NULL.
                                #
                                THE from_box'
                                    =>
                                    {
                                        my (to_point, from_box)                                                         # Update to_point and from_box to account for clipping due
                                            =                                                                           # to intersection between scrollport and old from_box value.
                                            if (g2d::box::eq (from_box', from_box))
                                                #
                                                (to_point, from_box);                                                   # No actual clipping (from_box must be completely visible in scrollport) so nothing to do here.

                                            elif (g2d::point::eq ( g2d::box::upperleft( from_box' ),                    # Is upperleft of from_box' different from upperleft of from_box?
                                                                   g2d::box::upperleft( from_box  )
                                                 )                 )

                                                (to_point, from_box');                                                  # No, so to_point stays the same, we just replace from_box with from_box'.
                                            else
                                                                                                                        # Yes, so we need to move to_point to reflect displacement between from_box and from_box'.
                                                frombox_displacement
                                                    =
                                                    g2d::point::subtract( g2d::box::upperleft( from_box' ),             # Compute that displacement.
                                                                          g2d::box::upperleft( from_box  )
                                                                        );

                                                to_point'   = g2d::point::add (to_point, frombox_displacement);         # Apply it.

                                                (to_point', from_box');                                                 # Done.
                                            fi;

                                                                                                                        # Now we just need to compute where from_box should be drawn in
                                                                                                                        # current sub/window, then update to_point and continue recursively.

                                        to_point = g2d::point::add (to_point, *upperleft);                              # Account for location of view  relative to scrollport.
                                        to_point = g2d::point::add (to_point, g2d::box::upperleft(*site));              # Account for location of scrollport relative to parent pixmap.

                                        parent_pixmap' = gtj::gadget_to_rw_pixmap__of  parent_subwindow_or_view;                # Find off-screen backing pixmap containing scrollport onto us.
                                        from_id    = pixmap.id;                                                         # Pixmap to copy rectangle from (as Id).
                                        gui_displayop  = gd::COPY_FROM_RW_PIXMAP { from_box, to_point, from_id };       # The actual copy from our pixmap to scrollport area in parent pixmap.
                                        #                                                                               #
                                        parent_pixmap'.draw_displaylist [ gui_displayop ];                              # Draw updated gadget appearance into its off-screen backing-pixmap home site.

                                        from_box = g2d::box::clone_box_at (from_box, to_point);                         # Our to-box is from_box for the next level of recursion.

                                        propagate_frombox_changes_to_all_parents_in_which_they_are_visible              # Continue recursively to next level of scrollport nesting.
                                            #
                                            (parent_subwindow_or_view, from_box);
                                    };
                            esac;

                        };

                    propagate_frombox_changes_to_all_parents_in_which_they_are_visible                                  # 
                      (
                        subwindow_or_view                                                                               # 
                        as                                                                                              # 
                        gt::TABBABLE_INFO {
                                            rg_widget:                  gt::Rg_Widget_Type,
                                            pixmap:                     g2p::Gadget_To_Rw_Pixmap,

                                            parent_subwindow_or_view:   gt::Subwindow_Or_View,                          # This can be a SCROLLABLE_INFO if we have a tabport located on a scrollport, for example.
                                            site:                       Ref(g2d::Box),                                  # Size and location of subwindow tabport in parent Subwindow_Or_View coordinates.
                                            #
                                            is_visible: Ref( Bool )                                                     # We do not have to worry about popups blocking our copies, since they are entirely internal to one guipane.
                                          },

                        from_box:       g2d::Box                                                                        # From-box in source pixmap.
                      )
                        =>
                        if (*is_visible)                                                                                # Do not propagate if we are one of multiple TABPORT and we are not visible.
                            #   
                            from_id   =  gtj::subwindow_or_view_id_of  subwindow_or_view;                               # The Id for the source pixmap for the copy.

                            tabport_upperleft_in_view                                                                   # Tabports do not support scrolling, so upperleft is always at zero.
                                =                                                                                       # 
                                g2d::point::zero;                                                                       # 


                            tabport' = g2d::box::clone_box_at (*site, tabport_upperleft_in_view);                               # 
                                                                                                                        # 
                                                                                                                        # 

                            to_point  = g2d::box::upperleft  from_box;                                                  # Where should we copy pixels to, on our parent pixmap?   We initialize this to the null transform -- we'll add in appropriate offsets to this momentarily.

                            case (g2d::box::intersection (tabport', from_box))
                                #
                                NULL => ();                                                                             # No intersection between from_box and tabport' means no part of gadget is visible, so just return NULL.
                                #
                                THE from_box'
                                    =>
                                    {
                                        my (to_point, from_box)                                                         # Update to_point and from_box to account for clipping due
                                            =                                                                           # to intersection between tabport and old from_box value.
                                            if (g2d::box::eq (from_box', from_box))
                                                #
                                                (to_point, from_box);                                                   # No actual clipping (from_box must be completely visible in tabport) so nothing to do here.

                                            elif (g2d::point::eq ( g2d::box::upperleft( from_box' ),                    # Is upperleft of from_box' different from upperleft of from_box?
                                                                   g2d::box::upperleft( from_box  )
                                                 )                 )

                                                (to_point, from_box');                                                  # No, so to_point stays the same, we just replace from_box with from_box'.
                                            else
                                                                                                                        # Yes, so we need to move to_point to reflect displacement between from_box and from_box'.
                                                frombox_displacement
                                                    =
                                                    g2d::point::subtract( g2d::box::upperleft( from_box' ),             # Compute that displacement.
                                                                          g2d::box::upperleft( from_box  )
                                                                        );

                                                to_point'   = g2d::point::add (to_point, frombox_displacement);         # Apply it.

                                                (to_point', from_box');                                                 # Done.
                                            fi;

                                                                                                                        # Now we just need to compute where from_box should be drawn in
                                                                                                                        # current sub/window, then update to_point and continue recursively.

                                        to_point = g2d::point::add (to_point, g2d::box::upperleft(*site));              # Account for location of tabport relative to parent pixmap.

                                        parent_pixmap' = gtj::gadget_to_rw_pixmap__of  parent_subwindow_or_view;        # Find off-screen backing pixmap containing tabport onto us.
                                        from_id    = pixmap.id;                                                         # Pixmap to copy rectangle from (as Id).
                                        gui_displayop  = gd::COPY_FROM_RW_PIXMAP { from_box, to_point, from_id };       # The actual copy from our pixmap to tabport area in parent pixmap.
                                        #                                                                               #
                                        parent_pixmap'.draw_displaylist [ gui_displayop ];                              # Draw updated gadget appearance into its off-screen backing-pixmap home site.

                                        from_box = g2d::box::clone_box_at (from_box, to_point);                         # Our to-box is from_box for the next level of recursion.

                                        propagate_frombox_changes_to_all_parents_in_which_they_are_visible              # Continue recursively to next level of tabport nesting.
                                            #
                                            (parent_subwindow_or_view, from_box);
                                    };
                            esac;
                        fi;
                end;
            end;

        fun refresh_hostwindow_rectangle
              (
                hostwindow_info:                        gt::Hostwindow_Info,
                from_box:                       g2d::Box                                                                # From-box in base window coordinates.
              )
            =
            gtj::all_guipanes_on_hostwindow_apply  hostwindow_info
                #
                (\\ (guipane: gt::Guipane) = {
                    #
                    subwindow_info
                        =
                        gtj::subwindow_info_of_subwindow_data
                            #
                            guipane.subwindow_info;

                    guipane_upperleft
                        =
                        gtj::subwindow_info_upperleft_in_base_window_coordinates
                            #
                            subwindow_info;

                    guipane_size =  (*subwindow_info.pixmap).size;

                    guipane_site =  g2d::box::make (guipane_upperleft, guipane_size);

                    case (g2d::box::intersection (from_box, guipane_site))
                        #
                        THE intersection                                                                                # They do intersect.  Intersection is in base-window coordinates.
                            =>
                            {   (g2d::box::upperleft_and_size intersection)
                                    ->
                                    ( intersection_upperleft_in_basewindow_coordinates,
                                      intersection_size
                                    );

                                intersection_upperleft_in_guipane_coordinates
                                    =
                                    intersection_upperleft_in_basewindow_coordinates
                                    -
                                    guipane_upperleft;

                                intersection_site_in_guipane_coordinates
                                    =
                                    g2d::box::make (intersection_upperleft_in_guipane_coordinates, intersection_size);

                                update_offscreen_parent_pixmaps_and_then_hostwindow
                                  (
                                    gt::SUBWINDOW_INFO  subwindow_info,
                                    intersection_site_in_guipane_coordinates,
                                    guipane.hostwindow
                                  );
                            };

                        NULL => ();     
                    esac;

                    ();
                });

        #
        fun kill__guipane__imps
              (
                guipane:                gt::Guipane,
                me:                     gt::Guiboss_State                                                               # This arg is not actually currently used.
              )
            =
            {
                gtj::guipane_apply
                    (
                      guipane,
                      [
                        gtj::RG_WIDGET_FN       kill__rg_widget__imp,
                        gtj::RG_SPRITE_FN       kill__rg_sprite__imp,
                        gtj::RG_OBJECT_FN       kill__rg_object__imp,
                        #
                        gtj::RG_OBJECTSPACE_FN  kill__rg_objectspace__imp,
                        gtj::RG_SPRITESPACE_FN  kill__rg_spritespace__imp,
                        gtj::RG_WIDGETSPACE_FN  kill__rg_widgetspace__imp
                      ]
                    )
                    where
                        fun kill__rg_widget__imp  (rg_widget:   gt::Rg_Widget) =   rg_widget.guiboss_to_widget.g.die ();
                        fun kill__rg_sprite__imp  (rg_sprite:   gt::Rg_Sprite) =   rg_sprite.guiboss_to_gadget.die ();
                        fun kill__rg_object__imp  (rg_object:   gt::Rg_Object) =   rg_object.guiboss_to_gadget.die ();
                        #
                        fun kill__rg_objectspace__imp  (rg_objectspace: gt::Rg_Objectspace) =  rg_objectspace.guiboss_to_objectspace.die ();
                        fun kill__rg_spritespace__imp  (rg_spritespace: gt::Rg_Spritespace) =  rg_spritespace.guiboss_to_spritespace.die ();
                        fun kill__rg_widgetspace__imp  (rg_widgetspace: gt::Rg_Widgetspace) =  rg_widgetspace.guiboss_to_widgetspace.die ();
                    end;
            };  


        #
        fun free__guipane__resources
              (
                guipane:                gt::Guipane,
                me:                     gt::Guiboss_State
              )
            =
            {   # Release all X-server pixmaps owned by gadgets in this guipane:
                #
                gtj::guipane_apply
                    (
                      guipane,
                      [
                        gtj::RG_WIDGET_FN       free__rg_widget__pixmaps,
                        gtj::RG_SPRITE_FN       free__rg_sprite__pixmaps,
                        gtj::RG_OBJECT_FN       free__rg_object__pixmaps
                      ]
                    )
                    where
                        fun free_gadget_pixmaps  (gadget_imp_info:      gt::Gadget_Imp_Info)
                            =
                            apply   release_pixmap  (im::vals_list *gadget_imp_info.pixmaps)
                                    where
                                        fun release_pixmap (rw_pixmap:  g2p::Gadget_To_Rw_Pixmap)
                                            =
                                            rw_pixmap.free_rw_pixmap ();                                                # This is a no-op if the widget already freed the rw_pixmap.
                                    end;

                        fun free__rg_widget__pixmaps  (rg_widget: gt::Rg_Widget) =  case (idm::get (*me.gadget_imps, rg_widget.guiboss_to_widget.id)) THE g => free_gadget_pixmaps g; NULL => (); esac; # We don't expect the NULL cases to happen; possibly we should log errors if they do.
                        fun free__rg_sprite__pixmaps  (rg_sprite: gt::Rg_Sprite) =  case (idm::get (*me.gadget_imps, rg_sprite.guiboss_to_gadget.id)) THE g => free_gadget_pixmaps g; NULL => (); esac;
                        fun free__rg_object__pixmaps  (rg_object: gt::Rg_Object) =  case (idm::get (*me.gadget_imps, rg_object.guiboss_to_gadget.id)) THE g => free_gadget_pixmaps g; NULL => (); esac;
                    end;

                # Release all X-server pixmaps owned by scrollports and tabports in this guipane:
                #
                gtj::guipane_apply
                    (
                      guipane,
                      [
                        gtj::RG_SCROLLPORT_FN       free__rg_scrollport__pixmap,
                        gtj::RG_TABPORT_FN          free__rg_tabport__pixmaps
                      ]
                    )
                    where
                        fun free__rg_scrollport__pixmap  (rg_scrollport: gt::Rg_Scrollport)
                            =
                            rg_scrollport.pixmap.free_rw_pixmap ();                                                     # This is a no-op if someone already freed the rw_pixmap.

                        fun free__rg_tabport__pixmaps  (rg_tabport: gt::Rg_Tabport)
                            =
                            apply   do_tab rg_tabport.tabs
                                    where
                                        fun do_tab (tab:   gt::Tabbable_Info)
                                            =
                                            tab.pixmap.free_rw_pixmap ();                                               # This is a no-op if someone already freed the rw_pixmap.
                                    end;
                    end;

                # Release backing pixmap for this guipane:
                #
                case guipane.subwindow_info
                    #
                    gt::SUBWINDOW_DATA  (subwindow_info:   gt::Subwindow_Info)
                        =>
                        (*subwindow_info.pixmap).free_rw_pixmap ();                                                     # This is a no-op if someone already freed the rw_pixmap.
                esac;


                # Drop all imps for this guipane from our global imp indices:
                # 
                gtj::guipane_apply
                    (
                      guipane,
                      [
                        gtj::RG_WIDGET_FN       drop__rg_widget__imp,
                        gtj::RG_SPRITE_FN       drop__rg_sprite__imp,
                        gtj::RG_OBJECT_FN       drop__rg_object__imp,
                        #
                        gtj::RG_OBJECTSPACE_FN  drop__rg_objectspace__imp,
                        gtj::RG_SPRITESPACE_FN  drop__rg_spritespace__imp,
                        gtj::RG_WIDGETSPACE_FN  drop__rg_widgetspace__imp
                      ]
                    )
                    where
                        fun drop__rg_widget__imp  (rg_widget:   gt::Rg_Widget) =   me.gadget_imps :=  idm::drop (*me.gadget_imps, rg_widget.guiboss_to_widget.id);
                        fun drop__rg_sprite__imp  (rg_sprite:   gt::Rg_Sprite) =   me.gadget_imps :=  idm::drop (*me.gadget_imps, rg_sprite.guiboss_to_gadget.id);
                        fun drop__rg_object__imp  (rg_object:   gt::Rg_Object) =   me.gadget_imps :=  idm::drop (*me.gadget_imps, rg_object.guiboss_to_gadget.id);
                        #
                        fun drop__rg_objectspace__imp  (rg_objectspace: gt::Rg_Objectspace) =  me.objectspace_imps :=  idm::drop (*me.objectspace_imps, rg_objectspace.guiboss_to_objectspace.id);
                        fun drop__rg_spritespace__imp  (rg_spritespace: gt::Rg_Spritespace) =  me.spritespace_imps :=  idm::drop (*me.spritespace_imps, rg_spritespace.guiboss_to_spritespace.id);
                        fun drop__rg_widgetspace__imp  (rg_widgetspace: gt::Rg_Widgetspace) =  me.widgetspace_imps :=  idm::drop (*me.widgetspace_imps, rg_widgetspace.guiboss_to_widgetspace.id);
                    end;
            };  

        fun popup_nesting_depth_of_gadget
              ( id:             Id,
                me:             gt::Guiboss_State
              )
              : Int                                                                                             # Result will be 0 for gadgets on base window, 1 for those on first-level popups, 2 for those on popups on popups, etc.
            =
            {
                i =  gtj::get_gadget_imp_info  (me.gadget_imps, id);
                #
                s =  gtj::subwindow_info_of_subwindow_or_view  *i.subwindow_or_view;

                nesting_depth_of (s, 0)
                    where
                        fun nesting_depth_of
                              (
                                s:      gt::Subwindow_Info,
                                depth:  Int
                              )
                            =
                            case s.parent
                                #
                                NULL                        =>  depth;
                                THE (gt::SUBWINDOW_DATA s)  =>  nesting_depth_of (s, depth+1);
                            esac;
                    end;
            };
    };
end;





Comments and suggestions to: bugs@mythryl.org

PreviousUpNext