A report is a tool for printing data to get an organized, formatted hard copy of your data. You can communicate data with presentation-quality reports. Paradox's built-in report writer is a high-quality tool that will suffice for the majority of your printouts. With the combination of reports and queries--and the ObjectPAL commands that enable you to use them--you can add sophisticated printing capabilities to your applications.
Using Reports in ObjectPAL
For manipulating reports, ObjectPAL offers the report object. The report object (or variable) is a handle to a report window. With a report variable, you can attach to an already opened report or you can open a report. After the handle is established, you can manipulate the report. If you browse the online help and explore the Form object type, Report type, and TableView object type, you will see that many methods and procedures are shared among them.
Opening a Report
The next bit of code demonstrates opening a report and setting the title of the report. To achieve a smooth opening of the report, note that WinStyleDefault + WinStyleHidden is used along with the show() method. Change working directories to Paradox's Samples directory and type lines 3--9 into the pushButton event of a button.
1: ;Button :: pushButton 2: method pushButton(var eventInfo Event) 3: var 4: r Report 5: endVar 6: 7: r.open("Customer", WinStyleDefault + WinStyleHidden) 8: r.setTitle("New report title") 9: r.show() ;You can also use bringToTop(). 10: endMethod
Printing Reports
The first step in learning how to handle reports with ObjectPAL is learning how to print an existing report. The next two examples demonstrate how to use the print() and open() report methods.
Suppose that you want to create two buttons on a form. The first button directly prints an existing report with no interaction from the user. The second button previews the report and prompts the user with the Print File dialog box.
Step By Step
Change your working directory to Paradox's Samples directory. Create a new form and place two buttons on it. Label the first button Print Report and the second Open then Print Report, as shown here:
Illustration 1
[Need Image Here]
Add lines 3--7 to the pushButton event of the Print Report button. Line 4 declares r to be a Report variable. In this case, r is a temporary variable, alive only for the duration of the method. This is important to note because, with display managers, the existence of the object (the report, in this case) does not close when the variable is destroyed. This is not true with all objects. For example, OLE and TCursor objects automatically close when the variable is destroyed. To optimize this form, you can combine these two declarations into a single declaration higher up in the containership path. In line 7, print() is used to print the report without first previewing it. You do not need to open the report first.
Add lines 3--8 to the pushButton event of the Open then Print Report button. Lines 7 and 8 on the second button use open() to open the report to preview it, and use print() to display the Print File dialog box.
Check your syntax, save the form as REPORT1.FSL, run the form, and press the Open then Print Report button (see Figure 12-1).
Figure 1: The Open then Print Report button previews a report and displays the Print File dialog box
[Need Image Here]
Printing a Report Based on the Current Record
In general, reports deal only with a set of data. They step through the master table one record at a time and print the report information once for every record. Often, you need to print a report based on a subset of the table or even just a single record. There are several techniques for doing this in Paradox, including using a query and using ObjectPAL. Both are demonstrated in this section. The query technique takes two steps. First, execute a query. Then, print a report that is based on the answer table for the query. In the following example, you print a report based on only the current record.
Using a Query to Print a Report Based on the Current Record
Suppose that you want to print a report based on the current record. This example uses an embedded query to generate a table with a single record in it. It also demonstrates using a temporary table to control the report.
Step By Step
Create a query with all the fields checked based on ORDERS.DB and run it. This generates an ANSWER.DB table in your private directory, which is used in step 2.
Change your working directory to Paradox's Samples directory and create a new form with the :PRIV:ANSWERS.DB, ORDERS.DB, and LINEITEM.DB tables in the data model.
Link the :PRIV:ANSWERS.DB table to the Orders table in a 1:1 relationship.
Link the Orders table to the Lineitem table in a 1:M relationship. Place a button on the form and label it Print Current Invoice. Note that you must initially create the :PRIV:ANSWER.DB table in order to link it.
Add lines 3--19 to the pushButton event of the Print Current Invoice button. If you would rather view the report on the screen then print it, substitute r.open("ORDER") for line 19 below. Line 9 takes the value from the Order_No field and puts it in the s variable. Lines 10--16 make up the query; line 14 uses the s variable. Line 18 executes the query, and line 19 prints the report. Because the report is based on the table created by the query executed in line 19, it consists of only one record.
1: ;Button :: pushButton 2: method pushButton(var eventInfo Event) 3: var 4: r Report 5: q Query 6: s String 7: endVar 8: 9: s = Order_No.value 10: q = Query 11: ANSWER: :PRIV:ANSWER.DB 12: 13: ORDERS.DB | Order No | 14: | Check ~s | 15: 16: EndQuery 17: 18: executeQBE(q) 19: r.print("ORDER") ;To see report, use r.open("ORDER") instead. 20:
endMethod
Check your syntax, save the form as REPORT2.FSL, and run it. Move to the record that you want to print, and click the button.
Fully Controlling a Report
An alternate and perhaps easier technique to print the current record and maintain full control of a report is to use setGenFilter(), setMenu(), and wait(). setGenFilter() is used to filter the data down to the current invoice. setMenu() is used to keep the user from using other menu options. Finally, wait() is used so that the user doesn't stray from the report. Following is the code in full for you to study:
1: ;btn :: pushButton 2: method pushButton(var eventInfo Event) 3: var 4: r Report 5: m Menu 6: pop PopUpMenu 7: dynFilter DynArray[] String 8: endVar 9: 10: ;Load the report hidden. 11: if not r.load(":INVOICE:INVOICE", WinStyleDefault + WinStyleHidden) then 12: errorShow() 13: return 14: endIf 15: 16: ;Apply filter. 17: dynFilter["Order No"] = String(Order_No.value) 18: r.Order_No.setGenFilter(dynFilter) 19: r.run() 20: 21: ;Give the user only a simple menu. 22: pop.addText("&Print...", MenuEnabled, MenuFilePrint) 23: pop.addText("&Printer Setup...", MenuEnabled, MenuFilePrinterSetup) 24: pop.addText("&Close", MenuEnabled, MenuControlClose) 25: m.addPopUp("&File", pop) 26: r.setMenu(m) 27: 28: ;Show report and wait. 29: r.maximize() 30: r.wait() 31:
endMethod
Caution: Line 17 in the above code works fine if the field's display attributes do not display the thousands separator. If you are having trouble adopting the above code to your application and cannot adjust the display attributes for the field, then strip out the thousands separator using ObjectPAL's format() method. For example, replace line 17 above with dynFilter["Order No"] = format("EI",Order_No.Value).
Manipulating the Printer Before You Print
ObjectPAL offers the capability to manipulate the printer before you print. The System type procedures printerGetInfo(), printerGetOptions(), printerSetCurrent(),printerSetOptions(), and enumPrinters(). For example, to get a listing of all the available printer setups, type lines 3--8 into the pushButton event of a button.
In addition to these System type procedures, the Report type print() method can control the printer.
Using the reportPrintInfo Variable
Another option for changing printer orientation is to set reportPrintInfo.orient = PrintLandscape. The following code fragment prints a report called Customer.rsl in landscape orientation.
Paper size cannot be set this way, but the number of copies, page incrementing, and starting and ending pages can be set--if the printer driver supports these features. To find out whether your printer driver supports these features, select File | Printer Setup: . If the Printer Setup dialog box provides fields for you to specify the number of copies, starting and ending pages, or page incrementing, then your printer driver does handle these features; otherwise, it does not.
You also can use this technique to change the master table of a report. The following code demonstrates this technique:
Other than menus that use the standard menu constants, you can't use user-defined pull-down menus with a report, because reports don't have ObjectPAL. Specifically, reports don't have a menuAction event to which you can add code. There is, however, an alternative solution. Create a small toolbar-style dialog box, then open the dialog box over the report. That is, after you preview the report, open the dialog box. Because dialog boxes are always on top, the options that you put on the dialog box are always seen; therefore, they are always active. In the following example, you launch a report and a dialog box from a button.
Adding a Toolbar to a Report
Suppose that you want to launch a report and a dialog box from a button and have the dialog box control the report. Because the second form is a dialog box, it always stays on top of the report and gives the user easy control over the report.
Step By Step
Change your working directory to Paradox's Samples directory. Create a new form or open an existing one, and place a button on it. Label the button Go, set the title of the form to Report Form (see the illustration shown next), and save the form as REPORT3.FSL.
Illustration 2
[Image Here]
Add lines 3--19 to the pushButton event of the Go button on the main form and save it as REPORT3.FSL. Lines 4 and 5 on the Go button of the first form declare f as a Form variable and r as a Report variable. Lines 8--13 use a Report variable to either open the report or attach to it and bring it to the top if it's open. Line 12 specifically sets a title when the report is opened. This is done so that later you can attach a Report variable to the open report. Lines 15--19 either open the small dialog box form or attach a Form variable to it and move to the small dialog box.
1: ;Button :: pushButton 2: method pushButton(var eventInfo Event) 3: var 4: f Form 5: r Report 6: endVar 7: 8: if r.attach("Report 1") then 9: r.bringToTop() 10: else 11: r.open("Customer") 12: r.setTitle("Report 1") 13: endIf 14: 15: if f.attach("Options") then 16: f.moveTo() 17: else 18: f.open("REP-MENU") 19: endIf 20: endMethod
Now, create another new form with four buttons on it, set the form title to Options, make it a dialog box, and save it as REP--MENU.FSL. Label the four buttons Design, Maximize, Go to page 5, and Close, as shown here:
Illustration 3
[Image Here]
Open the REP--MENU.FSL form that you created in step 2 and add line 3 to its Var window of the Page object. Line 3 on the dialog box form declares r as a Report variable. The r Report variable is used throughout the dialog box to deal with the open report.
1: ;Page :: Var 2: Var 3: r Report 4: endVar
Add lines 3--8 to the open event of the page object of the REP--MENU.FSL form.
1: ;Page :: open 2: method open(var eventInfo Event) 3: if r.attach("Report 1") then 4: r.bringToTop() 5: else 6: msgStop("Startup Error!", "This form is only for use with Report3.fsl") 7: close() 8: endIf 9: endMethod
Add line 3 to the pushButton event of the Design button on the REP--MENU.FSL form.
Add lines 3--5 and 12--16 to the depart event of the REP--MENU.FSL form.
1: ;Form :: depart 2: method depart(var eventInfo MoveEvent) 3: var 4: f Form 5: endVar 6: 7: if eventInfo.isPreFilter() then 8: ;This code executes for each object on the form 9: else 10: ;This code executes only for the form 11: if f.attach("Report Form") then 12: f.moveTo() 13: else 14: f.open("REPORT3") 15: endIf 16: endIf 17: endMethod
Check your syntax and save both forms. Close the REP--MENU form and run the first REPORT3.FSL form. Click the Go button. The report opens first, and the dialog box opens on top of it. You can select either the pull-down menus or the buttons from the dialog box form that you created. It doesn't matter whether the report or the dialog box is active (see Figure 12-2).
Figure 2: REP--MENU.FSL and a report; the form always stays above the report because it's a dialog box
[Image Here]
Summary
In this chapter, you learned about using reports and integrating them into your application. You learned how to run a report using the open() method, how to print a report with the print() method, and how to control a report with a query and with setGenFilter() and setMenu(). You also learned how to manipulate the printer before you print.