{\rtf1\mac\deff2 {\fonttbl{\f0\fswiss Chicago;}{\f2\froman New York;}{\f3\fswiss Geneva;}{\f4\fmodern Monaco;}{\f13\fnil Zapf Dingbats;}{\f14\fnil Bookman;}{\f15\fnil N Helvetica Narrow;}{\f16\fnil Palatino;}{\f18\fnil Zapf Chancery;}{\f20\froman Times;} {\f21\fswiss Helvetica;}{\f22\fmodern Courier;}{\f23\ftech Symbol;}{\f33\fnil Avant Garde;}{\f34\fnil New Century Schlbk;}{\f55\fnil Code 3 of 9;}{\f1904\fnil AppleIcon;}{\f2029\fnil Nadianne;}{\f2052\fnil Zeal;}{\f12899\fnil AppleGaramond LtIt;} {\f12900\fnil AppleGaramond BkIt;}{\f12901\fnil AppleGaramond BdIt;}{\f12902\fnil AppleGaramond Lt;}{\f12903\fnil AppleGaramond Bk;}{\f12904\fnil AppleGaramond Bd;}}{\colortbl\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blu e255; \red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\ green255\blue0;\red255\green255\blue255;}{\stylesheet{\f16 \sbasedon222\snext0 Normal;}{\s1\tx360 \f20\fs20 \sbasedon222\snext1 Normal;}{\s2\tx360 \i\f20\fs20 \sbasedon1\snext2 Byline;}{\s3\tx360 \b\f20\fs20 \sbasedon2\snext3 Head1;}{\s4\tx360 \i\f20\fs20 \sbasedon3\snext4 Head2;}{\s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx 1800\tx1980\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 \sbasedon1\snext5 Code;}{\s6\fi-180\li360\tx360 \f20\fs20 \sbasedon222\snext6 BulletList;}}\margl720\margr720\margt720\margb720\deftab360\ftnbj\fracwi dth \sectd \linemod0\cols1 \pard\plain \s2\tx360 \i\f20\fs20 {\b\fs28 Soup's On\par }\pard \s2\tx360 {\plain \i\f20 by Mike Engber\par }\pard \s2\keep\tx360 {\plain \i\f20 Apple Computer, PIE Technical Support\line Copyright \'a9 1993 - Michael S. Engber\par }\pard \s2\tx360 {\plain \i\f20 \par }\pard\plain \tx360\tx855\tx945\tx1035 \f16 {\f20 This article was published in the November 1993 issue of PIE Developers magazine. For information about PIE Developers, contact Creative Digital Systems at CDS.SEM@APPLELINK.APPLE.COM or 415.621.4252. \par }\pard \tx360\tx855\tx945\tx1035 {\f20 \par }\pard \tx360\tx855\tx945\tx1035 {\f20 With the myriad of different soup and store functions available on the Newton, it\rquote s hard to tell which functions are important and how they should be used. This article discusses how an application should properly create and use soups. This article is not a tutorial on the topic of soups. This article assumes that the reader is familiar with the chapter on soups in the Newton Programmer\rquote s Guide and has some experience using NTK to write Newton applications.\par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Union Soups\par }\pard\plain \tx360 \f16 {\f20 The first rule of using soups on the Newton is \ldblquote Use union-soups.\rdblquote Union-soups handle the details of whether you should be writing data to the internal store or an external store, a PCMCIA card. The Newton user should be in control of where his/her data goe s. When the user inserts a card or taps the Card item in the extras drawer, the Card Storage dialog comes up. This has a checkbox titled \ldblquote Store new items on card\rdblquote with which the user controls where data goes. By using union-soups your program automatically respects the user\rquote s wishes.\par }\pard \tx360 {\f20 \tab You can think of union-soups as grouping together the soups with a given name on the various available stores. Currently, that is just one or two soups, but future Newtons may change that. Later in this article I discuss how to properly create union-soups .\par }\pard \tx360 {\f20 \tab There are three basic calls that should handle 99% of your soup related code. First, there\rquote s GetUnionSoup. It takes a soup name (a string) as its parameter and returns a union-soup. Next there\rquote s the union-soup meth od, AddToDefaultStore, which does exactly what its name says. It stores a new entry to the store the user has chosen as the default. And finally, there\rquote s EntryChange, which you use to make sure any changes you make to a soup entry are written out to the storage device.\par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Stores and Soups\par }\pard\plain \tx360 \f16 {\f20 Stores can be likened to volumes and soups can be likened to databases. A store can contain many soups just as a volume can contain many databases. A soup is comprised of entries just like a database is composed of records. Soups can maintain indexes on their entries. These indexes can be specified when the soup is created or added or deleted later.\par }\pard \tx360 {\f20 \tab There are a variety of soup and store methods. They are documented and in practice you rarely need to use them, so I won\rquote t say much about them here. The GetStores() global function returns an array of the available stores. You can assume that the first element of the array is Newton\rquote s internal store. Currently, the second element of the array, if it exists, is always the PCMCIA card, but this assumption won\rquote t necessarily hold for future Newtons. Consider the problem of a Newton with multiple slots.\par }\pard \tx360 {\f20 \tab When debugging from NTK\rquote s Inspector it\rquote s sometimes useful to use the GetSoupNames method. For instance, }{\f22 GetStores()[0]:GetSoupNames()}{\f20 returns an array of the soup names from the internal store.\par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Entries\par }\pard\plain \tx360 \f16 {\f20 Soups are comprised of entries. As you access the soup, frames are created for the entries. Normally, you can just think of soups as consisting of these frames. In reality, these frames a re just caching information from the soup into RAM. That\rquote s why it\rquote s necessary to call EntryChange to write the information from an entry\rquote s frame in memory back out to the soup. Otherwise, the change would be lost when the Newton is restarted.\par \tab Deciding when to call EntryChange is sometimes difficult. For instance, if you have an input line, you wouldn\rquote t want to call EntryChange from its viewChangedScript. If the user was entering data into the line with the keyboard, your viewChangedScript gets called after every key press. Calling EntryChange with every key press would be noticeably slow. \par }\pard \tx360 {\f20 \tab In some situations it\rquote s obvious when to call EntryChange. For example, a natural time to call EntryChange is when the user dismisses a dialog box. Other cases may not be so clear cut. The Notepad uses a viewIdleScript to call EntryChange about every five seconds. That\rquote s why the Newton user manual suggests waiting 5-10 seconds before removing a PCMCIA card or you may lose data. If you manage to corrupt the Notepad or its s oup while hacking around, you may get error dialogs every five seconds, each time the Notepad tries to write to its soup.\par }\pard \tx360 {\f20 \tab There isn\rquote t much structure imposed on soup entries. For instance, there\rquote s no requirement they all have the same slots. All entries have a _uniqueId slot which contains an integer uniquely identifying the entry within its soup. The system creates this slot for you, so you don\rquote t need to worry about it. In fact, the _uniqueId slot is only unique within a soup, not within a union-soup. Since applications normally use union-soups, the _uniqueId slot isn\rquote t of much practical use.\par }\pard \tx360 {\f20 \tab You\rquote re free to put any slots you want in your entries and to vary them from entry to entry. The only slots you have to worry about are the slots that are indexed. When you create a soup, you decide what slots are used to build indexes. If you specify that an index should be built on slot foo and that foo contains a string, then it\rquote s important that you make sure that the foo slot in every entry contains a string. It\rquote s p ermissible for an entry not to have a foo slot at all, in which case it won\rquote t participate in queries on the foo index. The essential point is that if an entry has an indexed slot, the slot contains data of the right type.\par }\pard \tx360 {\f20 \tab To create an entry, you should create a new frame and pass it as the parameter to an AddToDefaultStore message. The frame is destructively modified. You\rquote ll see that it now has a _uniqueId slot. When a frame is added to a soup, a deep copy of it is made. That is, all its slots are followed and everything those slots reference is followed and copied, and so on, until everything is copied into the soup. This is necessary because soups persist across restarts, so they can\rquote t contain references to objects in RAM. Because of this you need to thin k carefully about the data structures you use for soup entries. For instance, a view whose _parent chain ultimately leads to the root view would be a very bad candidate for a soup entry.\par \tab There are two exceptions to this copying rule. Pointers into ROM are not followed. Since the ROM is persistent across restarts, pointers into ROM are safe to save in soups. The second exception is _proto slots. A frame\rquote s _proto slot is not followed when creating a soup entry, nor is a _proto slot created for the entry. The _proto slot is not removed when a frame is added to a soup, so it may appear that you have a soup entry with a _proto slot. However, the frame is only a temporary cache of the entry\rquote s data. Next time you retrieve the entry from the soup, you see it does not have a _proto slot. If you need it, you will to create it every time you retrieve the entry.\par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Cursors\par }\pard\plain \tx360 \f16 {\f20 Cursors are used to navigate through soups. You obtain a cursor as the result of a call to Query. This simplest possible query is:\par }\pard \tx360 {\f20 \par }\pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 curs := query(uSoup,\{type: \lquote index\})\par \par }\pard\plain \tx360 \f16 {\f20 I use this technique in the NTK Inspector to get a \ldblquote quick and dirty\rdblquote cursor for peeking at entries in a soup. I say \ldblquote quick and dirty\rdblquote because this code is sloppy. The argument specifies a query type of index, but it doesn\rquote t specify an indexPath. This works because all soups have an index on the _uniqueId slot which is used as the default indexPath.\par }\pard \tx360 {\f20 \tab There are a wide variety of options for specifying queries, as well as a wide variety of cursor functions. They are all documented in the }{\i\f20 Newton Programmer\rquote s Guide}{\f20 , so I won\rquote t go into them here. Here are a few points about using cursors.\par }\pard \tx360 {\f20 \par }\pard\plain \s6\fi-180\li360\tx360 \f20\fs20 {\plain \f20 \bullet \tab Cursor:Entry() can return nil if the cursor runs off either end of the soup. It can return the symbol \lquote deleted if the entry it references was deleted. Be sur e your code can handle these cases.\par }\pard \s6\fi-180\li360\tx360 {\plain \f20 \bullet \tab Traversing your soup creates a frame for each soup entry as it is encountered. (Yes, these frames are cached, so you don\rquote t pay a penalty for visiting the same entry twice.) Avoid traversing every entry in the soup. Use indexes and startKeys to limit your search. Using validTests doesn\rquote t help with this problem. The entry frame has to be created in order to pass it to the validTest.\par }\pard \s6\fi-180\li360\tx360 {\plain \f20 \bullet \tab Don\rquote t leave unnecessary references to entries lying around. For instance, building up an array of entries ties up a lot of memory. As soon as you\rquote re done with the array, delete any references to it so it can be garbage collected, which in turn allows all the entry frames it contains to be garbage collected.\par }\pard\plain \li360\tx360 \f16 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Creating Your Application\rquote s Soup\par }\pard\plain \tx360 \f16 {\f20 The benefits of using a union-soup depend on one crucial point we\rquote ve overlooked so far. The individual soups which comprise a union-soup must exist on all available stores so the user can direct his/her data there. There is a global array, CardSoups, which is used by the system to handle this. The elements of CardSoups are used in pairs. The first element of the pair is the soup name, a string. The second element is an array of index specifications (the frames you use to specify indexes when creating a soup) . Whenever a card is inserted, the system uses this array to make sure each soup in Card-Soups exists on the card, creating soups as necessary.\par }\pard \tx360 {\f20 \tab You need to use the function CreateAppSoup when creating soups. Call CreateAppSoup to make sure your soup exist s on the internal store. CreateAppSoup creates it only if necessary. CreateAppSoup takes four arguments: the soup name, an array of soup indices, a one element array containing your application\rquote s appSymbol, and your application\rquote s appObject. (appObject is an array of two strings, such as [\ldblquote entry\rdblquote , \ldblquote entries\rdblquote ], that describes the singular and plural of the data in that soup. Filing expects the appObject slot of your base view to contain an array like this.) The first two argument are used to create the soup; see CreateSoup\rquote s documentation for all the details on index specs. The last two arguments are added to the soup\rquote s info frame as an annotation describing who uses the soup and what\rquote s in it. Due to a bug in the SetInfo soup method, you need to use EnsureInternal to make sure these second two arguments reside in internal memory.\par }\pard \tx360 {\f20 \tab There are conventions for naming your soup. If your application creates only one soup, you should use your package name for the soup name. Your package name is formed by concatenating your Extras drawer name with your unique signature, for instance \ldblquote foo:myCompany\rdblquote . If your application creates multiple soups, use your package name as a suffix to a descriptive soup name (\ldblquote soup1:foo:myCompany\rdblquote ). \par }\pard \tx360 {\f20 \par }\pard\plain \s4\tx360 \i\f20\fs20 {\plain \i\f20 Basic Methods\par }\pard\plain \tx360 \f16 {\f20 Putting all this information together into a simple recipe, I recommend you define two methods in your application\rquote s base view, RegisterCardSoup and UnRegisterCardSoup. Here is their code.\par }\pard \tx360 {\f20 \par }\pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 //constants defined in \ldblquote Project Data\rdblquote \line constant kAppSymbol := \lquote |llama:PIEDTS|;\line constant kPackageName := \ldblquote llama:PIEDTS\rdblquote ;\line constant kAppObject := \lquote [\ldblquote llama\rdblquote ,\rdblquote llamen\rdblquote ];\line \line constant kSoupName := kPackageName;\line constant kSoupIndexes := \lquote [\{structure: slot, \tab \tab \tab \tab \tab \tab path: name.first, type: string\}];\line \line //2 base view methods\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \line RegisterCardSoup: func(soupName,soupIndexes,appSymbol,appObject)\line //returns a union-soup for your app to use\line begin\line //first check for system provided function\line if functions.RegisterCardSoup then\line return RegisterCardSoup(soupName,soupIndexes,\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab appSymbol,appObject);\line CreateAppSoup (soupName, soupIndexes, \tab \tab \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab \tab \tab \tab \tab EnsureInternal([appSymbol]), \tab \tab \tab \tab \tab \tab \tab \tab \tab \tab EnsureInternal(appObject));\line \line //ensure your soup will exist on stores \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab //which later become available\line AddArraySlot(CardSoups,soupName);\line AddArraySlot(CardSoups,soupIndexes);\line \line //ensure your soup exists on all \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab //currently available stores\line local store;\line foreach store in GetStores() do\line if NOT store:IsReadOnly() AND NOT \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab \tab \tab store:HasSoup(soupName) then\line store:CreateSoup(soupName,soupIndexes); \line GetUnionSoup(soupName);\line end,\line \line UnRegisterCardSoup: func(soupName)\line begin\line //first check for system provided function\line if functions.UnRegisterCardSoup then\line return UnRegisterCardSoup(soupName);\line \line local pos := ArrayPos(CardSoups,soupName,0,\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab func(x,y) ClassOf(y)=\rquote String AND \par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab StrEqual(x,y));\line if pos then ArrayRemoveCount(CardSoups,pos,2);\line end;\line \line //in your viewSetUpFormScript, call\line :RegisterCardSoup(kSoupName,kSoupIndexes,\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab kAppSymbol,kAppObject);\line \line //in your viewQuitScript, call\line :UnRegisterCardSoup(kSoupName);\line \par }\pard\plain \tx360 \f16 {\f20 For now, you should define RegisterCardSoup and UnRegisterCardSoup as methods in your base view. In future Newtons, these functions may be provided in the ROM. The methods defined above allow your application to take advantage of this future functionality by f irst checking to see if these global functions are available.\par }\pard \tx360 {\f20 \tab Your application should call RegisterCardSoup when it starts up. RegisterCardSoup works by first making sure your soup exists in the internal store. Next, it registers your soup with the CardSo ups global variable. Then it ensures your soup exists on any cards already inserted. Finally, it calls GetUnionSoup and returns that value. Your application can use the value returned by RegisterCardSoup as an alternative to calling GetUnionSoup directly. \par }\pard \tx360 {\f20 \tab Your application should call UnRegisterCardSoup when it quits. UnRegisterCardSoup simply unregisters your application from the CardSoups global variable. Your application only needs to worry about its soup existing on all available stores while it\rquote s actually open. If a new card is introduced while your application is closed, it is handled automatically when your application gets opened\endash RegisterCardSoup is run and creates the soup. When your application quits, it should also take care to remove any references it has to entries, soups, or cursors. This usually means setting to nil any slots in which you stored soup or cursor references. \par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Sharing Soups With Others\par }\pard\plain \tx360 \f16 {\f20 Newton programmers are encouraged to reuse existing soups for their own needs. The format of the standard soups is documented in the Newton Programmer\rquote s Guide. This section discusses how to properly handle the sharing of data.\par }\pard \tx360 {\f20 \par }\pard\plain \s4\tx360 \i\f20\fs20 {\plain \i\f20 Registering With SoupNotify\par }\pard\plain \tx360 \f16 {\f20 Applications can be notified when a particular soup gets changed. For instance, maybe your application has developed a following of third party utilities which manipulate your soups, or more likely, you\rquote re using a built in soup, like \ldblquote Names.\rdblquote If you implement the Find feature, the FindAll overview notifies your application of changes it makes to your soup using this same mechanism. It\rquote s generally a good idea for your application to set itself up for notification.\par }\pard \tx360 {\f20 \tab To register for notification all you have to do is add two entries to the SoupNotify global array. Your application should register in its viewSetupDoneScript and unregister in its viewQuitScript. The elements of SoupNotify are used in pairs. The first el ement in the pair is the name of the soup. The second element is the appSymbol of the application to be notified. Below is some code to regis ter an application for changes to the System Soup. This code assumes that a constant, kAppSymbol, has been defined for the application\rquote s appSymbol:\par }\pard \tx360 {\f20 \par }\pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 //register w/ SoupNotify in viewSetupDoneScript\line AddArraySlot(SoupNotify,ROM_SystemSoupName);\line AddArraySlot(SoupNotify,kAppSymbol);\line \line //unregister with SoupNotify in viewQuitScript\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \line local soupNotifyPos := ArrayPos(soupNotify,\par \tab \tab \tab \tab kAppSymbol,0,nil);\line ArrayRemoveCount(soupNotify,soupNotifyPos-1,2);\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \par }\pard\plain \tx360 \f16 {\f20 \tab Notice that the code for unregistering looks for the appSymbol inst ead of the soup name. This is essential since other applications may have requested notification for the same soup. Also note that this code can easily be generalized to unregister you from multiple soups:\par }\pard \tx360 {\f20 \par }\pard\plain \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 \f22\fs20 {\plain \f22 //unreg all SoupNotify entries in viewQuitScript\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \line local soupNotifyPos;\line while soupNotifyPos := ArrayPos(soupNotify,\par }\pard \s5\tx225\tx360\tx540\tx720\tx900\tx1080\tx1260\tx1440\tx1620\tx1800\tx19 80\tx2160\tx2340\tx2520\tx2700\tx2880\tx3060\tx3240 {\plain \f22 \tab \tab \tab \tab kAppSymbol,0,nil) do\line ArrayRemoveCount(soupNotify,soupNotifyPos-1,2);\par }\pard\plain \tx360 \f16 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Receiving Notifications\par }\pard\plain \tx360 \f16 {\f20 Once your application is registered it receives soupChanged messages when soups change. You need to define a soupChanged method in your base view which accepts one argument, the name of the soup that changed. Your application should respond to this in whatever manner is appropriate. Normal applications have no need to respond unless they \rquote re open. That\rquote s why I recommend you register when your application opens and unregister when your application closes.\par }\pard \tx360 {\f20 \par }\pard\plain \s4\tx360 \i\f20\fs20 {\plain \i\f20 Sending Notifications\par }\pard\plain \tx360 \f16 {\f20 If your application makes changes to a shared soup, it should use the BroadcastSoupChange function to notify other applica tions of the change. BroadcastSoupChange takes a single argument, the name of the soup that changed. Unfortunately, the current version of the Prefs application doesn\rquote t use BroadcastSoupChange. So if you try to register with the System Soup, don \rquote t be surprised if Prefs doesn\rquote t notify you.\par }\pard \tx360 {\f20 \par }\pard\plain \s4\tx360 \i\f20\fs20 {\plain \i\f20 Making Changes to Other Application\rquote s Soups\par }\pard\plain \tx360 \f16 {\f20 In addition to using BroadcastSoupChange, there is another convention you should following when modifying soups not owned by your application. If your application needs to add its own slots to entries in another application \rquote s soup, you should create only a single slot. Use your appSymbol as the slot name. This avoids name conflicts, keeps the entries from getting cluttered up, and allows for easy removal of your data.\par }\pard \tx360 {\f20 \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Cleaning Up Spilled Soup\par }\pard\plain \tx360 \f16 {\f20 You may have been wondering \ldblquote Doesn\rquote t this mean that while my application is open, it creates soups on every card inserted into the Newton?\rdblquote . The answer is \ldblquote yes.\rdblquote However, the soups will be empty unless the card is the default store and the user creates data with your application.\par }\pard \tx360 {\f20 \tab You may also have been wondering \ldblquote If the user inserts lots of cards in the process of using my application, can\rquote t his/her data be spread out all over the place?\rdblquote . Again, the answer is \ldblquote yes. \rdblquote There\rquote s not much you can do about this. Hopefully Newton users will learn to organize their data on cards, just like computer users learn organize their data on floppy disks.\par \tab The issue I haven\rquote t discussed is deleting your soups. Similar issues are deleting your preferences from the System Soup and deleting modifications you\rquote ve made to other applications\rquote soups. Analogous problems exist in the Macintosh world. How do you clean up your Preferences folder or get those out of date icons out of the Desktop Database?\par \tab I don\rquote t have a good answer. You certainly don\rquote t want to do it in your viewQuitScript. You probably don\rquote t want to do it in your RemoveScript. Remember, your RemoveScript is run when the card containing your application is removed, as well as when the user chooses Remove So ftware from the Prefs or Card applications. There is no straightforward way to determine which event is happening.\par }\pard \tx360 {\f20 \tab I suspect the eventual outcome will be one, or more, of the following:\par \par }\pard\plain \s6\fi-180\li360\tx360 \f20\fs20 {\plain \f20 \bullet A reliable method to distinguish having your card removed and Remove Software will be documented. Then, when your application detects the Remove Software case, it can prompt the user to see if they really want all traces of the application removed. You w ouldn\rquote t want to do this without prompting the user. Suppose the user is simply deleting an extra copy of your software from a card.\par }\pard \s6\fi-180\li360\tx360 {\plain \f20 \bullet Users will explicitly do the cleanup. Some applications will provide this option to users\endash as a button, part of their Prefs panel, or as a utility application. Alternately, if applications follow the guidelines for naming their soups and preferences data, it should be possible to write a cleanup application.\par }\pard \s6\fi-180\li360\tx360 {\plain \f20 \bullet Chaos will reign. Soups will build up until the ozone layer is depleted. Life, as we know it, will cease.\par \par }\pard\plain \s3\tx360 \b\f20\fs20 {\plain \b\f20 Preferences\endash Exception to Always Using Union Soups\par }\pard\plain \tx360 \f16 {\f20 You may recall that I said applications should always use union-soups. There\rquote s one exception\endash the SystemSoup, where preferences are stored. It doesn\rquote t make much sense to have your preferences spread out on PCMCIA cards. If you know your application is always run from a particular card, it might