Some implicitly triggered procedures are embedded into the property panels for forms, fields or form objects. For example, the Form Properties panel allows you to set up code that responds to form related events.
Other implicitly triggered procedures are simply ordinary named procedures with a special name. This special name always begins with a period, for example
.Initialize, which is automatically called when a database opens.
The procedure name must be spelled exactly as described in the documentation below, including upper and lower case. So of these three variations on the word
.Initialize, only the first will work.
.Initialize ☞ Ok .initialize ☞ Will not trigger .INITIALIZE ☞ Nope
The operation of many implicit procedures is tightly intertwined with the system’s run loop for dispatching events. See Understanding the Run Loop for more on this topic.
Some implicit procedures can perform different functions depending on what the user has done. To identify the code for each function, add a label at the start of the code for that section.
When a database opens, Panorama checks to see if it contains an
.Initialize procedure, and if so, runs it. You can use this procedure to initialize variables, set up custom menus, pre-sort or pre-select the data … anything that needs to be done automatically whenever the database file is first opened.
Warning: Most procedures can only be triggered from the data sheet or a form. However, the .Initialize procedure will start running immediately when the file is opened, in whatever window happens to be open. If this window is not a data sheet or form, the procedure may not operate correctly. Many procedure statements (sort, group, select, etc.) will not operate properly from a non-data window. If this may happen, the first thing the .Initialize procedure should do is open a form or the data sheet.
Warning: If several databases are opened at once, Panorama will open the databases first, then run the
Note: If the database is opened without any visible windows, Panorama will not run the
Opening a database without running .Initialize – If you want to open a database but skip the
.Initialize procedure, use the File>Find & Open dialog (see “Special Operations Menu” in Find & Open). Locate the database in the dialog, then right click on it and choose Data Sheet Only from the pop-up menu. This will open the database but skip the procedure.
Panorama supports three implicit procedures related to modifying data -
If a database contains a
.NewRecord procedure, that procedure will be called whenever a single new record is added to the database, for example by pressing the Return key or clicking on the Add icon in the toolbar. The info(“trigger”) function can be used to determine which of the three possible actions triggered the procedure:
New.Add ☞ Add New Record tool or menu item New.Insert ☞ Insert New Record tool or menu item New.Return ☞ Return key or Insert New Record Below menu item
Here is a typical
.NewRecord procedure that will not allow new records to be added if there already at 100 or more records in the database:
if info("records")≥100 message "Sorry, this database is limited to 100 records." else if info("trigger") = "New.Add" addrecord elseif info("trigger") = "New.Insert" insertrecord elseif info("trigger") = "New.Return" insertbelow endif endif
.NewRecord example that only allows new records to be added to the end of the database, not inserted in the middle. If the trigger is
New.Insert, or if the Return key is pressed in the middle of the database, no record is added.
if info("trigger") = "New.Add" addrecord elseif info("trigger") = "New.Return" and info("eof") addrecord else message "Sorry, new records must be added"+ " to the end of the database, not inserted in the middle." return endif call .ModifyRecord
.NewRecord procedure is only triggered when individual new records are added, it is not triggered when multiple new records are appended to the database, for example by importing text.
If it exists, the .DeleteRecord procedure will be triggered when the user attempts to delete a record from the database. This procedure could be triggered by the Delete Record tool or menu item, or by pressing the Delete key in data sheet or view-as-list windows. The example below allows records created today to be deleted immediately, but requires confirmation before allowing older records to be deleted.
if Date < today() alertsheet "Are you sure you want to delete this record? "+ "It was created "+pattern(today()-Date,"# day~")+" ago.", "Buttons","No,Yes" if info("dialogtrigger") = "No" return endif endif deleterecord
Notice that the record is not deleted unless the procedure deletes the record. The
.DeleteRecord procedure interrupts the normal deletion process and takes over. This puts you, the programmer, in control.
If it exists, the .ModifyRecord procedure will be triggered when the user modifies any field in the database. Warning: The
.ModifyRecord procedure will not run if a procedure is already running, or if the field has its own procedure (see Automatic Field Code). In those cases you may want the other procedure to call the
.ModifyRecord procedure as a subroutine (more on this in a moment). The
.ModifyRecord procedure is also not called if the data is modified with a command in the Fill menu, see
.ModifyRecord procedure example below automatically marks the latest date and time when a record was modified. This example assumes that the database has two fields for time/date tracking: ModifyDate (a date field) and ModifyTime (a numeric field).
Note: This example illustrates the
.ModifyRecord procedure, but a better way to perform this task would be to use the File>Database Options dialog to designate a numeric (Integer) field as the Time Stamp field. Once a field has been designated as a time stamp field, Panorama will automatically copy the current date and time into this field every time any other field in the record is modified. Setting up a time stamp field allows you to reliably track when each record in the database was last modified. The modification date and time are stored in this field using the SuperDates format.
If your database has other procedures that modify the database, they should call the
.ModifyRecord procedure to make sure that the time stamp is kept up to date. For example, here is a procedure that automatically subtracts one from the QtyInStock field.
QtyInStock=QtyInStock-1 call .ModifyRecord
If it exists, the .ModifyFill procedure will be triggered when the user uses most commands in the Field>Morph sub menu (Morph, Propagate, etc.). The
.ModifyFill procedure will not run if a procedure is already running, or if the field has its own procedure. In those cases you may want the other procedure to call the
.ModifyFill procedure as a subroutine (more on this in a moment).
.ModifyFill procedure example below automatically marks the latest date and time when a fill command is used. This example assumes that the database has two fields for time/date tracking: ModifyDate (a date field) and ModifyTime (a numeric field).
field ModifyDate formulafill today() field ModifyTime formulafill now()
If your database has other procedures that use fill statements they should explcitly call the .ModifyFill procedure to make sure that the time stamp is kept up to date. The example below selects all items that have over 500 in stock and have not been touched in 30 days. For those items, it reduces the price by 10%, then marks the modification date and time.
select QtyInStock>500 and today()-ModifyDate>30 field Price formulafill Price*0.90 call .ModifyFill
.ModifyFill procedure is not triggered by a FormulaFill statement in a procedure, the procedure must update the modification date and time itself by explicitly calling
If it exists, the .CurrentRecord procedure will be triggered when the user shifts to a different record. For example, this procedure is triggered when you move up or down in the database with the vertical scroll bar, or with the Find or Find Next commands. It could be used to initialize a variable or graphics object to display the newly current record. However, we recommend that you keep this procedure as short as possible, and avoid it altogether if you can, since it can cause performance problems. This procedure should definitely not ever change the current window or display a dialog or alert, and it should not ever cause a further shift in the database position (for example doing a further search). It’s possible to get Panorama into an infinite loop where the only way to stop is to force quit the program (or reboot the entire computer).
It’s possible to create a log of all changes made to a database, so you can see who changed what when. A Simple Journal example database illustrates this.
As the illustration above shows, the Journal field contains a log of all changes made to the database. This log is created by the
.ModifyFill procedures in the database. Here is the
if info("modifiedfield")="Journal" rtn endif let cellValue=grabdata("",info("modifiedfield")) let jline="«"+info("modifiedfield")+"»="+constantvalue("cellValue")+ " on "+datepattern(today(),"YYYY-MM-DD")+" @ "+timepattern(now(),"hh:mm:ss") Journal=jline+sandwich(cr(),Journal,"")
And here is the
let wasField=info("fieldname") let jprefix="«"+info("modifiedfield")+"»=" let jsuffix=" on "+datepattern(today(),"YYYY-MM-DD")+" @ "+timepattern(now(),"hh:mm:ss") field Journal if datatype(info("modifiedfield"))="Text" formulafill jprefix+quoted(grabdata("",info("modifiedfield")))+jsuffix+sandwich(cr(),Journal,"") else formulafill jprefix+str(grabdata("",info("modifiedfield")))+jsuffix+sandwich(cr(),Journal,"") endif field (wasField)
One possible problem with this logging mechanism is that the log quickly becomes larger than the actual data! We’re sure, however, that some of you will find this a valuable tool.
Panorama can run your custom code when certain form related events occur, including opening the form, making the form the front window, and resizing the form. Place the code in the Form Properties panel.
formOPEN: ☞ triggered when form is first opened formFRONT: ☞ triggered when form becomes the front window formRESIZE: ☞ triggered when form is resized
The labels are case-sensitive and must match the examples above exactly. Here is an example of form event code that customizes what happens when the form is opened or brought to the front. This example assumes that the window is opened with the openform clone option, so that there could be multiple open instances of this form. When the form first opens, the contents of the ID field are saved into a variable, and the text editor object Letter is opened. Later, if a differnt window is brought to the front and then this window is brought back to the front, the code automatically searches for the record that corresponds to that window, bringing the database back to the same spot. (There is no formRESIZE label, so nothing extra happens in that case. For performance reasons, you should omit any label you aren’t using.)
formOPEN: letwindowglobal windowID=ID objectaction "Letter","open" return formFRONT: windowglobal windowID find ID=windowID return
Here is what this code looks like in the Form Properties panel.
Keep in mind that this code is only triggered for events involving this form. If you want other forms to be customized, you’ll need to add code to their properties panel. (However, you can use a subroutine to share code between different forms, see below.)
If you prefer, you can move the form event code to a separate named subroutine, and call it from the form properties panel. This can be more convienient for editing and debugging, and also allows you to share the same code among multiple forms. If you do this, the code in the form properties panel can only contain one statement – the call statement that calls the subroutine. If Panorama sees just this one statement, it will look in the subroutine for the event labels. If there is more than one statement, the event labels must be in the properties panel.
|10.1||Updated||Form event procedures automatically trigger when form opened, brought to front, or resized.|
|10.0||No Change||Carried over from Panorama 6.0|