Creating Add-Ins for the Visual FoxPro Class Browser

August 1, 1995

Introduction

Working with class libraries and managing them is not an easy task. The Class Browser in Microsoft® Visual FoxPro® provides an easy, user-friendly interface for dealing with visual class libraries. However, ease of use represents only a mere fraction of the Class Browser's power. The Class Browser also supports the unique customization ability provided by add-ins.

Flexibility and the Class Browser

The key to a tool is how well it can work in getting a job done. Managing class libraries carries with it a myriad of chores, from checking the code in a class to merging it with the production libraries.

Visual FoxPro, in true keeping with the history of both Microsoft's visual development tools and the FoxPro product line, not only provides a simple-to-use tool for managing class libraries, but has also made the architecture of this utility totally open so that it can be customized to fit the needs of every individual. This customization is accomplished using add-ins.

Add-Ins

An add-in is a program that you write and "register" with the Class Browser. Once registered, this program can be executed automatically in response to an event in the Class Browser, or can be called manually by the user. An add-in can do just about anything you'd like.

An Add-In Program

*  Program...........: CLSNAME.PRG
*) Description.......: Simple add-in for the Class Browser that
*)                   : displays the current class name.
*  Calling Samples...: 
*  Parameter List....: 
*  Major change list.: 

LPARAMETERS toSource

WAIT WINDOW toSource.ProperBaseClass(toSource.cClass)

RETURN

Note the parameter—the Class Browser, when calling the add-in, passes itself as a parameter to the add-in. This means that all of the member objects, properties, events, and methods are available for reference. In the example above, the Class Browser's ProperBaseClass() method and cClass property are used in the add-in.

This capability allows you to do many things. You can use the information in the Class Browser to guide what you want to do (for example, if the currently selected class is really the .VCX file, you could loop through all the classes in the .VCX instead of just using the class currently selected), access the methods, or even get at the .VCX table itself. Understanding and using this powerful feature is key to creating add-ins.

Let's cover the nuts and bolts of registering and running add-ins.

Registering the Add-In

Before you can use an add-in, you must register it as follows (the Class Browser must be running):

_oBrowser.AddIn("AddIn Name", "ClsName")

The Addin() method registers an add-in. The syntax shown above specifies a name for the add-in (AddIn Name) and the program to run (ClsName.Prg). The name is case-insensitive, but the way you specify it determines the way it will be displayed in the Add-in menu.

Running the Add-In

The add-in can be run in several ways. First of all, the add-in can be run with the DoAddIn() method, using the syntax:

_oBrowser.DoAddIn("Class Name")

Passing the name of a registered add-in to the DoAddIn() method will run that add-in.

You can also run an add-in by clicking the Add-in button, which activates a menu with the installed add-ins. Figure 1 shows the Class Browser with the Add-In menu activated from the Add-in button.

Figure 1. Class Browser with Add-In menu

The same menu can be expanded manually with the AddInMenu() method as follows:

_oBrowser.AddInMenu()

Running this method will expand the menu at the location of the mouse pointer. You can close the menu with _oBrowser.DeactivateMenu() although you shouldn't need to because the menu is automatically closed when a selection is made.

Unregistering an Add-In

You can also use the AddIn() method to remove an add-in from the Browser registration table:

_oBrowser.AddIn("Class Name",.NULL.)

If the program name is provided as a value of .NULL., the Add-In record in the Browser registration table is marked for deletion.

Boosting Power with Add-Ins

Add-ins let you do all kinds of great things. The previous example is simple: It displays the name of the currently selected class by accepting the object parameter (remember, the Class Browser is the parameter) and then accessing the cClass property in the WAIT WINDOW command.

While the previous example is not particularly useful, it does illustrate a few key concepts. First, using the Class Browser as the parameter to the add-in gives the add-in access to the full power of the Class Browser. That's why it is so important to take the time to learn about the objects, properties, events, and methods of the Class Browser (they're all documented in the Visual FoxPro help file). Another key point here is that there is not much that an add-in cannot do for you.

The following add-in adds some additional header information to the code exported with the Class Browser's ExportClass() method. It is useful when having to represent a class in code. You may need to do this for system documentation, when writing articles or books, and so on.

*  Program...........: DOCCLASS.PRG
*) Description.......: DocClass is an add-in program for
*)                   : the Class Browser that exports
*)                   : the code for a class with a 
*)                   : standard heading at the top.
*  Calling Samples...: 
*  Parameter List....: 
*  Major change list.: 

LPARAMETERS toObject

#DEFINE cr_lf chr(13)+chr(10)

_cliptext = toObject.ExportClass()

LOCAL laHdrLine[5], lcText, lnCounter

laHdrLine[1] = "*  Class.............: " + ;
      toObject.ProperBaseClass(toObject.cClass)
laHdrLine[2] = "*  Author............: " + ;
      "Joe Developer"
laHdrLine[3] = "*  Project...........: " + ;
      "My Project"
laHdrLine[4] = "*  Copyright.........: " + ;
      "(c) My Company, Inc. 1995"
laHdrLine[5] = "*  Notes.............: " + ;
      "Exported code from Class Browser."

lcText = ""

FOR lnCounter = 1 TO ALEN(laHdrLine,1)
   lcText = lcText + ;
            laHdrLine[lnCounter] + cr_lf
ENDFOR

lcText = lcText + cr_lf

_cliptext = lcText + _cliptext

=MessageBox("Code exported to clipboard!", 32)

RETURN

In theory, this add-in is very simple: The ExportClass() method is used to dump the code for the class into the clipboard (modifying _cliptext modifies the clipboard). The array holds the additional header lines, which are then added, with carriage returns and line feeds, to the output text. The output text can then be pasted wherever it is needed.

This is a fairly good example of an add-in because it automates a procedure and reduces it to two simple mouse clicks.

Changing Class Browser Behavior with Add-Ins

By installing an add-using the manner shown earlier, you could run it by selecting a class, clicking on the Add-in command button, and selecting the appropriate menu item.

Sometimes an add-in most appropriately enhances or replaces existing functionality in the Class Browser. For example, the DocumentClass add-in could be viewed as analogous to the View Class command button. You could say that it would be appropriate to hook this add-in to the RightClick event of the View Class button instead of adding it to the Add-In menu.

To show how this is accomplished, let me take a closer look at the syntax of the AddIn() method.

AddIn(<Add-in Name>, ;
      <Program Name>, ;
      <Method Name>, ;
      <Active For Files>, ;
      <PlatForm>, ;
      <Comment>)

This function installs or removes a Class Browser add-in. It has the following parameters:

If you take a close look at the AddIn() method, you will notice that it provides a wealth of insight into the power and flexibility of the Class Browser. Add-ins can allow you to totally customize the Class Browser to run your code at almost any interval.

For example, if you wish to use Courier New as the font for the Class Browser, you can create the following add-in and register it for the Init() method:

*  Program...........: FONTSET.PRG
*  Author............: Menachem Bazian, CPA
*  Copyright.........: (c) Flash Creative Management, Inc., 1995
*) Description.......: Set the fonts for the class

LPARAMETERS toSource

toSource.SetFont("Courier New")

To register the add-in, use:

_oBrowser.AddIn("FontSet", "FontSet", "Init")

Figure 2 shows the Class Browser form after you run this add-in.

Figure 2. Class Browser with FontSet

Handling the Rename Crisis with an Add-In

Here's one final add-in. Renaming a class can be very dangerous because it can break the chain in a class hierarchy. Take the following example:

Suppose you have a CommandButton class called OKButton. OKButtonSubclass is a subclass of OKButton. Now, you rename OKButton to CloseButton. At that point, the OKButtonSubclass class is orphaned and no longer references the right parent class. In effect, OKButtonSubclass is now useless.

This situation can be remedied. If the Class Browser is set to display classes in alphabetical mode, you will be able to see OKButtonSubclass in the list of classes. You could then use the Redefine feature to set OKButtonSubclass as a subclass of CloseButton instead of OKButton. However, if you do a lot of renaming, or if there are many subclasses in a renamed class, all this work can get tedious.

The following add-in will take care of the problem. This add-in, called RenmAll.PRG, handles the renaming of all classes and objects based on the current object. Thus, if you rename a class from MyOriginalClass to MyNewClass, all subclasses of MyOriginalClass will be changed to reference MyNewClass instead.

The key to this add-in is knowing that the class name on which an object is based is stored in the Class field in the .VCX. The only limitation to this add-in is that it can adjust only classes and forms that are loaded in the Class Browser.

Here's the code:

*  Program...........: RENMALL.PRG
*) Description.......: Renames a class in the Class Browser
*)                   : and keeps the class hierarchy alive
*  Calling Samples...: 
*  Parameter List....: 
*  Major change list.: 

LPARAMETERS toSource

LOCAL lcOldClassName, lcNewClassName, lnOldDataSession, lnOldArea, lnOldDisplayMode

*-- The idea here is very simple. As long as the classes are 
*-- loaded in the Class Browser, we can get at the metadata
*-- tables (metadata1 - <n>).
*--
*-- All we need to do is get to all the records with the 
*-- OLD class name in the CLASS field and change it to the new name.

*-- Note, by the way, that this will REPLACE the cmdRename button's 
*-- click method entirely.

toSource.lNoDefault = .T.

*-- First, get and save the current class name.

lcOldClassName = ALLTRIM(LOWER(toSource.cClass))

*-- In order to prevent an error message, we need to
*-- set display mode to alphabetical. We'll set it back
*-- on the way out.

lnOldDisplayMode = toSource.opgDisplayMode.Value
toSource.opgDisplayMode.Value = 2

*-- Now, the name dialog.

toSource.RenameClass()

*-- Get the new class name.

lcNewClassName = ALLTRIM(LOWER(toSource.cClass))

*-- Now, go through all the classes in all open metadata tables.

lnOldDataSess = SET("datasession")

SET DATASESSION TO (toSource.DataSessionId)

*-- A quick note: You can have up to 255 tables open per 
*-- data session (according to the help file, anyway).
*--
*-- There is a slim chance that the Class Browser will have
*-- that many files open. However, since the following loop
*-- will run only until it finds an empty work area, this is 
*-- an easy way to run through all the work areas. 

lnOldArea = SELECT()

FOR lnCounter = 1 TO 255
   SELECT (lnCounter)
   
   IF EMPTY(alias())
      EXIT
   ENDIF
   
   IF "METADATA" $ UPPER(ALIAS())
      =RenameClasses(lcOldClassName, lcNewClassName)
   ENDIF
ENDFOR

SELECT (lnOldArea)
SET DATASESSION TO (lnOldDataSess)

toSource.RefreshClassList()
toSource.opgDisplayMode.Value = lnOldDisplayMode

RETURN

*  Procedure.........: RenameClasses                         
*  Created...........: June 19, 1995 - 12:23:43
*  Copyright.........: (c) Flash Creative Management, Inc., 1995
*) Description.......: Loops through the metadata table and renames
*  Calling Samples...: 
*  Parameter List....: 
*  Major change list.: 

************************************************************
PROCEDURE RenameClasses                         
************************************************************

LPARAMETERS tcOldClassName, tcNewClassName

GO TOP
SCAN 
   IF LOWER(ALLTRIM(tcOldClassName)) == LOWER(ALLTRIM(Class))
      REPLACE Class WITH LOWER(ALLTRIM(tcNewClassName))
   ENDIF
ENDSCAN

RETURN

The idea is simple: Call the RenameClass() method from here and trap the class name both before and after the class is renamed. Then, loop through all the records in all the open .VCX and .SCX files (that is, through all files with an alias of METADATA*) and replace all occurrences of the old class name with the new one.

Note that the add-in starts out by setting the display mode to alphabetical before playing with the class names. If the display is in hierarchical mode, the Class Browser will generate an error after renaming a parent class, indicating that it cannot rebuild the tree (because the chain has been broken). When all the names have been modified, the RefreshClassList() method is called to read in the classes again, and the display is reset to what it was before the add-in was executed.

Note also that this method is designed to replace the functionality of cmdRename.Click(). By setting lNoDefault to .T., the rename button's original click method will not run.

To register this add-in:

_oBrowser.AddIn("Full Rename", CURDIR()+"RenmAll", "cmdRename.Click")

Conclusion

The true test of a development environment lies not only in the features of the language but also in the tools it provides to get the job done.

Managing class libraries is not an easy task. The Class Browser represents, in its vanilla form, a powerful tool for managing class libraries. The hidden power of the Class Browser, however, in the form of the open architecture inherent in its class-driven design and in the add-in feature, increases its utility tremendously. Add-ins are being created to perform a myriad of tasks.

The job of managing class libraries is difficult enough without having to worry about the manual aspects. The Class Browser gives developers and class librarians a powerful way to facilitate many tasks.

Acknowledgments

We acknowledge the help of Flash Creative Management, Inc., Hackensack, NJ, in providing this material.