Approach Users Mailing List FAQ (Rich Text Format version)

Last updated: 12 Jun 2026

This FAQ contains a summary of answers to Frequently Asked Questions on the Approach Users Mailing List, as well as other material that may be helpful to a wide range of Approach users.

The text (.txt) version of the FAQ is available for download at https://approach.droppages.site/textfaq.htm

This Rich Text Format (.rtf) version of the FAQ is available for download at https://approach.droppages.site/textfaq.htm

The web version of the FAQ is at https://approach.droppages.site/faq.htm

Disclaimer:
Approach User Support FAQ (Web, Rich Text Format, and text versions) and associated documents (Web site, newsletters and mailing list documents) are provided as is without any expressed or implied warranties. The editor and contributors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Special thanks to www.XpertSS.Com for their many contributions to this FAQ 

Contents:
The Approach User Support web site  [UPDATED] 12 Jun 2026
How to get the latest edition of the FAQ  [UPDATED] 12 Jun 2026
How to get the Archive of LotusScripts  [UPDATED] 12 Jun 2026
Contributions, comments, corrections, or feedback
Lotus support now at http://www.ibm.com/support/

Notices

Our collective wisdom so far...

The basics ("Stuff you really should know before you read other stuff") 
      Getting started with joined databases!
      Help me design my database!
      Designing and problem solving techniques
      Looping macros
      Record numbering, including auto-incrementing serial numbers
      Using Boolean fields
      Reinstalling Approach
General information 
      What is Approach? (Is it truly relational?)
      How does Approach compare to other DB's?
      IBM's Lotus Approach Support web site
      Books
      Example and commercial Approach applications  [UPDATED] 12 Jun 2026
      Other information sources about Approach  [UPDATED] 12 Jun 2026
      Other information sources about LotusScript  [UPDATED] 12 Jun 2026
      Can I Develop Stand-alone databases (Run-time) using Approach?
      Can Approach handle big databases?
      Moving or copying databases (.apr/.vew .dbf .dbt files)
      Help for MS-Access uses moving to Approach
      Getting started with LotusScript
Approach/.dbf specifications and overcoming limitations 
      The latest version and upcoming developments
      Versions of Approach for MS-Windows / NT
      Versions of Approach for OS/2
      dBase IV limitations
      Limitations of Approach
      Maximum of 50 joins limitation
      .oyz index files
      Files and File Types
      Maximum of 20 charts per .apr
Optimizing databases and minimising the chance of problems 
      Optimizing the speed of a database
      Compressing databases with a macro or script
      Reducing ("Compressing") the size of a database file
      Why doesn't the .dbf file size decrease when I delete records?
      Good practices to reduce that chances of encountering problems
      DON'T delete macros, named find/sorts or scripts...
      Backing-up your data using a macro
Corrupt index (.adx) files 
      .apx files
      Repairing corrupt index (.adx) files
      Deleting and recreating index files (.adx)
Errors, lockups, crashes, and other problems 
      General Protection Faults (GPF's)
      Repairing corrupt .apr/.vew files (e.g. errors when changing views)
      Out of Memory error message
      "Record is locked- retry -cancel" error message
      "Someone locked this record" errors
      "Unable to open ___.dbf file, 5009" errors
      "Macro error: Import table has changed" error
      Internal Error: Cannot load string 0
      "Could not load infobox dynalink" error
      InfoBox, Tools Palette, etc... won't appear
      "Page Fault... at 0137:0049eb64" in Approach 96
      Approach 3.02 crashing in design mode and when switching views
      "An error has occured in your program" errors
      Database being used exclusively error
      "Internal Error - Cannot load string - 1035." error message
      "GPF in APPREXPR.DLL at 0009:0D2C." error message
      Vanishing scripts
      "Could not find database [path].mdb" error
      "couldn't open database because it is open in another language..."
      Globals Sub "Initialize" doesn't always get executed
Printing 
      Printing formatting problems
      Printer problems (including GPFs, HP Printers)
      Header / Footer problems in v2.1
      MS-Windows 95 printing blank pages (v2 & v3)
      Printing field definitions
      Printer fonts problems
      Pages sizes in v9.6
Dates and times 
      Calculating elapsed time
      Calculating ages from birth dates
      Calculating number of weekdays
      Finding or calculating dates
      21st Century Dates
      Two digit year assumptions (eg 01/01/29)
      Date format with 4 digit year (e.g. 1997)
      Importing Dates
      Dates in report titles
      Find the previous weeks records
      Y2K / Millennium bug compliance and leap years
      Time and Date Schedule (uses SQL and multiple records)  [UPDATED] 12 Jun 2026
Formatting and displaying fields and buttons 
      Field Formatting (including not / rounding numbers)
      Displaying Negative Values In Red (Changing fonts colours using LotusScript)
      Leading zeros (eg. 00054628)
      Trailing zeros (eg. 1.400)
      Display problems (including problems switching to a particular form)
      Removing unwanted characters from phone numbers
      Radio Buttons Field Label
      Make empty fields disappear (... rather than just turn invisible)
      Converting numbers to words 34 to thirty-four?
      Sliding fields together on mailing labels
      Including quotes, symbols and international characters in strings
      Phone numbers
      How to make a field invisible on a form
      Conditionally display a pushbutton
      Stopping Approach from using Marlett as the default font
Reports and form letters 
      Deleting the body in summary reports
      Tips on layout of Reports / Altering report width
      Total page counts on Reports
      Form letters (and multi-page reports)
      Stopping v96 (and beyond) from entering design mode when switching to a report
      Repeating panels in Reports
      Creating HTML and text file reports
Data entry 
      Limiting the options in a drop down list using an Alias table  [UPDATED] 12 Jun 2026
      Making keyboard entries into a formula or macro
      Limiting the number of characters entered into a text field
      Conditional form navigation ( tabs )
      Recovering deleted records
      Bypass the misuse of the "Esc"-button when using auto-increment
Finds 
      Automatically finding a 'drop down' list entry
      Preserving the found set when switching forms
      Finds on multipage forms
      Finding records which do not contain *text*
      Tricky finds
      Including Find criteria on a report
      How to pre-fill fields in a form-based find
Sorting 
      Setting default sort order for a database or view
      Saving records in their sort order
      Sorting memo fields or fields that reference a memo field
Calculated fields 
      IF limitations in v3.0*
      Using values from the previous record
      Tricky calculations
      Generating a random unused PIN number
      The Combine() function in calculated and memo fields
      Using calculated fields in joins and exports
      Calculations on numbers in a text field (eg imperial measurements such as 3'6")
Summary fields 
      Summary fields
      Demystifying Summary Functions
      Problems with summary panels
      Using a summary field to count records which meet certain criteria
Repeating panels 
      Problems with repeating panels
      Finds on repeating panels fields
      Using an alias to limit the records in a repeating panel
Working with other data formats and applications 
      Exporting numbers
      Approach 97 incorrectly exporting numbers via macros
      Working with Paradox database files
      Accessing Access files (*.mdb)
      Accessing .dbf's updated by Access / Paradox
      Using index files other than .adx (eg.mdx)
      Export of Crosstabs to 1-2-3
      Using Approach and IBM DB2
      SQL and Approach
      Using SQL from LotusScript  [UPDATED] 12 Jun 2026
      Using SQL in Approach with non-SQL data sources
      Sending email directly from a form
      Using the contents of a variable field as an export filename
      Exporting summary fields
      Using a macro or Script to open documents in other applications
      Problems getting TeamMail to work
      Export in the Quickbooks export format
      The Approach ODBC SQL server driver
      ODBC Connection to Oracle database tables
      Transferring data to the Clipboard
      Accessing information in an Approach database on the web (WWW)
      How to lauch your default browser to view a URL contained in a text field
Network installation of Approach 
      Running Approach on a network
      Finding the current Network ID
      Dealing with different paths to the same data from different computers
      Accessing .dbf files using Novell client 4.83 on Windows 2000
Upgrading Approach and/or your operating system 
      Do you need to upgrade?
      Conversion of old .apr's to Approach 96
      Approach and Windows NT
      Upgrading ...
      Free Approach 3.02 Upgrade
      Approach v97 and Windows 98/vME
      Running different versions of Approach concurrently
      Running applications developed in Approach for MS-Windows on OS/2 versions
      Installing the Approach 97 apfixpak
      Approach and Windows 2000
      Approach on a Linux file server
      Dialing phone numbers from within Approach
      Approach and Windows XP
      Re-enable Help in Win 10/11 for Lotus Approach or any other legacy application
Building applications and working with .apr's 
      Automatically opening files
      Automatically opening a particular view
      Automatic macro on opening/closing View file
      Keeping users out of design environment
      Database security
      Changing security passwords
      Stopping people accidentally entering data
      Stopping users from deleting records
      Stopping Approach from prompting to save an .apr
      Running a Macro in a different .apr files
      Moving a view from one .apr to another
      Starting Approach without displaying the startup banner
      User selection of database tables within an .apr
      Distributing .dbf database files to users
      Scripts are not imported with an .apr
Example LotusScripts 
      Getting the path of a database file
      Screen updating during script execution
      ConvertNumberToText
      CurrencyToWords function
      Conditional Automatic Entry
      Create a delimited text file from two fields
      Disable the action bar, smart icons, status bar and view tabs
      Serialized invoice number on billing
      Draw rounded rectangles to size (for envelope windows)
      Using keydown event to automatically enter or modify a date
      Launch multiple Approach applications at start-up
      Syntax use of currentview.body.objectname.text
      Script Launch at Record Change Event
      Check Existing Data and ask for details for new record
      Lotus Notes SQL script
      Read in the Approach User Section in win.ini
      Handling Approach Object Errors
      Error trapping and data manipulation script
      Creating a dot notation text file
      Progress bar
      Flashing text
      Import a text file (or .dbf) into a DBF
      Linked List class (beats 64K limit on arrays)
      Fill Field No.1
      Fill Field No.2
      Named Find Event
      Named Find Event with selection sort
      Named Find Event with a different sort routine
      Find the current network ID No.1
      Find the current network ID No.2
      User selection of database tables within an .apr
      Pushing selections from one list box to another  [UPDATED] 12 Jun 2026
      Accessing items in a list box
      SQL delete
      Activating a macro in another APR
      Converting Approach form to MS Word Document
      Creating a new row in a repeating panel
      Copying calculated fields to the clipboard
      A button activated script that launches URL contained in a text field
      Loading a document into some other application
      A "Translate" function in LotusScript
      Another "Translate" function in LotusScript
      Passing the contents of a field to Google.com
      Splitting a text field apart at Tabs
      Using Windows 95/98 API to launch another application
      Go back to the last thing that the user was using
      Go back to the last thing that the user was using (A different response)
      "Freezing" the screen while executing a LotusScript
      Controlling the window size when opening Approach
Unclassified articles 
      Generating and reading barcodes
      Zip codes and Post codes  [UPDATED] 12 Jun 2026
      Recovering deleted records
      Turning off screen updates when running a macro
      Advise when using memo fields...
      Creating a 'Help' file
      Storing pictures
      Remote dialup access to Approach
      Multi-line messagebox
      Tips on using the Script Editor
      Printing out .apr file properties
      Setting the auto-incrementing serial number
      Quick access to objects in the script editor
      Automatically saving your work

How the Approach Users Mailing List works

Contributors  [UPDATED] 12 Jun 2026
The Approach Users Mailing List
The Topic
Off topic and personal messages
The List Owner
Subscribing, unsubscribing, digests and other options
Question and Answer Posts
comp.databases
Lotus
Mail storms
Disclaimer
Editor

The Approach User Support web site  [UPDATED] 12 Jun 2026
The is the official web site associated with the Approach Users Mailing List and the other related services. Check it out at:

    https://approach.droppages.site/

How to get the latest edition of the FAQ  [UPDATED] 12 Jun 2026
The latest version of the FAQ is available at the Approach User Support web site:

    https://approach.droppages.site/

Archives of that web site have been created at:

    Wayback Machine: https://web.archive.org/web/20250701231017/https://www.johnbrown.com.au/approach/ 
    archive.today: https://archive.md/Z9KVf

How to get the Archive of LotusScripts  [UPDATED] 12 Jun 2026
The Archive of LotusScripts is now incorporated in the FAQ available at:

    https://approach.droppages.site/faq.htm#scripts

Contributions, comments, corrections, or feedback
If you have any comments, corrections, or feedback, please email me (John Brown) at approach-owner@mailman.anu.edu.au However, new FAQ articles and scripts should be set to the mailing list first so that people have a chance to comment on it before it goes into the FAQ.

Lotus support now at http://www.ibm.com/support/
Our frustrations with http://www.support.lotus.com have finally come to an end! The site has been shut down, and now the official place to get support for Lotus Approach on line is at http://www-3.ibm.com/software/lotus/support/approach/support.html, which at this stage looks as if it is going to take your frustration to new and dizzying hights!

I haven't got around to removing/updating my links from the old site yet. I'll try to get around to it soon. In the mean time, if anyone stumbles across an old link and knows where it should be redirected to, feel free to let me know at approach-owner@mailman.anu.edu.au. Thanks!

Notices

Our collective wisdom so far...

Getting started with joined databases!
Understanding relational (or "Joined") databases is a big area, but is vital to fully utilizing a program such as Approach. It can be a frustrating time with many concepts to learn, as well as all the ins and outs of Approach.

DO NOT START by launching into some major project that you expect to be fully functional tomorrow. Start small, using some test data, and test each function before you go onto the next. Read all you can, and spend time carefully planning what databases you need and how they should be joined. Once you have the basic structure of your database set up you can begin entering or importing you actual data.

But as I said, start by do some reading. Some books are recommended earlier in the FAQ, but don't forget the Approach manual either. It briefly takes you though the process of setting up joined databases. Looking at the example databases that come with Approach will also help you understand how the different data in different databases is linked together to provide many facilities that are not possible in flat databases.

Help me design my database!
(joins, repeating panels, what data in which database etc...)

Apart from simple 'card file' type databases, every piece of major database software you can think works with relational databases; Approach, Paradox, Access etc... In order to use any of these packages efficiently, including Approach, you first need to understand what a relational database is, and how you would divide your data into one. If you don't understand this then you are going to have difficulty using many features of Approach, or any other database software.

Questions concerning database design are quite common on the Approach Users Mailing List. However, strictly speaking most of these questions and answers have little or nothing to do with Approach. What the people asking these question lack not so much an understanding of how Approach works, but rather an understanding of what a relational database is and how they work.

And as it turns out, I am not going to attempt to explain this! Instead, what I suggest you do is this: Allocate some time and perhaps money, to do some reading or perhaps a short course in relational databases. Some books are suggested above. The Approach manual includes some information, but most people seem to need a more in-depth look at it before they really grasp what is happening. The better this ground work the easier and quicker you will be able to build an efficient database. I won't take all that long, and the benefits are huge.

Also have a careful took at the example databases supplied with Approach. Go into design and check out the structure of the database; the design of the various views; and how each aspect works. 

Its a good idea to also become familiar with all the features of Approach so that you can be aware of what is it ~ and isn't ~ capable of. In particular, getting to know macros and how to loop them is is very handy (see articles 'Looping macros' and 'Conditional form navigation ( tabs )' in this FAQ)

As you are doing these things keep some notes about how you might structure your database. When you are ready begin establishing your database one bit at a time with a small amount of test data. When it is fully functioning, or at least developed enough to do all the major functions, import your data, and begin using it!

Designing and problem solving techniques
Try this: Rather than working with you main database, try setting up and testing out things or trying to solve problems on smaller practice databases. This way you minimize the possibility of you loosing valuable data, or disabling your main database when you really need to use it. For example, if your having trouble with an import or export, set up a very simple flat (i.e. no joins) database and view form. Enter some junk data into a couple of records and try exporting it as a text file. If this works, try importing it back in again. When you can do this, expand this test database so that it has two databases that are joined, and the data from both database is displayed on the one form. Then export some data. Add a repeating panel. Export some more. Import some back in. At every stage do only one thing at a time and test it. This way you will easily identify at which point you are getting into trouble.

Once you have managed all of this on your test database you should be able to solve the problems on you main database.

Looping macros
(Including sequentially numbering a set of records and making global changes to a set of records)

This FAQ demonstrates macros implemented in Approach V3, 96 or 97 or any of the "Millennium Edition 9.x" releases. Macros were  implemented differently in prior versions of Approach, so if you used version 1 or 2, you will need to spend some time in the help files / manual of Approach to accomplish a similar result.

Broadly speaking, a looping macro (which may consist of several individual macros working together) does the following:

* performs a particular operation or operations 
* moves to the next record 
* runs the macro again from the top 

This sequence of steps means that the operation is performed on every record in the current found set. Conditions can be added to the operations so that they are only performed under certain conditions. For example, if you wanted to add a 6 to all of the phone numbers starting with a 5, then your macro would include the statement:

    SET db.phonenumber to If(Left(db.phonenumber,1)='5', Combine('6',
db.phonenumber),db.phonenumber)

   (where "phonenumber" is a text field)

Often it is necessary to have a macro that precedes the looping macro to set up the conditions that the loop needs for its correct operation. (I'll call this a Control macro). This may do things such as:

* Go to the required view 
* Find the set of records to be processed 
* Position to the first record in the set
* Set variable fields to initial values 
* Then "run" the looping macro
* Give the user a message that the operation has completed.

The last operation of the looping macro always runs the looping macro again (NOT THE CONTROL MACRO) This is demonstrated below, but first...

To carry information from one record to the next you need to use variable fields. For instance, if you want to sequentially number a set of records you need to know what number the previous record was in order to set the number of the current record.

The following example demonstrates both the use of a Control Macro and a Variable to sequentially number a set of records:

  1) Create a numeric field 'VarIDNumber' to hold the sequential number, and a variable field 'VarID' to use in calculating the sequential numbers.

  2) Create a looping macro 'SequentialLoop'. This macro should do the following:

* SET VarIDNumber to VarID 
* SET VarID to VarID + 1 
* ENTER   (to save the updated record)
* RECORDS Next (note this command will exit this macro when it runs out of records in the found set)
* RUN SequentialLoop 

     For this macro to work properly it needs the Control Macro created in Step 3)

  3) Create a macro 'Sequential numbering' to be the Control Macro. It should do the following:

* VIEW switch to the required view (optional) 
* FIND the required found set (optional) 
* SET VarID to the required number for the first record 
* RUN the looping macro 'SequentialLoop' and return to the next statement
* MESSAGE "The records have been numbered successfully." (optional)

To execute the looping macro, run the  'Sequential Numbering' macro.

Note: In V2.1 you need to use three macros (it does exactly the same things as shown above, but it just takes 3 macros to do it. In v2.1 it is easy to end up with loop macros containing several macros):

Macro 1: 
Set the variable field to 1 
Run Macro 2

Macro 2: 
Set the numeric field to variable field 
Menu Options: Next Record 
Run Macro 3

Macro 3: 
Set the variable field to variable field + 1 
Run Macro 2

Also see 'Record numbering' in this FAQ.

Record numbering, including auto-incrementing serial numbers
It is often useful to have a unique identifier for each record in a database -- like a customer number or order number, for example. Or to use in joins where other unique fields are not available. For example, you would not want to use a telephone number in a join because the number could change over time. So it would appear useful to just use the record numbers Approach assigns as you add records. You know, those numbers you see in the status bar at the bottom of the Approach screen? 

But this is not the case. Why?

* The record numbers are not part of the database and are not permanently assigned to particular records. 
* When you sort the records, the record that is #1 changes to the one that is "relatively" the first one in the current database. 
* When a record is deleted that record's number is not also deleted.
* The record numbers are not part of your data, and you do not have access to the record numbers in calculated fields or finds etc. Not even from a script!

If you want each record to have a fixed reference number that never changes as long as the record continues to exist, then you need to create a numeric 'serial number' field which automatically increments each time a new record is created. To do this, just create a numeric field using Field Definition. Then select the desired starting number and increment value in 'Options'. If you have existing records with no number in this field, you will want to add a unique number to them using the technique described elsewhere in this FAQ in "Looping Macros".

Unfortunately, the auto-incrementing serial numbering options on numeric fields are not accessible from LotusScript, which means that you cannot read or change the next serial number from a script. In other words, if you add a record using a ResultSet in LotusScript, the serial number will not be entered or incremented. In this situation you have two options: 

1) you can write your script so it doesn't use a ResultSet and instead adds and updates the record using a view.

2) you can create and use your own auto-incrementing field instead (details of how to do this can be found at http://www.xpertss.com, under 'Maintain auto-serial numbers when creating records with a ResultSet object' in the 'FAQ's - Tips and Techniques' folder).

Using Boolean fields
Boolean fields can contain a true or false value, or can be "empty" (ie contain a "null" value). Therefore if you want all records to have either a true or false value in the Boolean field then you need to specify a default value in Field Definition, and update any existing records using a macro or script, or FillField. Alternatively, when you need to search for false items, search for all of the items with do not contain a true value. This search will return all records in which the Boolean field contains either a false or null value.

You can't use a Boolean field in a join, but a way of getting around this is to simply use a numeric field that either contains a '1' or a '0', so use that instead. 

Reinstalling Approach
If you are reinstalling Approach to recover from a "one of the library files damaged" error or an unexplained GPF, you may need to uninstall Approach first. When reinstalling Approach it does not replace a file unless it really thinks it needs to. So if a file is corrupt but the installation program does not detect that it is corrupt, then the file may not be replaced - and your problem will remain unless you uninstall it first.

What is Approach? (Is it truly relational?)
This article assumes you are familiar with relational database terminology. If you are not, then all you need to know is that Approach is a database application!

Approach is a RDBMS (Relational Database Management System) or 'client-side file server' application. It does not have a native database format but operates as a front end to many different data formats (dBase IV by default). It will also operate as a fully functioning front end/client to Client/Server database servers such as Oracle, including the ability to take advantage of Stored Procedures, Triggers, and the like. Approach is more relationally compliant than many other desktop RDBMS's in that it partially supports cascaded updates and deletes, as well as referential integrity constraints.

How does Approach compare to other DB's?
The following is an excellent article comparing six major database packages, including Approach and Access.

http://www8.zdnet.com/products/content/cshp/1804/288515.html

Articles about how Approach ranks against other databases, as well as handy hints, and how to optimize your productivity with Approach and SmartSuite, are available by searching the following web sites:

http://www.pcworld.com

http://www.reviewsource.com

http://www.pccomputing.com

The following is a collection of comments made by different subscribers comparing Approach and Access:

Approach is oriented toward the non-programmer both in ease-of-use areas and in terminology. You can get a lot done with just Approach's design tools and a few simple macros, whereas in Access you must get into "basic" programming fairly quickly. Defining fields is an example of the programmerese in Access - it uses technical terms instead of the more simple "numeric  10.2" or "date fixed" terms in Approach. I also like the idea that my data is stored in commonly used formats like dBaseIV as that opens up the data to use with dBase shareware and reporting packages, if I need them. Access stores all your data and views in one file - you lose it and you have "lost the farm".

---

As far as Approach vs Access, both have advantages and disadvantages.  I find Approach easier to get started with.  It also handles data in a safer manner.  Your data tables remain separate from your front-end file (the .apr file) which makes them more secure, easier to save, and available for other programs.  (The last could also be a disadvantage under some circumstances.)  Access has much better third-party support - you'll find 20 or 30 Access titles in Borders or Barnes & Noble,  and (maybe) one on Approach.  And IBM (the owner of Lotus) does not have the
same commitment to desk-top computing and software that Microsoft has. 

Because your tables are not part of your various .apr files, you can start all over again with new .apr files, and safely destroy the old ones when you want to.  You may have noticed that you create calculated fields in Approach - for display in forms and worksheets, and summaries in reports.  The calculated fields are in the .apr files, so you might want to write down any neat ones you have developed for use in new .apr files. 

You can create and use multiple data-entry forms with any single table or any group of tables in one .apr file, and turn around and use some of the same tables, with other tables, in a separate .apr file. For example, the "customer" table can be in the sales-contact database, and also in the Accounts Receivable database, in such a way that the sales people would not see the money records, and the bookkeepers would not see the future sale projections. (Revenue and projections being in
separate, related tables.)

You can copy views within a .apr file.  The only way I have found to copy a view between .apr files is to import the entire source file, and delete the parts I don't want.  In doing this I also have to reset the table joins and connections.

---

When I evaluated Approach v Access the advice I got was that Approach was easier to learn and much faster to develop in. You can develop a larger app in Access. Approach has limits like 50 joined tables in an apr whereas I think (from memory) Access can handle 100. Anyhow, most Approach users would say that once you get above 20 joins in an apr you should split the app over multiple apr's for performance reasons. For our Company's circumstances Approach seemed the way to go and has turned out a big success. 

---

I use both Approach and Access.  I tend to agree with those that feel that Approach offers a more friendly user interface.  I have not come across any tool yet that can compare to Approach in terms of quickly designing and rolling out an app.  On the other hand, I have found that I am beginning to push the limits of what I can do with Approach.  I was forced recently to develop a billing application in Access, because I just could not get Approach to do what was needed without gobbling up all available memory or generating gpf's.  I qualify this by pointing out that I do my Approach development in v3.02 of Approach (16 bit).  I cannot speak to the relative merits of Approach 97.

Overall, though, I still prefer to use Approach whenever possible.  On your second question, there is no "correct" number of .apr files to underlying tables.  I find that I use multiple APRs with particular tables, each tailored to the needs of a particular group of users.  This flexibility is, IMHO, one of Approach's best features.

---

Approach vs Delphi 4?: Comparing the two products is like comparing apples and oranges. Delphi is a very good product if you like Pascal. It's not the easiest product to learn. Each time you perfect a level of understanding it, they come up with something new and better. If you write application for sale then it's one of the good products beside Topspeed.  If you write database management programs, you'll find Approach to be one of the best tools... For you and me Approach is better because it takes us maybe two days to knock out a database, in Delphi or Topspeed think of two weeks to two months or more.

IBM's Lotus Approach Support web site
IBM's Lotus Approach Support web site can be found at 
http://www-3.ibm.com/software/lotus/support/approach/support.html. It includes a downloads FAQ, 'Technotes', Product Information and 'White Papers'. 'Technotes' are technical documents which address particular problems people have had with Approach. It is a good idea to search here first if Approach is giving you an error message. 'White Papers' are "Detailed investigations into how products work, often including coding tips and techniques."

Books
The following books have been recommended in posts to the Approach Users Mailing List:

   * The manual that come with Approach. Always a good place to start!

   * Approach 97 for Windows for Dummies by Deborah S. Ray, Eric J. Ray / Paperback / Published 1997. This appears to be the only book that is specifically about v97. However, you may want to check out some of the v96 books first, as they cover most of what you will need to know and the 'dummies' style isn't for everybody.

   * Mastering Lotus Smart Suite 97 for Windows 95 by Sandra E. Eddy, includes 'reasonable' coverage of v97 apparently.

   * Teach Yourself Lotus SmartSuite in 24 Hours by Faithe Wempen and published by published by SAMS. Described by one person as a "Wonderful book, .... broken down into 24 lessons."

   * Using Lotus Approach 96 Special Edition (Que Books) (Described as being particularly good for New Users)

NOTE: If you are looking for a v97 book, the v96 books are still worth getting as the differences are minor and when you stumble across a difference you should be able to get the extra information you need from the Approach HELP files.

   * Using Lotus Approach 3 by Plotkin (Que Books)

   * Mastering Lotus Approach 96 for Windows 95 by James Powell (Sybex / Lotus Books, ISBN: 0-7821-1773-2) (This is the most highly recommended book, however, some subscribers have commented that IF YOU ALREADY OWN THE EARLIER VERSION (for v3.0*, see above) then check it out carefully before you by this one. They expressed strong disappointment that it has only been minimally from the earlier version, and major aspects of Approach 96 are not covered adequately. It therefore may not be worth the expense.)

   * Mastering Lotus Approach by James Powell (Sybex / Lotus Books) (This is the most highly recommended book for v3.0*)

   * Building Databases with Approach 3 by Elaine Marmel ("...seems to be an extended, well documented, tutorial on building a database that is usable in a business environment.")

   * Approach 3.0 for Dummies

   * Practical Approach version 3.0 by Siegel (MIS Press)

   * Introduction to Databases" by James T. Townsend  (recommend for business uses who are learning and updated designing databases. It is about databases in general rather than how to use Approach.)

Example and commercial Approach applications  [UPDATED] 12 Jun 2026
See the 'Archive of Example Database Applications' at the Approach User Support web site:

    https://approach.droppages.site/examples.htm

This includes a few of downloadable databases, as well as links to other such sites.

Other information sources about Approach  [UPDATED] 12 Jun 2026

   * A good way to learn about database structure and what Approach is capable of is to study the example databases that come with Approach.

   * The Approach User Support links page includes a load of Approach related web sites:

        https://approach.droppages.site/links.htm

   * The comp.databases newsgroup contains discussion about many different database applications including Approach. However, I haven't seem any Approach discussion on it for a long time. 
 
   * de.comp.office-pakete.lotus-smartsuite is an active German language newsgroup which contains discussion about all SmartSuite applications, including Approach. Also see the German language version of this FAQ at: http://smartsuitefaq.vol4u.de

The following non-Internet resources are the above web page, but are mentioned here for the benefit of those who don't have web access:

    * Lotus fax - back service: A blast from the past with was discontinued on November 6, 1998.

    * Lotus Forums on Compuserve: 
- GO LOTUSB for volunteer support on Approach - always good advice.
- GO PCAPP - the PC Applications forum has a general database section.
- GO BORLAND - for help and information on dBASE tips and utilities. 
- Search Compuserve for forums about DATABASE and you will get a long list of helpful contacts for Paradox, Access, Foxpro, DB2, and other database formats you may be using.
    * The Approach BBS: Now a mere memory in the history of online services

Other information sources about LotusScript  [UPDATED] 12 Jun 2026
   * A lot of the sources of information about Approach shown above will also include information about LotusScript

   * Don't forget to have a good look through the examples that come with Approach in templates, saved smartmasters etc...

   * See the Approach User Support Links page for some helpful web sites: https://approach.droppages.site/links.htm

   * LotusScript for Dummies (ISBN: 1-56884-638-X) I D G Books

   * Chapter 8 of "Developing SmartSuite Applications Using LotusScript". This book is on the SmartSuite CD in .pdf format which you read using Adobe Acrobat which is also on the CD, or you can download it from the PSC SmartSuite Developers site in the Resources section (
http://suitedev.psclistens.com/lotus/smtsuite/smtstdev.nsf/?Open) or by searching for the title at http://www.lotus.com.

   * "60 minute guide" for Notes LotusScript. This book assumes that you an understanding of programming background.

   * The LotusScript Language Reference, a comprehensive summary of the LotusScript language, presented in A-Z format. The LotusScript Language Reference is available as Help in all Lotus products that support LotusScript.

   * The LotusScript Programmer's Guide, a general introduction to LotusScript that describes it's basic building blocks and how to put them together to create applications. It is on the SmartSuite CD-ROM in .pdf format, and is also available for download (alone with other documents) from Lotus' SmartSuite Products pages (accessible via the Approach User Support Links page mentioned above).

   * The 'LotusScript Expression Assistant' is a programming tool available by searching for the title at http://www.lotus.com, which can apparently help you learn LotusScript language faster, and is useful to rethink ideas and which arguments can/must be used.

Can I Develop Stand-alone databases (Run-time) using Approach?
No, there is no way of creating fully stand-alone database applications using Approach. Any user of your application will need an appropriate version of Approach installed in order to use it. 

MS Access and some other database systems have either a "compiler" to create a runnable program from your design or they have a "run time" module that replaces the need for users to install the full product it was developed with. Some charge extra for this feature and some do not. But, there are currently no run time modules or compilers for Approach and none are under development. Lotus/IBM does not see any demand for this type of capability at all from their large corporate customers. 

According to Lotus, Approach was designed to be a 'desktop database' or 'front end' to data the user wants to manipulate. The idea is you can take an existing database (dBase, Access, Excel, Foxpro, DB2, Oracle, Paradox, SQL, 123 ...) and create your own little application, leaving the original database intact for others. Or you can develop your own simple database from scratch.

You can also develop a more complex application with macros and buttons that give your application the appearance of a full-blown application, and you can distribute it to other users, but everyone who uses it needs their own copy of Approach. Using the "Customizing and Automating Approach" chapter 9 in the user's manual you can secure the system such that  your users can't harm the database that you build for them. Pay particular attention to the Preferences, SmartIcons, and Custom Menus sections. You can design your database using custom pull-down menus and various buttons on forms so that all the native controls in Approach can be turned off or hidden from the user. And you can further protect both the data and your application's design by using Team Computing features covered in chapter 10. Also review the procedures for defining basic field settings in the various views you give the user to help the user enter data correctly and protect fields they should not change. FYI, this manual is available in Adobe .PDF format from the Free Downloads area on XpertSS.com.  Lotus is often amazed by the complex business applications that are being deployed with Approach!

If the cost of multiple user licenses for Approach users is a problem, purchasing site licenses and using IBM' volume purchasing scheme will greatly reduce the costs for businesses. And there are several discount software web sites that often discount older versions of the SmartSuite, making it very affordable.

Approach 96 and newer versions also include 'LotusScript' which gives you much the same capabilities of Access Basic. You need some programming skills to write your own scripts, but you can often copy scripts and modify them for your own use. This site and http://www.XpertSS.com contain many examples for your education and use.

Can Approach handle big databases?
Well, it probably depends on what you call big, the design of your database, and what hardware you have. People have reported databases of 3 or 4 million records, although at that size running summary reports and finds may take a while! Nobody has ever reported reaching a maximum number of records limit in Approach. In theory it should be able to handle the maximum number of records you can put in a dBase IV database, which is 4294967296 (4 gig = maximum 32 bit number).

In order to maintain the best ratio of speed and stability you may want to consider deleting the .adx files (indexes) on a regular basis (say every week or two). There are some possible complications with this so carefully read "Deleting and recreating index files" first.

It has been suggested that the number of joins has a far greater impact on the speed and stability of large databases rather than simply the number of records. One subscriber stated that they found a database having "80,000 records and approximately 20 tables joined" to be unusable slow, and suggested that a database of "100,000 records if there are more than 4 or 5 joins" to be a maximum usable size. Meanwhile somebody else replied saying that they had "databases exceeding an average of 400,000 records up to 2.2 million records and joins exceeding 10 databases with Approach 96 having no problems handling the data. "We have been doing multi field queries in an acceptable time frame and once the smart indexes are created, finds on fields are just as fast on a (database) with 2.2 million records as a find on a field with 400,000 records. Our standard hardware is a P5 166 with 64 meg of ram. On a P6-200 with 64MB RAM, doing a complex search on 4 fields takes 15 sec."

Moving or copying databases (.apr/.vew .dbf .dbt files)
The APR file includes the paths to the files as well as their names. Therefore unless you reproduce the directory structure exactly at the destination, the only reliable way to move or copy at database is using the FILE / SAVE AS menu item from within Approach. If you try and move them using a file manager program then the database may not be able to locate some of its components and OLE links may be lost.

However, for some strange reason, Approach returns a "Too many files open at once" error message if you try to use SAVE AS on a database with more than 25 joins. If you are using Win 3.1, this happens regardless of how many files your have specified in your config.sys.

Help for MS-Access uses moving to Approach
If you have been using Access for while, then the first thing need to understand is that good quality professional help is available, and you should make a full recovery in no time at all  ;-)

Sue Sloan has written a booklet oriented toward telling an Access user how to use Approach. A link to that booklet is in the Free Downloads area on http://www.XpertSS.com. Do a search with the word "access" in the title. 

Getting started with LotusScript
Getting started with LotusScript is a bit like driving a car - you do actually have to spend time learning how to do it!

The best place to start is the resources listed in article 'Other information sources about LotusScript'

The look through the example LotusScripts for ideas, especially if some of them come close to doing what you want a script to do. It is often easier to start playing with someone else's script and getting it going than starting from scratch with your own script (until you become an expert of course!).

Some good example scripts to start with are the following, because the authors have included pretty explicit instructions about how to get them going:

'A button activated script that launches URL contained in a text field'
'Passing the contents of a field to Google.com'

The latest version and upcoming developments
SmartSuite and stand-alone versions of Approach are no longer supported.

* Lotus SmartSuite version 9.8 with fixpack version 6.1 applied (9.8.6.1)  file version (9.8.902.406)

http://archive.org/download/LotusSmartSuite99 

Versions of Approach for MS-Windows / NT
The versions recommended are:

* Windows 3.* - v3.02 
* Windows 95/98 - v97 (NOT 97a) with the latest apfixpak applied, though you won't go wrong with v9.7
* Windows ME - "ME is a Dog! Avoid at all costs", so just buy a new operating system. It is a bad version of Windows 98, so if you are stuck with it, go with the W98 recommendation.
* Windows 2000/NT - v9.7 or V9.8.x ***See below for details
* Windows XP Home - Similar to the "ME" version, avoid it for business use.
* Windows XP and later - V9.8.x ***See below for details

Compatibility:
All versions can simultaneously access the same database files (.dbt/.dbf or paradox or other formats), but do not share an index file between v3.02 and any other version because the "SmartIndex" format is not compatible. SmartIndexes are only built for dBase files, which is the default type when creating a new database with Approach. 

Do not convert a V2.* ".vew" or v3.02 ".apr" file to any newer version than v97 directly. This is discussed further in the article "Upgrading ...". elsewhere in the FAQ. Once converted to v97 and saved in that format, you will no longer be able to use the ".apr" file in v3.02. 

Once in v97 format, your ".apr" file can be opened and used with all newer versions of Approach and it will remain useable in v97 unless you use a new feature, like the "dialog editor" that was introduced in  v9.5. You will get a warning about the conversion so you can avoid this problem. The version of LotusScript was updated also in the v9.5, so your scripts may not run correctly in earlier versions unless you "recompile" them. 

New ".apr" applications created in v9.5 and newer versions are saved in the new format automatically which makes them no longer work in earlier versions.  

Version history:
v1.* = the first version released by Approach Software of Mountain View. (circa 1992)
v2.0 = the last version before it was bought by Lotus. (circa 1993)
v2.1 = the first version released by Lotus. View files have .vew file extensions. Developed for MS-Windows 3.1 (16 bit). Despite the new version number it was basically unchanged from v2.0 but contained many fixes for problems in the original release. 

v3.00 = a substantial new version released in 1994. Completely new macros. View files have .apr file extension. Developed for MS-Windows 3.1 (16 bit). Problems are encountered (even with v3.01 and v3.02) if it is run on Windows NT v4 (see 'Approach and Windows NT') 
v3.01 = a free bug fix of version 3.00. If you are having any problems with v3.00 or 3.01 then get 3.02 before you bother to post. Everybody is going to tell you to get it anyway! 
v3.02 = a free bug fix of version 3.01. If you are having any problems with v3.00 or 3.01 then get 3.02 before you bother to post. Everybody is going to tell you to get it anyway! This version will work on all newer versions of Windows except XP.

v96 = Sometimes erroneously referred to as v4. A substantial update developed for MS-Windows 95 (32 bit). Introduction of LotusScript, the Find Assistant, etc. It was a terribly buggy release -- avoid it!
v97 = Developed for MS-Windows 95 and NT 4.0 but apparently runs fine on NT 3.51 (32 bit) Included in Lotus SmartSuite 97 or stand-alone. This version was given to owners of v96 for free due to the bugs in v96. The first version with bug fix updates available on the web -- the "C" patch (apfixpak.exe) is essential! Features include stored finds (very handy) and "Find/Replace" facility. This runs fine on Windows 95 but slows down considerably in design mode on Windows 98 and later. 
v97a = v97 with the first apfixpak "A" applied. Apply the latest apfixpak and the version will revert to v97 

vME = "Millennium Edition". Release Mid 1998. Mainly a compatibility update for Windows 2000 and a bug fix update with new 'Internet-FastSite' features.  It runs faster on Windows 98 compared to Windows 95. 
Release 9 or 9.0 = refers to vME. Release 9 was buggy and was very slow in design mode due to putting too many things into the Windows registry (a Microsoft recommendation!). 
Release 9.1 = a free bug fix release of vME in December 1998 which was a service release primarily for corporate customers and was not generally available. It relieved the registry problem found in the 9.0 release.

Release 9.5 = July 1999. A major update version released both as a stand-alone product and then as part of SmartSuite Release 9.5 around July, 1999. This version finally overcame the slow down in design mode problem mention since v97 on Windows 98 and later. It had enhanced 'Web functionality and Notes/Domino/DB2 interoperability'. Approach received a true "dialog editor", and the normalize feature of Notes Reporter let you use Approach to do reports on Notes databases better. It uses the v97 .apr file format by default, but if you use the Notes Normazlier or Dialog Editor or create a new .apr file it will convert the file to the newer v9.5 .apr format. Lots of bugs lead to the release of 'A', 'B' and 'C' bug fix patches for the SmartSuite version. Only one patch for the stand-alone product was made available.  (Approach v9.5 was the latest stand-alone version) 
Release 9.6 = November 2000. Certified for Windows 2000. Included a few bug fixes. Only available as part of Smartsuite 9.6.
Release 9.6.1 = June 2001 bug fix for  v9.6. 
Release 9.7 = Nov 2001. Certified for Windows XP. Finally fixed printing of crosstabs. Only available as part of Smartsuite 9.7. This release finally fixed the crashing of APR files created in W95 running now in W2000. 
Release 9.8 = October 2002. In addition to Windows XP compatibility updates and new filters for Microsoft Office file formats, many bugs were fixed including the problems with "expanding" fields in reports -- new reports will work correctly even if you run them in an older version, but old reports remain unfixed. Certified for Windows XP. Only available as part of Smartsuite 9.8. 
Release 9.8.1 = October 2003. Released only to IBM Passport Advantage customers who purchased SmartSuite v9.8 or a maintenance agreement for SmartSuite v9.8 under this program. Falsely listed as a "fixpack" on the Lotus support web site, it is a full release but contains only compatibility updates and a few bug fixes.
Release 9.8.2 = October 2004. Another update labeled a "release".
Release 9.8.3 = October 2005. Another update labeled a "release", but with major bugs in the Approach component making it non-functional. A "fixpack" for these problems was posted in December 2005. 
Release 9.8.4 = October 2006. Another update labeled a "release".
Release 9.8.5
Release 9.8.6.1 

To determine the fixpack version of an installatiom of Approach 9.8, right-click on the approach.exe file (which may be in C:\Lotus\approach or C:\Program Files (x86)\Lotus\approach), then click "Properties" then "Details" and read the File Version:

Fixpack Version / File Version
9.8 / 9.8.209.600 (original)
9.8.1 / 9.8.309.400
9.8.2 / 9.8.410.202
9.8 fix pack 3 (9.8.3) / 9.8.509.2203
9.8 fix pack 3.1 (9.8.3.1) / 9.8.511.1803
9.8 fix pack 4 (9.8.4) / 9.8.607.604
9.8 fix pack 5 (9.8.5) / 9.8.709.1005
9.8 fix pack 6.1 (9.8.6.1) / 9.8.902.406  http://archive.org/download/LotusSmartSuite99 

Along with the SmartSuite 9.8.1 release, it was announced by Lotus/IBM that future releases will be available only through the IBM Passport Advantage program. While "retail" versions will be announced and they will be for sale on the IBM web site, they are purchased without any support and will not receive fixpacks.  This announcement was not well received on the online forums where owners of SmartSuite 9.8 that were obtained outside of the Passport Advantage program felt betrayed by this change in policy. Note that small companies and individuals do not need to apply to join the Passport Advantage program -- there is an "Express" option that lets you buy the product thru the program directly and get support for only that one product. 

Versions of Approach for OS/2
"Approach for OS/2 Warp 4" was launched in 1998 as part of SmartSuite for OS/2 for Warp 4. It didn't have a version number. It was presumably equivalent to Approach for MS-Windows vME. Approach for OS/2 files are fully compatible with Approach 97 for Windows files (also used by Approach for MS-Windows vME and releases 9.*) and are not converted. Older files will be converted in the same way that v97 would convert them.

Later versions:
* v1.1 released December 1998. A minor update including some 'Euro Support', Y2K enhancements such as sliding windows, and bug fixes (of course!)
* v1.1.1 released July, 1999. A free bug fix update for v1.1. You can download it from http://www-3.ibm.com/software/lotus/support/approach/support.html
* v1.5 released December 1999. A minor release update containing more up to date import filters and updated LotusScript(v4.0)
* v1.6 released early 2001. It is an upgrade not a bug fix.
* v1.7 released Jan 2002. Another costly upgrade.
* v 1.7.1 released late 2002. An unannounced bug fix mentioned in some technotes, but only available to those with a Smartsuite support
contract.  This includes those that have purchased Smartsuite maintenance
from Passport Advantage or those that have purchased eCS Upgrade
Protection.

dBase IV limitations
Approach can use a number of different database file structures, but uses dBaseIV by default. dBaseIV has the following limitations that you might need to be aware of:

* maximum file size = 2 gigabytes (per type .dbf or .dbt file)
* maximum records per database = 1,000,000,000 (1 billion)
* maximum of 30 open dBaseIV databases on a single computer 

* maximum number of fields per record = 255 
* maximum size of a record = 4000 characters (= all the field-lengths added together)
   - Text field size equals the size defined in characters
   - Date field uses 8 characters stored as text YYYYMMDD
   - Time field uses 8 characters stored as text HHMMSSHH
   - Boolean field uses 1 character (T, F or blank is stored)
   - Memo or PicturePlus field uses 10 characters stored as a "record number" in the .DBT file
   - Numeric field uses characters equal to the size definition plus 1. (Numeric 10.2 stores in 13 characters, 5.3 stores in 9 characters. 

* maximum length of a text field = 254 characters 
* maximum number of characters in a dBaseIV memo field is 30,000 characters in releases prior to V9.8, which was fixed in that release to allow up to 65,536 (64KB).

Some common error messages when using dBaseIV files:
* -1003 when adding a field makes the record too long
* -1035 when the .DBT file has reached the 2 gigabyte limit and no more records can be added

Information about field names:
Approach Field Definition allows up to 32 characters for field names, and it allows alphabetic letters, whole numbers, spaces, commas, periods, and arithmetic signs. If you delete the SmartIndex file (.ADX):
* Your field names are truncated to the dBaseIV standard of  10 characters, all capitals, with no blanks or special characters other than the underscore. 
* Non-allowed characters and blanks will be replaced with the underscore. 
* A "Time" field name will always end with the characters "TM9" so that Approach will know it is a time field. 
* PicturePlus field names always end with the characters "PC9" so that Approach will know it is a PicturePlus field. 
* dBaseIV Field names must be unique, so if you have field names that are the same in the first 10 characters, you will see a series of similar field names which may be difficult to work with. For example, "Customer Name", "Customer Number", "Customer Notice" will become "CUSTOMER_N", "CUSTOMER_2" and "CUSTOMER_3". 

Information about sharing dBaseIV files:
Approach does not create or maintain the native dBaseIV index file for a database. This is important if you plan to also use the database in an application that depends on the presence of this index. Approach PicturePlus fields cannot be viewed in other dBASE applications.

Limitations of Approach

(also see 'dBase IV limitations' in this FAQ)

VIEWS:
* maximum number of views (ie forms, worksheets, crosstabs, reports,...) per .apr = 100 
* maximum of 5 pages per form type view for v96 and later; 1 page per form for v2.1 & v3.0* 
* maximum of 20 charts per .apr (see 'Maximum of 20 charts per .apr' article) 
* maximum of 20 crosstabs per .apr 
* Maximum number of summary panels in a report = 8 
* maximum rows displayed in a repeating panel = 30

FORMULAS: 
* maximum number of characters in a calc field formula or find condition formula is 255
* maximum of 30 imbedded IF's in v3.02 (see 'IF limitations in v3.0*' article) 

JOINS:
* maximum number of joins you can create between unique databases (that will be read-write) in one .APR file is 30. Any database joins you create after that will be read-only, up to a limit of 50 joins. 
* maximum of 450 characters in all the joined fields per .apr (but no limit to the number of actual joins). 
* circular joins are not allowed, but you can create an "alias" of a database and join it as appropriate. An "alias" is a "virtual copy" of the selected database. These joins count toward the other limits.  
* field types not allowed in a join are: Boolean, Memo, PicturePlus, Variable (these types are not displayed in the join dialog)
* calculated fields cannot be used in a join unless they reference only fields in one database and constants, and if they are not summary type fields.

MISCELLANEOUS:
* maximum number of characters in a variable type field of type text is 254
* maximum number of fields used in a sort = 255
* maximum of 340 characters in the text area of a MESSAGE command in a macro. Note that you can type more than 340 characters there, but if you save the .apr file it will crash on opening.
* maximum of 160 characters in a text string when using the SET command in a macro. This limit applies to all versions of Approach prior to v9.8
* maximum size of a string variable in LotusScript in v9.1 and earlier = 32,000 characters. In V9.5 and later it is limited only by your computer's available memory.

USING APPROACH: 
* maximum number of concurrent users of a .apr file = 16
* Maximum number of dBase database tables open at once = 29 (Approach produces and error on opening the 30th) 

Maximum of 50 joins limitation
There are no plans by Lotus/IBM to increase the number of joins allowed in a single Approach application file, which is currently 50. But I have rarely found that to be a problem in my applications. 

Here is my "approach" to a complex application with many potential databases being involved (per Sue Sloan):  

1) I separate a complex application into multiple Approach files based on the "functions" of the application. For example, I might have a Customer.apr for customer record maintenance, an Invoices.apr for invoicing activities, a PO.apr for purchase orders and a Reports.apr for reporting. This functional split is logical in that most business processes can be divided up this way. And this keeps things simple for maintenance purposes because I can work on one .apr while the users continue to use the others, if necessary.

2) I try to stay under 15 unique databases joined per .apr so that I can still use "SaveAs" to save the application .apr file and databases to a new location.  I do not worry so much about the total number of joins (that includes aliases), but I do try to avoid going over 30. 

3) I provide navigation between the .apr files with a central menu.apr and macro buttons. I use the OPEN command in a macro to switch between them. If the .apr is already open, the user will be switched to the open file at the place they left it. The user can also switch between .apr files using the "Window" menu at the top of the Approach window.

4) I use local "control" databases (one per user on their workstation) to pass values between the .apr files when necessary. I limit these to one record each by having a field that is defaulted to 'X' and validated as unique and filled in.

5) To avoid having to log in separately to each .apr file, I either use my own login method or have them log in using Team Security to the menu.apr. I record who is logged in using the "control" database record so I can mark "last updated by" fields in the databases.

6) I consolidate databases that are solely for lookup purposes into fewer databases. In them I use a "type" field to filter them for building drop-down lists. I can set a variable-type field to use as the filter for each different type in the database. Or I can access them from LotusScript even if they are not joined in the .apr and build the lists that way. 

This is not an exhaustive list of ways to make the 50-join limit irrelevant, but the ideas here may help others with similar projects.

.oyz index files
If you find a file with a .OYZ extension/type in the folder or directory where your databases are stored (.DBF extension/type) this indicates that the .ADX (SmartIndex) file for that database is corrupt. 

Approach has created the .OYZ file to allow access to the database but the results may not be reliable. In fact, since the .OYZ reverts the affected field names to their dBase default length and format, your scripts may not work at all! And you may be asked to "map fields" when opening your .APR file due to the field name changes. 

To get rid of this problem, you need to delete the .oyz AND then either repair or delete/rebuild the matching .adx file. 

IMPORTANT: BEFORE YOU DO ANYTHING, make sure you read the following two articles in the FAQ:

'Deleting and recreating index files (.adx)' which outlines this procedure and implications in more detail, and gives more details about .adx index files. 

'Repairing corrupt index (.adx) files' which outlines a widely affirmed method of rebuilding a .adx without losing any information. However, there is no guarantee that this procedure will work in every situation. 

Files and File Types
File types are:

.dbf = a dBase database file containing fixed length fields
.dbt = a supplementary dBase database file containing memo fields
.adx = ontains Smartindexes and information file created by Approach
         See FAQ articles 'Repairing corrupt index (.adx) files'
                and 'Deleting and recreating index files (.adx)'
.oyz = a replacement index and information file created by Approach
         if the .adx becomes corrupt. See article '.oyz index files'
.apx = an information file created by Approach when using Paradox
         database files. See FAQ article '.apx files'
.mdx = an index file created by other database applications.
         See FAQ article 'Using index files other than .adx (eg. mdx)'

File named like "Save49c6.apr" or "Savede40.apr" are temporary files Approach creates when saving an apr. Approach normally deletes them when the save is complete. If Approach crashes during a save they can be left behind but it's rare to see them. They aren't .apr files that can be opened by Approach. Attempting to open them will produce an "Internal Error 12345678" error message. You can delete them quite safely, but remember that there presence may indicate that one of your real .apr's was corrupted when your computer hung or crashed part way through a file save.

Maximum of 20 charts per .apr

The following was submitted by CRAVD01@aol.com and describes a method of getting around the maximum of 20 charts per .apr

Create a subdirectory called MENU.  Under MENU create a database called 
MENU_1.  The database consists of only one field named JOB.  Close MENU_1.

Create a database called CHARTS_1.  The database consists of only one field 
named JOB.  Close CHARTS_1.

Open a third database called CHARTS_2.  It also contains only one field named 
JOB.  Close CHARTS_2.

Now, open MENU_1 and join to CHARTS_1 and to CHARTS_2 on JOB.  Then join to 
your main database file and then to your other database files.  Save the 
Approach file and close.

Now, re-open CHARTS_1 and create a worksheet, etc. which will generate the 
first set of 20 charts from the data in your main database.

Now, re-open CHARTS_2 and create a worksheet which will generate 20 more 
charts from the data in your main database.

Now, re-open MENU_1 and create macros to open CHARTS_1 and its associated 
charts and CHARTS_2 and its associated charts.  If you design the MENU_1 menu 
correctly, the user will not realize that you are jumping from CHARTS_1 to 
CHARTS_2 to go to various charts.

You can continue to add CHARTS_3, CHARTS_4, etc. for an endless number of 
charts.

The following was submitted by CRAVD01@aol.com, and describes a method for getting around the 

The trick is to be sure that when you go from MENU_1 to CHARTS_1 that you 
create a form with menu choices that will guide the user to the correct 
chart.  Make the button macros choose which file to go to.

If MENU_1, CHARTS_1, CHARTS_2 are in the same subdirectory, when you create 
macro buttons, select the OPEN command and edit the location of the 
CHARTS_1.apr as follows:  change C:\MENU\CHARTS_1.apr to CHARTS_1.apr.  This 
allows Approach to operate correctly even if you change drives where the 
programs are saved.  If you leave the C:\MENU\.... in place, if you save to 
D:\ drive and then try to use the macro buttons, the system will tell you 
that it cannot find the C:\MENU\.... file.

Optimizing the speed of a database
Having the latest version of Approach on a suitably fast system with plenty of RAM (many suggest 64 meg), fast disk access, a large permanent swap file (ie. if you are using MS-Windows 3.1*. If you are using MS-Windows 95+ either make sure you have a lot of spare disk space (say 100meg or more) or that your virtual memory is set to 2.5 times the size of your RAM. Also on MS-Windows 95+: setting your file system to "server" instead of "desktop" can help disk access times), and fast disk caching will of course increase the performance of any large database. Defraging your hard disk regularly will help the performance of all of your applications, including Approach database. Also, make sure you regularly compress the databases.

You may also what to carefully consider your database design. A badly designed database will perform poorly in any application. For instance, the more joins you have, and the more joined databases shown on any one form, the slower your database will tend to be. One way to speed up a bit database is to split complex .APRs into 2 or 3 smaller less complex ones where each .apr opens fewer tables. Also make sure you don't have a lot of unused forms, reports, macros and scripts cluttering up your database. 

Databases work slower with 'full record locking' than with 'optimistic record locking'.

Minimize your use of color and graphics as these drastically effect the time it takes to update the screen.

In order to maintain the best ratio of speed and stability you may want to consider deleting the .adx files (indexes) on a regular basis (say every week or two). There are some possible complications with this so carefully read "Deleting and recreating index files" first. Saving your database using the FILE/SAVE AS menu command using the copy data option achieves much the same thing, although it means that everything get a new file name.

Delete excessive windows .tmp files. If this is a regular problem you may want to include the following statement in your autoexec.bat file: "del c:\windows\temp\apr*.tmp"

If you are using Approach on a LAN or WAN, also see 'Running Approach on a network' in this FAQ.

Wherever possible, use "numeric" data type as compared to "text".  Suppose you have a part number that is literally all numbers, containing no alphabet characters, and the number string is 10 digits long.  This field could be set up as a "text" field of 10 characters in length, or a "numeric" field of 12.3 (12 digits to the left of the decimal point and 3 to the right of the decimal point) or 10.0 (10 digits to the left of the decimal point and none to the right of the decimal point) characters in length.  All of these options will index and sort fine, but by setting up the field as a "numeric" field of 10.0 characters, the index .adx file will be smaller and Sorts and Finds will be faster. 

Whenever possible, join database files on numeric fields.  If numeric field joins are not possible, join database files on text fields with short lengths.  Why?  Two reasons.  The total number of joins in an application is limited by the total of the lengths of all of the joined fields.  The maximum limit for the total of the lengths of all of the joined fields is 450 characters.  [Where did this number 450 come from?  Answer:   personal communication from an Approach developer.]  Joining fields with shorter lengths will allow more joined database files to be included in the application.  The second reason is that fields that are joined are always indexed.  The .adx index files get large and inefficient when the joined, indexed, text field is long (over 10-20 characters).  This will slow down searches and screen updates.  [I am not sure that these details are correct.]
 
Joined fields in different database files must match in terms of data type (such as, numeric or text) and must match in terms of length (maximum number of characters) for optimal and reliable performance.  If the length of the joined fields is different, then the joins themselves can become unreliable and can become cross-linked so that the Unjoin function on the join map does not work. 

Big gains in the speed of Finds can obtained by using SQL queries. (see article 'Using SQL in Approach'). Conversely, doing finds, joins, or sorts on calculated fields may substancially reduce performance.

I have no idea why, but some people have reported significant gainsin speed by setting the default sort on the database files so the last record added appears first. In other words reverse the order completely.

Conclusions:  Design Approach applications for speed by:  1)  joining as few databases as possible in an application, 2) using 'optimistic record locking', 3) compressing .dbf files regularly, 4) deleting apr*.tmp files, 5) joining on numeric fields, 6) index on fields with short fieldnames, 7) joining on fields with the same data type and length, 8) consider using SQL queries on big Finds, and avoid doing finds, joins or sorts on calculated fields.

Stuart Tucker <Stutucker@aol.com> substantially contributed to the articles 'Optimizing the speed of a database' and 'Deleting and recreating index files (.adx)'. 

Compressing databases with a macro or script
First, read article 'Reducing ("Compressing") the size of a database file' in this FAQ.

Since compression of database files (Dbase or .dbf type) may need to be done regularly it is a good idea to automate the procedure. 

You cannot do this with macros in Approach v2.01 and v3.0*, however you can achieve the same result with these versions by using "Recorder" that comes with MS-Windows (look in the main or accessories folders). The key strokes from v3.0* are: ALT T P TAB 5 which takes you to the compression and where you can select the files. (This paragraph was not reviewed by XpertSS.com because those older versions are no longer installed in our offices.)

The rest of this articles applies to v96 and later:

Here is a couple of examples:

If you want to use a macro to compress a particular database then put the following into a macro:

Up to v9.1: 
    SendKeys ~%{F}UA{TAB 4}{RIGHT 4}{TAB}dbasename~{TAB 2}~

v9.5 and later versions: 
    SendKeys ~%{F}UA{TAB 4}{RIGHT 3}{TAB}dbasename~{TAB 2}~

... where dbasename is the name of the database, or at least the minimum number of characters of the name needed to uniquely identify it.

The following script will compress one table (i.e. the first table in the list of databases):

'Up to v9.1: 
Sub CompressTables 
        Sendkeys "~%{F}UA{TAB 4}{RIGHT 4}{TAB 4}~{TAB 2}~",True 
End Sub

'v9.5 and later versions: 
Sub CompressTables 
        Sendkeys "~%{F}UA{TAB 4}{RIGHT 3}{TAB 4}~{TAB 2}~",True 
End Sub

For every additional database the you want to compress, insert the one repetition of the following code into the script immediately BEFORE the {TAB 2)~:

        {TAB 7}{DOWN}{TAB 2}~

So, to compress four databases in v9.5 and later versions of Approach the script would look like:

Sub CompressTables 
         
Sendkeys "~%{F}UA{TAB 4}{RIGHT 3}{TAB 4}~{TAB 7}{DOWN}{TAB 2}~{TAB 7}{DOWN}{TAB 2}~{TAB 7}{DOWN}{TAB 2}~{TAB 7}{DOWN}{TAB 2}~{TAB 2}~",True

End Sub

You can find out the number and order of the tables in your APR using FILE/APPROACH/FILE PROPERTIES menu item.

If all this is a bit much, then you might want to consider purchasing DBFPack from http://www.XpertSS.com which does it all for you.

Reducing ("Compressing") the size of a database file
You may be able to significantly reduce the file sizes for your database depending on how your application uses your databases. This process is called "compressing" the database. But you may not see any change in the file sizes at all. Consider these two situations:

1) If you do not delete records from your database, there will not be any change in the .dbf or .dbt file size after compressing it. You will see a reduction in the size of the SmartIndex .adx file because all indexes not needed to support the current .APR file's joins will be eliminated. Approach will rebuild them when needed.

2) If you do delete records from your database, the records are only marked as deleted when you do that. They are not actually removed from the .dbf file. Therefore the .dbf is still the same size after you delete records. And any memo or PicturePlus fields are still in the .dbt, but the pointers to them are removed from the deleted record in the .dbf. 

To actually remove deleted records from the database you need to "compress" the database. This removes the records in the .dbf, the memo and PicturePlus fields in the .dbt, and unnecessary indexes in the .adx file.  In v96 and newer releases, you do this in Approach by going to the File menu, User Setup option, Approach Preferences option, and then select the Database tab. Then choose the database file name that you want to compress and click on the "Compress" button. This flags that you want it compressed, but Approach does not actually compress it until you press "OK". If you want to compress several databases files, you need to select and click the "Compress" button for each one and then click "OK". You need to be the only user of a database when you do this.

See article 'Compressing databases with a macro or script' in this FAQ to automate this procedure.

Once a database is compressed, deleted records cannot be recovered (see article 'Recovering deleted records' in this FAQ). For this reason, it is highly recommended that you create a backup copy of your files before doing this. 

Are there other reasons for compressing a database? Yes! A compressed database is more efficient in its indexing which speeds up your Finds. And if you don't ever compress a database then it is more likely to develop index errors or corruption. Some report they never compress their databases and they run fine that way for years, however!

Why doesn't the .dbf file size decrease when I delete records?
This is because when you delete a record it is not actually erased from the .dbf file, it is just marked as being deleted and further access to that record is denied. To actually erase a record from the .dbf you need to delete it and then compress the .dbf. (Also see articles 'Reducing ("Compressing") the size of a database file', 'Compressing databases with a macro or script' and 'Recovering deleted records' in this FAQ)

Good practices to reduce that chances of encountering problems
MGood practices to reduce that chances of encountering problems
Last updated: 23 February 2007 by XpertSS.com 

I. BACK UP YOUR FILES
 
Make backups of all of your application's files (.APR, .DBF, .ADX and .DBT and other types of database files you may use) regularly. How often you do this depends on how much work you want to do in reentering data that is not backed up. For some applications, once a week may be sufficient. For other applications, daily may be OK. For "critical" applications, you may want to consider a "mirror" backup method that some server software offers where files are continually backed up as changes are made. 

Make sure your backup is "good". No one should be using your application during backup operations because the files that are "in use" may not be backed up, depending on your backup method or software. Check the backup to see if it will restore OK by restoring it to another computer or location and opening the APR files.  

Make sure you have multiple "generations" of backups in case your most recent backup contains a problem you cannot fix. For example, for a daily backup situation, you could have backups for each day of the week and reuse them over the period of a week. At least one backup should be stored off-site just in case your office suffers a disaster and you need to continue operations elsewhere. Online storage facilities are also available that offer security and ease of access to backups. 

Specific backup and online storage solutions will not be offered in this FAQ.

II. CLEAN UP YOUR HARD DRIVES

Most Windows applications, like Approach, create temporary work files in your designated temporary file folder. These files are usually of type .TMP or .~MP with a random name. When an application is closed, it should delete all of its temporary files, but this does  not happen when the application does not close normally, when Windows itself crashes, or other interruptions occur. The problem is that a folder on your hard drive cannot contain an unlimited number of files. If this limit is reached, things do not work very well. Another problem specific to Approach is that those temporary files may be misinterpreted as current data files, causing various operational errors. 

So you should regularly go into your systems (all user computers will have these) with all applications closed, find those files and delete them all. Note that holding down the Shift key when you press the Delete key bypasses the recycle bin!

o In older versions of Windows, these temporary files were usually in C:\TEMP or C:\WINDOWS\TEMP.

o In newer versions of Windows, these temporary files are stored in C:\Documents and Settings\username\Local Settings\Temp

Regular use of Scandisk and Defrag utilities will also make sure your hard drives are in good condition. 

There are software packages that will clean up temporary files and perhaps even keep your "registry" clean, but those will not be offered in this FAQ.

III. SAVE YOUR WORK OFTEN WHEN DOING DESIGN WORK

When switching between design and browse frequently, get into the habit of periodically saving your work, then closing and reopening your application. This is especially important when modifying field definitions or joins, and when working with macros and scripts. Doing this will ensure that if something does go wrong you will lose the minimal amount of your work, and it also give Approach a chance to release system resources and refresh its links, indexes and graphics and start afresh.

Develop a practice of saving your APR file(s) such that you have at least 2-3 copies of the file saved with different names. I use something like Ordersdev001.apr, Orderdev002.apr, etc. for an APR that in production is named simply Orders.apr. During each restart of development per the above paragraph, copy the just closed APR file into another folder and rename the one you are working on. Keeping a log book of changes you are making is also a good practice, and you can note the APR names at the points in which you create them. 

If you are a bit forgetful about periodically saving your work, then you may want to consider adding the following line to the current script you are working on so that your changes will be automatically saved each time you run the script (don't forget to remove it once you finished developing that script):

    Current.Window.SaveChanges

The only caution about the above is that sometimes Approach appears to delete all of its scripts during testing them. And if you script does not run to completion, this statement will not be run. Therefore it is recommended that you not do this and instead merely use the script editor's File, Save Scripts after each change in them. This has the added advantage of recompiling your scripts and telling you if there are errors in them before you try another run.

IV. MINIMIZE PROBLEMS IF YOUR INDEXES ARE CORRUPTED NEED TO BE DELETED 

When naming fields, long field names are easy to read, but if you ever need to delete your index files (.ADX) you will have a lot of work to do to update all of those names. Deleting the index reverts your field names to the dBase standard which is:

o Maximum of 10 characters
o Underscore, alphabetic and numeric characters only (no spaces)
o Must start with an alphabetic character

Therefore if you ever need to re-map the fields names after deleting your .ADX file, they will all match except for "time" type fields. Approach modifies the stored field name for this type so it can distinguish them from "text" fields because there is no "time" field type in dBase. For example, you have a time field named ORDERTIME which will be modified to ORDERTITM9 where the "TM9" tells Approach it is a "time" field. This is also a good reason to not use a field name that ends with "TM9".

Instead of deleting index files, it is recommended that you keep a set of those .ADX files that are current to the field definitions in use. Then if your .ADX is corrupted, you merely replace the bad one with your copy and update any serial numbered fields next number value. This will preserve your long field names too!

V. BATTERY BACKUP FOR YOUR USER COMPUTERS AND/OR SERVER

It is a good idea to have battery backup devices on the power for all of your computers (and monitors) that use Approach as well as your server, if any. This is good for your computer because it is protected from power surges and you will have some time to shut down the computers normally. The main problem with power failures is that any write operation in progress to the hard drive may end badly -- writing out only part of a file or writing over other files!

VI. MISCELLANEOUS

Never delete a macro or global script. Just rename ones you no longer need to something like zobs1, zobs2, etc and reuse them for new macros or scripts. Deleting them in most releases of Approach will modify the macro/script used in MESSAGE and RUN commands! 

Don't type field or database names into formulas or text blocks. It is far more reliable to select them from the field name lists provided in field properties or in the Text Object, Insert, Field Value dialog. Apart from the possibility of making spelling mistakes, typing in field names can cause Approach odd behavior. For example, in a text block, a field may suddenly switch to a time format! (which may cause you to behave oddly...!)

If Approach starts "acting strange" when in Design mode, you may be experiencing a resource shortage. You should then exit all other Windows applications to free up memory and resources to stabilize things while you save your work. You can use the Windows Explorer to copy the APR file you are working on to another folder before you save the current version, just in case the current version is damaged in some way. Then restart the computer before continuing your development work.

DON'T delete macros, named find/sorts or scripts...
DON'T delete macros, named find/sorts or scripts! Deleting things is a primary cause of .apr file corruptions. Instead, just rename them to "unused1", "unused2" and recycle them when you need a new one.

Sue Sloan writes:

In A96 and later, three corruptions occur in the apr when you delete a macro. If you aren't doing these things then you won't notice adverse effects:

1. All Run commands in macros that run a LotusScript sub are corrupted. The first one runs a blank and all the others run a different sub. Each time a macro is deleted they are shuffled round again.

2. All Message commands that specify two buttons, where the buttons run macros other that **STOP** and **CONTINUE**, are corrupted. They all run
different macros after a macro is deleted.

3. The first time a macro is deleted in an apr all existing and future custom menus are affected. Any macro created subsequent to the first deletion won't run from a custom menu. This is irreversible.

This was partially fixed by Lotus in the newest releases of Approach (v9.5), but not 100%, so we are still recommending that you do not delete macros or scripts.

Backing-up your data using a macro
You have two options for doing this. One is a create a macro that exports all of the data that you want to backup to wherever you want it backed up to. However, this won't backup your .apr's.

To backup everything including your .apr's create a DOS batch file like the one below (a batch file is a plain text document which has been saved with a .bat file extension instead of a .txt, and which contains DOS commands):

cd c:\backup
copy c:\mydata\*.dbf c:\backup
copy c:\mydata\*.adx c:\backup
copy c:\mydata\*.dbt c:\backup

... you will of course have to customise this to the hard drive and file paths of your data and backup location.

The run this batch file using the OPEN command in a macro. 

.apx files
.apx files are used by Approach when working with Paradox database files. They contain Auto Increment Numbers and OEM or ANSI settings.

Repairing corrupt index (.adx) files
(This widely affirmed procedure was submitted to the Approach Users Mailing list by Jerry Sikes, Unisource Converting, and has been update from later suggestions that he has made.)

  1: Get exclusive control of database files. This means that no-one other than yourself has the database open in any application.
  2: Create a directory named "Safe", or "Backup" or whatever is meaningful to you. 
  3: Open each critical database, one at a time, as a new application with Approach. A default form and worksheet will be created.
  4: Run a find that gives you only 1 record in the found set. (Use Hide if necessary) This will create a minimal index and 1-record database when you do the next step.
  5: Export the "found set" to the safe directory, using the same database name. (Hint when Approach exports to this new directory, it creates a new one record index) 
  6: Close the new application without saving. (Optional...I do not normally save these since Approach can recreate this at will) 
  7: Repeat for all targeted tables.

The new safe copy indexes store the Long Field Name format that Approach uses, and the starting serial number for fields where that option is used.  

If you need to "uncorrupt" an index:
  1: Get exclusive control of that database's files again.
  2: Copy the safe adx file back to the working directory. 
  3: Reopen your application that uses the database and Approach should immediately launch into a "Smart Index Creation" mode. If not, do a "compress" of the database to force the reindexing.
  4: If you are using an old safe copy of the .adx file for a database with auto serial numbers, then you WILL need to manually reset the next serial numbers to the correct values.

It is important that you maintain a good revision control method. If you add fields to your working table, update the "safe copy" by exporting the 1 record again. Using an out-of-date index with an updated database structure could damage your data!

NOTE: Some people recommend that you compress your databases often as a corruption prevention method.  XpertSS.com has not found this to be necessary or advisable unless you delete many records from your database and need to reclaim the space they are using in the files. It is more important to discover and fix the problems that caused the index corruption in the first place. Some things that are helpful are:

 * Having backup UPS battery devices on your server and all workstations to prevent a power failure from interrupting data transfer over the network or writing incorrectly to your hard drive. 

 * Making sure that your validation rules prevent the "one" side of a join from accidentally becoming a "many" by validating join fields on the "one" side as both filled in and unique. If you use more than one field in a join, add a real field to that database with a modification formula that combines the join fields into one string and validates it as unique.

 * Be very careful when importing records into databases. The Import Data process ignores your field validation rules and will let you import duplicate values into unique-validated fields. It also invalidates the database index which forces a reindex cycle that will impact user access to that database until the import has finished (locking messages, SmartIndexing messages, etc). 

TIP: No amount of index backups will help if your database (.dbf and .dbt) becomes corrupted. Making regular backups of the entire set of files on a regular basis and storing them either in a fire-proof safe or off-site is highly recommended. And please, test your backup files to make sure they are OK and will restore when needed! Current technology makes backing up to CD/RW very simple and inexpensive, without resorting to compacted files on unreliable media like tape drives. 

Deleting and recreating index files (.adx)
In order to maintain the best ratio of speed and stability, you should consider either deleting the .adx files (indexes) on a regular basis (say every week or two) or compressing the databases. Alternatively, you can use the .adx repair procedure (see 'Repairing corrupt index (.adx) files') which preserves some of the information in the .adx, but may be a more involved depending on the design of your database.

Why is database/index maintenance recommended? 

  1) The SmartIndex files for dBase databases in Approach are built up over time to support your joins and finds. This can result in bloated index files -- sometimes they can be larger than the database (.dbf) file they are supporting!
     - When an .apr file is opened, Approach will make sure the indexes required for the joined fields in that application are current. 
     - Subsequent "finds" add indexes as needed to support them, and they are not deleted even if the find is never to be done again. 

  2) You suspect that an .adx file is corrupted. For example, a find gives you invalid results or a repeating panel that should show records does not show them. Deleting the .adx file will get rid of the fault, but perhaps only temporarily if you have an illegal many-to-many join that is causing the problem.

  3) Deleting records from your database does not delete them from the .dbf and .dbt files. The space is merely marked as "deleted" and excluded from subsequent display. The only way to reclaim the space is to compress the database.

  4) Unnecessarily large database files take more time to transfer over a network, and more time to execute finds and sorts. 

  5) If another application (say Paradox, or Access, or something) adds or updates records in your database, the Approach index will not be correct. 

NOTE: It is always a very good idea to do a full back up before doing any maintenance on your databases or indexes!

How do I compress a database?

Compressing your databases is preferable to deleting the index files because it preserves your long field names and serial numbered field settings. To compress a database, you use the File menu, User Setup, Approach Preferences sequence to display the Approach Preferences dialog. On the Database tab, select a database name, click the Compress button, and repeat for each database you want to compress. Finish with the OK button, which actually starts the compression process. (Also see articles  'Reducing ("Compressing") the size of a database file' and 'Compressing databases with a macro or script' in this FAQ)

How do I delete an index (.adx file)?

Before you delete the index files, there are some possible complications from doing this that you should be aware of:
 1) If your Approach database field names do not adhere to the dBaseIV standards, you will find your field names changed to fit that standard. 
   - The default dBase IV format is a fieldname written in all capital letters, with a maximum fieldname length of 10 characters as a combination of 10 letters, digits, and underscores. 
   - The first character must be a letter. 
   - Punctuation marks, blank spaces, and other special characters are not permitted. 
   - Field names that are similar, such as BILLAMOUNTDUE and BILLAMOUNTPAID will become BILLAMOUN1 and BILLAMOUN2, which can make mapping these names to the originals in your .apr file difficult.

 2) The .adx contains what the next number will be when automatically incrementing a serial numbered field. So, if you delete an .adx file for a .dbf that has a numeric field containing an auto - incrementing serial number, then the serial number will be reset to 1. You will therefore need to manually reassign the correct next record number in the field definition options.

To delete an .adx file, you should know how to navigate your hard drive or network drive using File Manager or the Windows Explorer. Go to the folder or directory where your database files are stored and you will see sets of files for each database. The set of files will all have the same name with a different type. For dBase files there is always a type .dbf where your records are kept, optionally a type .dbt where memo and PicturePlus fields are kept, and the .adx file. You can simply select and delete the .adx file for the database you are performing maintenance on. Approach will automatically rebuild the indexes when you reopen the application that uses it. 

Conclusion: If you only use dBase IV format fieldnames and if you do not use any auto-incrementing serial numbers and if you do not manipulate the .dbf file with another application, then you should not suffer any side effects when deleting a .adx.

General Protection Faults (GPF's)
If you are running v3.00 then upgrade to v3.02. It's free, and it will save somebody posting to the list telling you to upgrade because v3.00 is full of unwanted little surprises! See 'Free Approach 3.02 Upgrade'

If it might be something to do with printing then see 'Printer problems (including GPFs, HP Printers)'

If it might be something to do with the display then see 'Display problems (including problems switching to a particular form)'

Search the IBM's Lotus Approach support web site (http://www-3.ibm.com/software/lotus/support/approach/support.html) for the program involved in the error (eg krnl386.exe, or krnl386.exe module 0001:0ec9) and see what comes up. NOTE: search the Knowledge Base, which is a different thing than just doing a site search - they are different things.

Despite what Lotus says, creating reports with more than 8 repeating panels will cause GPFs (still occurs in July 1997 update of Approach 97). So if you have more than 8 that may be your problem.

A lot of GPFs are cleared up by repairing or deleting .adx files BUT make sure you see 'Repairing corrupt index (.adx) files' and 'Deleting and recreating index files (.adx)' first, otherwise you may find you create a few other problems as well...

Some GPFs may be associated with corrupt .apr/.vew files (eg if it only happens when changing to that particular view). See 'Repairing corrupt .apr/.vew files'.

If you get the GPF in Design mode, then try switching 'Show Data' off, especially if you have a lot of calculated fields.

If this doesn't work you could try reinstalling Approach, especially if the fault has appeared just after installing some other new software which may have overwritten some .DLL's in the windows/system directory. If at first this doesn't work, try unistalling Approach first, and then reinstalling it from scratch. Otherwise try buying a new computer and then installing Approach ... ;-) just kidding!

It might also be worth testing Approach while there is absolutely nothing else running, and if it works introduce other applications and utilities that you normally use to see if the conflict is particular to one of them.

GPFs can sometimes be caused by having duplicate copies a DLL's in the Approach, WINDOWS or WINDOWS/SYSTEM directories. Fontmod.dll is a known culprit. If you have a duplicate .dll then Approach's version should be copied into the Windows directory. Other copies should be removed. However, be careful, different version of Approach or other applications may require different versions of that .dll. It is probably best to keep old copies in a temporary directory that is not in the search path (see the autoexec.bat file) until you are sure you don't need then.

Repairing corrupt .apr/.vew files (e.g. errors when changing views)
If your application suddenly becomes unstable while you are working in Design Mode on it, you may see crashes simply changing views, strange views with objects from several views mixed together on them, error messages, etc. At that point, you should NOT save the Approach file yet, if you are even able to do so. Consider the following advice from experience with many unfortunate situations I have encountered over the years. :>(

If you have not made many changes since you last saved the Approach file, just close the APR file without saving it and open the prior copy to see if it is OK. Then recreate the changes. But if you have not saved your work and it would take considerable effort to recreate it, or the prior saved copy is similarly damaged,  you may want to try to salvage the APR file. Here is how to proceed:

* First, make sure you have a backup copy of the .APR file from the last time you saved it. You can use the Windows Explorer to copy that file to another folder for safekeeping and then try saving your current .APR file. Then reboot the computer. This may be all you need to do to relieve any shortages of memory or other system resources. 

* Open the APR file and see if it works correctly now. If not, try using the File menu, Save As option to another directory to make a new (hopefully error free) version of the .apr and database files. 

* If you still have problems, and you can get into design mode, try deleting bits of the last changes you made to see if the problem disappears. It would probably be best to start with things such as text blocks. Sometimes deleting entire views, like form letters in particular, will help. If you cannot get to a view without a crash, try turning off "Show Data" under the "View" menu while in design mode before switching to the view. If you reach a stable point, save the APR file and redo your changes from that point. Review your named Find/Sorts for missing conditions which can happen if you use "Query by box". Delete these and create new ones, and fix the macros that are impacted.

* If you still haven't managed to fix it, try creating a new .apr with all the exact same databases and joins, then import the old APR into it. Note that scripts are not imported so you would need to copy those over manually. 

If all else fails, you probably have to simply recreate everything from scratch (using your data (eg .dbf) files of course). But you should never get to this point if you follow good development practices. In particular, you should never save an Approach file over itself without having a backup of it prior to that save. And it is a good practice to use SaveAs often giving the APR file a different name such as myapr_dev01, myapr_dev02, etc. 

Finally, consider the possibility that the problem or error you are seeing is caused by a corrupt database index. This can cause missing records in repeating panels, Finds that do not find the right records, crashes switching to a view of that database, etc. Try your current APR file with a backup copy of your databases to see if the problem goes away! If so, there are other FAQs on this site that can help you with database and index corruption if you suspect that is the problem.

Out of Memory error message
First, careful read through the article 'Optimizing the speed of a database' for tips on how to get you database running as sleek as possible.

Also, remove anything that might be unnecessarily using memory, such as unused forms, worksheets, reports, macros that are no longer needed.

Check the manuals for your operating system to make sure your system configuration is making optimum of your memory. For instance, if you are using MS-Windows 3.1 and DOS, check that your EMM386 (probably should be set to NOEMS), and Upper Memory Blocks (UMB) are properly configured and utilized.

"Record is locked- retry -cancel" error message
Sometimes a shared Approach database repeatedly returns a "record is locked- retry -cancel" error message, even though the database has been set to optimistic record locking.

This can occur when the link to the network drive where the data and view files are held is down. In this situation you need to exit Approach, reestablish the link to the network drive (usually by rebooting your workstation), and reopening the database in Approach.

It will also occur if a user does a search that causes a new index to be created. Everyone else will receive a "locked" message until the indexing is completed. And, of course, if you have a lot of users and they often attempt to work on the same records then they are going to frequently get record locking errors.

It can also occur for a variety of other more obscure reasons. Some things that have been suggested to keep these at bay are:

   * delete excessive apr*.tmp files created by Approach. If this is a regular problem you may want to include the following statement in your autoexec.bat file: "del c:\windows\temp\apr*.tmp"

   * keep the users system clocks within a minute or so of each other. Locking can only work properly if all PCs are synchronized. For example:
        10:00:00 User A locks a record
        10:00:01 User B locks another record
        9:59:59 User C (whose time is not synchronized) tries to access User A's record, which is not yet locked because it isn't 10:00:00 yet. Ooops.

Get the network administrator to set up the network to synchronize the PCs clocks.

   * try compressing the database, and if all else has failed try deleting and recreating the .adx files. See articles 'Deleting and recreating index files (.adx)' and 'Repairing corrupt index (.adx) files'.

   * put a blank record as the first record in the database. This record must be devoid of any join relations. ( don't know what this does, but apparently it can help!)

"Someone locked this record" errors
This error generally means that either someone is updating a record, or if a smartindex is being created.

If your a single user and this error suddenly starts appearing, there may be something wrong with the database. Try compressing it, if that does work, try deleting the index files (.adx), but make sure you read articles 'Repairing corrupt index (.adx) files' and 'Deleting and recreating index files (.adx)' first.

Otherwise, it could be because the clocks in your server and workstation are not synchronized. This can cause Approach to rebuild the smartindexes more often than really necessary, making a clash more likely. Check this before doing anything else.

In the end, the faster that a process can run the less likely a clash will occur. So you might want to consider speeding up your network.

Processes such as finds, sorts, and reports can be initially sped up by running a comprehensive find on the database tables before users get access to them. This forces Approach to index the database files which speeds up processing. Do this immediately after creating or compressing database files.

Other things you can consider are:

   * making the databases read-only to the users.
   * create and use a separate copy of the database to run reports

"Unable to open ___.dbf file, 5009" errors
Approach displays this error message upon opening an .APR file if:
* The user has mapped the application's database folder to a different drive letter on their network server than the one mapped by the application developer. 
* The drive letter used by the developer is not mapped to anything on the user's computer.

Example 1: A developer puts databases on H:\mydata and shares that folder on the network. User A opens the Windows Explorer and maps the shared "mydata" folder as "R". The user has no F drive assigned. On opening the .APR file, the -5009 error message occurs. 

Example 2: A developer is working on the server and puts databases into C:\mydata and shares that folder on the network. User A opens the Windows Explorer and maps the shared "mydata" folder as "G". On opening the .APR file, Approach looks for the folder on C and gives the user an opportunity to substitute databases. If the user is allowed to do this substitution  and save the .APR file, the developer on the server will get a -5009 error the next time the .APR file is opened if no G drive exists on the server.

It is important to an Approach developer whose application will be shared over a network to plan for this situation and insist that the users of the application all map the shared drive to the same letter. Since many computers today have CD drives and perhaps other drives, it is recommended that the shared drive letter be above "F" to avoid conflicts. 

If you can open the .APR file, go to the FILE / APPROACH FILE PROPERTIES menu and you will see a list of all the files associated with the application and the complete path to them. For dBase databases, only each .dbf file is listed, but the .adx and .dbt files must be in the same folder as their .dbf file. 

"Macro error: Import table has changed" error
You have a macro with an Import command that has the incoming file and field mapping predefined in the macro. Suddenly you get a message "Macro error: Import table has changed". In general, the error is telling you that there is no longer a match between the file to import and the target file being imported into. What are the possible reasons for this problem? 

1) You changed the definition of a real field or added/deleted a field in the database you are importing into. (This does not include variable or calculated type fields, which are not in any database)

2) You changed the content/format of the file you are importing. 

3) You have a corrupt database index (.adx file) on the database you are importing into. (People have reported getting this error and discovering that all their field names have reverted to upper case and have been truncated.)

If your situation is caused by a change in the database or incoming file format/definition, you can correct the macro by editing the settings in the Import command. 

If your situation is caused by a corrupt index file, there are two other articles in the FAQ that can help you:

 * 'Deleting and recreating index files (.adx)' which outlines this procedure and implications in more detail, and gives more details about .adx index files. 
 * 'Repairing corrupt index (.adx) files' which outlines a widely affirmed method of rebuilding a .adx without loosing any information. However, there is no guarantee that this procedure will work in every situation. 

Internal Error: Cannot load string 0
This error is generated when more than 16 users try to access the same .apr at the same time in v3.0* (v95...?). In order to get around this, duplicate the .apr, and divided your users across the new .apr/s. All the .apr/s can use the same database files.

"Could not load infobox dynalink" error
This may be caused by not having done a network install of Approach on the desktop you are using to link it to the Approach executable on the network. To fix this, reinstall  Approach on the desktop, making sure that you do a network install. (also see 'Running Approach on a network' in this FAQ)

InfoBox, Tools Palette, etc... won't appear
This may be because the InfoBox has been moved off the edge of the screen. Carefully have a lot around to see if you can find it. If you can't find it then you may need to manually edit the location of the InfoBox. In v3.0* the infobox location is stored in the APPROACH.INI file in your Windows directory. Set the location to 10,10 then restart Windows. In v96 and later the location is stored in the Windows Registry. This must be edited very carefully.

If this does work the you probably need to reinstall Approach.

"Page Fault... at 0137:0049eb64" in Approach 96
To avoid getting "APPROACH caused an invalid page fault in module APPROACH.EXE at 0137:0049eb64" errors when entering new records in Approach 96, turn off the "In Field" option.

Approach 3.02 crashing in design mode and when switching views
A different times people have reported that this is due to the fontmod.dll file in the Windows System directory being replaced by a new installation of some other program (e.g. Quickbooks). To fix this just replace the new copy of the fontmod.dll file in the Windows System directory with Approach's version found in the Approach directory. 

"An error has occured in your program" errors
If you get the following error:

"An error has occurred in your program. To keep working anyway, click
Ignore and save your work in a new file. To quit this program, click Close. You will lose information you entered since your last Save."

  ... just follow the instructions. Apparently if you hit Ignore enough times ("A couple of hundred" according to one person?!) Approach generally gets the message and continues on. Just save your work, exit approach, do a cold boot (i.e. turn your computer off and turn it back on), then recommence your work. Often that is all that is required.

Database being used exclusively error
One possibility is to go to the FILE/OPEN dialogue box, select the dBase (.dbf) file format, then click on the SETUP button. You can then set the options regarding database sharing on the network.

Make sure you have 'share' or 'vshare' installed. Your autoexec.bat should have a line that looks like:

     C:\DOS\SHARE /F:8192

OR, the [386Enh] section of your system.ini should contain:
     
     device=vshare.386
     
     or
     
     device=*vshare

If you have a file named APR.V30 in your Approach directory, rename it to something else. It tells Approach that it is a stand-alone version and that it should open all databases exclusively regardless of other settings.

If you have a "sdBaseFileSharingMethod=Approach." in your approach.ini file, try replacing it with with "idBaseOptApproach=1". You do have to have a line in your approach.ini that reads "sdBaseFileSharingMethod=DBASE4" or "sdBaseFileSharingMethod=DBASE3" or some other file format. Make sure that all users have this parameter set the same.

Of course if all else fails, you can always revert to deleting every mention of Approach on your computer/s and re-install from scratch...

"Internal Error - Cannot load string - 1035." error message
Make sure you are running 3.02 and not 3.0 as this error is related to one of the main bugs in 3.0 to do with the Spell Checker

If you are using 3.02 you possibly have a corrupt .dbt file which contains your mem/picture plus fields. If you can, delete the offending .dbt file and restore from back up. Otherwise, their are other solutions available by searching the lotus support knowledgebase at http://www-3.ibm.com/software/lotus/support/approach/support.html

"GPF in APPREXPR.DLL at 0009:0D2C." error message
This is an Approach v3.0* error message indicating a corrupt index (.adx file). Try compressing the database first, otherwise you will need to delete the .adx files. You should probably also check to see if you have any .oyz files and get rid of those as well.

NOTE: read articles 'Deleting and recreating index files (.adx)' and 'Repairing corrupt index (.adx) files' before deleting the .adx or .oyz files!

For compressing, see article 'Reducing ("Compressing") the size of a database file'

Vanishing scripts

This article was submitted by Paul Bent (Northwind IT Systems):

The script editor may become unstable while you're developing and testing
code, particularly if its been open when you've been testing forms as dialog and/or stepping through with break points. I've seen very strange things happen including disappearing scripts.

This is my bomb proof method for not losing work!! I do these three things every time I make code changes and BEFORE I test them.

1. Press F2 to test compile global code.

2. Press Shift + F2 to test compile object event code. In A9.5, if Shift + F2 does not display a no errors message then close Approach immediately, don't try and save changes. After Approach has closed an Approach message box often appears!! - "Error 160, Unknown class of product object" - OK it and reboot! You see this one quite often if you run macros from scripts and vica versa.

3. Save As to an incremental file name (never save the apr over itself),
close and reopen the apr.

I run a full unattended backup of my server each night. In the morning I
delete all the incremental versions except the last.

"Could not find database [path].mdb" error
An error message "Could not find database [path]\name.mdb" is sometimes encountered when opening 32 bit ODBC data sources.

This can happen with the second and subsequent mdb's you try to open. The
solution is to select file type ODBC Data Sources each time, followed by the MS Access Driver folder. This will initiate a new "select mdb" procedure and bypass the bug that causes the "could not find..." error.

"couldn't open database because it is open in another language..."
This is a network problem that occurs if different computers accessing your database files on the network have different languages set in the "Regional Settings" section of the Control Panel (accessed from Settings on the Start Menu). If the language is set correctly on all the computers, try changing it so something else, exiting Regional Settings, then, going back in and resetting it back to what you want. Also check the currency and date settings are all the same while you are
there, because these can also cause problems.

Globals Sub "Initialize" doesn't always get executed
The has been some disagreement on the list about this. Some people swear it works perfectly all the time, others seemingly have frequent problems. Whatever, here is a summary of what has been said:

Paul Bent writes:
Initialize should execute when the apr is opened. Say you want to call a
logon procedure, check the registry, call API functions to get system
parameters (eg short date format) initialize variables etc etc. Not much
use if this only happens when the user first clicks a button (say); they
could have been working in the apr for any length of time already.

It isn't a good idea to put Initialize code in DocumentOpened or
OpenWindow events or it will execute every time these events fire
instead of just once when the apr opens. To avoid this you need to hard
code the apr name in the event script which will cause it to fail if the
apr is renamed.

However, putting a single comment character in OpenWindow works around
the Initialize problem thank goodness!

Conversely, David Legge writes:
The initialize sub will only run when (just before) the first bit of code
gets invoked. I suppose there is no need to run it if no other code ever gets run!

If you want a code to run when a doc is opened, (the script equivalent of the Open macro) then place it in the openwindow event of the Documentwindow object in your file. (BTW If you have anything in Sub initialize it will also then run - because the Openwindow event is triggered)

The Sub Initialize is also run if you have edited code in the IDE (again only when the next piece of your code is invoked) So placing "documentopen" initialisation in Sub Initialize can confuse you when editing your code.

You will find Documentwindow is the last item in your list of views in the Object: list in the IDE. You have to expand yourfile object above the Approach objects to see it.

Unlike Paul, I think Initialize is now working correctly!

Printing formatting problems
Also see 'Printer Problems', 'MS-Windows 95 printing blank pages (v2 & v3)', and 'Header / Footer problems in v2.1' in this FAQ

If you are missing edges of pages, first check the printer setup in the printers section of the MS-Windows Control Panel.

Also, in design check that all of your page boundaries are set correctly.

Approach will not use the resident fonts in the printer.

Printer problems (including GPFs, HP Printers)
A simple thing to try is to select the default printer in the printer setup (evening if you are currently using it), and then back to whatever set up you want. This ensures that the current set up is correct.

Then, download the latest drivers for your printer. Search your disk drives and remove all old versions of the printer driver before installing the new one. If you are using Approach on a network make sure all the drivers have been removed from the workstations and install the new driver on the server for network printers.

If this doesn't fix the problems, search the Lotus technotes for tips and solutions concerning your printer.

If that doesn't help, contact the technical support people for your printer to see what they have to offer. Searching for their web site is a good place to start as they probably have FAQs and the latest drivers there.

Note: You may get differently results from different versions of Approach at the same time, so for consistency its best to stick to using one version.

And just in case your wondering, for everybody that says that Approach doesn't work well with HP printers there is somebody else that says they use a variety of HP printers and never have a problem... Go figure! However, if you are having problems here are some work arounds that have been suggested:

   * if you are using MS-Windows 3.1 try updating to MS-Windows 3.11 (or later), and make sure you are using the latest drivers. This seems to solve some problems. Similarly with MS-DOS, if you are not using MS-DOS 6.22 then perhaps you should try it (with the latest drivers of course).

   * if you design a report for a 300dpi printer and then later switch to a 600 dpi you will probably get clipped text. One suggestion is to use a macro to set the printer to 300dpi and then print the report. Otherwise you could simply recreate the report for the 600dpi printer.

   * you could also try using current drivers for older models. Some suggestions are: if you are using a HP4, try using a HP3 driver; if you are using a HP Laserjet 2100M try using a HP Series II driver from the Windows CD. You never know, it just might work!?

Header / Footer problems in v2.1
People have experience problems where the top or bottom half of the footer or header line is cut off in v2.1. The easiest way to get rid of this type of problem is to recreate the report from scratch - this is likely to be quicker and easier than spending to much time playing with your current one. 1) set the printer setup to the correct values  2) create a very simple report using all the default values. Just specify  a couple of fields from the database the report is based on.  3) immediately insert and set up the footer and header as required. Do a couple of test prints to make sure it is right.  4) finally, set up the report contents the way you want it - insert further fields, do formatting etc...

MS-Windows 95 printing blank pages (v2 & v3)
To stop Approach from printing out a blank page after every printed page in Windows 95, make the following change to your WIN.INI file: Under the [Compatibility] heading change

'Approach=0x0004'

   ... by putting a ';' in front of it so that it reads

';Approach=0x0004'.

Then save and reboot. 

A side effect of this is that it effects the printing of field definitions and macros. Lotus recommends using either a Win 95 PostScript driver, or an older Win 3.x driver. Look for Document#132676 on the Lotus web site. http://www.lotus.com

NOTE: The problem will recur if you reinstall Approach or over write the WIN.INI file with an older version. If this happens then repeat the process

Printing field definitions
Just choose the print option on the Field Definitions screen. However, this won't work if you have the "Approach=0x0004" line commented out in your win.ini file (to prevent v3.02 on Windows 95 printing an extra blank page). To get it to work you will need to remove the semicolon and then reboot for the change to take effect.

Printer fonts problems
Approach will not use the resident fonts in your printer.

Having more than 200 fonts installed on your computer can cause invalid font substitutions in Approach.

Pages sizes in v9.6
v9.6 does not pick up the paper size from the printer. All views change to Letter size unless another size is selected in Page Setup and the file is saved in v9.6. Furthermore, new and duplicated views are default to Letter size.

Calculating elapsed time

Time is stored as a number starting at midnight, incrementing in 100'ths of a second. So as long as your time does not start on one day and end on
another, you can simply subtract the timeout from the timein and divide by 360000 to get minutes. (100 * 60 * 60) = 360000

The following formulas have been suggested to calculated elapsed time over the 00:00 boundary:

Either:

((Trunc(FinishDate) + (Trunc(FinishTime) / 8640000)) - (Trunc(StartDate) + (Trunc(StartTime) / 8640000))) * 24

Or:

((24 * (Date_expire) + time_exp))-((24 * (Date_ent) + time_ent))

Calculating ages from birth dates
The following formula produces a line of text e.g. "Age: 32 yrs 6 mths". If the PEOPLE.BIRTHDATE is empty them it produces an empty text string i.e..""

If(Isblank(PEOPLE.BIRTHDATE), '', If((Month(Today()) - Month(PEOPLE.BIRTHDATE)) < 0, Combine('Age: ', (Year(Today()) - Year(PEOPLE.BIRTHDATE) - 1), ' yrs ', (12 + Month(Today()) - Month(PEOPLE.BIRTHDATE)), ' mths'), Combine('Age: ', (Year(Today()) - Year(PEOPLE.BIRTHDATE)), ' yrs ', (Month(Today()) - Month(PEOPLE.BIRTHDATE)), ' mths')))

The next formula just returns the ages in years and increments on the exact birthday: 

If(Month(Today()) < Month(PEOPLE.BIRTHDATE)  or Month(Today()) = Month(PEOPLE.BIRTHDATE) and Day(Today()) < Day(PEOPLE.BIRTHDATE),  Year(Today()) - Year(PEOPLE.BIRTHDATE) - 1, Year(Today()) - Year(DOB))

The last forumla gives you the persons exact age in days. You can then roughly calculate how many years and months, but since months and years are different lengths the answer is an approximation, give or take a few days:

If(isblank(PEOPLE.BIRTHDATE),'',Trunc(((today()- PEOPLE.BIRTHDATE/365.25),0))

Calculating number of weekdays
The following formula will return the number of weekdays between the dates contained in DB.STARTDATE and DB.ENDDATE:

If(Year(DB.STARTDATE) <> Year(DB.ENDDATE),DB.ENDDATE - DB.STARTDATE - (WeekOfYear(DB.ENDDATE) + (Year(DB.ENDDATE) - Year(DB.STARTDATE) - Year(DB.STARTDATE)) * 52 - WeekofYear(DB.STARTDATE)) * 2, DB.ENDDATE-DB.STARTDATE - (WeekofYear(DB.ENDDATE) - WeekofYear(DB.STARTDATE)) *2)

Another method use two formula:

TotalDaysDiff:
EndDate-StartDate

NumWorkDays:
TotalDaysDiff - (((TotalDaysDiff + DayOfWeek(StartDate) - DayOfWeek(EndDate) + 7) * 2 / 7) - If(DayOfWeek(StartDate) = 1, 0, 1) - If(DayOfWeek(EndDate) = 1, 2, 1))

You may get funny results from these formulae if either the start or end date is a Saturday. You can get around this problem by not working on weekends!

You can have the formula automatically subtract any public holidays, if any, by adding the following formula segment for each possible public holiday:

-If(START <= '25/12/96' and FINISH >= '25/12/96', 1, 0)

where 25/12/96 is a public holiday. Make sure you use the same date format as your database.

Finding or calculating dates

First, make sure your date is in a date field and not a text field. Trying to work with date in text fields is inviting all sorts of problems - that why they invented date fields! You can extract a date from a text field (eg 09/11/2001 or 09-11-2001) using the TextToDate() function. If this doesn't work then try creating your own custom function such as:

   Date(Left(datefield,2),Middle(datefield,4,2),Right(datefield,4))

(However, TextToDate() will do that on correctly, but you get the idea...)

OK, once you have your dates in a date field, use the following to find what you are looking for:

Put the range (eg 9/1/1998...9/30/1998) into the desired field in FIND.

Try using or adapting the following formulas:

   For NOT December: IF(month(date_field)<>12)
   For this month: IF(month(today())=month(date_field))
   For December 1996: IF(month(date_field)<>12 AND year(date_field)=1996)
   For the last 90 days: <=@TODAY()&>=@TODAY()-90
   Between two dates: If(DB.Date >= SDate and DB.Date <= EDate)
                  or: <@end_date & >@begin_date
   3 business days from now: If(DayOfWeek(Date + 3) = 7 or DayOfWeek(Date
                             + 3) = 7, Date + 5, Date + 3)
   One year from a date: If(Month("Date Joined") = 2 and Day("Date Joined") = 29, Combine(Month("Date Joined"), '/', Day("Date Joined") - 1, '/', Year("Date Joined") +1),Combine(Month("Date Joined"),'/',Day("Date
Joined"),'/',Year("Date Joined") +1)).

   NOTE: This last calculation assumes that if the Month is 02 and day is 29, then the date a year will be 28.

In v96 (and following), To find "This Month" use the Find Assistant and opt for "This Month".

21st Century Dates
If you enter the full year (e.g. 10/31/2001) you should not encounter problems. However, if you want to enter dates like 10/31/01, and you want to add only future dates you can use the following solution.

Create 3 variable fields, all 2 digit numbers: VYear, VMonth, VDay. Display these three fields on your form. Enter the dates using these three fields. Create a calculated field called VEYear containing the formula If(VYear => 95, (If(VYear <= 99, VYear, VYear + 2000)), VYear + 2000.

Then create another calculated field called, say, DISPLAYDATE containing the formula: DATE(VMonth,VDay,VEYear). DISPLAYDATE will contain the full 21st century date.

Two digit year assumptions (eg 01/01/29)
To avoid problems caused by 2 digit year formats you should always use full 4 digit year format. But if you do use the two digit format, the Lotus Y2K FAQ (http://www.lotus.com/home.nsf/welcome/y2k) has this to say about the assumptions that Approach uses when dealing with two digit years:

"The first two releases of Lotus Approach, 1.x and 2.x, assumed all two digit years fell into the 20th century. With the 3.x release Approach implemented a fixed window approach to two digit dates. The window used 30 as the split. So if the user input a number between 0 and 29 that date would be input as 20xx. If the number is between 30 and 99, the year 19xx would be input. In Approach Millennium, Lotus standardized all the SmartSuite applications on a sliding window. This window defaults to and 80/20 split. Thus, two digit years up to 80 prior than the current year will be assumed to be in the current or previous century. So, if the current year is 98 and the user inputs a date with a year of 25, 1925 will actually be entered. But if the user input 05, 2005 would be entered. Likewise, if the current year were 2010 and the user entered 05, they would still get 2005. But the number 25 now produces the year 2025."

Date format with 4 digit year (e.g. 1997)
This is set in MS-Windows, not in Approach. In MS-Windows 3.1* see International / Regional settings in the Control Panel. In MS-Windows 95
it is in Settings / Control Panel / Regional Settings.

Importing Dates
It was once said that Approach only recognizes date field in mm/dd/yy format, but apparently it handles dd-mm-yy date format OK when importing a .csv file. If possible you should probably export the data from the old application using mm/dd/yy format as it seems to be the most reliable. Try using a dBase export filter if the program has one.

If you are not able to do this and are therefore importing dates that are currently stored in a different format then you will need to import them into a text field, and write some macros to convert the data in to mm/dd/yy format.

E.g. to convert yyyy-mm-dd to mm/dd/yy a macro would use the formula:

TextToDate(Combine(Middle(initial_date,6,2),'/',Middle(initial_date,9,2),'/',Middle(initial_date,3,2)))

EXCEL uses a slightly different method of calculating dates than 'industry standard' adopted from 1-2-3. This means that imported EXCEL dates are 366 days out. The easiest way around this is to export the data from EXCEL into dBase file and import it into Approach from there.

Dates in report titles
To include the date range of a found set in the title of the report, use the SMin and SMax functions to create calculated fields that hold the start and end dates for your found set.  Then insert these calculated fields into the title of the report.

Find the previous weeks records
For the previous week (Sun - Sat): ('date'= your date field)

     if(weekofyear(date)=weekofyear(today())-1 and
          year(date)=year(today()))

If the data is only for one year, then you can leave out the 'and' and all that follows.
   
For the last 7 days: (including today) 

     >=@TODAY()&<=@TODAY()+7
  
Or, if you need to find records produced during the last 7 days:

... starting with and including today's date, use the formula: 

     >=@TODAY()&<=@TODAY()+7

Y2K / Millennium bug compliance and leap years
While Lotus states that the latest versions of Approach is Y2K ("Year 2000") ready (see http://www.lotus.com/home.nsf/welcome/y2k), this is no guarantee that a particular database application is. For instance, if you use calculations on dates, remember that the leap year formula is:

(Y MOD 4 == 0) AND ((Y MOD 400 == 0) OR (Y MOD 100 <> 0))

Also see articles '21st Century Dates' and 'Two digit year assumptions (eg 01/01/29)'.

Time and Date Schedule (uses SQL and multiple records)  [UPDATED] 12 Jun 2026
If you click on the following link you will download a self-extracting archive that contains the script as well as a brief description and .jpg images of the associated worksheet and form. 

     https://approach.droppages.site/DATESC~1.exe [165KB] 

The script is a useful example of how to use SQL queries in a script, as well as how to access multiple records within a single script. 

Field Formatting (including not / rounding numbers)

In the info box, you can select or specify how you want data displayed, by selecting an option in the '#' tab. 

Note: There is a known problem with form letter views, that the format of the included field sometimes mysteriously changes from "display as entered" to "numeric" which alters the way the data is displayed. It can happen to text fields as well as other types! The only solution is to fix the format and save the APR file, hoping it does not change again before you finish fixing it! This occurs in v97 to v9.5 (I think)

To display a combination of formatted a numbers and text when a number has been entered into a field, first make sure the field has been defined with the number of significant digits and decimal places that you require, e.g. if you need to display currency up to $1000 make sure you have defined the field as a NUMBER, with a length of 4.2.

When displaying the field, either select or enter the format you require in the Styles / Field Format menu item. E.g. Using a field format of:

     #,##0.00 " Miles"

The following will be displayed:

     entered     displayed

      12          12.00  Miles
      .3          0.30  Miles

     #,000.## " Miles"

The following will be displayed:

     entered     displayed

      12          012  Miles
      .3          000.3  Miles

Check the manual and / or the help files for all the rules and options governing the format codes. This does not change the stored data. It only changes the way it appears in the field box you are working on.

[For some strange reason, when using above example in v2.1, and v3.0* (v96?), the data is only displayed correctly if there is a space after the first quotation mark. If this space is left out so it reads: #,##0.00 "Miles" it will incorrectly display 12.1 as 1.21 Miles. Strange, but true!]

Any information that includes anything other than numbers, or even a number that needs to be stored with leading zeros, must use a text field. E.g. zip / postal codes in many countries, or USA Social Security Numbers which have leading zeros. However, you can still use numeric format to control the display of a text field.

Displaying Negative Values In Red (Changing fonts colours using LotusScript)
In v2.1 and v3.0*: you need to create 2 calculated fields: one that will only display the number if it is negative: If(DB.NUMBER < 0, DB.NUMBER, ''), and the other that will only display the number when it is positive: If(DB.NUMBER >= 0, DB.NUMBER, '')

Display these fields on your report or form. Set the Fill Color to transparent on both of them, and the Pen Color to red on the field that will display the negative values. Then use the OBJECT / ALIGNMENT menu item to place one of the fields directly on top of the another. Set the STYLE / FIELD STYLE / LABEL: to be blank, and create a separate text box to label the field if required.

In v96: you can either use the same method outlined for v2.1 and v3.0* shown above, or the following LotusScript attached to the field wherever it is displayed:

Sub Lostfocus(Source As Fieldbox)
    If Val(CurrentView.Body.YourFieldName.Text) <= 0 Then
        currentview.Body.YourFieldName.font.color.setrgb(color_red)
    Else
        currentview.Body.YourFieldName.font.color.setrgb(color_black)
    End If
End Sub

To change the background colour, use:

   currentview.Body.YourFieldName.background.color.setrgb(color_red)

Leading zeros (eg. 00054628)
To output data with leading zeros create and output a calculated field containing the formula:

Right(Combine('0000000',DB.NUMBER), 7)

where '7' is the maximum number of digets, and hence the length of the output string, and 0000000 is a line of zeros of the same length.

Trailing zeros (eg. 1.400)
First check that you have that the field definition is numeric with the right number of decimal places.

While in design mode, select the field and then enter the Style/Field Format menu (v2.1, other versons may be different) and either select one of the custom numeric field formats provided, or enter your own. For exporting data, create and export a calculated field containing the formula:

NumToText(DB.NUMBER,'####.00')

where ####.00 is the output forumla you require.

Display problems (including problems switching to a particular form)
If it is just when switching to a particular view:

* make sure all of the objects on the page are completely within the page margins. Go to Design on another view, and under View on the top menu bar turn off "Show Data". Then switch to the view that is causing the problems.
 -- For report views, you can get an extra page to the right of your report page. To eliminate this page, under Edit on the top menu bar, select "Select All" to highlight all object boundaries. Scan the right margin of the page for anything that extends onto or beyond that right margin and resize it or move it back within the margin. Do not forget to check the footer panel! If your printer allows it, you may change the margins to a smaller value as an alternative solution. 
 -- On Form views, Approach will not let you leave objects outside of the margins. But if you have objects that extend under the right vertical scroll bar in Browse mode, that can cause a redraw loop with some video drivers. The only solutions are to change the video driver, move the objects to the left, or change the video resolution so that those objects are no longer under the scroll bar.
 -- On Form Letter views, most problems are caused by corrupt text blocks. If you can get to the form letter in design, you can try deleting the text block and adding one back, or delete the entire view and add one back to your application. If you cannot get to that view in design, you can hide it and add a new form letter to your application to replace the bad one.
* for v97 and newer versions of Approach, try going into design, switch to view causing the problem, then remove the check mark next to "Hide Page Margins" in the Form Properties Infobox Basics Tab. 
* you may have to delete that form and recreate it from scratch. First, try creating a new blank form and copying a few objects to it at a time. When recreating the form save the .APR frequently under different files names so that if the problem reoccurs you can tell what caused it, and you can then just go back to the previous version and do something differently. Make sure that you check view switching after copy/paste multiple objects or reordering views. 

Otherwise, 

* it could be a video driver problem. See if you can get another driver from the manufacturer of your video card (you can often get these via the web) or try the generic VGA driver supplied with by MS-Windows. 
* if you have just changed your printer: screen redrawing problems can be caused when the margins for the new printer are wider than those for the printer specified when the view was developed. Try different printer drivers, or recreating the form as described above. 
* check the video resolution is that same as the computer that the .APR was created on. 
* try closing down all other programs before running Approach just in case there is a video conflict with other software. 
* try using video resolution 640 x 480 and see if the problem goes away. This forces Windows to use the basic VGA video driver which is very reliable. If this works and you do not want to run your application this way, you will need either another video card or driver.

TIP: Since most newer inexpensive printers require 1/2 inch margins all around, it is a good idea to develop applications  with those margins to avoid problems in distribution of your application. 

Removing unwanted characters from phone numbers
The following formula removes all '-', '(', '(', and ' ' characters from a phone number. E.g. it turns (123) 4567 8987 into 12345678987 This may be necessary if phone numbers have been imported with these unwanted characters. 

You can them use Field Format to display the phone number however you want.

Translate(Translate(Translate(Translate(contacts.Phone, '-', ''), ')',
''), '(', ''), ' ', '')

Radio Buttons Field Label
Question: When using radio buttons, why can't I maintain the field label, as in getting it to appear above / below / etc. / anywhere in the box?

Page 6-12 of my User's Guide shows an example with a field label Type of Customer. But I have been unable to generate a similar example.

Answer: They got tricky. What they did to accomplish that is to create a Text Box and type the box label Type of Customer. Then they created their Radio Buttons box (outside of the text box, clicked the right mouse button, chose ARRANGE, BRING TO FRONT, and then dragged the radio buttons on top of the text box they way they wanted it.

Play with it a bit and you'll get the results you're looking for.

Make empty fields disappear (... rather than just turn invisible)
It is possible on reports but not on forms.

An alternative is to set up a multipage form and have a separate page for each field. On the first page, put a check box for each field set to automatically come on if there is an entry in the field. Then, when you are browsing through the forms, you look at the page if you need to.

Converting numbers to words 34 to thirty-four?
There isn't such a function built into any version of Approach. For Approach v96 there is a LotusScript called ConvertNumberToText in the Archive of Scripts (see section 3. Notices: How to get the Archive of Scripts) which works for numbers up to 999 999 999.

If you are using v2.1 or 3.0* you need create another database with the numbers and related text versions of the numbers, and join it into your current database to the field that you want to appear as text.

It might look something like this:

     number     text
       1        one
       2        two
      ..        ...
      27        twenty seven

Sliding fields together on mailing labels
If you are having difficulty getting fields to slide together on mailing labels (such as first and seconds names), there are a couple of things you can do to improve the situation.

1) Use calculated type fields Set up a formula that combines the fields in the format you want in a calculated type field. Then put the calculated field on the label view instead of the individual fields. A "full name" could be set up this way, for example:

FULL_NAME = Trim(Combine(SALUTATION,if(SALUTATION = '','',' '), FIRST_NAME,' ',Trim(MIDDLE_INI),if(MIDDLE_INI = '','',' '), LAST_NAME))

The above example formula only inserts spaces where they are required by testing to see if a field is "empty" and putting a "null" into the formula when it is empty. A "null" is two single-quotes with no space between them.

For a complete label address with an optional second address line, the calculated field and formula would look something like this:

CUSTOMER_LABEL = Trim(Combine(FIRST_NAME,' ',SURNAME,Chr(13),If(Isblank(ADDRESS2),Combine(ADDRESS, Chr(13),CITY,' ',STATE,' ',POSTCODE),Combine(ADDRESS,Chr(13),ADDRESS2,Chr(13),CITY,' ',STATE,' ',POSTCODE))))

2) Use a text object 
Delete the database fields from the label and create a text object on the label view. Then insert the desired fields into it. In design mode, click inside the text object to put the cursor where you want a field to appear. Never type in the field name! Always use the "Text" option on the top menu bar, and the sub-option "Insert", then "Field Value" to add a field and avoid problems. You can add spaces between the fields and punctuation if needed, and put them on different lines. If a field is empty, Approach will not leave a blank line in its place. 

Database fields are expressed in the text object as <<database.field>> where "database" is the name of the database and "field" is the name of the field. Calculated fields have only the field name in the brackets. For example:
 
<<FULL_NAME>>
<<database.address1>>
<<database.address2>>
<<database.city>>, <<database.state>> <<database.zip>> 

or just this:

<<CUSTOMER_LABEL>>

Including quotes, symbols and international characters in strings
There is some information the readme.wri file in the Approach directory about problems with international characters.

To include a single quote or any other such difficult symbol in a text string it is sometimes easiest to use the Chr() function. For example to create the text string: 3'6" you would use: "3" & chr(39) & " 6" & chr(34).

However, with single and double quotes you can achieve the same thing by simply repeating the quote: eg: "3'' 6""" (where the outermost set of double quotes are the boundaries of the string) but this can get a little confusing to read!

You can find all the ASCII codes in old DOS manuals or a freeware utility like ASCIICAT at http://ourworld.compuserve.com/homepages/r_harvey

Phone numbers
If all the phone numbers in your database have uniform formatting eg.  (xxx) xxx-xxxx in the USA, then you can store the phone numbers a numeric field and set the field box to display them in the desired format. To do this go into Design mode, click on the numeric phone number field. When the InfoBox appears, click on the '#' tab, and select 'numeric' format type. Then click on 'Edit format' and enter (000) 000-0000 in the format code.

If you are importing phone numbers from another application they may be stored as text fields containing both text and numbers eg. "(205) 765-4321". You can either import them into a text field, or use find and replace to remove all the unwanted characters, and then import them into a numeric field.

If you have phone numbers in an Approach database in a text field, you can convert them to a new numeric field using Left, Right and Mid functions in a SET command in a looping macro: eg: to convert (205) 765-4321 from a text field (say 'TextPhoneNumber') to numeric format, set the numeric field to:

    Middle(TextPhoneNumber, 2, 3) * 10000000
       + Middle(TextPhoneNumber, 7, 3) * 10000
       + Middle(TextPhoneNumber, 11, 4)

(also see article 'Looping Macros' in this FAQ)

How to make a field invisible on a form
Set the field to invisible in a script using the following command:

currentview.Body.fieldname.Visible=False

You could use this in conjunction with an 'if' statement if you need it to be conditional.

Conditionally display a pushbutton

'The purpose of this script is to allow a pushbutton to be visible when data
'changes in certain fields. In my form the pushbutton is invisible when the
'form is switched to. When data in fields with script attached changes, the
'push button unhides. The pushbutton is clicked and both script and macro run.
'The script attached to the pushbutton, hides the push button again. The macro
'does its actions at the same time.  The idea for this came from the manual
'Approach96, Using LotusScript in Approach page 5-7.
'The purpose of this script is to allow a pushbutton to be visible when data
'changes in certain fields. In my form the pushbutton is invisible when the
'form is switched to. When data in fields with script attached changes, the
'push button unhides. The pushbutton is clicked and both script and macro run.
'The script attached to the pushbutton, hides the push button again. The macro
'does its actions at the same time.  The idea for this came from the manual
'Approach96, Using LotusScript in Approach page 5-7.
'
'Open the script editor and choose Menu...New Sub (F3). Type, MakeInvisible,
'click, Make global sub and OK. In the Script Editor, finish this entry.

Sub MakeInvisible (dis As Display)
        dis.Visible = False
End Sub

'Repeat the process for the next global sub, MakeVisible

Sub MakeVisible(dis As Display)
        dis.Visible=True
End Sub

'These two are your global subs you will call from another script.
'
'Next, on any form you have that has a pushbutton, go to design, properties,
'click on the button and note its object name. Return to the script editor and
'using the object dropdown box, toggle to the correct form, then object. Use
'the Script dropdown box and choose click. The complete the following

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim dis As Display
        Set dis=Source.ObjButton
        MakeInvisible  dis
End Sub

'All thats left is choosing which field(s) you want to monitor. Again in the
'object dropdown, find the object(typically your fieldname), choose change and
'complete the following. Do this for as many fields as you want to cover.

Sub Change(Source As Fieldbox)
        Dim dis As Display
        Set dis=Source.ObjButton
        MakeVisible dis
End Sub

'The lines with Source.ObjButton should read Source.YourButtonName where
'YourButtonName is the objectname you noted earlier for your pushbutton.
'
'What happens? You are passing the argument dis to the sub MakeVisible or
'MakeInvisible and the sub processes the request. Try substituting a fieldbox
'object or any other display class object instaed of the pushbutton and let me
'know how it works.
'
'Written by Jerry Sikes <Unisource Converting>, 1996
'(C) Copyright 1996 by Jerry Sikes
'
'Permission is granted to freely copy this script in electronic form,
'or to print for personal use. It may be use in any Approach database,
'but may not be distributed for profit either by itself or as part of
'a collection or database.
'
'Disclaimer: This script is provided as is without any express or
'implied warranties. The author assumes no responsibility for errors or
'omissions, or for damages resulting from the use of the information contained
'herein, or your own version containing your desires for the usage of the
'script.'
'Open the script editor and choose Menu...New Sub (F3). Type, MakeInvisible,
'click, Make global sub and OK. In the Script Editor, finish this entry.

Sub MakeInvisible (dis As Display)
        dis.Visible = False
End Sub

'Repeat the process for the next global sub, MakeVisible

Sub MakeVisible(dis As Display)
        dis.Visible=True
End Sub

'These two are your global subs you will call from another script.
'
'Next, on any form you have that has a pushbutton, go to design, properties,
'click on the button and note its object name. Return to the script editor and
'using the object dropdown box, toggle to the correct form, then object. Use
'the Script dropdown box and choose click. The complete the following

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim dis As Display
        Set dis=Source.ObjButton
        MakeInvisible  dis
End Sub

'All thats left is choosing which field(s) you want to monitor. Again in the
'object dropdown, find the object(typically your fieldname), choose change and
'complete the following. Do this for as many fields as you want to cover.

Sub Change(Source As Fieldbox)
        Dim dis As Display
        Set dis=Source.ObjButton
        MakeVisible dis
End Sub

'The lines with Source.ObjButton should read Source.YourButtonName where
'YourButtonName is the objectname you noted earlier for your pushbutton.
'
'What happens? You are passing the argument dis to the sub MakeVisible or
'MakeInvisible and the sub processes the request. Try substituting a fieldbox
'object or any other display class object instaed of the pushbutton and let me
'know how it works.
'
'Written by Jerry Sikes <Unisource Converting>, 1996
'(C) Copyright 1996 by Jerry Sikes
'
'Permission is granted to freely copy this script in electronic form,
'or to print for personal use. It may be use in any Approach database,
'but may not be distributed for profit either by itself or as part of
'a collection or database.
'
'Disclaimer: This script is provided as is without any express or
'implied warranties. The author assumes no responsibility for errors or
'omissions, or for damages resulting from the use of the information contained
'herein, or your own version containing your desires for the usage of the
'script.

Stopping Approach from using Marlett as the default font
Marlett font may be useful for something but its dammed annoying as a default font, so why does Approach sometimes insist that it is the only font for me?!
  
Well, there are a number of possible causes for this problem:

* your default printer is set to "Generic/Text"
* your default printer is set to a fax, .pdf or some other software driver rather than to an actual printer
* you have a font named "Monotype Sorts" installed on your computer. Remove it!!
* you have a field or default font set to a font or style that is not installed on your computer. This is a particular pain if you accidentally uninstall a font which you have used throughout your .apr's. I speak from personal experience on this one. Months later I was still opening not-often-used .apr's only to find everything was now written in Marlett which is not a language I can readily read!

Deleting the body in summary reports
Use your mouse to drag the bottom line of the body frame up above the top line of the body frame. That way the body disappears and only the summary lines show up on the report.

Tips on layout of Reports / Altering report width
The default method of working with a columnar, repeating panel, or summary only type report is to use PowerClick reporting (see Approach Help for more information about it). In a nutshell, this feature works when you are in Design mode and have the View menu option "Show Data" turned on. Columns then become moveable and resizeable with the mouse, but working with individual elements such as the column header can be difficult. 

If you can't get your layout the way you want it using PowerClick, try turning 'Show Data' off (by clicking on it in the View menu). You can now resize and reposition the field boxes and column headings as required.

It is not possible to alternate the background color in the line items of a report (like in a repeating panel). Therefore if you really want to do this you either need to export the data to a program that is capable of it, or create a really big repeating panel! Remember that a repeating panel has a maximum of 30 rows.

A common problem with reports is that a second unwanted page is created to the right of the page you are working on. This is because one or more of your report objects (fields or text blocks) is extending beyond the right margin of your page. To find the offending object(s), turn off the "Show Data" option. Then under Edit, Select All to highlight all the object boundaries. Scan down the right margin and move/resize the object as needed. Remember that the object may be in the header or footer.

Total page counts on Reports
There is no automatic function to do this, so basically you have to get your Report to work it out itself. The following two methods have been submitted:

Method 1: using imbedded IF statements:
--------

Create a calculated field with the formula:

if(SCount(Surname) <= 25, 1,if(SCount(Surname) >25 and SCount(Surname)
<= 50,2, If (SCount(Surname)> 50 and SCount(Surname) <= 75,3,4)))

This presumed that the surname field would be on every record and
that their would be a maximum of 25 records to the page and that I would
never have any more than 4 pages worth. You will need to adapt it to your requirements.

Then placed the normal page counter field with this field next
to it. Eg: Page 1 of 4

Note that this formula will only give the correct result for a particular printer type and page layout. If either of these change you will need to check the formula.

Method 2: using 3 calculated fields
--------

Create three calculated fields PCOUNT1, PCOUNT2 and PCOUNT3 using the following formula:

PCOUNT1:

     (SCount(CONTENTS) + 4) / 90

PCOUNT2:

     Trunc(PCOUNT1, 0)

PCOUNT3:
     If(PCOUNT1 > PCOUNT2, PCOUNT2 + 1, PCOUNT2)

Then put the following in a text block in the header or footer of
your report:

Page <<#>> of <<PCOUNT3>>

Form letters (and multi-page reports)
The bad news is Approach (up to v97 at least) only supports single page form letters. If this is a problem then do your form letters in WordPRO, or some other word processor, and either have the word processor access the .dbf directly, or export your data from Approach in whatever format your word processor wants. This can be automated by creating a macro which exports the desired fields into a temporary database, and then opens your WordPRO form letter containing the desired embedded merge fields. You can complete the automation by imbedding a Script in the letter which performs the mail merge and perhaps starts it printing. This script would need to automatically execute when the letter is opened.

A work around in Approach is to create your form letter as a Report. Eg. have the heading info in the header panel, the greeting
and opening paragraph in a leading summary panel. Body panels could contain a list of items to be billed, and a trailing summary panel could contain the total and include the "Yours truly," line and space for a signature. If you are keen the opening paragraph could be a calculated field combining various sentences based on flags in the customer record. When using this method for multi-page letters/reports, make sure you do not have a footer panel. Set the properties for both the memo field and the body panel to "expand" using the InfoBox (click on field and press ALT-ENTER). As long as there are no fields beneath the memo field in the panel, the memo field and the panel will expand to span as many pages as is required.

Note: There is a bug in v3.02: A memo field that spans more than one page, can cause weird problems apparently.

Stopping v96 (and beyond) from entering design mode when switching to a report
You may not have noticed this, but there is no "Browse Mode" option on Approach reports that contain summary panels. But there is a "Browse" option on reports that are simple listings of records. This "fact" lets your user update the report's records in Browse Mode if the report fields are not read-only!

Back when V3.02 was current, as an enhancement the Lotus developers were asked to make it possible to show totals on reports containing summary panels in Browse mode. Since this was not possible, their "solution" was to switch the user to Design Mode automatically if the user manually switched to a summary report view in Browse mode! Oddly, a macro VIEW switch to a summary report will go to Print Preview mode even if you put BROWSE commands in your macro. 

This behavior is controlled by a new setting starting with V96. Go to the FILE menu USER SETUP, APPROACH PREFERENCES dialog, and uncheck the checkbox next to "Show Report Summaries" on the Display tab. Then save your work and restart Approach. This setting is stored in each .APR file and is not universal to your Approach installation. This will prevent accidental switching to design mode on summary reports!

This setting will affect all the summary reports in the .APR file. 

Repeating panels in Reports
A report view can only have one Repeating Panel. A work around involves using a macro to export fields from different databases into one temporary database which is used to create the report.

Creating HTML and text file reports
While Approach has features to allow you to save a view as .html or .rtf, by far the best way of creating web and text reports in by using LotusScripts. There is a great example of how to do this on the example databases titled "Writing HTML and text files from Approach". After you've played around with it to see how it works, just press CTRL-K to open the script editor and see the LotusScripts!

Also see the following example LotusScripts in this FAQ:
* Create a delimited text file from two fields
* Creating a dot notation text file

Limiting the options in a drop down list using an Alias table  [UPDATED] 12 Jun 2026
You can use an Alias database table to limit the list of options in one dropdown list by the option that have selected in another dropdown list. An "Alias" is when Approach opens two connections to the same database table which can then be used in separate joins. 

You can download a working version of the following example at https://approach.droppages.site/state-city.exe

Create a database (in this example called St_City) containing the desired fields (in this example called State, City).

Then create an Alias of the database table (automatically named St_City:2, which the original database table becomes St_City:1. In v97 you create an alias by opening the Join dialog box, selecting the database table, and clicking the ALIAS button). Join the Alias and the original database tables by their City fields.

Then go into design mode, select the State field, make it a Field Box & List and select the 'Create list automatically from field data' option. Then select the City field and similarly make it a Field Box & List and select the 'Create list automatically from field data' option, but before you leave that dialog box click on the OPTIONS button, and select the options such that the dropdown list is created from the City field in the St_City:2, filtered on the State fields in St_City:1 and St_City:2. 

Now go to Browse mode and start entering records. Once you have entered a few Cities in different states, you will find that once you have selected a state, the options in the city list are limited to those previously entered in that state. Pretty neat really!

Making keyboard entries into a formula or macro
To do this, link every record in your database to a single record in a specially created database. Fields in this record in the special database can be easily changed by putting the fields on any form, and can be used to input information required by any macro or formula. The method for doing this in Approach v2.1 is as follows:

  1) In your existing database (let's call it EBASE) create a new field called (say) DUMMYLINK, and set the field definition options so that a '1' is automatically entered into DUMMYLINK every time a new record is created. Then enter a '1' into the DUMMYLINK field in all existing records. The end result of this is that every record in EBASE will always have a '1' in the DUMMYLINK field. Once it is set up there is no need for DUMMYLINK to appear on any forms.

  2) Then create a new database (INPUTDBASE), with two fields INPUTDATA, and DUMMYLINK. Create one (and only one) record and enter '1' into the its DUMMYLINK field. Then join INPUTDBASE to EBASE by the DUMMYLINK fields. The data you enter into the INPUTDATA field is now linked to every record in EBASE. 

  3) Put the EBASE:INPUTDBASE field onto any form or forms you commonly use.

  4) Create the macro or formula using the EBASE:INPUTDBASE
field where you want to input data.

e.g. In an phone book database you could enter your current location into the INPUTDATA field. This information could then be used by various formulas to calculate the actual phone numbers you need to dial from there, including any national, or international codes, as well as perhaps giving the time and date at the destination.

Limiting the number of characters entered into a text field
In v97, using a text field displayed in a Field Box, the default format settings will cause Approach to limit the number of characters entered to the length of the text field. As soon as the maximum number of characters is exceeded Approach bring up a message box explaining the error. For this to happen, the text field must be displayed in a Field Box, and 'Show data Entry Format' must not checked in the field format (in Design mode, click on the desired text field, then go to the  FIELD OBJECT / OBJECT PROPERTIES / # menu item). Note: if the format is set to 'Display as Entered', then 'Show data Entry Format' will be grayed out and unchecked.

However, Approach will not bring up the message box until you enter the data if the field is displayed in a "Field Box and List" or if 'show data entry format' set.

Conditional form navigation ( tabs )
It is possible to attach macros to fields so that they are executed on data change or entering or leave the field. To have a conditional tab out of a field, construct a macro that checks the condition when you tab out of the field, which then in turn executes other macros depending on the outcome of the condition check. These subsequent macros would each contain different navigation commands. Eg the following describes three macros called Macro1, MacroTAB and MacroTAB2:

Macro1: (executed when you tab out of the 'ChristmasCard' field)
   IF(ChristmasCard=TRUE, MacroTAB, MacroTAB2)

MacroTAB: (this takes you to the next field)
   TAB

MacroTAB2: (this skips the next field)
   TAB
   TAB

You can use this technique for all sorts of things, include data checking and standardising the format of data.

Like many tasks, this can also be done with a LotusScript. Create a LostFocus script for the object or field that checks the condition and then use SetFocus to jump to the desired field:
   
   CurrentView.Body.myfieldbox.SetFocus

Recovering deleted records
If you have compressed the .dbf file then the records have been erased and cannot be recovered.

If you haven't compressed the database then what you need to do is in some way remove the "deleted" tag from the records. You can't do this within Approach. You can do this in some other database software, or you can get utilities designed for the purpose. One that has been recommended on the mailing list is a small freeware tool called Database Manager, which can be downloaded from http://www.inner-smile.com/ Make sure you read the readme.txt file carefully. You find it has three levels of access, and recovering deleted records requires full access. Unless you speak German you may find the tooltips in the Options dialog box a little hard to read ... A more serious limitation with the current version (v2.1) is that is it not possible to open any dBase IV that has a memo field (*.dbt). To get around this, export the memo field to a temporary database, then delete the memo field in the original database. Use Database Manager to recover the deleted records, then re-create the memo field and import the information back into the memo field from the temporary database. 

Another method to recover delete records is to create an ODBC data source using the MS dBase driver. Select the option to show deleted records. Open the dbf with this driver then export to a new dbf file to recover the deleted records.

It is also possible to recover records by opening the .dbf in a text editor, and replacing the asterisk at the beginning of a deleted record with a space character. However, this is not recommended!

Bypass the misuse of the "Esc"-button when using auto-increment
(Created by Johan de Kock in appreciation for the work of Sue Sloan of XpertSS.com)
 
Forms often include a field to pre-number especially documents, e g invoices, etc. The auto-increment default numbering allows each new record to pre-populate with the next available number. When a user however creates a new record, but then press the "Esc"-button before completing the record, the default number is lost, causing "gaps" in the numbering of these records.
 
The purpose of this procedure is to bypass the auto-increment default numbering option, and thus avoid the frustrating misuse of the "Esc"-button, when creating new records (without using scripting, or external means to deactivate the use of the "Esc"-button).
 
I assume that you currently have created one or more forms, in more than one "APR", which use auto-increment default numbering, incrementing all numbers by one, and will proceed with the procedure to replace the current auto-increment default numbering.
 
The first logical step is to list all uses of the auto-increment default numbering, and group these per APR.
 
The second step is to create one field called "Variable Serial Number" per APR of the type "Variable" and the subtype "Number".
 
The third step is to edit the existing macro which takes the user to the specific form:  First ensure that the macro includes the instructions "Records: Last Record" and perhaps "Page To: Specific Page". If these are not yet included, add these lines at the particular logical point. If you have used the instruction "Records: New Record", include these lines before the instruction "Records: New Record". You may also want to include the line "Browse" to ensure that the coming "Set"-command works correctly. Now add the line "Set: Variable Serial Number = Current Number + 1" directly after the line "Records: Last Record" (Current Number refers to the numeric field currently used to number the records.). You have to repeat this for all existing macro's which takes the user to the specific form.
 
The fourth step is optional, but a good help in any problem-solving:  Add the "Variable Serial Number"-field temporarily to the particular form. You may want to change the field's properties to Non printing.
 
The next step is to create the macro "Set Var Serial No + 1_" to each APR:  This macro includes only one command, being "Set: Variable Serial Number = Variable Serial Number + 1". You only need one macro per APR.
 
The next step is to create the macro's "Set Var Serial No + 1 - Form_":  This macro also includes only one command, being "Set: Current Number = Variable Serial Number" (Again Current Number refers to the numeric field currently used to number the records.) (Form refers to any indication to the current form, e g "Inv" for invoices.). You need to create a macro for each "Current Number" used.
 
The next step is to create the macro's "Set Var Serial No + 1 - Form":  The name of the macro is similar to the previous macro, but excluding the underscore (Form again refers to any indication to the current form, e g "Inv" for invoices.). This macro includes only two commands, both being the conditional "Run"-commands. The first "Run"-command will execute the earlier "Set Var Serial No + 1 - Form_"-macro on condition that IsBlank("Current Number") = 'Yes'. The second "Run"-command will execute the earlier "Set Var Serial No + 1_"-macro on condition that "Current Number" = "Variable Serial Number". You again need to create a macro for each "Current Number" used.
 
The next step is to attach the macro's "Set Var Serial No + 1 - Form" to a field on the particular forms:  As you want to ensure that the macro's are executed, and executed as soon as possible, you need to attach each macro to a field which will most definitely be changed by the user. The choice of field to be used, will also determine the timing of the macro. Ensure that the user will be in Browse-mode when the macro is executed. Better not attach the macro to the "Current Number"-field itself. You need to repeat this step for each form being used to create a new record.
 
The next step is to remove the auto-increment default numbering option from each particular field:  Simply go to Field Definition, select the particular "Current Number"-fields, and change to default to "None". Ensure that no other option or procedure will add a default value to any "Current Number"-fields. You need to repeat this step for each "Current Number"-field being used.
 
Now it is time to change to Browse-mode, and test the use of serial numbers in the new records, as well as the bypass for the misuse of the "Esc"-button. Once you are satisfied, you may remove the temporary "Variable Serial Number"-fields from the forms. Should you wish, you can now refine some of the above, e g by resetting serial numbers at specific dates, e g year-ends. Once you are done, go to your home page and Save.

Automatically finding a 'drop down' list entry
If you use a "Drop-down list" the list will automatically move to the first entry for the key you press. E.g... if you press an 'L' it will move to the first entry starting with 'L'. If you then press an 'E' it will move to the first entry starting with 'E'. 

If you use a "Field box and List" the list will automatically show the first entry for what you have typed so far. E.g.. as before, if you press an 'L' it will show the first entry starting with 'L'. Then, if you then press an 'E' it will show the first entry starting with 'LE', and so forth. Note: 1) The list won't move down if what you type is already shown on the list. 2) You have to click or TAB onto the entry you want. 3) in v96 and later, there is a check box in the FILE / USER SETUP / APPROACH PREFERENCES / GENERAL menu to 'Expand Drop-down lists automatically'.

Some people have expressed the opinion that allowing users to enter data into a "Field box and List" can lead to corrupt data due to multiple entries, misspellings, incorrect punctuation etc..... There are a couple of ways to avoid this:

   1) determine all the possible entries and enter them into a drop down list.

   2) create a separate database to hold all the values for the drop down list. Have separate Forms for the Users (which just has a drop down list and doesn't allow them to make new entries) and Administrators (using a Field box and List" to make new entries and maintain the data).

In v3.02 and earlier, a "Drop-down list" is called a "Value list", and a "Field box and List" is called a "Text and Value List", but they do the same things.

Preserving the found set when switching forms
The basic way of preserving the found set when switching between forms is to make sure that both forms are based on the same database table.

In certain situations you can preserve the found set by including a repeating panel in your forms. Approach will keep the same found set as long as the forms you are switching between are based on database tables which are joined along a route to the main table of your repeating panel. If you don't actually want to use or display the repeating panel you can put it on a second page.

Finds on multipage forms
Make a single page form that contains all of the fields that you want to search on. Switch to this search form, do the find, and then switch back to your multipage form. The found set will be preserved as long as search form must also be based on the same database as your original multipage form.

Finding records which do not contain *text*
In Find, enter the following formula in any field:

if(NOT like(DBFNAME.fieldname,'*text-in-question*'))

Tricky finds
If you have exhausted the possibilities of Find and the Find Assistant then you will probably have to create a formula to find the records for you (see the Help files on how to do this). However, remember that the formula is lost once the find is performed. This can be annoying if you don't enter it correctly or you need to modify it for any reason. One way around this is to cut and paste the formula to and from a text document so that you retain a copy of it. However more permanent solutions are:

   * create a calculated field that simplifies the find

   * create a looping macro to do the search and set a Boolean field to TRUE if the records matches the criteria (then just search for all those records with TRUE in the Boolean field. Don't forget to fill the Boolean field with FALSE before you use it next time!)

   * create a variable field and do a worksheet find on that variable field. You do this by placing @VarField (where VarField is the name of your variable field) in the field that you wish to search.

Including Find criteria on a report
The best way of doing this is to use one or more variable-type fields in the report header in a text block or as separate fields. These fields will not appear except in PrintPreview or when the report is printed. 

For example, lets say that you have a Named Find/Sort that uses two variable fields to select records between two dates. The fields are named VarStart and VarEnd. The user fills in the fields and then runs a macro to go to the report. The header panel would have a text block containing the fields and text like this: 

 Sales Report for <<VarStart>> thru <<VarEnd>>

You insert the fields into the text block not by typing them in as text, but by using the Text, Insert, Field Value method.

Another example might simply have your report macro set a variable field to the a description of the find criteria before the report is shown. A sample macro where a user has a radio button to select a value of ThisYear or LastYear:

View Report
 RUN If(userselect = 'ThisYear' run and return from FindThisYearMacro
      else run and return from FindLastYearMacro
 SET VarConditions = Combine('Report for ',If(userselect = 'ThisYear',Year(Today()),Year(today()) - 1))
 VIEW switch to report xxx
 PrintPreview

You would then put VarConditions in the header panel of your report.

How to pre-fill fields in a form-based find
(An edited version of a post by Carlos A Rodrigues Alves)

In order to pre-fill some or all of the fields when the user doing a form -based find, do the following:

1) Create a 'stored find' with all desired presets criteria
2) Create a macro that runs this stored find and then start a 'find again' search

It probably easiest if you attach the macro to a button. The when you run the macro (or click your button) the stored find is executed and then a find form appears with the stored find criteria already filled in. Then the user can type new criteria on others fields.

Setting default sort order for a database or view
In v97, you can set default sort settings for a database in FILE / USER SETUP / APPROACH PREFERENCES / ORDER menu item. Don't forget to click on 'Save Default' after you have set up your desired sort order.

In v3.0*, you can set the default sort order in the TOOLS / PREFERENCES menu.

If you want to have a default order for a particular report then:

  1. create a Name Find/Sort
  2. in the FORM / FORM PROPERTIES / MACROS select the macro name in the 'on switch to..." pull down list

In v 2.1, you can't set a default sort order. A work around is described in article 'Saving records in their sort order' in this FAQ.

Saving records in their sort order
Sort the records as you want them, and then export (ALT-FILE-EXPORT) that database into a new .dbf file. This .dbf must contain the same files as the old .dbf. Delete the old .dbf and rename the new .dbf with the old .dbf file name.  e.g. Say you have a database called PEOPLE.DBF. Sort the records and export all the fields in PEOPLE.DBF into another database called TEMP.DBF. Exit Approach. Delete PEOPLE.DBF. Rename TEMP.DBF as PEOPLE.DBF.

In short, it is much better to just save them in entry order and sort them once they are loaded...

Sorting memo fields or fields that reference a memo field
Keith Seeley writes: Approach is resistant to sorting any field that is a memo field or references a memo field.  However, there is one way to get the first 23 characters into a calculated field that can be sorted (more than 23 if you want to add additional calculated fields).

You need to convince Approach that it's not dealing with a memo field by converting each of the first 23 characters in the memo field to it's ASCII value, then converting it back to a character and combining as you go along.

Define two calc fields, SortableMemo and SM, and sort on SortableMemo.  All you need to do is simply change "YourMemoFieldHere" in the SM formula to the name of your memo field.

SM = Left(YourMemoFieldHere, 23)

SortableMemo =
Combine(Chr(Asc(Middle(SM, 1, 1))), Chr(Asc(Middle(SM, 2, 1))), Chr(Asc(Middle(SM, 3, 1))), Chr(Asc(Middle(SM, 4, 1))), Chr(Asc(Middle(SM, 5, 1))), Chr(Asc(Middle(SM, 6, 1))), Chr(Asc(Middle(SM, 7, 1))), Chr(Asc(Middle(SM, 8, 1))), Chr(Asc(Middle(SM, 9, 1))), Chr(Asc(Middle(SM, 10, 1))), Chr(Asc(Middle(SM, 11, 1))), Chr(Asc(Middle(SM, 12, 1))), Chr(Asc(Middle(SM, 13, 1))), Chr(Asc(Middle(SM, 14, 1))), Chr(Asc(Middle(SM, 15, 1))), Chr(Asc(Middle(SM, 16, 1))), Chr(Asc(Middle(SM, 17, 1))), Chr(Asc(Middle(SM, 18, 1))), Chr(Asc(Middle(SM, 19, 1))), Chr(Asc(Middle(SM, 20, 1))), Chr(Asc(Middle(SM, 21, 1))), Chr(Asc(Middle(SM, 22, 1))), Chr(Asc(Middle(SM, 23, 1))))

IF limitations in v3.0*
v3.0* allows for a maximum of 30 levels of imbedded IF's e.g.:

FIELD1=IF(x,a,IF(y,b(IF z,c,IF...

To get around this, simply use additional formulae, e.g.:

FIELD1=IF(x,a,IF(y,b(IF z,c,FIELD2)))
FIELD2=(IF(x1,a1,IF(y1,b1,IF(z1,c1...)))

Using values from the previous record
There are a couple of ways of doing this depending on what you are trying to achieve. 

One method is to go to the 'previous record', set a variable field/s to the required value/s, and then go back to your original record.

A more complex method is to use an alias of the database to access both the current and previous forms at the same time, using joining fields that a set one record apart using macros. I.e. if you are joining by a field Id, then create a field PreviousID, set its value to Id - 1 using a macro, and use PreviousId to join to the alias. This solution only works though if Id is sequential with no omissions (which is unlikely).

Tricky calculations
One way around some curly calculations is to imbed and IF statement in it. Check this one out:

     SSum(If(condition,field.A,(field.B + field.C)))

One paper you can write this out as:
    
     IF (condition) THEN Ssum(field.A)
                    ELSE =(field.B + field.C)

Generating a random unused PIN number
The following method for generating a random usused PIN number (ie a numeric password') was submitted by Mark Pearson:

My aim was to create a random number of 6 digits between 400000 and 999999, check that the number generated was not allocated to a record anywhere else and apply to a specific record for use. The reason I needed this was as a pin code for a call accounting system to be used by residents.

I used two variable numeric fields, two regular fields and three macros.

Macro one (This is actioned when on the record to receive the new number)

set var1 = studentID ' This sets a var field to the student or record id and will be used later to complete a find'

set var2 = Trunc(random()*1000000)+400000 'This creates the random number
but it is possible for it to be greater than 999999'

set var2 = If(Var2 > 400000 and Var2 < 999999, Var2, Var2 - 400000) 'This
reduces the value if was greater'

Find - Stored find 'create a stored find based on a worksheetview that has the var2 field and the field to get the random number. This find locates any record that matches the new generated number. If it finds a
match  it continues this macro. If it finds no matches it goes to a macro2

Run macro3

Macro2
Find - Stored find that uses a worksheet view with the studentID field.
Finds the student that =@var1
Set - Random number field = var2

Macro3
Set var2 = Trunc(random()*1000000)+400000
set var2 = If(Var2 > 400000 and Var2 < 999999, Var2, Var2 - 400000)
Find stored find as per macro1
Run macro3

The Combine() function in calculated and memo fields
The results of the Combine() function in a calculated or variable field is truncated to 254 characters. However, the results are not truncated if the function is used in a macro SET command on a memo field.

Use 'CHR(13), CHR(10)' in a Combine function to insert a Carriage Return/Line Feed. Eg. Combine(your.memofield, Chr(13), Chr(10), Chr(13), Chr(10), Chr(13), Chr(10),'Yours faithfully,', Chr(13), Chr(10), Chr(13), Chr(10), Chr(13), Chr(10), your.LetterWritersName)

The Chr(10)'s may not be needed depending on what you are doing. If you end up with twice as many lines as you expect, then remove all the Chr(10)'s.

Using calculated fields in joins and exports
A calculated field will not appear in the join dialog if it uses fields from more than one database (.dbf) file or if it includes summary functions. Note that using a calculate field in a join may result in substantially decreased database performance.

A work around in situations in which you cannot get a calculated field to appear in a dialog box (join, export, etc...) is to create another field and set the value of that field to the calculated field using a macro.

Also see article 'looping macros'

Calculations on numbers in a text field (eg imperial measurements such as 3'6")
If you want to do calculations on numbers contained within a text, you first have to get the number out of the text field and then do the calculations. To do this you will have to use the Position function to locate where the single and double quotes are in the text string, and the Left and Mid functions to extract the numbers. Then you do your calculations. Then you can use the results of your calculations to create a new text string if that is what you need (using the Combine function).

This could all be done in the one formula, but it would be a long a complicated one!

Here is an example of this process spread over 4 formulas to make it more readable. The original measurement is contained i a text field called 'size'. It calculates half the length contained in 'size' and puts the result in 'halfsizedtext'. It should work regardless of how many digits or decimal places are in the original measurement (eg 3' 6", or 123' 4.57")

   - feet = Left(size, Position(size, Chr(39), 1) - 1)
   - inches = Middle(size, Position(size, Chr(39), 1) + 1, Position(size, Chr(34), 1) - Position(size, Chr(39), 1) - 1)
   - halfsize = ((feet * 12) + inches) / 2
   - halfsizetext = Combine(Trunc(halfsize / 12, 0), Chr(39), ' ', halfsize - (Trunc(halfsize / 12, 0) * 12), Chr(34))

Summary fields
Calculated fields that have a "summary" type functions like SCOUNT, SSUM, etc... only work and display properly when they are used on fields in joined databases, and not the main database for the form. E.g. If your form is based on a database called DEPOSITS, which is joined to another database called BANK then ON THAT FORM you can only use SSUM on BANK fields. A summary of a DEPOSITS field would have to be displayed on a form that uses BANK as its main database.

If you want to count only some records, use an If statement within
the SCount expression. Eg:

     Bigrecords = SCount(If(DB.bigsmall = 'BIG',1,0))

   ... will count records containing BIG in the field named.

The following summary was contributed by Paul Bent:

If you have a form (say) based on table A with a repeating panel based on table B then it works like this:

SSum(tableA.field)   summary option all records in table A  - will only
display in print preview or design. If you want to display it in browse you need a workaround using an alias table.

SSum(tableB.field)   summary option all records in table B  - will display in Browse if placed outside the repeating panel.

SSum(anyfield)   summary option summary panels where placed - will only
display in design or print preview when placed in a summary panel on a
report. What it summarises depends on the panel properties.

Demystifying Summary Functions
(This article was submitted to the Approach Users Mailing list by Jerry Sikes, Unisource Converting)

Here is a guideline on proper Summary Functions use. All page references are to Lotus Approach Release 3.0 users Guide, copyright 1994 Approach Software Corporation.

   1. p7-15 - "You can add a calculated field to a form to show a total, average, count, or other summary on data in a repeating panel." The key is placement of the summary calculated field "on form", not in panel.

   2. p7-16 - "A summary on data in a repeating panel is calculated in Browse and Preview, and in Design if you're showing data rather". This means that a summary calculated field can be visible in all Approach modes.

   3. p7-16 - In reference to the field definition of a summary calculated field, "In the Define Summary panel, select "Summary of all records in database," where database is the name of the main database for the (repeating)panel;". p7-21 - "Because of the join the function counts(summarizes) only the records that appear in the (repeating)panel." Most important aspect for correct calculation is not using the "Where-placed" default Summary Definition. In Field definition, you must define the summary for the repeating panels main database.

   4. p8-19 - "For more flexibility, however, you can use a calculated field that has been defined as where-placed, which you can reuse as often as you wish. A where-placed calculated field looks at the summary panel it's place in and automatically summarizes" This applies only to report summary panel usage. You can use the same summary calculated field in leading, trailing, and grand total summary panels. When you place a summary calculated field on a form, its where-placed is not a summary panel, therefore Approach cannot figure which group of records to summarize in browse.

   5. p8-19 - "A calculated field in a summary panel is calculated when you preview the report÷" This reference applies only to calculated fields in summary panels. Summary calculated fields are visible on forms provided they are defined properly.

   6. A summary calculated field can be bound by Mathematical functions. Such as; Round(SSum( Database."Item Extension"),2).

   7. A summary calculated field cannot be bound by an If function. The If function must be bound by the summary calculated field. For example; Round(SSum( If (Database."Ship Status"="Shipped", Database." Amount",0),2). This would selectively summarize only those items in the repeating panel whose status is shipped.

Problems with summary panels
Sometimes a problem is encountered when creating a summary report in which a 'Zero Items could be added to the report' error is received, and the is body panel in the report in which to add fields.

Believe it or not, the problem is caused by a video driver conflict! The solution is to create a regular columnar report and add the summary panels afterward using the Create - Summaries option in Design
mode.

Using a summary field to count records which meet certain criteria
To count the number of records that have a value within a certain range, you can use a formula like the following:

Count1 = SSum(If(Amount <= 1000,1,0))
Count2 = SSum(If(Amount > 1000 and Amount <= 3000,1,0))

Count1 will contain the number of records with values less than $1000, and Count2 will contain the number of records with values between $1000 and $3000

Problems with repeating panels
Firstly, does the repeating panel contain the right fields? If so, try moving the repeating panel a bit on the form. Do the fields move with it? If not then they are not really in the repeating panel. To correct this, select all the fields and cut them from the form; select the repeating panel so that the panel edges are highlighted and paste the fields into it. Try moving it again to check they are now in the repeating panel. If they still don't move, try deleting both the fields and repeating panel and setting it up from scratch.

Other things you may need to check are whether or not the data is actually there in the right database and fields, and that the database is meaningfully joined to the parent database.

Finds on repeating panels fields
If any find criteria are put in a repeating panel the find will return all the records that fulfill the criteria but will not display any other records in the repeating panel. A way around this is to set up a view that shows the specific fields you will be searching. Do your search with that view, then switch back to your original view with the found set. 

Using an alias to limit the records in a repeating panel
This describes how to use an alias to limit the number of records shown in a repeating panel. Say you have a form based on a database A, which contains a repeating panel based on database B, and you want to specify criteria beyond that which satisfies the join to limit the records shown in the repeating panel.

There is no "filter" option on a repeating panel,  but you can achieve the result using an alias:

1) Add a field or fields to database A that contains the values for limiting the repeating panel content.
2) Go to Create-Join and make an alias of database B.
3) Join B to A using the new fields.
4) Base the repeating panel on the alias of B.

Note that the contents of the repeating panel will always be limited by the criteria you specified. If you want a repeating panel to show all the records in database b limited only by the join, then you will need to create another repeating panel based on database B and not its alias.

Exporting numbers
If you are having difficulty exporting numbers as fixed length fields, or any other particular format, use the NumToText function and if necessary manipulate the text field into the desired format.

Approach 97 incorrectly exporting numbers via macros
Approach 97 has a bug in its exporting of numeric values via macros. If you are experiencing any difficulties make sure have installed the latest apfixpak.exe. It is available from:
*IBM's Approach Support web site: http://www-1.ibm.com/support/manager.wss?rs=1&rt=0&org=sims&doc=83FE03EFA9A58FC5852569EE0079ED8B
*Lotus FTP site: FTP://FTP.SUPPORT.LOTUS.COM/ (From this site browse to pub, desktop, Approach, win32, 97, updates). 
*http://www.xpertss.com. Go to the "free downloads" area, and search in Products for Approach

See technote 148713, title:  Incorrect Results Exporting Numeric Values Via Macro:  http://www-1.ibm.com/support/manager.wss?rs=1&rt=0&org=sims&doc=4BFAC85907156BA6852569EE007BD337

Working with Paradox database files
A number of people have reported indexing and General Protection Fault problems when working with Paradox files with Approach versions up to and including v96. To avoid these make sure you are using:

  either: Approach v3.02 on MS-Windows 3.1 or (WFWG v3.11)

      or: Approach v96 or later on MS-Windows 95.

You will find problems if you attempt to use v3.0* on MS-Windows 95, or if you use v3.00 or 3.01 on any operating system. However, you will probably be better off converting your database files to dBase IV. Also see 'Accessing .dbf's updated by Access / Paradox' in this FAQ.

The Approach equivalent of Paradox DFunctions is to define calculated fields using the SSum, SAvg, SMax etc functions.

The following freeware purports to mend dbase and paradox datafiles, but I have not received any reports of its effectiveness:
http://www.ppsoft.dk/Paradox_Dbase.htm 

Accessing Access files (*.mdb)
The 16-bit ODBC drivers for Access 2.0 can be purchased from Microsoft.

The 32-bit driver is available from:

http://www.microsoft.com/odbc/download/default.htm

However, it can only be installed if you have a Microsoft product already installed on your hard drive. If you don't have any Microsoft products then you will need purchase a third party driver set from a company like Intersolv:

http://www.intersolv.com

Accessing .dbf's updated by Access / Paradox
When records are added to a database using a different database  application, Approach's index file (.adx) is not updated, and therefore Approach cannot access the new records. The indexes can be rebuilt by closing and reopening Approach. Once this is done the new data can be seen. Apart from this inconvenience, adding data to a database using other applications while Approach is open, may also increase the possibility of index files becoming corrupt. It's therefore not recommended.

Using index files other than .adx (eg.mdx)
All versions of Approach are capable of maintaining .mdx index files used by other database applications, but it is not capable of creating them. If you need to use them you have to first create them using some other program.

Up to version v96 Approach was able to maintain .mbx indexes, but this facility was removed in v97 and later because Lotus considered it to be "unreliable".

Some .mdx indexing utilities which subscribers have mentioned are:

"Database Manager" http://inner-smile.com/dl_dm.htm (freeware)
"DBFViewer 2000" http://www.dbf2002.com 
"Developer 1" http://downloads-zdnet.com.com/3000-2210-10179455.html?tag=lst-0-2
http://www.dbcommander.com/ ($100)
http://delphi.icm.edu.pl/ftp/tools/dbu13.htm ($60)

Export of Crosstabs to 1-2-3
Just copy and paste it! Highlight the entire crosstab by clicking in the upper left corner, then copy it. Switch over to your spreadsheet, and paste it.

Using Approach and IBM DB2
Check out the web site:

http://www-4.ibm.com/software/data/db2/db2lotus/appindex.html

It provides step-by-step instructions, tips and general information.

In vME and v9.1 people experienced a weird problem in that they couldn't scroll past the 50th record. Apparently it DOES NOT occur if you open the approach file NOT fully maximized, and login through the db2 client. However, if the first thing you do is scroll to the RIGHT to see your columns, you'll get the 50 record problem. As long as your APR is NOT fully maximized and you scroll past record 50, and you'll probably never see the 50 record problem for the remainder of that session.  Weird... The problem was fixed in v9.5

SQL and Approach
SQL or 'Structure Query Language' is a whole big area of database theory that if you don't know you will just have to go out there and learn it. It is a 'language' and therefore is akin to programming. So if you are not a programming or are not interested in going in that direction then SQL probably isn't for you. The SQL entries in this FAQ assume that you know SQL but want to know about how you implement SQL queries using Approach.

In v96 and later you can create and use SQL queries with just Approach by itself.

People have reported a few different things that don't quite match up about v3.* and SQL: either it is unable to connect to Oracle, or requires an Oracle or SQL-Server backend, or can only connect to SQL Server v5.0 or earlier tables. Whatever the true situation is, it is probably best to avoid v3.0* if you want to access SQL tables.

Whatever version you are using, make sure you downloaded the latest GA MDAC from Microsoft at http://www.microsoft.com/data

If you are using dBase files don't forget to start your query with:

    Type=dBase

You can perform an SQL query on only a single dBaseIV table at a time. However, you can add and join more than one SQL tables simultaneously. 

If you know SQL and don't want to use the Approach's SQL Assistant, you can manually create an SQL statement in a text editor and save it with a .qry extension.

Also see article 'Using SQL from LotusScript'

Using SQL from LotusScript  [UPDATED] 12 Jun 2026
On page 8-51 of 'Developing SmartSuite Applications Using LotusScript' there is an example of embedding SQL SELECT statements in a Script.

Other example LotusScripts using SQL can be found in the Approach User Support Example Databases for Lotus Approach web page:

    https://approach.droppages.site/examples.htm

Using SQL SELECT statements in scripts means that you can use values obtained from the user in the select statement without having to create new queries each time.

Sub GetItem(FindItem as String)

 'the ITEM_NO the user is looking for gets passed to the sub

 ' the line we're focusing on in the SELECT statement would
 ' look something like this:

  Type=dbase

  SELECT invoice97.ACCT_NO,invoice97.INVOICE_DATE,invoice97.QTY_SHIPPED,
   invoice97.ITEM_NO,invoice97.SELL_PRICE,invoice97.PERIOD,invoice97.WEEK
  FROM "C:\lotus\work\approach\invoice97.dbf" invoice97
  WHERE (invoice97.ITEM_NO = FindItem)
  ORDER BY invoice97.PERIOD,invoice97.WEEK
End Sub

Using SQL in Approach with non-SQL data sources
The following is an unedited post from Jerry Sykes:

Here's a primer on SQL Select theory in Approach for Non-SQL database sources such as dBASE IV. SQL select is comprised of 4 sections

1. SELECT <fields>
2. FROM <table>
3. WHERE <Conditions are meet> optional
4. ORDER BY <Sort> optional
As with any script string, enclosed the entire SQL statement in double quotes.
q.SQL = " <SQLStatement>"
Before beginning with the Select part, lets look at the "FROM" statement
"FROM" has two parts; FROM ""table full path name"" aliasname
Alias is the important concept here. It is the short reference name you are calling this datasource. Here is a FROM clause from of one of mine;
FROM ""F:\Programs\RawMaterials\BoxTrans.dbf"" BoxTrans 
then the variable substitution example from Help examples.
FROM """"+ Q.Tablename+ """"+ tname + " 
Notice my database table full path name has is enclosed by double quotes, yet my alias has none. Approach will interpet this when it translates the full string to;
FROM "F:\Programs\RawMaterials\BoxTrans.dbf" BoxTrans
stripping the outer set of quotes. In the second example. Q.Tablename is the Approach Query class property that is meant to hold the full tablename path.
Assuming Q.tablename is set previously in the script and is a variable a third set of  double quotes is necessary for runtime interpetation. In the same manner, the Alias variable must have one set of double quotes. The alias does not necessaraly have to be the database name. Any unique character combination except script keywords is ok. Such as;
FROM "F:\Programs\RawMaterials\BoxTrans.dbf" B
Just be sure you use the declared alias in all SQL alias.fieldname references.
Summing up the WHERE clause, if you want to supply the path in a SQL
statement, then 
"" mappeddrive:\..\databasename "" alias
else
""" + scriptvariable + """ " + aliasvariable + "
where scriptvaiable is the full path and name and aliasvariable is what you are naming this datasource within this script.
After you understand the alias naming convention, the alias is used in the other three sections.
If all fields are desired then
SELECT * FROM
where * is a all fields wildcard
else
SELECT alias.fieldname1, alias.fieldname2 ... alias.fieldnameN FROM
the last field selected should have no comma after it, just a space then FROM.

An example;
SELECT BoxTrans.ItemNum, BoxTrans.JobNum, BoxTrans.TransDate,
BoxTrans.FoldWeekOf FROM
If you use a variable alias type, as in this example,
FROM """"+ Q.Tablename+ """"+ tname + " ,
then  "+ tname + ".fieldname syntax is used. My preference is to avoid the "+
tname + " type of alias. It requires much more care in typing. If you are
using long fieldnames in your tables, it may be necessary to use double
quotes.
SELECT BoxTrans.""Item Number", BoxTrans.""Job Number"",
BoxTrans.""Transaction Date", BoxTrans.""Fold Week Of"" FROM
Continuing, the choice of syntax in your WHERE clause, follows the exact
syntax rules for the alias.fieldname part, but a slight variation is required for the criteria. From the Script Help...Approach Objects...SQL...Example,
WHERE ("& TName & ".""Room Name/Number"" = '" & DeletedRooms(i) & "')"
and one of mine
WHERE (BoxTrans.TransDate = '""'and BoxTrans.FoldWeekOf = '"+MySQL+"')
In both of these examples, the right hand conditional phrase takes a slightly different syntax. Instead of double quotes, you use 
alias.fieldname = single quote double quote +VariableCondition+ double quote single quote.
Notice an & and a + are interchangable when building string combinations.
The best method of troubleshooting a bad syntax SQL statement is to put a
breakpoint in the script after the SQL. Drill to the SQL statement of the
query object in the variable pane. Click on the SQL statement in the Variables pane. The runtime/substitution construction of the SQL statement will be visible on the bottom line of the variables pane. The following complete script,
qu.SQL = "SELECT * FROM ""F:\Programs\RawMaterials\BoxTrans.dbf"" BoxTrans
WHERE (BoxTrans.TransDate = '""'and BoxTrans.FoldWeekOf = '"+MySQL+"') ORDER
BY BoxTrans.ItemNum,BoxTrans.JobNum"
shows this string in the variables pane at runtime, when viewing the query object at a breakpoint script halt.
SELECT* FROM "F:\Programs\RawMaterials\BoxTrans.dbf" BoxTrans WHERE
(BoxTrans.TransDate = '"'and BoxTrans.FoldWeekOf = '4/6/98') ORDER BY
BoxTrans.ItemNum,BoxTrans.JobNum

Sending email directly from a form
Using a macro, you can send email to a particular email address 

OPEN   mailto:username@domain.com

If the email address is in a database field, then use:

OPEN   mailto:<<mytable.emailfield>>

If the email address is in a variable field, then use:

OPEN   mailto:<<varfield>>

The following does the same thing using a script:

Sub CreateEMail

	Dim lngRtn As Long

	lngRtn = Shell("rundll32.exe url.dll,FileProtocolHandler mailto:" &
CurrentView.Body.emailfield.Text)

End Sub

Note that the mailto protocol also lets you insert the subject and a limited amount of text in the message body, but can't use it to send file attachments. Full details are in RFC2368 (search for this document on the web).

Using the contents of a variable field as an export filename
Create an macro that performs the required export to a set filename (say c:\temp\tempdata.dbf), then runs the script shown below. The script copies the exported file to the desired diskdrive, filename and directory contained in a variable field (VarField). For the script to work, the variable field must be displayed on the current view.

Sub Rename
    Dim filename As String
    filename$ = Cstr(currentview.body.VarField.text)
    Filecopy "c:\temp\tempdata.dbf", filename$
End Sub

If you want the variable field to only contain the filename then use the following variation:

Sub Rename
    Dim filename As String
    filename$ = Cstr(currentview.body.VarField.text)
    Filecopy "c:\temp\tempdata.dbf", "c:\path\"&filename$&".dbf"
End Sub

If you don't know the path name of the destination file (because it's on a network, or installed on different computers), you could use the LotusScript function in article "Getting the path of a database file" in this FAQ.

The next script basically does the same thing as the first, except that it renames the file rather than creating a new copy of it.

Sub MyReName
     Name "c:\temp\tempdata.dbf" As filename$
End Sub

Exporting summary fields
Let's say that the database that contains the data that is summarised in your summary field is called 'Main'.

Create a form based on a mock database that displays: 
    * a repeating panel of the records in 'Main'
    * the summary field
    * either a calculated (or variable field).

Set the calculated field to equal the summary field, or if you are using a variable field use a looping macro to set its value to that of the  summary field.

The calculated or variable field can then be exported.

Using a macro or Script to open documents in other applications
To opening a WordPro to a particular document from Approach, just create a macro with an OPEN command, and enter the path and filename of the WordPro document.

However, there is a problem with the OPEN command in that it gives an error if you have a space in the path or file name. To get around this, create a one line global LotusScript using the OpenDocument method to open the WordPro file. You can then run the sub LotusScript with the macro Run command if you like. An example LotusScript is:

Sub TestLWPOpen
'Contributed by Paul Bent XpertSS.com
    Dim LWPApp As Variant
    Set LWPApp = CreateObject("WordPro.Application")
    LWPApp.Visible = True
    Call LWPApp.OpenDocument("test doc.lwp", "c:\temp\")
End Sub

If you don't /can't use LotusScript, or need to do something a little more complicated, then consider using a DOS batch file which calls WordPro (or any other applications or applications) with the filename as a parameter. To do this, simply put the following line in a plain text file that has a .bat filename extension (eg. mybatch.bat) where 'f:\rescon\letter.lwp' is the path and filename of the WordPro document you wish to open:

c:\lotus\wordpro\wordpro.exe f:\rescon\letter.lwp

Then create a macro in Approach that calls the .bat file with the 'open' command:

OPEN c:\batch\letter.bat

Problems getting TeamMail to work
If you are using TeamMail in Approach to send email through Lotus Notes, then make sure that the mail Program in FILE / PREFERENCES / USER PREFERENCES / MAIL AND NEWS in Notes is set to "Lotus Notes", not "None". If it is set to "None" then you may find that Approach occasionally locks up or will take an excessive amount of time trying to send the message, followed by an error message stating "Mail Error: Operation Successful", although the email is never actually sent.  This scenario specifically occurs when the user attempts to send a "Snapshot of the current view".

This following applies to v3.0 and later and was submitted by Bill Tarkulich:

The "Mail Error" dialog box shows up usually because you have both Notes and other Email clients on your local system. Assuming you want to use a non-notes client to do the mailing, you'll want to change the configuration. A 32 bit application will look in the Registry; a 16 bit application, will look in the WIN.INI file. 

32 bit apps:
TeamMail gives preference to Lotus mail applications by looking at the following key and sees the Notes app. and may go berserk: HKEY_LOCALMACHINE_SOFTWARE_LOTUS_COMPONENTS TEAMMAIL  You'll want to remove the reference to the mail app so that Approach can find the default MAPI client.

16 bit apps:
Because cc:Mail, Release 2.1 and Release 2.2, are 16 bit applications, they require changes to WIN.INI.  You must comment out the call to the mail application from the WIN.INI file under the heading  [Lotusmail]. 

Outlook is a 32 bit app and requires removing the mail application reference from the Registry Key.  I've got Outlook running on 3 different machines and Approach sends mail through them all quite nicely, once I resolved this.

Good Luck,
Bill

The following further thoughts come from Rainer Schmidt (Germany):

<b>Windows 98SE and prior</B>

We found out that TeamMail prefers to use a Lotus Mail Application and looks to the registry key as described by Bill Tarkulich. If it doesn't find any Lotus mail application, it produces the mail error message.

If you delete the key there, it's name might be „AddressbookLastFirst" with value, Outlook Express should work already, because TeamMail then looks automatically for the standard MAPI client.

As I use <B>Eudora</B> and not OE, this had no success so far. I had to install Windows Messaging and Windows Fax which are on the Win98 CD, but were not installed automatically. Maybe it is necessary to setup a path for the „Post-Office", it doesn't matter which path, it takes „any" path.
The programs can be found somewhere on the Win98 CD, I found them under: Win98 CD -> Tools -> OldWin95 -> message
After installing these, everything worked fine here with Teammail and Win98SE and Eudora.

<B>Windows ME and probably prior</B> (couldn't test it anymore after it worked)

A new thought was brought to us by Roland Bierlein with articles in the newsgroups "de.comm.software.outlook-express" and in "http://smartsuitefaq.vol4u.de" (many thanks to him), which again needs some manipulation of the Windows registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\OptionalComponents\MAPI

There you can find the string entry 'Installed' with the value '0' (zero). After changing the value to '1' (one) TeamMail and WinME and Eudore worked properly together, (it did not work after 'simply' installing Windows Messaging and deleting the Teammail key). Maybe this valuechange is enough to get it run without changing and installing as descibed above (for WinME _and_ older Win 9x versions) - would be nice!

Export in the Quickbooks export format
This is an example in which data is exported in the Quickbooks export format. The print # statement lays dowm exactly what you want.

Sub QBexport
        '-----QuickBooks(tm) Export Script
        '-----Jerry Sikes 2.7.98
        '-----
        '-----Declare Account Constants
        Const A1$ = "A/R 902:Direct - 902"
        Const A2$ = "Revenue from Sale"
        Const A3$ = "Invoice"
        Const A4$ = "Sale"
        Const A5$ = "Credit Memo"
        Const A6$ = "A/R 902:UWW"
        Const A7$ = "A/R 902:Southeast - 902"
        Dim TypeOfInvoice As String, AccountName As String, DueDate As Variant,td As Variant
        Dim RS As New ResultSet,ay As Integer, am As Integer, ad As Integer
        Dim FileNum As Integer, i As Integer
        Set RS = CurrentDocument.Tables(0).CreateResultSet()
        
        '-----Start Process and Output QB Record Declaration type
        FileNum% = Freefile()
        td = Datevalue(currentview.body.MyList.text)
        ay%=Year(td)
        am%=Month(td)
        ad%=Day(td)
        A8$ = Format(Datenumber(ay%,am%,ad%),"mm-dd-yy") + ".iif"
        TableName$ = "j:\programs\accounting\qbexport\"+  A8$
        Open TableName$ For Output As FileNum%
        Print #FileNum%, "!TRNS"&Chr(9)&"TRNSTYPE"&Chr(9)&"DATE"&Chr(9)&"ACCNT"&Chr(9)& _
            "NAME"&Chr(9)&"AMOUNT"&Chr(9)&"DOCNUM"&Chr(9)&"CLEAR"&Chr(9)&"PONUM"&Chr(9)&"DUEDATE"
        Print #FileNum%,"!SPL"&Chr(9)&"TRNSTYPE"&Chr(9)&"DATE"&Chr(9)&"ACCNT"&Chr(9)& _
            "NAME"&Chr(9)&"AMOUNT"&Chr(9)&"DOCNUM"&Chr(9)&"CLEAR"&Chr(9)&"INVITEM"
        Print #FileNum%, "ENDTRNS!"
        '-----Start Main Process Loop
        Do
                If rs.getvalue(34) = "CN" Then
                        TypeOfInvoice = A5$
                Else
                        TypeOfInvoice = A3$
                End If
                If Left(RS.GetValue(8),1) = "J" Then
                        AccountName = A6$
                Elseif Len(RS.GetValue(8))  = 3 Then
                        AccountName = A7$
                Else    
                        AccountName = A1$
                End If
                td = RS.GetValue(9)   'Current system date
                ay%=Year(td)
                am%=Month(td)
                ad%=Day(td)
                Select Case currentview.body.terms.text
                Case Is = "Net 10"
                        duedate = Datenumber(ay%,am%,ad% + 10)
                Case Is = "1% Net 10"
                        duedate = Datenumber(ay%,am%,ad% + 10)
                Case Is = "Net 30"
                        duedate = Datenumber(ay%,am%,ad% + 30)
                Case Is = "2% 10 Net 26"
                        duedate = Datenumber(ay%,am%,ad% + 26)
                Case Is = "2%10 Net 26"
                        duedate = Datenumber(ay%,am%,ad% + 26)
                Case Is = "2% 10 Net 30"
                        duedate = Datenumber(ay%,am%,ad% + 30)
                Case Is = "2% 10 Net 30"
                        duedate = Datenumber(ay%,am%,ad% + 30)
                Case Is = "COD"
                        duedate = Datenumber(ay%,am%,ad%)
                Case Else
                        duedate = Datenumber(ay%,am%,ad% + 30)
                        
                End Select
                        '-----Output Invoice TRNS LINE
                i% = i% + 1
                Print #FileNum%, "TRNS" & Chr(9) & TypeOfInvoice & Chr(9) & Format(RS.GetValue(9),"mm/dd/yy") & Chr(9) & AccountName _
                       & Chr(9) & RS.GetValue(8) & Chr(9) & RS.GetValue(5) & Chr(9)& RS.GetValue(1) _
                   Chr(9) & "N" & Chr(9) & RS.GetValue(12) & Chr(9) & Format(duedate,"mm/dd/yy") 
                        '-----Output Invoice SPL LINE
                Print #FileNum%, "SPL" & Chr(9) & TypeOfInvoice & Chr(9) & Format(RS.GetValue(9),"mm/dd/yy") & Chr(9) & A2$ _
                   & Chr(9) & RS.GetValue(8) & Chr(9) & -RS.GetValue(5) & Chr(9) & RS.GetValue(1) _
                   & Chr(9) & "N" & Chr(9) & A4$
                Print #FileNum%, "ENDTRNS"
        Loop While RS.NEXTROW
        Close FileNum%
        RS.Close
        Messagebox "Invoices Ready For Import Into QuickBooks. Open QuickBooks and Click on File / Import. Select file " + A8$, 0 + 64, i% &"Records Exported"
End Sub

The Approach ODBC SQL server driver
The Approach v97 ODBC SQL server driver has a character limit of  254 (or 256, depending). There is no easy workaround, although there is apparently a complicated one. Basically this requires creating an intermediate database in which memo fields have been broken up into 254 character lots. This could be done either using multiple fields within the same record (in which case you in effect extended the limit to 254 times the number of fields you define) or by splitting the contents of the memo field over several records which all contain a common identifier in another field so that that can be linked together. So, an 800 character memo record would either take up 4 fields in a single record, or be spread over 4 separate records depending which method you use. Memo fields can be split up using the Middle() function.

Note: v9.5 doesn't have this limitation (apparently). I am not sure about the intervening versions of Approach

Search the FAQ for other articles on using Approach as a front-end for SQL tables.

ODBC Connection to Oracle database tables
The following combinations have been reported to work:

     v97 to connect to Oracle 7.3.x and 8.1

The Intersolv drivers that come with Approach have a few limitations, including a maximum field length is 254 characters and that you cannot see tables owned by sys or system

Note that object names (tables, fields, etc.) are case-sensitive and names that have no underscores automatically appear with an initial cap regardless of how you entered it. Names with underscores appear as you entered them.

Transferring data to the Clipboard
The best method of copying information to the Clipboard in MS-Windows depends on what information you want to copy, and what format you need the copied information to be in.

If you want to copy a number of fields from linked tables as a single block of text (eg a particular [persons name, address, phone number etc...) then you first need to combine all of the information into a single text, variable or memo field and display it on an appropriate view. You can populate the field using a macro attached to a button that combines all of the information you want in the desired format (eg Combine(Customer.firstname, ' ', Customer.surname', ', Customer.addess1, ' ',Customer.addess2, ' ', Customer.addess3).

Once you have it set up, all you need to do is click the button, then click into the field and cut or copy the text as required. Alternatively you can add the following lines to you macro so that it will copy it to the clipboard for you as well:

send keys {TAB}  (add additional tabs if required)
edit copy
send keys {ESC}  (optional, this just deselects the field)  

If even clicking on the button is too much for you, then you can attach the macro to some other event such as any data change on some other field (to do this: go into Design mode, select the desired field, open the info box [Alt-Enter] and select the macro under the desired option in the Macro Tab)

If you want to copy fields as separate cells so that you can paste them as a table into a word processor document or spreadsheet, you first need to create a Worksheet that contains all of the desired fields in each record. To copy the information: 1) Switch to the Worksheet; 2) Find the desired set of records either by using Approach's Find menu item and clicking on EDIT, SELECT ALL, EDIT, COPY VIEW, INCLUDE DATA, ALL DATABASE, OK; or by selecting a block of records by clicking the mouse on the left hand side of the Worksheet next to the first record in the block, then click next to the last record in the block while holding down the <shift> key, then click on EDIT, COPY. Then go to the application where you want to paste the data and paste it.
* You can also do it using LotusScript. See article 'Copying calculated fields to the clipboard' for an example.

Accessing information in an Approach database on the web (WWW)
If you want to publish static pages on the web, then you can use FILE / SAVE VIEW AS... / SAVE FILE TYPE = HTML. To save information as database tables on a web server use Approach's 'Saving to Internet' feature (search Approach HELP for more details). If you want to have a live database running, then you need to find an ISP that will let you do some CGI, PHP, or some other scripting, and write or buy the appropriate scripts that you need. These do not come with Approach. 

You can open MySQL databases via the web using its ODBC driver with Approach, as long as the MySQL database resides on a web server and you have a connection to the internet open before attempting to open the db from Approach.

The following words of advice were contributed by Michael Cook:

I've been using Approach to develop databases and making them accessible over the web for about 1 ½ years now.  Here are the major issues:

1. The current (Jan, 2002) implementation of many ODBC database drivers are not thread safe. In your web server configuration, you need to set the ODBC drivers to a single thread configuration; assuming your web server has that ability.  This applies to dBase, FoxPro, Access, and  many Intersolve drivers.

2. If you delete a record using the Approach client, and do not compress the dBase table, an SQL INSERT will no longer work for your browser clients out on the web; i.e. new records will not get written to the Approach dBase table.  Conversely, where your database has parent and child tables (joins), if a record is deleted or updated using a  browser client, a user in an Approach client will not see the change until the dBase table is compressed.  A strategy that I have used is to build in a macro or script in Approach that does a "compress" after a record is deleted, or on APR file open/file close.

3.  Depending on the development environment you are in (PHP, ASP, etc.) you may have some limitations.  For example, dBase tables are NOT recommended if your scripting language is PHP.  The PHP documentation warns that using dBase as your back end tables will result in your tables being corrupted if more than one user accesses your database at the same time.

4. The ODBC drivers do not support indexing over multiple columns (fields), so depending on the size of your tables, searching make take quite a while. If you only need to search on one field, then you can do an SQL CREATE INDEX and index on the chosen field.  This is a different index from what Approach uses.  Approach uses ADX files, and the MS ODBC implementation uses MDX files.  You will have to periodically recreate an MDX index using SQL when new records are added and/or deleted.

If you want to dramatically extend the capabilities of Approach and dBase, check out dB2k at http://www.dbase.com  

I hope this helps.

Mike

How to lauch your default browser to view a URL contained in a text field

Create a button to activate a script or macro along the following lines:

Macro: use the Open command and substitute the field contents in the parameter by enclosing tablename.fieldname in double angle brackets. Eg:
 
Open 'http://'<<Customers.URL>>

This apparently doesn't work in properly in v97.
 
Script: use shell rundll32.exe to invoke the http protocol handler. Say the object name of the fieldbox containing the url is fbxURL

Dim intRtn As Integer

intRtn = Shell("rundll32.exe url.dll,FileProtocolHandler http://" & CurrentView.Body.fbxURL.Text)

See article 'A button activated script that launches URL contained in a text field' for more details.

Running Approach on a network
You can have multiple Approach applications use the same database at the same time on a network with no problems. The .adx index files are specific to a particular .dbf and they can be shared across any number of .apr files as long as the .apr file formats are the same. 

Make sure the time and date on all machines are synchronized and have the same file sharing options set, otherwise problems can occur. Also ensure that each machine has plenty of spare hard disk space for temporary files.

There is no special network version of Approach. When you are doing the installation you will be asked if it is to go onto a desktop or a server. Check the README.WRI that comes with Approach and the Manual for future information.

However, as with all software (executables) you will notice a drop in performance running Approach from the LAN server compared to the running it on a desktop. Some report that the drop in performance is particularly marked if you use calculated fields. The general consensus therefore seems to be that you should run Approach on each desktop individually, but just store the data and the .vew/.apr files on the server. With this configuration the speed of your desktops is more critical than the speed of your server. So if you have a choice, use an older slower computer as the server, and your fastest computers as workstations. This configuration also means you won't be loading your network up with large executables. However, unless you have software distribution software, the "software" person to go nuts if there are 100 workstations to upgrade at some stage in the future... I guess you'll just have to way up the pros and cons. Multiple users can open the APR file and the data file. Approach will handle record locking to prevent people overwriting data someone else updated.

Another thing that may influence the performance of the network (and hence Approach) is your choice of protocol. If you are running NT or a peer-to-peer Win 95 network then you will get much better performance out of NetBEUI (a Microsoft protocol) compared to IPX/SPX (a Novell protocol)

Note: The file locking protocol (Approach, dBase4, or dBase3 ...) must be the same on all the workstations to maintain the integrity of your files. This is specified in approach.ini (in MS-Windows 95 or earlier) or the registry (in MS-Windows 98 or later). For MS-Windows 95 and earlier, the approach.ini should have one of the following settings:

 ... To optimize sharing for approach (preferred) use:
sdBaseFileSharingMethod=APPROACH

 ... If applications other than approach may access the dBase4 files use:
sdBaseFileSharingMethod=DBASE4

In MS-Windows 98 or later, the registry should contain the following key:

HKEY_CURRENT_USER\Software\Lotus\Approach\99.0\General\sdBaseFileSharingMethod = DBASE4. 

Restart Approach for these settings to take effect.

If you are having trouble with network errors, try searching this FAQ for the error message. There are a few articles on different error messages.

One problem that frequently drives people up the wall is when a database is made read only using network security or read and write passwords in the .apr, the has user go through several 'file is read only' messages when the database is opened. To get around this, rather than protecting the database files with network security, make different views for the different user types, and make appropriate fields on the forms Read-only. You will not be able to use Worksheets, though. Use network security to control access to the .apr's

Finding the current Network ID
See scripts titled 'Find the current network ID No.1' and 'Find the current network ID No.2' in the 'Example LotusScripts' section of the FAQ.

Dealing with different paths to the same data from different computers
Problems can arise when writing scripts in a database application that runs on a network when the precise path to the database tables or other files depends on what computer you are on. Eg on all the client computers the path might be \\Vaio\database\ or e:\data, but on the server computer the path to the same files may be \\Admin\database\ or c:\data. There are several ways of dealing with this depending on what you are doing.

If the files you are looking for are in the same path as the database application (ie. when you are running Approach locally, but the .apr's and data files are on the network), then declare a global string variable called, say, Path [ie put 'DIM Path as String' into (Globals)(Declarations) ] and initialize that variable to the current document path [ie put 'Path = CurrentDocument.Path' into (Globals) Initialize ]. ~However~, apparently there is a bug in v95 to v9.7 in which the Initialize sub doesn't always run (although I personally haven't every encountered this problem. It always works fine in my applications...). If you encounter this problem try putting the path command in a different global sub called, say StartUp, and then call StartUp from a MACRO called OPEN. Approach is designed to always run the a macro call OPEN if it is present when the application opens.

If it is not in the exact same path then you can use string functions to create other variations. For instance, a useful one I use a lot is the create and initialize a string variable that contains just the drive information: Drive = Left$(Path, 3).

If the path is unrelated and independent of the current document path and/or drive then you have a couple of options. One option is to the DOS SUBST command to create an alias of the same name on each computer that points to the network folder you want to access.

An alternate method is to create a text file on each computer which contains a declaration of a constant which contains the path for that computer. Eg. create a text file on every computer called "c:\data\path.txt". The contents of the file should be: 'Const Path = "\\Vaio\database\' or whatever the path is for that computer. Then, in (Globals)(Options) of the .apr put:

%INCLUDE "c:\lotus\MachineBackground.txt"

Then just use the 'Path' constant wherever you need to specify the path of the files.

Accessing .dbf files using Novell client 4.83 on Windows 2000
To avoid getting a "file has been damaged" error when trying to access Approach .dbf files stored on a Novell server using Novell client 4.83 on Windows 2000, you apparently need to the Novell client patch from Novell (which updates the NWFS.SYS file)

Do you need to upgrade?
If you are using versions of Approach newer than v3.x, see the article 'Versions of Approach for MS-Windows / NT' in this FAQ to see what the later versions give you, and which version is the best one for your operating systems.

Assuming you are running Approach v2.x or 3.x: 
* If you are running Windows 3.x and using anything prior to v3.02*, then yes you should upgrade to 3.02 at a minimum! 
* If you are running Windows 95/98 or ME, you have an option to stay where you are or to move up to any newer version, but the best one to start with is v97. With it you get many new features, LotusScript, and you avoid the bugs that were in v96.
* If you are running or considering upgrading to Windows 2000 or XP, you will likely find that your older Approach versions no longer work properly and they do not take advantage of the power of these newer operating systems. The 9.7 and 9.8 releases come within SmartSuite only, and are the best 9.x versions for these operating systems.

NOTE : If you are upgrading from v3.* to v9.* or later, then it is highly recommended that you first upgrade and convert your database applications to v97, and then upgrade to later versions from there.

The rest of this article relates to upgrading from v3.02 to v97 or later:

You only really need to upgrade if you want to use LotusScript to create your own functions that are not possible using macros and calculated fields. But there are other "goodies" in the newer releases you will want to consider. Starting with V96/97 many new features were added to make developing your applications easier to do: (And this is not an exhaustive list!)
* LotusScript enables sophisticated task automation using an object-oriented BASIC-like programming system. Attach scripts to a wider variety of object-related events than are available when using macros.
* New Internet features include the ability to save views as .HTML or save entire applications in a .APT file for sending to remote users or storing on a FTP or web server.
* Multiple-page forms - up to 5 pages per form are allowed.
* The ability to display a form view as a "dialog box" with tabs for each page. 
* The Find Assistant helps you create complex queries with the help of an "Assistant" to guide you step-by-step.
* The ability to assign names to Finds and Sorts created manually on a view or using the new "Assistant" and run them either directly or from a macro. 
* A new "Action Bar" with common functions immediately available (Browse, Design, New Records, Find, and select from a list of Named Find/Sorts)
* Updated SmartMasters with examples of LotusScript.
* Drill-down to data from a chart or crosstab to view the records summarized on a worksheet or other view that you select. 
* The Message command in a macro can have two buttons labeled by you and set to run other macros of your choice.
* TeamSecurity increases data security by providing customized security privileges for individuals or groups. The application designer can set up explicit control over database access privileges, view access, and design privileges.

Like all upgrades, earlier versions cannot use the .apr's created / converted to a later version. Newer versions of Approach cannot save .apr's in earlier formats (i.e... v97 and vME cannot save a .apr in v3.02 format). And you should not share databases between users running V3.02 and V97 or V9.x because the "SmartIndex" file formats are different.

Firstly, install the new software and install the latest version of the fix pack (available from the XpertSS.com web site's Free Downloads area usually) before you do anything else. Then test thoroughly all parts of your application. 

Conversion Tips:
1) In order to work reliably, all finds you created within macros should be made into Named Find/Sorts and assigned names. Then update the macro Find command to refer to the named find. 

2) If you edit a Find command that references a Named Find/Sort in a macro and then exit the macro editor using the "Cancel" key, you will delete the Named Find Sort (a bug that exists in all versions of Approach to-date).

3) You can't use the Find Assistant for variable field finds. Use a view find instead. If the name of the variable field includes spaces, the correct syntax is @"Name of variable Field"

4)  Finds you created with formulas and variable type fields may not work. The problem is the order of the fields in the formula. For example, a find with "If(table.field = variablefield)" will work, but if you have it set up as "If(variablefield = table.field)" it will not. (although this DOES work in v3.02)

5) v96/ v97 are unforgiving regarding mismatched field types when using a variable with a predefined find. The find will not work if the variable field type is not the same as the criteria field (although this DOES work in v3.02).

6) Do not save your .apr on a report, crosstab, or chart view unless you also change your Approach Preferences to not "Show" Report Summaries. Otherwise the .apr will open in Design Mode on that view.

7) The format of your form letters may be corrupted and need recreating.

Some people have experienced difficulties with some macros not working after upgrading. If you find some that don't work then you will need to rebuild them. Apparently macros with references to Lotus Notes are particularly susceptible.

Conversion of old .apr's to Approach 96
The conversion is straight forward, however people have struck problems with stored finds. In order to work reliably, all stored finds should be recreated in Approach 96 and assigned a name. See  'Stored Finds in Approach 96/97'.

Once an .apr has been converted it can no longer be used by an older version of Approach. Therefore make sure you keep a copy of your old .apr just in case you strike problems or need the old version for some reason.

Your data remains unchanged and can still be access by any version of Approach, as well as any other database program that uses the data format (dBase IV by default) you are using. (MS-Access, Paradox, etc...)

Approach and Windows NT
Reports have been made to the list of problems encountered running Approach v3.0* and v96 on Windows NT v4.0, including losing drives and corrupting .apr's. It is therefore probably not a good idea to do this. However, Approach 97 appears to work fine on NT.

Performance can be enhanced by increasing the size of the paging file (ie 'virtual memory on the hard disk). This is done in the NT control panel under systems performance.

If after installing NT you find that the layout of your printed reports have all changed, try reinstalling your fonts and printer drivers.

Upgrading ...
Firstly, do you need to upgrade? If you are using anything prior to v3.0* than yes, you need to upgrade!

For other versions, see the article 'versions of Approach' in this FAQ to see what the later versions give you, and which version is the best one for your operating systems.

If you are upgrading from v3.* to v9.* or later, then it is recommended that you first upgrade and convert you database applications to v97, and then upgrade to later versions from there.

The rest of this article relates to upgrading from v3.02 to v96 or later:

You only really need to upgrade if you need to use LotusScript to create your own functions that are not possible using macros and calculated fields, or if you need to be able to use the web features.

Like all upgrades, earlier versions cannot use the .apr's created / converted to a later version. Newer versions of Approach cannot save .apr's in earlier formats (i.e... v97 and vME cannot save a .apr in v3.02 format).

Firstly, install the new software and install the latest version of the fix pack (available from the web site) before you do anything else.

In order to work reliably, all stored finds should be assigned a name. In v96 and later an unnamed stored find is only stored as a 'current find' which is then overwritten by any subsequent unnamed stored find. So it is important to name your stored finds.

You can't use the assistant for variable finds. Use a view find instead. If the name of the variable field includes spaces, the correct syntax is @"Name of variable Field"

v96/ v97 are unforgiving regarding mismatched field types when using a variable with a predefined find.  The find will not work if the variable field type is not the same as the criteria field (although this DOES work in v3.02).

Some people have reported the following problems: (personally, I encountered none. Ed)

The v97 fix pack fixes problems with Export Macros causing GPF's along with other bugs.

In v97 reports with outer joins enabled fail to display data in two .apr's. In v96 they wouldn't group correctly but the body was OK.

The format of form letters may be corrupted and need recreating.

Some people have experience difficulties with some macros not working after upgrading. If you get some that don't work then you will need to rebuild them. Apparently macros with calls to Lotus Notes are particularly susceptible.

Free Approach 3.02 Upgrade
According to the technotes available at the Lotus web site, version 3.02 of Approach is available free to customers experiencing an issue with Approach 3.0. Technotes on the following topics all contain the phrase, 'This issue has been addressed in Approach 3.02':

     * Only One Mailing Label Prints Per Page
     * Error: Unable to Build Index Required for XXX
     * Fill Field Option is Grayed Out in Browse/Worksheet
     * Stack Overflow Running Approach 3.0 QuickStart Tutor.
     * Import Table Has Changed, Unable to Import Data
     * Sending the Current View Only Sends All Views
     * General Protection Fault in Module KRNL386.EXE
     * Record Header Size... Bytes Too Small Saving an .APR
     * Form or Report Places Fields on Top of Each Other
     * Repeating Panel Will Not Sort in Approach 3.0
     * Crosstab Repeats the Same Value in Each Row
     * Changing Number to Boolean Returns Incorrect Results
     * Slow Performance using PowerClick for Grand Total
     * Internal Error - Couldn't Load String 1035 with Spell
     * Approach Export Macro to Fixed Length Inserts Spaces
     * Find Using a Wild Card Returns Blank Date Fields
     * Error: No Records Found Finding Times in DB2 Database
     * Error: Out of Disk Space Finding a Date in SQL or DB2
     * Fields Formatted as Zip Code Corrupt Data in Approach
     * Error: Macro Step Not Valid Editing Valid Macro
     * Percent Format Displays Zeros after the Decimal

So, if you are having troubles in any of the above areas it may pay to check what version of Approach you have (by selecting Help: About Approach). If you are using 3.00 or 3.01 then you may be able to get a free upgrade to v3.02 by contacting Lotus.

Approach v97 and Windows 98/vME

Many people have reported that when using v97 on Windows 98 and Windows ME the performance in design mode is very poor, but the problem doesn't appear to be universal. Apparently Lotus is aware of the problem and is working on it, but Windows 95 is probably the best for match for v97. If you are using later version of windows then you probably should move to v9.5 or v9.6.*

The poor performance arises from Approach making tens of thousands of registry reads for every click you make in design mode. The slowness varies from system to system depending on size of registry, hard disk set up etc. The same click in v9.* versions results in just a handful of registry operations, and is consequently much faster. Download Regmon.exe from http://www.sysinternals.com if you'd like to see what registry calls are being made by any software.

Other problems are caused by a conflict with Norton's Anti-Virus software. Make sure you have the latest version of Norton's, including installing the upgrade available from Norton's web site, or else remove it from your system altogether. Just turning it off apparently doesn't always help the problem (although it would be worth a try). One person reported success by wiping their hard drive and starting again, which seems to be a good thing to do when you upgrade the operating system anyway.

Another performance suggestions is to turn off the Microsoft "FastFind" option, as that is overhead you do not need.

Running different versions of Approach concurrently
A number of people have reported do this without any problems. However, each version before v97 requires a different .apr's. The following applies to running any pre-v97 version  of Approach with any other version of Approach:

Since Approach can't save to older versions, it means that .apr's have to be developed on the oldest version that you want to use. Then, open copies of that .apr in the newer versions. BUT you should avoid using different versions to access the same .dbf's they also use different index files (.adx). Therefore you really should create separate copies of the database files (.dbf's) as well, but this creates difficulties if you want to make new entries or changes to that data. So therefore, if you want to share databases so that you can update them, everybody should use the same version.

Running applications developed in Approach for MS-Windows on OS/2 versions

The main thing to be careful of this that you use only True Type fonts. Otherwise you formatting will probably change.

Obviuously any calls to Windows specific OLE functions won't work under OS/2

This article was submitted by Ward Scarff <wscarff@westernqbe.com.au>:

My experiences with apps developed on Win, then run on Os/2 operating
system, is restricted to A3.x and A97 (not more recent versions). However, I hope the following items do not hinder your implementation ...

* language settings - a continual problem I had in 3.x (and sometimes A97) is the language settings differences between Win machines and OS/2 Warp. A message along the lines of "... can't open dbf because a user has it open in another language ..." often prevented concurrent access. Even when just one person opened the app (without another user in there) Approach would rebuild the indexes for each field as the first new record was entered. Thereafter it worked ok for that user in that session, but if the next user had different language settings (ie likely using the other operating system) then the indexes would rebuild again.

* tabbing - at times I have developed on Win and then found that for OS/2 users their tab key will not move them between fields. I thought it was related to a setting in App which treated Enter as tab, but could not get it to work that way. The only way I solved this was to rebuild the offending forms bit by bit. That worked ok - my conclusion was that a form edited/saved many times was too hard for os/2

* drop down lists - this one I have not solved ... sometimes standard Windows functions like typedown do not work on OS/2 machines e.g. you have the user pick from a list of names and expert them to type T to go to the first one on the list. This is a problem for users scrolling through long lists of items.

* screen (colours?) - some apps I have done look fine on my Win machine but when I open them up on OS/2, elements of the form are "not there" e.g. short lines of text, or the form label within a text box. Also have noticed that on os/2 some report forms have no column headings, but they print ok. I have never solved this, but perhaps using a narrow range of colours (e.g. the Approach equivalent of browser-safe palette?) might work better.

* progress bar - when doing large sorts, exports/imports etc I notice that on OS/2 the progress bar looks slightly different. Not a major issue, but some discerning users may ask why it looks different.

* macro pause - most of my report macros use the sequence Find All>run named find >Find again and pause for imput>change view to the report or chart. Sometimes on os/2 the find again and pause for input does not occur, which prevents the user from altering the find criteria if they want to. Note that I use macros, not scripts, so perhaps scripts may work better.

Installing the Approach 97 apfixpak
Apfixpak.exe is a self-extracting file. To update Approach 97 for Windows:

     1. Make sure Approach 97 is not running.
     2. Copy Apfixpak.exe to your Approach 97 product directory
(c:\lotus\Approach\).
     3. Click the Start button and choose Run.
     4. From the c:\lotus\Approach\ directory Type "Apfixpak.exe -o" on the command line.

You can get it from the "free downloads" area on http://www.xpertss.com 
- Search in Products for Approach and it will be a little way down the list.

You can tell it is installed because the approach.exe file will be dated in October 1997. 

Approach and Windows 2000
Crosstabs don't print in v9.5 or v97 under Windows 2000.

A number of people has said they were unable to install Approach on Windows 2000 with service pack 1 on it.

Approach on a Linux file server
A Linux Samba (cifs) file server can be used to share Approach files and share Smart Suite Installation media.

The Samba project including bug reporting and forum is located at: https://www.samba.org

Be sure to set the Read Only, Archive, and file lock flags in: /etc/samba/smb.conf.

If you use Linux as a file server you can do a network install of Approach onto your Linux server and run it from an MS-Windows workstations. There are minor problems that can be worked around, minimised if you use Win10+.

Approach will run on Linux using the WINE application layer. Wine is an open source MS-Windows application layer. Approach files can be shared between both Windows and WINE.

The Wine project, inclining bug reporting and forum, is located at: https://winehq.org

If Wine doesn't show your printer's proper page types (e.g.. envelopes) or you get "Unhandled exception" errors after printing a page in Approach, then these problems can be fixed by:
   1. Finding all of your printer's page definitions in Wine using the Linux command: $ find ~/.wine -iname \*.ppd
   2. Remove all of those .ppd's using the Linux 'rm' command or the file manger of your choice

When you start a fresh Windows program under Wine, Wine will re-import/collect your changed ppd's from Linux's Common Unix Printing System (CUPS)

Getting Approach running under Wine 9 can be a tricky thing to do, with the possibilty of having to to erase and recreate your WINEPREFIX (.wine) and reinstall Approach several times which identifying and solving installation and setup problems.

The following notes were supplied by Todd Chester:

How to install Smart Suite on Wine 9 in Fedora 39:
-------------------------------------------------

(At this time Approach still isn't working in Wine 10)

"#" means as root
"$" means as a user

1) install all your updates before installing Wine.  (Termbrrible things
    happen if you do not).
     # dnf upgrade

2) install Wine
     # dnf install wine

3) run `wine wincfg` and set programs as "XP"
      --> Applications (tab)
        --> "Windows Version"  (pull down at the bottom)
           --> Windows XP
             --> Okay

4)  Go into `wine regedit` and set the "PATH" to include
     "C:\lotus\compnent"

      HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session
Manager\Environment
      PATH = C:\lotus\compnent;<the reset of the stuff that was already 
there>

      Note: deliminter is a semicolon ";"

5)  Install Approach from the ISO.
     *** DO NOT *** install from a network drive.
     *** DO NOT *** install as root.  Install as a your "user" account.

     DO NOT install "Everything".

     Consider install WordPro too.  It is very useful.

     Under "Shared Tools", install ONLY the "ODBE" Drivers, your 
dictionary, your thesaurus.
     Leave everything else alone.

     If installing on a host Fedora, mount the iso with
          # mkdir /mnt/iso
          # mount -v -o loop -t iso9660 
/home/CDs/ISOs/SmartSuite-N99.8.0208.0800.iso /mnt/iso

     If installing in a qemu-kvm virtual machine, mount the ISO as a CDROM:
       <disk type="file" device="cdrom">
             <driver name="qemu" type="raw"/>
             <source file="/home/kvm/SmartSuite-N99.8.0208.0800.iso"/>
             <target dev="sdb" bus="sata"/>
             <readonly/>
       <address type="drive" controller="0" bus="0" target="0" unit="1"/>
       </disk>

     $ cd /mnt/iso
     $ wine setup.exe

6) Sample launchers:

     Simple:
          $ cd  ~/wine/drive_c/lotus/approach
          $ wine ./approach

     Virtual Terminal for high resolution monitors:
         $  bash -c "cd $HOME/.wine/drive_c/lotus/jumble; wine explorer 
/desktop=Approach_`date +%%H:%%M:%%S`_1680x1050,1680x1050 ./approach.exe"

Dialing phone numbers from within Approach
Prior to v9.5 Approach used it's own internal dialer. See the Dialer tab in the FILE / USER SETUP/ PREFERENCES menu

In v9.5 and later versions Approach uses the windows dialer which has its own setup. You can use the following script instead of using approachdial so that the name (in Aname) of the person you are calling is displayed in Windows Dialer.

Put this bit in declarations:
Declare Sub PhCall Lib "tapi32" Alias "tapiRequestMakeCall" (Byval No As String, Byval A As String, Byval B As String, Byval C As String)

The create the following script in Globals:

Sub PhoneCall (Number As String, AName As String)
        Dim no As String, nul As String
        no =  Number
        Nul = ""
        PhCall no, nul, Aname, nul
End Sub

The Windows Dialer also maintains a log of calls made that can be accessed with the following LotusScript commands:

with source.parent
     call Phonecall (.Telno, .PersonName)
end with

Approach and Windows XP
Microsoft's latest Compatibility Update (Dec 2001) covers Approach v9.1, 9.5 and 9.6. 

v9.7 was XP certified at release anyway.

When install v9.7 on Windows XP you get a message stating that there are problems with running Approach on Windows XP. Actually, v9.7 works perfectly fine on XP. Other v9.*'s also run fine except that printing crosstabs doesn't work. (Note: Printing crosstabs from any version before v9.7 doesn't work on any edition Windows, so if this hasn't been a problem for you in the past then it obviously isn't a feature you use anyway!) 

16 bit applications such as v3.0* and earlier won't run under XP

Re-enable Help in Win 10/11 for Lotus Approach or any other legacy application
The Windows Help system that supported .hlp (WinHelp) files was depreciated after Win Vista, and is not officially available in Win 10/11.

This means that Approach Help files can't be used by default on Win 10/11. However, it can be re-enabled. See:

https://github.com/zeljkoavramovic/hlp4win11

Automatically opening files
Make sure .apr (.vew for v2.1) extensions are associated with Approach. You can do this in the File/Associate menu of File Manger. Then create a Windows program item with a command line something like: 

c:\path\database.apr (for >v3.0*)

where a:, \path, and database.* are the drive, path, and filename of your database file.

To automatically open several files use the command line:

c:\approach\approach.exe c:\path\db1.apr c:\path\db2.apr

Use .vew instead of .apr for v2.1 files

Automatically opening a particular view
Approach automatically opens the form that you had up on the screen the last time you saved the .apr file (or .vew in v2.1). So next time you make some changes make sure the right form is on the screen when you save it.

If you find this annoying, or it isn't working for some reason, you can use either a macro or script to automatically switch you to the desired form.

Macro method: Set up a macro to automatically change to that form when the database is opened. (See "Automatic macro on opening/closing View file").

LotusScript method: Place the following script in the initialize event of your database application:

Set currentwindow.activeview = currentdocument.YOURFORMNAME
CurrentWindow.Browse

Automatic macro on opening/closing View file
You can create a macro that runs automatically each time you open a particular approach view file by assigning it the name "OPEN". Similarly, if you assign the name "CLOSE" to a macro it will run each time you close it's associated file.

In version of Approach including and after v96 you can also do it by specifying the macro in the form properties.

Some people have experienced Approach 'forgetting' the OPEN macro. This happens if you save the .apr from design mode. If it happens you have to delete then recreate the OPEN macro. So beware of saving in design mode! In v97 the same problem has been report if the tabs are visible. Well..., it keeps life interesting!

Keeping users out of design environment
If you are using v3.0* you can set a design mode password by selecting the Tools/Preferences/Password menu item. Approach will then not let anyone in design unless they know the password (even by status bar, icon or key combination).

Read on if you are using v2.1, or if you would also like to remove the temptation of changing the form:

Menus can be customized so that the design environment option never appears.  Apparently, custom menus are kept in .APR files so that no custom files on user PCs would be required.

Icon bars can be customized so the design environment icon never appears.

The status bar on the bottom of the Approach window will let the user go into the design environment. The status bar can be suppressed but this prevents the user from reading useful information such as current record number and how many records were found out of how many.  There are no Approach macros and formulas that support display of such record information on a form. 

In v2.1 there is no way to prevent a user from getting into the design environment via CTRL-D. 

.APR / .VEW files can be made read-only by changing their properties in File Manager, but if a user gets into the design environment, Approach won't tell them they cannot save design changes until they try to save their changes, and they may know how to change the file properties.

Database security
In v2.1 you can give each .dbf a password which is set in the FILE / DATABASE OPTIONS menu in design mode. Passwords can be set for either read-only or read-write access. However, the user has to enter a password for each joined .dbf that requires it.

A better alternative may be to create your own security system. The first view that users see could prompt them for an ID and password, this checks against a list of users and sets a variable field to their security level.  All macros that switch to a view could then check if the current user has the right access level.

In v3.0*, is exactly the same but passwords are set in TOOLS / PREFERENCES.

v96 and v97 come with a team security feature. See the manuals and / or help for details.

Database encryption (introduced in v96?) will most likely protect the database from any simple attempts at decrypting it, but most methods of encryption are likely to get cracked within the course of a few minutes in the hands of a skilled crypt-analyst with the proper equipment.

Changing security passwords

A design password can only be changed in design mode.

If you delete a password in TeamSecurity then you are in effect setting the password to "". Therefore the password dialogue box will still come up. To stop the password dialog box from coming up you need to uncheck the database in the appropriate TeamSecurity section. 

If you've forgotten the password, there's a recovery service available (for a small fee) at http://www.xpertss.com in the XpertSS Products section.

Others which have been mentioned are:
* http://www.pwcrack.com/approach.shtml
* http://www.bykeyword.com/pages/detail1/download-1144.html

Stopping people accidentally entering data
One way of doing this is to create two separate forms: one for data entry and on for retrieval. Make all the fields on the retrieval form read-only so that data cannot be entered.

Stopping users from deleting records
You can't actually disable the delete function. 

The easiest way around this is to use a custom menu that doesn't contain Delete Current Record or Delete Found Set so that the user doesn't have access the Delete Record menu item. This also disables the CTRL-Del Hot Key for deleting a record, so they won't be able to use that either.

Alternatively you display information in calculated fields and getting its contents from the fieldbox's Text property in LotusScript.

CurrentView.Body.CalcFieldName.Text

Note that the object name becomes the calculated field name. You can't edit the object name of a control bound to a calc field and if you subsequently rename the calc field the object name doesn't change. This can get very confusing when identifying objects in the IDE!

Stopping Approach from prompting to save an .apr
If you have modifiable text boxes on reports, you will be familiar with the fact that every time you close the .apr after modifying it you are prompted is save the .apr.

You can suppress the save prompt with the following script...

Sub ClearSaveFlag
        Currentdocument.modified = False
End Sub

Running a Macro in a different .apr files
Using Win 3.1, one way of doing this would be to use the Windows Macros (Recorder) rather than Approach Macros.

With the Win 95 versions, it would be easiest to convert the macro/s to Scripts, and then cut and paste them into the new .apr

Moving a view from one .apr to another
The following method can be used to move or copy a view (eg a form or report) from one .apr to another:

   1. Save the .apr if you have modified it
   2. Save a temporary version of the .apr under a different name, using FILE / SAVE AS menu item with the APR ONLY set.
   3. Delete all the views from the temporary .apr except the views you want to transfer.
   4. In the .apr you want to transfer the views to, go into DESIGN mode, and import the temporary .apr file using the FILE / Import Approach file... menu item

Starting Approach without displaying the startup banner
When you execute Approach, is you use the -s switch Approach will start without displaying the startup banner. Eg. The following Windows shortcut will start Approach without the startup screen an load people.apr

C:\lotus\approach\approach.exe -s c:\_data\people.apr

User selection of database tables within an .apr
For a long time the answer to the questions, "Is it possible for the user to select which database they want to work with within an .apr?", and "Is it possible to use the one .apr to work with different database tables?" was...

    "No!"

However, if you know LotusScript then it is possible! Just check out Jerry Sikes' solution which uses the ReplaceWithResultSet method to allow the user to select a database table from a dropdown list populated from the contents of a directory. See the example LotusScript titled 'User selection of database tables within an .apr'

Distributing .dbf database files to users
It is possible to distribute updated versions of .dbf files for users to access using their existing .apr, as long as the name and structure of the database remains totally unchanged. You should also distribute the new .adx along with it. They just need to replace the old .dbf and .adx (and .dbt if used) with the new copies that you supply.

Scripts are not imported with an .apr
When importing an .apr, the scripts are not imported along with it (up to version v9.6 at least). Hopefully this will be fixed in future versions.

Example LotusScript: Getting the path of a database file
You can get the current directory with the CurDir() function and the current drive with the CurDrive() function. Note that while you are using an apr on a drive E:\, the Curdrive$ could still be C:\ so you should maybe use a Chdrive before.

Another way, if the dbf are associated with your apr, is to use 
TableName.Property. Eg:

Dim TN, TNF As String
TN = CurrentDocument.Tables(i).TableName 'TablesName
TNF = CurrentDOcument.Tables(i).FullName  'Full path with extension

An example of this method is the following LotusScript function was submitted by Steve Carpenter. If you know the table name it will give you the current path.

Function GetTablePath (Byval pTableName As String) As String
    ' Takes table name and returns path of first occurence of the table from tables
    ' collection, or "" if table not found.
    ' 2/9/98 sc
    GetTablePath = ""
    Dim Testname As String
    pTableName = Trim$(Ucase$(pTableName))
    Forall mTable In currentdocument.tables()
        If Instr(mTable.Tablename,":") > 0 Then
            Testname = Left$(mTable.Tablename,Instr(mTable.Tablename,":") - 1)
        Else
            Testname = mTable.Tablename
        End If
        If Ucase$(Testname) = pTableName  Then
            GetTablePath = mTable.Path
            Exit Forall
    End If
    End Forall
End Function

Another example is this following Script, which will bring up a help file which is in the same folder as the current document:

Sub RunHelp
    Dim a as integer
    a = Shell("WINHELP.EXE "+currentdocument.path+"helpfile.hlp",1)
End Sub

Example LotusScript: Screen updating during script execution
Using the REPAINT statement, followed by a 'YIELD' statement. The 'YIELD' statement transfers control to the operating system, so that it can process the events in its queue - including Approach events such as 'REPAINT' as well as non-Approach events. Eg:

'Script submitted by Jerry Sikes 

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
   Dim buttonlabel As String
   buttonlabel = source.text
   source.enabled = False
   source.text = "Executing Order Update"
   CurrentWindow.Repaint
   Yield
   RunStoredProc("exec ucd.jerry.usp_fg_setvalues_01")
   source.text = buttonlabel
   source.enabled = True
End Sub

Example LotusScript: ConvertNumberToText

'Written by Bruno Dumon, 1996 (Belgium)
'Copyright 1996 by Bruno Dumon
'
'Permission is granted to freely copy this script in electronic form,
'or to print for personal use. It may be use in any Approach database,
'but may not be distributed for profit either by itself or as part of
'a collection or database.  It may also be used in other programs
'using lotusscript, like WordPro, Notes or Freelance. 
'
'Disclaimer: This script is provided as is without any express or implied
'warranties. The author assumes no responsibility for errors or omissions,
'or for damages resulting from the use of the information contained herein.
'
'If you have any suggestions or problems, mail Bruno Dumon at
'dumona@unicall.be.  Also, if you make any improvements to it,
'please mail me a copy of it.
'
'How To Use It:
'=============
'Copy and paste the three following functions into the script editor:
'   - ConvertNumberToText
'   - ConvertTwoNumbers
'   - ConvertThreeNumbers
'Then, somewhere in your own code, call the ConvertNumberToText
'function by putting:
'    result$ = ConvertNumberToText(x)
'The script works for numbers ranging from 1 to 999,999,999.

Function ConvertNumberToText(getal)
        Dim e$(10)
        Dim t$(10)
        Dim h$(10)

        e$(1) = "one"
        e$(2) = "two"
        e$(3) = "three"
        e$(4) = "four"
        e$(5) = "five"
        e$(6) = "six"
        e$(7) = "seven"
        e$(8) = "eight"
        e$(9) = "nine"
        e$(10) = "ten"

        t$(1) = "eleven"
        t$(2) = "twelve"
        t$(3) = "thirteen"
        t$(4) = "fourteen"
        t$(5) = "fifteen"
        t$(6) = "sixteen"
        t$(7) = "seventeen"
        t$(8) = "eighteen"
        t$(9) = "nineteen"
        t$(10) = "twenty"

        h$(1)="ten"
        h$(2)="twenty"
        h$(3)="thirty"
        h$(4)="fourty"
        h$(5)="fifty"
        h$(6)="sixty"
        h$(7)="seventy"
        h$(8)="eighty"
        h$(9)="ninety"
        h$(10)="hundred"

        g$ = Format$(getal)  'Make a string of the number
        L = Len(g$)
        If L &gt;=1 Then
                o1$ = e$(Val(Right$(g$,1)))
                If Len(g$) = 1 Then r$ = o1$:Goto finished
        End If
        If L &gt;= 2 Then
                o2$ = ConvertTwoNumbers(g$,e$(),h$(),t$())
                If Len(g$) = 2 Then r$ = o2$:Goto finished
        End If
        If L &gt;=3 Then
                o3$ = ConvertThreeNumbers(g$,e$(),h$(),t$())
                If Len(g$) = 3 Then r$ = o3$:Goto finished
        End If
        If L &gt;= 4 Then
                v$ = Right$(g$,4)
                If Val(Left$(v$,1)) &lt;&gt; 1 Then
                        o4$ = e$(Val(Left$(v$,1))) + " thousand " + o3$
                Else
                        o4$ = " thousand " + o3$
                End If
                If Len(g$) = 4 Then r$ = o4$: Goto finished
        End If
        If L &gt;= 5 Then
                v$ = Right$(g$,5)
                o5$ = ConvertTwoNumbers(Left$(v$,2),e$(),h$(),t$()) + " thousand " + o3$
                If Len(g$) = 5 Then r$ = o5$:Goto finished
        End If
        If L &gt;=6 Then
                v$ = Right$(g$,6)
                o6$ = ConvertThreeNumbers(Left$(v$,3),e$(),h$(),t$()) + " thousand "  + " "+o3$
                If Len(g$) = 6 Then r$ = o6$:Goto finished
        End If
        If L &gt;= 7       Then
                v$ = Right$(g$,7)
                o7$ = e$(Val(Left$(v$,1))) + " million " + ConvertThreeNumbers(Mid$(v$,2,3),e$(),h$(),t$()) + " thousand " + o3$
                If Len(g$) = 7 Then r$ = o7$: Goto finished
        End If
        If L &gt;= 8       Then
                v$ = Right$(g$,8)
                o8$ = ConvertTwoNumbers(Left$(v$,2),e$(),h$(),t$()) + " million " + ConvertThreeNumbers(Mid$(v$,3,3),e$(),h$(),t$()) + " thousand " + o3$
                If Len(g$) = 8 Then r$ = o8$: Goto finished
        End If
        If L &gt;= 9       Then
                v$ = Right$(g$,9)
                o9$ = ConvertThreeNumbers(Left$(v$,3),e$(),h$(),t$()) + " million " + ConvertThreeNumbers(Mid$(v$,4,3),e$(),h$(),t$()) + " thousand " + o3$
                If Len(g$) = 9 Then r$ = o9$: Goto finished
        End If finished:
        ConvertNumberToText = r$
End Function

Function ConvertThreeNumbers(what$,e$(),h$(),t$())
        v$ = Right$(what$,3)
        v2$ = ConvertTwoNumbers(Right$(what$,2),e$(),h$(),t$())
        If Left$(v$,1) = "0" Then ConvertThreeNumbers = ConvertTwoNumbers(v$,e$(),h$(),t$())    :Exit Function
        If Val(v$) = 0 Then ConvertThreeNumbers = "":Exit Function
        If Val(Left$(v$,1)) &lt;&gt; 1 Then
                If Val(v2$) &lt;&gt; 0 Then
                        ConvertThreeNumbers = e$(Val(Left$(v$,1))) + " hundred and " + v2$
                Else
                        ConvertThreeNumbers = e$(Val(Left$(v$,1))) + " hundred " + v2$
                End If
        Else
                If Val(v2$) &lt;&gt; 0 Then
                        ConvertThreeNumbers = " hundred and " + v2$
                Else
                        ConvertThreeNumbers = " hundred " + v2$
                End If
        End If
End Function

Function ConvertTwoNumbers(what$,e$(),h$(),t$())
        v$ = Right$(what$,2)
        If Val(v$) &gt;=11 And Val(v$) &lt;= 19 Then
                ConvertTwoNumbers = t$(Val(v$)-10)
        Else
                If Val(Right$(v$,1)) &lt;&gt; 0 Then
                        ConvertTwoNumbers = h$(Val(Left$(v$,1))) + "-" + e$(Val(Right$(v$,1)))
                Else
                        ConvertTwoNumbers = h$(Val(Left$(v$,1)))
                End If
        End If
End Function

Example LotusScript: CurrencyToWords function
Submitted by Paul Bent, Northwind IT Systems

fCurrencyToWords converts any currency value to text, adds the currency name and handles cents in nn/100 format. It can be adapted to support multiple languages.

Public Function fCurrencyToWords(Byval curValue As Currency, _
Byval strCurrency As String) As String

     '--- Converts a monetary value to words

     '--- Parameters
     '     [In]
     '     curValue the monetary value to be converted. Note the calling proc
     '          must use CCur() if the value to be passed is not currency data type
     '     strCurrency name of the currency such as dollars or pounds
     '          function assumes it is plural

     '--- Return value
     '     returns the value in words else an empty string if an error occurred

     '--- Author Paul Bent, Northwind IT Systems
     '--- Contact paulbent@nothwindit.co.uk

     'Constants representing conversion strings
     'Note these could be declared as Public and moved into a LSS file
     'There could be several versions of the LSS file to provide multiple
language support
     Const W_MILL = "million"
     Const W_THOU = "thousand"
     Const W_HUND = "hundred"
     Const W_1 = "one"
     Const W_2 = "two"
     Const W_3 = "three"
     Const W_4 = "four"
     Const W_5 = "five"
     Const W_6 = "six"
     Const W_7 = "seven"
     Const W_8 = "eight"
     Const W_9 = "nine"
     Const W_10 = "ten"
     Const W_11 = "eleven"
     Const W_12 = "twelve"
     Const W_13 = "thirteen"
     Const W_14 = "fourteen"
     Const W_15 = "fifteen"
     Const W_16 = "sixteen"
     Const W_17 = "seventeen"
     Const W_18 = "eighteen"
     Const W_19 = "nineteen"
     Const W_20 = "twenty"
     Const W_30 = "thirty"
     Const W_40 = "forty"
     Const W_50 = "fifty"
     Const W_60 = "sixty"
     Const W_70 = "seventy"
     Const W_80 = "eighty"
     Const W_90 = "ninety"

     Dim lngValue As Long               'Integer portion of curValue
     Dim intNum As Integer               'Digit set being processed in integer portion of
curValue
     Dim intC1 As Integer               'Counter for parsing loop
     Dim strTmp1 As String               'Temp string buffer
     Dim strTmp2 As String               'Temp string buffer
     Dim strRtn As String               'Buffer to build the return string

     'Check curValue is not zero or negative
     If curValue <= 0 Then Exit Function

     'Get the integer portion of curValue
     lngValue = Int(curValue)

     'Handle cents
     strRtn = Format$(100 * (curValue - lngValue), "00") & "/100"

     'Handle the integer portion
     If lngValue > 0 Then
          'Need to insert " and " before the cents
          If Len(strCurrency) = 0 Then
               'No currency name to be inserted
               strRtn = " and " & strRtn
          Else
               'Currency name to be inserted, check the plurality
               'Might need changes here if not English language
               If lngValue = 1 Then
                    'Currency should be singular
                    If Right$(strCurrency, 1) = "s" Then
                         strCurrency = Left$(strCurrency, Len(strCurrency) - 1)
                    End If
               End If
               'Insert the currency name
               strRtn = strCurrency & " and " & strRtn
          End If

          'Zero prefil to ensure digits processed are in sets of three
          strTmp1 = "000" & Cstr(lngValue)

          'Process chars in strTmp in sets of three from least to most significant
          Do While Isnumeric(strTmp1) And Val(strTmp1) > 0

               'Handle tens and units
               'Convert to numeric
               intNum = Val(Right$(strTmp1, 2))
               'Remove them from the temp buffer
               strTmp1 = Left$(strTmp1, Len(strTmp1) - 2)
               'Initialize the second buffer
               strTmp2 = ""
               'Convert to word
               If intNum > 9 And intNum < 20 Then
                    Select Case intNum - 9
                    Case 1  strTmp2 = W_10 & " " & strTmp2
                    Case 2  strTmp2 = W_11 & " " & strTmp2
                    Case 3  strTmp2 = W_12 & " " & strTmp2
                    Case 4  strTmp2 = W_13 & " " & strTmp2
                    Case 5  strTmp2 = W_14 & " " & strTmp2
                    Case 6  strTmp2 = W_15 & " " & strTmp2
                    Case 7  strTmp2 = W_16 & " " & strTmp2
                    Case 8  strTmp2 = W_17 & " " & strTmp2
                    Case 9  strTmp2 = W_18 & " " & strTmp2
                    Case 10  strTmp2 = W_19 & " " & strTmp2
                    End Select
               Elseif intNum > 0 Then
                    Select Case Val(Right$(Cstr(intNum), 1))
                    Case 1  strTmp2 = W_1 & " " & strTmp2
                    Case 2  strTmp2 = W_2 & " " & strTmp2
                    Case 3  strTmp2 = W_3 & " " & strTmp2
                    Case 4  strTmp2 = W_4 & " " & strTmp2
                    Case 5  strTmp2 = W_5 & " " & strTmp2
                    Case 6  strTmp2 = W_6 & " " & strTmp2
                    Case 7  strTmp2 = W_7 & " " & strTmp2
                    Case 8  strTmp2 = W_8 & " " & strTmp2
                    Case 9  strTmp2 = W_9 & " " & strTmp2
                    End Select
               End If
               If intNum >= 20 Then
                    Select Case Left$(Cstr(intNum), 1)
                    Case "1"  strTmp2 = "Error" & " " & strTmp2
                    Case "2"  strTmp2 = W_20 & " " & strTmp2
                    Case "3"  strTmp2 = W_30 & " " & strTmp2
                    Case "4"  strTmp2 = W_40 & " " & strTmp2
                    Case "5"  strTmp2 = W_50 & " " & strTmp2
                    Case "6"  strTmp2 = W_60 & " " & strTmp2
                    Case "7"  strTmp2 = W_70 & " " & strTmp2
                    Case "8"  strTmp2 = W_80 & " " & strTmp2
                    Case "9"  strTmp2 = W_90 & " " & strTmp2
                    End Select
               End If

               'Handle hundreds
               intNum = Val(Right$(strTmp1, 1))
               strTmp1 = Left$(strTmp1, Len(strTmp1) - 1)
               If intNum > 0 Then
                    If Len(strTmp2) = 0 Then
                         strTmp2 = W_HUND & " " & strTmp2
                    Else
                         strTmp2 = W_HUND & " and " & strTmp2
                    End If
                    Select Case intNum
                    Case 1  strTmp2 = W_1 & " " & strTmp2
                    Case 2  strTmp2 = W_2 & " " & strTmp2
                    Case 3  strTmp2 = W_3 & " " & strTmp2
                    Case 4  strTmp2 = W_4 & " " & strTmp2
                    Case 5  strTmp2 = W_5 & " " & strTmp2
                    Case 6  strTmp2 = W_6 & " " & strTmp2
                    Case 7  strTmp2 = W_7 & " " & strTmp2
                    Case 8  strTmp2 = W_8 & " " & strTmp2
                    Case 9  strTmp2 = W_9 & " " & strTmp2
                    End Select
               End If

               'Handle thousands and greater
               If Not Len(strTmp2) = 0 Then
                    Select Case intC1 / 3
                    Case Is < 1  strRtn = strTmp2 & strRtn
                    Case  1
                         If Instr(1, strRtn, W_HUND, 5) > 0 Then
                              strRtn = strTmp2 & W_THOU & " " & strRtn
                         Else
                              strRtn = strTmp2 & W_THOU & " and " & strRtn
                         End If
                    Case Else
                         If Instr(1, strRtn, W_HUND, 5) > 0 Or _
                         Instr(1, strRtn, W_THOU, 5) > 0 Then
                              strRtn = strTmp2 & W_MILL & " " & strRtn
                         Else
                              strRtn = strTmp2 & W_MILL & " and " & strRtn
                         End If
                    End Select
               End If

               'Increment the digit counter
               intC1 = intC1 + 3
          Loop

          'Return value
          fCurrencyToWords = strRtn
     End If

End Function

Example LotusScript: Conditional Automatic Entry

'Written by Mr Rhodri Edwards, 1996
'(C) Copyright 1996 by Rhodri Edwards
'
'Permission is granted to freely copy this script in
'electronic form, 'or to print for personal use. It may be
'use in any Approach database, but may not be distributed
'for profit either by itself or as part of a collection or
'database.
'
'Disclaimer: This script is provided as is
'without any express or implied warranties.
'The author assumes no responsibility for errors or
'omissions, or for damages resulting from the use of the information contained
'herein.
'
'This script tests the value held within a text box and if it equals
'"Do it" assigns a value to 5 other fields and finishes by tabbing to
'another fieldbox.

 If CurrentView.Body.Field0.Text = "Do it" Then
      CurrentView.Body.Field1.Text = "Test1"
      CurrentView.Body.Field2.Text = "Test2"
      CurrentView.Body.Field3.Text = "Test3"
      CurrentView.Body.Field4.Text = "Test4"
      CurrentView.Body.Field5.Text = "Test5"
      CurrentView.Body.Field6.SetFocus
End If

'This example assumes that all the fields you want to set are not on a
'repeating panel.  If they are replace "Body" with "RepeatingPanel".
'
'Field* is where you would put the name of the fieldbox you want to
'set/move to.
'
'The script itself should be attached to the "LostFocus" event of
'field0 (i.e. the field the user fills in).
'
'One important thing to note is:
'
'SetFocus won't work if you are moving from a repeating panel to a body
'part of a form.  Instead the field you move to "hovers" in the top
'left hand corner of the screen.

Example LotusScript: Create a delimited text file from two fields

'The purpose of this script is to create an delimited text file from 2 fields
'in a database. Field_1 is a text value to output in every record. Field_2 is
'a grouping field. Many records with similiar attributes can be this value.
'The database is sorted by Field_2 prior to Script execution. This script is
'used to export invoice transactions to a mainframe Cost of Sales and Cost of
'Production application. The mainframe input format requires a batch header
'(Field_2) to preceed each group of detail records (Field_1).  In my App
'Field_1 and Field_2 are calculated fields. I'm testing another variety of
'this as an audit trail or event log on users activity.

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim fileNum As Integer, I As Integer
        Dim N As Long
        Dim fileName As String, many_rec As String, few_rec As String, inv_test As String

        fileNum% = Freefile()
        fileName$ = Curdrive () +"\WINDOWS\TEMP\DATA.TXT"
        N=CurrentWindow.NumRecordsFound
        many_rec$=Source.Field_1.Text
' Field_1 is an Object name for detail text string to output
        few_rec$=Source.Field_2.Text
        batch_test$=Source.Field_2.Text
' Field_2 is an Object name for batch section text string to output
' Write out the data.

        Open fileName$ For Output As fileNum%
        Write #fileNum%, few_rec$
        Write #fileNum%, many_rec$
' Writes First records information
        I%=I%+1
        Do While I%<= N
                CurrentApplication.ActiveDocWindow.NextRecord
                I%=I%+1
                many_rec$=Source.Field_1.Text
                few_rec$=Source.Field_2.Text
                If few_rec$<>batch_test$ Then Gosub ResetHeader
                Write #fileNum%, many_rec
        Loop
        Close fileNum%
        Goto Skip
ResetHeader:
        Write #fileNum%, few_rec$
        section_test$=Source.Field_2.Text
        Return
Skip:
End Sub

'Written by Jerry Sikes <Unisource Converting>, 1996
'(C) Copyright 1996 by Jerry Sikes
'
'Permission is granted to freely copy this script in electronic form,
'or to print for personal use. It may be use in any Approach database,
'but may not be distributed for profit either by itself or as part of
'a collection or database.
'
'Disclaimer: This script is provided as is without any express or
'implied warranties. The author assumes no responsibility for errors or
'omissions, or for damages resulting from the use of the information contained
'herein, or your own version containing your desires for the usage of the
'script.

Example LotusScript: Disable the action bar, smart icons, status bar and view tabs

'Written by Jerry Sikes, 1996
'Copyright 1996 by Jerry Sikes
'
'Permission is granted to freely copy this script in electronic form,
'or to print for personal use. It may be use in any Approach database.
'This script was electronically copied in part from Script Editor -
'Help - Approach Objects - Contents - Approach LotusScript Reference
'- All Elements A-Z - ActionBarVisible property - Example
'
'Disclaimer: This script is provided as is without any express or implied
'warranties. The author assumes no responsibility for errors or omissions,
'or for damages resulting from the use of the information contained herein.
'
'How to use
'This script allows you to disable the action bar, smart icons, status bar
'and view tabs at program opening. Used in conjunction with a custom menu,
'you can effectively disable a user from any Approach action or event
'that you do not allow. This example is used in Globals - Initialize.
'However, it could be modified as a sub and used conditionally to control
actions
'on a form until the action is complete.

(Globals)

Sub Initialize
        'This program performs the same function
        'that the Clean Screen menu item on the Edit
        'menu does.
        CurrentWindow.Redraw=False   'Turn off redraw temporarily
        'Turn off each bar.
        CurrentWindow.ActionBarVisible=False
        CurrentWindow.IconBarVisible=False
        CurrentWindow.StatusBarVisible=False
        CurrentWindow.ViewTabVisible=False
        CurrentWindow.Redraw=True   'Turn it back on
        CurrentWindow.Repaint   'Now repaint the window.
End Sub

'Of interest in this example are the Redraw and Repaint properties. These
'following sub routines work great to freeze the screen during macro
'processing or can be used within other scripts during execution. Named sub
'routines can be called using the run command within the Macro Editor.

Sub Freeze_screen
        CurrentWindow.Redraw=False   'Turn off redraw temporarily
End Sub

Sub Redraw_screen
        CurrentWindow.Redraw=True   'Turn it back on
        CurrentWindow.Repaint   'Now repaint the window.
End Sub

Example LotusScript: Serialized invoice number on billing

   'Written by Jerry Sikes, 1996
   '(C) Copyright 1996 by Jerry Sikes
   '
   'Permission is granted to freely copy this script in electronic form,
   'or to print for personal use. It may be use in any Approach database,
   'but may not be distributed for profit either by itself or as part of
   'a collection or database.
   '
   'Disclaimer: This script is provided as is without any express or implied
   'warranties. The author assumes no responsibility for errors or omissions,
   'or for damages resulting from the use of the information contained herein

In my invoicing application, it is necessary to assign a serialized invoice
number when the invoice is billed. The invoice record was created when the
order was taken. If this was a make to order job, it could be 1 week per
operation before it is ready to bill. I created an invoice number log table
with the following fields;inv_no1(serial increment);sub_number(join field
with invoice record-unique to each invoice) plus fields with creation
formulas to set account number,po number, bill and ship name from the invoice
record table. On my main data entry screen, I did not want to leave a
fieldbox from the join table, that could accidently be focused on. Accidental
click or tab would create a new record in the join table. So I hid it behind
a text box and took it out of the tab order. I attached this script to the
invoice record inv_no fieldbox.

Sub Gotfocus(Source As Fieldbox)
        If currentview.body.inv_no.text ="" Then
'Checks if an invoice number is already assigned
                If currentview.repeatingpanel.ITEMNO.TEXT="" Then
'Checks if repeating panel(invoice line items) has at least one item
                        Messagebox "Invoice not ready",MB_OK + MB_ICONSTOP,"Finish Invoice"
                Else
                        If source.inv_date.text<>"" Then
'Forces user to enter invoice date prior to assignment of invoice number
                                currentview.body.inv_no1.SetFocus
'This moves focus to the hidden empty field from invoice number assignment table
'Approach, by definition, creates a new record in the join table when
'typing into an empty join field
                                currentview.body.inv_no.text=currentview.body.inv_no1.text
'new invoice number is set in invoice record from hidden join field
                                currentview.body.terms.SetFocus
'moves focus to invoice terms field
                        Else
                                Messagebox "Enter Invoice Date", MB_OK + MB_ICONEXCLAMATION, "Invalid Date"
                                currentview.body.inv_date.SetFocus
                        End If
                End If
        Else
                Messagebox "Invalid Operation" , MB_OK + MB_ICONEXCLAMATION, "INVOICE NUMBER ALREADY EXISTS"
        End If

End Sub

I've sent this script to show syntax use of currentview.body.objectname.text
The ".text"  syntax is used regardless if the field type is text, numeric or
date. If you want to manipulate a numeric field with a mathematical
operation, then use
Val(currentview.body.numeric_field_object_name.text)*0.6. Date fields have
similiar script operators.
If your object name(field name) is two words use a ~(tilde) in place of the
space.
currentview.body.Account~Number.text

Jerry Sikes
Unisource Converting

Example LotusScript: Draw rounded rectangles to size (for envelope windows)

'Written by Jerry Sikes, 1996
   '(C) Copyright 1996 by Jerry Sikes, Unisource Conveting
   '
   'Permission is granted to freely copy this script in electronic form,
   'or to print for personal use. It may be use in any Approach database,
   'but may not be distributed for profit either by itself or as part of
   'a collection or database.

My company produces envelopes, roughly 8 million per day. Many envelopes have
windows cut into them so address, postal barcodes or other information may be
displayed. We have about 250 double window dies of various configurations.
LotusScript made it possible to accurately draw rounded rectangles of each
die, exactly to scale and proportion. The first time a die is input, we use
the the size and positional data based on that job. Approach, through
formulas, determines absolute coordinates. This is used to compare requests
for the same proportions, but perhaps a differant positioning of the die
relative to the left and bottom of an envelope. I will not get into the
search criteria used in finds. This script shows how to manipulate screen
objects locations and size. For referance, 1 inch = 1440 twips. A twip
(TWentIeth of a Point) is a measurement equal to 1/1440th of an inch. For
referance, input for each window (2 per double die) is, width, height,
distance from left edge of envelope to left edge of each die and distance
from bottom of envelope to bottom of each die as numeric 2.4. The
intersection of the left edge of the left most die and the bottom of the
lowest die become coordinates (0, 0). The coordinates for the four corners
for each window are then calculated. (X_1, X_2, X_3, X_4. Y_1, Y_2, Y_3, Y_4)

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim ln As LINEOBJECT
        Dim rb As ROUNDRECT
        Set rb=Source.body.ObjRoundRect1
        rb.Left = 2880+(1440*Val(SOURCE.x_1.TEXT))
'The 2880 keeps the result no closer than 2 inches from the left of the form
        rb.Top = 8640-(1440*Val(source.y_1.TEXT))
'8640 is 6 inches from top
        rb.Width = 1440*Val(source.SIZE_WIDTH_1.TEXT)
        rb.Height = 1440*Val(SOURCE.SIZE_HEIGHT_1.TEXT)
        rb.NamedStyle="Default"
        rb.Background.Color.SetRGB(COLOR_CORNFLOWER)
        Set rb=Source.body.ObjRoundRect2
        rb.Left = 2880+(1440*Val(SOURCE.x_3.TEXT))
        rb.Top = 8640-(1440*Val(source.y_3.TEXT))
        rb.Width = 1440*Val(source.SIZE_WIDTH_2.TEXT)
        rb.Height = 1440*Val(SOURCE.SIZE_HEIGHT_2.TEXT)
        rb.NamedStyle="Default"
        rb.Background.Color.SetRGB(COLOR_CORNFLOWER)
        Set ln=Source.body.objLine2
        ln.Left = 2880
        Set ln=Source.body.objLine3
        ln.Top = 8640
'objLine2 & 3 are my 0,0 referance lines
        Set ln=Source.body.objLine21
        If Val(source.x_1.text)>Val(source.x_3.text) Then
                ln.Left = 2880+(1440*Val(source.x_1.text))
        Else
                ln.Left = 2880+(1440*Val(source.x_3.text))
        End If
        Set ln=Source.body.objLine31
        If Val(source.y_2.text)>Val(source.y_4.text) Then
                ln.Top = 8640-(1440*Val(source.y_2.text))
        Else
                ln.Top = 8640-(1440*Val(source.y_4.text))
        End If
'objLine21 and 31 float as necessary
End Sub

Example LotusScript: Using keydown event to automatically enter or modify a date

Written by Jerry Sikes <Unisource Converting>, 1997
(C) Copyright 1997 by Jerry Sikes

Permission is granted to freely copy this script in electronic form,
or to print for personal use. It may be used in any Approach database,
but may not be distributed for profit either by itself or as part of
a collection or database.

Disclaimer: This script is provided as is without any express or
implied warranties. The author assumes no responsibility for errors or
omissions, or for damages resulting from the use of the information contained
herein, or your own version containing your desires for the usage of the
script.

The scheduled manufacturing date of an order in our plant is always set to a
Monday's date. (Scheduled week of) All orders for that week are numerically
sequenced. The main table for this app is a Lotus Notes view whose selection
formula returns only unscheduled orders. The scheduler uses two Approach
applications in a tiled top - bottom view. The second app is the same Notes
database but based on a Notes view with a selection formula that returns only
scheduled orders. The objective of the script is to allow the scheduler to
press the + key to return this Mondays date and additional + keystrokes
increments the date by 7 days. This script allows normal date typing. It does
not intefer with the use of the spacebar to set the current month-day-year.
It does not intefer with normal tabbing through the field. Dates in scripting
are variants, My source as fieldbox is FoldStartDate, a date field. But when
scripting reads a fieldbox, it reads the text representation of the date,
thus Datevalue() must be used to convert the text to variant type.
DateNumber(Year,Month,Day) is used to modify the date. Year, month and day
are integer values and can be additive. DateNumber(97+1,12-1,14+6)=11/20/98.

Sub Keydown(Source As Fieldbox, Charcode As Long, Repeats As Integer, Flags As Integer, Overridedefault As Integer)
        Dim td As Variant,wd As Integer,ay As Integer, am As Integer, ad As Integer

        If charcode=107 Then
                currentview.body.fold_id.SetFocus
'I have to temporarily move out of the source field to check/change its contents
                If source.text="" Then
'True condition when FoldStartDate is blank
                        td = Date$   'Current system date
                        wd%=Weekday(td)
                        ay%=Year(td)
                        am%=Month(td)
                        ad%=Day(td)
                        source.text=Datenumber(ay% ,am%, (ad%-(wd%-2)))
                        source.setfocus
                Else
'False condition when FoldStartDate is not blank
                        td = Datevalue(source.text)
                        wd%=Weekday(td)
                        ay%=Year(td)
                        am%=Month(td)
                        ad%=Day(td)
                        source.text=Datenumber(ay% ,am%, (ad%+7))
                        source.setfocus

                End If

        End If

End Sub

'---------------------------------------------------------------------------
This is the next field in the tab order is called ReadyDate, which indicates
the ready date at plant after all production operations. It's keydown script
is similiar to the above script but initially uses the FoldStartDate(if not
blank) with a + keystroke and then increments by one day each additional +
keystroke.

Sub Keydown(Source As Fieldbox, Charcode As Long, Repeats As Integer, Flags As Integer, Overridedefault As Integer)
        Dim td As Variant,wd As Integer,ay As Integer, am As Integer, ad As Integer

        If charcode=107 Then
                currentview.body.fold_id.SetFocus
                If source.text="" Then
                        If currentview.body.FoldStartDate.text="" Then
                                td = Date$
                                wd%=Weekday(td)
                                ay%=Year(td)
                                am%=Month(td)
                                ad%=Day(td)
                                source.text=Datenumber(ay% ,am%, ad%)
                                source.setfocus
                        Else
                                td = Datevalue(currentview.body.FoldStartDate.text)
                                wd%=Weekday(td)
                                ay%=Year(td)
                                am%=Month(td)
                                ad%=Day(td)
                                source.text=Datenumber(ay% ,am%, ad%)
                                source.setfocus
                        End If

                Else

                        td = Datevalue(source.text)
                        wd%=Weekday(td)
                        ay%=Year(td)
                        am%=Month(td)
                        ad%=Day(td)
                        source.text=Datenumber(ay% ,am%, (ad%+1))
                        source.setfocus

                End If

        End If

End Sub

Example LotusScript: Launch multiple Approach applications at start-up

Written by Jerry Sikes <Unisource Converting>, 1997
(C) Copyright 1997 by Jerry Sikes

Permission is granted to freely copy this script in electronic form,
or to print for personal use. It may be used in any Approach database,
but may not be distributed for profit either by itself or as part of
a collection or database.

Disclaimer: This script is provided as is without any express or
implied warranties. The author assumes no responsibility for errors or
omissions, or for damages resulting from the use of the information contained
herein, or your own version containing your desires for the usage of the
script.

This global script is attached to an open macro in Approach which forces a
second application to open with the first eveytime. It also checks to see if
the app is aready open.

Sub OpenMyDocs
        Dim MyDoc As String
        Dim NumDocs As Integer
        Dim Docs As Variant
        Dim MyOpen As String
        Dim fname As String
        If(CurrentApplication.Documents.IsEmpty=False) Then
                NumDocs = CurrentApplication.Documents.count
                For i = 0 To NumDocs-1
                        Print CurrentApplication.Documents(i).Name
                        If CurrentApplication.Documents(i).Name="Scheduling" Then
                                MyOpen="True"
                        End If
                Next
        End If
        If MyOpen ="True" Then
                CurrentApplication.Scheduling.activate
        Else
                Set CurrentDocument= CurrentApplication.OpenDocument("Scheduling.apr","f:\orders")
        End If
        CurrentApplication.By_Assignment.activate
        CurrentApplication.ApplicationWindow.domenucommand(IDM_TILE_TOP_BOTTOM)
End Sub

Example LotusScript: Syntax use of currentview.body.objectname.text

 'Written by Jerry Sikes, 1997
   '(C) Copyright 1997 by Jerry Sikes
   '
   'Permission is granted to freely copy this script in electronic form,
   'or to print for personal use. It may be use in any Approach database,
   'but may not be distributed for profit either by itself or as part of
   'a collection or database.
   '
   'Disclaimer: This script is provided as is without any express or implied
   'warranties. The author assumes no responsibility for errors or omissions,
   'or for damages resulting from the use of the information contained herein

I've sent this script to show syntax use of currentview.body.objectname.text
The ".text"  syntax is used regardless if the field type is text, numeric or
date. If you want to manipulate a numeric field with a mathematical operation,
then use Val(currentview.body.numeric_field_object_name.text)*0.6. Date fields
have similiar script operators.
If your object name(field name) is two words use a ~(tilde) in place of the
space.

currentview.body.Account~ Number.text

Example LotusScript: Script Launch at Record Change Event

 'Written by Jerry Sikes, 1997
   '(C) Copyright 1997 by Jerry Sikes
   '
   'Permission is granted to freely copy this script in electronic form,
   'or to print for personal use. It may be use in any Approach database,
   'but may not be distributed for profit either by itself or as part of
   'a collection or database.
   '
   'Disclaimer: This script is provided as is without any express or implied
   'warranties. The author assumes no responsibility for errors or omissions,
   'or for damages resulting from the use of the information contained herein

I've been stumbling along with scripting and stumped on how to have a script
launch as I browse through each record. Finally I stumbled across how. The
event is RecordChange and available only as a DocWindow event.
In the script window, under objects, the objects are divided into three
sections. Globals, your application and Approach itself. The last object
available as you drill down your application objects is DocWindow. Care must
be used with attached script. The script will run regardless of the form your
on. If - Then - Else statements must be used to control action for a singular
form. I have had success with the following:

If currentview.name="My Form Name" Then
'add true action here
Else
'add false action here. False action is optional.

End If

The script executes with each change in record number, regardless of the
method of change.

Example LotusScript: Check Existing Data and ask for details for new record

 'Written by Ian Curry <Jist Enterprises Limited>, 1997
     '(C) Copyright 1997 by Ian Curry
     'Permission is granted to freely copy this script in electronic form,
     'or to print for personal use. It may be use in any Approach database,
     'but may not be distributed for profit either by itself or as part of
     'a collection or database.
     '
     'Disclaimer: This script is provided as is without any express or
     'implied warranties. The author assumes no responsibility for errors or
     'omissions, or for damages resulting from the use of the information contained
     'herein, or your own version containing your desires for the usage of the
     'script.
     'Please mail any improvements to the Approach Users Mailing List

I have created a credit control and diarying database, database names in
CAPS :

There are a number of different accounts (ACCTMAIN) each with a number of
transactions (TRANMAIN), each transaction can have one action (CURRENT) a
current database and then old actions are transferred to history.  There
are also notes (CURRNOTE) that can be allocated to an account, action or
transaction and which are also later transferred to history.

The main working screen "Account Action Details" is based on the
ACCTMAINDatabase and has 2 repeating panels - CURRNOTE and TRANMAIN.  In
the repeating panel for the TRANMAIN database (transacspanel) users can
enter one action into the CURRENT database.

The CURRENT database has a STD Ref, Action Reference, Diary Date, Raised
Date ans Completed Date.

The following script checks that the diary date is after TODAY() and if it
isn't checks with the user to see if they want to change it.
Then if the standard reference is 13, a new record is created in the
CURRNOTE database and will appear in the other repeating panel.

Sub Lostfocus(Source As Fieldbox)
        Dim DDATE As Variant
        INITRAN=source.transacspanel.Transaction~ Reference.text
        ANUMBER=currentview.body.Account~ Number.text
        DDATE = source.transacspanel.Diary~ Date.text
        AREF= source.transacspanel.Action~ Reference.text
If DDATE="" Then
        Messagebox ("Please complete a Diary Date")
        source.transacspanel.Diary~ Date.setfocus
        End
End If
        If Datenumber(Year(DDATE) , Month(ddate) , Day(DDATE)) < Datenumber(Year(Today()) , Month(Today()) , Day(Today())) Then
                answer% = Messagebox("The Diary Date that you have entered is before today - would you like to change it ?",36,"CCS Warning")
                If answer% = 6 Then
                        source.transacspanel.Diary~ Date.text=""
                        source.transacspanel.Diary~ Date.setfocus
                End If
        Else
                If source.transacspanel.STDAction.text="13" Then
                        NEWNOTES = Inputbox$( "Please Enter the Notes","CCS Quick Note Entry",,250,250)
                        If NEWNOTES ="" Then
                                End
                        Else
                                answer2% = Messagebox("Does this note only apply to this transaction?",3+32+256,"CCS")
                                If answer2%=2 Then
                                        source.transacspanel.STDAction.setfocus
                                        End
                                End If
                                Dim con As New Connection
                                Dim qry As New Query
                                Dim rs As New ResultSet
                                If (con.connectto ("dbase IV")<>False) Then
                                        Set qry.connection = con
                                                    qry.tablename="m:\group\ba\ccs\currnote.dbf"
                                        Set rs.query = qry
                                        If ((rs.execute)<>False) Then
                                                rs.addrow
                                                NewValue=rs.setvalue(1,ANUMBER)
                                                If answer2%=6 Then NewValue=rs.setvalue(2,INITRAN)
                                                End If
                                                NewValue=rs.setvalue(3,Today())
                                                Newvalue=rs.setvalue(4,NEWNOTES)
                                                NewValue=rs.setvalue(5,AREF)
                                                rs.updaterow
                                        End If
                                End If
                                con.disconnect
                        End If
                End If
                CurrentApplication.ApplicationWindow.DoMenuCommand(IDM_REFRESH)
        End If
End Sub

Example LotusScript: Lotus Notes SQL script

 'Written by Jerry Sikes, 1997
   '(C) Copyright 1997 by Jerry Sikes
   '
   'Permission is granted to freely copy this script in electronic form,
   'or to print for personal use. It may be use in any Approach database,
   'but may not be distributed for profit either by itself or as part of
   'a collection or database.
   '
   'Disclaimer: This script is provided as is without any express or implied
   'warranties. The author assumes no responsibility for errors or omissions,
   'or for damages resulting from the use of the information contained herein

This script is connected to a button for testing. My production order system
exist in Lotus Notes. This script pull values from Notes into an Approach
invoicing application. The final version of this script will bypass the
inputbox and pull all records assigned to the user as a batch rather than
this one at a time version.

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)      
 
        Dim CON As New CONNECTION
        Dim QRY As New QUERY
        Dim RS As New RESULTSET
        jobcode = Inputbox$("Please Enter Job Number?","Notes Query",,144,144)
        If jobcode = "" Then Goto Finish
        
        If (CON.connectto("Lotus Notes - Workspace", , ,"Production
Orders.nsf")<>False)Then
‘the actual NSF is prod_env.nsf. Approach recognizes the formal database
title as shown if you opened a Lotus Notes Workspace from Approach. This
syntax forces Approach to ask for a log in, using the current user id.
The session remains active until you quit Approach, even though I’m forcing
a disconnect at the end of this script.

                Set QRY.CONNECTION = CON
                QRY.TABLENAME = "SPECIALS" ‘ SPECIALS is a form name. A view name could have been used
                QRY.SQL = "SELECT *  FROM""Production Orders.nsf\SPECIALS"" SPECIALS WHERE (SPECIALS.job_no = '"+jobcode+"')"
                Set rs.query = qry
                If((rs.execute)<>False)Then
                        currentapplication.applicationwindow.domenucommand (IDM_NEWREC)
                        currentview.body.branchno.text = RS.GetValue("branch")
                        If RS.GetValue("order_suffix") = "" Then
                                currentview.body.PONUMBER.text = RS.GetValue("po_number")
                        Else
                                currentview.body.PONUMBER.text = RS.GetValue("po_number") +"-" + RS.GetValue("order_suffix")
                        End If
                        currentview.body.recieved_from.text = RS.GetValue("salesman")
                        currentview.body.SHIP_VIA11.text = RS.GetValue("ship_route")
                        currentview.body.SHIP_TERMS1.text = RS.GetValue("freight_type")
                        currentview.body.comments1.text = Left(RS.GetValue("cnr_card_type") +" "+ RS.GetValue("branch_specs"),110)
                        currentview.body.comments11.text = jobcode + Chr(10) + RS.GetValue("special_ship")
                        currentview.repeatingpanel.ITEMNO.text = RS.GetValue("prod_no")
                        currentview.repeatingpanel.rp_qty.text = RS.GetValue("quantity")
                        currentview.repeatingpanel.rp_sell.text =  RS.GetValue("branch_cost_per_m")
                        currentview.body.req_date.text = RS.GetValue("req_ship_date")
                        currentview.body.ship_name.text = RS.GetValue("ship_name")
                        currentview.body.sn2.text = RS.GetValue("sn2")
                        currentview.body.sn3.text = RS.GetValue("sn3")
                        currentview.body.ship_addrs.text = RS.GetValue("ship_addrs")
                        currentview.body.ship_addrs2.text = RS.GetValue("ship_addrs2")
                        currentview.body.ship_city.text = RS.GetValue("ship_city")
                        currentview.body.ship_st.text = RS.GetValue("ship_st")
                        currentview.body.ship_zip.text = RS.GetValue("ship_zip")
                        currentview.body.ship_attn.text = RS.GetValue("ship_attn")
                        currentview.body.ord_date11.text = RS.GetValue("ord_date")
                        currentview.body.ord_date11.SetFocus
                        currentapplication.applicationwindow.domenucommand (IDM_NEXT)  ‘commits record
                Else
                        Messagebox "The Item was not found. Please Try Again", 0 + 16,"Notes Query"
                End If
                CON.disconnect          
        End If  
Finish:
End Sub

Example LotusScript: Read in the Approach User Section in win.ini
This script will search the win.ini for an Approach User Section, read the value of the next line and place it into a variable field. You can then use this variable field in new record creation formula to tag who input the record.

Sub UserID
     'Written by Jerry Sikes , 1997
     '(C) Copyright 1997 by Jerry Sikes
     '
     'Permission is granted to freely copy this script in electronic form,
     'or to print for personal use. It may be use in any Approach database,
     'but may not be distributed for profit either by itself or as part of
     'a collection or database.
     '
     'Disclaimer: This script is provided as is without any express or
     'implied warranties. The author assumes no responsibility for errors or
     'omissions, or for damages resulting from the use of the information contained
     'herein, or your own version containing your desires for the usage of the
     'script.

     'Add a section to a workstations win.ini
     '[Approach User]
     'user name
     
     'create a variable field named id_var (type:text)
     'make sure this field is present on the start up Form
     'run this script via the OPEN macro, which will self execute at apr start.
     
     Dim fileName As String, fileNum As Integer
     Dim id As String, Label As String, MyFlag As String
     fileNum% = Freefile()                'reserve the next file open number
     fileName$ = "c:\windows\win.ini"     'name the target
     MyFlag = "false"   'set the flag
     Open fileName$ For Input As fileNum% 'open the file for read only
     Do Until Eof(fileNum%)               'check every line
          Line Input #fileNum%, Label     'input current line
          If MyFlag = "true" Then         'test MyFlag
               id = Label                 'Set id to one line below the True flag operation
               MyFlag = "false"           'reset MyFlag
          End If
          If Label = "[Approach User]" Then  'Looking for section
               MyFlag= "true"             'found it
          End If
     Loop
     currentview.body.id_var.text = id    'set variable field with supplied name
     Close fileNum%                       ' close the win.ini
End Sub

Example LotusScript: Handling Approach Object Errors
This example stops an error generation when no records from a Named Find
called from within LotusScript.

In Globals --> Declarations include the following statement on a line by itself. (Help says to leave a blank space after the statement, so I typed a space before a carraige return)

     %INCLUDE "C:\LOTUS\APPROACH\APRLSERR.LSS" 

The aprlserr.lss declares as public constants some Approach specific errors.

The line "Public Const AprErrNAMEDFIND          = 11022" gives you the error handler to use within a script. Open it with a text editor and print it for future reference. 

This is an example of a button script that calls a Named Find. If no records are found, the flow of control passes to the end:

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        On Error 11022 Goto theend
        CurrentWindow.NamedFindSort = "A6"
        Exit Sub

theend:
        Print "Error"
        Exit Sub
End Sub

Example LotusScript: Error trapping and data manipulation script
I wasn't aware of the

   %INCLUDE "C:\LOTUS\APPROACH\APRLSERR.LSS"

   ... detailed by Jerry Sikes (see article 'Handling Approach Object Errors') but the following script will trap the same error whilst also containing examples of SQL commands and references to databases that are both on and off the current view

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim TRANID As String, NOTES As String
        Dim DDATE As Variant, CDATE As Variant, RDATE As Variant
        Dim AREF As Integer, ANUMBER As Integer, STDREF As Integer, NUMCURR As Integer
        If source.transacspanel.Action~ Reference.text="" Then 'ie there is no CURRENT record
                Messagebox "To enter a new action, just choose a STD Action Reference",65,"CCS Message"
                End
        Else 'this section sets all values that will be required throughout the script
                Currentwindow.redraw=False 'prevents the screen from re-drawing
                TRANID=source.transacspanel.Transaction~ Reference.text
                DDATE=source.transacspanel.Diary~ Date.text
                CDATE=Today()
                RDATE=source.transacspanel.Raised~ Date.text
                AREF=source.transacspanel.Action~ Reference.text
                ANUMBER=currentview.body.Account~ Number.text
                STDREF=source.transacspanel.STDAction.text
' This section is purely used to work out how many transactions (records in CURRENT.DBF) have the same action reference
' therefore whether to remove the note or not
                Dim con As New Connection
                Dim qry As New Query
                Dim rs As New ResultSet
                If (con.connectto ("dbase IV")<>False) Then
                        Set qry.connection = con
                        qry.tablename = "m:\group\ba\ccs\current.dbf"
                        Qry.SQL="SELECT * FROM"""& qry.tablename &"""Current WHERE (Current.""Action Reference"" = "& AREF & ")"
                        Set rs.query = qry
                        If ((rs.execute)<>False) Then
                                NUMCURR = (rs.numrows) 'number of records in current with the same action reference
                        End If
                End If
                con.disconnect
'End of section for counting transactions
'This section will delete the record from current
                If (con.connectto ("dbase IV")<>False) Then
                        Set qry.connection = con
                        qry.tablename = "m:\group\ba\ccs\current.dbf"
                        Qry.SQL="SELECT * FROM"""& qry.tablename &"""Current WHERE (Current.""Unique Transaction Reference"" = '" & TRANID & "')" 'Note the syntax for TE
                        Set rs.query = qry
                        If ((rs.execute)<>False) Then
                                rs.deleterow
                                rs.updaterow
                        End If
                        con.disconnect
                        Dim newdata(1 To 7) As Variant
                        newdata(1)=AREF
                        newdata(2)=ANUMBER
                        newdata(3)=TRANID
                        newdata(4)=DDATE
                        newdata(5)=RDATE
                        newdata(6)=CDATE
                        newdata(7)=STDREF
'And now to add a new record in actions
                        If (con.connectto ("dbase IV")<>False) Then
                                Set qry.connection = con
                                qry.tablename="m:\group\ba\ccs\actions.dbf"
                                Set rs.query = qry
                                If ((rs.execute)<>False) Then
                                        rs.addrow
                                        For i=1 To Ubound(newdata) 'Used to enter value in each
                                                                FIELD NewVAlue=rs.setvalue(i,newdata(i))
                                        Next
                                        rs.updaterow
                                End If
                        End If
                        con.disconnect
                End If
'This section creates a new record in the NOTES database and removes
from currnote if required
                If (con.connectto ("dbase IV")<>False) Then
                        Set qry.connection = con
                        qry.tablename = "m:\group\ba\ccs\currnote.dbf"
                        Qry.SQL="SELECT * FROM"""& qry.tablename &"""currnote WHERE (currnote.""Action Reference"" =" & AREF &")"
                        Set rs.query = qry
                        On Error Goto Skip 'An error will occur when there are no notes against a transaction
                        If ((rs.execute)<>False) Then
                                NOTES=rs.getvalue(rs.fieldid("Notes"))
                                RDATE=rs.getvalue(rs.fieldid("Date")) ' DDATE is Date of the NOTE
                                If NUMCURR = 1Then
                                        rs.deleterow
                                        rs.updaterow
                                Else
                                End If
                        End If
                        con.disconnect
                        Dim newdata2(1 To 5) As Variant
                        newdata2(1)=ANUMBER
                        newdata2(2)=TRANID
                        newdata2(3)=RDATE
                        newdata2(4)=NOTES
                        newdata2(5)=AREF
                        If (con.connectto ("dbase IV")<>False) Then
                                Set qry.connection = con
                                qry.tablename="m:\group\ba\ccs\notes.dbf"
                                Set rs.query = qry
                                If ((rs.execute)<>False) Then
                                        rs.addrow
                                        For i=1 To Ubound(newdata2)
                                                                NewVAlue=rs.setvalue(i,newdata2(i))
                                        Next
                                        rs.updaterow
                                End If
                        End If
                        con.disconnect
                End If
        End If
Skip : 'If there are no notes the script will continue from here
        'This little section is purely to make the screen update and NOT
SHOW deleted records !
        currentwindow.redraw=True
        CurrentApplication.ApplicationWindow.DoMenuCommand(IDM_REFRESH)
        End 'I needed this to prevent a "No Resume" error when there were no
NOTES
End Sub

I have a large application, the main screen is based on the ACCOUNTS
database and contains 2 panels : TRANSACATIONS and CURRNOTES.
Against each transaction, the user can enter ACTIONS which can be
repeated for more than one transaction.  Notes can be entered which
refer to a particular action OR transaction.

When a new action is required, the current action is transfered to
ACTNSOLD alongwith any CURRNOTES being transferred into NOTES so that
a history is maintained.

---------------------------------------------------------------------
(c) 1997 Ian Curry - Jist, All rights reserved, permission is granted
for this document to be distributed in electronic form (such as
email, newsgroups, and the WWW) as long as it is posted in its
entirety including this copyright statement, and is not distributed
for financial gain.

Example LotusScript: Creating a dot notation text file

Sub Create90
        '-----Jerry Sikes 01.21.98
        '-----This script creates a tab delimited text file
        '-----It's purpose is create a dot notation string
        '-----with day of year dot machine id dot shift
        '-----This will be used with a machine scheduling application
        '-----where I need to know if a shift is available for a
        '-----specific machine at some future date.
        '-----This script is for testing purposes only and does not
        '-----represent it's final structue.
        Dim filenum As Integer
        Dim filename As String
        Dim rec As DayRecord
        '-----I previously declare my own data type
        '-----in Globals...Declarations
        '-----Type DayRecord
        '               Machine_Shift_Block As String * 10
        '               Shift_Status As String * 3
        '       End Type
        Dim Numeric_Date As Single, Day_of_Year As Single
        Dim rs As New ResultSet
        Set rs =CurrentDocument.Tables(1).CreateResultSet()
        '-----This immediately brings in an already open, join table
        '-----This is all records from that table. Very fast
        '-----To determine the table number, I look in the Approach
        '-----file properties. The table are listed in order 0...n
        counter1% = 0
        Record_Number%=0
        filenum% = Freefile()
        filename$ = "c:\windows\temp\mach_rec.txt"
        Open filename$ For Output As filenum% 
        'Open filename$ For Random As filenum%  Len = Len(rec)
        '-----Random stores the info in a binary format. This may be my final output type
        '-----This way I can call a record by its record number with the Get statement
        '-----(I have not figure out all the details yet)
        For counter1% = 0 To 90
                Numeric_Date!=Csng(Date)+counter1%
                Do
                        For shift_number% = 1 To 3
                                Record_Number%=Record_Number%+1
                                Day_of_Year! = Csng(Cdat(Numeric_Date)-Datenumber(Year(Date$)-1,12,31))
                                '-----This mimics the Approach DayOfYear function
                                rec.Machine_Shift_Block = Trim(Str(Day_of_Year!))+"."+ rs.GetValue("id")+"." _
                                +Trim(Str(shift_number%))
                                Select Case Weekday(Numeric_Date!)
                                Case Is = 7'-----Saturday
                                        rec.Shift_Status = "Off"
                                Case Is = 1'-----Sunday
                                        rec.Shift_Status = "Off"
                                Case Else
                                        rec.Shift_Status = "On"
                                End Select
                                '-----For those not familiar with VB..case is much better than Nested If...Then...Else
                                Select Case Day_of_Year!
                                Case Is = 100 '-----Good Friday
                                        rec.Shift_Status = "Off"
                                Case Is = 145 '-----Memorial Day
                                        rec.Shift_Status = "Off"
                                Case 180 To 184'-----Vacation Shutdown
                                        rec.Shift_Status = "Off"
                                Case Is = 250'-----Labor Day
                                        rec.Shift_Status = "Off"
                                Case 330 To 331'-----Thanksgiving
                                        rec.Shift_Status = "Off"
                                Case 355 To 359'-----Vaction Shutdown
                                        rec.Shift_Status = "Off"
                                        'No Case Else. Would reset Saturday and Sunday to On
                                End Select
                                Print #fileNum%,Str(Record_Number%)+Chr(9)+rec.Machine_Shift_Block+Chr(9)+rec.Shift_Status
                                'Put # filenum%,Record_Number%,rec
                                '-----Print # works with Output while Put is for Random
                        Next shift_number%
                Loop While rs.nextrow
                rs.firstrow
        Next counter1%
        Close filenum%
        rs.close
        '-----Always put things away
End Sub

Example LotusScript: Progress bar
The following script and class is an reusable progress bar. It is called by script and will divide you data to progress into 10 parts. Run the Sub
CreatePBar to make the screen objects on a form. Include the Class "Progress" in your Global declarations. Look at the sample On Click Script at end to see usage example.

Jerry Sikes

'-----Progress Bar Class
'-----Jerry Sikes 2.18.98
'Install Class in Global Declarations
Class Progress
                                                                
Public ProgressName As String
Public pb(10) As display
Public sb(3) As display
Private maxValue As Long
Private js As Integer
Private Sub CheckMax
                                                                
If maxValue = 0 Then Error 999
End Sub
                                                                
Sub Value(v)
CheckMax
Select Case v
Case Is > maxValue * 0.9
pb(10).visible = True
Case Is > maxValue * 0.8
pb(9).visible = True
Case Is > maxValue *0.7
pb(8).visible = True
Case Is > maxValue * 0.6
pb(7).visible = True
Case Is > maxValue * 0.5
pb(6).visible = True
Case Is > maxValue * 0.4
pb(5).visible = True
Case Is > maxvalue * 0.3
pb(4).visible = True
Case Is > maxValue * 0.2
pb(3).visible = True
Case Is > maxValue * 0.1
pb(2).visible = True
Case Else
pb(1).visible = True
End Select
End Sub
                                                                
Sub Max(m)
maxValue = m
End Sub

Sub reset
For js% = 1 To 10
pb(js%).visible = False
Next
End Sub
                                                                
Sub ReplaceText(t)
sb(3).text = t
End Sub
                                                                
Sub New
Set pb(1) = currentview.body.Pbar1
Set pb(2) = currentview.body.Pbar11
Set pb(3) = currentview.body.Pbar12
Set pb(4) = currentview.body.Pbar13
Set pb(5) = currentview.body.Pbar14
Set pb(6) = currentview.body.Pbar15
Set pb(7) = currentview.body.Pbar16
Set pb(8) = currentview.body.Pbar17
Set pb(9) = currentview.body.Pbar18
Set pb(10) = currentview.body.Pbar19
Set sb(1) = currentview.body.Sbar
Set sb(2) = currentview.body.Sbar2
Set sb(3) = currentview.body.SText1
For js% = 1 To 3
sb(js%).visible = True
Next
sb(3).text = "Loading..."
End Sub
                                                                
Sub delete
For js% = 1 To 10
pb(js%).visible = False
Next
For js% = 1 To 3
sb(js%).visible = False
Next
End Sub
End Class

Sub CreatePbar
        '-----Progress Bar Objects
        '-----Jerry Sikes 2.18.98
        '-----Install As Global Sub
        '-----Run From Script Editor to create objects on
        '-----each desired form
        '-----I usually group these items after creation to 
        '-----place where I want it to appear.
                                                                
Dim MyRect(12) As rectangle
Dim MyText As  textbox
Dim pb(13) As display
                                                                
For i% = 1 To 12
Set MyRect(i%) = New Rectangle(currentview.body)
Set pb(i%) = MyRect(i%)
Next
                                                                
Set MyText = New Textbox(currentview.body)
Set pb(13) = MyText
With pb(11)
.name = "SBar"
.height = 810
.width = 2250
.left = 4590
.top = 3420
.visible = False
.NamedStyle = "Default"
.BackGround.Color.SetRGB(COLOR_25_GRAY)
.border.pattern = $LtsBorderPatternRaised
End With
                                                                
With pb(13)
.name = "SText1"
.height = 420
.width = 1110
.left = 4680
.top = 3510
.visible = False
.text = "Loading..."
.border.left = False
.border.right = False
.border.top = False
.border.bottom = False
.BackGround.Color.SetRGB(COLOR_25_GRAY)
End With
                                                                
With pb(12)
.name = "SBar2"
.height = 360
.width = 2070
.left = 4680
.top = 3780
.visible = False
End With
                                                                
With pb(1)
.name = "PBar1"
.height = 180
.width = 90
.left = 4860
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(2)
.name ="PBar11"
.height = 180
.width = 90
.left = 5040
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(3)
.name ="PBar12"
.height = 180
.width = 90
.left = 5220
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With

With pb(4)
.name = "PBar13"
.height = 180
.width = 90
.left = 5400
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(5)
.name = "PBar14"
.height = 180
.width = 90
.left = 5580
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(6)
.name = "PBar15"
.height = 180
.width = 90
.left = 5760
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(7)
.name = "PBar16"
.height = 180
.width = 90
.left = 5940
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(8)
.name = "PBar17"
.height = 180
.width = 90
.left = 6120
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(9)
.name = "PBar18"
.height = 180
.width = 90
.left = 6300
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
With pb(10)
.name = "PBar19"
.height = 180
.width = 90
.left = 6480
.top = 3870
.visible = False
.BackGround.Color.SetRGB(COLOR_BLUE)
.border.pattern = $LtsBorderPatternSolid
.Border.Color.SetRGB(COLOR_BLUE)
End With
                                                                
pb(12).SendToBack
                                                                
pb(13).SendToBack
                                                                
pb(11).SendToBack
End Sub

Usage Sample:

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        Dim LocalRS as New ResultSet
        Dim IsJob List As Long
        Dim b As New progress
        CurrentWindow.Repaint
        Set LocalRS =CurrentDocument.Tables(0).CreateResultSet()
        localrs.lastrow
        Call b.max (LocalRs.CurrentRow)
                                                                
localrs.firstrow
                                                                
Call b.ReplaceText("Loading Jobs...")
                                                                
Do
Call b.value(LocalRs.CurrentRow)
IsJob(LocalRs.GetValue(1)) = LocalRs.CurrentRow                  
Loop While LocalRs.NextRow
localrs.firstrow
Call b.reset
CurrentWindow.Repaint
        'The sub could continue with reuse of the Progress bar
End Sub

Example LotusScript: Flashing text
When this script is called, the message turns RED, blinks 5 times and then turns black and stops.

Notes: 

     "Warn" is a text box with the required message on a view i.e. Please Wait....... 
     The text color of the message is Black. 
     The fill color of the view is white. 
     The flashing color of text is Red. 

This script was forwarded from the Approach Discussion community on Lotus Support Website.

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        for i = 1 to 5
                source.warn.font.color.setrgb(color_red)
                source.warn.setfocus
                for q = 1 to 50000
                next
                source.warn.font.color.setrgb_white
                source.warn.setfocus
                for q = 1 to 50000
                next
        next
        source.warn.font.color.setrgb(color_black)
End Sub

Example LotusScript: Import a text file (or .dbf) into a DBF
The following was posted to the Approach Users Mailing List by Ankur R. Desai: 

Sub import
        Dim t as table       'the table we will replace
        Dim r as ResultSet   'the object we use to modify the table
        Dim a$, j%               'extra variables
        Dim afile as integer 'the file number

        Set t = CurrentDocument.GetTableByName("tablename")
          'Where tablename is the table you want
        Set r = t.CreateResultSet()
          'Saves the hassles of using the connect and query
          'At this point however, you could do an SQL query with the Query
          'statement to select specific records the following way I think
          'r.query.sql = "SELECT ... "
          'r.execute()     <- returns true if successful
        If r.NumRows() > 0 Then
                ans = MessageBox("Yo, dis table be not empty, delete it?",4)
                if ans = 6 Then
                        Call r.FirstRow() 'go to the first row
                        For j = 1 to r.NumRows()
                                Call r.DeleteRow() 'delete it
                        Next
                end if 
        End if

        afile = freefile()   'Get a file number to read from
        Open "filename.txt" For Input As afile
          'we open the filename and give it a filenumber you could ask
          'the user for the filename with the InputBox statement
          'in another message, I can post the routine to call the
          'OPEN-CommonDialog to get the filename from a user using
          'The standard Windows 95 OPEN box, it's not that hard
          'Search Lotuscript on altavista and you'll find it one the
          'first page of results        

        Do Until Eof(afile) 'EOF is End of File
                Line Input #afuke,a$
              'Read in one line - if you have a comma delimted file you
              'could use the Input statement to read each item
                Call r.AddRow() 'Add a row to the table
                Call r.SetValue("field1",Mid(a$,1,6))
                Call r.SetValue("field2",Mid(a$,7,10)+".0")
              'Sets field1 to the first 6 characters of a$
              'and field2 to to the next 10adding a .0 in the end
                Call r.UpdateRow
              'This commits the addition to the table
        Loop                 'Around we go
        Close afile                'Always close any files you open
        Call CurrentWindow.refresh()
          'Refreshes the data in the current window with the updated dbf
        Call CurrentWindow.repaint()
          'And redraw the window

          'you could do anything you want at this point
          'for example you could do a find on certain records and
          'find all records with a specific value and fill field3 with
          'a value, ex:
        Dim myfind as new FIND
          'make a new find object
        Call myfind.And("field1","Horseradish")
          'add an AND condition to find all records where field1 =
          'Horseradish
        Call CurrentWindow.FindSort(myfind)
          'excute the find
        Call CurrentWindow.FillField("field3","foobar")
          'fill all field3 in the found set with the work Yuck
End Sub

You can also import another DBF using the Connection property by doing the following:

Dim Q as New Query
Dim C as New Connection
Dim RS as New Resultset

        If C.ConnectTo("dBASE IV") Then   'Connect to dBASE.
                Set Q.Connection = C
                Q.Tablename = "path\filename.dbf"   
                Set RS.Query = Q
                If (RS.Execute)Then
                'Do funky stuff here
                        Call RS.FirstRow()
                        For j = 1 To RS.numrows()
                                Call R.AddRow()
                                Call R.Setvalue("CT_CODE", RS.GetValue("CT_REAS_CD"))
                                Call R.SetValue("CT_DESC",RS.GetValue("CT_REASON"))
                                Call RS.Nextrow()
                                Call R.UpdateRow()
                        .......

Other nice lotusScript functions:

c$ = CurrentDocument.GetTableByName("tablename").NumRecords
          'Gives you the number of records in a table in your APR
          'Could replace "tablename" with CurrentView.MainTable
b$ = CurrentWindow.NumRecordsFound
          'Gives you the number of records in the current found set
          'With these two numbers you can fill in a variable field in a report
          'and be able to print out the info in the status bar
CurrentView.Footer.Field = "Found "+b$+" of "+c$

Example LotusScript: Linked List class (beats 64K limit on arrays)
'List' is an Approach 97 class that allows you to make dynamic lists of any length. The following notes on its usage were posted to the Approach Users Mailing List by Ankur R. Desai: 

Dim MyList List
'Note no AS
'or
Dim MyList List as Integer
'Thefirst list was a variant list, the second an integer list

'Next cool part - you don't need numbers to reference it:

List("Fred") = 12
List("Barney") = 35
List(0) = 2
List(1) = 3
Print List("Fred")
'Values are added in the order of statements List(1) is actually the
'fourth element in the list

'You can of course
Erase List

'There's also
IsList(MyList)
'and
IsElement(MyList("Fred"))

'and in a forall you can use the ListTag statement to get the name of the
current element

ForAll E in MyList
  Print ListTag(e) 'will print name of current element
  e = e + 1  'adds one to each element in list
End ForAll

Example LotusScript: Fill Field No.1

Sub FillField
        
'This script was written by John Brown (of the Approach Users Mailing List)
'It fills a Numeric field with an incremental series of numbers
' eg 1, 2, 3, ... or 3, 6, 9 ... or 10, 7, 5, ...
'
'To use, create a new sub in the Script editor and paste the script over the
'default script contents. Then modify those lines of the script indicated my
'comments
        
        Dim Num As Integer
        Dim Inc As Integer
        Dim Counter  As Integer
        
        Num = 1    'This line sets the starting number (change as desired)
        Inc = 1    'This line sets the increment (change as desired)
        
        Counter = 0
        
        Dim rs As New ResultSet
        Dim C As New Connection
        Dim Q As New Query      
        
        If C.ConnectTo("dBASE IV") Then
                Set Q.Connection = C    
                
' In the next line set the path and filename of your .dbf file (retain the quotes)
                
                Q.Tablename = "C:\_DATA\scm\ascm\ascm6.dbf"
                Set rs.Query = Q
                If (rs.Execute)Then
                        Call rs.FirstRow
                        Do Until Counter = rs.NumRows
                                
'In the next line replace "PersonID" with your the name of your Numeric field
                                
                                Call rs.SetValue( "PersonID", Num)
                                rs.UpdateRow
                                Num = Num + Inc
                                Counter = Counter +1
                                If Counter < rs.NumRows Then Call rs.NextRow
                        Loop
                End If
        End If
        CurrentDocument.Window.Refresh
        Messagebox "Done!", 0, "FillField Script"
End Sub 

Example LotusScript: Fill Field No.2
This script was sent to the mailing list by Paul Bent (with some comments added by John Brown). It does approximately the same thing as Fill Field No.1, expect that it demonstrates different techniques and has different features: Paul writes: 

     "The table's already opened by the apr so I've used a Table object and the CreateResultSet method instead of a
     Connection object. 
     I've used a For Next loop instead of a Do loop 
     I made the ID a text field, length 6 and the ID must be 6 long, zero filled from the left - just for fun! 
     I put in the statements to get the start & step values from the user. 

In real life I would validate the user input but didn't bother for this demo." 

Sub FillField
        
        Dim Tbl As Table, RS As New ResultSet, C As Long, CurVal As Long, IntVal As Long
        Dim ConfMsg As String, MR As Integer
        
        On Error Goto ErrTrap
        
        CurVal = Clng(Inputbox$("Please enter the starting ID number:", "Enter ID", "1", 180, 180))
        IntVal = Clng(Inputbox$("Please enter the value to increment each ID number by:", "Enter Step", "1", 180, 180))
        
        ConfMsg = "Start value: " & Format$(CurVal, "000000")
        ConfMsg = ConfMsg & Chr$(10) & Chr$(13)
        ConfMsg = ConfMsg & "Increment: " & Cstr(IntVal)
        ConfMsg = ConfMsg & Chr$(10) & Chr$(13)
        ConfMsg = ConfMsg & Chr$(10) & Chr$(13)
        ConfMsg = ConfMsg & "Update all records with the new ID numbers ?"      
        MR = 2
        MR = Msgbox(ConfMsg,33,"Confirm Update")
        If MR = 2 Then
                Goto ExitSub
        End If
        
        Set Tbl = CurrentDocument.Tables(1)   'Use appropriate Table number. Tables are listed in the
                                            'FILE / APPROACH FILE PROPERTIES menu item.
                                            'The first table listed is Table (0), the second is Table (1),...
        Set RS = Tbl.CreateResultSet
        
        RS.FirstRow
        For C = 1 To RS.NumRows
                RS.SetValue("PersonID"), Format$(CurVal, "000000") 'Replace PersonID with your field name
                RS.UpdateRow
                CurVal = CurVal + IntVal
                RS.NextRow
        Next
        CurrentApplication.ActiveDocWindow.Refresh
        
        Msgbox Trim$(Str$(RS.NumRows)) & " records have been updated with new ID numbers.", 64, "Update Completed"
        Goto ExitSub
        
ErrTrap:      
        Msgbox "Error " & Str$(Err) & " - message: " & Error$, 16, "System Error"
        Resume ExitSub
        
ExitSub:
        Exit Sub
        
End Sub

Example LotusScript: Named Find Event
Paul Bent submitted the following script, saying: 

At http://XpertSS.com we were asked recently how to trigger a script when a named find is run from the list in the Action Bar.

Well there is no such event but you can hide the Action Bar and substitute a drop down list box containing the named finds. A script or macro can then run the find on data change in the drop down box. 

Sub NewFindList
        
        Dim aFinds As Variant, UB As Integer, aFStr() As String, C As Integer
        
'We need an array of variants to get the named finds then a string array to set the list from
        aFinds = CurrentDocument.NamedFindSorts
        UB = Ubound(aFinds)
        
'Now we know how many so resize an array of strings. We need
'data type string to use the SetList method.
        Redim aFStr(UB)
        
'Load the strings into the array
        For C = 0 To UB
                aFStr(C) = aFinds(C)
        Next
        
'Populate a dropdown box list with the string array.
'The dropdown box is named FList and is unbound. When I created it I
'had to type z as the first list item because an empty list is not allowed at creation.
        CurrentView.Body.FList.SetList(aFStr)
        
End Sub

To perform a named find as soon as it is selected, put the following script in the Change Script of the FList object: 

Sub Change(Source As Dropdownbox)

If CurrentView.Body.Flist.Text = "" Then Goto ExitSub
CurrentWindow.NamedFindSort = CurrentView.Body.Flist.Text

ExitSub:

End Sub

Alternatively, you perform the find by clicking a button (after you have selected the Named find in the dropdown box), by
inserting the following in the Click script of the button object: 

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        
        If CurrentView.Body.Flist.Text = "" Then Goto ExitSub
        CurrentWindow.NamedFindSort = CurrentView.Body.Flist.Text
        
ExitSub:
        
End Sub

Example LotusScript: Named Find Event with selection sort
This is another version of Paul Bent's Named Find Event script with a selection sort routine added by John Brown 

Sub Switchto(Source As Form, View As VIEW)
        
        Dim aFinds As Variant, UB As Integer, aFStr() As String, C As Integer, Temp As String, Lowest As Integer, Start As Integer
        
'We need an array of variants to get the named finds then a string array to set the list from
        aFinds = CurrentDocument.NamedFindSorts
        UB = Ubound(aFinds)
        
'Now we know how many so resize an array of strings. We need
'data type string to use the SetList method.
        Redim aFStr(UB)
        
'Load the strings into the array
        For C = 0 To UB
                aFStr(C) = aFinds(C)
        Next C
        
' Selection Sort routine
        For Start = 0 To (UB -1)
                Lowest = Start
                For C = (Start + 1) To UB
                        If aFStr(C) < aFStr(Lowest) Then
                                Lowest = C
                        End If
                Next C
                If Lowest <> Start Then
                        Temp = aFStr(Start)
                        aFStr(Start) = aFStr(Lowest)
                        aFStr(Lowest) = Temp
                End If
        Next Start
        
'Populate a dropdown box list with the string array.
'The dropdown box is named FList and is unbound. When I created it I
'had to type z as the first list item because an empty list is not allowed at creation.
        CurrentView.Body.FList.SetList(aFStr)
        
End Sub

Example LotusScript: Named Find Event with a different sort routine
This is another version of the Named Find Event script submitted by Paul Bent. This time he has included a sort routine to sort the Named Finds. 

Sub NewFindList
        
        Dim aFinds As Variant, intLB As Integer, intUB As Integer, aFStr() As String
        Dim intC1 As Integer, intC2 As Integer, intC3 As Integer, varTmp As Variant
        
        'Get the named finds in the apr as an array of variants
        aFinds = CurrentDocument.NamedFindSorts
        
        'Get the bounds of the array
        intLB = Lbound(aFinds)
        intUB = Ubound(aFinds)
        
        'Loop through the array comparing each element with all higher elements
        'If the lower element is greater than the higher element then switch them
        For intC1 = intLB To intUB - 1
                For intC2 = intC1 + 1 To intUB
                        If aFinds(intC1) > aFinds(intC2) Then
                                varTmp = aFinds(intC1)
                                aFinds(intC1) = aFinds(intC2)
                                aFinds(intC2) = varTmp
                        End If  
                Next
        Next
        
        'Now resize an array of strings. We need the finds as data type string
        'to use the SetList method.
        Redim aFStr(intUB)
        
        'Load the strings into the array
        For intC3 = 0 To intUB
                aFStr(intC3) = aFinds(intC3)
        Next
        
        'Populate a dropdown box list with the string array.
        'The dropdown box is named FList and is unbound. When I created it I
        'had to type z as the first list item because an empty list is not allowed at creation.
        CurrentView.Body.FList.SetList(aFStr)
        
End Sub

Example LotusScript: Find the current network ID No.1
This script was submitted to the Approach Users Mailing List by Steve Carpenter.

To get the username you need to access the Windows 95/98 registry, using functions not part of LotusScript but part of the
Windows API (Application Programming Interface). LotusScript lets you use external functions if you declare them first and
follow the calling conventions. Put the declares in the "Declarations" section of your document under "Globals", then add the
function to your scripts. 

' Registry Declarations:
Declare Public Function RegOpenKeyExA Lib "advapi32" Alias "RegOpenKeyExA"
(Byval HKEY As Long,Byval lpszSubKey As String,Byval dwreserved As
Integer,Byval samDesired As Long, keyresult As Long) As Long
Declare Public Function RegQueryValueExA Lib "advapi32" Alias
"RegQueryValueExA" (Byval HKEY As Long,Byval lpszValueName As String,Byval
dwreserved As Integer, lpdwtype As Long, Byval lpData As String, readbytes
As Long) As Long
Declare Public Function RegCloseKey Lib "advapi32" Alias "RegCloseKey"
(Byval HKEY As Long) As Long

Function GetUserName()
        ' Returns user name from Windows 9x registry
        ' 10/22/98 sc  (based mainly on a Lotus sample script)
        
        Dim happkey As Long
        Dim HKEY_LOCAL_MACHINE As Long
        Dim KEY_READ As Long
        Dim HKEY_CURRENT_USER As Long
        Dim ValueType As Long
        Dim ReturnedKeyContents As String * 255
        Dim readbytes As Long
        ReturnedKeycontents$=String$(255,Chr$(32))
        Dim Username As String
        
        HKEY_LOCAL_MACHINE= &H80000002
        HKEY_CURRENT_USER= &H80000001
        
        KEY_QUERY_VALUE=1
        KEY_ENUMERATE_SUBKEYS=8
        KEY_NOTIFY=16
        KEY_READ=KEY_QUERY_VALUE Or KEY_ENUMERATE_SUBKEYS Or KEY_NOTIFY
        
        ' this is the key to read
        KeyName$="Network\Logon"
        
        ' this is the value to look up in the key
        ValueName$="UserName"
        lstat=RegOpenKeyExA(HKEY_LOCAL_MACHINE,KeyName$,0,KEY_READ,happkey)
        ReadBytes=255
        lstat=RegQueryValueExA(happkey,ValueName$,0,valueType, ReturnedKeyContents$,ReadBytes)
        regclosekey(happkey)
        
        UserName=Left$(ReturnedKeyContents$,ReadBytes-1)
        UserName=Trim$(UserName)
        If Len(UserName) = 0 Then
                GetUsername = "UnknownUser"
        Else
                GetUsername = UserName
        End If
End Function

Example LotusScript: Find the current network ID No.2
The following script was submitted to the Approach Users Mailing List by Bill Tarkulich

Using Win32 API "getusername", he did it this way: 

Declare Function GetUserName& Lib "advapi32.dll" Alias "GetUserNameA" (Byval _
lpBuffer As String, nSize As Long)

Dim results As Long
Dim Patha As String *200

results = GetUserName(Patha,pathlen)

Example LotusScript: User selection of database tables within an .apr
In this script, Jerry Sikes demonstrates how to use the ReplaceWithResultSet method to allow the user to select which
database table they want the .apr to access. This is the ultimate work around for people who need to access more than one
database with a single .apr. 
 
Sub DropDownExample
        '-----Jerry Sikes 1998.09.15
        '-----To test, create a unbound fieldbox
        '-----change to dropdown list
        '-----use automatic values
        '-----name the object MyDropList
        Dim lbx As DROPDOWNBOX
        Dim lbxVal() As String
        Set lbx  = CURRENTVIEW.body.MyDropList
        Dim pathName As String, fileName As String
        '----- substitute your path
        pathName$ = "c:\Lotus\work\Approach\*.dbf"
        fileName$ = Dir$(pathName$, 0)
        MyCount% = 0
        '----- First  do loop counts number of qualifying tables, in path
        '----- You must limit the tables presented
        '----- to only those that will map properly
        '----- if more than one type of table exists in this directory,
        '----- try using a common prefix on all qualifying tables
        '----- "x:\path\Z*.dbf" where Z090898.dbf, Z091098.dbf...Zmmddyy.dbf"
        Do While fileName$ <> ""
                MyCount% = MyCount% + 1
                'Print fileName$
                fileName$ = Dir$()
        Loop
        '-----This sets the number of variables to the correct count
        Redim lbxVal(MyCount%-1)
        '-----Reset the counter
        MyCount% = 0
        '-----Force back to top of list
        fileName$ = Dir$(pathName$, 0)
        Do While fileName$ <> ""
                '----Load the strings
                lbxVal(MyCount%) = fileName$
            '-----get the next file
                fileName$ = Dir$()
                MyCount% = MyCount%+1
        Loop
        '-----Transfer the entire list to the screen object
        lbx.SetList lbxVal
        lbx.text = ""
End Sub

Sub ReplaceTableExample
        '-----Jerry Sikes 1998.09.15
        '-----This assumes the table selected is of identical
        '-----structure as the original
        '-----no error trapping
        '-----it also assumes the target table is table(0)
        '-----it also assumes, no alias to table(0)
      '-----in production, this script would be launched on the change event
      '-----of the drop down list
        Dim c As New connection
        Dim qu As New query
        Dim rs As New ResultSet
        qu.Tablename = "c:\Lotus\Work\Approach\" & currentview.body.MyDropList.text
        'Print qu.tablename
        Set qu.connection = c
        Set rs.query = qu
        If c.connectto("dBASE IV") Then
                If rs.execute Then
                        RVal = CurrentDocument.tables(0).ReplaceWithResultSet(RS)
                End If
        End If
End Sub

Example LotusScript: Pushing selections from one list box to another  [UPDATED] 12 Jun 2026
This script comes care of Paul Bent at XpertSS.com. He has kindly provided use with an example .apr (https://approach.droppages.site/LBoxes.exe ) containing the script so that you can see it in action (41k self-extracting archive. Just download and execute it. The default unzip directory is c:\lotus\approach\demo).

Paul explains: 
<Blockquote>
This shows how to design a control using two list boxes and use LotusScript to pop selections from one list and push them into the other list.

I created the control using a dialog form. On the form I added two unbound fields, made them List Boxes then resized them vertically so they will display the full list without a scroll bar. When I changed them to List Boxes I just typed a z as the list item because an empty list isn't allowed by the Info Box. In this example the list box objects are named UpdList and SelList. UpdList contains the items available for selection and SelList contains items that have been selected already.

In a real application I would set the UpdList from a script attached to the dialog form's Switchto event. For this demo you could type in the UpdList items using the Info Box - Apple, Banana, Cherry, Orange, Pear and Peach would do fine.

Placed between the two list boxes are four command buttons:

Select One >>
<< Remove One
Select All >>
<< Remove All

Once the selections have been made you would have a "Done" button that runs
a script to read the selections from SelList and batch processes them as
required.

The following global subs are called from the click events of the
respective buttons. The public sub, StrSort, bubble sorts arrays containing the list items.

Public Sub AddAll
        
        'Pops all items from lbxUpdList and pushes them into lbxSelList
        
        Dim aUpdList() As String
        Dim RV As Integer, UB As Integer, NB As Integer, C1 As Integer, CurItem As String, CurTag As Integer
        
        On Error Goto ErrTrap
        
        'Exit if no items in the list
        If CurrentView.Body.lbxUpdList.Count = 0 Then Goto ExitSub
        
        'Store the updates list items
        CurrentView.Body.lbxUpdList.SetFocus
        UB = CurrentView.Body.lbxUpdList.Count - 1
        Redim aUpdList(UB)
        For C1 = 0 To UB
                CurrentView.Body.lbxUpdList.CurrentSelection = C1
                aUpdList(C1) = CurrentView.Body.lbxUpdList.Text
        Next
        
        'Pop the items from the updates list
        For C1 = 0 To UB
                CurrentView.Body.lbxUpdList.RemoveListItem(0)
        Next
        
        'If there are already items in the selected list then add them to the array
        If CurrentView.Body.lbxSelList.Count > 0 Then
                CurrentView.Body.lbxSelList.SetFocus
                NB = CurrentView.Body.lbxSelList.Count
                UB = Ubound(aUpdList)
                Redim Preserve aUpdList(UB + NB)
                For C1 = 0 To NB - 1
                        CurrentView.Body.lbxSelList.CurrentSelection = C1
                        aUpdList(C1 + UB + 1) = CurrentView.Body.lbxSelList.Text
                Next
        End If
        
        'Push the items into the selected list
        Call StrSort(aUpdList)
        RV = CurrentView.Body.lbxSelList.SetList(aUpdList)
        
        'Make sure there is a selected item in the selected list
        CurrentView.Body.lbxSelList.SetFocus
        CurrentView.Body.lbxSelList.Text = aUpdList(0)
        
        Goto ExitSub
        
ErrTrap:
        Msgbox "Error " & Str$(Err) & " - message: " & Error$, 16, "System Error"
        Resume ExitSub
        
ExitSub:
        Exit Sub
        
End Sub

Example LotusScript: Accessing items in a list box
If you are wanting to use a DropDownBox then the news is bad ... you can't get the list items. However, if you use a ListBox
then you can. This snippet loads a string array aUpdList with the list items in a ListBox named lbxUpdList 

'submitted by Paul Bent, XpertSS.com
'
If CurrentView.Body.lbxUpdList.Count > 0 Then
  UB = CurrentView.Body.lbxUpdList.Count - 1
  Redim aUpdList(UB)
  For C1 = 0 To UB
   CurrentView.Body.lbxUpdList.CurrentSelection = C1
   aUpdList(C1) = CurrentView.Body.lbxUpdList.Text
  Next
End If

Example LotusScript: SQL delete
This example LotusScript deletes all rows from the table fg_orders. A WHERE clause or any other SQL statement can also be included. You could also create a stored procedure that accepts a parameter. Note that as far as anyone can tell the query method cannot accept return values. 

'Submitted by Jerry Sikes
Dim  con2 As New connection
        Dim  QRY2 As New query
        Dim RS2 As resultset
        If (CON2.connectto("SQL Server", "uid","pwd" , "jaxcnvq1")<>False)Then
                Set QRY2.CONNECTION = CON2
                qry2.sql = "Delete fg_orders"
                If Not (qry2.execute) Then
                        Msgbox "Not success"
                        Goto TheEnd
                End If
                
                Msgbox "Success"
        End If
TheEnd:
        con2.disconnect

Example LotusScript: Activating a macro in another APR
Jerry Sikes wrote:
<blockquote>
Here is the script method I use to activate a macro in another APR. I have a global sub called OpenMyDocs in the first APR(calling or source APR). It can be called by a click event or run from a macro. This script checks to see if the target application is already open. If true it uses the activate method and if false it use the opendocument method. This is followed by a RunProcedure method. This runprocedure method is how you start a global script in the second APR. The called script
can launch a macro with the RunApproachMacro method. The RunProcedure arguments allows a parameter to be passed.

This example passes a do nothing parameter. The called or target APR must have a global sub, such as the example OutsideSub shown. Here I accept the do nothing parameter and simply launch the desired macro. One note, if your target application's name has multiple words with spaces, you must be carefull with the activate method syntax. The OpenDocument method uses string quotes to enclosed the apr name. The activate method does not. For example, If I had previously opened the "Inventory Control Program.APR" and switched backed to another APR, the OpenMyDoc sub
would find this already open and branch into true section of the MyOpen = "True" if statement. The correct syntax should be:

     CurrentApplication.Inventory~ Control~ Program.activate 

The trick is to use a tilde(~) before each space. 

'Submitted by Jerry Sikes
Sub OpenMyDocs
        Dim MyDoc As String
        Dim NumDocs As Integer
        Dim Docs As Variant
        Dim MyOpen As String
        Dim fname As String
        If(CurrentApplication.Documents.IsEmpty=False) Then
                NumDocs = CurrentApplication.Documents.count
                For i = 0 To NumDocs-1
                        If CurrentApplication.Documents(i).Name="INVYTRANS" Then
                                MyOpen="True"
                        End If
                Next
        End If
        If MyOpen ="True" Then
                CurrentApplication.INVYTRANS.activate
        Else
                Set CurrentDocument=currentapplication.OpenDocument("INVYTRANS.apr","e:\standard\") 
        End If
        Call currentapplication.RunProcedure("OutsideSub","Hello")
End Sub

Sub OutsideSub(MyArg As String)
        RunApproachMacro("Update Records Macro")
End Sub

Example LotusScript: Converting Approach form to MS Word Document
This is a small sample of OLE automation between Approach and Word. It copies the current view, intitializes a new
instance of the installed version of word and pastes a picture of the approach view. More sophisticated automation, including
transfer of formated text is avaiable for a fee. Paste this sub directly into the global area of your application. Create a new
marco, choose the run command and select this sub. Switch to the desired view and run the macro.

Submitted by Jerry Sikes (Jacksonville Converting) SikesJ01@unisourcelink.com

Sub Sub1
        Dim wApp As Variant 
        Dim doc As Variant 
        Dim r As Variant 
        CurrentWindow.SelectAll 
        rval = currentwindow.CopyView($aprCurrentView) 
        Set wApp = CreateObject("Word.application") 
        wApp.Application.Visible = True 
        Set doc = wApp.documents.Add()
        'wApp.Selection.TypeText  "add some text"
        wApp.Selection.Paste
End Sub

Example LotusScript: Creating a new row in a repeating panel

In order to add a new line to a repeating panel the focus (cursor) must be in the repeating panel. This means that your macro will either have to tab into the panel, or you need to attach it to a button which is in the repeating panel such that it appears on every line of the panel.

A neater approach is to use a script, such as this one submitted by Paul Bent. Paul writes:

"This code is attached to a "New Line" button on an Orders form. The ProductID field is first in the rp's tab order. If the field in the first row is empty there are no existing order lines. In this case it just has to set focus in the first row to start a new record. If there are rows, it sets focus in the first one then starts a new record."

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)
        On Error Goto ErrTrap
        If CurrentView.rpOrdItems.cboProductID.Text = "" Then
                CurrentView.rpOrdItems.ProductID.SetFocus
        Else
                CurrentView.rpOrdItems.cboProductID.SetFocus
                CurrentWindow.NewRecord
        End If
        Goto  ExitSub
ErrTrap:
        Msgbox "Error " & Str$(Err) & " - message: " & Error$, 16, "System Error"
        Resume ExitSub

ExitSub:

End Sub

If you want to add the new line at the top of the repeating panel, then check out "Inserting a new blank record at the top of a repeating panel" in the <A HREF="examples.htm">Example Databases</A> section of this web site.

Example LotusScript: Copying calculated fields to the clipboard
The following script was converted to LotusScript by Keith Seeley from Visual Basic routine contained in a Microsoft Office Developers article titled "Creating Reusable Class Modules" by Mike Gilbert (http://www.microsoft.com/officedev/articles/movs109.htm).

Keith writes: "Obviously this only works for a predefined field (hardwired into the sub). I use a function key, but anything that can trigger the sub will work. Also note that the error checking in the function is how I got it. Only recently have I been trying to design 'well behaved' apps with error checking, and this is not one of the routines I've gone through."

*****Declares
Declare Private Sub MoveMemory  Lib "kernel32" Alias "RtlMoveMemory" ( Byval strDest As Any, Byval lpSource As Any, Byval Length As Any)
Declare Private Function GlobalAlloc Lib "kernel32" (Byval uFlags As Long, Byval dwBytes As Long) As Long
Declare Private Function GlobalFree Lib "kernel32" (Byval hMem As Long) As Long
Declare Private Function GlobalLock Lib "kernel32" (Byval hMem As Long) As Long
Declare Private Function GlobalUnlock Lib "kernel32" (Byval hMem As Long) As Long
Declare Private Function OpenClipboard Lib "user32" (Byval hWnd As Long) As Long
Declare Private Function GetClipboardData Lib "user32" (Byval uFormat As Long) As Long
Declare Private Function CloseClipboard Lib "user32" () As Long
Declare Private Function EmptyClipboard Lib "user32" () As Long
Declare Private Function SetClipboardData Lib "user32" (Byval uFormat As Long, Byval hData As Long) As Long

'Clipboard Constants...
Private Const GMEM_MOVABLE = &H2&
Private Const GMEM_DDESHARE = &H2000&
Private Const CF_TEXT = 1
Private Const CANNOTOPENCLIPBOARD = 2
Private Const CANNOTGLOBALLOCK = 4
Private Const CANNOTCLOSECLIPBOARD = 5
Private Const CANNOTGLOBALALLOC = 6
Private Const CANNOTEMPTYCLIPBOARD = 7
Private Const CANNOTSETCLIPBOARDDATA = 8
Private Const CANNOTGLOBALFREE = 9

*****Copy Data Function
Function fSendToClipboard(strText As String) As Variant
        Dim varRet As Variant
        Dim fStClpData As Long
        Dim hMem As Long
        Dim lpMemory As Long
        Dim lngSize As Long
        Dim varTemp As Variant

        varRet = False
        fStClpData = False

        lngSize = Len(strText) + 1
        hMem = GlobalAlloc(GMEM_MOVABLE Or _
        GMEM_DDESHARE, lngSize)
        If  (hMem) =0 Or Isnull(hMem)Then
                varRet = Error(CANNOTGLOBALALLOC)
                Goto sTxtDone
        End If

        lpMemory = GlobalLock(hMem)
        If  (lpMemory) =0 Or Isnull(lpMemory) Then
                varRet = Error(CANNOTGLOBALLOCK)
                Goto sTxtGlblFree
        End If

        Call MoveMemory(lpMemory, strText, lngSize)

        Call GlobalUnlock(hMem)
        varTemp = (OpenClipboard(0&))
        If  varTemp=0 Or Isnull(varTemp) Then
                varRet = Error(CANNOTOPENCLIPBOARD)
                Goto sTxtGlblFree
        End If
        varTemp = (emptyClipboard())
        If  varTemp=0 Or Isnull(varTemp) Then
                varRet = Error(CANNOTEMPTYCLIPBOARD)
                Goto fSendToClipboardCloseClipboard
        End If
        varTemp = SetClipboardData(CF_TEXT, hMem)
        If  varTemp=0 Or Isnull(varTemp) Then
                varRet = Error(CANNOTSETCLIPBOARDDATA)
                Goto fSendToClipboardCloseClipboard
        Else
                fStClpData = True
        End If

fSendToClipboardCloseClipboard:
        varTemp = closeclipboard()
        If   varTemp=0 Or Isnull(varTemp) Then
                varRet = Error(CANNOTCLOSECLIPBOARD)
        End If

sTxtGlblFree:
        If Not fStClpData Then
                varTemp = globalfree(hmem)
                If  varTemp=0 Or Isnull(varTemp) Then
                        varRet = Error(CANNOTGLOBALFREE)
                End If
        End If

sTxtDone:
        fSendToClipboard = varRet
End Function

*****Representative Sub to copy the data
Sub CopyData
        Dim varRet As Variant
        Dim strText As String
        strText = currentview.body.calctest.text
        varRet =        fSendToClipboard(strText)
End Sub

Example LotusScript: A button activated script that launches URL contained in a text field
(Contributed by "Paul Bent" <paulbent@northwindit.co.uk>)

1. Select the button. On the macros tab of the Info Box, give it a
meaningful object name like btnGoToURL.

2. Open the script editor (Ctrl + K)

3. Select Global in Object list, select Options in the Scripts list. Enter these statements in the script pane: (Option Public is probably there already)
Option Public
Option Explicit
Option Compare Nocase

4. Expand the Object list. Click the green twisty by the name of the apr to expand its object collection one level. Locate the name of the view
containing the button and click the twisty to expand it. Expand the Body
object, locate the name of the button and select it.

5. With the button selected in the Object list, select Click in the Scripts list. Paste this code into the scripts pane so the end result looks like:

Sub Click(Source As Button, X As Long, Y As Long, Flags As Long)

  '--- Opens the default browser and goes to the
  '    url contained in the URL field of the current record

  Dim intRtn As Integer         'Function return value

  On Error Goto ErrTrap

  intRtn = Shell("rundll32.exe url.dll,FileProtocolHandler http://" _
  & CurrentView.Body.fbxURL.Text)

  Goto ExitSub

ErrTrap:
  'Non-specific handler
  Msgbox "Error " & Format$(Err, "#0") & _
  " - message: " & Error$, 16, "System Error"
  Resume ExitSub

ExitSub:

End Sub

6. Press F2 and OK the no errors msg, press Shift + F2, ditto. Save the apr then test.

Example LotusScript: Loading a document into some other application
(Contributed by "David Legge" <dave.legge@lineone.net>)

It is possible to 'tell MS-Windows' to load a document into its default application for any file type recognised by windows using the Shell command. Eg.

x = Shell("rundll32.exe url.dll,FileProtocolHandler  d:\clipart\acs.pcx")

To make like simple you might like to imbed the command in a script like the following:

Sub Loadfile( Filename as string)
dim intRtn as integer
On Error Goto ErrTrap

intrtn  = Shell("rundll32.exe url.dll,FileProtocolHandler " & Filename)
ExitSub

ErrTrap:
  'Non-specific handler
  Msgbox "Error " & Format$(Err, "#0") & " - message: " & Error$, 16,"System
Error"
  Resume Next
End Sub

Example LotusScript: A "Translate" function in LotusScript
Paul Bent" <paulbent@northwindit.co.uk> writes:

LotusScript is a language implemented in many Lotus products and it doesn't have a built in function equivalent to Translate. Approach product functions like Translate() that can be used in calc fields, macros etc can't be called in a script (apart from a condition in a Find object). You have to write your own parsing function. Something like:

Public Function fReplaceText(Byval strSource As String, _
Byval strOldText As String, Byval strNewText As String) As String

 '--- Replaces all occurences of strOldText in strSource
 '    with strNewText

 '--- Parameters
 '      strSource: the string in which text is to be replaced
 '      strOldText: text to be replaced
 '      strNewText: replacement text

 '--- Return value
 '      returns the source string with the text replaced

 Dim intP1 As Integer   'Position in string

 Do While Instr(1, strSource, strNewText) > 0
  intP1 = Instr(1, strSource, strNewText)
  strSource = Left$(strSource, intP1 - 1) & strNewText _
  & Right$(strSource, Len(strSource)- intP1 - Len(strOldText) + 1)
 Loop
 'Return value
 fReplaceText = strSource

End Function

Then you can call the function from any subroutine, eg:

CurrentView.Body.fbxFirst.Text = fReplaceText(CurrentView.Body.fbxFirst.Text, "A", "a")

Example LotusScript: Another "Translate" function in LotusScript
"Steven Levine" <steve53@earthlink.net> contributed his version of a "translate" function in LotusScript:

Function swapchars(target As String, this As String, forthis As String) As
String
' replaces every occurance of 'this' in 'target' by 'forthis'
' swapchas is considered to be a local variable within the subroutine
 Dim at As Integer, limit As Integer, forthislen As Integer
 swapchars = target
 at = 1
 limit = Len(target)*2
 forthislen = Len(forthis)
 Do
   at = Instr(at,swapchars,this)
   If at = 0 Then Exit Do
   swapchars = Left$(swapchars,at-1) & forthis & Mid$(swapchars,at+1)
   at = at + forthislen
   limit = limit-1
 Loop Until (limit = 0)
End Function

Example LotusScript: Passing the contents of a field to Google.com
"Tony Franks" <T.Franks@ccw.gov.uk> writes:

For all you folks out there who, like me, are Lotus Script virgins,
this is how you do the passing of field contents to Google.

First open your database and go into design.
Select the field in question and right click for "Field Properties".
Click on the "Macro" tab and note the contents of the "Object name"
box.
Close the "Field Properties" box.
Open the Script Editor [Ctrl+K], make sure the first box at the top
left corner says (Globals), then paste in the following:

Sub www
        Dim intRtn As Integer 
        Dim strURL As String, googleSearchString As String 
        strURL = "www.google.com/search?sourceid=navclient&querytime=iE&q="
        googleSearchString = currentview.body.ObjectName.text
        strURL = Lcase$(strURL) & googleSearchString
        intRtn = Shell("rundll32.Received: from CCW_Gateways-MTA exe url.dll,FileProtocolHandler" & strURL)
End Sub

In the line googleSearchString replace ObjectName for the content you
noted previously.
Make sure there is a space at the beginning of the google URL after the
first quotation mark (or it won't work!).
Create a button next to the field, and selecting the button right click
for "Field Properties".
Select the Macro tab, and click on "Define Macro", and Click on "New".
Give the macro a memorable name (eg "Google search for address").
Select the command "Run", and select the "radio button": Run macro.
Look in the dropdown list for "www" and select it.
Put a tick in the "Return to the next line in this Macro"
Click on [OK], Click on [Done] and save everything.
Test your Google search button!

Thanks are due to Paul Bent, Jerry Sikes, Keith Seeley, and Rainer
Schmid for all the help and encouragement!

Example LotusScript: Splitting a text field apart at Tabs
You can look for a tab character in a text sring using Chr$(9).

"Paul Bent" <paulbent@northwindit.co.uk> writes:

(The following script) loads parsed values into a passed in array and returns it to the calling proc:

Public Function fSplitLineByTabs(Byval strLine As String, _
astrData() As String) As Integer
        
 '--- Given a tab delimited string, parses it into "fields"
 '--- and loads them in an array
 '--- Author: Paul Bent, Nothwind IT Systems, 14-Apr-2001
        
 '--- Parameters
 '      [In]
 '      strLine: the string from which to parse the field values
 '      [In/Out]
 '      astrData: array to load the field values into
        
 '--- Return value
 '      returns the number of field names placed in the array
        
 Dim intP1 As Integer                   'Position in string
 Dim intP2 As Integer                   'Position in string
 Dim intP3 As Integer                   'Position in string
 Dim intC1 As Integer                   'Array index

 'Initialize the parameter passed in by reference
 Erase astrData
        
 'Check strLine is not null
 If Len(strLine) = 0 Then Exit Function
        
 'Parse the field names
 intP1 = 1
 Do While Instr(intP1, strLine, Chr$(9)) > 0
  'Size the array
   Redim Preserve astrData(0 To intC1)
   intC1 = intC1 + 1
  'Get the next tab char
  intP2 = Instr(intP1, strLine, Chr$(9))
  If intP2 > 0 Then
  'Get the following tab character
   intP3 = Instr(intP2 + 1, strLine, Chr$(9))
   If intP3 > 0 Then
   'If there's another tab char then parse the text between them
    astrData(intC1) = Mid$(strLine, intP2 + 1, intP3 - intP2 - 1)
   Else
    'Last field in the string
    astrData(intC1) = Right$(strLine(Len(strLine) - intP2)
    Exit Do
   End If
   'Reposition the starting point in the input string
   intP1 = intP2 + 1
 Loop
        
 'Return value
 fSplitLineByTabs = intC1
        
End Function

Then call it from your sub as follows:

Dim intC1 As Integer
Dim astrData() As String

If fSplitLineByTabs(strToBeSplit, astrData) Then
 For intC1 = Lbound(astrData) To Ubound(astrData)
  'Print the values to the Output pane
  Print astrData(intC1)
 Next
End If

Example LotusScript: Using Windows 95/98 API to launch another application
Paul Bent submitted the following script which demonstrates the use of Windows API to launch another application, stating that it is much  better than using the Shell function, as there is no need to know the path or name of the exe and no parsing problems.

'[Globals - Declarations]
ShellExecute API
'--- ShellExecute uses the shell to open or print a file or run a program.
'--- Under Win 95/98, this function will also open a My Computer or Explorer window to a given directory.
'--- If an executable program is specified, Windows will run that program.
'--- If a document file is specified, Windows will open or print it using the associated program (whatever it happens to be).
'--- If successful, the function returns a handle to the instance of the opened program
'--- or (in the case of printing) a handle to the invoked DDE server application.
'--- If unsuccessful, the function returns either 0 (meaning out of memory or resources) or one of the following error code flags:

Declare Public Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(Byval hwnd As Long, Byval lpOperation As String, Byval lpFile As String, _
Byval lpParameters As String, Byval lpDirectory As String, Byval nShowCmd As Long) As Long
 
'hwnd    The handle of the window calling the function.
'lpOperation  The operation to perform on lpFile. "open" means open the file or run the program (or directory in Win 95/98). "print" means print
'the document. In Win 95/98, "explore" means open the directory in an Explorer window. The default is "open".
'lpFile    The file to perform the operation on.
'lpParameters Any command-line parameters to pass to an opened application.
'lpDirectory  The working directory for the operation.
Public Const ERROR_FILE_NOT_FOUND = 2&  'The specified file could not be found.
Public Const ERROR_PATH_NOT_FOUND = 3&   'The specified directory could not be found.
Public Const ERROR_BAD_FORMAT = 11&   'The specified executable file (.EXE) was somehow invalid.
Public Const SE_ERR_ACCESSDENIED = 5   'Win 95/98 only: Windows denied access to the specified file
Public Const SE_ERR_ASSOCINCOMPLETE = 27   'The filename association is either incomplete Or invalid
Public Const SE_ERR_DDEBUSY = 30   'The DDE action could not run because other DDE actions are in process
Public Const SE_ERR_DDEFAIL = 29   'The DDE transaction failed
Public Const SE_ERR_DDETIMEOUT = 28   'The DDE transaction was not completed because the request timed out
Public Const SE_ERR_DLLNOTFOUND = 32   'Win 95/98 only: The specified DLL file was not found
Public Const SE_ERR_FNF = 2   'Same as ERROR_FILE_NOT_FOUND
Public Const SE_ERR_NOASSOC = 31   'There is no program associated with the specified type of file
Public Const SE_ERR_OOM = 8   'Win 95/98 only: Windows has insufficient memory to perform the operation
Public Const SE_ERR_PNF = 3   'Same as ERROR_PATH_NOT_FOUND
Public Const SE_ERR_SHARE = 26   'A sharing violation occured
Public Const SW_HIDE = 0   'Hide the opened window
Public Const SW_MAXIMIZE = 3   'Maximize the opened window
Public Const SW_MINIMIZE = 6   'Minimize the opened window
Public Const SW_RESTORE = 9   'Restore the opened window (not maximized nor minimized)
Public Const SW_SHOW = 5   'Show the opened window
Public Const SW_SHOWMAXIMIZED = 3   'Show the opened window maximized
Public Const SW_SHOWMINIMIZED = 2   'Show the opened window minimized
Public Const SW_SHOWMINNOACTIVE = 7   'Show the opened window minimized but do not activate the it
Public Const SW_SHOWNA = 8   'Show the opened window in its current state but do not activate it
Public Const SW_SHOWNOACTIVATE = 4   'Show the opened window in its most recent size and position but do not activate it
Public Const SW_SHOWNORMAL = 1   'Show the opened window and activate it (as usual)
 
Then call it as follows
 
Dim hWnd As Long        'Approach doc window handle
Dim lngRtn As Long        'API function return value
 
hWnd = CurrentWindow.GetHandle
lngRtn = ShellExecute(hWnd, "open", "d:\my docs\mypowerpoint.ppt", "/s", "", SW_SHOWNORMAL)

Example LotusScript: Go back to the last thing that the user was using
David Legge writes: First cut and paste this declaration into Global declarations. Then follow the instructions in the first set of comments 
- they come out green in the IDE (Script editor)

Class ViewCollection
' coded by Dave Legge, 2001
' ---------------------------------------------
' HOW TO DO IT
' 1  Declare global variable 
'    Dim Views As Viewcollection
' 2  In (Globals.initialise) or in your files documentwindow.openwindow event add the line
'    Set Views = New Trailcollection
' 3  in yourdocument's Documentwindow.Viewswitch event place 
'    Call Views.moveon ToView  
'    NOTE:  up to Apr 9.7 ToView and FromView parameter Declarations are reveresed in the IDE!
'           it ought to be Call Views.moveon FromView !!!!!
' 4  From your button.click  or worksheet.cellgetfocus event
'    Call Views.moveBack 
' ---------------------------------------------
trail As collection
goingback As Integer

Sub New
' Print "Calling TrailCollection.New"
goingback = False
Set trail = New collection
' On Event Viewswitch From currentdocument.DocumentWindow Call moveonevent
End Sub

Sub moveon ( Fromview As VIEW)
If Not goingback Then 
trail.add fromview      'Push View onto collection
' Print "Push: "; fromview.name 
End If 
goingback = False
End Sub 

Sub moveback
If trail.count <> 0 Then
goingback = True
' Print " Pop "; Trail(Trail.count-1).name, trail.count 
Set CurrentWindow.ActiveView = Trail(Trail.count-1) ' Top one
trail.remove(Trail.count-1) ' Pop it
Else
Messagebox "At start of Trail" & Chr$(10) & "Can't go back any further",64,"BACK"
End If
Yield           ' Let windows take you there before proceeding
goingback = False 
End Sub
End Class

Example LotusScript: Go back to the last thing that the user was using (A different response)
Paul Bent writes: You have to use script to get the TS user, this statement will return the username:
  
CurrentDocument.User
 
Where you store this depends on how it will be used later. If it's to be available to scripts then assign it to a global variable. If to be available to macros, calc fields, default creation & mod formulas etc you need to transfer the username to a variable field.
 
You can run a script automatically when the apr opens by placing it in Globals - Initialize but if using later than A97 there's a bug to workaround. You have to:
 
1. Locate the DocumentOpened sub in the Approach object and enter a single comment character '. This avoids the bug where Initialize doesn't run when the apr opens but only when the first event script fires which can be a lot later.
2. In the Initialize sub, test for the name of the apr because it will now try to run when other aprs are opened too.
3. One other (harmless) bug is that having entered or modified code in the IDE and pressed F2 to test compile it, when you close the apr, Initialize will run! It's only a design-time problem but it throws you a bit to perhaps see an error that can only come from Initialize when you close the apr.
 
So if you were assigning the username to a global variable you'd first declare it in Globals - Declarations:

Public gstrUser As String

Then assign the user name in the Initialize sub:

Sub Initialize
 
 On Error Goto ErrTrap
 
 'Check this apr is the active one
 With CurrentDocument
  If .Name = "xyz" Then
   'Store the username
   gstrUser = .User
   'Switch to the opening view etc
   '.......
  End If
 End With
 
 Goto ExitSub
 
ErrTrap:
 Msgbox "Error " & Format$(Err, "#0") & " - message: " & Error$, 16, "System Error"
 Resume ExitSub
 
ExitSub:
 
End Sub

If you need to transfer the user to a variable field, the only way is via the Text property of a fieldbox on the active view. You can run a one off sub at design-time to make the fieldbox invisible. Say you create a variable field named vUser, option text, add it to a form and give the fieldbox an object name of fbxUser. Create a global sub to make it invisible, use the F5 key in the IDE to run the sub then save the apr.

Sub sHideCtrl
  CurrentView.Body.fbxUser.Visible = False
End Sub

Then the Initialize sub, instead of/as well as storing the username in the global variable, has to switch to the view containing the variable field and set its Text property:

Sub Initialize
 
 On Error Goto ErrTrap
 
 'Check this apr is the active one
 With CurrentDocument
  If .Name = "xyz" Then
   'Store the username in a global variable
   gstrUser = .User
   'Switch to view containing the variable field
   Set CurrentApplication.ActiveView = .Name~ Of~ View
   'Store the username in the variable field
   CurrentView.Body.fbxUser.Text = .User
   'Switch to the opening view etc
   '.......
  End If
 End With
 
 Goto ExitSub
 
ErrTrap:
 Msgbox "Error " & Format$(Err, "#0") & " - message: " & Error$, 16, "System Error"
 Resume ExitSub
 
ExitSub:
 
End Sub

No there isn't a way to "go back" as such. To a limited extent you could do things like when a user switches view, store the "from view" in a global variable and a generic "back" button can switch to the stored view.

Example LotusScript: "Freezing" the screen while executing a LotusScript
(Based on a post by David Legge)

Easy! Just use the command:

 currentwindow.redraw = False

   ... and will stop the window from redrawing automatically.

The trick is that you REALLY REALLY need to make sure it gets unfrozen again!

To do this set currentwindow.redraw = True at a point where the code is guaranteed to execute otherwise the window will be frozen forever. 
Best to place it in an error trap, so that if anything goes wrong it will
unfreeze the screen:

Sub Mysub(......)etc
 'Error trap
 On Error Goto Trap
currentwindow.redraw = False

' YOUR CODE

ExitSub:
 currentwindow.redraw = True
 Exit Sub

Trap:
 Msgbox "Error " & Format$(Err, "####") & " in Mysub" _
 & Chr(10) & Error$, 16, "Trapped Error"
 Resume ExitSub
End Sub

Example LotusScript: Controlling the window size when opening Approach
Dave Legge writes:

Cut and paste this, into the Lotus Script Editor

In Declarations, put:

Public Type RECT
 Left As Long
 Top As Long
 Right As Long
 Bottom As Long
End Type

Declare Public Function GetDesktopWindow Lib "user32" _  'The GetDesktopWindow function returns the handle of the Windows desktop window.
Alias "GetDesktopWindow" () As Long     'The desktop window covers the entire screen.

Declare Public Function GetClientRect Lib "user32" _
Alias "GetClientRect" (Byval hwnd As Long, lpRect As RECT) As Long

Declare Public Function  MoveWindow Lib "user32"  _
Alias "MoveWindow" (Byval hwnd As Long,  Byval x As Integer, Byval y As Integer,Byval  w As Integer,Byval h As Integer, Byval repaint As Integer) As Long

In globals, put:

Sub AprWindowsize( wide As Long, high As Long)
'Set Approach application window to wide x high  (in pixels)
' and center it on desktop
 Dim Hdesktop As Long
 Dim Hapr As Long
 Dim RDesktop As rect
 Dim Rapr As rect
 Dim ok As Long
 Dim lft As Long
 Dim top As Long

 Hdesktop = GetDesktopWindow
 GetClientRect  Hdesktop , Rdesktop
 Hapr =Currentapplication.applicationWindow.Gethandle
 If (wide>RDesktop.right) Or (high>Rdesktop.bottom) Then
  Currentapplication.applicationWindow.maximize
 Else
  Hapr = Currentapplication.applicationwindow.gethandle
  GetClientRect Hapr , Rapr
  lft =    (Rdesktop.right-wide) / 2
  top = (Rdesktop.bottom-high)/2
  MoveWindow  Hapr, lft,top,wide,high,1  ' h,x,y,w,h,true
 End If
End Sub

Then in your-document's DocumentWindow, "OpenWindow" Script put:

AprWindowsize 790, 595

It resizes and centers on the screen

Generating and reading barcodes
You need the Lotus Postal Barcode font (WPPOST1.TTF) which is included with WordPRO. There are really specific instructions on the Lotus web site about this. Just search the knowledgebase in the Approach support area.

http://www.lotus.com

Zip codes and Post codes  [UPDATED] 12 Jun 2026
USA Zip codes are available from:

    * The v96 CD-ROM includes zipcode.apr, zipcode.adx and zipcode.dbf. zipecode.dbf in the Extra\Dbases directory (78,000 listings)
    * Download ZIPCODE.ZIP from IBM's Lotus Approach support site: http://www-3.ibm.com/software/lotus/support/approach/support.html.
    * http://www.lycos.com (enter zipcodes)
    * Try the US postal service: http://www.usps.gov/

Australian postcodes are available from the Australia Post website in .csv format (ie delimited text) which is easily imported into Approach: http://www.auspost.com.au/postcodes.

UK postcodes are made up of two parts: The first part (outward) contains the area and district. The second part (inward) is used by district sorting office and contains the sector and unit. There are 7 formats for post codes: AN NAA; ANN NAA; AAN NAA; AANN NAA; ANA NAA; AANA NAA; AAA ANA. A list of Valid outward post codes is contained in the mailsort database available from http://www.mailsorttechnical.com/. You can down load a very handy Approach database of UK postcodes (valid 2002) here:

     https://approach.droppages.site/Pcodes.exe [File size: Compressed = 205kb, "uncompressed" = 124kb]

Recovering deleted records
If you have compressed the database then the deleted records have been discarded and cannot be recovered. Your only recourse would be to recover using a good recent backup of the database.

If you haven't compressed the database but your database has memo or PicturePlus fields in the deleted records, you can recover the base records but not these types of fields. This is because the "pointer" to them in the .DBT file is zeroed out when the record is marked deleted. These fields would need to be reentered or recovered from a good recent backup of the database by isolating the "deleted records" so that you can export only the fields you need! Or you could take the recovered database and join it to the backup database and copy over the missing fields that way.

Some ways to recover deleted records:
Note that what you need to do is in some way remove the "deleted" tag from the base records in the .DBF file.

1) Get a utility program designed for the purpose.
 
Some utility programs will let you undelete one record at a time, or all deleted records at once, or simple export all records to another set of files.
 * One that has been recommended on the mailing list is a small freeware tool called Database Manager, which can be downloaded from http://www.inner-smile.com/ Make sure you read the readme.txt file carefully. You find it has three levels of access, and recovering deleted records requires full access. Deleted records are "grey" in the listing and there is an icon to undelete the selected row.
 * Another one is dSalvage Pro ($199.95 from http://www.accutek.com/comtech/) which can undelete records and it has a way to rematch memo fields (not PicturePlus fields) to their base records providing you have something in the memo field to identify its base record match. This could be very laborious on a large database or with recovery of many deleted records! There are many more useful features in this utility program to consider when evaluating it.  

2) Use the 32-bit ODBC driver and Approach.

You can use ODBC to open your .dbf with Approach and recover deleted base records. If you do not already have the 32-bit ODBC option in your Control Panel, download and install the driver for your operating system from Microsoft's web site. 
The following steps were tested on a W95 system: 

* Open the ODBC Data Source Administrator from Control Panel. 
* On the User DSN page, Click the "Add" button to add a data source. 
* Select the "Microsoft dBase Driver(*.dbf)" driver then click the "Finish" button. 
* Enter a data source name of your choice and description. 
* Select the Database Version as dBase IV. 
* Uncheck the "Use Current Directory" check box. 
* Click the "Select Directory" button and navigate to the folder where your .dbf file is located. Select the .dbf file name, then the "OK" button. 
* Click the "Options" button and you will see a checkbox for "Show Deleted Rows" which you must check to see the deleted records. 
* Open Approach, then use File, Open and select the "ODBC Microsoft dBase Driver(*.dbf)" option in "Files of type:". You will need to navigate down the path double-clicking the "Connection DBASE", then the folder-name path to your ODBC source, and then the .dbf file name. Then click the "Open" button. 
* Use File, Export Data to export all records to a new dbf, same name, different folder. 
* Use the new dbf to restore the deleted records as follows:

If you do not have any memo or pictureplus fields, the exported .dbf and .adx files will have all the records restored and can be used in place of the original database. Note that the field names and serial number defaults will not be the same as your original database because ODBC does not use the SmartIndex .ADX file, so you may want to import the exported database into the original database with the update and add records option to add back the deleted records and preserve your field definitions.

If you have memo or pictureplus fields, you should not use the exported database as a substitute. Import the exported database into the original database with the update and add records option to preserve existing records with all fields, add deleted records, and preserve field definitions.

3) Use a text or hex editor program

It is also possible to recover base records by opening the .dbf in a text editor or hex editor, and replacing the asterisk at the beginning of a deleted record with a space character. If you have only one or two records to fix, this may be the simplest method. However, this is not recommended because you may damage the database instead of fixing it! If you decide to try it, use a copy of your .DBF file, PLEASE!

Turning off screen updates when running a macro
The following LotusScript (by Jerry Sikes) turns off Approach's screen redraw function while it runs your macro. A warning though, if something goes wrong while the script/macro is running, then the screen won't be updated to tell you about it...!

Sub MyPrint
    CurrentWindow.Redraw=False   'Turn off redraw temporarily

    RunApproachMacro("YourMacro")

    CurrentWindow.Redraw=True
    CurrentWindow.Repaint
End Sub

Replace "YourMacro" with the name of your macro.

Advise when using memo fields...
You cannot enter a tab into a memo field like you can into a word processor document. If you want things to "align" in a column, you use spaces and a fixed-width font like Courier.

All fields in dBase records are stored in .dbf files with the exception of Memo fields. For Memo fields the .dbf contains a pointer that indicates the location of the memo field data in a corresponding .dbt file of the same name. It is possible for the pointers in the .dbf and the actual data in the .dbt to get out of sync. In this situation the memo box displayed on a view may display the incorrect information or none at all. There is no automatic way of recovering from this, except of course by restoring the files from yesterdays backup you made...

However, there is a way that you can protect yourself against this problem: When creating a record, insert the primary key (i.e. a piece of data that uniquely identifies the record) into the memo field in such a way that it can be extracted if needed. This is best done by having a macro insert the primary key at the beginning of the memo field when the field is created. If the memo field ever gets out of sync, then you can then set up some looping macros to extract the primary key from the memo fields and link it back up to the original record.

Personally I've used memo fields for years in v2.1 and v97 in order to create this FAQ and store posts to the mailing list, and I have never had a problem. However, others have had a lot of problems. I have no idea why this is!

Creating a 'Help' file
One idea is to create a database with a PicturePlus field and a text or memo field. In the picture field insert an image of the screen that the particular help file entry pertains to. You can capture this image by going to the screen and then hitting the PrtSC (Print Screen) key and then pasting it straight into the PicturePlus field. Alternatively you mat want to paste it into a graphics program (such as 'Paint' that comes with MS-Windows) and edit it first. Then put the require explanation in the text field.

A help database such as this can either be search in it's own right, or linked to an application to create a context sensitive help file.

Rather than trying to create an exhaustive help file in one go, another approach is to make a small start, and then add entries as it becomes apparent that they are required. This is the way that this FAQ has been created. Every now and then I add a few more entries, and over the years it has become quite extensive.

Storing pictures
When you put a picture into a PicturePlus file,  Approach converts the format into its own common format regardless of the external file type.

An alternate method is to leave the photo files as separate files and just put a Link into each record, or put the file name into each record and use the OPEN command in a macro to launch another program to
view the photo. You could put a small low-res version in a PicturePlus field so that users can preview the picture before loading.

Using LotusScript it is possible to use the Picture object to display pictures stored separate on the hard drive. This is the best overall solution that avoids all of the problems with PicturePlus fields. There is a superb example database available for download which shows exactly how this is done in the <A HREF="examples.htm">Example Databases</A> section of this web site.

Pictures stored using JPEG format are generally take a lot less file space than using bit maps or .gifs. The following example was submitted to the Approach Users Mailing List:

   362K   memory size of image
   183K   *.bmp file size
   193K   *.gif file size
   52K    *.jpg file size (low compression)
   27K    *.jpg file size (moderate compression)

Note that the efficiency of jpg compression will vary with the complexity of the image.

Remote dialup access to Approach
Neither Approach nor SmartSuite have inbuilt facilities to accommodate this, so you need other applications to handle the communications.

If you're just interested in a remote dialup connection when needed, people have suggested using PC Anywhere or LapLink. This is by far the simplest and cheapest solution but only one remote user can access the host computer at once, and the host computer cannot be used by office staff while a remote users is linked to it. Depending on the complexity of the database and the number of screen redraws required it can be very slow, and so is really limited to situations in which the remote user just needs to access or enter a few records, rather than any major work. A major problem is that neither PC Anywhere or LapLink provides any transaction protection so that if the connection is lost mid-transaction your database could be corrupted costing you time and money... Approach does not contain any inbuilt protection against this. However, someone pointed out that with PC Anywhere or LapLink you are taking control of the host machine (you don't even need to have Approach installed on the computer that you're working on) so that the only data coming down the phone line is only to redraw the screen. All processing is done on the host computer, if a connection breaks the host the integrity of the database files should not be affected...

You can download a trial version of PC Anywhere from their web site.

To have remote multi-user sessions you really need a client / server
network and Dial Up Networking.

To accommodate more advanced requirements consider using Notes which can be set up over an IntraNet or for dialin access. The advantages include the ability to have simultaneous users, greater stability, security, transaction protection, scaleability, remote users can have their own local copy which will "replicate" with the server meaning that it is not necessary to maintain a constant phone connection. This last point may make it a cheaper option in the long run. However, it is considerably more expensive (you need a Notes Server licence, plus a Notes Desktop Licence for each user) and you need to be reasonable competent with Notes to set it up initially.

Multi-line messagebox
It is possible to create a messagebox that contains two or more lines.

If you create the messagebox in a macro, just hit CTRL-M at the end of each line.

If you create the messagebox in LotusScript, then put a Chr$(13) in the string at the end of every line. Eg:

    Messagebox "First line of text" & Chr$(13) & "Second line of text"

Tips on using the Script Editor
If the script editor is open and you double-click an object in design, that object will be active in the script editor.

Unfortunately the script editor Browser Tab doesn't limit the lists to just those items associated with the object you are working on, which can be rather confusing. One way around this is to use the lists provided in the help file instead. In the v97 Script Editor, go the HELP / LOTUS APPROACH 97 OBJECTS menu, select the "classes" index entry and click "Display". You will see a list of all the Approach classes.

Once this is open, just click on the class you are interested in, then click "Class members" to see a list of the Properties, Methods, and Events for that class.

Printing out .apr file properties
If you try to print out the .apr file properties, the printout is truncated.

The way to get around this is to use the XpertSS Documenter to do that, plus much much more. It is in the XpertSS Products area on http://XpertSS.com

Setting the auto-incrementing serial number
Unfortunately the auto-incrementing serial numbers that you can set up in the Options>>Default values part of the field definition of a numeric field cannot be accessed or changed using a macro or LotusScript. The only way of changing it is via hand, or by using the SendKey command.

Alternatively you can ignore the auto-increment function altogether and create your own auto-incrementing serial number. This would allow you to also us a much more meaningful serial number. For example in the following section Sue Sloan explains how to create an auto-incrementing serial number which starts with the year (eg 2002-00001). She write:

1) Set up a new database - I would call it "control" - with a field in it to use for the "next number", a field for the "current year",  and a field "link", text, 1 character defaulting to an X and validated as "unique". Enter a starting number in the "next number" field (not a default number or serial number - enter the starting number on a form view). Enter the current year in the "year" field.

2) Add a similar "link" field to your database where you have the field to be numbered. The default value also would be X. Fill that field with X
using Fill Field on a worksheet view,  Remove the serial numbering default option from the number field in this database and validate it to not be blank.

3) Join the control database to your database using the "link" fields. Set all join options off.

4) Now you must force the user to create new records in your database using a macro and a button. The macro would be:

NewRecord
.    RECORDS New
.    SET yearfield = control.year
.    SET  numberfield = control.nextnumberfield
.    SET control.nextnumberfield = control.nextnumberfield + 1
add whatever you need here

This works even in a multi-user system because two users cannot get the
same control.nextnumberfield value. 

5) Now all you need to do is add a macro named OPEN to your application. It goes to a view based on the "control" database and compares Year(Today) to the "year" field. If it is not the same, the "year" is changed and the "next number" is set to 1.

OPEN
.   VIEW switch to control worksheet
.   RUN If(Year(Today()) = control.year then end macro (or go to whatever
macro you wish to go to)
.       else continue this macro
.   SET control.year = Year(Today())
.   SET control.nextnumberfield = 1
.   ENTER
.   do whatever else you need to do here

Note that the only danger in having this run automatically when the
Approach file opens is that you may have a user with his/her computer date set incorrectly. You may want to add a test to make sure the Year(Today()) is > control.year, for example. 

Quick access to objects in the script editor
If you are sick and tired of trying to find what you are looking for in the Objects dropdown list in the Script Editor, then just minimize the Script Editor and double-click the object on your view that you want to access the scripts for. The InfoBox will appear, but just ignore that. Instead, click on the minimize Script Editor to bring it up and you'll find that it is now the scripts associated with the object you just clicked on !

Automatically saving your work
Data is automatically saved once you enter it, so you don't every need to do anything to "save" it.

On the other hand, all of your forms, reports, Named Finds, macros and LotusScripts are only saved when you select FILE / SAVE. Unfortunately there isn't any facility in Approach to get it to automatically save your work on a periodic basis.

However, if you are developing script are a bit forgetful about periodically saving your work, then you may want to consider adding the following line to the current script you are working on so that your changes will be automatically saved each time you run the script (don't forget to remove it once you finished developing that script):

    Current.Window.SaveChanges

How the Approach Users Mailing List works

Contributors  [UPDATED] 12 Jun 2026
The list is people who have contributed to this FAQ now contains over 125 names! You can see the list at the following URL:

    https://approach.droppages.site/official.htm#acknowledgements

If you would like you name added to this esteemed list, please feel free to post some helpful suggestions and answers to the Approach Users Mailing List.

The Approach Users Mailing List
This is a discussion area for anyone uses or provides user support for the Lotus Approach relational database. Subscribers receive an occasional newsletter summarizing recent additions to the FAQ as well as other relevant information and notices. A copy of part 1 of the latest edition of the FAQ is attached to the end of this message.

The Approach Users Mailing List is not moderated.  Any message sent to it is automatically bounced back out to everyone on the list.

The Topic
An on topic message is any message which concerns how to use Lotus Approach software, including relevant books, web sites, helper utilities, and information about new versions that is of broad interest to Approach Users.

Requests for people to do paid work on an Approach database are permissible, but replies to these should go directly to the person concerned and not to the mailing list.

Off topic and personal messages
Anything that is not on topic is considered off topic, including messages intended for a particular person, replies to other off topic messages or spams, and comments about the value of somebody else's response / option / whatever. The list gives you privileged access to many people around the world. Please be considerate, and don't load their email boxes with unnecessary drivel.

People are encouraged to post thank you messages to the people concerned rather than to the mailing list as they are not of general interest.

Similarly, expressions of interest in offers of paid work should be sent to the person offering the work rather than to the mailing list.

The List Owner
I am student, and do this as a hobby, so be nice!

I personally respond to every off topic message in private email. If you see an off topic message on the list, including a subscribe or unsubscribe request, you can assume that I have taken care of it. If you post an off topic message to the list you can expect to hear from me...!?

You can email me (John Brown) at approach-owner@mailman.anu.edu.au

Subscribing, unsubscribing, digests and other options
You can subscribe, unsubscribe, and change your personal options (including digest mode) at:

http://mailman.anu.edu.au/mailman/listinfo/Approach

Subscribers can post to the list APPROACH@mailman.anu.edu.au

If you have any difficulties with the mailing list please email me at approach-owner@mailman.anu.edu.au with "APPROACH" in the subject line.

I hope you find the mailing list useful, and get lots of helpful information from it.

Question and Answer Posts
I suggest you use the following format:

     a) Summarize your question in one sentence, being as specific as you can. E.g. 'Is it possible to save records in alphabetical order?'

     b) In a short paragraph following the question, provide any further information that people may need in order to fully understand your question E.g. 'I find it annoying to have to continually sort the records and it would be easier if I could just save them in their sorted order. Is this possible?'

     c) In a final paragraph state what version of Approach you are using (2.1, 3.0,...) as well as any other software or hardware that you refer to. THIS IS VERY IMPORTANT.

comp.databases
There is no official association between the mailing list and the Usenet newsgroup comp.databases, or any other newsgroup. However comp.databases is a good forum for discussion. Relevant information gathered from the newsgroup will be included in the Newsletter and FAQ. If you do post to comp.databases please include the word APPROACH in the subject line. Most people on the mailing do not regularly access comp.databases.

Lotus
There is official no connection between this mailing list and the Lotus Corporation what so ever. This is a completely unofficial and unconnected mailing list.

Mail storms
A mail storm is caused by a mail server which incorrectly sends error messages to the mailing list instead of the Error_to: address in the message header. When it sends an error message to the list it sometimes causes the same error to be repeated. This goes on and on and our inboxes quickly fill up with recurring error messages. This is what is called mail storm. They are unusual, but one has occurred in the life of this mailing list.

There is nothing I can do to stop this from happening because it is caused by an error in the remote system. I am able to remove the offending address from the mailing list when I log on and discover the problem, but the mail storm will probably have been stopped by the mail server before I do this.

I will however make a complaint to the postmaster of the offending system, asking them to permanently correct the problem.

If this ever occurs again DO NOT SEND ANY MAIL TO THE LIST AS IT WILL ONLY MAKE THE SITUATION WORSE BECAUSE EVERY ADDITIONAL MESSAGE SIMPLY INCREASES THE RATE OF THE MAIL STORM.

I will appreciate your understanding and cooperation with this, as will everybody else on the list.

Disclaimer
This document is provided as is without any express or implied warranties. The editor and contributors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.

Editor
Editor: approach-owner@mailman.anu.edu.au (John Brown)

© Copyright 1995 - 2026  by John Brown, all rights reserved. Permission is given for Approach User Support web pages and associated documents (FAQ, Archive of Scripts, newsletters and mailing list documents) to be linked to or distributed in electronic form (such as email, newsgroups, and the WWW) as long as it is posted in its entirety and contains this copyright statement, and is not for financial gain.