You are here:

Foxfire! allows you to control many aspects of its operation by modifying the basic feature set. This is made possible by dividing its core logic into two modules. The main program (FOXFIRE.APP) controls the sequence and details of regular processing, and a “custom-code container” module (FFCONFIG.PRG) holds certain variables and custom program logic for each process. 

FFCONFIG.PRG is really a very large Case statement with 22 section headers corresponding to key events that occur during the execution of the main module. The section headers are referred to as “hooks”. Initially, each hook contains a Case header, comments to explain its purpose and operation, variables that can be modified to affect processing during this event, and a Return statement to end the section and return control to the main program. 

As the main Foxfire! program executes, it passes control to the appropriate hook in FFConfig.prg, executes the custom code inserted there, and then returns to the main program. The custom code may alter the value of the Foxfire! variables, circumvent or alter the logic of an existing process, or add an entirely new feature (such as a data acquisition procedure that enables a user to create a “reporting database” from a collection of unrelated application databases). Whatever the modifications, the effect is to append the additional logic in the container to that of the main program, so it is executed before, after, in addition to, or instead of the logic of the regular Foxfire! event (depending on the timing identified by the name of the hook e.g. “Before Output”). 

Besides the 28 hooks for general program logic, there are four hooks in each Request definition record, which provide opportunities for custom processing related to that Request. 

A big advantage of this approach is that since FFCONFIG.PRG is not bound into the main Foxfire! application, you can make substantial changes in what Foxfire! does, but still have only one program to recompile before you test your work. You will also be spared the trouble of re-applying your changes when your version of Foxfire! is updated. 

Using the Event Editor in the System Administrator tools, you can modify FFConfig.prg in an organized and controlled fashion. 

Mastering the Foxfire! Event model 

When you use Event Editor, you will be presented with a list of Events. For each Event, you may set Variables or Source Code. The Variables are each documented with their purpose and usually have a default value specified by Foxfire! The Source Code is well commented and is self explanatory. The key to mastering the use and modification of FFConfig is to understand the individual Events. We will present and explain the Events here. 

General Comments on Events: While most events already have defined code, some do not. You will usually add code to the standard Foxfire! Event code, not change the existing code. If an event has associated local variables, the variables and their default values will be displayed on the Variables pane of the FFConfig Generator screen. You can modify the variable’s values to modify the behavior of Foxfire! You may also add custom variables that can be used in your custom code. 

If you are modifying an Event that has no pre-defined code then your custom code will execute when the event fires along with the standard Foxfire! behavior for the Event in question. 

All Events represent actual behaviors within Foxfire! The code that is contained in FFConfig is the code that you can modify. Your modified code will run when the associated Foxfire! event fires. 

Events 

Header: FFConfig program header – you will probably not need to change this. This contains the start of the FFConfig DO CASE statement that drives the Event model. 

Startup: Adjust memory variables which control the basic appearance of Foxfire! and the way in which is saves and/or adjusts the runtime environment. Other settings should be adjusted in the GLOBAL SETUP phase. 

Note: Report Sets were called Preference Sets in previous versions of Foxfire!. 

Preference File Setup: Open and/or position the Report Set table to the desired record. The choice of a Report Set record determines nearly all aspects of how Foxfire! can be used, so controlling Report Set record selection upon startup is very important. 

The task is to determine which Report Set record should be used initially. 

A default Report Set record name is contained in the variable m.lcWhatPref. This variable can be changed in FFCONFIG. 

Determining which Report Set record will be used first is important, because Foxfire!’s security and privilege features depend on the Report Set record. Therefore, it follows a fairly elaborate set of rules. 

The order of precedence is: 

Is there a Report Set table open (alias FFPREFER)? If yes, use the current record from that table and do not execute the PREFERENCE FILE SETUP phase of the configuration program, and return control to the calling program. If no, execute that phase, starting at step 2

Attempt to open a Report Set table (by default, FFPREFER.DBF). If not successful, warn the user and terminate. If the file is opened, and only one record is present, use that as the initial Report Set and return control to the calling program. Otherwise, go to step 3. 

If a file FOXFIRE.INI exists, open it and grab the first line, using that as the name of a Report Set to seek (jump to step 6). If the file is not present or cannot be opened, go to step 4

If a DOS environment variable FOXFIRE.INI exists, use its value as the name of a Report Set to seek (jump to step 6). Otherwise, go to step 5. 

If the variable m.lcWhatPref is not empty, use its value as the name to seek (go to step 6). Otherwise, jump to step 7. 

Using the name determined above, search the table for a record with a matching name. If found, use that as the initial Report Set record and return control to the calling program. If not found, go to step 7. 

If no value for an initial Report Set can be determined, Foxfire! looks in the Report Set table to see if there are one or more records. If none, an error message is issued. If only one record is found, Foxfire! uses that as the initial Report Set. If more than one record is found, Foxfire! displays a list of Report Set names and allows the user to select one. If the user chooses to cancel without selecting a Report Set, Foxfire! terminates. If the user does select an initial Report Set, it returns control to the calling program. 

If none of these conditions apply, Foxfire! issues an error message and terminates operation. 


Note: Users with System Administrator access may change the startup Report Set by choosing Startup in the Choose a Report Set dialog, which writes a FOXFIRE.INI file. 

Note: Report Set variables are first initialized and given their default values when Foxfire! starts up. This provides Foxfire! with defaults that can be overridden. You tailor the configuration using the values from a particular Report Set record. 

Global Setup: Called after all system variables have been initialized and the current Report Set record has been read into memory with SCATTER MEMVAR. 

This is where you set any system-wide preferences. These include the global preferences that use the pg_ variable names. The pg_ variables provide for customization and control over many of Foxfire!’s most important features. 

You might also open any of your application’s tables you expect to need for filter value validation or SQL SELECTs. (Be sure to remember to close them in the CLEANUP phase.) 

If you define any variables, you can declare them PUBLIC here and then RELEASE them in the CLEANUP phase. It is best to define them here so that they will be scoped throughout all of Foxfire! execution. 

Note: See FFCONFIG.PRG for detailed comments on all of the variables that may be assigned values during this phase. 

Request Setup: Called when Foxfire! opens a specific Request. 

The events that invoke this phase are calls to:

Edit, Run, Count, or Preview a Request 

Create a New Request 

Show SQL 

These are the pertinent facts about the REQUEST SETUP phase: 

The Request name is stored in m.rq_name. 

When the user calls for a new Request, m.rq_name will be empty. 

The description of the Request type is stored in m.rq_reqtype. The descriptions of the four types of reports, as displayed in the New Request – Type and Style dialog, are user-customizable and are stored in lb_ variables. 

The four variables (and their un-customized values) are: 

m.lb_basic = “Detail” 

m.lb_summary = “Summary” 

m.lb_xtab = “Cross-Tab” 

m.lb_labels= “Labels” 

Before SQL Generation: Adjust the Request or the operating environment to affect the way Foxfire! will generate the SQL query to be executed. There is a parallel “hook” available on a Request-specific basis using the Before SQL Generation, Execute field of the Custom Processing dialog on the Request Design Workbench. 

Before FRX Generation: Examine or adjust the memory variables which determine the form and attributes of the layout that gets generated by Foxfire!. Although the name mentions “FRX” (report form) explicitly, it is also called when the layout is a label form (LBX). 

After FRX Generation: Make any necessary changes to system variables to reverse changes made in the BEFORE FRX GENERATION phase. You can also make modifications directly to the generated layout. 

Before Select: Called just before the SQL SELECT is executed. If you are running with pf_keepopn = .f., you can use this phase to open any tables that might be required for special routines. 

For Example: You have placed a UDF in a data item’s expression. This “UDFs: opening a table” UDF requires a lookup to a table not explicitly named anywhere outside of the UDF. You can open the table here. 

You may also use this phase to prevent Foxfire! from executing its own SQL query, if for instance you want to write your own query and substitute the results for those Foxfire! would have retrieved. 

The BEFORE SELECT phase contains extensive comments detailing the names of Foxfire! variables that hold the Request selections from which the SQL SELECT command is constructed. In particular, all filter specifications are available in a group of arrays. Values entered in response to a set of “Ask at Runtime” prompts are stored in the same arrays. This allows you to manipulate or supercede the SQL SELECT command yourself. 

If you construct your own SQL SELECT statement programmatically (which might be intended for another platform via ODBC, for instance), set the value of pg_SQLEXECUTE to .f. This tells Foxfire! to bypass execution of the SQL SELECT command entirely, but to continue with reporting. 

There is a parallel “hook” available on a Request-specific basis using the Before Query, Execute field in the Output Selector’s Special Processing dialog on the Report Design Workbench. 

After Select: Called after the SQL SELECT has been executed and before any output processing (that is, reporting) has started. This allows you to customize the processing on the temporary SQL result table. 

Note:

The result table is a cursor and is open in the current work area. 

If the output type for a Request is Data-Array, then the result table is an array (and not a cursor). 

The result table bears an alias identical to the Request name. 

You are free to create a new DBF here before processing, and perform other such data manipulations. 

The current work area is significant. After leaving this case statement, Foxfire! uses the current work area as the basis for further Request processing. If your code moves the focus to a new work area, be sure to return to the original one before leaving this phase. (or insure that the new work area is appropriate for reporting) 

There is a parallel “hook” available on a Request-specific basis using the After Query, Execute field in the Output Selector’s Special Processing Options dialog on the Request Design Workbench. 

Example: Two-Pass Reporting 

FoxPro’s query and reporting mechanism is based on passing through the data a single time to generate output. This achieves very high performance, but certain types of results are impossible in this model. An example which requires two-pass reporting is if your report needs to show groups sorted by a percentage of total. You can’t calculate the percentages of total without passing the data once to calculate the totals. A second pass is needed to sort the data prior to output. 

Using Foxfire!’s AFTER SELECT phase, supplemental processing (including a possible second pass for calculation purposes) can be achieved before the result is output. To see this in action, view the sample Request QUARTILE. Quartiling is an example of a simple calculation that requires a second pass through the data. You can’t split the result records into four groups until you know how many there are, and how they are sorted. 

The code that makes the second pass (to assign quartiles, based on the results derived in the first pass) must be invoked after the first pass has been performed but before the report is run. The code (in FCONFIG.PRG) transforms the Request’s result cursor into a table, opens it in the same work area that the cursor formerly occupied, then calculates the quartile standings and writes an appropriate value into each record for proper sorting. The combination of this code with the “native” first pass yields a “two-pass reporting” result. 

Because this type of activity imposes an additional processing step, the code is written in such a way that it is only activated if the “Quartile” data item is used in the Request. Alternatively, the code could have been invoked from the After Query, Execute field in the Output Selector’s Special Processing dialog, which can contain the name of a UDF or other expression to be executed. The AFTER SELECT phase, which fires at roughly the same point in the processing of the Request, is global (executed for every Request) unlike the After Query, Execute field, which is Request-specific. Depending on your requirements, it may be better or more maintainable to keep code at the global level or the Request level. In particular, if the code performs a lot of work, it’s best to avoid having it executed for every Request. In such a case, using the After Query, Execute hook for specific Requests may be more appropriate. 

Before Report: Modify processing immediately before or after Foxfire! actually runs the report with the REPORT FORM… command. You can also replace the REPORT FORM… command with your own, if you wish to integrate GENREPOX, or replace FoxPro’s preview with a Web browser, etc. 

After Output: Perform any follow-up processing required after Foxfire! has produced the specified output. For example, if running your Request produces a file and you want to automatically send that file out as an e-mail attachment, you could write code here to perform the transmission. The number of records actually retrieved by the query is available in m.SQL_TALLY. There is a parallel “hook” available on a Request-specific basis using the After Output, Execute field of the Custom Processing dialog on the Request Design Workbench. 

Batch Builder Add: When a Request is added to the batch. 

Batch Builder Run: When a batch is selected and Run is chosen. 

Cleanup: Called just before Foxfire! exits. Any preexisting states or conditions that need resetting should be attended to here. This includes releasing any public variables you may have declared in the GLOBAL SETUP phase, closing of tables you may have opened in an earlier phase, and so on. 

Queue Job: Called when a job is placed in the queue for the Server. 

Note: Server features are not available in Developer’s Edition or standalone versions. The client/server version is available separately. 

Before Email: Called before sending an Email. If you don’t want to use Foxfire! built-in Email support, you can set the value of Ret_val to .F. and reference the values yourself here. 

After Email: Called after sending an Email. 

Pdf Output: Called if you want to use your own PDF Output engine. 

Before Import: Called before importing a Request. 

After Import Success: Called after successfully importing a Request. 

Before Export: Called before exporting a Request. 

After Export Success: Called after successfully exporting a Request. 

New Server Record: Called after a new server record is added. 

Note: server features are not available in Developer’s Edition or standalone versions. The client/server version is available separately. 

Before Connection: Called just before an attempt to connect to an ODBC data source. 

After Connection: Called just after an connection to an ODBC data source. 

Before Disconnect: Called just before an attempt to disconnect from an ODBC data source. 

After Disconnect: Called just after an disconnection from an ODBC data source. 

After User Login: Called just after a user login. 

End: The end of the FFConfig program. This contains the ENDCASE statement that closes the Event model.

Table of Contents