home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / program / books / progem / windows.2 < prev   
Text File  |  1986-11-01  |  27KB  |  584 lines

  1.     Permission to reprint or excerpt is granted only if the following line
  2.     appears at the top of the article:
  3.  
  4.       ANTIC PUBLISHING INC., COPYRIGHT 1985. REPRINTED BY PERMISSION.
  5.  
  6.  
  7.  
  8.     Professional GEM  by Tim Oren
  9.     Column #2 - Windows, part 2
  10.  
  11.  
  12.     EXCELSIOR!
  13.  
  14.        In  this  installment,  we continue the exploration of GEM's window
  15.     manager  by  finding  out  how  to process the messages received by an
  16.     application when it has a window defined on the screen.
  17.  
  18.        Also,  beginning  with this column, sample C code demonstrating the
  19.     techniques discussed will be available on SIG*ATARI in DL5.  This will
  20.     allow  you  to  download  the  code  without  interference  by the CIS
  21.     text-formatter  used by ANTIC ONLINE output.  The file for this column
  22.     is  GEMCL2.XMO.   All  references  to  non-GEM routines in this column
  23.     refer  to  this  file.   Please note that these files will not contain
  24.     entire  programs.   Instead,  they  consist of small pieces of utility
  25.     code which you may copy and modify in your own programs.
  26.  
  27.  
  28.     REDRAWING WINDOWS
  29.  
  30.        One  of  the  most misunderstood parts of GEM is the correct method
  31.     for  drawing  within  a  window.   Most  requests  for  redrawing  are
  32.     generated  by  the  GEM  system,  and  arrive  as  messages (read with
  33.     evnt_multi)  which  contain  the  handle of the window, and the screen
  34.     rectangle  which  is "dirty" and needs to be redrawn. Screen areas may
  35.     become  dirty  as  a  result  of  windows being closed, sized down, or
  36.     moved,  thus  "exposing"  an  area  underneath.   The  completion of a
  37.     dialog,  or closing of a desk accessory may also free up a screen area
  38.     which  needs  to be redrawn.  When GEM detects the presence of a dirty
  39.     rectangle,  it  checks  its  list  of  open  windows,  and  sends  the
  40.     application  a redraw message for each of its windows which intersects
  41.     the dirty area.
  42.  
  43.  
  44.     CAVEAT EMPTOR
  45.  
  46.        GEM   does   not  "clip"  the  rectangle  which  it  sends  to  the
  47.     application;  that  is,  the rectangle may not lie entirely within the
  48.     portion  of  the window which is exposed on the screen.  It is the job
  49.     of  the  application  to determine in what portion of the rectangle it
  50.     may  safely  draw.   This  is  done  by examining the "rectangle list"
  51.     associated  with the window. A rectangle list is maintained by GEM for
  52.     each active window.  It contains the portions of the window's interior
  53.     which  are  exposed, i.e., topmost, on the screen and within which the
  54.     app may draw.
  55.  
  56.        Let's  consider  an example to make this clear.  Suppose an app has
  57.     opened  two windows, and there are no desk accessory windows open. The
  58.     window  which  is  topmost  will always have only one rectangle in its
  59.     list.   If  the two are separate on the screen, then the second window
  60.     will  also  have  one rectangle.  If they overlap, then the top window
  61.     will  "break" the rectangle of the bottom one.  If the overlap is at a
  62.     corner,  two  rectangles  will be generated for the bottom window.  If
  63.     the  overlap  is on a side only, then three rectangles are required to
  64.     cover the exposed portion of the bottom window.  Finally, if the first
  65.     window  is  entirely within the second, it requires four rectangles in
  66.     the list to tile the second window.
  67.  
  68.        Try  working  out a few rectangle examples with pencil and paper to
  69.     get  the feel of it.  You will see that the possible combinations with
  70.     more  than  two windows are enormous.  This, by the way, is the reason
  71.     that  GEM  does  not send one message for each rectangle on the list:
  72.     with  multiple windows, the number of messages generated would quickly
  73.     fill up the application's message queue.
  74.  
  75.        Finally,  note that every app MUST use this method, even if it only
  76.     uses a single window, because there may be desk accessories with their
  77.     own  windows  in  the  system at the same time.  If you do not use the
  78.     rectangle lists, you may overwrite an accessory's window.
  79.  
  80.  
  81.     INTO THE BITS
  82.  
  83.        First, we should note that the message type for a redraw request is
  84.     WM_REDRAW,  which  is  stored  in  msg[0],  the  first location of the
  85.     message  returned  by  evnt_multi.   The  window  handle  is stored in
  86.     msg[3].   These  locations  are  the same for all of the message types
  87.     being  discuss.   The rectangle which needs to be redrawn is stored in
  88.     msg[4] through msg[7].
  89.  
  90.        Now let's examine the sample redraw code in more detail. The redraw
  91.     loop is bracketed with mouse off and mouse on calls.  If you forget to
  92.     do  this,  the  mouse pointer will be over-written if it is within the
  93.     window  and  the  next  movement of the mouse will leave a rectangular
  94.     blotch  on  the  screen  as a piece of the "old" screen is incorrectly
  95.     restored.
  96.  
  97.        The  other  necessary  step is to set the window update flag.  This
  98.     prevents  the  menu  manager from dropping a menu on top of the screen
  99.     portion  being  redrawn.  You must release this flag at the end of the
  100.     redraw, or the you will be unable to use any menus afterwards.
  101.  
  102.        The  window  rectangles  are  retrieved using a get-first, get-next
  103.     scheme  which  will be familiar if you have used the GEM DOS or PC-DOS
  104.     wildcard  file  calls.  The end of the rectangle list has been reached
  105.     when  both the width and height returned are zero.  Since some part of
  106.     a  window  might be off-screen (unless you have clamped its position -
  107.     see  below), the retrieved rectangle is intersected with the desktop's
  108.     area, and then with the screen area for which a redraw was requested.
  109.  
  110.        Now you have the particular area of the screen in which it is legal
  111.     to  draw.   Unless  there  is only one window in your application, you
  112.     will  have to test the handle in the redraw request to figure out what
  113.     to  put in the rectangle.  Depending on the app, you may be drawing an
  114.     AES  object  tree,  or executing VDI calls, or some combination of the
  115.     two.   In  the AES case, the computed rectangle is used to specify the
  116.     bounds  of  the objc_draw.  For VDI work, the rectangle is used to set
  117.     the clipping area before executing the VDI calls.
  118.  
  119.  
  120.     A SMALL CONFESSION
  121.  
  122.        At  the  beginning  of  this discussion, I deliberately omitted one
  123.     class  of redraws: those initiated by the application itself.  In some
  124.     cases  a  part  of  the  screen  must  be  redrawn immediately to give
  125.     feedback  to  the user following a keystroke, button, or mouse action.
  126.     In these cases, the application could call do_redraw directly, without
  127.     waiting  for  a  message.  The only time you can bypass do_redraw, and
  128.     draw  without walking the rectangle list, is when you can be sure that
  129.     the  target  window  is  on  top,  and  that the figure being drawn is
  130.     entirely contained within it.
  131.  
  132.        In  many  cases,  however,  an application initiated redraw happens
  133.     because  of a computed change, for instance, a spreadsheet update, and
  134.     its timing is not crucial.  In this instance, you may wish to have the
  135.     app send ITSELF a redraw request.
  136.  
  137.        The main advantage of this approach is that the AES is smart enough
  138.     to see if there is already a redraw request for the same window in the
  139.     queue,  and,  if  so,  to merge the requests by doing a union of their
  140.     rectangles.   In  this  fashion,  the  "blinky" appearance of multiple
  141.     redraws  is  avoided,  without  the  need to include logic for merging
  142.     redraws within the program.
  143.  
  144.        A  utility routine for sending the "self-redraw" is included in the
  145.     down-load for this article.
  146.  
  147.  
  148.     WINDOW CONTROL REQUESTS
  149.  
  150.        An application is notified by the AES, via the message system, when
  151.     the  user manipulates one of the window control points.  Remember that
  152.     you  must  have  specified  each  control  point  when  the window was
  153.     created, or will not receive the associated control message.
  154.  
  155.        The most important thing to understand about window control is that
  156.     the  change  which  the  user  requested does not take place until the
  157.     application  forwards  it  to  the AES.  While this makes for a little
  158.     extra work, it gives the program a chance to intervene and validate or
  159.     modify the request to suit.
  160.  
  161.        A second thing to keep in mind is that not all window updates cause
  162.     a  redraw  request  to  be  generated  for the window, because the AES
  163.     attempts to save time with raster moves on the screen.
  164.  
  165.        Now  let's  look  at  each  window  control request in detail.  The
  166.     message  code  for  a  window move is WM_MOVED.  If you are willing to
  167.     accept any such request, just do:
  168.  
  169.         wind_set(wh, WF_CXYWH, msg[4], msg[5], msg[6], msg[7]);
  170.  
  171.     (Remember that wh, the window handle, is always in msg[3]).
  172.  
  173.        The  AES  will  not  request  a redraw of the window following this
  174.     call,  unless  the  window  is  being  moved  from a location which is
  175.     partially  "off-screen". Instead, it will do a "blit" (raster copy) of
  176.     the  window  and its contents to the new location without intervention
  177.     by the app.
  178.  
  179.        There  are two constraints which you may often wish to apply to the
  180.     user's  move  request.   The first is to force the new location to lie
  181.     entirely  within  the  desktop, rather than partially off-screen.  You
  182.     can do this with the rc_constrain utility by executing:
  183.  
  184.         rc_constrain(&full, &msg[4]);
  185.  
  186.     before  making  the  wind_set  call.   (Full is assumed to contain the
  187.     desktop dimensions.)
  188.  
  189.        The  second common constraint is to "snap" the x-dimension location
  190.     of  the new location to a word boundary.  This operation will speed up
  191.     GEM's  "blit" because no shifting or masking will need to be done when
  192.     moving  the window.  To perform this operation, use align() before the
  193.     wind_set call:
  194.  
  195.         msg[4] = align(msg[4], 16);
  196.  
  197.     The message code for a window size request is WM_SIZED.  Again, if you
  198.     are  willing to accept any request, you can just "turn it around" with
  199.     the same wind_set call as given for WM_MOVED.
  200.  
  201.        Actually,  GEM  enforces a couple of constraints on sizing.  First,
  202.     the  window  may  not be sized off screen.  Second, there is a minimum
  203.     window size which is dependent on the window components specified when
  204.     it  was created.  This prevents features like scroll arrows from being
  205.     squeezed into oblivion.
  206.  
  207.        The  most  common  application  constraint on sizing is to snap the
  208.     size  to  horizontal words (as above) and/or vertical character lines.
  209.     In  the latter case, the vertical dimension of the output font is used
  210.     with align().
  211.  
  212.        Also,  be  aware  that the size message which you receive specifies
  213.     the  EXTERNAL  dimensions of the window.  To assure an "even" size for
  214.     the  INTERNAL  dimensions,  you  must make a wind_calc call to compute
  215.     them,  use  align() on the computed values, back out the corresponding
  216.     external  dimensions  with  the  reverse  wind_calc, and then make the
  217.     wind_set call with this set of values.
  218.  
  219.        A  window resize will only cause a redraw request for the window if
  220.     the  size  is  being  increased  in  at  least one dimension.  This is
  221.     satisfactory  for  most  applications, but if you must "reshuffle" the
  222.     window  after  a  size-down,  you  should  send  yourself a redraw (as
  223.     described  above)  after  you  make  the  wind_set  call.   This  will
  224.     guarantee  that  the display is updated correctly.  Also note that the
  225.     sizing  or  movement  of  one  window  may cause redraw requests to be
  226.     generated for other windows which are uncovered by the change.
  227.  
  228.        The window full request, with code WM_FULLED, is actually a toggle.
  229.     If  the  window  is  already  at  its  full  size (as specified in the
  230.     wind_create),  then  this is a request to shrink to its previous size.
  231.     If  the window is currently small, then the request is to grow to full
  232.     size.
  233.  
  234.        Since  the  AES  records  the current, previous, and maximum window
  235.     size,  you  can  use  wind_get  calls  to  determine  which  situation
  236.     pertains.  The  hndl_full  utility  in  the  down-load  (modified from
  237.     Doodle),  shows  how to do this.  The "zoom box" effects when changing
  238.     size  are  optional, and can be removed to speed things up.  Again, if
  239.     the  window's  size is decreasing, no redraw is generated, so you must
  240.     send  yourself  one  if necessary.  You should not have to perform any
  241.     constraint  or "snap" operations here, since (presumably) the full and
  242.     previous sizes have had these checks applied to them already.
  243.  
  244.        The  WM_CLOSED  message  is received when the close box is clicked.
  245.     What  action  you  perform depends on the application.  If you want to
  246.     remove the window, use wind_close as described in the last column.  In
  247.     many applications, however, the close message may indicate that a file
  248.     is  to  be saved, or a directory or editing level is to be closed.  In
  249.     these  cases,  the  message  is  used to trigger this action before or
  250.     instead  of the wind_close.  (Folders on the Desktop are an example of
  251.     this situation.)
  252.  
  253.        The  WM_TOPPED  message  indicates  that the AES wants to bring the
  254.     indicated window to the "top" and make it active.  This happens if the
  255.     user  clicks  within a window which is not on top, or if the currently
  256.     topped  window  is  closed  by  its  application  or  desk accessory.
  257.     Normally, the application should respond to this message with:
  258.  
  259.         wind_set(wh, WF_TOP, 0, 0);
  260.  
  261.     and allow the process to complete.
  262.  
  263.        In  a  few  instances, a window may be used in an output only mode,
  264.     such  as  a status display, with at least one other window present for
  265.     input.  In this case, a WM_TOPPED message for the status window may be
  266.     ignored.   In  all  other cases, you must handle the WM_TOPPED message
  267.     even  if  your  application  has only one window: Invocation of a desk
  268.     accessory could always place another window on top.  If you fail to do
  269.     so,   subsequent   redraws  for  your  window  may  not  be  processed
  270.     correctly.
  271.  
  272.  
  273.     WINDOW SLIDER MESSAGES
  274.  
  275.        If you specify all of the slider bar parts for your window, you may
  276.     receive up to five different message types for each of the two sets of
  277.     sliders.   To  simplify  things a little, I will discuss everything in
  278.     terms  of  the  vertical  (right  hand side) sliders.  If you are also
  279.     using  the horizontal sliders, the same techniques will work, just use
  280.     the alternate mnemonics.
  281.  
  282.        The WM_VSLID message indicates that the user has dragged the slider
  283.     bar  within  its  box,  indicating  a new relative position within the
  284.     document.   Along  with  the  window handle, this message includes the
  285.     relative position between 1 and 1000 in msg[4].
  286.  
  287.        Recall from last column's discussion that this interval corresponds
  288.     to  the "freedom of movement" of the slider. If you want to accept the
  289.     user's request, just make the call:
  290.  
  291.         wind_set(wh, WF_VSLIDE, msg[4], 0, 0, 0);
  292.  
  293.     (Corresponding horizontal mnemonics are WM_HSLID and WF_HSLIDE).
  294.  
  295.        Note  that this wind_set call will not cause a redraw message to be
  296.     sent.   You  must  update  the  display  to  reflect  the new scrolled
  297.     position,  either  by  executing  a  redraw  directly,  or  by sending
  298.     yourself  a  message.   If  the  document  within  the window has some
  299.     structure,  you  may not wish to accept all slider positions.  Instead
  300.     you  may  want  to  force the scroll position to the nearest text line
  301.     (for  instance).   Using  terms  defined  in  the last column, you may
  302.     convert the slider position to "document units" with:
  303.  
  304.         top_wind = msg[4] * (total_doc - seen_doc) / 1000 + top_doc
  305.  
  306.     (This will probably require 32-bit arithmetic).  After rounding off or
  307.     otherwise  modifying  the request, convert it back to slider units and
  308.     make the WF_VSLIDE request.
  309.  
  310.        The  other  four  slider  requests  all  share  one  message  code:
  311.     WM_ARROWED.   They  are  distinguished  by sub-codes stored in msg[4]:
  312.     WA_UPPAGE, WA_DNPAGE, WA_UPLINE, and WA_DNLINE.  These are produced by
  313.     clicking  above  and  below the slider, and on the up and down arrows,
  314.     respectively.   (I  have  no  idea why sub-codes were used in this one
  315.     instance.)   The corresponding horizontal slider codes are: WA_LFPAGE,
  316.     WA_RTPAGE, WA_LFLINE, and WA_RTLINE.
  317.  
  318.        What  interpretation  you give to these requests will depend on the
  319.     application.   In  the  most  common  instance,  text  documents,  the
  320.     customary method is to change the top of window position (top_wind) by
  321.     one  line for a WA_UPLINE or WA_DNLINE, and by seen_doc (the number of
  322.     lines in the window) for a WA_UPPAGE or WA_DNPAGE.
  323.  
  324.        After  making  the  change, compute a new slider position, and make
  325.     the  wind_set call as given above.  If the document's length is not an
  326.     even  multiple  of "lines" or "pages" you will have to be careful that
  327.     incrementing  or  decrementing  top_wind  does not exceed its range of
  328.     freedom:  top_doc  to  (top_doc  + total_doc - seen_doc).  If you have
  329.     such  an  odd  size document, you will also have to make a decision on
  330.     whether to violate the line positioning rule so that the slider may be
  331.     put  at  its  bottom-most  position, or to follow the rule but make it
  332.     impossible to get the slider to the extreme of its range.
  333.  
  334.  
  335.     A COMMON BUG
  336.  
  337.        It  is easy to forget that user clicks are not the only things that
  338.     affect  slider  position.  If the window size changes as a result of a
  339.     WM_SIZED  or  WM_FULLED  message, the app must also update its sliders
  340.     (if  they  are  present).   This  is  a good reason to keep the top of
  341.     window information in "document units".
  342.  
  343.        You  can just redo the position calculation with the new "seen_doc"
  344.     value, and call wind_set.  Also remember that changing the size of the
  345.     underlying  document  (adding or deleting a bottom line, for instance)
  346.     must also cause the sliders to be adjusted.
  347.  
  348.  
  349.     DEPT. OF DIRTY TRICKS
  350.  
  351.        There  are  two remaining window calls which are useful to advanced
  352.     programmers.   They require techniques which I have not yet discussed,
  353.     so you may need to file them for future reference.
  354.  
  355.        The  AES  maintains  a quarter-screen sized buffer which is used to
  356.     save  the  area  under alerts and menu drop-downs.  It is occasionally
  357.     useful  for  the application to gain access to this buffer for its own
  358.     use in saving screen areas with raster copies.  To do so, use:
  359.  
  360.         wind_get(0, WF_SCREEN, &loaddr, &hiaddr, &lolen, &hilen);
  361.  
  362.     Hiaddr and loaddr are the top and bottom 16-bits (respectively) of the
  363.     32-bit  address  of the buffer.  Hilen and lolen are the two halves of
  364.     its  length.   Due  to  a  preculiarity  of  the  binding  you have to
  365.     reassemble  these  pieces  before  using  them.   (The actual value of
  366.     WF_SCREEN  is  17;  this  does  not  appear  in  some  versions of the
  367.     GEMDEFS.H file.)
  368.  
  369.        If  you  use this buffer, you MUST prevent menus from dropping down
  370.     by  using  either  the  BEG_UPDATE  or  BEG_MCTRL  wind_update calls.
  371.     Failure  to  do so will result in your data being destroyed.  Remember
  372.     to use the matching wind_update: END_UPDATE or END_MCTRL, when you are
  373.     done.
  374.  
  375.        The  other  useful call enables you to replace the system's desktop
  376.     definition with a resource of your choosing.  The call:
  377.  
  378.         wind_set(0,WF_NEWDESK, tree, 0,0);
  379.  
  380.     where  tree  is  the 32-bit address of the object tree, will cause the
  381.     AES  to  draw  your  definition  instead  of  the  usual gray or green
  382.     background.  Not  only that, it will continue to redraw this tree with
  383.     no  intervention  on your part.  Obviously, the new definition must be
  384.     carefully  built  to  fit  the desktop area exactly or garbage will be
  385.     left  around  the  edges.  For the truly sophisticated, a user-defined
  386.     object  could  be  used  in  this  tree,  with  the  result  that your
  387.     application's  code would be entered from the AES whenever the desktop
  388.     was  redrawn.   This  would  allow  you to put VDI pictures or complex
  389.     images onto the desktop background.
  390.  
  391.  
  392.     A SIN OF OMISSION
  393.  
  394.        In  the  last  column,  I  neglected  to mention that strings whose
  395.     addresses are passed in the WF_NAME and WF_INFO wind_set calls must be
  396.     allocated  in  a  static  data  area.   Since  the  AES  remembers the
  397.     addresses  (not  the characters), a disaster may result if the storage
  398.     has  been  reused  when  the  window manager next attempts to draw the
  399.     window title area.
  400.  
  401.  
  402.     COMING SOON...
  403.  
  404.        This   concludes   our   tour  of  GEM's  basic  window  management
  405.     techniques. There have been some unavoidable glimpses of paths not yet
  406.     taken (forward references), but we will return in time.
  407.  
  408.        On  our  next  excursion,  we  will  take  a look at techniques for
  409.     handling  simple  dialog  boxes,  and start exploring the mysteries of
  410.     resources and object trees.
  411.  
  412.  
  413.  
  414.  
  415. >>>>>>>>>>>>>>>>>>>>>>>>>>   Sample  Redraw  Code  <<<<<<<<<<<<<<<<<<<<<<<<<<<<
  416.  
  417.  
  418.      VOID
  419. do_redraw(wh, area)          /* wh = window handle from msg[3] */
  420.      WORD     wh;          /* area = pointer to redraw rect- */
  421.      GRECT     *area;          /*   tangle in msg[4] thru msg[7] */
  422.      {
  423.      GRECT     box;
  424.  
  425.      graf_mouse(M_OFF, 0x0L);
  426.      wind_update(BEG_UPDATE);
  427.  
  428.      wind_get(wh, WF_FIRSTXYWH, &box.g_x, &box.g_y, &box.g_w, &box.g_h);
  429.      while ( box.g_w && box.g_h )
  430.           {
  431.           if (rc_intersect(full, &box))       /* Full is entire screen */
  432.           if (rc_intersect(area, &box))
  433.                {
  434.                if (wh == w1_handle)       /* Test for window 1 handle */
  435.                     {            /* AES redraw example           */
  436.                     objc_draw(w1_tree, ROOT, MAX_DEPTH, box.g_x,
  437.                          box.g_y, box.g_w, box.g_h);
  438.                     }
  439.                else if (wh == w2_handle) /* Test for window 2 handle */
  440.                     {            /* VDI redraw example           */
  441.                     set_clip(TRUE, &box);
  442.                     /*  Put VDI drawing calls here */
  443.                     }
  444.                /* add more windows here */
  445.                }
  446.           wind_get(wh, WF_NEXTXYWH, &box.g_x, &box.g_y, &box.g_w,
  447.                &box.g_h);
  448.           }
  449.  
  450.      wind_update(END_UPDATE);
  451.      graf_mouse(M_ON, 0x0L);
  452.      }
  453.  
  454.  
  455. >>>>>>>>>>>>>>>>>>>>>>>>> Utilities used in do_redraw <<<<<<<<<<<<<<<<<<<<<<<
  456.  
  457.      VOID
  458. set_clip(clip_flag, area)     /* set clip to specified area     */
  459.      WORD     clip_flag;
  460.      GRECT     *area;
  461.      {
  462.      WORD     pxy[4];
  463.  
  464.      grect_to_array(area, pxy);
  465.      vs_clip(vdi_handle, clip_flag, pxy);
  466.      }
  467.  
  468.      VOID
  469. grect_to_array(area, array)     /* convert x,y,w,h to upr lt x,y and     */
  470.      GRECT     *area;          /*                lwr rt x,y     */
  471.      WORD     *array;
  472.      {
  473.      *array++ = area->g_x;
  474.      *array++ = area->g_y;
  475.      *array++ = area->g_x + area->g_w - 1;
  476.      *array = area->g_y + area->g_h - 1;
  477.      }
  478.  
  479.      WORD
  480. rc_intersect(p1, p2)          /* compute intersect of two rectangles     */
  481.      GRECT     *p1, *p2;
  482.      {
  483.      WORD     tx, ty, tw, th;
  484.  
  485.      tw = min(p2->g_x + p2->g_w, p1->g_x + p1->g_w);
  486.      th = min(p2->g_y + p2->g_h, p1->g_y + p1->g_h);
  487.      tx = max(p2->g_x, p1->g_x);
  488.      ty = max(p2->g_y, p1->g_y);
  489.      p2->g_x = tx;
  490.      p2->g_y = ty;
  491.      p2->g_w = tw - tx;
  492.      p2->g_h = th - ty;
  493.      return( (tw > tx) && (th > ty) );
  494.      }
  495.  
  496.  
  497. >>>>>>>>>>>>>>>>>>>>>>>>> "Self-redraw" Utility <<<<<<<<<<<<<<<<<<<<<<<<<<<
  498.  
  499.      VOID
  500. send_redraw(wh, p)
  501.      WORD     wh;
  502.      GRECT     *p;
  503.      {
  504.      WORD     msg[8];
  505.  
  506.      msg[0] = WM_REDRAW;          /* Defined in GEMBIND.H         */
  507.      msg[1] = gl_apid;          /* As returned by appl_init */
  508.      msg[2] = 0;
  509.      msg[3] = wh;               /* Handle of window to redraw */
  510.      msg[4] = p->g_x;
  511.      msg[5] = p->g_y;
  512.      msg[6] = p->g_w;
  513.      msg[7] = p->g_h;
  514.      appl_write(gl_apid, 16, &msg);     /* Use ADDR(msg) for portability */
  515.      }
  516.  
  517.  
  518. >>>>>>>>>>>>>>>>>>>>>> Utilities for Window Requests <<<<<<<<<<<<<<<<<<<<
  519.  
  520.      VOID
  521. rc_constrain(pc, pt)
  522.      GRECT          *pc;
  523.      GRECT          *pt;
  524.      {
  525.      if (pt->g_x < pc->g_x)
  526.           pt->g_x = pc->g_x;
  527.      if (pt->g_y < pc->g_y)
  528.           pt->g_y = pc->g_y;
  529.      if ((pt->g_x + pt->g_w) > (pc->g_x + pc->g_w))
  530.           pt->g_x = (pc->g_x + pc->g_w) - pt->g_w;
  531.      if ((pt->g_y + pt->g_h) > (pc->g_y + pc->g_h))
  532.           pt->g_y = (pc->g_y + pc->g_h) - pt->g_h;
  533.      }
  534.  
  535.      WORD
  536. align(x,n)          /* Snap position x to an n-bit grid         */
  537.      WORD      x, n;   /* Use n = 16 for horizontal word alignment */
  538.      {
  539.      x += (n >> 2) - 1;          /* Round and... */
  540.      x = n * (x / n);          /* remove residue */
  541.      return (x);
  542.      }
  543.  
  544.  
  545. >>>>>>>>>>>>>>>>>>>>>>>>> Window full utility <<<<<<<<<<<<<<<<<<<<<<<<<<
  546.  
  547.      VOID
  548. hndl_full(wh)          /* depending on current window state, make window    */
  549.      WORD     wh;     /*   full size -or- return to previous shrunken size */
  550.      {          /* graf_ calls are optional special effects.          */
  551.      GRECT     prev;
  552.      GRECT     curr;
  553.      GRECT     full;
  554.  
  555.      wind_get(wh, WF_CXYWH, &curr.g_x, &curr.g_y, &curr.g_w, &curr.g_h);
  556.      wind_get(wh, WF_PXYWH, &prev.g_x, &prev.g_y, &prev.g_w, &prev.g_h);
  557.      wind_get(wh, WF_FXYWH, &full.g_x, &full.g_y, &full.g_w, &full.g_h);
  558.      if ( rc_equal(&curr, &full) )
  559.           {          /* Is full, change to previous           */
  560.           graf_shrinkbox(prev.g_x, prev.g_y, prev.g_w, prev.g_h,
  561.                full.g_x, full.g_y, full.g_w, full.g_h);
  562.           wind_set(wh, WF_CXYWH, prev.g_x, prev.g_y, prev.g_w, prev.g_h);
  563.                     /* put send_redraw here if you need it */
  564.           }
  565.      else
  566.           {          /* is not full, so set to full          */
  567.           graf_growbox(curr.g_x, curr.g_y, curr.g_w, curr.g_h,
  568.                full.g_x, full.g_y, full.g_w, full.g_h);
  569.           wind_set(wh, WF_CXYWH, full.g_x, full.g_y, full.g_w, full.g_h);
  570.           }
  571.      }
  572.  
  573.      WORD
  574. rc_equal(p1, p2)          /* tests for two rectangles equal     */
  575.      GRECT     *p1, *p2;
  576.      {
  577.      if ((p1->g_x != p2->g_x) ||
  578.          (p1->g_y != p2->g_y) ||
  579.          (p1->g_w != p2->g_w) ||
  580.          (p1->g_h != p2->g_h))
  581.           return(FALSE);
  582.      return(TRUE);
  583.      }
  584. əəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəəə