Example Project code excerpts
The Hide, Clone and Undo procedurres. These 4 are what you need in your own code.
MAP
RptBandHideControls PROCEDURE(REPORT ReportRef, LONG BandToHideFEQ) !Hide all controls on a BAND
RptBandHideUndo PROCEDURE(REPORT ReportRef, LONG BandToUndoHideFEQ) !Undo Hide all controls on a BAND
RptBandCloneControls PROCEDURE(REPORT ReportRef, LONG IntoBandFEQ, LONG CloneFromBandFEQ) !Clone all controls on a BAND
RptBandCloneUndo PROCEDURE(REPORT ReportRef, LONG UndoIntoBandFEQ, LONG UndoFromBandFEQ) !Undo Clone of controls on a BAND
END
The Report Declaration and Data:
Test2Headers PROCEDURE()
PreviewQ QUEUE(PreviewQueue),PRE(PreQ)
END
EmpCode LONG
EmpName STRING(40)
TotalCat STRING(40)
TotalAmt DECIMAL(9,2)
PageNoVar LONG !See if using a Var will fix Clone PageNo
!#3. >>>--Define your Report
Report REPORT,AT(302,1542,10500,6458),FONT('Arial',10),PRE(RPT),LANDSCAPE,PAPER(PAPER:LETTER),THOUS
HEADER,AT(302,479,10500,1000),USE(?PageHeader)
STRING('Report Test Page HEADER for Employee Details, i.e. not totals'),AT(240,240), |
USE(?STRING1),TRN,FONT(,,,FONT:bold)
STRING(@N4),AT(6917,240),PAGENO,USE(PageNoVar),TRN,RIGHT,FONT(,10)
STRING('PAGE:'),AT(6469,240),USE(?PagePrompt),TRN,FONT(,10)
LINE,AT(62,500,13500,0),USE(?Line1:2),COLOR(COLOR:Black),LINEWIDTH(10)
STRING('Code'),AT(83,660),USE(?HeadEmpCode),TRN
STRING('Name of Employee'),AT(1083,656),USE(?HeadEmpName),TRN
LINE,AT(73,875,13500,0),USE(?Line1),COLOR(COLOR:Black),LINEWIDTH(10)
END
EmployeeDetail DETAIL,AT(0,0,10500,950),USE(?EmployeeDetail),WITHNEXT(1)
STRING(@s5),AT(83,0,,156),USE(EmpCode)
STRING(@s40),AT(1083,0,,156),USE(EmpName)
STRING('Extra Line 1'),AT(2083,187,1813,156),USE(?Extra1)
STRING('Extra Line 2'),AT(3083,354,1813,156),USE(?Extra2)
STRING('Extra Line 3'),AT(4083,521,1813,156),USE(?Extra3)
STRING('Extra Line 4'),AT(5094,698,1813,156),USE(?Extra4)
END
Underline DETAIL,AT(0,0,,42),USE(?Underline),WITHPRIOR(1)
LINE,AT(0,0,13500,0),USE(?LineE),COLOR(COLOR:Black),LINEWIDTH(5)
END
TotalHeader2 DETAIL,AT(0,479,10500,1000),FONT(,,COLOR:Green),USE(?TotalHeader2)
STRING('Total Page Header is DETAIL that will CLONE()'),AT(250,135),USE(?TitleTot)
STRING('PAGE:'),AT(4146,135),USE(?PagePromptTot),TRN,FONT(,10)
STRING(@N4),AT(4615,115,417),PAGENO,USE(PageNoVar,, ?ReportPageNoTot),TRN,RIGHT, |
FONT(,12,,FONT:bold)
GROUP,AT(240,400,4781,377),USE(?Group4CloneTest),BOXED,TRN
STRING('TOTAL CATEGORY'),AT(323,562),USE(?TotalHeadCat)
STRING('TOTAL AMOUNT'),AT(3323,562),USE(?TotalHeadAmt)
END
LINE,AT(73,875,13500,0),USE(?TOTALSLine1),COLOR(COLOR:Green),LINEWIDTH(30)
END
TotalDetail1 DETAIL,AT(0,0,10500,180),FONT(,,COLOR:Green),USE(?TotalDetail1)
STRING(@s30),AT(323,0,1813,156),USE(TotalCat)
STRING(@n11.2),AT(3281,0,1000,156),USE(TotalAmt),RIGHT
END
END
Report printing code with Hide and Clone calls to RptBandHideControls() and RptBandCloneControls()
OPEN(Report)
!--Print details using Page Header--------------------------
LOOP EmpCode=1 TO 9
EmpName=ALL(CHR(64+EmpCode))
!How many Details will Print
PRINT(RPT:EmployeeDetail)
PRINT(RPT:Underline)
END
ENDPAGE(Report)
!--Total Summary needs TotalHeader2-------------------------
RptBandHideControls(REPORT, ?PageHeader) !Hide all the Page Header controls
RptBandCloneControls(REPORT, ?PageHeader, ?TotalHeader2) !Clone 2nd Header onto Page Header
LOOP T#=1 TO 9
TotalCat='TOTAL '& ALL(T#) ; TotalAmt = RANDOM(11111,99999)
PRINT(RPT:TotalDetail1)
END
ENDPAGE(Report) !Test page break with Total Header 2
SETTARGET(REPORT) !You can change a Cloned control's PROPs using ?OriginalFEQ{'BandClone_IntoFEQ',IntoBandFEQ}
TotPageCloneFeq# = ?PagePromptTot{'BandClone_IntoFEQ',?PageHeader}
(TotPageCloneFeq#){PROP:Text} = 'Page#' !Change 'PAGE:' to "Page#' on Page Header after Clone
SETTARGET()
TotalCat='Sub Total on New Page ' ; TotalAmt = RANDOM(11111,99999)
PRINT(RPT:TotalDetail1)
!--Totals page done, resume printing Employee Details by undoing Clone
ENDPAGE(Report)
RptBandCloneUndo(REPORT, ?PageHeader, ?TotalHeader2) !Destroy Cloned 2nd Header
RptBandHideUndo(REPORT, ?PageHeader) !Unhide original Page Header
EmpCode=91 ; EmpName='Return to Normal Page HEADER' ; PRINT(RPT:EmployeeDetail)
EmpCode=92 ; EmpName='After CloneUndo() & HideUndo()' ; PRINT(RPT:EmployeeDetail)
ENDPAGE(Report)
ENDPAGE(Report)
DO ReportPreviewRtn
CLOSE(Report)
Report Header HIDE and Undo Hide Functions:
RptBandHideControls PROCEDURE(REPORT ReportRef, LONG BandToHideFEQ) !Hide all controls on a BAND
ChildFEQ LONG,AUTO
ChildNdx LONG,AUTO
TargetWas &WINDOW,AUTO !Preserve the caller SETTARGET()
CODE
TargetWas &= (SYSTEM{PROP:Target})
SETTARGET(ReportRef)
LOOP ChildNdx=1 TO 999
ChildFEQ=(BandToHideFEQ){PROP:Child,ChildNdx}
IF ~ChildFEQ THEN BREAK.
(ChildFEQ){'BandHide_Was'}=(ChildFEQ){PROP:Hide} !Save what HIDE was to Undo.
(ChildFEQ){PROP:Hide}=TRUE
END
IF TargetWas &= NULL THEN SETTARGET() ELSE SETTARGET(TargetWas).
RETURN
!--------------------------------------------------------------------------------------
RptBandHideUndo PROCEDURE(REPORT ReportRef, LONG BandToUndoHideFEQ) !Undo Hide all controls on a BAND
ChildFEQ LONG,AUTO
ChildNdx LONG,AUTO
TargetWas &WINDOW,AUTO !Preserve the caller SETTARGET()
CODE
TargetWas &= (SYSTEM{PROP:Target})
SETTARGET(ReportRef)
LOOP ChildNdx=1 TO 999
ChildFEQ=(BandToUndoHideFEQ){PROP:Child,ChildNdx}
IF ~ChildFEQ THEN BREAK.
(ChildFEQ){PROP:Hide} = (ChildFEQ){'BandHide_Was'} !Set to value saved in Hide cALL
END
IF TargetWas &= NULL THEN SETTARGET() ELSE SETTARGET(TargetWas).
RETURN
The Clone Detail code is wrapped in the RptBandCloneControls () procedure. It’s more complicated than my first example code because a Header can have a GROUP. That is the Parent of its Child controls so a recursive call to the local procedure CloneChildren() is needed.
RptBandCloneControls PROCEDURE(REPORT ReportRef, LONG IntoBandFEQ, LONG CloneFromBandFEQ) !Clone all controls on a BAND
! E.g. RptBandCloneControls(REPORT, ?PageHeader, ?Header_2nd)
MAP
CloneChildren PROCEDURE(LONG IntoParentFEQ, LONG FromParentFEQ)
END
TargetWas &WINDOW,AUTO !Preserve the caller SETTARGET()
CODE
TargetWas &= (SYSTEM{PROP:Target})
SETTARGET(ReportRef)
CloneChildren(IntoBandFEQ, CloneFromBandFEQ)
IF TargetWas &= NULL THEN SETTARGET() ELSE SETTARGET(TargetWas).
RETURN
CloneChildren PROCEDURE(LONG IntoParentFEQ, LONG FromParentFEQ)
ChildFEQ LONG,AUTO
ChildNdx LONG,AUTO
CloneFEQ LONG,AUTO
PropNdx LONG,AUTO
CODE
LOOP ChildNdx=1 TO 999
ChildFEQ=(FromParentFEQ){PROP:Child,ChildNdx}
IF ~ChildFEQ THEN BREAK.
IF ~ChildFEQ{PROP:PageNo} THEN
CloneFEQ=CLONE(0, ChildFEQ, IntoParentFEQ ) !Clone control of the "From Band" onto the Into
ELSE
DO PageNoCloneUsingCreateRtn
END
(CloneFEQ){'BandClone_FromBand'}=CloneFromBandFEQ !Clone Control Save {'...From Band'} property so Undo can find and DESTROY clones of the From Band
(ChildFEQ){'BandClone_IntoFEQ',IntoParentFEQ}=CloneFEQ !Original Child Control save {'Property'} with Clone's FEQ for if I need to set PROP's
CASE ChildFEQ{PROP:Type}
OF CREATE:Group
CloneChildren(CloneFEQ, ChildFEQ) !Into Clone Group from original Child Group with Parent as GROUP
END
END
RETURN
PageNoCloneUsingCreateRtn ROUTINE !PageNo does Not Clone() right so Create() and set all the PROP's.
CloneFEQ=CREATE(0,ChildFEQ{PROP:Type},IntoParentFEQ) !Create SString STRING(@N4),AT(,),USE(?ReportPageNo),PAGENO
CloneFEQ{PROP:Use} =ChildFEQ{Prop:Use}
CloneFEQ{PROP:TRN} =ChildFEQ{PROP:TRN}
CloneFEQ{PROP:Text} =ChildFEQ{PROP:Text}
CloneFEQ{PROP:PageNo} =ChildFEQ{PROP:PageNo}
CloneFEQ{PROP:Color} =ChildFEQ{PROP:Color}
LOOP PropNdx=1 TO 4 ; CloneFEQ{PROP:AT ,PropNdx}=ChildFEQ{PROP:AT ,PropNdx} ; END
LOOP PropNdx=1 TO 5 ; CloneFEQ{PROP:Font,PropNdx}=ChildFEQ{PROP:Font,PropNdx} ; END
IF ChildFEQ{PROP:Left} THEN CloneFEQ{PROP:Left}='1' ; CloneFEQ{PROP:LeftOffSet} =ChildFEQ{PROP:LeftOffSet}
ELSIF ChildFEQ{PROP:Right} THEN CloneFEQ{PROP:Right}='1' ; CloneFEQ{PROP:RightOffSet} =ChildFEQ{PROP:RightOffSet}
ELSIF ChildFEQ{PROP:Center} THEN CloneFEQ{PROP:Center}='1' ; CloneFEQ{PROP:CenterOffSet} =ChildFEQ{PROP:CenterOffSet}
END
CloneFEQ{PROP:Hide} =ChildFEQ{PROP:Hide} !Finally unhide it, but only if the original was UnHide
EXIT !??? do I have all the PROP's ???
A second complication you see above in the PageNoCloneUsingCreateRtn ROUTINE. The Page Number control (STRING(@n4),PAGENO) did not CLONE() correctly (showed 0) so I had to use CREATE(). Its possible other STRING attributes specific to Reports will have problems like CNT, SUM, AVE, MIN, MAX, RESET, TALLY, PAGE.
Finally … RptBandCloneUndo() has the code to Undo the Clone by doing a DESTROY() on all the cloned controls. The way it finds the controls is with the property {'BandClone_FromBand'} set when cloned. There are other possible ways like a Queue or putting the Clones in a Group.
RptBandCloneUndo PROCEDURE(REPORT ReportRef, LONG UndoIntoBandFEQ, LONG UndoFromBandFEQ) !Undo Clone of controls on a BAND
! E.g. RptBandCloneUndo(REPORT, ?PageHeader, ?Header_2nd)
MAP
DestroyCloneChildren PROCEDURE(LONG IntoParentFEQ)
END
DestroyQ QUEUE
FEQ LONG
END
DNdx LONG
TargetWas &WINDOW,AUTO !Preserve the caller SETTARGET()
CODE
TargetWas &= (SYSTEM{PROP:Target})
SETTARGET(ReportRef)
!The UndoIntoBandFEQ should have Children from prior Clone of Band UndoFromBandFEQ
DestroyCloneChildren(UndoIntoBandFEQ)
LOOP DNdx=1 TO RECORDS(DestroyQ)
GET(DestroyQ,DNdx)
DESTROY(DestroyQ.FEQ) ! DB(' Destroy('& DestroyQ.FEQ & ')' )
END
IF TargetWas &= NULL THEN SETTARGET() ELSE SETTARGET(TargetWas).
RETURN
DestroyCloneChildren PROCEDURE(LONG IntoParentFEQ)
ChildFEQ LONG,AUTO
ChildNdx LONG,AUTO
CODE
LOOP ChildNdx=1 TO 999
ChildFEQ=(IntoParentFEQ){PROP:Child,ChildNdx} !Walk the Parent with the Clones
IF ~ChildFEQ THEN BREAK.
IF (ChildFEQ){'BandClone_FromBand'} <> UndoFromBandFEQ THEN !In Clone Saved this User Prop to know what was cloned
CYCLE
END
CASE ChildFEQ{PROP:Type}
OF CREATE:Group !GROUP wraps Children ...
DestroyCloneChildren(ChildFEQ) !Help says Destroy(Group) Destroys Children, YES that worked in my test, but I decided to leave this
END
!!! DESTROY(ChildFEQ) !DESTROY changes Child Index, so after needs "ChildIdx -= 1" to work ...
DestroyQ.FEQ = ChildFEQ ; ADD(DestroyQ) !Seems safer to Queue them all to Destroy at the end
END
RETURN
The above also handles a GROUP with recursion using DestroyCloneChildren(). This is not actually required because DESTROY(Group) also destroys Children (documented in Help and verified). I choose to leave the recursive code in rather rely on that working in all versions.