Report Pro 2 + 3

You have that data, but you need to display/print it in a form that users can understand and work with it. Great reports are a critical step in the development cycle.
ReportPro is a great general purposed reporting engine.
In comes in two flavours know as the RP2xx and RP3xx series. Both have designers to produce .RPT files and RP2 is easyto produce Handcoded reports.. if you know how to do it. RP3 handcoded reports from VO GUI is difficult and this paper shows the techniques to handcode reports for both RP2 and RP3
Special thanks for Oscar Schneider and Eric Visser for the work they had done re RP3 and handcoded reports.

ReportPro was written for Visual Objects and is written completely in Visual Objects. When I first saw this product for VO1 in 1994 it convinced me what VO + Reportpro were a great team. The evolution continued through all versions of VO as the ReportPro 2 series which included 2.10 / 2.11 and 2.12. The ReportPro  developers created “Classmate” and “ReportPro 3.x” and access was via an Active X component for VO GUI class users, or it could be used natively from Classmate.  This article will explore by way of samples and code snippets, the techniques of “hand-coding” reports which add incredible power + flexibility to your reports.

ReportPro has a GUI report designer, why handcode your reports? You must be a masochist. The advantage of ReportPro is that you can select both types of reports but once you get a few hundred [.FRM ] reports this is a lot of report files to maintain and distributed. They also require a different skill set to program and how they will work in your application. If you want to port your reports to another system, you are locked into a proprietary format. Recently the source code for ReportPro II & III was made available by Grafx software which may get around this issue.

 

“Hand-coded” reports.. use specific CALLBACK methods of the RPPRINTER class to provide the Preview and Print and page formatting, including Header and Footer functionality. This class subclasses the PTRDEVICE class which is a complete low-level base class/print engine built around the Windows API.  When reports are built there are “NO” .FRM files and the report(s) are compiled into your DLL or EXE.  Therefore you can have say 300 reports in one DLL.  Lot easier to maintain believe me.

 

When your handcoded report is run, Reportpro looks for callback methods in the following order. If they do not exist, NO ERRORS occur and simply they do not get executed. You build your report using callbacks to have an automated reporting system.


PrintStart()        // Called once for Initialisation.

PrintSetPage()   // Called before Prebody()

PrintPreBody()   // Called once (used for Title Pages)

PrintSetPage()   // Loop for each page.. each of the below methods     are called ONCE for EACH page.
PrintPageHeader()
PrintPageBody()
  
PrintPageFooter() //
Loop ends when PRINT_NOMORE or
  PRINT_ERROR is returned from PrintPageBody()

PrintPostBody()  // Called once (usefull for Footnotes etc)

PrintEnd()         // Called once and can be used for Cleanup

 

OK.. how do ReportPro Callbacks work ?

You could use say the PrintStart() method to setup some or all of your variables or even plug in your servers or indexes into the report. You can be as flexible as you like in your design.

From experience at a minimum you will use PrintSetPage() and PrintPageBody() and say PrintPageHeader() in your reports. The PrintPagebody() can be used to produce the BODY of the report and the PrintSetPage() is used to control the Skipping / Forwards and Backwards of the report in Preview mode. When a PrintPageBody() returns PRINT_OK  or PRINT_NOMORE  or PRINT_ERROR this lets Reportpro know what to do next.  That is if PRINT_ERROR is returned then do not proceed any further. If PRINT_NOMORE is returned we have reached the end of the data and we can stop processing the data.. and PRINT_OK says there is more data coming and ReportPro should call its callback methods which include PrintPageHeader() and then call PrintPagebody(). This process continues ‘looping’ until it finally returns PRINT_NOMORE and the report is completed.

The ReportPro preview dialog window has buttons for a SKIP forward and BACK and “goto first” and “goto end” and the PrintSetPage() is your friend when programming these buttons. The technique I use is to build an array of VARIABLES at the time the PrintPageHeader() is called which record the value of variable counters for each page of the report. When PrintSetPage() is called by ReportPro your code will ‘reset’ these values so the report uses the same values to produce the same page of results.

The PrintPageHeader() is the "Header" section of the report and the PrintPageFooter() handles the "Footer" section of the report.

WORKING EXAMPLE of a REPORT from Scratch.

 oPrinter := RpPrinter{ SELF:oShell, SELF }                                   

//

IF oPrinter:IsValid            // Is our Object ready.   

   oPrinter:SetPrinterByName(SELF:oShell:cReportPrinter)

   oPrinter:UnitOfMeasure := UNIT_MM

   oPrinter:LeftMargin := 9           // Sets the Preview/printing..

   oPrinter:TopMargin := 10 // Sets the Preview/printing..

   oPrinter:BottomMargin := 30 // Sets the Preview/printing..

   IF lPreview

      oPrinter:PrintPreview( cPrintJob, cPrintJob + ".PRN", cTitle,

                                    "Printing...",TRUE,FALSE,FALSE )

  ELSE

       oPrinter:Print( cPrintJob, cPrintJob + ".PRN", cTitle,

                           "Printing...",FALSE,FALSE)

   ENDIF

   //

ENDIF

This example code shows the oPrinter variable instantiating the RPRINTER class object and by checking oPrinter:isValid before passing in our Printer name. This could be a UNC printer. We can set the report to be MM or INCHES and left/ right/ top/ bottom margins. We could select PrintPreview() and print from there or just select Print() and the job will be processed.

Either way the Reportpro engine will call our CALLBACK methods as you have designed them for your handcoded report.  The result is a powerful and easy method of reporting.

Your ReportPro “Callbacks” in ACTION

When the Printpreview() or Print() are called in the example code the ReportPro callback system will look ‘automatically’ for the PrintStart() method first and if it exists, it will process this code and Reportpro will proceed if a PRINT_OK is returned.

If a PrintSetPage() method exists it will be processed next. At this point in time the PrintPageHeader() has not been called previously and therefore the length of SELF:_aPages will be 0. The SELF:_aPages is an empty array at this stage and is used to store results as we traverse through the report. Mainly this array will contain Counters, sub-totals, recno’s numbers for databases relating to this page. Therefore the first CASE statement will be TRUE and the ‘SetPage’ will return nToPage which is equal to 1.

If the PrintPreBody() exists it will be called and it should return PRINT_OK. The callback will then call PrintSetPage() again. If PrintPrebody() did not exist it would not call either PrintPreBody() or PrintSetpage(). Reportpro will call the PrintPageheader() method [ if it exists ] and it should also return PRINT_OK.

The technique I use stores for each page, counters and record pointers which can be “reset” in the event of skip backwards  and forwards through pages. This ‘resetting’ is done by PrintSetPage() method. In this example we are saving the PAGE# and the SERVER:RECNO.. but there is no limit to what you could do. As an example you could have 5 or 6 servers and numerous counters for subgroups or complex indexes. Whatever you want.

[ i.e.     AAdd( SELF:_aPages, { SELF:server:recno, nPage }) ]

The PrintPageheader() method prints the data / text / images that will appear as the header of the report page(s). Remember only PRINT_OK or PRINT_NOMORE or PRINT_ERROR can be returned or ReportPro will freeze. ReportPro will now call PrintPageBody() and we enter a recursive situation. If PRINT_OK is returned the system will call PrintPageFooter() and then PrintSetPage() and PrintPageHeader() and then recall the PrintPageBody() again. It is important that the record pointer(s) to the database you are processing do not say GOTOP again in your code using this reporting style or callbacks will put you into an endless loop.

We will look at various programming techniques later in this article. In the example code shown we will loop through the database checking for EOF status and if we fill the page and there are still database records to process you should return PRINT_OK which instructs Reportpro to call PrintPageFooter() followed by a call to PrintSetpage() followed by calls to PrintPageHeader() and then a call to PrintPageBody(). The process repeats producing pages for your report ..OR.. the PrintPageBody() should return PRINT_NOMORE status and PrintPageFooter() is called for the final time as the report is completed.

When Printpagebody() returns a PRINT_NOMORE status the PrintPageFooter() will be called followed by PrintPostBody() and then PrintEnd(). If none of these methods exists no errors will be reported and the report is considered completed. If PrintPreview() mode was selected the ReportPro dialog window will show the report data using Fonts and Colors in the report and allow you to skip forward / backwards / go to top or go to end of the report.  If the Print() mode method was selected the job would work the same but the job would be automatically flushed to the printer.

 

Managing “Callback” recursion issues

You may be tempted in the PrintPagebody() to use a Gotop() and a DO WHILE loop to print say 30 pages of a report. Sounds easy to loop around outputting data and then check the page length and issue a PRINT_OK and you have a Footer and a then a new Header for the new page and then we are returned to PrintPageBody(). You would have a potential problem if in your  ‘Pagebody’ code on method entry, it set TOP OF FILE, you would be in an endless loop. You could get around it using a STATIC variable or a class variable or you could use the PrintStart() to setup the record pointers and this is not a bad design philosophy. However as the reports get more complex you need a constant approach to the situation. You need recursive techniques which are detailed on the next page.

 

Basic Report using Callbacks.

 

METHOD PrintSetPage(oPrinter, nToPage) CLASS _ myReport

     LOCAL _nPage := nToPage              AS LONG

     DO CASE

      CASE ALen(SELF:_aPages) = 0        // If we have no info

     CASE nToPage == 1

           SELF:server:recno := SELF:_aPages[1][1]

     CASE nToPage = 32000

SELF:server:recno := SELF:_aPages[_nPage:= ALen(SELF:_aPages)][1]

     OTHERWISE

          IF nToPage <= ALen(SELF:_aPages)

             SELF:server:recno := SELF:_aPages[_nPage][1]

          ENDIF

      ENDCASE

      RETURN _nPage                          

      -----------------

METHOD PrintPageHeader(oPrinter, lPrint) ) CLASS _myReport

     LOCAL nPage := oPrinter:CurrentPageNumber AS DWORD

     IF oPrinter:CurrentPageNumber > ALen(SELF:_aPages)

          AAdd( SELF:_aPages, { SELF:server:recno, nPage })              

     ENDIF

      IF lPrint

         oPrinter:Prow := 0

         oPrinter:SetFont("Arial",16,TRUE,FALSE,FALSE)       

         //   bold italic underline nrotation escapement

          oPrinter:TextOut(oPrinter:Prow,98, "SHERLOCK TEST HEADER", ;

                                  ALIGN_CENTER,,,Color{COLORBLUE})

       ENDIF

       RETURN PRINT_OK     

       ---------------

METHOD PrintPageBody(oPrinter, lPrint) CLASS _myReport

    DO WHILE ! SELF:server:EOF .AND. ;

         oPrinter:Prow <= oPrinter:PrintAreaLength

         oPrinter:TextOut(oPrinter:Prow, ;                                               SELF:server:FIELDGET(#zzCreditor) )

          IF oPrinter:Prow > oPrinter:PrintAreaLength

              RETURN PRINT_OK

           ENDIF

       ENDDO            

       RETURN PRINT_NOMORE

       -----------------

METHOD PrintPageFooter(oPrinter) CLASS _myReport

       // Original footer

       oPrinter:SetFont("Arial",10,TRUE,FALSE,FALSE)

       oPrinter:DrawText(oPrinter:PrintAreaLength,0,”FOOTER  STUFF”, ;

                                   ,,,Color{COLORBLUE})

       RETURN PRINT_OK


“Callback” recursion tricks and techniques

The first rule with Hand-coded reports is to think recursion from the outset in your code design. As most reports will be greater than one page this means you will “renter” the PrintPagebody() possibly in the middle of the same routine you just left, courtesy of ReportPro and you must deal with it. How do you get to exactly the same place and record number you left from ?  Well let me share the secrets ;

I use a SYMBOL variable “symStage” in my Class and I set it in the PrintStart() method to symStage  := #STAGE1

In the PrintPageBody() when it processes through the “Stages” of the report and as each stage is completed, I update the SELF:symStage to say #STAGE2 or #STAGE3 or #STAGE4 etc. This way when the recursive call is made I can bypass earlier stages and move to the one I was working on.

Another trick is if you need to change ORDERS for say the Dbserver or SET a SCOPE . FILTER etc..  required for the next stage you set it in the last stage just before you exit this stage.  This way it will remain the way it was when you “recursed” and not be reset on re-entry.

 

Recursive example ;

 

METHOD PrintPageBody(oPrinter, lPrint) CLASS _myReport

       DO WHILE ! SELF:serverONE:EOF .AND. ;

           oPrinter:Prow <= oPrinter:PrintAreaLength

           if self:symStage = #STAGE1

                 // Do stuff.. output data .. whatever

                 // Set an index.. position a record

                 self:symStage := #STAGE2

                oServerTWO:Gotop() // ß-- We set this is Stage 1 for Stage 2

           endif

           if self:symStage = #STAGE2

                  // Do other stuff.. output data .. whatever

                  // Set an index.. position a record

                DO WHILE ! SELF:serverTWO:EOF .AND. ;

                     oPrinter:Prow <= oPrinter:PrintAreaLength

                       // Print stuff and loop around        

                    SELF:serverTWO:Skip()

                     IF oPrinter:Prow > oPrinter:PrintAreaLength

                           RETURN PRINT_OK  // We exit here and recurse back

                     ENDIF

                  ENDDO

                  self:symStage := #STAGE3

            endif

            IF oPrinter:Prow > oPrinter:PrintAreaLength

                  RETURN PRINT_OK

            ENDIF

            SELF:serverONE:Skip()

       ENDDO            

       RETURN PRINT_NOMORE

 

In this code we have a STAGED approach which automatically sets up the next stage. Look at Stage2 and its job is to LOOP around printing stuff out from oServerTWO but the primary loop is oServerONE. In this example either test for page space left could invoke the PRINT_OK to be returned. When this happens we would get our Footer and new Header and then return to PrintPageBody(). If Stage2 was the exit point when we returned, STAGE1 will be bypassed and we will be positioned on the same record we left for oServerTWO as well as oServerONE. If however we had an oServerONE:Gotop() before the major loop we would have an endless loop ..or..  if we had the oServerTWO:Gotop() in the STAGE2 code we also could have an endless loop situation. Think of the repercussions of this problem.

The user selects a 10 page report and walks away from the printer and returns and 400 pages are printed until paper ran out.

 

Images in Hand-coded reports

 

To print BMP files in your reports, Reportpro has the DrawBMP() and the _DrawBMP() methods. The _DrawBMP() method requires an already initialised DIBMP object from a resource or field. The DrawBMP() automatically handles loading and unloading the Bitmap from a file.

 

  oPrinter:_DrawBmp(5,154, SELF:oDIBMP,7,34,,,TRUE)

 

This syntax would position the Row and Column at 5, 154 using the resource image with a Height of 7 and a Width of 34 and the TRUE says lets “stretch“ the image to fit these co-ordinates.

 

Lets imagine you have an external BMP file and you need to show it on your report, this code would do the trick.

 

cFileBMP := cDatapath + “SHERLOCK.BMP"

IF File( cFileJPG )

   oPrinter:DrawBmp( 5,154, cFileBMP, 7, 34,,,TRUE )

ENDIF                                        

 

Lets imagine you have an external JPG file and you need to show it on your report, we have a problem as ReportPro only handles BMP files.  This is how we get around the issue.  Using FabPaint you create a DIB ( Device Independant Bitmap) from the JPG on the fly and draw it on the report surface and then delete it.

 

cFileJPG := cDatapath + “SHERLOCK.JPG"

cFileBMP := cDatapath + SSRandfile() + ".BMP"

IF File( cFileJPG )

    hPTR := DIBCreateFromFile(String2Psz(cFileJPG))

    IF hPTR != NULL_PTR

        DIBSaveAs( hPTR, String2Psz(cFileBMP))

        oPrinter:DrawBmp(5,154, cFileBMP, 7, 34,,,TRUE )

        FErase(String2Psz(cFileBMP))              

     ENDIF

     hPTR := NULL_PTR

ENDIF

 

Programming the PRINTSETPAGE()

ReportPro calls the ‘Setpage’ as part of the callback system and as its name indicates it SETS THE PAGE and it is called prior to PrintPageHeader(). When you click on the Next/Backward buttons in the report dialog window this method is called with the PAGE# you are about to goto as the variable nToPAGE. In the code we store this to variable to _nPage and we check what action to take in our CASE statements. Firstly is there is an empty SELF:_aPages array ? We can do nothing and return the nTopage value. If the user has skipped back to Page#1 or selected FIRST PAGE the nToPage will be 1 and the SELF:_aPages array element 1 or any other values in element one of the array will reset the appropriate values. If the user skips to the last page or END of REPORT button the nTopage will be set to 32000 and the last element of the SELF:_aPages array will be used to reset the values. If the nToPage is not the first and not the last a check will be made to make sure it is less than or equal to where the report system has been thus far and it will use that element of the array to reset our variables / counters etc.

 

METHOD PrintSetPage(oPrinter, nToPage) CLASS _ myReport

     LOCAL _nPage := nToPage    AS LONG

     DO CASE

      CASE ALen(SELF:_aPages) = 0        // If we have no info

     CASE nToPage == 1

           SELF:server:recno := SELF:_aPages[1][1]

     CASE nToPage = 32000

SELF:server:recno := SELF:_aPages[_nPage:= ALen(SELF:_aPages)][1]

     OTHERWISE

          IF nToPage <= ALen(SELF:_aPages)

             SELF:server:recno := SELF:_aPages[_nPage][1]

          ENDIF

      ENDCASE

      RETURN _nPage                          

 

INHERITANCE and Handcoded reports

 

The greatest thing about handcoded reports and the superb ReportPro class structure is that you can build yor own classes and inherit from them. This has an advantage when you say have a similar Header and Footer for your reports. You simply build a subclass and it has a PrintPageBody() and the headers and footers will be added automatically. When you need say a different header or NO header you create a PrintPageHeader() for your subclass which overrides the parent class header.

 

CLASS _MasterSystemReports

    PROTECT symSTAGE          AS SYMBOL

    PROTECT _aPages := {}    AS ARRAY

    PROTECT cReportTitle       AS STRING

--

METHOD PrintPageFooter(oPrinter) CLASS _MasterReports

--

METHOD PrintSetPage(oPrinter,nToPage) CLASS _MasterReports

   LOCAL _nPage := nToPage                AS LONG

   DO CASE

   CASE ALen(SELF:_aPages) == 0     // Empty do nothing..

   CASE nToPage == 1

      SELF:ResetStatus( nToPage )

   CASE nToPage = 32000

      _nPage := ALen(SELF:_aPages)

      SELF:ResetStatus( _nPage )

    OTHERWISE

       IF nToPage <= ALen(SELF:_aPages)

            SELF:ResetStatus( _nPage )

       ENDIF

     ENDCASE

     //

     RETURN _nPage                              

 

METHOD PrintPageHeader(oPrinter,lPrint)CLASS _MasterReports

   IF oPrinter:CurrentPageNumber > ALen(SELF:_aPages)                   

         AAdd( SELF:_aPages,  ;

 { nPage, SELF:oOwners:Recno, ;

SELF:oProperti:Recno, ;

SELF:oTenants:Recno } )

    ENDIF

---

METHOD ResetStatus( _nPage ) CLASS _MasterReports

    SELF:oOwners:Recno        := SELF:_aPages[_nPage][2]               

    SELF:oProperti:Recno        := SELF:_aPages[_nPage][3]               

    SELF:oTenants:Recno        := SELF:_aPages[_nPage][4]

 

In this example you see that we are storing numerous Recnos and the ResetStatus() method allows us to reset the record pointers to match the page we want to reposition to.

Using subclassing we only have to supply a PrintPagebody() to get our report.

 

CLASS _OwnersONHOLD INHERIT _MasterOwnerReports

  .. or ..

CLASS _OwnersGSTTREE INHERIT _MasterOwnerReports

 

Programming the PRINTPreviewDlg

 

The ReportPro dialog window can be “programmed“ by intercepting the buttons calls by using a coding technique like this. To introduce this code to your report you pass in the symbol name of the class in the PrintPreview method as shown below.

 

  oPrinter:PrintPreview( cReportTitle, cReportTitle+".PRN", cReportTitle,;

              "Printing...",TRUE,FALSE,FALSE,,,#SherlockPrintPreviewDlg)

 

In this case I am intercepting the ExportBtn to Email a ReportPro report as a PDF file. You can program those buttons to call your own code and if you purchases the source code to ReportPro you can make it do anything you want.

 

CLASS SherlockPrintPreviewDlg INHERIT rpPrintPreviewDlg

     PROTECT oReportOwner                  AS OBJECT

     //

METHOD ExportBtn() CLASS SherlockPrintPreviewDlg

    LOCAL aEmail    AS ARRAY

    aEmail  := SELF:oReportOwner:EmailDetails()

    AAdd( aEmail, aReport )

    _dlgEmailStatement{ SELF, aEmail }:Show()

---

METHOD GotoBtn() CLASS SherlockPrintPreviewDlg 

  InfoBox{SELF, "Development Message", "Goto button"}:Show()

----

METHOD INIT( oParent, oPrinter, cCaption, lModal, ptrPlacement, ;

    nShowState, lLandScape, nZoom) CLASS SherlockPrintPreviewDlg

      SUPER:Init(oParent, oPrinter, cCaption, lModal, ptrPlacement, ;

                          nShowState, lLandScape, nZoom)

       SELF:oReportOwner := oPrinter:ReportOwner

       If ! IsMethod(SELF:oReportOwner, #EmailDetails)

          SELF:oCCExportBtn:hide()   // They do not want to EXPORT data.

       ENDIF

METHOD PrintBtn()  CLASS SherlockPrintPreviewDlg     

    IF IsMethod( SELF:owner, #Printstart )

         SELF:owner:PrintStart()  

    ELSEIF IsMethod(SELF:oReportOwner, #Printstart)       

          SELF:oReportOwner:PrintStart()

    ENDIF

   SUPER:PrintBtn()

 

RP3 and handcoded reports

 

Reportpro 3 was a total rewrite in ‘Classmate’, a product developed by the same authors of ReportPro and its all VO. For exising VO GUI users you needed to use the Active-X aspect of RP3 or switch to Classmate which gives you native access. This was not a great commercial success and a lot of VO GUI developers remained with the RP 2 series. An article written by Oskar Sneider in April, 2000 showed a technique to get “around” the issue of using the Active X, where the PRINTSERVER class must have a “cWINDOW” class as the owner. Basically he took the Handle() of VO window class and pumped this into the hWND of the cWindow class. The following code shows the trick and we get to this code compliments of the Report() method of StandardShellWindow. ;

 

Method Init( symPrintServer, oVOOwner ) CLASS reportserver

     if ! isNIL( oVOOwner )

        _oOwner := cWindow{}                        // ß Classmate

        _oOwner:hWnd := oVOOwner:Handle() // <- VO GUI handle

     endif

     _oPrinter := cPrinter{ _oOwner }              // ß Classmate

     _oPrinter:previewmodal := TRUE              // Create our report

     _oForm := CreateInstance( symPrintServer,oVOOwner  )

     _oForm:Printer := _oPrinter               // introduce cPrinter to Form

     _Printer:printserver := _oForm           // Introduce Form to Printserver

     //

     RETURN SELF

 

We pass our RP3 handcoded VO report using its symbol name via the variable [ symPrintServer ] to CreateInstance() which creates / instantiates the class via the standard INIT() mechanism.

In the INIT() you could open servers or build indexes or setup some variables but you could use the PrintStart() method for this.

 

METHOD Init(oParentWindow) CLASS RPro_Hard_Coded

    SELF:oDetails := DBSERVER{ GetDefault() + 'detail', DBSHARED }

    IF SELF:oDetails:Used

        SELF:cReportTitle := "HAND CODED Report in RP3 - DevFest 2005'

        SELF:cInfoLine1 := "Part" + Space(16) + "Description" + ;

                                     Space(34)+ "Unit" + Space(9) + "Unit"

        SELF:cInfoLine2 := "Num#" + Space(14) + "Item" + Space(39) + ;

                                    "Weight" + Space(9) + "Price"

        SELF:ParentWindow := oParentWindow

        SELF:oBodyFont     := cFont{,44,"Arial"}

        SELF:oFooterFont   := cFont{,40,"Arial"}

        SELF:oHeaderFont   := cFont{,60,"Arial"}

        SELF:lPrint := TRUE

        //

        RETURN SELF

Note in this example we set SELF:lPrint toTRUE and this will determine that this report will be a PRINT output and not a PREVIEW and then PRINT output.  The ReportServer object acts as wrapper and then calls _oForm:PrintStart() and either _oForm:Print() or _oForm:Preview().

 

METHOD print( ) CLASS ReportServer

    IF _oForm # NULL_OBJECT

        _oForm:PrintStart()

        _oPrinter:print()            

    ENDIF

    RETURN SELF

---

METHOD preview( ) CLASS ReportServer

    IF _oForm # NULL_OBJECT

        _oForm:PrintStart()

        _oPrinter:preview()

    ENDIF

    RETURN SELF

---

When “ReportServer” class is instantiated it relies on the report in the “Rpro_hand_Coded” class to produce our report. The result is a call to the Init() of the Reportserver class and this calls the Init() of the “Rpro_hard_coded” class.

 

METHOD Report CLASS StandardShellWindow

    LOCAL oReport          AS ReportServer

    LOCAL lDirect2Printer AS LOGIC

    oReport := CreateInstance(#ReportServer, #RPro_Hard_Coded,SELF)

     //

     lDirect2Printer := FALSE            // preview first

     oReport:Landscape := TRUE

     oReport:StartPrinting( lDirect2Printer )

 

RP3 Sample code in detail

The StartPrinting() method of report server when a job is completed will proceed after the Print() or Preview() method and this is where you can install your CLEANUP code. In this example case I close the open Dbserver.. but you could expand this to close multiple servers.

 

METHOD StartPrinting( lDirect AS LOGIC ) CLASS ReportServer

     IF _oForm # NULL_OBJECT

         IF lDirect

             _oPrinter:Print()

         ELSE

             _oPrinter:preview()

         ENDIF

     ENDIF

     //

      IF IsMethod( _oPrinter:PrintServer, #Cleanup )

           Send( _oPrinter:PrintServer,#Cleanup)

      ENDIF

 

The rest of the code and technique is similar to the explanation on previous pages like RP2.  That is PRINTSTART() is called first followed by PRINTSETPAGE() and the PRINTPAGEHEADER() and then PRINTPAGEBODY() and then PRINTPAGEFOOTER() and then the cycle continues until the report is completed.

 

One major difference I noticed between RP2 and RP3 is that RP2 will process one page and stop.. where the RP3 processes all pages first.  Check the documentation on both systems and don’t assume the same method name has the same X/Y data or parameters passed.

 

What you need to run the demo code

For “RP2_HandCoded_Reports_Demo” you need “RP2RDD32.lib” in your VO repository which is the standard RP2 interface to the RP DLL’s and this code should work with RP210/211 and 212. 

 

The “RP3_NO_CM_Reports_Demo” you need “RP3 runtime DLL” and “cm GUI 20x.dll”. I have tested it with RP203 / 205 and 206 and it all seems to work.

 

Handcoded report to print a Listview

 

Reports do not always derive their data directly from say a DBF or SQL backend but it may be XML or CSV file or even a Listview on the screen.  I have in one of my applications a Diary system which is a scalable listview based on time slots and I need to print a day planner to match the list views current user selection.

This code snippet gives you the general technique to do this. In the PrintConfig() we are looping through the Listview using “columncount” and adding the Column name and Caption and width to a working array. In this example I want to add two more columns that do not exist in the listview and I even adjust the last column based on the previous columns’ width.  I retrieve data from the Listview and the Dbserver which is a big advantage of Handcoded reports as you can access any VO object.

 

CLASS _DiaryReport

      PROTECT oListView  AS ListView

----

METHOD PrintConfig()  CLASS _DiaryReport

     LOCAL nTotWidth := 0  AS WORD

     LOCAL I                       AS INT

     LOCAL oColumn            AS ListViewColumn

 

     SELF:aListView := {}

     FOR I := 1 TO SELF:oListView:ColumnCount

           // Check the Listview and build an Array of Columns

           oColumn:= SELF:oListView:GetColumn(I)

           // column name          column caption     column width

           AAdd(SELF:aListView, {oColumn:NameSym ,oColumn:Caption ;

                ,Min(oColumn:Width,28-1) })

      NEXT

       AEval(SELF:aListView, { |aVal | nTotWidth += aVal[3]})

       // Calculate width of the Columns in Listview

       AAdd(SELF:aListView,{ #ACTION, 'Action' , 5 })

       // Add two more columns -  Adjust width of final column

       AAdd(SELF:aListView,{ #NOTES , 'Extra Notes', ;

            Max((75+2)- nTotWidth, 19) })

----

METHOD PrintPageHeader(oPrinter,lPrint) CLASS _DiaryReport

     LOCAL nStringLen             AS FLOAT

     LOCAL nX                          AS FLOAT

     LOCAL nCharWidth           AS FLOAT

     LOCAL nCharHeight          AS FLOAT

     LOCAL cText                     AS STRING

     LOCAL cTextStr                                AS STRING

     LOCAL I                             AS INT

 

     IF lPrint

          oPrinter:Prow := 0

          cText := SELF:cTitle

          nStringLen := oPrinter:GetTextLength(cText)

          oPrinter:SetFont("Arial",16,TRUE,TRUE,FALSE)

          oPrinter:DrawText(oPrinter:Prow,((oPrinter:PrintAreaWidth-

                           nStringLen)/3)-8,cText,,,,Color{COLORBLUE})

     ENDIF

     IF lPrint

         oPrinter:SetFont("Arial",10,FALSE,FALSE,FALSE)      

         oPrinter:Prow += oPrinter:AvgCharHeight * 3/2                       

         cText := "Print Date: " + DToC(Today())

         oPrinter:DrawText(oPrinter:Prow,0,cText)

         //

         cText := "Page: " + AllTrim(Str(oPrinter:CurrentPageNumber,3,0))

         nStringLen := oPrinter:GetTextLength(cText)

         oPrinter:DrawText(oPrinter:Prow, oPrinter:PrintAreaWidth, cText)

     ENDIF

     oPrinter:PRow += oPrinter:AvgCharHeight * 3/2                          

     oPrinter:SetFont("Arial",10,TRUE,TRUE,FALSE)                            

      nX := 0

      nCharWidth  := oPrinter:AvgCharWidth

      nCharHeight  := oPrinter:AvgCharHeight                      

      FOR I := 1 UPTO ALen(aListView)

         cTextStr := aListView[I,2]

         nStringLen := FLOAT(aListView[I,3])* nCharWidth

         IF lPrint

            oPrinter:DrawRectangle(oPrinter:PRow, nX, ;

                                 oPrinter:PRow+oPrinter:AvgCharHeight, ;

                            nX+(aListView[I,3]*oPrinter:AvgCharWidth), ;

                            0,PS_SOLID,,HS_SOLID,color{192,192,192})

             oPrinter:DrawText(oPrinter:PRow, nX, cTextStr, NIL, ;

                            nStringLen, DT_CENTER)

         ENDIF

         nX += nStringLen                                                        

      NEXT

      oPrinter:PRow += nCharHeight

     //

     RETURN PRINT_OK

 

In the PrintPageBody() we loop for the number of lines /rows in the Listview and use the GetNextItem() method to step through the listview data. I store with the Listview data the RECNO of the database that the data relates to and the oServer:GOTO() allows me not have to store memo data or any unnecessary data in the listview and retrieve it in realtime during the report.  I can evaluate /read the data and draw the appropriate grids and boxes on my Dailyplanner report.

METHOD PrintPageBody(oPrinter, lPrint) CLASS _DiaryReport

    LOCAL nX,nWidth                        AS FLOAT

     LOCAL xText, xValue                  AS USUAL

     LOCAL cText, cType, cNotes       AS STRING

     LOCAL nAlignment                       AS DWORD

     LOCAL oItem                               AS ListViewItem

     LOCAL XX, nLine, nCurrline := 0  AS DWORD

 

     oPrinter:SetFont("Arial",10,FALSE,FALSE,FALSE)

     DO WHILE SELF:LineNumber <= SELF:nItems .and. ;

          oPrinter:PRow <= oPrinter:PrintAreaLength

          nX := 0

          // relationship /disabled/droptarget/focused/selected/nItemStart

          oItem := oListView:GetNextItem(LV_GNIBYITEM, FALSE, FALSE, ;

                                                        FALSE,FALSE,SELF:LineNumber)

          IF oItem = NIL

                SELF:LineNumber += 1

                EXIT

          ENDIF          

          FOR XX := 1 UPTO ALen(aListView)

             xValue := oItem:GetValue(aListView[XX,1])

           // Get Array with user ID and RECNO for position for other info

           xText := oItem:GetText(aListView[XX,1])

           // Get text display from Listview Item

           IF IsArray(xValue)

                oServer:Goto( xValue[2] )                                                                    // The second element of GETVALUE is RECNO

                cType := SELF:oServer:FIELDGET(#APP_TYPE)

                cNotes :=  SELF:oServer:FIELDGET(#APP_NOTES)

           ENDIF         

           DO CASE

                CASE aListView[XX,1] = #ACTION

                     DO CASE

                         CASE cType = '1'           ;   xText := 'Appnt'

                         CASE cType = '2'           ;   xText := 'Phone'               

                         CASE cType = '3'           ;   xText := 'To-do'

                         CASE cType = '4'           ;  xText := 'Letter' 

                   ENDCASE

                   cType := ''

                CASE aListView[XX,1] = #NOTES         

                    xText := cNotes

                    cNotes := ''

           ENDCASE    

           //

           cText  := Proper(Transform(xText,""))

           nWidth := FLOAT(aListView[XX,3]) * oPrinter:AvgCharWidth

           //

           IF lPrint                                                                                                      nAlignment := iif( ValType(xText) == "N", ;

                                        ALIGN_RIGHT, ALIGN_LEFT)

                IF SLen(cText) > 59

                    nCurrline := 0

                    nLine  := MLCount( cText, 59 )

                  //

                    oPrinter:DrawRectangle( oPrinter:Prow, nX, oPrinter:Prow + ;

                                                  (oPrinter:AvgCharHeight*nLine), nX +;

                     (aListView[XX,3] * oPrinter:AvgCharWidth ), 0.5, PS_SOLID )

                    oPrinter:Prow -= oPrinter:AvgCharHeight                                           DO WHILE nCurrline <= nLine                                                                  oPrinter:DrawText( oPrinter:Prow,  ;

                                        nX+oPrinter:AvgCharWidth/2, ;

                       MemoLine( cText, 59, nCurrline), NIL,nWidth, nAlignment )

                        nCurrline += 1

                        oPrinter:Prow += oPrinter:AvgCharHeight

                    ENDDO

                    oPrinter:Prow -= oPrinter:AvgCharHeight

                ELSE       

                    oPrinter:DrawRectangle( oPrinter:Prow, nX, oPrinter:Prow + ;

                                              oPrinter:AvgCharHeight, nX + ;

                    (aListView[XX,3] * oPrinter:AvgCharWidth ), 0.5, PS_SOLID )

                    oPrinter:DrawText(oPrinter:Prow, nX +  ;

                     oPrinter:AvgCharWidth/2, cText, NIL, nWidth, nAlignment )

                ENDIF     

           ENDIF

           nX += nWidth

           //

           IF nX > oPrinter:PrintAreaWidth                                                              

               EXIT

           ENDIF

        NEXT

        oPrinter:Prow += oPrinter:AvgCharHeight

        SELF:LineNumber += 1

     ENDDO

     //

     RETURN iif( SELF:LineNumber > SELF:nItems, ;

                     PRINT_NOMORE, PRINT_OK )

 

In Summary

There are some minor but significant differences that you should be aware of between RP2 and RP3 versions.  Here we can see the X and Y data is reversed between RP2 and RP3

 

RP2 syntax ;

oPrinter:TextOut( oPrinter:Prow, 23, , “somedata”)                                                       

RP3 syntax ;

SELF:Printer:TextOut( 23, SELF:Printer:NextProw, “somedata” )

 

Handcoded reports are fantastic for speed and flexibility and reduces the .RPT clutter for individual report files. For some things they are quick and easy and recommended. For very complex and tedious reports I would use the handcoded method everytime. The great thing about ReportPro is you can have the best of both worlds and mix and match.  More power to you….


DOWNLOAD THE SAMPLECODE 
                                                                                                             

Bibliography

Phil McGuinness is General Manager of Sherlock Software - Australia since 1994 which is a vertical market solution provider specialising in REAL ESTATE TRUST ACCOUNTING, webdesign / intranet and Videowall technology. This writer has a long history in financial data processing systems in Bank and general business and technology solutions. A career in Electronics. Chemical processing and programming in Basic and dBase, Clipper 87, Clipper 5 and all versions of VO. He has held senior positions as General Manager Sales & Marketing and National Sales and Marketing Manager outside of the Software Industry.  The Sherlock Trust Accounting system has been in continuous market use for over 25 years.
Email: sherlock@sherlock.com.au]

Commentaar van anderen:
ChristianLouboutin op 14-8-2010 om 11:22
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Christian Louboutin Copyright 2010, Chemicals Chemistry via VerticalNews. Christian Louboutin Shoes, Wedding Shoes Pattinson great actorly virtue is that he wears clothes well, so it too bad he slackered-out in cargo pants here. Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Discount Christian Louboutin, Manolo Blahnik Shoes Tyler is less revealed than telegraphed through accessories a dead brother depth, a pack-a-day habit angst, a bookstore job smart, Discount Christian Louboutin, Louboutin, Christian Louboutin Sale, Louboutin Shoes, Sale Christian Louboutin Rodita zip sandals New style Black 14 a rich, aloof, and permanently disappointed daddy Pierce Brosnan. Louboutin Sale, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress, Herve Leger Bandage Dress Falling for You Love, angst, and something else is in the air in Remember Me Remember Me Herve Leger Dress, Chanel Shoes, Yves Saint Laurent Shoes, Manolo Blahnik Shoes Platform Cage Sandal 13 by Allen Coulter Summit Entertainment Opens March 12 Putatively a new romance starring Robert Pattinson, Remember Me begins like a vigilante movie Alexander Wang Shoes, Louboutin Shoes, Louboutin Sale, Louboutin, Christian Louboutin Sale, Buy Christian Louboutin A Brooklyn subway platform, a racially charged stickup girl watches her mother get shot. Christian, Christian Louboutin Discount, Christian Dior Shoes, Christian Louboutin Pumps Pattinson great actorly virtue is that he wears clothes well, so it too bad he slackered-out in cargo pants here.
GHT op 24-8-2010 om 5:33
LRH20100824

Do you wish to select an ideal and stylish Designer Handbags? Do you want perfection in every item that you use? Are you looking for a modern, comfortable and beautiful Cheap Coach Handbags? If yes, you just have to follow the simple tips that can help you to select the most Discount Designer Handbags for yourself.

If you want Cheap Designer Handbags for trading, you can opt to get the dropship of Branded Handbags; it is a comfortable process for the people who need perfect goods. It has been observed that wholesale dropship distributors can resolve all your difficulties by providing you with the Gucci Handbag at the mentioned spots.

nike air max 90 op 25-8-2010 om 9:06
gucci shoes nike air yeezy shoes fake purse okeybuy
nomat op 1-9-2010 om 6:48
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
nomat op 1-9-2010 om 6:49
back in 1884 Breitling company was founded in gucci chronomat evolution orRegister Although Hugo Ferdinand Boss died in 1 breitling windrider watches features a deployant clasp With the finest chopard aquanaut watches Argentina to settle in Catalonia in order to patek philippe had the GPS facility and was designed for replica watches assume that a 50 meter depth watch will be okay at copy watch designed to be unique on each watch They come in franck muller watches could be a scam c Check the mode of payment gucci watches scales indicating day and month flanking 12 o tag heuer watches ;Decorate yourself with colorful watches It is montblanc TAG Heuer TAG Heuer Formula 1 Grande Date cartier a 20 jewel Lexus movement with the case buckle chopard watches lv classic watches discussed the watch has a hand wound movement gucci Their exquisite and classy watch design and their iwc progress with only a speedy glance GPS watches cpcp watches replica watch recently launched Monaco V4 which features a replica breitling watches alternative of colors besides sizes to capture bvlgari watches creative design makes the timepiece a unique one bvlgari and appealing design Owing to the competitive ladies watches signature on the slate color dial completed with movado the prestigious Swiss brand member of the Swatch rolex watches two oclock: this is a peculiarity of many vacheron constantin watches to time These watches normally fall within the $5 cartier watches com The best source for replica watcheshigh movado this very special watch I am honoured and I am tag watches field which will show their new series of watch cartier pasha watches cartier watches com The best source for replica watcheshigh.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar