TweetFollow Us on Twitter

Stack Translators
Volume Number:10
Issue Number:3
Column Tag:Hypercard

Related Info: File Manager Apple Event Manager Resource Manager

Stack Translators for Hypercard 2.2

Transmogrification, another trick up your sleeve

By Kevin Calhoun, Apple Computer, Inc.

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

About the author

Kevin is a member of the HyperCard team at Apple. In his spare time, oh, wait a minute, what spare time?

HyperCard developers have frequently asked, reasonably and rightly, for a version of HyperCard which could build standalone applications, often calling it a “HyperCard compiler”. They have also asked for a cross-platform version of HyperCard, particularly one for Microsoft Windows. Recently, they have requested versions that can build QuickTime movies, Newton applications, and ScriptX projects. Perhaps soon I’ll be hearing about HyperCard for OpenDoc, Magic Cap, and Taligent.

HyperCard’s ease of use and breadth of functionality make it an attractive choice among software development tools. Until recently, HyperCard’s ease of use has also carried the limitation of only running on Macintosh. Ideally, software developers could develop with HyperCard and avoid the limitations imposed by HyperCard’s runtime requirements. Why should HyperCard be present in order to run stackware? Furthermore, why should a Macintosh be required?

While HyperCard 2.2 doesn’t directly target other platforms and technologies, it can get you closer. It still only builds stacks, and they require HyperCard to run. However, it offers an open interface for add-on components, called “stack translators,” which can translate stacks into other forms. Given the appropriate stack translators, you can use HyperCard 2.2 as a development tool for runtime environments other than its own. In addition, HyperCard 2.2 ships with a stack translator that builds standalone Macintosh applications, called StackToApp.

You can create your own stack translators to turn HyperCard stackware into running applications for any of the platforms and runtime environments I’ve mentioned above. You can also use them to extract data from HyperCard stacks and store them in alternate forms for use by other applications or platforms. For example, you could put styled text from text fields into word processing documents. You might extract AppleScript scripts from stacks and save them as script files on the desktop. Anything that a stack might contain - data, links between data, interface objects, configurations of interface objects, scripted functionality, etc. - can be extracted by stack translators and munged and transformed into just about any form you can imagine. As for me, I’d like to see a stack translator that takes my various stacks and merges data within them into the data stored on my Newton.

The User Experience

HyperCard’s stack translators are Component Manager components. HyperCard 2.2 finds translators any of three ways. First, the System registers all components (files of type 'thng' in the System Folder or the Extensions folder) during startup. Second, HyperCard registers components it finds in files of type 'thng' and type 'STAK'. These can be in the HyperCard folder, or a sub-folder called “Stack Translators”. Third, XCMDs can also register and unregister a component, so you can choose when it’s available.

The names of all registered stack translators appear in a popup menu in the standard file dialog HyperCard displays when the user chooses “Save a copy...” from the File menu.

When the user chooses a stack translator from the popup menu and clicks the “Save” button in that dialog, HyperCard calls the appropriate stack translator to translate the current stack. HyperCard passes the component the data and resource forks reference numbers, as well as an FSSpec for the destination file.

To write a stack translator, there’s not much you need to know about the Component Manager. It helps to have some experience in writing standalone code resources, and you can read much more about components in Inside Macintosh QuickTime Components.

Components are standalone code resources with a single entry point, and have the calling interface defined by the Component Manager, as follows:

  FUNCTION ComponentEntrypoint(VAR params:ComponentParameters;
 storage: Handle): ComponentResult;

Every component has this entry point, and uses the parameters to dispatch to the right subroutine. The ComponentParameters record contains everything you need to know to decide how to respond to a call to your component. In particular, the what field of a ComponentParameters record is the selector for your component routines. If what is negative, it represents one of the standard selectors defined by the Component Manager, of which six are currently defined: selectors for 1) opening, 2) closing, and 3) registering the component, 4) inquiring whether a component supports a given selector value, 5) requesting the version number of the component, and 6) informing the component that it has been “captured” by a descendent component. Routines for handling each of these selectors also have calling interfaces defined by the Component Manager.

If the what field of a ComponentParameters record is non-negative, it represents a selector defined by the component. The Component Manager leaves wide open both what your component does with these selectors and the data it expects to be passed in order to do it. However, for each standard type of component, each of which is identified by a four-character code, there is a set of selectors and a corresponding set of routines with calling interfaces that all components of that type are expected to support. Each component of a particular type has a subtype, which is also a four-character code, which should be unique among components of the same type.

The code resource that contains the implementation of a component must be accompanied by several additional resources. The most important of these is of type 'thng', which identifies the component’s type, subtype, and manufacturer . It also tells the Component Manager where to look to find the component’s code, name, description, and icon (if any).

The Stack Translation Interface

The component type for stack translators is 'STRN'. In addition to the standard component routines, each stack translator must implement a routine for the selector CONST kTranslateStack = 0. Subtypes 'STKP' and 'SCFT' are already defined, so don’t use these.

When HyperCard calls you with selector kTranslateStack, it passes parameters as follows:

FUNCTION TranslateStack (componentSubtype: OSType;
 stackFile: INTEGER;
 stackResFile: INTEGER;
 fileSpecPtr: FSSpecPtr): ComponentResult;

componentSubtype is useful if you use the same code resource for more than one stack translator.

stackFile is the stack’s data fork file reference number, which is open and usable for File Manager routines.

stackResFile is the stack’s resource fork file reference number, which is open and usable for Resource Manager routines.

fileSpecPtr is a pointer to an FSSpec that describes the location of the file your translator should write to. The file is not open, and may or may not already exist. If it does exist, the user will already have confirmed that the file is to be replaced, and you should overwrite it.

A Sample Stack Translator

StackToText is an example of a stack translator. In addition to demonstrating the calling conventions for stack translators, it introduces a technique for implementing the main portion of a stack translator in HyperTalk. HyperTalk contains all of the necessary constructs for iterating through all of the elements of a stack, and it has the necessary commands for putting text into a text file, so why do all of that in Pascal or C? Instead, StackToText tells HyperCard (via AppleEvents from the translator to HyperCard) to go to the stack in which its HyperTalk script is stored, and then it invokes the script, passing parameters along that tell the script which stack to translate and where the resulting text file should reside on disk. StackToText expects its resources to be stored in the same stack resource fork where its HyperTalk script resides.

The sole entry point for the code resource is the routine TranslatorComponent. It’s the routine the Component Manager calls to talk to our component. TranslatorComponent examines the what field of the ComponentParameters record and dispatches to its internal routines depending on the value of what. The Component Manager uses CallComponentFunction and CallComponentFunctionWithStorage to do the dispatching. DoTranslatorOpen demonstrates how to allocate storage for the component and how to inform the Component Manager about it. DoTranslatorClose demonstrates how to pass the component’s storage handle to a component function. It also deallocates the storage before the Component Manager closes the component. Because our component relies on System 7 and the Apple Event Manager, DoTranslatorRegister demonstrates the way a component tells the Component Manager whether it’s safe to register itself.

The meat of the component is in the routine DoTranslateStack. DoTranslateStack doesn’t actually translate a stack - instead it collects the necessary data for invoking the HyperTalk script that does the translation, wraps the data into Apple events, and sends the Apple events to HyperCard.

First, DoTranslateStack needs to know what file HyperCard is calling the component to translate. That’s easy - HyperCard passes it an access path reference number for the data fork of the stack. However, File Manager reference numbers aren’t very helpful in HyperTalk scripts, which are more at home with full path names for files. Therefore, DoTranslateStack calls our handy utility routine, FRefToFSSpec, which returns a file specification for a file given its access path reference number.

DoTranslateStack needs to know where the stack that contains its HyperCard script is on disk. Even though the component’s resources reside in the stack, finding out where the stack is on disk is a little tricky. The Component Manager doesn’t give you direct access to this information. After some pondering, I decided to use the Component Manager call OpenComponentResFile to get a resource file reference number for the component’s resource file and to go from there. I don’t need to look at anything in that resource file; I just need to know where the file is so I can construct my Apple events. Also, you might be interested in the method I use to determine whether a call to open a resource file opens a new access path to it. This is useful for knowing whether to close it. I won’t take credit for this trick. I learned it from the good folks in Developer Technical Services when I asked them, back in 1989, how to do this.

Now that DoTranslateStack knows everything it needs to know to call the HyperTalk script, it creates and sends an Apple event that causes HyperCard to open the stack that contains the script. It uses the standard “open documents” Apple event for this purpose. While the component is running, HyperCard is the current process but isn’t in its main event loop, and therefore the Apple event ought to be addressed to the current process and not to HyperCard by its TargetID or its actual ProcessSerialNumber. Using an address descriptor that contains the generic ProcessSerialNumber for the current process causes the Apple Event Manager to jump directly to the application’s event handler when the event is sent instead of putting the event in a queue, where it would await a call to WaitNextEvent. This is good for us; we can’t afford to wait until HyperCard calls WaitNextEvent in order to process our Apple event, because it won’t do that until after we’ve left the component and returned control to HyperCard!

I should mention before I go on that although StackToText does a reasonable job of checking errors, it doesn’t work very hard to determine what went wrong when an error is reported. For example, it doesn’t ask for a reply event from HyperCard when it sends Apple events - if it did, it could look in the reply for a descriptive error string when an error occurs. To keep the sample code small I didn’t include complete error handling.

Once DoTranslateStack sends the “open documents” event to HyperCard, it creates and send the Apple event that will invoke our custom script. The name of the message we want to send is “translatestack”, and we want it to be sent with parameters that describe the stack to be translated, the file to be created, and the type of file to create. Fortunately, an Apple event for this purpose has been defined and is supported by HyperCard 2.2. It allows Apple event clients to invoke handlers in scripts by name with parameters. It’s called the “subroutine event”, and its class, ID, and keywords are defined in the interface files for OSA and for AppleScript, which are available from Apple in the latest interface files for MPW and also from Symantec with THINK C 6.0. The name of the handler is given in the parameter with keyword keyASSubroutineName. The direct object of the event is a list of parameters, in first-to-last order.

You might note that even though our HyperTalk script expects to receive full path names for the stack to be translated and the file to create, DoTranslateStack adds those parameters to its “subroutine event” as FSSpecs. That’s because HyperCard automatically converts several types of Apple event descriptors to text in order to allow HyperTalk scripts to work with them, including numeric forms and forms that describe files on disk.

These sources are compatible with MPW Pascal 3.3. Build it like this:


/* 1 */
Pascal "StackTranslatorComponent.p"
Link -o "StackToText" "StackTranslatorComponent.p.o" 
        "{Libraries}Interface.o" 
        -sg StackTranslator -rt PROC=128 -t STAK 
        -c WILD -m TRANSLATORCOMPONENT

StackTranslatorComponent.p
©Kevin Calhoun 1993.  All Rights Reserved.

UNIT StackBasedStackTranslator;
{$N+}
{$R-}
{$D-}
INTERFACE

USES Types, Memory, Files, Resources, Errors, Packages, AppleEvents, 
ASRegistry, OSA, Components, GestaltEqu, SysEqu;

FUNCTION  TranslatorComponent(VAR params: ComponentParameters;
               storage: Handle): ComponentResult;

IMPLEMENTATION

TYPE StorageHandle = ^StoragePtr; { Our private storage }
     StoragePtr = ^StorageRecord;
     StorageRecord = RECORD      
       self: ComponentInstance;   { We don't need much }
     END;
     HandlePtr = ^Handle;

CONST kComponentVersion = $01000100; { Our version number }
      kTranslateStack = 0; { our stack translation component selector}

{ Forward declarations for our private routines. }
FUNCTION  DoTranslatorCanDo(selector: INTEGER):
 ComponentResult; FORWARD;  
FUNCTION  DoTranslatorClose(storageHndl: Handle;
           self: ComponentInstance): ComponentResult; FORWARD;
FUNCTION  DoTranslatorOpen(self: ComponentInstance):
 ComponentResult; FORWARD;
FUNCTION  DoTranslatorRegister: ComponentResult; FORWARD;
FUNCTION  DoTranslateStack(storageHndl: StorageHandle;
           componentSubType: OSType;
           stackFile: INTEGER;
           stackResFile: INTEGER;
           fileSpecPtr: FSSpecPtr): ComponentResult; FORWARD;
FUNCTION  FRefToFSSpec(fRefNum: INTEGER;
 VAR spec: FSSpec): OSErr; FORWARD;
PROCEDURE MyDisposeDesc(VAR desc: AEDesc); FORWARD;
PROCEDURE MyDisposHandle(VAR hndl: Handle); FORWARD;

FUNCTION TranslatorComponent(VAR params: ComponentParameters;
                 storage: Handle): ComponentResult;
{ The sole entrypoint for the component. }
BEGIN
  IF params.what < 0 THEN  { component manager values}
    CASE params.what OF
      kComponentRegisterSelect: TranslatorComponent :=
 CallComponentFunction(params,
 ComponentRoutine(@DoTranslatorRegister));
      kComponentVersionSelect: TranslatorComponent := 
 kComponentVersion;
      kComponentCanDoSelect: TranslatorComponent :=
 CallComponentFunction(params,
 ComponentRoutine(@DoTranslatorCanDo));
      kComponentCloseSelect: TranslatorComponent :=
 CallComponentFunctionWithStorage(storage,params,
 ComponentRoutine(@DoTranslatorClose));
      kComponentOpenSelect: TranslatorComponent :=
 CallComponentFunction(params,
 ComponentRoutine(@DoTranslatorOpen));
      OTHERWISE
        TranslatorComponent := badComponentSelector;
    END
  ELSE  { Our component-specific routines }
    BEGIN
      IF params.what = kTranslateStack
      THEN TranslatorComponent :=
 CallComponentFunctionWithStorage(storage,
 params,ComponentRoutine(@DoTranslateStack))
      ELSE TranslatorComponent := badComponentSelector;
    END;
END;

FUNCTION DoTranslatorCanDo(selector: INTEGER): ComponentResult;
{ Called  when the component is asked whether it supports a particular 
selector. }
BEGIN
  IF (selector >= kComponentRegisterSelect) 
 & (selector <= kTranslateStack)
  THEN DoTranslatorCanDo := 1   { valid request }
  ELSE DoTranslatorCanDo := 0;  { invalid request }
END;

FUNCTION DoTranslatorClose(storageHndl: Handle;
              self: ComponentInstance): ComponentResult;
{Called when the component is closed.
 We allocate global storage when we're opened, so we'll deallocate it 
here. }
BEGIN
  MyDisposHandle(storageHndl);
  DoTranslatorClose := noErr;
END;

FUNCTION DoTranslatorOpen(self:ComponentInstance):
 ComponentResult; 
{Called when the component is opened.
 This component uses global storage, so we allocate it here. }
VAR storageHndl: Handle;
BEGIN
  DoTranslatorOpen := noErr;
  storageHndl := NewHandle(SizeOf(StorageRecord));
  StorageHandle(storageHndl)^^.self := self; { Remember it }
  { Tell the Component Mgr to remember our storage handle. }
  SetComponentInstanceStorage(self,storageHndl);
END;

FUNCTION DoTranslatorRegister: ComponentResult;
{ Return FALSE if it's OK to register this component. }
VAR theWorld:   SysEnvRec;
    gestaltInfo: LongInt;
    dummyResult: INTEGER;
    registerOK: BOOLEAN;
BEGIN
  { this component needs System 7 and the Apple Event Manager. }
  dummyResult := SysEnvirons(1,theWorld);
  registerOK := (theWorld.systemVersion >= $0700)   { 7.x system }
   & (Gestalt(gestaltAppleEventsAttr,gestaltInfo) = noErr)  
   & BTST(gestaltInfo,gestaltAppleEventsPresent);{ AEM is present }
  DoTranslatorRegister := ORD(NOT registerOK);
END;

FUNCTION DoTranslateStack(storageHndl: StorageHandle;
                     componentSubType: OSType;
                     stackFile: INTEGER;
                     stackResFile: INTEGER;
                     fileSpecPtr: FSSpecPtr): ComponentResult;
{ Handles requests for translating stacks. }
VAR thisComponent: Component;
    saveTopMapHndl: Handle;
    hyperPSN: ProcessSerialNumber;
    addressDesc: AEDesc;
    appleEvt: AppleEvent;
    replyEvt: AppleEvent;
    fileList: AEDescList;
    paramList: AEDescList;
    msgStr: Str255;
    compFSSpec: FSSpec;
    stackFSSpec: FSSpec;
    compResFile: INTEGER;
    result: OSErr;
    ignoreResult: OSErr;

  PROCEDURE CleanExit;
  { Dispose of everything we allocated before exiting. }
  BEGIN
    MyDisposeDesc(addressDesc);
    MyDisposeDesc(appleEvt);
    MyDisposeDesc(replyEvt);
    MyDisposeDesc(fileList);
    MyDisposeDesc(paramList);
    EXIT(DoTranslateStack);
  END;

  PROCEDURE CheckError(err: LongInt);
  { If an error occurred, set our return value and call CleanExit. } 

  BEGIN
    IF err <> noErr THEN 
      BEGIN
        DoTranslateStack := err;
        CleanExit;
      END
  END;
  
BEGIN
  DoTranslateStack := noErr;  { assume success }

  {Set everything to nil now that we need to allocate later.  This will 
tell us on an
 error exit what we've allocated and what we haven't. }
  addressDesc.dataHandle := NIL;
  appleEvt. dataHandle := NIL;
  replyEvt.dataHandle := NIL;
  fileList.dataHandle := NIL;
  paramList.dataHandle := NIL;
  
 { Get FSSpec of stack to be translated. }
  CheckError(FRefToFSSpec(stackFile,stackFSSpec));

 { Get FSSpec of the stack that contains the HyperTalk script; this is 
the same
    as the FSSpec for the component resource file. }

 { Open the component's resource fork. }
 { We saved this little nugget in DoTranslatorOpen. }
 thisComponent := Component(storageHndl^^.self);  
  IF thisComponent = NIL THEN CheckError(badComponentInstance);
 { Remember what TopMapHndl is just before we open the resource file. 
}
  saveTopMapHndl := HandlePtr(TopMapHndl)^;
  compResFile := OpenComponentResFile(thisComponent);
  CheckError(ResError);
  IF compResFile = -1 THEN CheckError(resFNotFound);
 { Get file spec for this component and close the resource file. }
  result := FRefToFSSpec(compResFile,compFSSpec);
 { If TopMapHndl changed when the component's resource file was opened,
    then we opened a new access path to it, and therefore we should close 
it. }
  IF saveTopMapHndl <> HandlePtr(TopMapHndl)^
  THEN ignoreResult := CloseComponentResFile(compResFile);
  CheckError(result);
  
  { We've got our FSSpecs; now use Apple events to make HyperCard go 
to the
    stack in which this component resides and, once the stack is opened,
    execute the custom 'translatestack' handler stored within the stack. 
}

{ Create address descriptor for HyperCard (which is the current process) 
and then
  create an "open documents" Apple event for sending to the current process. 
}
  hyperPSN.highLongOfPSN := 0;
  hyperPSN.lowLongOfPSN := kCurrentProcess;
  CheckError(AECreateDesc(typeProcessSerialNumber,@hyperPSN,
 SizeOf(hyperPSN),addressDesc));
  CheckError(AECreateAppleEvent(kCoreEventClass,
 kAEOpenDocuments,addressDesc,kAutoGenerateReturnID,
 kAnyTransactionID,appleEvt));
{ Create list of documents to open and put it into the direct parameter. 
}
  CheckError(AECreateList(NIL,0,FALSE,fileList));
  CheckError(AEPutPtr(fileList,1,typeFSS,@compFSSpec,
 SizeOf(compFSSpec)));
  CheckError(AEPutParamDesc(appleEvt,keyDirectObject,fileList));
  MyDisposeDesc(fileList); { AEPutParamDesc copied it into the Apple
   event, so we don't need this anymore. }
{ Send "open documents" Apple event to HyperCard. }
  CheckError(AESend(appleEvt,replyEvt,
 kAENoReply + kAEDontRecord,
 kAENormalPriority,kAEDefaultTimeout,NIL,NIL));

  MyDisposeDesc(appleEvt);
  MyDisposeDesc(replyEvt);{ desc should be null but can't hurt to make 
sure }


 { Now create &send our custom scripting message, "translatestack", with 
parameters. }
CheckError(AECreateAppleEvent(kOSASuite,
  kASSubroutineEvent,addressDesc,
  kAutoGenerateReturnID,
  kAnyTransactionID,appleEvt));
  { Put message name into "subroutine name" parameter. }
  msgStr := 'translatestack';
  CheckError(AEPutParamPtr(appleEvt,keyASSubroutineName,
 typeChar,Ptr(ORD4(@msgStr)+1),Length(msgStr)));
  { Create list of parameters to "translate stack" message. }
  CheckError(AECreateList(NIL,0,FALSE,paramList));
  CheckError(AEPutPtr(paramList,1,typeFSS,@stackFSSpec,
   SizeOf(stackFSSpec)));
  CheckError(AEPutPtr(paramList,2,typeFSS,Ptr(fileSpecPtr),
   SizeOf(FSSpec)));
  CheckError(AEPutPtr(paramList,3,typeType,@componentSubType,
   SizeOf(componentSubType)));
  CheckError(AEPutParamDesc(appleEvt,keyDirectObject,
  paramList));
  MyDisposeDesc(paramList);
  { Send subroutine event to HyperCard }
  CheckError(AESend(appleEvt,replyEvt,
 kAENoReply+kAEDontRecord,
 kAENormalPriority,kAEDefaultTimeout,NIL,NIL));
  CleanExit;
END;

FUNCTION FRefToFSSpec(fRefNum:INTEGER; VAR spec:FSSpec):OSErr;
{ Convert a file reference number for an open access path to an FSSpec 
for the file.
  Returns a file spec for a currently open file. }
VAR fcbPBlock:  FCBPBRec;
    nameStr:    Str255;
    result:     INTEGER;
BEGIN
 WITHfcbPBlock DOBEGIN
    ioCompletion := NIL;
    ioNamePtr := @nameStr;
    ioVRefNum := 0;
    ioRefNum := fRefNum;
    ioFCBIndx := 0;
  END;
  result := PBGetFCBInfo(@fcbPBlock,FALSE);
  IF result = noErr THEN
    BEGIN
      spec.vRefNum := fcbPBlock.ioFCBVRefNum;
      spec.parID := fcbPBlock.ioFCBParID;
      spec.name := nameStr;
    END;
  FRefToFSSpec := result;
END;

PROCEDURE MyDisposeDesc(VAR desc: AEDesc);
{ Dispose of an Apple event descriptor, if it's non-nil, and then set 
the dataHandle 
  field to nil.  Prevents accidental double-disposals of handles. }
VAR result: OSErr;
BEGIN
  IF desc.dataHandle <> NIL THEN result:=AEDisposeDesc(desc);
  desc.dataHandle := NIL;
  desc.descriptorType := typeNull;
END;

PROCEDURE MyDisposHandle(VAR hndl: Handle);
{ Dispose of a handle, if it's non-nil, and then set it to nil.  Prevents 
accidental 
  double-disposals of handles. }
BEGIN
  IF hndl = NIL THEN EXIT(MyDisposHandle);
  DisposHandle(hndl);
  hndl := NIL;
END;

END.

Rez Source For StackToText

Every component requires a 'thng' resource to tell the Component Manager about our component’s type, subtype, manufacturer, executable code resource, name, description string, and icon. We also want to tell the Component Manager to check with us before the component is registered. The 'thng' resource allows us to do all of this. HyperCard gets the string for the popup in Standard File from the componentName fields. The strings and icon can be marked purgeable.


/* 2 */
data 'thng' (129, "StackToText") {
   "STRN" /* componentType: 'STRN' */
   "STTT" /* componentSubType: 'STTT' */
   "KCAL" /* componentManufacturer: 'KCAL' */
   $"80000000" /* componentFlags: cmpWantsRegisterMessage */
   $"00000000" /* componentFlagsMask: 0 */
   "PROC" /* component Type: 'PROC' */
   $"0080"/* component ID: 128 */
   "STR " /* componentName Type: 'STR ' */
   $"0080"/* componentName ID: 128 */
   "STR " /* componentInfo Type: 'STR ' */
   $"0081"/* componentInfo ID: 129 */
   "ICON" /* componentIcon Type: 'ICON' */
   $"0080"/* componentIcon ID: 128 */
};

HyperTalk Source For StackToText

Our translatestack handler loops through all of the cards in the stack to be translated, writing the text of all of the card fields and the background fields on each card. It could do a great deal more - add headers for each field, for example. But this is enough for illustrative purposes.


/* 3 */
on translateStack stackName,newFileName,componentSubType
  open file newFileName
  if the result is not empty then
    answer the result
    exit translateStack
  end if
  
  lock screen
  lock messages
  lock recent
  push card
  go stack stackName
  if the result is not empty then exit translateStack
  
  push card
  repeat with i = 1 to the number of cards
    go card i
    repeat with j = 1 to the number of card fields
      write card field j & return & return to file newFileName
    end repeat
    repeat with k = 1 to the number of bg fields
      write bg field k & return & return to file newFileName
    end repeat
  end repeat
  close file newFileName
  pop card
  pop card
  unlock recent
  unlock messages
  unlock screen
  close card window
end translateStack

Beyond StackToText

StackToText demonstrates a simple stack translator, but it doesn’t show how to extract more than text from HyperCard stacks. You can do more with XCMDs than with HyperTalk, but even that doesn’t get you all the way there. Many other pieces of information about objects stored within stacks are not easily available either via HyperTalk or the XCMD interface. So how is it possible for stack translators to extract all of the relevant data from stacks?

The answer, I hope, has already been suggested to you by StackToText. As you’ve seen, stack translators can communicate with HyperCard via Apple events. And HyperCard 2.2 supports a generous Apple event interface, through which the contents and properties of all objects stored within HyperCard stacks are available in their richest formats, including styled text and compiled OSA scripts. Therefore stack translators can obtain their data via Apple events. For a list of the Apple events and Apple event objects supported by HyperCard 2.2, open HyperCard's Apple event dictionary with the AppleScript Script Editor. For further examples of the use of Apple events and Apple event objects for communicating with HyperCard from standalone code resources, see the article tentatively entitled “Advanced XCMD Techniques: Apple Events”, coming soon to a MacTech issue near you.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

TunnelBear 3.5.1 - Subscription-based pr...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
Typinator 7.4 - Speedy and reliable text...
Typinator turbo-charges your typing productivity. Type a little. Typinator does the rest. We've all faced projects that require repetitive typing tasks. With Typinator, you can store commonly used... Read more
Fantastical 2.4.5 - Create calendar even...
Fantastical 2 is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
Monosnap 3.4.9 - Versatile screenshot ut...
Monosnap lets you capture screenshots, share files, and record video and .gifs! Features Capture Capture full screen, just part of the screen, or a selected window Make your crop area pixel... Read more
Skim 1.4.32 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
ForkLift 3.1.1 - Powerful file manager:...
ForkLift is a powerful file manager and ferociously fast FTP client clothed in a clean and versatile UI that offers the combination of absolute simplicity and raw power expected from a well-executed... Read more
Direct Mail 5.2.1 - Create and send grea...
Direct Mail is an easy-to-use, fully-featured email marketing app purpose-built for macOS. Create, send, and track great looking email campaigns that get results. Start your newsletter by selecting... Read more
Direct Mail 5.2.1 - Create and send grea...
Direct Mail is an easy-to-use, fully-featured email marketing app purpose-built for macOS. Create, send, and track great looking email campaigns that get results. Start your newsletter by selecting... Read more
Skim 1.4.32 - PDF reader and note-taker...
Skim is a PDF reader and note-taker for OS X. It is designed to help you read and annotate scientific papers in PDF, but is also great for viewing any PDF file. Skim includes many features and has a... Read more
ForkLift 3.1.1 - Powerful file manager:...
ForkLift is a powerful file manager and ferociously fast FTP client clothed in a clean and versatile UI that offers the combination of absolute simplicity and raw power expected from a well-executed... Read more

Latest Forum Discussions

See All

What mobile gaming can learn from the Ni...
While Nintendo might not have had things all its own way since it began developing for mobile, one thing it has got right is the release of the Switch. After the disappointment of the WiiU, which I still can't really explain, the Switch felt a... | Read more »
Programmer of Sonic The Hedgehog launche...
Japanese programmer Yuji Naka is best known for leading the team that created the original Sonic The Hedgehog. He’s moved on from the speedy blue hero since then, launching his own company based in Tokyo – Prope Games. Legend of Coin is the... | Read more »
Why doesn't mobile gaming have its...
The Overwatch League is a pretty big deal. It's an attempt to really push eSports into the mainstream, by turning them into, well, regular sports. But slightly less sweaty. It's a lavish affair with teams from all around the world, and more... | Read more »
Give Webzen’s new billiard game PoolTime...
Best known for producing hugely popular MMO titles, South Korean publisher Webzen is now taking aim at a different genre altogether. PoolTime is a realistic eight ball pool simulator, allowing you to compete in real-time matches against players... | Read more »
Let Them Come Guide - How to survive aga...
Let Them Come is all about making it as far as possible against overwhelming odds. Check out some of these tips to help you last a little longer in your unwinnable fight: [Read more] | Read more »
All the best games on sale for iPhone an...
Happy last day of the week. I hope you've been having a good one. I have. I saw ten doggos today. So because I'm in a good mood, I thought I'd round up all of the best games that are currently on sale on the App Store. [Read more] | Read more »
The very best games that came out for iP...
We're getting to the end of the first real, full, proper week of 2018. And in that time we've seen some pretty awesome games landing on the App Store. Of course, we've seen some absolute duffers as well. The sort of games that you look at and... | Read more »
Rusty Lake Paradise (Games)
Rusty Lake Paradise 1.4 Device: iOS Universal Category: Games Price: $2.99, Version: 1.4 (iTunes) Description: Jakob, the oldest son of the Eilander family, is returning to Paradise island after his mother passed away. Since her... | Read more »
Antihero Guide - Sneaky tricks to get ah...
Games of Antihero start out small and streamlined, but they quickly turn into long strategic conquests as you fight for control of the Victorian-era streets. If you find yourself struggling in the skullduggery department, here are a few things you... | Read more »
Here's why Niantic pulling Pokemon...
If there's one thing that Pokemon GO did well, it was bringing people together. I still remember seeing groups of people around the marina near where I live in the weeks after the game came out, all of them trying to grab some water Pokemon. There... | Read more »

Price Scanner via MacPrices.net

10″ iPad Pros on sale for $50-$75 off MSRP, n...
B&H Photo has 10″ and #Apple #iPad Pros on sale for up to $75 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only. Note that some sale prices are restricted to certain... Read more
Apple refurbished Mac minis available startin...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Amazon offers Silver 13″ Apple MacBook Pros f...
Amazon has new Silver 2017 13″ #Apple #MacBook Pros on sale today for up to $150 off MSRP, each including free shipping: – 13″ 2.3GHz/128GB Silver MacBook Pro (MPXR2LL/A): $1199.99 $100 off MSRP – 13... Read more
Sale: 12″ 1.3GHz MacBooks on sale for $1499,...
B&H Photo has Space Gray and Rose Gold 12″ 1.3GHz #Apple MacBooks on sale for $100 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 12″ 1.3GHz Space... Read more
Apple offers Certified Refurbished 2017 iMacs...
Apple has a full line of Certified Refurbished iMacs available for up to $350 off original MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: – 27... Read more
13″ MacBook Airs on sale for $120-$100 off MS...
B&H Photo has 2017 13″ 128GB MacBook Airs on sale for $120 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $... Read more
15″ Touch Bar MacBook Pros on sale for up to...
Adorama has Space Gray 15″ MacBook Pros on sale for $200 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: – 15″ 2.8GHz MacBook Pro Space Gray (MPTR2LL/A): $2199, $200 off... Read more
21″ 3.4GHz 4K iMac on sale for $1399, $100 of...
Adorama has the 21″ 3.4GHz 4K #Apple #iMac on sale today for $1399. Their price is $100 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: – 21″ 3.4GHz 4K iMac (MNE02LL/A... Read more
B&H offering 13″ Apple MacBook Pros for u...
B&H Photo has 13″ MacBook Pros on sale for up to $75-$120 off MSRP. Shipping is free, and B&H charges sales tax for NY & NJ residents only: – 13-inch 2.3GHz/128GB Space Gray MacBook Pro (... Read more
B&H continues to offer clearance 2016 15″...
B&H Photo has clearance 2016 15″ #MacBook Pros available for up to $800 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.7GHz Touch Bar MacBook Pro... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Site Reliability Engineer, *Apple* Pay - Ap...
# Site Reliability Engineer, Apple Pay Job Number: 113356036 Santa Clara Valley, California, United States Posted: 12-Jan-2018 Weekly Hours: 40.00 **Job Summary** Read more
UI Tools and Automation Engineer, *Apple* M...
# UI Tools and Automation Engineer, Apple Media Products Job Number: 86351939 Santa Clara Valley, California, United States Posted: 11-Jan-2018 Weekly Hours: 40.00 Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
UI Tools and Automation Engineer, *Apple* M...
# UI Tools and Automation Engineer, Apple Media Products Job Number: 113136387 Santa Clara Valley, California, United States Posted: 11-Jan-2018 Weekly Hours: 40.00 Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.