Important: The information in this document is obsolete and should not be used for new development.
Implementing Your Cyberdog Item Class
To implement your own kind of Cyberdog item, you must define a subclass ofCyberItem
and override most of its methods. These methods are called by the service while opening the Cyberdog item. Listing 2 shows the SOM class definition for the Cyberdog item associated with the Finger service.Listing 2 The SOM class definition of the Finger service item
module AppleCyberdog { interface FingerItem : CyberItem { #ifdef __SOMIDL__ implementation { functionprefix = FingerItem__; override: somInit, somUninit, Open, SetUpFromURL, GetFlatSize, Flatten, Unflatten, ExternalizeContent, Clone, Compare, GetStringProperty, SetDefaultName, GetURL, GetIconSuite, CreateCyberStream, IsDownloadable, GetContentKind, GetFileInfo; #ifdef __PRIVATE__ FingerItemDatafData; #endif }; #endif }; };The following sections describe how to implement these methods.Initializing a Cyberdog Item
Your Cyberdog item must contain enough information to identify the location to which the item refers. It should also contain, or be able to derive, the information that needs to be flattened when the item is saved in a storage unit. Listing 3 shows the data maintained in a Finger service's item, which is referenced by the fData field.Listing 3 The data structure of the Finger service item
struct CFingerItemData { CFingerItemData(); virtual ~CFingerItemData(); char* fURL; StringPtr fDefaultName; ScriptCode fDefaultNameScript; static Handle gIconSuite; };The item holds a URL, a display name for the URL, the script system with which to display the name, and a handle to the icon suite associated with the item. A structure for this data is created when the item is initialized, as shown in Listing 4.Listing 4 The
somInit
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__somInit (AppleCyberdog_FingerItem *somSelf) { ... AppleCyberdog_FingerItem_parent_CyberItem_somInit(somSelf); CFingerItemData* data = new CFingerItemData; somThis->fData = data; }The item's data is deleted when the item is destroyed, as shown in Listing 5.Listing 5 The
somUninit
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__somUninit (AppleCyberdog_FingerItem *somSelf) { ... AppleCyberdog_FingerItem_parent_CyberItem_somUninit(somSelf); CFingerItemData* data = (CFingerItemData*) somThis->fData; if(data) { delete data; somThis->fData = nil; } }Setting Up a Cyberdog Item
Your Cyberdog item must provide aSetUpFromURL
method. Your service calls thisSetUpFromURL
method when it creates the item. TheSetUpFromURL
method is called with the URL, the URL's name, and the name's script as input parameters. If the name is not specified, yourSetUpFromURL
method must create a name. Listing 6 shows theSetUpFromURL
method of the Finger service item, which stores the URL and sets the name.Listing 6 The
SetUpFromURL
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__SetUpFromURL (AppleCyberdog_FingerItem *somSelf, Environment *ev, char* url, StringPtr defaultName, ScriptCode defaultNameScript) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; if(url) { data->fURL = (char*) ODNewPtr(strlen(url)+1); strcpy(data->fURL, url); if(defaultName) somSelf->SetDefaultName(ev, defaultName, defaultNameScript); else { short port; char host[256]; char user[256]; char manufacturedName[256]; manufacturedName[0] = 0; ParseFingerURL(url, host, user, &port); if(user[0] != 0) { strcat(manufacturedName, user); strcat(manufacturedName, " at "); } strcat(manufacturedName, host); ctopstr(manufacturedName); somSelf->SetDefaultName(ev, (StringPtr) manufacturedName, smRoman); } } }TheParseFingerURL
function decomposes the URL in theurl
field into its host, user, and port components.Opening a Cyberdog Item
You must override your Cyberdog item'sOpen
method to specify how to open the Cyberdog item. Your implementation can either open the item synchronously or asynchronously. Listing 7 shows theOpen
method of the Finger service item. A preference in the Internet Config file controls how to open the item. See "Creating the Preferences Panel" (page 37).
Listing 7 The
- Note
- It is not necessary to open a Finger service item asynchronously. The data type being accessed is known to be text and only a small amount of data is transferred. The asynchronous option is provided to show the differences between asynchronous and synchronous opening of Cyberdog items.
Open
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__Open (AppleCyberdog_FingerItem *somSelf, Environment *ev, ParameterSet* theParams) { ... // Try to find a window that already shows the Cyberdog item long search4ExistingWindow = true; if(theParams) { if( ! theParams->GetParameter(ev, kCDSearch4ExistingWindowKey, (void**) &search4ExistingWindow) ) search4ExistingWindow = true; } if(search4ExistingWindow) // Existing window found and selected; do not create a new part. if(GetCyberSession(ev)->SelectCyberItemWindow(ev, somSelf)) return; // Extract the opener part (if any) from the parameter set. ODPart* openerPart = kODNULL; if(theParams) if(!theParams->GetParameter(ev, kCDInitialOpenerPartKey, &openerPart)) theParams->GetParameter(ev, kCDObtainedOpenerPartKey, &openerPart); // Read the opening mode preference from Internet config. OSErr err; long size; ODBoolean openAsync; size = sizeof(ODBoolean); err = GetPrefsValue(ev, kFingerOpenAsyncPref, (Ptr) &openAsync, &size); if(err != noErr) openAsync = kODFalse; if(openAsync) // Open asynchronously. { // Provide a parameter set object for the call to ObtainOpener. theParams = AcquireCreateParameterSet(ev, theParams); // Obtain an opener part for opening asyncronously. openerPart = GetCyberSession(ev)->ObtainOpener(ev, openerPart, kODNULL, somSelf, theParams); // Create a thread that simulates deferred opening const Size kThreadStackSize = 0; ThreadID threadID; AsyncOpenData* openData = new AsyncOpenData(somSelf, theParams, openerPart); OSErr err = NewThread(kCooperativeThread, gFingerItemAsyncOpenThread, openData, kThreadStackSize, kCreateIfNeeded, nil, & threadID); } else // Open synchronously. { // Create the right kind of part in the right document. CyberSession* cyberSession = ::GetCyberSession(ev); TempODPart cyberPart = cyberSession->CreateCyberPart(ev, openerPart, kTextPlainKind, kODNULL); // If the display part handles the kTextPlain kind of data, // create a download part instead. if(cyberPart == kODNULL) cyberPart = cyberSession->CreatePartInCyberDocument(ev, kDownloadPartKind, kODNULL); // Open the Cyberdog item. if(cyberPart) { TempCyberPartExtension cyberPartExt(cyberPart, kCyberPartExtension); cyberPartExt->OpenCyberItem(ev, fingerItem, openerPart, params); } } }TheOpen
method attempts to find a window that already displays the item. If it cannot find the window, it opens the window asynchronously or synchronously. If the mode is asynchronous, theOpen
method initiates a cooperative Thread Manager thread to open the window, as shown in Listing 8.Listing 8 The thread that opens an item asynchronously for the Finger service
pascal void* FingerItemAsyncOpenThread(AsyncOpenData* openData) { Environment localEv; SOM_InitEnvironment(&localEv); Environment* ev = &localEv; CyberItem* fingerItem = openData->fFingerItem; ParameterSet* params = openData->fParams; ODPart* openerPart = openData->fOpenerPart; Boolean aborted = false; { // Set up a progress broadcaster object. CyberProgressBroadcaster* broadcaster = new CyberProgressBroadcaster; broadcaster->ICyberProgressBroadcaster(ev, gMyCyberAbortUPP, (Ptr) &aborted); broadcaster->SetProgressMode(ev, kUnmeteredProgress); // Attach the broadcaster object to the opener part. TempOpenerPartExtension openerPartExt(openerPart, kCyberOpenerPartExtension); openerPartExt->AttachProgressBroadcaster(ev, broadcaster); // Set the status string to something informative. broadcaster->SetStatusString(ev, "\pUm...thinking..."); // Simulate a need for asynchronous operation by waiting. unsigned long tickOut = TickCount() + 4*60; // 4 seconds while(TickCount() < tickOut && !aborted) YieldToAnyThread(); // Detach the broadcaster object and delete it. openerPartExt->DetachProgressBroadcaster(ev, broadcaster); delete broadcaster; } // If the user didn't press the Stop button, create and open a // Cyberdog display part. if(!aborted) { // Create the right kind of part in the right document. CyberSession* cyberSession = ::GetCyberSession(ev); TempODPart cyberPart = cyberSession->CreateCyberPart(ev, openerPart, kTextPlainKind, kODNULL); // If the display part handles the kTextPlain kind of data, // create a download part instead. if(cyberPart == kODNULL) cyberPart = cyberSession->CreatePartInCyberDocument(ev, kDownloadPartKind, kODNULL); // Open the Cyberdog item. if(cyberPart) { TempCyberPartExtension cyberPartExt(cyberPart, kCyberPartExtension); cyberPartExt->OpenCyberItem(ev, fingerItem, openerPart, params); } } // Release the parameter set. params->Release(ev); delete openData; SOM_UninitEnvironment(&localEv); return nil; }Resolving a Cyberdog Item
When a Cyberdog item is opened, the item determines the kind of data to which it refers. This process is called resolving the item. You must specify how to resolve your Cyberdog item and provide information about the item's state as it is being resolved. To resolve a Cyberdog item, you must override the following methods:
- Override the item's
IsResolved
method to specify whether or not the item has been resolved. If you do not override theIsResolved
method, it returnskODTrue
, meaning that it is not necessary to resolve the item. The Finger service's item class does not override this method because the URL points to data whose type is known before the item is opened.- Override the item's
Resolve
method to identify the kind of data to which the item refers. The Finger service's item class does not override this method because theIsResolved
method always returnskODTrue
. Thus, theResolve
method is never called.- Override the item's
IsDownloadable
method to specify whether or not the data associated with the item is downloadable. The superclass implementation returnskODFalse
, meaning that the data cannot be downloaded. The Finger service's item class overrides this method to returnkODTrue
, because the Finger service downloads data.- Override the item's
IsSecure
method to specify whether or not the item points to a secure site. The superclass implementation returnskODFalse
, meaning that the data does not require a protocol that provides security. The Finger service's item class does not override this method.- If the data can be downloaded, override the item's
GetContentKind
method to specify the kind of data that is returned by the Cyberdog stream. The kind is specified as an OpenDoc token. The Finger service's item specifies text data, thus the item'sGetContentKind
method returns the token representingkTextPlain
data, as follows:
ODSession* session = GetCyberSession(ev)->GetODSession(ev); return session->Tokenize(ev, kTextPlainKind);Listing 9 The
- Override the item's
GetFileInfo
method to return the file creator and file type of the item. This information is used when the item or its associated data is represented as a file in the Finder. The Finger service item'sGetFileInfo
method specifies a creator of'????'
and a type of'TEXT'
for data created by downloading with this item and a creator of'odtm'
and a type of'FNGR'
for references to the data, as shown in Listing 9.
GetFileInfo
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__GetFileInfo (AppleCyberdog_FingerItem *somSelf, Environment *ev, CDItemInfoType infoType, ODOSType* flCreator, ODOSType* flType) { ... if(infoType == kCDInfoDownload) { *flCreator = '????'; *flType = 'TEXT'; } else { *flCreator = 'odtm'; *flType = 'FNGR'; } }The kCDInfoDownload constant indicates that the data is in its native form, which is text in the Finger sample.Creating a Cyberdog Stream
If your item's content is accessible via a stream, you must override your item'sCreateCyberStream
method to create a Cyberdog stream that uses the service's protocol to download data. Listing 10 shows theCreateCyberStream
method of the Finger service item.Listing 10 The
CreateCyberStream
method of the Finger item
SOM_Scope CyberStream* SOMLINK FingerItem__CreateCyberStream (AppleCyberdog_FingerItem *somSelf, Environment *ev) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; AppleCyberdog_FingerStream* stream = new AppleCyberdog_FingerStream; stream->IFingerStream(ev, data->fURL); return stream; }Flattening and Unflattening a Cyberdog Item
You must override your Cyberdog item'sFlatten
andUnflatten
methods so that the item can be written to and read from a storage unit. These methods create a flat representation of a Cyberdog item and recreate an item from its flat representation, respectively. You must override itsGetFlatSize
method to return the size of the flattened item.You must use Cyberdog's format for your flattened items. You can place your item's unique values (called private data) after the required data. See the description of the
CyberItem
class in the Classes and Methods chapter of the Cyberdog Programmer's Kit for the structure of a flattened Cyberdog item.Listing 11 shows the
Flatten
method of the Finger service item. The Finger service item does not contain any private data.Listing 11 The
Flatten
method of the Finger service item
SOM_Scope long SOMLINK FingerItem__Flatten (AppleCyberdog_FingerItem *somSelf, Environment *ev, Ptr buffer, long length) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; long flatSize = somSelf->GetFlatSize(ev); if(flatSize <= length) { long classNameLen = strlen(kFingerItemClassName); char* url = nil; StringPtr defaultName = nil; ScriptCode defaultNameScript = smRoman; if(data) { url = data->fURL; defaultName = data->fDefaultName; defaultNameScript = data->fDefaultNameScript; } Ptr p = buffer; *((long*)p) = flatSize; p += sizeof(long); *((short*)p) = kCyberItemSignature; p += sizeof(short); *((short*)p) = kCyberItemVersionNum; p += sizeof(short); *((long*)p) = classNameLen; p += sizeof(long); memcpy(p, kFingerItemClassName, classNameLen); p += classNameLen; *((short*)p) = defaultNameScript; p += sizeof(short); if(defaultName) { memcpy(p, defaultName, defaultName[0] + 1); p += defaultName[0] + 1; } else { *p = 0; p += 1; } if(url) { long urlLen = strlen(url); *((long*)p) = urlLen; p += sizeof(long); memcpy(p, url, urlLen); p += urlLen; } else { *((long*)p) = 0; p += sizeof(long); } *((long*)p) = 0; p += sizeof(long); } else // Error--the buffer is not large enough to flatten into flatSize = 0; return flatSize; }Listing 12 shows theUnflatten
method of the Finger service item, which unflattens the item. The method shows the minimum amount of effort required to unflatten any Cyberdog item.Listing 12 The
Unflatten
method Finger service item
SOM_Scope long SOMLINK FingerItem__Unflatten (AppleCyberdog_FingerItem *somSelf, Environment *ev, Ptr buffer) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; Ptr p = buffer; long flatSize = *((long*)p); p += sizeof(long); if(data) { short cyberItemSignature = *((short*)p); p += sizeof(short); short cyberItemVersion = *((short*)p); p += sizeof(short); long classNameLen = *((long*)p); p += sizeof(long); p += classNameLen; short defaultNameScript = *((short*)p); p += sizeof(short); StringPtr defaultName = (StringPtr) p; somSelf->SetDefaultName(ev, defaultName, defaultNameScript); p += defaultName[0] + 1; long urlLen = *((long*)p); p += sizeof(long); if(data->fURL) ODDisposePtr(data->fURL); data->fURL = (char*) ODNewPtr(urlLen + 1); memcpy(data->fURL, p, urlLen); data->fURL[urlLen] = 0; p += urlLen; } return flatSize; }Listing 13 shows theGetFlatSize
method of the Finger service item, which returns the size of the flattened item. Because the item does not contain any private data, it returns the minimum size of any flattened item.Listing 13 The
GetFlatSize
method of the Finger service item
SOM_Scope long SOMLINK FingerItem__GetFlatSize (AppleCyberdog_FingerItem *somSelf, Environment *ev) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; long flatSize; long classNameLen = strlen("AppleCyberdog_FingerItem"); long urlLen = 0; long defaultNameLen = 0; if(data) { if(data->fURL) urlLen = strlen(data->fURL); if(data->fDefaultName) defaultNameLen = data->fDefaultName[0]; } flatSize = sizeof(long) + // Overall length sizeof(short) + // CyberItem Signature sizeof(short) + // Version sizeof(long) + // Class length classNameLen + // Class name (no null // termination) sizeof(ScriptCode) +// Display name script code defaultNameLen + 1 +// Display name (plus 1 byte // for the length byte) sizeof(long) + // URL length urlLen + // URL sizeof(long); // Private data length return flatSize; }Writing a Cyberdog Item to a Storage Unit
You must override your Cyberdog item'sExternalizeContent
method to write its content to storage. Listing 14 shows theExternalizeContent
method of the Finger service item. The method writes a Finger service item to storage in its internal format (kCyberItemKind
) and also writes the URL as text.Listing 14 The
ExternalizeContent
method of the Finger service item
SOM_Scope void SOMLINK FingerItem__ExternalizeContent (AppleCyberdog_FingerItem *somSelf, Environment *ev, ODStorageUnit* su) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; // Add a kCyberItemKind key with the flattened // Cyberdog item as its value. su->AddValue(ev, kCyberItemKind); somSelf->StreamToStorageUnit(ev, su); // Add a kODPlatformDataType key (scrapbook text) with the URL // as its value. if(data && data->fURL) { ODTranslation* translation = ::GetCyberSession(ev)->GetODSession(ev)->GetTranslation(ev); TempODValueType textType = translation->GetISOTypeFromPlatformType (ev, 'TEXT', kODPlatformDataType); if(textType != nil) { su->AddValue(ev, textType); StorageUnitSetValue (su, ev, strlen(data->fURL), (ODValue)data->fURL); } } }Cloning and Comparing a Cyberdog Item
You must override your Cyberdog item'sClone
method to allow the item to be copied. You must override your Cyberdog item'sCompare
method to allow an item to be compared with other items. An item that is created as a clone of another item should be equivalent when they are compared.Listing 15 shows the
Clone
method of the Finger service item. After the method creates and initializes a new Finger service item, the method calls the item's SetUpFromURL method to set initial values for the item's fields. See "Setting Up a Cyberdog Item" (page 10) for information about the SetUpFromURL method.Listing 15 The
Clone
method of the Finger service item
SOM_Scope CyberItem* SOMLINK FingerItem__Clone (AppleCyberdog_FingerItem *somSelf, Environment *ev) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; AppleCyberdog_FingerItem* clone = new AppleCyberdog_FingerItem; clone->ICyberItem(ev); clone->SetUpFromURL(ev, data->fURL, data->fDefaultName, data->fDefaultNameScript); return clone; }Listing 16 shows theCompare
method of the Finger service item. The method calls its inheritedCompare
method to determine whether the items being compared are derived from the same SOM class. If so, the URL text is compared for equality. If the URLs match, the items are equivalent.Listing 16 The
Compare
method of the Finger service item
SOM_Scope CDCompareType SOMLINK FingerItem__Compare (AppleCyberdog_FingerItem *somSelf, Environment *ev, CyberItem* compare) { ... // Call the inherited method first to determine whether the // Cyberdog items are of the same SOM class or not. CDCompareType val = AppleCyberdog_FingerItem_parent_CyberItem_Compare (somSelf,ev,compare); if(val == kCDCompareEqual) { // The item is a Finger service item. // Base the comparison on the URL. char* ourURL = somSelf->GetURL(ev); char* itsURL = compare->GetURL(ev); int stringCompare = strcmp(ourURL, itsURL); if(stringCompare < 0) val = kCDCompareLessThan; else if(stringCompare > 0) val = kCDCompareGreaterThan; else val = kCDCompareEqual; } return val; }Retrieving a Cyberdog Item's Icons
You must override your Cyberdog item'sGetIconSuite
method to retrieve the item's icon suite. Listing 17 shows theGetIconSuite
method of the Finger service item.Listing 17 The
GetIconSuite
method of the Finger service item
SOM_Scope Handle SOMLINK FingerItem__GetIconSuite (AppleCyberdog_FingerItem *somSelf, Environment *ev, CDItemInfoType infoType) { ... CFingerItemData* data = (CFingerItemData*) somThis->fData; if(data->gIconSuite == nil) { CUsingLibraryResources rsrcAccess; ::GetDetachedIconSuite(&data->gIconSuite, 128); } return data->gIconSuite; }TheGetIconSuite
method calls theGetDetachedIconSuite
function, shown in Listing 18, to retrieve the icon suite and detach its handles.Listing 18 The
GetDetachedIconSuite
function
OSErr GetDetachedIconSuite (Handle *theIconSuite, short resID, IconSelectorValue selector) { OSErr err; THz zone = GetZone(); SetZone(SystemZone()); *theIconSuite = nil; err = GetIconSuite(theIconSuite, resID, selector); // Detach the resource handles. if (err == noErr) { RoutineDescriptor detachProc = BUILD_ROUTINE_DESCRIPTOR(uppIconActionProcInfo, DetachProc); err = ForEachIconDo(*theIconSuite, selector, &detachProc, nil); } SetZone(zone); return err; }TheGetDetachedIconSuite
function calls a the DetachProc function for each icon in the suite. The DetachProc function, shown in Listing 19, detaches the icon's handle.Listing 19 The
DetachProc
function
static pascal OSErr DetachProc (ResType /* theType */, Handle* theIcon, void* /* yourDataPtr */) { if (theIcon && (*theIcon)) DetachResource(*theIcon); return noErr; }Getting and Setting a Cyberdog Item's Characteristics
Your service's item class may need to override additional methods that get or set the data associated with the Cyberdog item, as follows:
- Override the
GetFlags
method if you do not want to log your Cyberdog item or if you do not want to allow the item to be embedded. The Finger service's item class does not override this method.- Override the
GetStringProperty
method to return the value of each string associated with your Cyberdog item. The Finger service's item class overrides this method to return the default name property and the name's script system.- Override the
GetURL
method to return the URL for the item in a null-terminated string. The Finger service item class overrides this method.- Override the
SetDefaultName
method to set the default display name for the Cyberdog item. The Finger service item class overrides this method.- Override the
ShowInfoPart
method to change the display in the Item Info window for your Cyberdog item. The superclass implementation of this method displays the Cyberdog item's default display name and URL. The Finger service's item class does not override this method.- If your Cyberdog item refers to a secure location, override the
ShowSecurityInfo
method to display the Security Info window for your Cyberdog item. The Finger service's item class does not override this method because its items do not refer to secure locations.