790.TXT ===================================================================== Using PAGEMODE ===================================================================== PRODUCT: R:BASE VERSION: 4.5++ or Higher ===================================================================== CATALOG: Forms, Reports & Labels AREA : Reports PAGEMODE is an exciting new feature in R:BASE 4.5 ++ that allows you to create reports with endless possibilities. PAGEMODE allows you to: Produce multi-column reports Create reports from tables with many-to-many relationships Create reports with more than one detail section Design different layouts for different pages of the report Create reports with pages defined longer than 84 lines long or wider than 255 characters Format odd and even pages differently Customize the report to always place break headers and footers together on the same page Place a different page header on each page Number every other page of a report Have fully conditional data printing, for example, don't print a detail section if the balance is zero PAGEMODE does not use the regular R:BASE report writer. Instead, you use the R:BASE programming language to create the report definition. The R:BASE report writer processes data a row at a time and then prints that row. With PAGEMODE, a report is processed a page at a time. A virtual page (many rows of data) is laid out in memory, then sent to the printer or to a file. This, combined with the power of the R:BASE programming language is what allows you to create so many different and complex reports. The DECLARE CURSOR command is usually used to retrieve the data for printing a report created with PAGEMODE. For additional information and tips on using DECLARE CURSOR, refer to the July/August 1994 issue of the Exchange. The virtual page is defined by the current settings of LINES and WIDTH. Once PAGEMODE is ON, these settings cannot be changed. A report page is laid out in memory, then sent to the printer or a file using either the NEWPAGE or OUTPUT screen commands. To lay out the page, use the WRITE or SHOW VARIABLE commands with AT to place the data in a particular location, or use SELECT with the new SELMARGIN setting to designate the starting column for the output. Because the R:BASE programming language is used to create a PAGEMODE report, you need to approach the report definition task differently. Creating reports using PAGEMODE is not as easy as using the regular R:BASE report writer; you can't see the layout on the screen as you create the report. Also, since you are writing a program, many iterations are usually needed to get the layout just right. The Basic PAGEMODE Program Structure When creating a PAGEMODE report, you follow the same basic structure for all reports: Define row and column location variables Use a DECLARE CURSOR to retrieve the data Use the row and column variables to display the data Send the completed page to the printer Variables are set up to hold the row and column numbers for displaying data. These variables are controlled and modified by conditions in your program. In addition to row and column location variables, you may also want row count or line count variables to indicate actual location on the virtual page. The variables display data in columns and tell when you have reached the end of a page. A page in a report created with PAGEMODE is not a physical thing, but a virtual page that you have defined using the LINE and WIDTH settings in R:BASE. You define the virtual page, place text and data on the virtual page, then send the page to the printer or to a file. A PAGEMODE report prints a page at a time, but the data is still retrieved and processed a row at a time. After defining the page size using the LINES and WIDTH settings, PAGEMODE is invoked by the SET PAGEMODE ON command. PAGEMODE is not relevant to screen display; you must set your output to a file or the printer when placing data on a virtual page. You have the most control over data placement by using the DECLARE CURSOR command to retrieve the data. DECLARE CURSOR retrieves the data from the specified table or view a row at a time. You then manipulate the data from that row, determine where to display it on the virtual page and get the next row of data. As you get each row of data, variables are checked in your program to see if a break has occurred or end of page has been reached. The NEWPAGE or OUTPUT SCREEN commands are used to send the current virtual page and get the next page ready for data. Working with Page Layout The virtual page size is determined by the settings for LINES and WIDTH when PAGEMODE is turned ON. LINES and WIDTH cannot be changed while PAGEMODE is on. To change the page size within a report, turn PAGEMODE OFF, change the LINES or WIDTH setting, and then turn PAGEMODE back on. The size you set determines where you can place data on the virtual page. Row and column positions for display are specified as literal values or are kept in variables, for example, vRow and vColumn. Using variables makes it easy to display data on the page one row after the other. Note that you cannot use expressions with the AT row, column option; you must set the correct row and column values first using SET VARIABLE. The vRow variable can be used to check for end of page, or you can set up a row counter variable, vRowCount, for example. There is no automatic form feed at the end of a page in a PAGEMODE report. You control the form feeds (new pages) through your program code by sending a NEWPAGE or OUTPUT SCREEN command. Thus, your program needs to be aware of the current setting for LINES so you can test for the end of a page. The current row count is compared to a constant value or to the LINES setting to check for the end of page. The CVAL function is used to place the current line setting in a variable. For example, SET VARIABLE vLines = (CVAL('LINES')) Then, an IF statement compares the vLines variable with the vRowCount variable. For example, IF vRowCount >= .vLines THEN NEWPAGE SET VARIABLE vRowCount = 1 SET VARIABLE vRow = 1 SET VARIABLE vColumn = 1 ENDIF The vRowCount, vRow, and vColumn variables must be reset for each new page. Leave a five line margin at the bottom of the page by comparing vRowCount to vLines minus five. For example, IF vRowCount >= (.vLines - 5) THEN NEWPAGE SET VARIABLE vRowCount = 1 SET VARIABLE vRow = 6 SET VARIABLE vColumn = 5 ENDIF Margins at the top of the page and on the left side are determined by the initial settings for the vRow and Vcolumn variables. You can calculate the number of rows to print for the next data item, and compare that to see if all the data will fit on a page. This calculation is used to make sure break header, detail, and break footer information all appear on the same page. For example, SELECT COUNT(*) INTO vNumToPrint + FROM transmaster WHERE custid = .vCustid IF (.vRowCount + .vNumToPrint) >= .vLines THEN NEWPAGE SET VARIABLE vRowCount = 1 SET VARIABLE vRow = 6 SET VARIABLE vColumn = 5 ENDIF There are many different ways to keep track of the number of rows that have been printed on the page. The important thing to remember is that you need to keep track of it and issue the form feed yourself; it does not happen automatically. Checking Column Width PAGEMODE reports easily print data in a columnar format. Decide on the column width based on the length of data to display, then simply set variables to contain the starting column position for each column. For example, SET VARIABLE vColumn1 = 5 SET VARIABLE vColumn2 = 30 SET VARIABLE vColumn3 = 55 ... WRITE .vName AT .vRow, .vColumn1 WRITE .vName AT .vRow, .vColumn2 WRITE .vName AT .vRow, .vColumn3 The above commands are used to display data in three columns evenly spaced across the page with a four space margin on the left side of the page. You determine in your program whether the columns go across the page and then down, or first down the page and then across. You should set up the column positions at the beginning of your program rather than automatically incrementing the column display variable by a certain amount and checking against the WIDTH setting. The number of columns can be dynamically calculated based on the maximum length of values to display for a group of data. Use the SLEN and MAX functions to find the maximum data length for the group of data. -- compute the maximum text length SELECT MAX(SLEN(title)) INTO vLen + FROM books WHERE lstnm = .vLName -- set the number of columns based -- on the maximum text length IF vLen <= 25 THEN SET VAR vNumCols = 3 SET VAR vColumn1 = 5 SET VAR vColumn2 = 30 SET VAR vColumn3 = 55 ELSE IF vLen <= 50 THEN SET VAR vNumCols = 2 SET VAR vColumn1 = 5 SET VAR vColumn2 = 40 ELSE SET VAR vNumCols = 1 SET VAR vColumn1 = 5 ENDIF ENDIF Set SELMARGIN to specify the beginning column for SELECT command output. SELECT and SELMARGIN can be used with WHERE CURRENT OF CURSOR to display a row of data starting at any column position. The example programs show detailed code for setting the column width. Ways to Display Data in PAGEMODE The key to using PAGEMODE is working with the data layout. There are three commands used to display data on the virtual page: WRITE, SHOW VARIABLE, and SELECT. WRITE The WRITE command is the one you'll use the most. You can specify a display width and wrap text using the new =w option, format the data directly with the USING format option, specify the page location using AT row column, and display either variables or literal text. The WRITE command does not reposition the cursor, however, so it is not as easy to use with wrapped text. SHOW VARIABLE The SHOW VARIABLE command is similar to the WRITE command in that you can display variables at a specific location using AT row column, and you have the =w option for wrapping text. You don't have the formatting options, however, and can only display variables, not literal text. To format data, first execute a separate SET VARIABLE command using the FORMAT function. Literal text is first placed in a variable, and then shown. The SHOW VARIABLE command repositions the cursor, which makes it easier to use with wrapped text. SELECT The SELECT command is not as easy to use or as useful as the WRITE and SHOW VARIABLE commands. There is no straightforward way to limit the number of rows displayed unless you use SELECT with DECLARE CURSOR, which displays the current row only. You can select only columns specified in the cursor declaration; you can't use any expressions or functions in the SELECT command. To specify a column display width, the =w option must be included in the cursor declaration. Also, SELECT does not have an AT row column option, but displays at the current cursor location. There are situations where SELECT is applicable, however. When using it, SET HEADING OFF, and use SET SELMARGIN to designate the starting column for display. Wrapping Text All three commands that display data will wrap text or note data; however, even though the WRITE command wraps lines, you have no way of knowing how many lines were wrapped and where the current cursor position is. WRITE does not reposition the cursor location. Both the SHOW VARIABLE and SELECT commands reposition the cursor to the next row after the command is completed. When using SHOW VAR or SELECT, both of which reposition the cursor to the next row, ISTAT('PAGEROW') is used to indicate the new row position. PAGEROW is a new ISTAT function for use with PAGEMODE reports only. For example: SHOW VAR vNoteVar=40 AT .vRow, .vColumn SET VAR vRow = (ISTAT('PAGEROW')) If using the WRITE command, calculate the number of lines to wrap by taking the length of the variable and dividing by the width wrapped in. This gives you the number to increment the vRow variable by. For example: SET VAR vLength = (SLEN(.vNoteVar)) SET VAR vWrap = (NINT(.vLength / 40)) WRITE .vNoteVar=40 AT .vRow, .vColumn SET VAR vRow = (.vRow +.vWrap + 1) Creating the Different Report Sections In a report created with PAGEMODE, the report sections aren't made for you; you create them in your program code. Following are guidelines for creating the various report sections. As with a regular R:BASE report, you can have any combination of sections in a PAGEMODE report. Page Header/Footer A page header is data that is written at the top of every page. In a regular R:BASE report, this data is printed before any other data on the page is processed or printed. The page header can be written at any time in a PAGEMODE report, even after all the other data for a page has been processed. Often code is placed at the beginning of the program to write the page header. Wherever the code is placed in a PAGEMODE report program, the code references row numbers to write the data at the top of the page, and the code executes only once for each page. The code is normally placed either before or after the WHILE loop that processes detail data. Like the page header, the page footer is only written once per page and can be written in code placed either at the beginning or end of page. The row number where the data is written indicates whether it is a page header or page footer. The placement in the program code for both the page header and footer is determined by when all the data needed to be printed for the header or footer has been processed. If you have all the necessary information, it is easier to print the header at the beginning of the page because you don't need to save any of the data in variables for later printing. Or, place code for both the page header and footer just before the page is sent. The columnar phone list report example shows how to write a page header that has both data from the first row and data from the last row on the page. Break Header/Footer The easiest way to set up a breakpoint for a PAGEMODE report is to use cursors. Declare a cursor for just the column that will be the breakpoint and use a DISTINCT so only unique values are retrieved. This cursor controls the program and defines the breaks. Each time the cursor fetches a new row, a break occurs. To access detail information, define a second, nested, cursor. The second cursor is on the same table and retrieves all the other data needed from the row. The second cursor is dependent on the first cursor; the second cursor retrieves rows based on the current breakpoint value. The first cursor uses a WHILE loop to step through each distinct breakpoint value. This WHILE loop prints the break header and footer. The WHILE loop for the second cursor steps through all the detail data for the break. The code sample below does not have all the code to invoke PAGEMODE and write out the data, but shows the logic of using cursors to define breakpoints and how the cursors easily identify where a group of data begins or ends. The code sets up a breakpoint on custid; transaction information is printed grouped by each customer. By declaring a cursor to be distinct for a single column, we emulate a breakpoint in a report. Each row that is fetched is a new breakpoint value. Code that is executed between getting each row is used to calculate subtotals or print out detail data. This code example returns all customers who have transactions; cursor c1 is defined to look at data from the Transmaster table. For a report on all customers whether or not they have had transactions, declare the cursor on the Customer table instead of the Transmaster table. Then cursor c2 will return a "no data found" error for customers with no transactions. No outer join view is needed. SET VAR vCustid TEXT DECLARE c1 CURSOR FOR SELECT DISTINCT custid + FROM transmaster DECLARE c2 CURSOR FOR SELECT transid, empid, + invoicetotal, transdate FROM transmaster + WHERE custid = .vCustid OPEN c1 FETCH c1 INTO vCustid Ind1 WHILE SQLCODE <> 100 THEN -- This is the break header position, -- the first customer ID is retrieved. -- Cursor c2 fetches all transaction rows for -- that customer. OPEN c2 RESET FETCH c2 INTO vTransid vind1, vEmpid vind2, + vInvoiceTotal vind3, vTransDate vind4 WHILE SQLCODE <> 100 THEN --- The detail rows are processed here. FETCH c2 INTO vTransid vind1, vEmpid vind2, vInvoiceTotal vind3, vTransDate vind4 ENDWHILE -- The break footer position is here, -- right before the next customer row is fetched. FETCH c1 INTO .vCustid ENDWHILE Reports can print one break per page or many breaks. To print only one break per page, send the virtual page with NEWPAGE or OUTPUT SCREEN before fetching the next row. Both the break header and footer can be easily printed at the end of the break since the break header data will appear in the same location on each page - just remember to save the header data into variables for printing later. When printing many breaks per page, check in the program code for the end of the page. Make sure there is enough room left on the page to print the break header, detail and break footer data. Otherwise, send the virtual page. With many breaks per page, it is easier to print the header at the beginning of the break; then you have the correct row number to place the header data. Multiple Breaks Expand the breakpoint concept outlined above to set up multiple breakpoints. Each breakpoint is a cursor; each succeeding breakpoint cursor is dependent on the previous cursor. Remember, place all the cursor declarations at the beginning of the program and use OPEN RESET for speed. OPEN RESET can't be used, however, on a cursor declaration that includes an ORDER BY clause. For those cursors, CLOSE and OPEN the cursor. Be sure to initialize all variables used in the WHERE clauses of the DECLARE CURSOR statements. The variables won't contain actual data until the row for the cursor is fetched. This code example shows how to first group customers by state, then group transactions by customer. Again, the commands for invoking PAGEMODE and displaying data are not included; this code is used to demonstrate breakpoint logic only. -- Initialize variables SET VAR vCustState TEXT, vCustid TEXT -- Set up the breakpoints with cursors. -- Cursor c1 is break 1, cursor c2 is break 2, -- cursor c3 retrieves the detail data. DECLARE c1 CURSOR FOR + SELECT DISTINCT custstate FROM customer DECLARE c2 CURSOR FOR + SELECT DISTINCT custid FROM transmaster + WHERE custid IN + (SELECT custid FROM customer + WHERE custstate = .vCustState) DECLARE c3 CURSOR FOR + SELECT transid, empid, invoicetotal, transdate + FROM transmaster WHERE custid = .vCustid OPEN c1 FETCH c1 INTO vCustState Ind1 WHILE SQLCODE <> 100 THEN -- This is the break header 1 position, -- the first state value is retrieved. -- Cursor c2 then fetches all customer rows -- for that particular state OPEN c2 RESET FETCH c2 INTO vCustid Ind2 WHILE SQLCODE <> 100 THEN -- This is the break header 2 position, -- the first customer row. Cursor c3 then -- fetches all transaction rows for -- that customer OPEN c3 RESET FETCH c3 INTO vTransid vind1, vEmpid vind2, + vInvoiceTotal vind3, vTransDate vind4 WHILE SQLCODE <> 100 THEN -- The detail rows are processed here FETCH c3 INTO vTransid vind1, vEmpid vind2, + vInvoiceTotal vind3, vTransDate vind4 ENDWHILE -- Break footer 2 position is here, + -- right before the next -- customer row is fetched. FETCH c2 INTO vCustid Ind2 ENDWHILE -- Break footer 1 position is here, after -- all the customer data has been fetched, -- before the next state value is fetched. FETCH c1 INTO .vCustid ENDWHILE Detail The detail section in a regular R:BASE report is data that is printed for every row in the table or view. The detail section may be data printed in columns or row by row. Detail data can be placed anywhere on the page from any place in the program code, but usually the detail data is written within the innermost DECLARE CURSOR, since that is where each row of data is accessed. Debugging a Report created with PAGEMODE A PAGEMODE report is a program; debug it using TRACE just as you would any other command file or application. Pay special attention to the row, column, and page length variables. These variables are likely to be set to a wrong value causing the output to be off. We recommend you direct your PAGEMODE report to a file rather than to the printer directly. Some time lags are likely between pages. Also, outputting to a file lets you quickly check for formatting problems. There is no built-in Print preview; add the WHERE clause, WHERE LIMIT = 10, to restrict the amount of data and to check the formatting. The Preview option in the R:BASE report writer uses this same WHERE clause. Note that when using PAGEMODE, your report will always end with a form feed. The OUTPUT SCREEN sends the current page and ends the page with a form feed. You can't get rid of it - you must send the final page, which sends a form feed. In regular R:BASE reports, you learn to base a report on the many table and define lookups to the one table, or base the report on a view. Writing a PAGEMODE report, you don't need to define lookups or create a view. You can declare a cursor on your one table, then a second, nested cursor on the many table. Note that if you are only doing calculations on a table, you may not need to declare a cursor on it. Use the SET VAR or SELECT...INTO commands for defining calculations or lookups. You might see extra blank lines in your output. PAGEMODE sets up a page based on the settings for LINES and WIDTH; that entire page is sent to the printer or file, including any blank lines. Thus, if LINES are set to 60, each time a virtual page is sent, 60 lines are sent. Examples Now let's look at some examples to see how to apply this logic to actual PAGEMODE reports. The best way to understand how PAGEMODE works is to look at some examples of reports. We'll start with a simple columnar report such as a phone list. Then, add break points and put a break header on every page when the break detail is more than one page. These code examples can be used as templates for your applications. The examples use the Hifi sample database. There are common elements to all the examples. LINES are set artificially low in each example to force a page break to occur. All the examples output to a file and the file is displayed to the screen when the report is complete. MESSAGES are set off to make sure they don't interfere with the report data. Common elements are commented in the first example where they are used; they are not commented in later examples for space considerations and to make it easier for you to see he changes and differences in the examples. Remember these are just examples. There are many ways to create PAGEMODE reports, these examples are designed to show you some of the basic techniques. Example 1 - prints an employee telephone list in columnar format. The page header data is actually written at the end of the page so it includes data from the first row printed on the page, and data from the last row printed on the page. *(PHONE1.RMD) *(example 1 - a columnar report with a page header written at the end of the page) CON hifi CLS SET MESSAGE OFF SET PAGEMODE OFF SET LINES 10 DECLARE c1 CURSOR FOR + SELECT (LastName + ',' & FirstName), Phone, Lastname + FROM salespeople ORDER BY lastname OPEN c1 FETCH c1 INTO vName i1 ,vPhone i2, vLastname i3 SET VAR vCol INT = 1, vRow INT = 3, vRowLimit = 9 SET VAR vFirst TEXT = .vLastname, + vLast TEXT = .vLastname, + vPage INT = 1 SET PAGEMODE ON OUTPUT phone.out WHILE SQLCODE <> 100 THEN SET VAR vRow = (.vRow + 1) IF vRow >= .vRowLimit THEN IF vCol = 40 THEN WRITE .vFirst '-' .vLast AT 2 1 WRITE 'Page' .vPage AT 2 68 WRITE '----------------------------------' AT 3 1 WRITE '----------------------------------' AT 3 40 NEWPAGE SET VAR vFirst = .vLastname, vLast = .vLastname SET VAR vPage = (.vPage + 1), vCol = 1 ELSE SET VAR vCol = 40 ENDIF SET VAR vRow = 4 ENDIF WRITE .vName=21 .vPhone AT .vRow .vCol SET VAR vLast = .vLastname FETCH c1 INTO vName i1, vPhone i2, vLastname i3 ENDWH WRITE .vFirst '-' .vLast at 2 1 WRITE 'Page' .vPage at 2 68 WRITE '----------------------------------' at 3 1 WRITE '----------------------------------' at 3 40 OUTPUT SCREEN CLOSE c1 DROP CURSOR c1 SET PAGEMODE OFF SET LINES 20 TYPE phone.out Lines are set artificially low to show two pages. Note the cursor definition concatenates first and last nameready to be displayed on the report. Lastname is included twice in the DECLARE CURSOR statement - the first for display purposes, concatenated with first name, the second reference is used to retrieve just the last name for placement in the page heading. The first row of data is fetched Before the rows are iteratively retrieved, the row and column values for display are initialized. In addition, the variable vRowLimit is used to indicate how many rows to print per column. It is set to one less than the pagesize (lines) for this report, The variables are initialized for the page heading. The lastname value from the first row is saved in the variable vFirst. The variable vLast is reset within the cursor WHILE loop Turn PAGEMODE on, indicate the output device and start looping through the rows of data The row display variable is incremented by one for each row that is fetched. The names start printing on row 4 to allow space for the page heading. Check to see if we have reached the end of a page. When displaying one row of output for each row retrieved from the table, it is easy to use the vRow variable to track where you are on the page. When wrapping data, or displaying a variable number of rows of output for each row of data, use SHOW VAR and (ISTAT('PAGEROW')) to keep track of the actual page location. Now check to see if the second column of names has printed. If so, write the page heading and send the page. Write the page heading. Send the page. Re-initialize variables for page 2. Column 1 is done, set up parameters for displaying the names in column 2. Reset the row display variable for either column 2 or the next page. Write the detail data. Each row, get the current lastname value. When the page is done the current value is displayed by the WRITE command that does the page heading. Get the next row of detail data. No more data, write the page heading on the last page. Close the file, cleanup and display the report. Example 2 - prints a phone list report using breakpoints. Each break is printed on a separate page. The data is not printed in columns to make it easier to see how breakpoints are set up. The employee phone list data is printed by state. *(PHONE2.RMD) *(example 2 - a report with break points. An employee phone list is printed by state, each state on a separate page.) DEL phone.out CON hifi CLS SET MESSAGE OFF SET PAGEMODE OFF SET LINES 15 SET VAR vState TEXT DECLARE c1 CURSOR FOR SELECT DISTINCT state + FROM salespeople DECLARE c2 CURSOR FOR + SELECT (LastName + ',' & FirstName), Phone, Lastname + FROM salespeople WHERE state = .vState + ORDER BY lastname SET VAR vCol INT = 3, + vRow INT = 2, + vCount INT = 0, + vPage INT = 1, + vPageLimit = (CVAL('LINES')) SET PAGEMODE ON OPEN c1 FETCH c1 INTO vState ii1 WHILE SQLCODE <> 100 THEN OUTPUT phone.out APPEND WRITE 'List of employees in state of:', .vState, + ' Page:', .vPage AT .vRow, .vCol SET VAR vRow = (.vRow + 1) WRITE '----------------------------------' + AT .vRow, .vCol OPEN c2 FETCH c2 INTO vName i1 ,vPhone i2, vLastname i3 WHILE SQLCODE <> 100 THEN SET VAR vRow = (.vRow + 1) SET VAR vCount = (.vCount + 1) IF vRow >= .vPageLimit THEN NEWPAGE SET VAR vRow = 2, vPage = (.vPage + 1) WRITE 'List of employees in state of:', .VState, + ' Page:', .VPage AT .vRow, .VCol SET VAR vRow = (.vRow + 1) WRITE '----------------------------------' + AT .vRow, .vCol ENDIF WRITE .vName=21 .vPhone AT .vRow .vCol FETCH c2 INTO vName i1, vPhone i2, vLastname i3 ENDWH CLOSE c2 SET VAR vRow = (.vRow + 2) WRITE 'Number of employees for', .vState, 'is', + .vCount AT .vRow, 10 OUTPUT SCREEN SET VAR vRow = 3, vPage = (.vPage + 1), vCount = 0 FETCH c1 INTO vState ii1 ENDWH OUTPUT screen DROP CURSOR c1 DROP CURSOR c2 SET PAGEMODE OFF SET LINES 20 TYPE phone.out Initialize the variable used in the WHERE clause of cursor c2. Set up the breakpoint by declaring a cursor on the break column. The cursor is specified as DISTINCT to retrieve one row for each unique value in the column. Declare a cursor to retrieve the other data to display. Initialize the row and column display variables. The variable vCol is set even though the data is not printed in columns. The pagesize is found by using the CVAL function to query the current setting for LINES. Get the first break value. Opening and closing the file each time through the WHILE loop eliminates extra form feeds. The break heading acts as the page heading. The OUTPUT..APPEND command in combination with the OUTPUT SCREEN at the end of the break puts each break on a new page. Begin data retrieval for the first state (break). Cursor c2 is re-opened with different data for each state (break value). Increment the row display value, and count the number of records per break. Test to see if the end of a page has been reached. If so, send the page, and print the break header on the next page. If the detail data is more than will fit on a single page, the break header values repeat on the next page Write the detail data. Write the break footer, all the detail for the break value has been processed. The OUTPUT SCREEN sends the current page and closes the file. It puts the next break on a new page Get the next state or break value and reset variables. Close the file, cleanup and display the report. Example 3 - a breakpoint report with multiple breaks printed per page. The break heading is repeated on the next page if the data for the break won't fit on the page. The report prints a page header as well as a break header and break footer. *(PHONE3.RMD) *(example 3 - this example includes a page header along with the break header. Multiple breaks are printed per page. The break header is repeated on subsequent pages if the break detail spans a page) DEL phone.out CON hifi CLS SET MESSAGE OFF SET PAGEMODE OFF SET LINES 25 SET VAR vState TEXT DECLARE c1 CURSOR FOR SELECT DISTINCT state + FROM salespeople DECLARE c2 CURSOR FOR + SELECT (LastName + ',' & FirstName), Phone, Lastname + FROM salespeople WHERE state = .vState + ORDER BY lastname SET VAR vCol INT = 3, + vRow INT = 2, + vPage INT = 1, + vPageLimit = (CVAL('lines')), + vCount INT = 0 SET PAGEMODE ON OUTPUT phone.out WRITE 'Employee Phone List by State' AT .vRow, 25 SET VAR vRow = (.vRow + 1) WRITE .#date AT .vRow, 32 WRITE 'Page:', .VPage AT .vRow, 60 SET VAR vRow = (.vRow + 2) OPEN c1 FETCH c1 INTO vState ii1 WHILE SQLCODE <> 100 THEN WRITE 'List of employees in state of:', .VState + AT .vRow, .VCol SET VAR vRow = (.vRow + 1) WRITE '----------------------------------' + AT .vRow, .vCol OPEN c2 FETCH c2 INTO vName i1 ,vPhone i2, vLastname i3 WHILE SQLCODE <> 100 THEN SET VAR vRow = (.vRow + 1) SET VAR vCount = (.vCount + 1) IF vRow >= .vPageLimit THEN NEWPAGE SET VAR vRow = 2, vPage = (.vPage + 1) WRITE 'Employee Phone List by State' + AT .vRow, 25 SET VAR vRow = (.vRow + 1) WRITE .#date AT .vRow, 32 WRITE 'Page:', .vPageAT .vRow, 60 SET VAR vRow = (.vRow + 2) WRITE 'List of employees in state of:', .VState + AT .vRow, .VCol SET VAR vRow = (.vRow + 1) WRITE '----------------------------------' + AT .vRow, .vCol SET VAR vRow = (.vRow + 1) ENDIF WRITE .vName=21 .vPhone AT .vRow .vCol FETCH c2 INTO vName i1, vPhone i2, vLastname i3 ENDWH CLOSE c2 SET VAR vRow = (.vRow + 2) WRITE 'Number of employees for', .vState, 'is', + .vCount AT .vRow, 10 SET VAR vRow = (.vRow + 3), vCount = 0 FETCH c1 INTO vState ii1 ENDWH OUTPUT screen DROP CURSOR c1 DROP CURSOR c2 SET PAGEMODE OFF SET LINES 20 TYPE phone.out Initialize the variable used in the WHERE clause of cursor c2. Declare the cursors for the breakpoint and the detail data. Initialize row, column and page variables. Variable vCount is used to count the rows and is displayed in the break footer. Write the page heading for page 1. Write the break heading. Begin retrieving the detail data. Test to see if we are the end of the page. Send the current page and then write the page heading and break heading for the next page. The page heading. The break heading. Display the detail data. All of the detail for the current break value has been processed. Write the break footer before fetching the next break value from cursor c1. Reset variables for the next break. Close the file, cleanup and display the report.