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 ofCyberItemand 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
somInitmethod 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
somUninitmethod 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 aSetUpFromURLmethod. Your service calls thisSetUpFromURLmethod when it creates the item. TheSetUpFromURLmethod is called with the URL, the URL's name, and the name's script as input parameters. If the name is not specified, yourSetUpFromURLmethod must create a name. Listing 6 shows theSetUpFromURLmethod of the Finger service item, which stores the URL and sets the name.Listing 6 The
SetUpFromURLmethod 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); } } }TheParseFingerURLfunction decomposes the URL in theurlfield into its host, user, and port components.Opening a Cyberdog Item
You must override your Cyberdog item'sOpenmethod to specify how to open the Cyberdog item. Your implementation can either open the item synchronously or asynchronously. Listing 7 shows theOpenmethod 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.
![]()
Openmethod 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); } } }TheOpenmethod 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, theOpenmethod 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
IsResolvedmethod to specify whether or not the item has been resolved. If you do not override theIsResolvedmethod, 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
Resolvemethod to identify the kind of data to which the item refers. The Finger service's item class does not override this method because theIsResolvedmethod always returnskODTrue. Thus, theResolvemethod is never called.- Override the item's
IsDownloadablemethod 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
IsSecuremethod 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
GetContentKindmethod 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'sGetContentKindmethod returns the token representingkTextPlaindata, as follows:
ODSession* session = GetCyberSession(ev)->GetODSession(ev); return session->Tokenize(ev, kTextPlainKind);Listing 9 The
- Override the item's
GetFileInfomethod 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'sGetFileInfomethod 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.
GetFileInfomethod 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'sCreateCyberStreammethod to create a Cyberdog stream that uses the service's protocol to download data. Listing 10 shows theCreateCyberStreammethod of the Finger service item.Listing 10 The
CreateCyberStreammethod 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'sFlattenandUnflattenmethods 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 itsGetFlatSizemethod 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
CyberItemclass in the Classes and Methods chapter of the Cyberdog Programmer's Kit for the structure of a flattened Cyberdog item.Listing 11 shows the
Flattenmethod of the Finger service item. The Finger service item does not contain any private data.Listing 11 The
Flattenmethod 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 theUnflattenmethod 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
Unflattenmethod 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 theGetFlatSizemethod 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
GetFlatSizemethod 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'sExternalizeContentmethod to write its content to storage. Listing 14 shows theExternalizeContentmethod 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
ExternalizeContentmethod 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'sClonemethod to allow the item to be copied. You must override your Cyberdog item'sComparemethod 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
Clonemethod 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
Clonemethod 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 theComparemethod of the Finger service item. The method calls its inheritedComparemethod 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
Comparemethod 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'sGetIconSuitemethod to retrieve the item's icon suite. Listing 17 shows theGetIconSuitemethod of the Finger service item.Listing 17 The
GetIconSuitemethod 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; }TheGetIconSuitemethod calls theGetDetachedIconSuitefunction, shown in Listing 18, to retrieve the icon suite and detach its handles.Listing 18 The
GetDetachedIconSuitefunction
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; }TheGetDetachedIconSuitefunction 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
DetachProcfunction
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
GetFlagsmethod 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
GetStringPropertymethod 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
GetURLmethod to return the URL for the item in a null-terminated string. The Finger service item class overrides this method.- Override the
SetDefaultNamemethod to set the default display name for the Cyberdog item. The Finger service item class overrides this method.- Override the
ShowInfoPartmethod 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
ShowSecurityInfomethod 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.