home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / program / progem / gem2.asc < prev    next >
Encoding:
Text File  |  1987-10-10  |  19.1 KB  |  417 lines

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