Addins in A5v5

by Dr. Peter Wayne

You think A5 is missing something? Well, in version 5 Alpha Software lets you add your own commands to Alpha Five. Read this article and you can be on equal footing with Alpha Software's development team! 

A5v5 allows the Xbasic user to modify Alpha Five's own internal structure. You can add new menu items to the Control Panel or to the default form view or browse view menu structure. The generic term for Xbasic code that modifies A5 is "addins."

Alpha Software is adding much of the new functionality of A5 as addins. This allows Alpha Software to rapidly redesign A5 and respond much more quickly than they would be able to respond if all the coding had to be done in "C".

Disclaimer: The version of Alpha Five that I used in this article is a pre-release beta copy of Alpha Five version 5. At the time of composition of this article (12/99), A5v5 will probably not be released for another 6 months.

Since addins are primarily used by Alpha Software for in-house development of A5 itself, there is almost no documentation about addins for the outside developer. Nevertheless, by studying the structure of the addins that Alpha Software has produced, and by careful reading of what documentation is available. it is possible to figure out how to write an addin.

If you haven't yet read the article on dot variables, I encourage you to read it now before you go much farther. It will make the scripts that follow more understandable.

My first addin script adds "Set bookmark" and "Go to bookmark" menu choices to the Records drop-down menu in Form view:

Fig1

Figure 1. A5v5 doesn't come with "Set bookmark" and "Go to bookmark" menu choices - they were created by the addin script I wrote.

You can see that there are 2 new menu items on this form. These menu items are present on every form that doesn't have a custom menu, because I added them to A5v5 itself.

The code to create an addin is short but it introduces some new concepts:

''XBasic
a=addin.create("form_set_bookmark")
a.set_context("form_view")
a.set_menu("Set bookmark","Bookmark this record","|&Records|First|")
a.set_code(<<%template%
with addin.variables()
this_rec=table.current().recno()
end with
%template% )
b=addin.create("form_goto_bookmark")
b.set_context("form_view")
b.set_menu("Go to bookmark","Go to prior bookmarked record",\
"|&Records|First|") 
b.set_code(<<%code%
'with addin.variables()
'table.current().fetch_goto(this_rec)
'end with
table.current().fetch_goto(addin.variables().this_rec)
<layout>.resynch()
%code% )

Script 1. Initial bookmark script.

Virtually every line of the addin script introduces a new concept. The concepts we will discuss are:

  1. addin.create() : the command to create a new addin
  2. .set_context() : indicates the context for the addin. From what I have been able to glean, valid contexts are "control_panel", "form_view", and "browse_view". There may be other valid contexts. I want my addin to operate when a form is being viewed, therefore the appropriate context is "form_view".
  3. .set_menu() : allows me to add a menu item and a help text to the menu structure. In this case, my menu items are added just before "First" in the "Records" drop-down menu list.
  4. .set_code(): defines the actual Xbasic code for the addin.
  5. <<%template%
    …..
    %template%

    This defines multiple lines of code. The special character is the "<<" which, in conjunction with whatever follows, defines the beginning of a multi-line code segment. The code segment is terminated by repeating whatever initially followed the starting "<<" symbols.

  6. with addin.variables() … end with

    A5v5 introduces the concept of a variable namespace. You are already familiar with the idea of local, shared, and global variables. Think of these as inhabiting different universes, or namespaces. Version 5 adds a new namespace, the addin namespace. Variables in the addin namespace are global to the addin namespace. You can refer to variables in another namespace by prefixing them with the namespace designation, e.g.

    username=global.variables().user

    will assign the value of the global variable "user" to the local variable "username." You can also refer to a bunch of variables in another namespace by enclosing lines of code in between

    with namespace
    end with

    So, in our code,

    with addin.variables()
    this_rec=table.current().recno()
    end with

    assigns the number of the current table's current record to a variable, this_rec, that exists in the addin namespace.

    Just to make it clear what is happening, in the code to move to the bookmarked record, I use the "longhand" approach of addin.variables().this_rec.

  7. <layout>.resynch : Alpha Five v. 5 allows the use of macros in scripts. Macros are enclosed in 2 angle brackets. The <layout> macro refers to the current layout, whether it be a report, a form, or a browse. It is predefined and can be used in all A5 scripts. Another useful predefined macro is <object> which can be used to refer to the current object on which the current script acts.
  8. Installation of addins: I saved this script to my own new library, "peter.alb". I placed "peter.al?" in the \packages directory under a5v5 along with all the other libraries defined already for v5. Then I deleted the addins.al? files in the a5v5 directory. When I launched a5v5 again, all the scripts in the libraries in the \packages directory were combined into a new addins.alb file, and the addins became part of my own customized Alpha Five.

Individualizing bookmarks for each table

You may have realized that the code in Script 1 creates a single bookmark reference, this_rec, which applies to all the forms and tables in my database. What if I want to individualize the bookmarks, so that the bookmark for the "checkbook" table is different from the bookmark for the "sendmail" table? I can accomplish this by adding further elements to the addin.variables().bookmark dot variable, and saving the bookmark to those elements instead of to the this_rec variable.. For example, for the "sendmail" table, I can write

with addin.variables()
bookmark.sendmail=table.current().recno()
end with

when setting the bookmark. Because of a syntactical inconsistency in the current beta version of A5v5, this particular code fragment won't work, but the equivalent statements,

p=addin.variables()
p.bookmark.sendmail=table.current().recno()

work just fine. In writing a general-purpose script, I obviously can't hard-code the table name, so I use the eval() function to create the entire dot variable:

''XBasic
a=addin.create("form_set_bookmark")
a.set_context("form_view")
a.set_menu("Set bookmark","Bookmark this record","|&Records|First|")
a.set_code(<<%template%
p=addin.variables()
eval("p.bookmark."+table.current().name_get())=table.current().recno()
%template% )
b=addin.create("form_goto_bookmark")
b.set_context("form_view")
b.set_menu("Go to bookmark",\
"Go to prior bookmarked record","|&Records|First|") 
b.set_code(<<%code%
p=addin.variables()
table.current().fetch_goto(eval("p.bookmark."+table.current().name_get()))
<layout>.resynch()
%code% )

Script 2. Separate bookmarks for each table.

Script 2 allows for separate bookmarks for each table in the database. But these bookmarks only exist for the life of the current Alpha Five session. Is there a way to save bookmarks from one session to the next?

Persistent bookmarks

You betcha! We can save the bookmarks by saving the dot variables to the database. A5 provides methods for saving dot variables to the database, to the registry, or to text files. In this final refinement, I'll save the bookmark dot variables to the database when the bookmarks are set, and restore them when saved:

''XBasic
a=addin.create("form_set_bookmark")
a.set_context("form_view")
a.set_menu("Set bookmark","Bookmark this record","|&Records|First|")
a.set_code(<<%template%
p=addin.variables()
eval("p.bookmark."+table.current().name_get())=table.current().recno()
:A5.save_settings("bookmarks",p.bookmark)
%template% )
b=addin.create("form_goto_bookmark")
b.set_context("form_view")
b.set_menu("Go to bookmark",\
"Go to prior bookmarked record","|&Records|First|") 
b.set_code(<<%code%
p=addin.variables()
p.bookmark.dummy=0
:A5.load_settings("bookmarks",p.bookmark)
table.current().fetch_goto(eval("p.bookmark."+table.current().name_get()))
<layout>.resynch()
%code% )

Script 3. Saving the bookmarks to the database.

Here, :A5.save_settings("bookmarks",p.bookmark) saves all the p.bookmark elements in the current database under a "bookmarks" node. Similarly, :A5.load_settings("bookmarks",p.bookmark) reads all the settings saved under "bookmarks" to p.bookmark elements. Currently the beta version of A5v5 requires you to create one instance of the dot variable before you can use the load_settings() method: that's why I have the line, p.bookmark.dummy=0

I admit I had some trouble understanding this, so for those of you who are struggling the way I was, here is an example from the Interactive Window of how :A5.load_settings() works:

p=addin.variables()
p.bookmark.dummy=0
? p.bookmark
=dummy=0.000000
:A5.load_settings("bookmarks",p.bookmark)
? p.bookmark
=dummy=0.000000
SENDMAIL=1.000000

One caveat: Script 3 saves the bookmark settings to the database. On a network, bookmark settings should be saved to the user's own machine, either in the Windows registry or in a file stored on the user's local workstation. v5 provides methods to save settings to both those locations. But that could be the subject of another article!

12/18/99

Don't forget, we need your feedback to make this site better!

Return to home