home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / apps / 659 / winshell / winshell.lst < prev    next >
Encoding:
File List  |  1993-01-01  |  44.2 KB  |  1,106 lines

  1. '
  2. ' *****************************************************************************
  3. ' *                                                                           *
  4. ' *                             GFA Basic Vers. 3+                            *
  5. ' *                          GEM Multiple Window Shell                        *
  6. ' *                           by Sterling Kelly Webb                          *
  7. ' *                                                                           *
  8. ' *                                    1992                                   *
  9. ' *                      ---------- SKWare One -----------                    *
  10. ' *                                                                           *
  11. ' *****************************************************************************
  12. '
  13. ' Date: 12-15-92
  14. '
  15. ' {some portions of this code adapted from code provided by  GFA Systemtechnik}
  16. ' {some portions of this code adapted from code by Jeff and Tim Randall,  1987}
  17. ' {some portions of this code adapted from GEMSHELL.GFA by John Eidsvoog, 1992}
  18. ' {Hey!  I wrote a lot of it myself!  Stop being so damn modest! SKWare1, 1992}
  19. '
  20. ' I suppose that WinShell is a response to the GEM Shell code recently
  21. ' posted by John Eidsvoog of the Codeheads. His stated concern was about
  22. ' how GFA programmers handle the redraws created by calling .ACC's
  23. ' (many are CodeHeads' pretty products), a reasonable concern, but...
  24. '
  25. ' As a GFA programmer, I don't honestly feel that the GFA code methods
  26. ' that John proposed are the proper, correct, or even desirous methods of
  27. ' managing GEM through GFA programming.   The xxxxW #N commands are
  28. ' inadequate, unreliable, and dangerous to the health of your operating
  29. ' system; even GFA discourages their use (by implication, at least)!
  30. '
  31. ' GFA provides command bindings for all AES libraries. But you have to
  32. ' understand that GEM leaves window management entirely up to the appli-
  33. ' ication programmer, and the AES provides a powerful set of tools to do
  34. ' it with. They can be used to produce a variety of interface protocols;
  35. ' This program is an illustration of the simplest, most elementary kind
  36. ' of GEM interface: a multiple window environment in which each window
  37. ' represents a buffer containing data of the same type (graphic images).
  38. '
  39. ' -------------------------- WINSHELL.GFA --------------------------
  40. '
  41. ' This program is designed to display multiple graphic windows,
  42. ' with GEM activities being handled directly through GFA commands.
  43. ' Although this example is for graphic display, it could be easily
  44. ' adapted to text buffers or any variety of multiple buffer operations.
  45. ' This program does not "do" anything, it only provides a structure
  46. ' and the GFA tools to manage the logic of GEM and the AES in a direct
  47. ' fashion. It also demonstrates some management of resource files, menus
  48. ' in particular. It is reasonably resolution-independent and non-hardware-
  49. ' oriented. This is about as GEM-legitimate as GFA gets, you guys...
  50. '
  51. ' GEM Multiple Window Shell may be freely distributed, dissected, and
  52. ' any portion of this code may be utilized in any way you deem useful.
  53. '
  54. ' (Despite the size of this file, the GFA code, when stripped of com-
  55. ' mentary and a few "teaching" lines, is less than 12 Kbytes in length.)
  56. '
  57. ' Ok, here we go...
  58. '
  59. ' ********************************* INIT ***********************************
  60. '
  61. '
  62. DEFINT "a-z"
  63. '
  64. ' Message buffer so our APPL can chat with the boys up at GEM HeadQuarters
  65. DIM pipeline&(20)
  66. adr_mes=V:pipeline&(0)                       ! this address used in EVNT_MULTI
  67. '
  68. ' gather basic information about the system executing this program
  69. max_x=WORK_OUT(0)                            ! horizontal monitor resolution
  70. max_y=WORK_OUT(1)                            ! vertical monitor resolution
  71. mid_x=SHR(max_x,1)                          ! center of screen --- x 'n y
  72. mid_y=SHR(max_y,1)
  73. '
  74. num_of_colors=WORK_OUT(13)                   ! number of displayable colors
  75. hi_color_index=PRED(num_of_colors)          ! highest color index 0-to-?
  76. num_of_planes=LEN(BIN$(hi_color_index))     ! true of all interleaved rasters
  77. ' and of course, Num_of_colors% = 2^Num_of_planes%, if you want it backwards
  78. '
  79. ' GFA always does this anyway, John, but why not? Couldn't hurt...
  80. ~APPL_INIT()
  81. ' and while we're at it, the basic question, who am I?
  82. ' when I'm interpreted...
  83. gfa=APPL_FIND("GFABASIC")                     ! GEM only accepts CAPS for this
  84. ' when I'm compiled...
  85. winshell=APPL_FIND("WINSHELL")                ! has to be eight chars, pad
  86. '
  87. ' this location is guaranteed, so Atari says...
  88. tos_version_number=VAL(HEX$(DPEEK(ADD(LPEEK(&H4F2),2))))
  89. ' say what? &H4f2 contains pointer to a LONG, least significant WORD of which
  90. ' contains the tos version number in hexadecimal, if =>2(.)00 allows 7 windows
  91. tos=DIV(tos_version_number,100)
  92. IF tos>1
  93.   max_num_of_windows=7
  94. ELSE
  95.   max_num_of_windows=4
  96. ENDIF
  97. ' shorthand, 'cause who want to type "max_num_of_windows" a hunnerd times?
  98. wn=max_num_of_windows
  99. '
  100. ' since this program is going to display pictures in Degas uncompressed format
  101. ' and the Degas picture format uses the hardware color registers for palettes
  102. ' (sorry, vdi-fans), we will gather this machine's current hardware register.
  103. userpal$=""
  104. FOR i=0 TO 15                         ! STe hardware register always 16 words
  105.   userpal$=userpal$+MKI$(XBIOS(7,i,-1))
  106. NEXT i
  107. ' a palette for every window and a window for every palette...
  108. DIM palette$(SUCC(wn)),empty!(SUCC(wn))
  109. FOR i=0 TO wn
  110.   palette$(i)=userpal$
  111.   empty!(i)=TRUE                      ! nobody home right now
  112. NEXT i
  113. '
  114. '
  115. ' first, check the maximum size limits to a window
  116. CLR wx,wy,ww,wh
  117. ~WIND_GET(0,4,wx,wy,ww,wh)       ! Get work area of the desktop (window 0)
  118. ' these coordinates are the absolute maximums for fulled windows
  119. abswx=wx
  120. abswy=wy
  121. absww=ww
  122. abswh=wh
  123. '
  124. ' Send a redraw message to the desktop and get that lovely green blotto...
  125. ~FORM_DIAL(3,0,0,0,0,wx,wy,ww,wh)
  126. '
  127. ' in order to have multiple windows (and images), we must able to maintain
  128. ' window parameters in tabled form: stored as indexed variables. Their names
  129. ' are shorthand for: window_x, window_y, window_width, window_height, which
  130. ' here ALWAYS refer to the total area of the window to its outer borders!
  131. DIM wx(9),wy(9),ww(9),wh(9)
  132. ' and a set of tables to store former values of the above variables
  133. DIM ox(9),oy(9),ow(9),oh(9)       ! for restoration after WM_FULLED
  134. ' a variable to hold the window_title strings
  135. DIM title_string&(80)                 ! an explanation of this odd behavior
  136. ARRAYFILL title_string&(),0           ! can be found at the end of the code
  137. DIM wintitle$(9)                      ! under the heading: ODD BEHAVIOR
  138. FOR i=0 TO 9
  139.   wintitle$(i)="No Loaded File"
  140.   BMOVE V:wintitle$(i),V:title_string&(MUL(i,8)),14
  141. NEXT i
  142. '
  143. ' since memory for screens will be pre-allotted, provide the option to limit the
  144. ' number of screens to four even if seven windows are possible. Particularly
  145. ' useful where screens are 153,600 bytes each...
  146. IF wn=7
  147.   ALERT 2," | To Reduce Screen Memory, | You Have the Option to | Limit Windows to Four. ",2,"Four|Seven",choice
  148.   IF choice=1
  149.     wn=4
  150.   ENDIF
  151. ENDIF
  152. '
  153. ' now we're going to initialize our own WINDOW_LIST:  a stack that maintains
  154. ' the order in which windows are opened and closed; our own window management
  155. ' tool. The actual values contained in Window%() are the GEM window handles.
  156. ' GEM keeps a stack, too, but it holds the displayed order of overlaps in
  157. ' any current window configuration on-screen. We need our stack to sort
  158. ' through their stack!
  159. DIM window(9)
  160. FOR activew=1 TO wn                ! all attributes except the INFO line.
  161.   window(activew)=WIND_CREATE(&X111111101111,wx,wy,ww,wh)
  162.   wx(activew)=wx                  ! initialize the tables
  163.   wy(activew)=wy
  164.   ww(activew)=ww
  165.   wh(activew)=wh
  166.   ox(activew)=wx
  167.   oy(activew)=wy
  168.   ow(activew)=ww
  169.   oh(activew)=wh
  170. NEXT activew
  171. ' windows are indexed by us from 1; when no window is open, this index is 0.
  172. activew=0
  173. '
  174. ' this program is intended to display only screen-size raster images
  175. ' making it suitable for simple picture formats, like PrismPaint, Degas,
  176. ' NeoChrome and TINY images. The following memory array creates a single
  177. ' large buffer which will hold four images in a vertical stack of
  178. ' continuous scan-lines (like the buffer in Spectrum512). You will note
  179. ' that, since windows are indexed 1 through Wn%, that leaves the 0th screen
  180. ' position in the buffer always empty: the perfect place to store a
  181. ' temporary screen or a full-screen menu of icons, etc. for rapid display
  182. ' or for use as an offscreen drawing buffer with xbios 5 paging for some
  183. ' smooth fast screen alteration; an extra screen always a handy thing to have...
  184. '
  185. ' first, let's calculate the memory for a bit-interleaved screen raster
  186. scan_line_byte_length=(SUCC(max_x)*num_of_planes)\8
  187. screen_size=SUCC(max_y)*scan_line_byte_length
  188. integer_data_length=4
  189. screen_array_size=PRED(screen_size\integer_data_length)
  190. '
  191. DIM picture(screen_array_size,wn)  ! GFA always creates one more than asked!
  192. '
  193. ' we will be loading only DEGAS uncompressed files in this example.
  194. '
  195. ' WIDTH may be defined in two contradictory ways: first, as a diferential
  196. ' distance between two pixel positions, and second as the number_of_pixels
  197. ' per scan line. The two definitions differ from each other by ONE, as in
  198. ' an image 640 pixels WIDE has a WIDTH of 639. The AES always uses the
  199. ' first definition (differential width) as WIDTH and this program does
  200. ' the same for the images it displays (also for HEIGHT and HIGH) as below:
  201. '
  202. DIM width(9),height(9),wide(9),high(9)  ! tables for image dimensions
  203. FOR i=0 TO 8
  204.   width(i)=max_x                         ! initialize for all images
  205.   height(i)=max_y
  206.   wide(i)=SUCC(max_x)
  207.   high(i)=SUCC(max_y)
  208. NEXT i
  209. '
  210. ' for image display, we need to specify the starting coordinates IN THE
  211. ' IMAGE to be placed in the upper left-hand corner of the window display
  212. DIM startx(9),starty(9)
  213. '
  214. ' indexing all these variables also insures that they will be something that
  215. ' ALL variables should be:  GLOBAL! GLOBAL! GLOBAL!  (OK, enough prejudices)
  216. '
  217. DIM file$(9)                     ! names of loaded files
  218. FOR i=0 TO 9
  219.   file$(i)="                    "+CHR$(0)
  220. NEXT i
  221. ' where to get'em
  222. path$=CHR$(ADD(GEMDOS(25),65))+":\*.PI"+STR$(SUCC(XBIOS(4)))
  223. '
  224. '
  225. DIM in_use!(9)                   ! records whether window is open or closed
  226. CLR rez&
  227. '
  228. '
  229. ' the WinShell menu contains most of the neat stuff that Atari wants
  230. ' a menu to have, where they want it to have them, named the way they
  231. ' want it named, with the keyboard equivalents they want, yassir, baas!
  232. '
  233. ' our menu is to be loaded from this program's .rsc file
  234. ' every GFA 3+ programmer has the Resource Construction Set #2
  235. ' every GFA 3+ programmer should USE IT! --- USE IT! --- USE IT!
  236. '
  237. ' ok, rant'n'rave over...
  238. '
  239. ' create our own little array to check out the .rsc file header
  240. DIM rsc_header&(20)
  241. ' insert the name of your .rsc here...
  242. rsc_file$="\winshell.rsc"
  243. ' we can do this, can't we? sure we can...
  244. IF EXIST(rsc_file$)
  245.   OPEN "I",#1,rsc_file$
  246.   rsc_file_len=LOF(#1)
  247.   BGET #1,V:rsc_header&(0),39
  248.   CLOSE #1
  249. ELSE
  250.   EDIT
  251. ENDIF
  252. '
  253. ' some questions we might be interested in: how many trees in this .rsc?
  254. num_of_trees=rsc_header&(11)         ! now you know where the num_of_trees is!
  255. IF rsc_header&(17)<>rsc_file_len
  256.   ' oh well, it probably is, anyway...
  257.   ' Alert 3," | May Not Be Valid | RSC File Format! | ",1,"GO AHEAD!",Courage%
  258. ENDIF
  259. IF rsc_header&(11)<1
  260.   ' if the num_of_trees is not at least 1, we in real trouble...
  261.   ALERT 3," | Not Object Tree Structure! | ",1,"Abort!",uh_oh
  262.   EDIT
  263. ENDIF
  264. '
  265. ' other resource-ful questions we might be interested in some other time...
  266. '
  267. ' Rsc_header&(1) contains the Offset_to_first_object%
  268. ' Rsc_header&(2) contains the Offset_to_first_tedinfo_object%
  269. ' Rsc_header&(3) contains the Offset_to_first_iconblk_object%
  270. ' Rsc_header&(4) contains the Offset_to_first_bitblk_object%
  271. ' Rsc_header&(5) contains the Offset_to_free_string_pointer_table%
  272. ' Rsc_header&(6) contains the Offset_to_tied_string_table%
  273. ' Rsc_header&(7) contains the Offset_to_icon_data%
  274. ' Rsc_header&(8) contains the Offset_to_free_images%
  275. ' Rsc_header&(9) contains the Offset_to_object_tree_pointer_table%
  276. ' Rsc_header&(10) contains the Num_of_objects%
  277. ' Rsc_header&(11) contains the Num_of_trees%
  278. ' Rsc_header&(12) contains the Num_of_tedinfos%
  279. ' Rsc_header&(13) contains the Num_of_iconblks%
  280. ' Rsc_header&(14) contains the Num_of_bitblks%
  281. ' Rsc_header&(15) contains the Num_of_free_strings%
  282. ' Rsc_header&(16) contains the Num_of_free_images%
  283. ' Rsc_header&(17) contains the Len_of_rsc_file%
  284. '
  285. ' oh well, I suppose we have to give a little memory back to GEM...
  286. ' for absolute compatibility on all TOSes, measure memory in 256-byte chunks
  287. RESERVE FRE(0)-10240
  288. ' once RESERVEd, we MUST exit through a RESERVE FRE(0)+10240 statement
  289. ON ERROR GOSUB escape
  290. ON BREAK GOSUB escape
  291. ' now, let GEM load the .rsc file up top
  292. rsc_ok!=RSRC_LOAD(rsc_file$)
  293. IF rsc_ok!=FALSE
  294.   ' no gottum .rsc! how come?
  295.   ALERT 3," | Cannot Find WINSHELL.RSC! |  Your Next Stop Will Be | ",1,"DeskTop",nurtz
  296.   @escape
  297. ENDIF
  298. '
  299. ' now, here's how a program can look for a principal menu in a resource file
  300. men_tree=-1
  301. FOR i=0 TO PRED(num_of_trees)
  302.   ~RSRC_GADDR(0,i,tree_adr)
  303.   ' the first title of the menu is the 3th (=0,1,2,3=4th) obj in a menu tree
  304.   IF OB_TYPE(tree_adr,3)=32            ! 32 is the ob_type of a menu title
  305.     men_tree=i
  306.   ENDIF
  307.   ' this only looks for ONE, but a resource may contain as many as it wants to!
  308.   ' you could always keep on looking till you find'em all, you know...
  309.   EXIT IF men_tree>-1
  310. NEXT i
  311. '
  312. IF men_tree=-1
  313.   ' this .rsc has no menu!
  314.   @escape
  315. ELSE
  316.   '
  317.   ' DON'T YOU JUST HATE IT WHEN...
  318.   ' you start a GEM program in low resolution and its menu wrap around the
  319.   ' screen from the right to the left and screws everything up!
  320.   '
  321.   ' I purposely created a high-rez menu, wide enough for its entries to do
  322.   ' a screen wrap in low resolution, so I could show you how to fix'em
  323.   ' on the fly, so one menu will work in all resolutions. The only hitch is
  324.   ' the menu titles themselves must fit in the low-resolution width.
  325.   '
  326.   ' get the address of the menu tree
  327.   ~RSRC_GADDR(0,men_tree,tree_adr)
  328.   ' and, of course, the position of that line below the menu is...
  329.   bar=ADD(OB_Y(tree_adr,1),OB_H(tree_adr,1))
  330.   ' ...and, if we want to avoid it, we want the first position beneath it
  331.   ADD bar,1
  332.   ' nor do we want the menu_bar line to wrap around the screen, either...
  333.   OB_W(tree_adr,1)=max_x
  334.   ' probably already is...
  335.   OB_Y(tree_adr,2)=0
  336.   ' skip over the menu titles themselves, which follow, because
  337.   ' they are the children of the menu title box...
  338.   obj=3
  339.   WHILE OB_TYPE(tree_adr,obj)=32             ! Ob_type 32 = Menu Title
  340.     obj=ADD(obj,1)
  341.   WEND
  342.   ' set the menu background to whole screen
  343.   OB_H(tree_adr,obj)=SUB(max_y,bar)
  344.   OB_Y(tree_adr,obj)=bar
  345.   ' go through all the menu entry objects and ...
  346.   REPEAT
  347.     ' ...skip over the G_STRING (ob_type=28) items until...
  348.     ' (we will also need to know the ob_type of the previous object)
  349.     last_obtype=OB_TYPE(tree_adr,obj)
  350.     obj=ADD(obj,1)
  351.     ' (set all G_BOX objects to new coordinates, ignoring ob_type=28=entries,
  352.     ' UNLESS it's inside ANOTHER G_BOX, especially one with an ICON in it!)
  353.     IF OB_TYPE(tree_adr,obj)=20 AND last_obtype<>20 AND last_obtype<>23
  354.       ' ...we can check all the G_BOX objects in the menu tree to see...
  355.       obx=OB_X(tree_adr,obj)
  356.       obw=OB_W(tree_adr,obj)
  357.       ' ...if they're going to extend off the screen and wrap around and...
  358.       IF ADD(obx,obw)>max_x
  359.         obe=ADD(obx,obw)
  360.         IF obe>max_x
  361.           offset=SUB(obe,max_x)
  362.           ' ...move them to the left to avoid the screen edge!
  363.           OB_X(tree_adr,obj)=SUB(obx,offset)
  364.         ENDIF
  365.       ENDIF
  366.     ENDIF
  367.   UNTIL OB_TYPE(tree_adr,obj)=0              ! end of the menu tree...
  368. ENDIF
  369. '
  370. '
  371. ' Jeez, I think we're ready to start now!
  372. '
  373. '
  374. ' these memory locations WILL NOT not move around... (uh, better not!)
  375. ABSOLUTE mes_type&,adr_mes                        ! Adr_mes is V:Pipeline&(0)
  376. ABSOLUTE m_title&,ADD(adr_mes,6)                  ! M_title& is Pipeline&(3)
  377. ABSOLUTE m_entry&,ADD(adr_mes,8)                  ! M_entry& is Pipeline&(4)
  378. ' get the menu_bar address
  379. ~RSRC_GADDR(0,men_tree,menu_adr)
  380. ' put up the menu
  381. ~MENU_BAR(menu_adr,1)
  382. '
  383. ' we're about ready, but first I'd like to advertize a little...
  384. @about
  385. '
  386. ' ********************************* MAIN ***********************************
  387. '
  388. DO
  389.   '
  390.   oret=ret                                      ! last return from evnt_multi
  391.   '
  392.   ' This EVNT_MULTI looks for timer_evnts, message_evnts, and keyboard_evnts
  393.   ret=EVNT_MULTI(&X110001,0,0,0,0,0,0,0,0,0,0,0,0,0,adr_mes,5,mx&,my&,mk&,kstat&,key&,clks&)
  394.   '
  395.   ' @Pipeline     ! un-comment this if you want to see message buffer values
  396.   '
  397.   ' first, is it a keyboard event?
  398.   IF key&>0
  399.     scan_code=BYTE{V:key&}
  400.     ascii_code=BYTE{SUCC(V:key&)}
  401.     key&=0
  402.     ' here's one of those cases where GFA's mouse handling and GEM's mouse stuff
  403.     ' quarrel over the poor little rodent, causing it to flicker like crazy.
  404.     ' another such occurance is the FILESELECT that comes on with a stuck mouse.
  405.     ' the cure's a brief refreshing EVNT_MULTI; GEM really likes these quickies.
  406.     REPEAT
  407.       ' repeat until the keyboard event is cleared out
  408.       junk_ret=EVNT_MULTI(&X111111,0,0,0,0,0,0,0,0,0,0,0,0,0,adr_mes,1,mx&,my&,mk&,kstat&,key&,clk&)
  409.     UNTIL key&=0
  410.     @key_reader(ascii_code,activew)
  411.   ENDIF
  412.   '
  413.   ' we will look to the first message of the buffer as our signal to act:
  414.   '
  415.   ' so, is it a message event?
  416.   message=mes_type&                     ! fixed memory location: Pipeline&(0)
  417.   '
  418.   SELECT message
  419.     '
  420.   CASE 10                                ! these are menu messages
  421.     '
  422.     ' object number of the menu item selected
  423.     obj_nr=m_entry&                     ! fixed location: Pipeline&(4)
  424.     ' we handle response to all menu messages, natch
  425.     mt&=m_title&                         ! save the title; it may change...
  426.     @menu_wrangler(obj_nr,activew)     ! go get wrangled
  427.     ' since we use first message, we must clear first message = no repeats
  428.     pipeline&(0)=0                       ! I heard ya already!
  429.     CLR menu                            ! compulsive neatness
  430.     ' turn off menu title highlight
  431.     ~MENU_TNORMAL(menu_adr,mt&,1)       ! turn out the light
  432.     '
  433.   CASE 20 TO 28                          ! these are window messages
  434.     '
  435.     window_handle=pipeline&(3)          ! which window?
  436.     IF window_handle>wn
  437.       ' this could never happen!  Still, I have heard the sound
  438.       SOUND 1,15,3,2,16
  439.       SOUND 1,0,0,0
  440.     ELSE
  441.       ' we do windows, too...            ! window messages are wrangled also
  442.       @western_union(window_handle,activew)
  443.     ENDIF
  444.     ' clear the message buffer of the primary message
  445.     pipeline&(0)=0                       ! enough
  446.     '
  447.   ENDSELECT
  448.   '
  449. LOOP
  450. '
  451. @quit
  452. '
  453. ' ********************************* PROC ***********************************
  454. '
  455. PROCEDURE menu_wrangler(menu,VAR activew) ! menu messages come here
  456.   '
  457.   ' the value passed as Menu% is the obj_number of the menu entry chosen
  458.   SELECT menu
  459.     '
  460.     ' the actual object numbers must be read from the resource file
  461.     ' by a program designed to analyze and record their values for you.
  462.     '
  463.   CASE 9                                    ! Credit_panel
  464.     @about
  465.   CASE 18                                   ! New_picture
  466.     @new_picture(activew)
  467.   CASE 19                                   ! Open_window
  468.     @open(activew)
  469.   CASE 20                                   ! Close_window
  470.     @close(activew)
  471.   CASE 27                                   ! Quit_program
  472.     @quit
  473.   DEFAULT
  474.     @escape
  475.   ENDSELECT
  476.   '
  477. RETURN
  478. '
  479. PROCEDURE key_reader(key,VAR activew)     ! key messages come here
  480.   '
  481.   SELECT key
  482.   CASE 14                                   ! you get the idea...
  483.     @new_picture(activew)
  484.   CASE 15
  485.     @open(activew)
  486.   CASE 17
  487.     @quit
  488.   CASE 23
  489.     @close(activew)
  490.   ENDSELECT
  491.   '
  492. RETURN
  493. '
  494. '
  495. PROCEDURE western_union(winhand,VAR activew)     ! window messages come here
  496.   '
  497.   window_message=pipeline&(0)                     ! this is the message
  498.   '
  499.   ' This is our window stack manager...
  500.   w=1                                             ! this little roundel
  501.   REPEAT                                           ! flips through our
  502.     EXIT IF window(w)=winhand                   ! window stack, looking
  503.     ADD w,1                                       ! at each one in order
  504.   UNTIL w>wn                                     ! for a match to windohandle
  505.   '
  506.   IF w=<wn
  507.     '
  508.     ' the message may refer to windows other than the active window,
  509.     ' especially in the case of redraws of non-active windows
  510.     ' and in the case of a non-active window being made the new
  511.     ' active window. So, we will change the window index to that of
  512.     ' the window being affected by the menu message and then restore
  513.     ' the window index after the message has been acted upon...
  514.     ' ...unless the message requires a change of window index.
  515.     ' this insures that all window commands may be safely made
  516.     ' with Activew% as the window index in all cases, for simplicity.
  517.     '
  518.     resactivew=activew             ! save the true active window index
  519.     activew=w                      ! temporary window index
  520.     '
  521.     wx(activew)=pipeline&(4)       ! intout coordinates for window message:
  522.     wy(activew)=pipeline&(5)       ! very handy for updating our table values
  523.     ww(activew)=pipeline&(6)       ! in theory (i.e., with one window), this
  524.     wh(activew)=pipeline&(7)       ! would handle the table values without
  525.     '                                ! our having to bother with updating them;
  526.     '                                ! however, in practice, we must update
  527.     '                                ! them whenever they are changed inside
  528.     '                                ! this procedure (or any procedure called
  529.     '                                ! from within this procedure (see below)
  530.     '
  531.     ' returns window workarea (without borders) of this window
  532.     ~WIND_GET(window(activew),4,ax,ay,aw,ah)
  533.     '
  534.     SELECT window_message           ! What's the word?
  535.       '
  536.     CASE 20                          ! WM_REDRAW
  537.       @redraw
  538.       activew=resactivew           ! restore the proper window index
  539.       '
  540.     CASE 21                          ! WM_TOPPED
  541.       ' in this case the proper window index is changed to a new window...
  542.       ' = top this window = make this window the active window
  543.       ~WIND_SET(window(activew),10,wx(activew),wy(activew),ww(activew),wh(activew))
  544.       ' set the color palette for this window
  545.       ~XBIOS(6,L:V:palette$(activew))
  546.       '
  547.     CASE 22                          ! WM_CLOSED
  548.       ' in this case the proper window index is changed to a new window...
  549.       @close(activew)
  550.       ' the color palette is set for new window in PRO CLOSE
  551.       '
  552.       '
  553.       ' in all the following routines, the active window index does not change.
  554.       '
  555.     CASE 23                          ! WM_FULLED
  556.       '
  557.       full!=NOT full!                ! Toggle between full and normal
  558.       IF full!=TRUE
  559.         ox(activew)=wx(activew)  ! save the current window coordinates for
  560.         oy(activew)=wy(activew)  ! restoration after the fulling operation
  561.         ow(activew)=ww(activew)
  562.         oh(activew)=wh(activew)
  563.         ' record slider positions for restoration
  564.         ~WIND_GET(window(activew),8,ohmark,dum,dum,dum)
  565.         ~WIND_GET(window(activew),9,ovmark,dum,dum,dum)
  566.         ' record image starting coordinates for restoration
  567.         ostartx=startx(activew)
  568.         ostarty=starty(activew)
  569.         ' = close the window at the old size
  570.         ~WIND_CLOSE(window(activew))
  571.         ' = re-open the same window at maximum allowed size
  572.         ~WIND_OPEN(window(activew),abswx,abswy,absww,abswh)
  573.         ' = returns new window workarea
  574.         ~WIND_GET(window(activew),4,ax,ay,aw,ah)
  575.         @bound_check(aw,ah)
  576.         @size_both_sliders(activew)
  577.       ELSE
  578.         ' restore the window to its former coordinates
  579.         ~WIND_SET(window(activew),5,ox(activew),oy(activew),ow(activew),oh(activew))
  580.         ' restore image starting coordinates
  581.         startx(activew)=ostartx
  582.         starty(activew)=ostarty
  583.         ' restore slider positions
  584.         ~WIND_SET(window(activew),8,ohmark,dum,dum,dum)
  585.         ~WIND_SET(window(activew),9,ovmark,dum,dum,dum)
  586.         ' = returns new window workarea
  587.         ~WIND_GET(window(activew),4,ax,ay,aw,ah)
  588.         @bound_check(aw,ah)
  589.         @size_both_sliders(activew)
  590.         ' forced re_draw required for new image coordinates
  591.         @refresh(activew)
  592.         ' see, I told you GEM left the management of windows to the programmer!
  593.       ENDIF
  594.       '
  595.     CASE 24                          ! WM_ARROWED
  596.       ' an additional message is included, indicating which gadget was twiddled!
  597.       sub_message=SUCC(pipeline&(4))
  598.       ON sub_message GOSUB raster_up,raster_down,pixel_row_up,pixel_row_down,raster_left,raster_right,pixel_col_right,pixel_col_left
  599.       ' forced re_draw required for new image coordinates
  600.       @refresh(activew)
  601.       '
  602.     CASE 25                          ! WM_HSLID
  603.       ' GEM passes new position to appl
  604.       hmark=pipeline&(4)
  605.       @use_horz_slider(hmark)
  606.       ' forced re_draw required for new image coordinates
  607.       @refresh(activew)
  608.       '
  609.     CASE 26                          ! WM_VSLID
  610.       ' GEM passes new position to appl
  611.       vmark=pipeline&(4)
  612.       @use_vert_slider(vmark)
  613.       ' forced re_draw required for new image coordinates
  614.       @refresh(activew)
  615.       '
  616.     CASE 27                          ! WM_SIZED
  617.       full!=FALSE
  618.       ' numerical values indicate minimum window sizes, set as you wish
  619.       ' = set this window to these dimensions at this position
  620.       ~WIND_SET(window(activew),5,wx(activew),wy(activew),MAX(80,ww(activew)),MAX(48,wh(activew)))
  621.       ' re-set table values right away whenever changed
  622.       ~WIND_GET(window(activew),5,wx(activew),wy(activew),ww(activew),wh(activew))
  623.       ' = returns new window workarea
  624.       ~WIND_GET(window(activew),4,ax,ay,aw,ah)
  625.       @bound_check(aw,ah)
  626.       @size_both_sliders(activew)
  627.       '
  628.     CASE 28                          ! WM_MOVED
  629.       full!=FALSE
  630.       ' = set this window to these dimensions at this position
  631.       ~WIND_SET(window(activew),5,wx(activew),wy(activew),ww(activew),wh(activew))
  632.       '
  633.     ENDSELECT
  634.   ENDIF
  635.   '
  636. RETURN
  637. '
  638. ' forced re_draw
  639. PROCEDURE refresh(activew)
  640.   '
  641.   @bound_check(aw,ah)
  642.   ~WIND_UPDATE(1)
  643.   RC_COPY V:picture(0,window(activew)),startx(activew),starty(activew),aw,ah TO XBIOS(3),ax,ay,3
  644.   ~WIND_UPDATE(0)
  645.   '
  646. RETURN
  647. '
  648. ' these are the routines to size the sliders and handle messages
  649. ' involving the sliders and arrows fiddled at by the user...
  650. '
  651. PROCEDURE size_both_sliders(activew)  ! sets size and position of both sliders
  652.   ' GEM size index = 0 to 1000
  653.   horz_slider_size=INT((aw/(width(activew)))*1000)
  654.   vert_slider_size=INT((ah/(height(activew)))*1000)
  655.   ' set sizes of both sliders
  656.   ~WIND_SET(window(activew),15,horz_slider_size,dum,dum,dum)
  657.   ~WIND_SET(window(activew),16,vert_slider_size,dum,dum,dum)
  658.   ' calculate position of both sliders
  659.   @set_vmark(activew)
  660.   @set_hmark(activew)
  661.   ' set position of both sliders
  662.   @adjust_vert_slider(vmark,activew)
  663.   @adjust_horz_slider(hmark,activew)
  664. RETURN
  665. PROCEDURE set_hmark(activew)        ! sets horizontal position of slider
  666.   IF width(activew)>aw
  667.     hmark=INT((startx(activew)/(width(activew)-aw))*1000)
  668.   ELSE
  669.     hmark=0
  670.   ENDIF
  671. RETURN
  672. PROCEDURE set_vmark(activew)        ! sets vertical position of slider
  673.   IF height(activew)>ah
  674.     vmark=INT((starty(activew)/(height(activew)-ah))*1000)
  675.   ELSE
  676.     vmark=0
  677.   ENDIF
  678. RETURN
  679. PROCEDURE use_horz_slider(hmark)    ! calculates horizontal position of image
  680.   ' = sets slider to position index indicated by user and passed us in pipeline
  681.   ~WIND_SET(window(activew),8,hmark,dum,dum,dum)
  682.   ' calculate new position in image
  683.   ' GEM position index = 0 to 1000
  684.   place=INT((hmark/1000)*(width(activew)-aw))
  685.   startx(activew)=place
  686. RETURN
  687. PROCEDURE use_vert_slider(vmark)    ! calculates vertical position of image
  688.   ' = sets slider to position index indicated by user and passed us in pipeline
  689.   ~WIND_SET(window(activew),9,vmark,dum,dum,dum)
  690.   ' calculate new position in image
  691.   ' GEM position index = 0 to 1000
  692.   place=INT((vmark/1000)*(height(activew)-ah))
  693.   starty(activew)=place
  694. RETURN
  695. PROCEDURE adjust_horz_slider(hmark,activew) ! sets horizontal slider
  696.   ' = sets position index of horizontal slider
  697.   ~WIND_SET(window(activew),8,hmark,dum,dum,dum)
  698. RETURN
  699. PROCEDURE adjust_vert_slider(vmark,activew) ! sets vertical slider
  700.   ' = sets position index of vertical slider
  701.   ~WIND_SET(window(activew),9,vmark,dum,dum,dum)
  702. RETURN
  703. PROCEDURE raster_up                           ! jump up slider-height
  704.   SUB starty(activew),ah
  705.   @bound_check(aw,ah)
  706.   @set_vmark(activew)
  707.   @adjust_vert_slider(vmark,activew)
  708. RETURN
  709. PROCEDURE raster_down                         ! jump down slider-height
  710.   ADD starty(activew),ah
  711.   @bound_check(aw,ah)
  712.   @set_vmark(activew)
  713.   @adjust_vert_slider(vmark,activew)
  714. RETURN
  715. PROCEDURE pixel_row_up                        ! jump up one scan line
  716.   DEC starty(activew)
  717.   @set_vmark(activew)
  718.   @adjust_vert_slider(vmark,activew)
  719. RETURN
  720. PROCEDURE pixel_row_down                      ! jump down one scan line
  721.   ADD starty(activew),1
  722.   @set_vmark(activew)
  723.   @adjust_vert_slider(vmark,activew)
  724. RETURN
  725. PROCEDURE raster_left                         ! jump left slider-width
  726.   SUB startx(activew),aw
  727.   @bound_check(aw,ah)
  728.   @set_hmark(activew)
  729.   @adjust_horz_slider(hmark,activew)
  730. RETURN
  731. PROCEDURE raster_right                        ! jump right slider-width
  732.   ADD startx(activew),aw
  733.   @bound_check(aw,ah)
  734.   @set_hmark(activew)
  735.   @adjust_horz_slider(hmark,activew)
  736. RETURN
  737. PROCEDURE pixel_col_left                      ! jump left one pixel
  738.   ADD startx(activew),1
  739.   @set_hmark(activew)
  740.   @adjust_horz_slider(hmark,activew)
  741. RETURN
  742. PROCEDURE pixel_col_right                     ! jump right one pixel
  743.   DEC startx(activew)
  744.   @set_hmark(activew)
  745.   @adjust_horz_slider(hmark,activew)
  746. RETURN
  747. PROCEDURE bound_check(wind_wide,wind_high)  ! check boundary conditions
  748.   IF ADD(startx(activew),wind_wide)>max_x ! and curb excesses; keep in
  749.     startx(activew)=SUB(max_x,wind_wide)  ! limits of image boundaries
  750.   ENDIF
  751.   IF startx(activew)<0
  752.     startx(activew)=0
  753.   ENDIF
  754.   IF ADD(starty(activew),wind_high)>max_y
  755.     starty(activew)=SUB(max_y,wind_high)
  756.   ENDIF
  757.   IF starty(activew)<0
  758.     starty(activew)=0
  759.   ENDIF
  760. RETURN
  761. '
  762. '
  763. PROCEDURE open(VAR activew)                 ! Open Window
  764.   '
  765.   l=1
  766.   REPEAT                                     ! window list manager
  767.     EXIT IF NOT in_use!(l)
  768.     ADD l,1
  769.   UNTIL l>wn
  770.   IF l=<wn
  771.     activew=l                              ! assign a window
  772.     IF empty!(window(activew))=FALSE       ! previously closed
  773.       ~WIND_OPEN(window(activew),wx(activew),wy(activew),ww(activew),wh(activew))
  774.       in_use!(activew)=TRUE                 ! restore its status
  775.     ENDIF
  776.     IF empty!(window(activew))=TRUE        ! never opened yet
  777.       ~WIND_OPEN(window(activew),wx(activew),wy(activew),ww(activew),wh(activew))
  778.       ~WIND_GET(window(activew),5,wx(activew),wy(activew),ww(activew),wh(activew))
  779.       ~WIND_GET(window(activew),4,ax,ay,aw,ah)
  780.       @size_both_sliders(activew)
  781.       adr_ptr=V:title_string&(MUL(activew,8))
  782.       ' = pass window title address to AES   ! see ODD BEHAVIOR at end of code
  783.       ~WIND_SET(window(activew),2,CARD(SWAP(adr_ptr)),CARD(adr_ptr),0,0)
  784.       in_use!(activew)=TRUE
  785.       @load_picture(activew)                ! if CANCELED, In_use! is set false
  786.     ENDIF
  787.   ELSE
  788.     ALERT 3," | No More Windows |    Available! | ",1,"Sorry",nurtz
  789.   ENDIF
  790.   ~XBIOS(6,L:V:palette$(activew))
  791.   '
  792. RETURN
  793. '
  794. '
  795. PROCEDURE close(VAR activew)                ! Close Window
  796.   '
  797.   ~WIND_CLOSE(window(activew))             ! close the open window
  798.   in_use!(activew)=FALSE                    ! tag it as not_in_use
  799.   ' find out who's now top of GEM's little list...
  800.   ~WIND_GET(0,10,windohand,dum,dum,dum)  ! get handle of new active window
  801.   ' so we can keep them straight, re-set the ACTIVEW variable
  802.   l=1
  803.   REPEAT                                     ! window list manager
  804.     EXIT IF window(l)=windohand
  805.     ADD l,1
  806.   UNTIL l>wn
  807.   IF l>wn
  808.     activew=0
  809.   ELSE
  810.     activew=l
  811.     ' top new active window
  812.     ~WIND_SET(window(activew),10,wx(activew),wy(activew),ww(activew),wh(activew))
  813.   ENDIF
  814.   ' re-set the color palette for the new window
  815.   ~XBIOS(6,L:V:palette$(activew))
  816.   '
  817. RETURN
  818. '
  819. '
  820. PROCEDURE new_picture(VAR activew)          ! New Picture
  821.   '
  822.   IF activew>0 AND activew=<wn
  823.     @load_picture(activew)
  824.   ELSE IF activew=0 AND empty!(1)=TRUE
  825.     @open(activew)
  826.   ELSE
  827.     ALERT 1," |   No Window Open! | Open a Window First!",1,"Will Do!",nurtz
  828.   ENDIF
  829.   '
  830. RETURN
  831. '
  832. '
  833. PROCEDURE about                              ! About WinShell
  834.   '
  835.   ~XBIOS(6,L:V:userpal$)                     ! return to desk colors
  836.   ~WIND_UPDATE(1)
  837.   about=1                                   ! credits is second tree in .rsc
  838.   ~RSRC_GADDR(0,about,about_adr)           ! get address of about object tree
  839.   ok=6                                      ! objc_number of exit button
  840.   ~FORM_CENTER(about_adr,x0,y0,x1,y1)   ! set coordinates to center dialog
  841.   ~FORM_DIAL(0,mid_x,mid_y,2,2,x0,y0,x1,y1)      ! reserve screen area
  842.   ~FORM_DIAL(1,mid_x,mid_y,2,2,x0,y0,x1,y1)      ! draw pop_up_out lines
  843.   ~OBJC_DRAW(about_adr,0,7,x0,y0,x1,y1)           ! draw the object tree
  844.   ' NOTE: NEVER USE A DEPTH INDEX GREATER THAN SEVEN!  ! La Bomba!
  845.   ~OBJC_CHANGE(about_adr,ok,0,x0,y0,x1,y1,32,1)  ! unselect the exit
  846.   REPEAT
  847.     exit=FORM_DO(about_adr,0)                        ! let GEM manage things
  848.   UNTIL exit=ok
  849.   ~OBJC_CHANGE(about_adr,exit,0,x0,y0,x1,y1,33,1)! select the exit
  850.   PAUSE 24                           ! WAIT, so user can see the selected button
  851.   ~FORM_DIAL(3,mid_x,mid_y,2,2,x0,y0,x1,y1)   ! release reserved screen &
  852.   '                                                 ! slap down green_screen
  853.   ~FORM_DIAL(2,mid_x,mid_y,2,2,x0,y0,x1,y1)   ! draw pop_back_in lines
  854.   ~WIND_UPDATE(0)
  855.   ~XBIOS(6,L:V:palette$(activew))                  ! return to image colors
  856.   '
  857. RETURN
  858. '
  859. '
  860. PROCEDURE redraw                   ! Walk the rectangle list and do redraws
  861.   '
  862.   ~WIND_UPDATE(1)                  ! Lock out other activity while we redraw
  863.   ~WIND_GET(window(activew),11,rx,ry,rw,rh) ! Get first rectangle in the list
  864.   ~WIND_GET(window(activew),4,ax,ay,aw,ah)  ! Workarea of our window
  865.   REPEAT
  866.     IF RC_INTERSECT(ax,ay,aw,ah,rx,ry,rw,rh)  ! Find intersection
  867.       CLIP rx,ry,rw,rh OFFSET ax,ay   ! Set clipping to the area in question
  868.       DEFFILL 0,2,8
  869.       PBOX rx,ry,ADD(rx,rw),ADD(ry,rh)
  870.       @fillwindow                           ! Call our routine to redraw the area
  871.       CLIP 0,0,max_x,max_y OFFSET 0,0     ! Reset full-screen clipping
  872.     ENDIF
  873.     ~WIND_GET(window(activew),12,rx,ry,rw,rh) ! Get next rectangle in the list
  874.   UNTIL rw=0 AND rh=0                ! Keep repeating until no more rectangles
  875.   ~WIND_UPDATE(0)                      ! Re-enable other GEM activity
  876.   '
  877. RETURN
  878. '
  879. '
  880. PROCEDURE fillwindow                   ! Redraw sections of our window
  881.   '
  882.   c1=ADD(startx(activew),SUB(rx,ax))
  883.   c2=ADD(starty(activew),SUB(ry,ay))
  884.   IF ADD(rx,rw)=<max_x
  885.     c3=ADD(c1,rw)
  886.   ELSE
  887.     c3=ADD(SUB(startx(activew),ax),max_x)
  888.   ENDIF
  889.   IF ADD(ry,rh)=<max_y
  890.     c4=ADD(c2,rh)
  891.   ELSE
  892.     c4=ADD(SUB(starty(activew),ay),max_y)
  893.   ENDIF
  894.   '
  895.   ' RC_COPY is ideal for screen-sized "pages," but of no use where image size
  896.   ' is variable (IMG, PCX, IFF, TIFF, etc.). There, the safest thing is to use
  897.   ' copy_raster_opaque and copy_raster_transparent or to use GFA's VDI BITBLT,
  898.   ' the BITBLT Source%(),Destination%(),Parameters%() form of GFA's BITBLiTters.
  899.   '
  900.   RC_COPY V:picture(0,window(activew)),c1,c2,rw,rh TO XBIOS(3),rx,ry,3
  901.   '
  902. RETURN
  903. '
  904. ' THIS PROCEDURE NOT CALLED IN WINSHELL:
  905. ' while RC_INTERSECT is a very handy command (probably why Frank wrote it!),
  906. ' calculations of redraw areas from AES rectangle lists are straightforward:
  907. '
  908. PROCEDURE redraw_calculation
  909.   x1=pipeline&(4)
  910.   y1=pipeline&(5)
  911.   w1=pipeline&(6)
  912.   h1=pipeline&(7)
  913.   gret=WIND_GET(window(activew),11,x2,y2,w2,h2)
  914.   ~WIND_UPDATE(1)
  915.   WHILE w2<>0 AND h2<>0
  916.     tww=MIN(ADD(x1,PRED(w1)),ADD(x2,PRED(w2)))
  917.     thh=MIN(ADD(y1,PRED(h1)),ADD(y2,PRED(h2)))
  918.     txx=MAX(x1,x2)
  919.     tyy=MAX(y1,y2)
  920.     x2=txx
  921.     y2=tyy
  922.     w2=SUB(tww,txx)
  923.     h2=SUB(thh,tyy)
  924.     IF tww=>txx AND thh=>tyy
  925.       CLIP x2,y2,w2,h2
  926.       ' place the call to your re_drawing routine here...
  927.     ENDIF
  928.     gret=WIND_GET(window(activew),12,x2,y2,w2,h2)
  929.   WEND
  930.   ~WIND_UPDATE(0)
  931.   CLIP 0,0,max_x,max_y
  932. RETURN
  933. '
  934. PROCEDURE load_picture(VAR activew)                ! Degas pic loader
  935.   '
  936.   flag!=TRUE                                        ! assume empty-ness as true
  937.   IF empty!(activew)=TRUE
  938.     ~WIND_GET(window(activew),4,ax,ay,aw,ah)  ! total workarea
  939.     ~WIND_UPDATE(1)                                 ! taking over
  940.     CLIP ax,ay,aw,ah OFFSET 0,0                 ! just the window workarea
  941.     DEFFILL 0,2,8                                   ! clear the window workarea
  942.     PBOX ax,ay,ADD(ax,aw),ADD(ay,ah)
  943.     CLIP rx,ry,rw,rh OFFSET ax,ay             ! re-set
  944.     ~WIND_UPDATE(0)                                 ! all yours again
  945.   ENDIF
  946.   '
  947.   ' this will cure the "stickies," the "flickers," and other mouse disorders!
  948.   REPEAT
  949.     junk_ret=EVNT_MULTI(&X111111,0,0,0,0,0,0,0,0,0,0,0,0,0,adr_mes,1,mx&,my&,mk&,kstat&,key&,clk&)
  950.   UNTIL mk&=0 AND kstat&=0
  951.   '
  952.   FILESELECT #"LOAD DEGAS FILE",path$,"",file$(activew)
  953.   '
  954.   IF file$(activew)<>"" AND file$(activew)<>"\"   ! got one!
  955.     rez&=0
  956.     OPEN "I",#11,file$(activew)
  957.     BGET #11,V:rez&,2                               ! resolution flag
  958.     IF rez&=XBIOS(4)                                ! go complain to Tom
  959.       BGET #11,V:palette$(activew),32                  ! suck in palette
  960.       ' Raster_address%=V:Picture%(0,Window%(Activew%)) ! where we is
  961.       BGET #11,V:picture(0,window(activew)),screen_size   ! suck in screen
  962.       flag!=FALSE                    ! a file has been loaded = NOT empty
  963.       path$=file$(activew)                         ! update the path
  964.       WHILE RIGHT$(path$,1)<>"\"
  965.         path$=LEFT$(path$,PRED(LEN(path$)))
  966.       WEND
  967.       path$=path$+"*.PI"+STR$(SUCC(XBIOS(4)))
  968.       wintitle$(activew)=file$(activew)           ! convert file name
  969.       WHILE INSTR(wintitle$(activew),"\",1)        ! to window title$
  970.         wintitle$(activew)=RIGHT$(wintitle$(activew),PRED(LEN(wintitle$(activew))))
  971.       WEND                                          ! window title string
  972.       wintitle$(activew)=" "+wintitle$(activew)+" "+STRING$(2,0) !+ 2 NUL's
  973.       BMOVE V:wintitle$(activew),V:title_string&(activew*8),16
  974.       adr_ptr=V:title_string&(activew*8)          ! see ODD BEHAVIOR (below)
  975.       ~WIND_SET(window(activew),2,CARD(SWAP(adr_ptr)),CARD(adr_ptr),0,0)
  976.       ' instead of:
  977.       ' ~Wind_set(Window%(Activew%),2,Card(Swap(V:Wintitle$(Activew%))),Card(V:Wintitle$(Activew%)),0,0)
  978.       ~WIND_UPDATE(1)                               ! watch it!
  979.       @fillwindow                                   ! re_draw
  980.       ~WIND_UPDATE(0)                               ! all clear
  981.       @size_both_sliders(activew)
  982.     ELSE
  983.       ' either wrong rez or compressed file
  984.       IF rez&<128
  985.         ALERT 3," | Wrong Monitor! | ",1,"Abort!",nurtz
  986.       ELSE
  987.         ' compressed files have a value in the high byte = $8000+xbios(4),
  988.         ' but this won't happen because of untrapped EOF error first,
  989.         ' but that won't happen because of the Rez&-check trap first!
  990.         ALERT 3," | Compressed File! | ",1,"Abort!",nurtz
  991.       ENDIF
  992.     ENDIF
  993.     CLOSE #11
  994.   ENDIF
  995.   ~XBIOS(6,L:V:palette$(activew))
  996.   IF empty!(activew)=TRUE AND flag!=FALSE
  997.     empty!(activew)=FALSE
  998.   ENDIF
  999.   ' if no image is loaded and no prior image in buffer, then...
  1000.   IF empty!(activew)=TRUE AND flag!=TRUE
  1001.     @close(activew)                       ! withdraw the window
  1002.   ENDIF
  1003.   '
  1004. RETURN
  1005. '
  1006. ' two exit routines
  1007. '
  1008. PROCEDURE quit
  1009.   '
  1010.   ALERT 3," | Are You Sure | You Want to | Exit to GEM? ",1,"Exit|Stay",dummie
  1011.   IF dummie=1                          ! I gotta go!
  1012.     FOR a=1 TO wn
  1013.       IF in_use!(a)=TRUE               ! if windows open...
  1014.         ~XBIOS(6,L:V:palette$(a))      ! well, I think it's a nice touch...
  1015.         PAUSE 14
  1016.         ~WIND_CLOSE(window(a))        ! ...close'em
  1017.       ENDIF
  1018.       ~WIND_DELETE(window(a))         ! kill'em all
  1019.     NEXT a
  1020.     ~XBIOS(6,L:V:userpal$)              ! desk colors returned
  1021.     ~MENU_TNORMAL(menu_adr,m_title&,1) ! turn off the lights
  1022.     ~MENU_BAR(menu_adr,0)              ! menu kill
  1023.     CLIP 0,0,max_x,max_y              ! clip liberation front
  1024.     ~RSRC_FREE()                        ! dump the objects
  1025.     RESERVE FRE(0)+10240                ! gimme my memory back
  1026.     EDIT                                ! I'm outta here!
  1027.   ENDIF
  1028.   '
  1029. RETURN
  1030. '
  1031. '
  1032. PROCEDURE escape                        ! as above, without freedom of choice
  1033.   '
  1034.   ~XBIOS(6,L:V:userpal$)
  1035.   FOR a=0 TO wn
  1036.     IF in_use!(a)=TRUE
  1037.       ~WIND_CLOSE(window(a))
  1038.     ENDIF
  1039.     ~WIND_DELETE(window(a))
  1040.   NEXT a
  1041.   ~WIND_UPDATE(0)                       ! just in case we err'ed out before...
  1042.   ~MENU_TNORMAL(menu_adr,m_title&,1)
  1043.   ~MENU_BAR(menu_adr,0)
  1044.   CLIP 0,0,max_x,max_y
  1045.   ~RSRC_FREE()
  1046.   RESERVE FRE(0)+10240
  1047.   EDIT
  1048.   '
  1049. RETURN
  1050. '
  1051. ' this routine, if uncommented from MAIN, shows the values contained in the
  1052. ' message pipeline in the menu title box; it requires an 80-column display.
  1053. '
  1054. PROCEDURE pipeline
  1055.   IF oret<>ret AND max_x>320
  1056.     PRINT AT(40,1);"PIPE(";STR$(activew);"):  ";     ! Activew% also shown
  1057.     FOR i=0 TO 7
  1058.       PRINT pipeline&(i);" ";
  1059.     NEXT i
  1060.     PRINT "   ";
  1061.   ENDIF
  1062. RETURN
  1063. '
  1064. '
  1065. '
  1066. ' ODD BEHAVIOR EXPLAINED:
  1067. ' Window Title String Addresses:
  1068. '
  1069. ' Here is one of GFA's real weak points: GEM assumes a string address
  1070. ' to be kept at a fixed address in memory; once this address is passed
  1071. ' to GEM, it will always refer to this location until another address
  1072. ' passed. However, GFA has the most dynamic string management of any
  1073. ' programming language ever; it slings strings around constantly and
  1074. ' they never stay in one place very long. So, in program operation,
  1075. ' it's inevitable that GEM will go to a string address and find a stranger
  1076. ' living there. This wouldn't be so bad if GEM didn't also have an
  1077. ' absolute requirement that strings end in a NUL byte (or two). When
  1078. ' GEM starts flushing in what it thinks is string data and the nearest
  1079. ' NUL byte happens to be a long way down the pike, GEM will overwrite
  1080. ' itself, resulting in what can only be described as operating-system
  1081. ' psychosis! Cures are as follows: BMOVE the string data (+ 2 NUL bytes)
  1082. ' to a fixed (indexed) word-variable array and pass GEM the indexed array
  1083. ' address instead of the string address (tricky), or if you only have one
  1084. ' or two windows in a program, put the WIND_SET title call in the bottom of
  1085. ' of the window message handler (PRO WESTERN_UNION in this program) so
  1086. ' the window title address is re-set with EVERY window message. Crude,
  1087. ' but it works... WINSHELL uses the first method (above). Since file names
  1088. ' are twelve characters + a space before and after = 14 bytes, the array
  1089. ' allows 16 bytes for each title_string, with two NUL's included. This
  1090. ' method prevents the occasional brief display of high_ascii garbage in
  1091. ' the window title line, which does no harm but annoys me nevertheless!
  1092. '
  1093. '
  1094. '
  1095. ' TERMINAL NOTES:
  1096. '
  1097. ' These routines can be adapted to any image format that limits itself to
  1098. ' screen-sized images (Neo, Degas, PrismPaint, Tiny). Neo's are uncompressed,
  1099. ' Degas and Prism may be, Tiny can't be. There is a Neo specification for all
  1100. ' Atari screen sizes (including TT). There isn't any reason why Degas (or
  1101. ' rather the .P?? file extent sub-set of image files) could not be easily
  1102. ' extended to include TT and all Falcon screens, too.
  1103. '
  1104. '
  1105. ' *********************************** END **********************************
  1106.