A Data Viewer implements user interface for displaying a particular kind of data. For instance, a JPEG viewer takes binary data of MIME type image/jpeg and displays it in a Newton view. An HTML viewer takes HTML text data and displays it in a Newton view..
The default Data Viewer plug-ins will be for plain text and HTML.
Note that all Data Viewers are contained within a parent "Container" view. The Container deals with scrolling events (up/down, left/right) and with opening, caching, and closing the Viewer at the appropriate times. The default Containers provided by NetHopper will likely also implement stationery hooks so that your Viewer won't need to deal with creating yet another stationery.
The Data Viewer life cycle:
The NetHopper Registration Manager unit provides a method for registering Data Handlers. All Data Handlers must use this method to tell NetHopper they are installed:
RegistrationManager:RegMIMEDataViewer(viewerSym, infoFrame);
The viewerSym must be a symbol with your developer signature appended (ex: '|FooDataViewer:ALLPEN|) to avoid collisions.
infoFrame contains the following slots:
codeFrame This is a reference to a frame that implements all of the required Data Handler methods.
name This is a user-viewable string that will be used to name your Viewer for things like helping the user decide which viewers are activated.
types This slot should contain an array of MIME types supported, as described in "Specifying MIME Types" on page 7. This array is used by NetHopper to keep track of which MIME types are supported by your Viewer. NetHopper will only provide your Viewer with data of a type that you've claimed to support.
preferencesForm This slot should contain a reference to any preferences view template you might have for configuring your Viewer. This view might contain things like a color-depth picker for an image viewer. The default value is NIL, no preference view. If this slot exists and is non-NIL, this view will be displayed when the user picks "(name) Prefs" in the NetHopper preferences view. Note that this view template must use protoPrefsRollItem as its template and must have its overview slot set to the name of this prefs view as it should appear in the NetHopper prefs.
To unregister a Data Viewer, call the UnRegMIMEDataViewer method:
RegistrationManager:UnRegMIMEDataViewer(handlerSym);
This method removes the Viewer named by handlerSym from NetHopper's Data Viewer registry.
The Data Viewer needs to provide several methods, described below.
dataViewer:Instantiate(content)
This method provides your Data Viewer with the input stream from which you can read input data, and the original transaction request frame which yielded that data. In your Instantiate method you should prepare for being Opened, but you should not yet open any views. Also, it's a good idea to wait until you receive a Start call before actually reading input data.
Parameters:
content. This is a content specification frame:
The slots are defined as follows:
type. Either 'cacheObject or 'stream. This slot contains a symbol. If the symbol is 'gotStream then the inputStream slot will contain a raw input stream. If the symbol is 'gotCacheObject then the cacheObject slot will contain a cache object which you placed in the cache. (See the Cache documentation for information on how to store an arbitrary object in the cache).
cacheObject. If type is 'cacheObject, then a reference to a frame, else NIL. If the data object being returned is a cache object, this slot contains a reference to the cache object. Otherwise, this slot is NIL.
inputStream. If type is 'stream, then a reference to an input stream, else NIL. If the data object being returned is an input stream, this slot contains a reference to the input stream. Otherwise, this slot is NIL.
mimeType. The MIME type of the data. This is provided for the case where the content is cacheObject, since there's no way to tell the MIME type of an ordinary frame.
transReq. A transaction request frame which describes the source of the content, and may include information such as the content title.
Note: You only need to handle cache objects if your viewer slams arbitrary objects into the cache. If you do not place any data in the cache, or if you place raw stream data into the cache, you will only ever receive an input stream in this method.
The Viewer's client calls this method to start the Viewer processing the input data. You should generally wait until you receive this call to start processing content.
dataViewer:Start(cbContext, cbMethod);
When your Viewer is done with the transaction request handed in Instantiate, call the callback method with a result code (NIL for no error, or an integer error code).
Your client will call this method to stop a document processing in progres. This method will typically be called before Start completes. If it's called after Start completes, this method is essentially a no-op.
This method sets the absolute maximum bounds that your Data Viewer can occupy. Typically the container containing your Data Viewer calls this method with a fixed width, but it can conceivably call it with both a fixed width and height.
dataViewer:SetMaxBounds(boundsFrame);
Where the boundsFrame is a typical viewBounds frame, except that top and left are set to 0, and right and bottom are the max width and height, respectively.
Open/Close/Show/Hide/viewDrawScript All of the usual view methods...A Data Viewer is a view!
This method is optional. If your viewer supports find it should return the number of times the keyword string was found in your viewer's content. If you return a nonzero value then it's likely your DisplayNextFoundItem method will subsequently be called.
local int numHits := dataViewer:FindKeywordStr("keyword");
If your Viewer cannot find any instances of the keyword then it should return zero.
This method is optional. If your viewer supports find, this method may be called after your FindKeywordStr method returns nonzero.
dataViewer:DisplayNextFoundItem();
If this method is called and you've reached the limit of the found items in your Viewer, then simply wrap back to the first item.
If your Viewer supports the concept of "headings", then you should a frame containing two slots:
items. An array of heading strings which will be displayed to the user.
objects.An array of heading objects which will be used to hand you back a magic cookie to allow you to goto the correct heading. (See GotoHeading) These objects can be any arbitrary data structure you choose, but they should correspond to the strings in the item list.
This method is passed a single parameter: one of the heading objects you provided as a result to GetHeadings. Your viewer should scroll (or whatever) to show the heading.
Dispose() This method is called when the container around your Data Viewer is completely finished with your Data Viewer. When you receive this message, you should completely release any resources you allocated to operate your viewer, and you should release your reference to the inputStream.
This method may be provided by a Data Viewer's "container" view to handle URL requests that the viewer may generate (for instance, when the user taps on a hyperlink in the view).
Because this method may or may not be present, the viewer should call it as follows:
:?DispatchRawURL(URLstring);
This method's return value is undefined, and there is no callback mechanism. Once your viewer calls this method, it can assume the URL string was either handled or dropped on the floor.
Send
self:Parent():?ReflowView();
to tell your parent when you need to change your viewBounds.
You can use this method to tell your parent your status. This status may be displayed to the end user.
self:Parent():?SetTransactionStatus(self, paramFrame);
Always pass self as the first parameter.
paramFrame is a frame containing the following slots:
icon. A bitmap icon to display to the user, if any.
titleText. A string to be displayed in the title area of a status display view, if any.
statusText. A string to be displayed in the status area of a status display view, if any.
gauge. A numeric value which will be translated into a 0 to 100 progress gauge.
When you're done passing status to your parent, you should call:
self:Parent():?SetTransactionStatus(self, NIL);
This notifies your parent that you no longer need to display status.