TweetFollow Us on Twitter

Netscape Plugins
Volume Number:12
Issue Number:11
Column Tag:Internet Development

Netscape Navigator Plug-Ins

How to Enhance Navigator's Features

By Keith McGlauflin, Sidney, ME

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

With the release of Netscape Communications' Navigator 2.0, dynamic code modules, called plug-ins, were introduced to seamlessly enhance Navigator's functionality without altering its user interface. This article contains four sections: an introduction to plug-ins and their installation, HTML tags for loading plug-ins, the plug-in Application Programming Interface (API), and a sample plug-in.

Plug-in Introduction and Installation

Plug-ins provide a way for third party developers to enhance Navigator to support other document types without requiring Navigator to launch helper applications on the user's Macintosh. Unlike Java applets, plug-ins are platform native, which means that a plug-in written for the Macintosh will not work on Windows or UNIX platforms. All Navigator plug-ins exist in the Plug-ins folder that is located in the same folder as Navigator.

Plug-ins can be used to communicate through AppleEvents to other software applications on the user's computer, or can take advantage of other Apple system software features, such as OpenDoc or QuickTime VR, without changing Navigator's source code.

Above all, plug-in implementation doesn't affect the overall user interface for Navigator, making navigation around the World-Wide Web (by clicks on links as well as the "Back" button and bookmarks) the same from the user's perspective even though a plug-in is loaded on the current page.

To install a new plug-in, simply drop the plug-in into the Plug-ins folder and restart Navigator if it is running. When Navigator starts up it scans the Plug-ins folder for files of type NSPL, the type used for plug-ins. Next, a list of MIME types and file name extensions handled by each plug-in is created so that Navigator can map the MIME types and extensions of documents referenced on HTML pages to plug-ins. For example, the MIME type application/myplug could be mapped to the extension .mypl for the plug-in myplugin which would cause any files with names that end with .mypl to load the plug-in (the HTML used to load plug-ins and how to specify MIME types and extensions for plug-ins are explained in the second and third sections of this article respectively). The MIME types for plug-ins are briefly displayed on Navigator's splash screen as it starts up. When Navigator loads a document with a name that ends with one of the extensions mapped to a MIME type for a plug-in, the plug-in is loaded by Navigator. The plug-in is unloaded when the user leaves the page which references the plug-in or when the user quits.

HTML Needed to Display Plug-ins

Plug-ins can be one of three types: full screen, embedded, or background. Full screen plug-ins are displayed in a window separate from HTML code, and are usually loaded by clicking on a link to a document with a name that ends with an extension that is mapped to a plug-in MIME type. Embedded plug-ins are included in the same screen as the HTML for the current page using the EMBED tag (the sample plug-in demonstrated in this article is an embedded plug-in). Background plug-ins are used to perform tasks which don't require user interaction, such as playing audio clips.

Full screen and background plug-ins can be loaded with a anchor tag that the user clicks on, such as:

<A HREF="http://foo.bar.com/testdoc.spec">

where .spec files are a plug-in extension .

For embedded plug-ins the EMBED tag is employed, such as:

<EMBED SRC="http://foo.bar.com/testdoc.spec" 
PLUGINSPAGE="http://foo.bar.com/plugins/testdoc.html"
ALIGN=CENTER WIDTH=200 HEIGHT=75>

where SRC is the URL to a document with a name that ends with of one of the extensions which is mapped to one of the MIME types handled by one of the plug-ins in the Plug-ins folder (in this case .spec). The optional PLUGINSPAGE tag gives the URL of a page which has documentation for this plug-in. The other options specify alignment and size, similar to the IMG tag. See the documentation which is included in the Plug-in Software Development Kit (SDK) for other optional attributes that can be used with the EMBED tag (the URL for downloading the SDK is given in the plug-in API section of this article).

Whichever method you use, loading a file with an extension that is mapped to a MIME type of a plug-in creates an "instance" of the plug-in. Instances are used to differentiate between references to the same plug-in using multiple data files with the same extension. This means you can have several EMBED tags which call the same plug-in but use different data files.

The PLUG-IN API

Netscape Communications provides three platform dependent SDKs (UNIX, Windows, Mac). The latest version of the SDK (version 3.0 at the time of this writing) is available at http://home.netscape.com/comprod/development_partners/plugin_api/index.html.

Version 3.0 of the Plug-in SDK doesn't include the very useful Plugin Template folder that was included with version 2. When creating plug-ins, it is much easier to start with a template and fill in those methods than it is to write the plug-in from scratch. The SDK does include several sample plug-ins that you can use as templates for your plug-in.

Mac SDK Methods

The Macintosh SDK includes CodeWarrior example projects which provide the method headers, resources, and C++ source code for a several sample plug-ins. You can either modify the source for one of these examples, or download the source for this article's sample plug-in and use it as your template for creating a plug-in. All you do is fill in the plug-in methods and write the subroutines which are called by those methods. Table 1 shows the method names which Navigator will call in your plug-in and the purpose of each method.

NPP_Initialize Global initialization of the plug-in. Use to load
resources shared by all plug-in instances.

NPP_Shutdown Called when the last instance of the plug-in is
destroyed.

NPP_New Creates a new instance of the plug-in.

NPP_Destroy Deletes an instance of a plug-in.

NPP_SetWindow Assigns a window for the plug-in to draw into.

NPP_NewStream Notifies the plug-in that a new data stream
has been created.

NPP_WriteReady Returns the maximum number of bytes the
plugin can handle from a data stream.

NPP_Write Reads data from a data stream and returns
the number of bytes read.

NPP_DestroyStream Indicates that a stream is to be destroyed.

NPP_StreamAsFile Gives a local file for the data from a stream.

NPP_Print Print an instance.

NPP_GetJavaClass Returns the Java class of a plug-in.

NPP_URLNotify Notifies the plug-in when a URL request
completes.

Table 1: Plug-in Methods

Your plug-in must contain the routines listed in Table 1, even if you don't implement them fully (see NPP_GetJavaClass and NPP_URLNotify in the example plug-in's source code).

Also, there are methods available for your plug-in to call which cause Navigator to perform some action. These Navigator methods are beyond the scope of this article, but are documented in the Plug-in SDK's documentation.

Macintosh plug-ins also have methods which are Mac platform specific, as shown in Table 2.

NPP_HandleEvent Used to handle an event received from
Navigator.

NPN_MemAlloc Allocate a block of memory

NPN_MemFree Free up allocated memory

Table 2: Macintosh Specific Plug-in and NavigatorMethods

NPP_HandleEvent is a method that you must implement in your plug-in to handle user events (mouse clicks, etc.). NPN_MemAlloc and NPN_MemFree are methods that you don't implement in your plug-in, but, instead are available for your plug-in to call to allocate and free memory (they are Navigator methods).

The order in which Navigator calls the these methods in your plug-in is described in the sample plug-in section of this article.

Windows and Events

Several of these methods receive as parameters pointers to structures which hold the platform specific pointers to windows and events. Your methods will cast the generic pointers to the platform specific pointers your methods will need. For example, NPP_HandleEvent's header looks like:

 int16 NPP_HandleEvent(NPP instance, void* event)

and the event pointer can be cast into an EventRecord pointer:

 EventRecord*ev;
 ev = (EventRecord*) event;

Windows are a little more complicated but can be as easily cast into a WindowPtr or CGrafPtr. The structure for a generic window, NPWindow, looks like:

typedef struct _NPWindow 
{
    void*   window;                 
    uint32  x;
    uint32  y;                   
    uint32  width;              
    uint32  height;
    NPRect  clipRect;         
} NPWindow;

and the pointer window points to an NP_Port:

typedef struct NP_Port
{
    CGrafPtr    port;  
    int32       portx;   
    int32       porty;
} NP_Port;

The CGrafPtr port is the pointer to the CGrafPort for the plug-in to draw to. Converting from the NPWindow record to a WindowPtr can be done as follows:

 WindowPtrtheWindow; 
 NP_Port*port; 

 port = ( NP_Port* ) window->window;
 theWindow = ( WindowPtr ) port->port;

Creating a PLUG-IN: QuickTest

The sample plug-in, QuickTest, reads in a file of extension .test, converts its text to minimum, maximum, and current values, and displays an indicator. Two controls are also displayed, one to increment the indicator's current value and one to decrement the value (see figure 1).

Figure 1: Screen shot of the QuickTest plug-in

To create the sample plug-in first download the Netscape Navigator Macintosh SDK at the URL listed above. If you don't have access to the MacTech Web site to download the project files for the sample plug-in you can easily create them using the example plug-ins included with the SDK.

First, create a new folder for the plug-in named QuickTest. Copy the project file from one of the example plug-ins to the QuickTest folder (e.g. copy PPViewPict68K.µ to QuickTest68K.µ ).

Next, open up the project file for the platform you choose to implement (in this example I'll be using 68k code; PPC code is virtually the same). Remove the source code files from the example's project window except for npmac.cp and the routines under the Glue section if you are implementing a PPC plug-in. Create QuickTest.rsrc with your favorite resource editor. Open up the QuickTest.rsrc file and create STR# 128 string 1 for the MIME type for QuickTest, application/test, and create string 2 for the extension for our plug-in files, .test (see figure 2). STR# 128 is used by Navigator to determine the extension/MIME type mapping for a plug-in. Next, create two PICT resources: one ID 3000 which is a light colored rectangle, and the other ID 3001 which is dark colored. Save QuickTest.rsrc and add it to the QuickTest project window.

Figure 2: STR# 128 resource for QuickTest

Next, type in the code for the plug-in (see Listing 1) and save it as QuickTest.cp.

Listing 1: QuckTest.cp
// QuickTest.cp          by Keith McGlauflin
// Based on npshell.cp by Netscape Communications 
// Portions copyright 1996 Netscape Communications

// This Netscape Navigator plug-in displays an indicator 
// strip based on the contents of the .test file referenced
// in an HTML file.  Controls are displayed to increase and 
// decrease the indicator's level.

#ifndef _NPAPI_H_
#include "npapi.h" // Include Netscape's header file
#endif

#define kMax20   // Maximum indicator value
#define kMin1    // Minimum indicator value
#define kDecrease"\pDecrease" // Decrease button text
#define kIncrease"\pIncrease" // Increase button text
#define kBufferSize1 // Size of the read buffer in bytes

// Plug-in Instance Data

typedef struct _Plug-inInstance    
 // Data structure to hold all our variables
{// for this plug-in instance:
 NPWindow*fWindow; // Netscape Plug-in Window record
 uint16 fMode;   // Plug-in's mode (Full screen, embedded, 
    // or background)
 Boolean  amBusy;// Are we loading data?
 Ptr    data;    // Pointer to our data read from 
    // the .test file
 int    datalength;// Length of the data in Ptr
 Byte   min;// Indictator's minimum value
 Byte   max;// Indictator's maximum value
 Byte   current; // Indictator's current value
 ControlHandle decControl;// Handle to the Decrement control
 ControlHandle incControl;// Handle to the Increment control
} Plug-inInstance;

// Our plug-in's globals:

CGrafPort gSavePort; // Saved Port setting
CGrafPtr  gOldPort;// Original Port settings
short   gRFRN;   // Resource fork reference number 
       // for this plug-in's resource fork
Handle   gonPict;// Handle to the indicator's ON pict
Handle   goffPict; // Handle to the indicator's OFF pict

// Method prototypes:

Boolean SavePort( NPWindow *window );
void  RestorePort( NPWindow *window );
void  DrawContents( Plug-inInstance *This );
void  HandleContents( Plug-inInstance *This, Point where, 
 WindowPtr theWindow );
void  AddControls( Plug-inInstance *This );
void  UpdateCntrls( NPWindow *window );
void  GetData( Plug-inInstance *This, unsigned long len, void 
 *buffer );
void  SetValues( Plug-inInstance *This );
void  SetDefaults( Plug-inInstance *This );
intGetValue( Ptr bufferstart );

NPP_Initialize

// NPP_Initialize: (from npshell.cp)  This procedure sets the clip region for our
// gSavePort global, loads the gonPict and goffPict ControlHandles, and returns 
// NPERR_NO_ERROR.

NPError NPP_Initialize(void)
{
 gSavePort.clipRgn = ::NewRgn();
 
 gRFRN = CurResFile();    // Get the plug-in's resource file

 if ( ResError( ) == noErr )
 {
 gonPict = GetResource( 'PICT', 3000 );
 // Get the ON pict
 goffPict = GetResource( 'PICT', 3001 );     
 // Get the OFF pict
 }

 DetachResource( gonPict );
 DetachResource( goffPict );

 return NPERR_NO_ERROR;
}

NPP_Shutdown

// NPP_Shutdown: (from npshell.cp)  This procedure disposes of our clip region in 
// gSavePort and releases the indicator's ON and OFF pict handles.

void NPP_Shutdown(void )
{
 if (gSavePort.clipRgn)
 ::DisposeRgn(gSavePort.clipRgn);
 
 ReleaseResource( gonPict );// Release the on Pict's resource
 ReleaseResource( goffPict ); // Release the off Pict's resource
 
}

NPP_New

// NPP_New: (from noshell.cp)  This routine creates a new plug-in instance.  First the
// plug-in validates the instance we received from Navigator, then it allocates enough
// memory to hold the PluginInstance data structure.  Finally, the plug-in sets all
// the variables of our instance to their initial state.

NPError NPP_New(NPMIMEType plug-inType,
 NPP instance,
 uint16 mode,
 int16 argc,
 char* argn[],
 char* argv[],
 NPSavedData* saved)
{
 if (instance == NULL)
 return NPERR_INVALID_INSTANCE_ERROR;
 // Check for invalid plug-in instance

 instance->pdata = NPN_MemAlloc(sizeof(Plug-inInstance));
 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;
 if (This != NULL)
 {          // Initialize our plug-in instance's variables
 This->fWindow = NULL;    // No window assigned yet
 This->amBusy = FALSE;    // Not currently loading data 
 This->data = NULL;// Data pointer is NULL
 This->datalength =0;// Length of data is zero
 This->min = 0;  // Min, max and current are set
 This->max = 0;  // to zero
 This->current =0;
 This->decControl = NULL; // ControlHandles set to NULL
 This->incControl = NULL;
 
 return NPERR_NO_ERROR;
 }
 else
 return NPERR_OUT_OF_MEMORY_ERROR; 
 // Couldn't get enough memory
}

NPP_Destroy

// NPP_Destroy: ( from npshell.cp )  This procedure destroys a PluginInstance.  First
// the data pointer's memory is freed, then the controls are destroyed, and finally 
// the instance itself is freed and set to zero (so that it won't be errantly used again).

NPError NPP_Destroy(NPP instance, NPSavedData** save)
{
 if (instance == NULL)    // Check for invalid plug-in instance
 return NPERR_INVALID_INSTANCE_ERROR;

 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;

 if (This != NULL)
 {
 if (This->data)
 NPN_MemFree(This->data); // Free up data pointer
 
 if ( This->decControl != NULL )   // Destroy the controls
 DisposeControl( This->decControl );
 if ( This->incControl != NULL )
 DisposeControl( This->incControl );

 NPN_MemFree(instance->pdata);// Free instance data
 instance->pdata = NULL;  // Set instance to NULL
 }

 return NPERR_NO_ERROR;
}

NPP_SetWindow

// NPP_SetWindow: ( from npshell.cp )  This procedure sets our 
// PluginInstance's window to the window passed as a parameter 
// in this procedure.

NPError NPP_SetWindow(NPP instance, NPWindow* window)
{
 if (instance == NULL)
 return NPERR_INVALID_INSTANCE_ERROR;

 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;

 This->fWindow = window;  // Set this instance window to the NPwindow

 return NPERR_NO_ERROR;
}

NPP_NewStream

// NPP_NewStream: (from npshell.cp)  Sets the amBusy boolan to true, indicating that
// we are ready to load data from the .test file.

NPError NPP_NewStream(NPP instance,
 NPMIMEType type,
 NPStream *stream, 
 NPBool seekable,
 uint16 *stype)
{
 if (instance == NULL)
 return NPERR_INVALID_INSTANCE_ERROR;
 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;

 This->amBusy = TRUE;// Loading data...

 return NPERR_NO_ERROR;
}

NPP_WriteReady

// NPP_WriteReady: ( from npshell.cp )  Returns the value of our read buffer.
// (Navigator will be doing the writing, our plug-in will be reading.)

int32 NPP_WriteReady(NPP instance, NPStream *stream)
{
 return kBufferSize; // Return our buffer size
}

NPP_Write

// NPP_Write: ( from npshell.cp )  Loads the data from the .test file into the 
// PluginInstance.

int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, 
 int32 len, void *buffer)
{
 if (instance == NULL)    // Check for invalid plug-in instance
 return NPERR_INVALID_INSTANCE_ERROR;
 
 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;

 if (This != NULL)
 GetData(This, len, buffer);// Load the .test file's data into 
    // This->data
 return 0;
}

NPP_DestroyStream

// NPP_DestroyStream:  ( from npshell.cp )  When finished reading data, the plug-in will
// set amBusy to false, set the min, max and current values of our PluginInstance,
// draw the controls, and draw the indicator.

NPError NPP_DestroyStream(NPP instance, NPStream *stream, 
 NPError reason)
{
 if (instance == NULL)    // Check for invalid plug-in instance
 return NPERR_INVALID_INSTANCE_ERROR;
 
 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;

 This->amBusy = FALSE;    // Finished loading...

 if (SavePort(This->fWindow)) // Save the current GrafPort
 { 
 SetValues( This );// Set min, max and current
 AddControls( This );// Draw the controls
 DrawContents(This); // Draw the indicator
 RestorePort(This->fWindow);// Restore the old GrafPort
 }

 return NPERR_NO_ERROR;
}

NPP_StreamAsFile

// NPP_StreamAsFile: ( from npshell.cp )

void NPP_StreamAsFile(NPP instance, NPStream *stream, const 
  char* fname)
{
// QuickTest doesn't support loading files
}

NPP_HandleEvent

// NPP_HandleEvent: ( from npshell.cp )  Takes a pointer to an event, casts it to
// an EventRecord* and handles the event.  This plug-in only handles update and
// mousedown events.

int16 NPP_HandleEvent(NPP instance, void* event)
{
 Boolean eventHandled = false;// Has the event been processed?
 WindowPtr theWindow;// Window where mouse was pressed
 short wherePressed; // Coordinates where mouse was pressed
 EventRecord*ev; // Event record for this event
 NP_Port*port;   // The window for mouse click
 
 if (instance == NULL)    // Check for invalid plug-in instance
 return eventHandled;
 
 Plug-inInstance* This = (Plug-inInstance*) instance->pdata;
 if (This != NULL && event != NULL)
 {
 ev = (EventRecord*) event; // Convert the event to an EventRecord
 switch (ev->what) // Get the type of event
 {
 case updateEvt: // Update event:
 if( SavePort( This->fWindow ) )   // Save the current 
    // GrafPort
 {
 DrawContents(This); // Draw the contents
 UpdateCntrls( This->fWindow );  // Update the controls
 RestorePort( This->fWindow );// Restore the old GrafPort
 }
 eventHandled = true;// Event was processed
 break;
 
 case mouseDown: // Mouse down:
 port = (NP_Port*) This->fWindow->window;    
 // Get the GrafPort from the
 theWindow = ( WindowPtr ) port->port; 
 // fWindow data structure
 if ( theWindow != FrontWindow() ) 
 // If window isn't front window
 BringToFront( theWindow );
 // bring it to the front.
 else
 {
 wherePressed = FindWindow( ev->where, 
 &theWindow );
 // Find where mouse down
 switch ( wherePressed )  // If mouse press down in...
 {
 case inContent: // ...content...
 if( SavePort( This->fWindow ) )
 // Save current GrafPort
 {
 HandleContents( This, ev->where, 
 theWindow );
   // Handle the mouse down event
 RestorePort( This->fWindow );
 // Restore the old GrafPort
 }
 break;
 }
 }
 break;
 
 default:
 break;
 }
 
 }
 
 return eventHandled;// Let Navigator know if we processed the event
}

NPP_Print

// NPP_Print: ( from npshell.cp )  For brevity this procedure isn't implemented.
// The code in this procedure does the minimal processing required to make printing
// work (note that the plug-in's indicator and controls are not printed - see the
// examples in the Plug-in SDK for information on printing within plug-ins).

void NPP_Print(NPP instance, NPPrint* printInfo)
{
 if (instance != NULL)
 {
 if (printInfo->mode == NP_FULL)   // in fullscreen mode we don't do 
    // anything for printing.
 printInfo->print.fullPrint.plug-inPrinted = false;
 }
}

NPP_URLNotify

// NPP_URLNotify: Not implemented

void  NPP_URLNotify(NPP instance, const char* url,NPReason reason, void* 
notifyData)
{
// QuickTest doesn't implement this method
}

NPP_GetJavaClass

// NPP_GetJavaClass: Not Implemented (NOTE: this gives a warning during Make)
 
jref  NPP_GetJavaClass(void)
{
// QuickTest doesn't implement this method     
}

SavePort

// SavePort: Since Mac plug-ins share the drawing environment with Navigator we 
// MUST  save the current GrafPort settings.  This subroutine saves the current port's
// clipping rectangle.  Save other port settings before you change them.

Boolean SavePort(NPWindow *window)
{
 Rect clipRect;
 NP_Port* port;
 if (window == NULL)
 return FALSE;
 
 port = (NP_Port*) window->window;
 if (window->clipRect.left < window->clipRect.right)
 {
 // Preserve the old port
 ::GetPort((GrafPtr*)&gOldPort);
 ::SetPort((GrafPtr)port->port);
 
 // Preserve the old drawing environment
 gSavePort.portRect = port->port->portRect;
 ::GetClip(gSavePort.clipRgn);
 
 // Setup our drawing environment
 
 clipRect.top = window->clipRect.top + port->porty;
 clipRect.left = window->clipRect.left + port->portx;
 clipRect.bottom = window->clipRect.bottom + port->porty;
 clipRect.right = window->clipRect.right + port->portx;
 ::SetOrigin(port->portx,port->porty);
 ::ClipRect(&clipRect);
 clipRect.top = clipRect.left = 0;
 return TRUE;
 }
 else
 return FALSE;
}

RestorePort

// RestorePort: restore the old port settings so Navigator has the same GrafPort settings

void RestorePort(NPWindow *window)
{
 NP_Port* port;
 CGrafPtr myPort;
 
 port = (NP_Port*) window->window;
 ::SetOrigin(gSavePort.portRect.left, gSavePort.portRect.top);
 ::SetClip(gSavePort.clipRgn);

 ::GetPort((GrafPtr*)&myPort);
 ::SetPort((GrafPtr)gOldPort);
}

DrawContents

// DrawContents: draw the indicator.  Indicator is made up of on and off Picts which
// must be loaded from the plug-in's resource fork.

void DrawContents(Plug-inInstance *This)
{
 Rect drawRect;  // Drawing rectangle
 short  loop;    // Loop to process indicator
 
 drawRect.top = 0; // Set the initial draw rectangle
 drawRect.bottom = 34;
 drawRect.left = 0;
 drawRect.right = 17;
 
 for ( loop = This->min; loop <= This->current; loop++ ) 
 // Draw the ON picts for
 { // the indicator
 ::DrawPicture( (PicHandle) gonPict, &drawRect );  
 drawRect.left += 23;// Move the draw rectangle
 drawRect.right = drawRect.left + 17;
 // to the right
 }
 
 for ( loop = This->current+1; loop <= This->max; loop++ ) 
 // Draw OFF picts for the indicator
 ::DrawPicture( (PicHandle) goffPict, &drawRect );
 drawRect.left += 23;// Move the draw rectangle
 drawRect.right = drawRect.left + 17;
 // to the right
 }
 
}

HandleContents

// HandleContents: process mouse clicks in the contents of the window.  Track clicks
// in the controls and process those clicks.

void HandleContents( Plug-inInstance *This, Point where, 
 WindowPtr theWindow )
{
 int part, thePart;// part mouse down occurred
 ControlHandle theControl;// Control mouse was pressed in
 Str255 theTitle;// Control's title
 
 GlobalToLocal( &where ); // Convert coordinates
 part = FindControl( where, theWindow, &theControl );
 // Find control were mouse
    // down occurred
 if ( theControl != NULL )// If control valid
 {
 thePart = TrackControl( theControl, where, nil );
 // Track the press
 if ( thePart == inButton ) // If release in button
 {
 ::GetControlTitle( theControl, theTitle );  
 // Get the title of the control
 if ( EqualString( theTitle, kDecrease, false, false ) )
 { // If decrease title...
 This->current--;// Decrease indicator's current  value
 if ( This->current < This->min )  
 // Make sure current >= min
 {
 This->current = This->min; // Make current = min
 ::SysBeep( 10 );
 } 
 DrawContents( This );    // Draw the contents
 }
 if ( EqualString( theTitle, kIncrease, false, false ) )
 { // If increase title...
 This->current++;// Increase indicator's current value
 if ( This->current > This->max )  
 // Make sure current <= max
 {
 This->current = This->max; // Make current = max
 ::SysBeep(10);
 } 
 DrawContents( This );    // Draw the contents
 }
 }
 }
}

UpdateCntrls

// UpdateCntrls: updates the controls for the window.

void  UpdateCntrls( NPWindow *window )
{
 WindowPtrtheWindow; // Window pointer for window which contains controls
 NP_Port*port;   // NP_port which points to GrafPort

 port = ( NP_Port* ) window->window; // Convert fWindow
 theWindow = ( WindowPtr ) port->port; // to a WindowPtr

 UpdateControls( theWindow, theWindow->visRgn );   
 // Update the window's controls
}

GetData

// GetData: Get the data out of the buffer and put it into the data pointer of
// our plug-in instance.

void GetData( Plug-inInstance *This, unsigned long len, void *buffer 
)
{
 char *newText;
 long offset;
 
 if (This->data == NULL)  // No data loaded yet
 {
 newText = (char*) NPN_MemAlloc(len); // Allocate newText
 This->datalength = 0;    // Set length of data
 offset = 0;// Set initial offset
 }
 else   // We've loaded data
 {
 newText = (char*) NPN_MemAlloc(This->datalength+len);
 BlockMove(This->data, newText, This->datalength);
 NPN_MemFree(This->data);
 offset = This->datalength;
 }
 BlockMove(buffer, newText+This->datalength, len);
  // Move buffer data 
 This->data = newText;    // info data
 This->datalength += len; // and set the length
}

SetValues

// SetValues: convert data to min, max and current values.  If values are out
// of bounds then set min, max and current to default values.

void SetValues( Plug-inInstance *This )
{
 This->min = GetValue( This->data ); 
 // Set min
 This->max = GetValue( ( This->data ) + 7 );
 // Set max
 This->current = GetValue( ( This-> data ) + 14 );
 // set current

 if ( ( This->min > This->current ) || ( This->max < 
 This->current ) )
 SetDefaults( This );// Use default if current too low or too high
 
 if ( ( This->min < kMin ) || ( This->min > kMax ) )
 SetDefaults( This );// Use default if min too low or too high
 
 if ( ( This->max < kMin ) || ( This->max > kMax ) )
 SetDefaults( This );// Use default if max too low or too high
 
 if ( This->max < This->min ) 
 SetDefaults( This );// Use default if min greater than max
}

SetDefaults

// SetDefaults: If min, max or current out of range set all three to defaults

void SetDefaults( Plug-inInstance *This )
{
 This->min = kMin;
 This->max = kMax;
 This->current = kMin;
}

GetValue

// GetValues: read three bytes starting at bufferstart+4 ( to skip over the
// min=, max=, or cur= ) and convert it to an integer.

int GetValue( Ptrbufferstart )
{
 int value = 0;

 bufferstart = bufferstart + 4;
 value = ( ( *(bufferstart) - 48 ) * 100 ) + 
 ( ( *(bufferstart + 1 ) - 48 ) * 10 ) + 
 ( *(bufferstart + 2 ) - 48 );
 return value;
}

AddControls

// AddControls: add the controls for increase and decrease to the window

void AddControls( Plug-inInstance *This )
{
 Rect   drawRect;
 WindowPtrtheWindow;
 NP_Port*port;

 port = (NP_Port*) This->fWindow->window;    // Convert the fWindow
 theWindow = ( WindowPtr ) port->port; // to a window pointer
 
 SetRect( &drawRect, 10, 46, 100, 71 );
 // Rect for the decrement control
 This->decControl = NewControl( theWindow, &drawRect, 
 "\pDecrease", true, 0, 0, 1, pushButProc, 0);
 
 SetRect( &drawRect, 120, 46, 210, 71 );
 // Rect for the increment control
 This->incControl = NewControl( theWindow, &drawRect, 
 "\pIncrease", true, 0, 0, 1, pushButProc, 1);
}

Add QuickTest.cp to the QuickTest project. Change the project's preferences File name to QuickTest68K (or QuickTestPPC if you are creating a PPC plug-in) and the SYM name to QuickTest68K.SYM if you are implementing a 68K plug-in (see figure 3) . Finally, you need to add the Plug-in SDK's Include folder to the access path. You can do this by either adding the Include folder to the access path with the Access Path option of the Preferences dialog box or copying the Include folder into the Quick Test folder.

Figure 3: Screen shot of QuickTest project preferences

Select Make to compile the project. When the project successfully compiles, drop the resulting binary into the Plug-ins folder, which is in the same folder as the Navigator application, and restart Navigator if it is running.

Next, we need to create an HTML test document and a document, with a name that ends in the extension .test, that holds our data for the indicator.

Here's the HTML:

<TITLE>Plug-in Test</TITLE>
Here are the indicator and controls:<BR>
<EMBED SRC="example1.test" ALIGN=CENTER WIDTH=470
HEIGHT=75><P>
This is a quick sample plug-in.<P>

and the .test document referenced in the above HTML:

min=001max=015cur=005

Now we can test the plug-in. Drag the test HTML file's icon to Navigator's icon. The indicator will be displayed along with the controls to change the indicator's value. Try changing the HTML and .test file (you can even have multiple occurrences of the plug-in within one HTML document) to see how the HTML and .test files interact with the plug-in.

So, how exactly does QuickTest work? First, you'll notice that there isn't a main method. Instead there are several methods with names that begin with NPP and several other methods that are called by those NPP methods. Navigator will be calling these your plug-in's NPP routines, controlling how your plug-in gets executed.

First, Navigator calls the plug-in's NPP_Initialize method to set the clipping region of the plug-in's window, and loads the resources from QuickTest's resource fork for the on and off PICTs of the indicator. NPP_Initialize is only called once, the first time a plug-in is loaded.

Next, Navigator calls QuickTest's NPP_New method to initialize an instance of a plug-in, setting the instance's variables to their initial values. NPP_SetWindow is then called to set a window for the newly created instance.

To read in the data file which ends with the .test (in this example example1.test), Navigator calls NPP_NewStream, which sets the instance's amBusy boolean to true. This boolean indicates that the instance is reading data. NPP_WriteReady is then called to determine the number of bytes that will be read in at one time; in QuickTest's case this is one byte. Navigator then calls NPP_Write to write out bytes from the .test file to the plug-in. NPP_Write is continually called until all the data is read from the file. QuickTest reads this data in and sets the instance's data pointer to point to what was read. After the data is read Navigator calls NPP_DestroyStream which sets amBusy to false.

At this point in NPP_DestroyStream we save the current GrafPort settings, convert the data pointed to by data to minimum, maximum , and current values with SetValues, add the controls to the instance's window, and finally, draw the indicator with DrawContents. After this the GrafPort settings are restored.

The user then can click on the controls to change the indicator's setting. Any clicks, keystrokes, etc. cause Navigator to call QuickTest's NPP_HandleEvent method. This method checks which control is pressed and increments or decrements the indicator. NPP_HandleEvent also handles update events for redrawing the screen when an instance's window needs to be updated.

When the user goes to another page the instance is destroyed with NPP_Destroy. In QuickTest, NPP_Destroy frees the memory used by the instance and then destroys the instance by setting the instance to NULL. This is also a precautionary measure, since all NPP routines in QuickTest check for a NULL instance before doing anything with the instance. When the plug-in is unloaded, NPP_Shutdown is called to dispose of the clip region and release the PICT resources.

Adding a better input checking feature and displaying a value for the minimum and maximum would be two nice enhancements for this plug-in.

Conclusions

If you write you own plug-in which uses a MIME type that isn't already registered, you should register the MIME type so that the MIME type will be reserved for your plug-in. Information on registering MIME types is available at:

http://home.netscape.com/assist/helper_apps/rfc3.html

The future for plug-ins looks encouraging. Microsoft's Internet Explorer 2.0 also supports the same plug-in structure as Navigator, and Netscape's latest SDK (version 3.0, currently in beta) includes documentation to integrate plug-ins with Java and JavaScript. This means that a majority of users surfing the web have the capability to use plug-ins that you produce. Given the speed and flexibility of plug-ins, many software companies are rushing to produce plug-ins for their document types, including Adobe's Acrobat documents and RealAudio's RealAudio audio files.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

VMware Fusion 11.5.6 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more
Alfred 4.1 - Quick launcher for apps and...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
Dashlane 6.2032.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
Skype 8.63.0.76 - Voice-over-internet ph...
Skype is a telecommunications app that provides HD video calls, instant messaging, calling to any phone number or landline, and Skype for Business for productive cooperation on the projects. This... Read more
Mellel 5.0.3 - The word processor for sc...
Mellel is the leading word processor for OS X and has been widely considered the industry standard for long form documents since its inception. Mellel focuses on writers and scholars for technical... Read more
A Better Finder Rename 11.20 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
TunnelBear 3.9.10 - Subscription-based p...
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
Dropbox 103.4.383 - Cloud backup and syn...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
Daylite 2020.29.1 - Dynamic business org...
Daylite helps businesses organize themselves with tools such as shared calendars, contacts, tasks, projects, notes, and more. Enable easy collaboration with features such as task and project... Read more
HoudahSpot 5.1.5 - Advanced file-search...
HoudahSpot is a versatile desktop search tool. Use HoudahSpot to locate hard-to-find files and keep frequently used files within reach. HoudahSpot will immediately feel familiar. It works just the... Read more

Latest Forum Discussions

See All

Motorball is a car football game from No...
A few years back Noodlecake Studios announced that they would be dipping in the multiplayer gaming realm with two different games. The first of those, Golf Blitz, released a while back and has proven to be very popular. Now, the second has arrived... | Read more »
SINoALICE's latest update introduce...
SINoALICE's latest update has now arrived, adding several fan-favourite characters from popular RPG series NieR. Young Nier, Kaine, and Young Emil are available in-game as part of a limited-time crossover event set to run until August 20th. [Read... | Read more »
Rocat Jumpurr is an intense roguelite pl...
Rocat Jumpurr is a roguelite platformer from developer Mousetrap Games. You might already be familiar with it if you follow the Big Indie Pitch, where it won first place during this year's Pocket Gamer Connects London competition. Following its... | Read more »
PUBG Mobile's Play As One campaign...
Back in mid-July, we reported that PUGB Mobile had teamed up with Direct Relief to help raise money for the charity's COVID-19 response project. It focused on an in-game running challenge for players, which lead to the PUBG Mobile donating $2... | Read more »
Marvel Contest of Champions' latest...
Marvel Contest of Champions' latest motion comic has arrived, and it shows off new fighters Air-Walker and Dragon Man. Both characters are set to arrive in-game this month. [Read more] | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Global Spy is an intriguing 2D spy sim f...
Developer Yuyosoft Innovations' Global Spy launched last month for iOS and Android, though if you missed it at the time, we're here to tell you why it's well worth a go. This one's all about international espionage, tracking down elusive spies,... | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Hyena Squad is sci-fi turn-based strateg...
Wave Light Games has just revealed its latest release, Hyena Squad, a turn-based RPG set in a space station infested by gross aliens and the living dead. The announcement was first reported on by Touch Arcade. [Read more] | Read more »
Idle Guardians: Never Die is a pixel art...
SuperPlanet has been fairly prolific with game releases so far this year with both Evil Hunter Tycoon and Lucid Adventure releasing earlier this year. Now, they've released another idle RPG called Idle Guardians: Never Die, which you can download... | Read more »

Price Scanner via MacPrices.net

Apple restocks refurbished 2020 13″ MacBook A...
Apple has restocked Certified Refurbished 2020 13″ MacBook Airs starting at only $849 and up to $200 off the cost of new Airs. Each MacBook features a new outer case, comes with a standard Apple one-... Read more
Apple restocks clearance 2019 13″ 2.4GHz MacB...
Apple has restocked Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros starting at $1359 and up to $560 off original MSRP. Apple’s one-year warranty is included, shipping is free,... Read more
Apple restocks refurbished iPhone XR models s...
Apple has restocked Certified Refurbished, unlocked, iPhone XR models in the refurbished section of their online store starting at $539. Each iPhone comes with Apple’s standard one-year warranty,... Read more
Price drops! $100-$200 off clearance 27″ 5K i...
B&H Photo has dropped prices on clearance, previous-generation 27″ 5K iMacs by up to $200 off Apple’s original MSRP: – 27″ 3.0GHz 6-Core 5K iMac: $1699 $100 off original MSRP – 27″ 3.1GHz 6-Core... Read more
Woot offers Apple Watch and iPhone models fro...
Amazon-owned Woot has refurbished Apple Watch and iPhone models available from $99-$749 through August 6th. According to Woot, the items may show some wear, but they have all been fully tested and... Read more
Apple’s Phil Schiller Steps Down As SVP OF Wo...
NEWS: 08.05.20 – Former Apple senior Vice President of worldwide marketing, Phil Schiller, is stepping down from his long time role at the company in order to focus on spending more time with family... Read more
Expercom offers $320 discount on the 6-core 1...
Apple reseller Expercom has the Silver 16″ 6-core MacBook Pro on sale for a limited time for $2079 shipped. Their price is $320 off Apple’s MSRP for this model, and it’s the cheapest price currently... Read more
Apple announces Education pricing for new 202...
Purchase a new 2020 iMac or iMac Pro at Apple using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email... Read more
Apple reseller Expercom offers $256 discount...
Expercom has Apple’s new 2020 10-core iMac Pro available for order and on sale for $4743 shipped. Their price is $256 off Apple’s MSRP for this new model, and it’s the cheapest price we’ve seen so... Read more
Apple releases refreshed 2020 27″ iMacs with...
Apple today released updated versions of their 27″ iMacs featuring 10th generation Intel processors, SSDs across the board, a better 5K display, and improvements to the camera, speakers, and mic.... Read more

Jobs Board

Executive Team Leader GM Sales (Assistant Man...
…(Assistant Manager General Merchandise and Operations) - Apple Valley, CaliforniaApply NowJob ID:R0000082364job family:Store Managementschedule:Full Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 2 days ago Requisition # 122305 Sign Up Read more
Part-time Geek Squad *Apple* Consultation P...
**770829BR** **Job Title:** Part-time Geek Squad Apple Consultation Professional-Store 384(Ithaca) **Job Category:** Store Associates **Store Number or Department:** Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 1 day ago Requisition # 122305 Sign Up Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.