tim@hoptoad.uucp (Tim Maroney) (04/13/90)
In article <1049@biar.UUCP> trebor@biar.UUCP (Robert J Woodhead) writes: >Ok, so I need to add a custom button to the standard file dialog. Thats easy - >just copy the DLOG and DITL resources from the system file and edit them. The >DLOGHook routine is easy too. No problem. Except... > >The hook routine just gets the item number and a pointer to the dialog. What I >need to do is be able to set a flag so that my main program knows the extra >button is pressed. It would be lovely to be able to diddle the SFReply field >but no can do apparently. Which means i have to alter a global from within the >hook routine. Which means futzing with the A5-WORLD. > >Unfortunatley, the recommended way to do this seems to change with every >new set of tech-notes, and I for one have never been able to get it to work. > >Anyone run into this problem before and got a simple solution that works? The only solution I know involves global storage of some kind or another. In applications, it's easy, because your hooks will be called with the proper A5. Otherwise, you need to use the techniques of Tech Note #256 to maintain an A5 world; or, perhaps more cleanly but probably less compatibly, you can store your "globals" in the ApplScratch region. Be sure to save the old contents and restore them after returning from Standard File, so as not to interfere with an application that's using ApplScratch. The techniques of Tech Note #256 do work, sort of. However, you may find that you need not only globals, but a valid QuickDraw setup as well. If so, then allocate a pointer, not a handle, because the QuickDraw global area is self-referencing. (In fact, I think you should *always* use a pointer rather than a handle. Globals may point to other globals, and if the area gets relocated, you're screwed.) You also need to add 206 bytes for the QuickDraw globals and adjust your A5 pointer accordingly. You also need to SetPort each time you enter the A5 world to make sure you're pointing at a valid port, and you need to call InitGraf when you make it. I recently pounded these routines into shape, and this seems like as good a time as any to unleash them on the world. MPW C, natch, but they should be reasonably easy to translate to Pascal. #define QDglobSize 206 #define AppParmSize 32 long A5Size(void); void A5Init(long globals); Ptr MakeA5World(long *oldA5); long SetA5World(Ptr A5Ref); void DisposeA5World(Ptr A5Ref); void RestoreA5World(long oldA5, Ptr A5Ref); void StashA5(Ptr a5); Ptr FetchA5(void); Ptr MakeA5World(long *oldA5) { long size, newA5; Ptr A5Ref; if ((A5Ref = NewPtrClear(size = A5Size() + QDglobSize)) == 0) return 0; StashA5(A5Ref); /* only if you're keeping A5 in code-relative space */ newA5 = ((long)A5Ref) + size - AppParmSize; A5Init(newA5); *oldA5 = SetA5(newA5); InitGraf(&qd.thePort); return A5Ref; } long SetA5World(Ptr A5Ref) { GrafPtr port; long oldA5; GetPort(&port); oldA5 = SetA5(((long)A5Ref) + A5Size() + QDglobSize - AppParmSize); SetPort(port); return oldA5; } void RestoreA5World(long oldA5, Ptr A5Ref) { SetA5(oldA5); #pragma unused (A5Ref) } void DisposeA5World(Ptr A5Ref) { DisposPtr(A5Ref); } Note that unlike the TN#256 routines, you do *not* call SetA5World after MakeA5World, which does it for you and returns the old a5. Also, for INIT-installed trap patches, be sure you allocate your A5 world in the system heap, not the application heap. You can either change my NewPtrClear to a NewPtrSysClear, or set the zone to the system zone before calling MakeA5World. (Be sure to restore it for the next INIT to use!) You won't need the StashA5 call for a simple application like yours where you only need temporary globals and you can keep track of the A5Ref in a local variable in the routine that calls SFPGetFile. However, for more sophisticated applications such as trap patches where you don't have some higher-level caller keeping track of your A5 for you, you need to use assembly language to keep a copy of the A5Ref in code-relative space. Here's the file to do that. StashA5 is called from MakeA5World. Call FetchA5 to get your A5Ref before calling SetA5World. You'll need to use a slightly different calling sequence (and name the routines in all capitals) for Pascal. CASE ON FetchA5 PROC EXPORT EXPORT StashA5 bra.s fetch Stashed dc.l 0 fetch lea Stashed,a0 move.l (a0),d0 rts StashA5 lea Stashed,a0 move.l 4(sp),(a0) rts ENDPROC END All this code was actually written by Clarus's evil twin, Oscar. >PS: It seems really STUPID that the dloghook routine in SFGetFile can't touch >SFReply! You got *that* right! One of many deficient callback hooks in the Mac OS, I'm afraid. PS. The MPW C 3.0 compiler sometimes generates A5-relative references for string globals (probably string constants as well) at the beginning of a routine, long before the code actually accesses it. So, if the routine sets up its own A5, the global reference will be bad, and you will be singing "Las Cucarachas" or perhaps "This Is Radio Crash". For this reason, it is wise to use a front-ending approach in which the main callback simply sets up the a5 world, calls the routine which accesses all the global data, then restores the old A5 when that routine returns to the main callback. -- Tim Maroney, Mac Software Consultant, sun!hoptoad!tim, tim@toad.com "I am of the opinion that my life belongs to the whole community, and as long as I live it is my privilege to do for it whatever I can." -- Shaw