Skip to content

Instantly share code, notes, and snippets.

@CarlTBarnes
Last active April 30, 2021 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CarlTBarnes/1e0ddd480e083f8feedb4a98f53176a4 to your computer and use it in GitHub Desktop.
Save CarlTBarnes/1e0ddd480e083f8feedb4a98f53176a4 to your computer and use it in GitHub Desktop.
Report Manual Paging Class and Template
!This CLASS was written to **replace page checking code** in reports that was in many places
!For most all people the **replace** comment will NOT apply.
!
!This is not an easy an easy thing to do. I cannot explain it all here.
!
! Steps to Implement ManRpt Class
!
!1. Add the Extension Template ReportLineHeightMaxSet Template so all lines have
! a. Sets all DETAIL PROP:MaxHeight=Height so line Height is exactly correct
! b. For all DETAILs defines LnHt_Label LONG variable and sets = DETAIL PROP:Height for use with Height calcs
!
!2. Add the Below ManRpt CLASS Declarion to DATA and CODE to Local procedures.
! 2.1 Search and Replace all Height variable with the new LnHt_Xxxx geenrated variables
!
!3. Start of "After Opening Report"
! [Priority 3600]
ManRpt.ReportOpenInit(Report, 167) !167 is the Fudge about.
!4. Find CheckPageOverFlow ROUTINE **replace page checking code**
! and comment it out
! Then add its code to ManRpt.NewPage
!
!5. Search for all PRINT( and change to new logic **replace page checking code**
!
!6. Remove old line counting LCNT variable so you get errors to fix **replace page checking code**
!
!7. Existing REPORT DETAIL cannot have stuff that affect printing and paging
! a. DETAILs must NOT have WithPrior() and WithNext()
! b. All Detail attributes must be considered e.g. ALONE, TOGETHER
! c. BREAK, HEADER, FOOTER must be done manually
! d. Details with PageAfter PageBefore should be removed and handled in Code with .NewPage()
!
!8. Find every PRINT( and do stuff to add ManRpt.
!See *Developer* comments
SECTION('CLASS Declarion goes in DATA Embed')
ManRpt CLASS !Manual Report Class Declarion (Put in "Declaration Section")
ReportOpenInit PROCEDURE(*REPORT RptRef, LONG FudgeLines) !After OPEN(Report) call (Report, FudeLines) to Initilize class
CheckOverflow PROCEDURE(LONG LnHt_ToCheck),BOOL,PROC !Check if PRINT() overflows page, will Eject, does not +=LNCT, call with (LnHt_Xxx+LnHt_Xxx)
AddToLCNT PROCEDURE(LONG LnHt_TotalPrinted) !After PRINT() adds (LnHt_xxx) to LCNT. Does NOT check for Overflow
CheckOverflowAndAddToLCNT PROCEDURE(LONG LnHt_ToPrint, LONG LnHt_ExtraRoom=0, LONG ExtraMultiplier=1),BOOL,PROC !Before PRINT() check if fits then +=LNCT. Call with (LnHt_Xxx, Xtra ROom, Multiplier)
NewPage PROCEDURE() !When Page Overflows this finishs old page and starts New Page (code was in CheckPageOverflow ROUTINE)
ForceNewPage PROCEDURE() !Sets LCNT=99999 so Next CheckOverFlow will go to a New Page.
WillLinesOverflow PROCEDURE(LONG LnHt_TotalLinesToCheck),BOOL !Check if lines fit on page, does NOT do NewPage nor + LCNT
zDB PROCEDURE(STRING DebugMessage) !Debug Output ManRpt LCNT Lines/Page Information
zDebugOff BOOL !Set to True to Turn OFF Debug
!---- Class Data -----
RptRef &REPORT,PRIVATE
LinesPerPage LONG ,PRIVATE !was LinesPerPage !Total Lines on Page = REPORT{Prop:Height} - Fudge lines
LCNT LONG ,PRIVATE !was LCNT !How many lines have printed on the page
PagesPrinted LONG ,PRIVATE !was PAGE# !Number of times hit NewPage
!------ Internal Methods that are used only by class
xWasInitCalled PROCEDURE(),PRIVATE !Chck if forgot to Call ReportOpenInit() and show Message()
END
SECTION('CLASS Definition goes in Local "Procedures" Embed')
!ManRpt Class Procedure Code - Put in "Local Procedures" ========================================
ManRpt.NewPage PROCEDURE() !Called when ManRpt.CheckOverflow() does NOT find enough room to print
! *Developer* Modify this procedure to print Page Heading and do Page breaks the way you want to
! E.g. you may add code to repeat a subheading
CODE
SELF.PagesPrinted += 1 ! PAGE#+=1
SELF.LCNT = 0 ! was LCNT=0
Message('*Developer* YOU need to add your page break logic')
IF SELF.PagesPrinted<>1 THEN ! IF PAGE#<>1 THEN PRINT(RPT:DetailNewPage).
PRINT(RPT:DetailNewPage)
END
! IF PrintingTotals
! PRINT(RPT:BldgTotalsHdg)
! SELF.LCNT = LnHt_BldgTotalsHdg !was LCNT=BldgTotalsHdgHeight
! RETURN
! END
PRINT(RPT:PageHdr)
SELF.LCNT = LnHt_PageHdr !LCNT=PageHdrHeight
RETURN
!------------------------------
ManRpt.ForceNewPage PROCEDURE() !Sets LCNT=99999 so Next CheckOverFlow will go to a New Page.
CODE
SELF.LCNT = 999999
RETURN
!===========================================================================================
! Carl notes the below .CheckOverflowAndAddToLCNT() should NOT need to be changed
!===========================================================================================
ManRpt.CheckOverflowAndAddToLCNT PROCEDURE(LONG LnHt_ToPrint, LONG LnHt_ExtraRoom=0, LONG ExtraMultiplier=1)!,BOOL,PROC !Check if PRINT() fits on page, +=LNCT, call with (LnHt_Xxx+LnHt_Xxx)
IsOverflowed BOOL(False) !^^ Print This ^^ ^^ Allow Room ^^^ ^^ Room for LnHt_ToPrint ^^
PrintPlusXtraLines LONG,AUTO
CODE
PrintPlusXtraLines = LnHt_ToPrint + LnHt_ExtraRoom !allow extra room for Underline that may not print this time
IF ExtraMultiplier > 1 THEN
PrintPlusXtraLines += LnHt_ToPrint * (ExtraMultiplier -1) !can say allow room for 2
END
IF SELF.WillLinesOverflow(PrintPlusXtraLines) THEN
IsOverflowed = True
SELF.NewPage() !Page overflow, call the New Page
END
SELF.AddToLCNT(LnHt_ToPrint) !We ASSume this will PRINT() a Detail this tall after the call
RETURN IsOverflowed !Tell the caller if we ejected the page
!Example:
! ManRpt.CheckThenPrint(LnCt_Detail,0,2) !Print 1, allow room for *2 LnCt_Detail
! PRINT(RPT:Detail) !Now Print 1 Detail
! !Nothing to do here because CheckThenPrint() already added to LCNT
!------------------------------
ManRpt.CheckOverflow PROCEDURE(LONG LnHt_ToCheck)!,BOOL,PROC
IsOverflowed BOOL(False)
CODE
IF SELF.WillLinesOverflow(LnHt_ToCheck) THEN
IsOverflowed = True
SELF.NewPage() !Page overflow, call the New Page
END
RETURN IsOverflowed !Tell the caller we ejected the page
!Example:
! ManRpt.CheckOverflow( LnCt_Detail * 2 + LnHt_Underline) !Will 2 + Underline fit?, ejects page if not room
! PRINT(RPT:Detail) !Now Print 1 Detail
! ManRpt.PRINTed(LnCt_Detail) !This adds to LCNT
!------------------------------
ManRpt.WillLinesOverflow PROCEDURE(LONG LnHt_TotalLinesToCheck)!,BOOL,PRIVATE !Check for new page, but do nothing else
IsOverflowed BOOL(False)
CODE
SELF.xWasInitCalled()
IF LnHt_TotalLinesToCheck + SELF.LCNT > SELF.LinesPerPage THEN
IsOverflowed = True
!this one does NOT --> SELF.NewPage()
END
RETURN IsOverflowed !Tell the caller we ejected the page
!----------------------------
ManRpt.ReportOpenInit PROCEDURE(*REPORT _RptRef, LONG FudgeLines)
CODE
IF _RptRef &= NULL OR STATUS(_RptRef) <> 0 THEN
Message('Programming bug:|ManRpt.ReportOpenInit was called with the REPORT not open, or null.|' & 0{'Proc_Name'})
END
SELF.RptRef &= _RptRef
SELF.LinesPerPage = _RptRef{PROP:Height} - FudgeLines
DB('ManRpt.ReportOpenInit LinesPerPage=' & SELF.LinesPerPage & ' adjusted by Fudge=' & FudgeLines )
SELF.PagesPrinted = 0
SELF.LCNT = 999999 !Force a New Page
RETURN
!------------------------------
ManRpt.xWasInitCalled PROCEDURE() !Tell Forgot to Call ReportOpenInit
CODE
IF SELF.LinesPerPage <=0 OR SELF.RptRef &= NULL THEN
Message('Programming bug:|ManRpt.ReportOpenInit was not been called before calling CheckOverFlow or Printed.|' & 0{'Proc_Name'})
END
RETURN
!------------------------------
ManRpt.AddToLCNT PROCEDURE(LONG LnHt_TotalPrinted) !After PRINT(Rpt:Detail) call with Ht Printed
CODE
SELF.LCNT += LnHt_TotalPrinted
RETURN
!------------------------------
ManRpt.zDB PROCEDURE(STRING DebugMessage) !Debug Output ManRpt LCNT Lines/Page Information
CODE
IF ~SELF.zDebugOff THEN
DB('ManRpt: LCNT=' & SELF.LCNT &' Lines/Page='& self.LinesPerPage &' Page#='& self.PagesPrinted &' - '& CLIP(DebugMessage))
END
RETURN
#!============================================================
#EXTENSION(ReportLineHeightMaxSet,'Report Set Prop:MaxHeight'),PROCEDURE
#!---------------------------------------------
#! would like to #RESTRICT use to when a REPORT is available
#DISPLAY('Code will be generated for each DETAIL to')
#DISPLAY(' set LnHt_DetailName=Report$?Detail{{Prop:Height}')
#DISPLAY(' and set Prop:MaxHeight = LnHt_DetailName.')
#DISPLAY('')
#DISPLAY('Also define a LnHt_DetailName LONG for each DETAIL')
#DISPLAY('')
#DISPLAY('Below list details to exclude:')
#BUTTON('Excluded Details'),MULTI(%ExcludedDetails,%ExcludedDetail),INLINE
#PROMPT('Detail Name:',FROM(%ReportControl,%ReportControlType = 'DETAIL',%ReportControlLabel)),%ExcludedDetail
#ENDBUTTON
#!---------------------------------------------
#! UI to allow not generating
#! skip if it has no AT Height defined i.e. Blank
#! scan if it contains resize controls
#! Report Label, cannot assume "Report?
#!---------------------------------------------
#ATSTART
#DECLARE(%DetailsWithResize),MULTI,UNIQUE
#DECLARE(%DetailsWithExclude),MULTI,UNIQUE
#DECLARE(%Current0Label,STRING)
#DECLARE(%RptAtH,STRING)
#FOR (%ExcludedDetails)
#ADD(%DetailsWithExclude,%ExcludedDetail)
#ENDFOR
#SET(%Current0Label,'')
#FOR(%ReportControl)
#IF(%ReportControlIndent=0)
#SET(%Current0Label,%ReportControlLabel)
#! !Label "%Current0Label" %ReportControlStatement
#CYCLE
#ELSE
#IF(EXTRACT(%ReportControlStatement,'RESIZE')>'')
#! !In Label "%Current0Label" has resize-> %ReportControlStatement
#IF(%Current0Label>'')
#ADD(%DetailsWithResize,%Current0Label)
#ENDIF
#ENDIF
#ENDIF
#ENDFOR
#ENDAT
#!---------------------------------------------
#AT(%DataSectionBeforeWindow)
#!-----------
!ReportLineHeightMaxSet Variables
#FOR(%ReportControl),WHERE(%ReportControlLabel>'')
#CASE(%ReportControlType)
#OF('DETAIL')
#OROF('HEADER')
#OROF('FOOTER')
#ELSE
#CYCLE
#ENDCASE
#! a Blank AT(,,,Height) means no defined ht so leave it out
#SET(%RptAtH,EXTRACT(%ReportControlStatement,'AT',4))
#IF(%RptAtH='')
!Skipped %ReportControlLabel has no AT() Height: %(REPLACE(%ReportControlStatement,'<13,10>',' '))
#CYCLE
#ENDIF
#FIND(%DetailsWithResize,%ReportControlLabel)
#IF(%DetailsWithResize>'')
!Skipped %ReportControlLabel %ReportControlType has RESIZE controls
#CYCLE
#ENDIF
#FIND(%DetailsWithExclude,%ReportControlLabel)
#IF(%DetailsWithExclude>'')
!Skipped %ReportControlLabel %ReportControlType is Excluded in Template by user
#CYCLE
#ENDIF
#! ReportControlStatement could have a |<13,10> at the end and mess up
#!LnHt_%[25]ReportControlLabel LONG !%(CLIP(SUB(%ReportControlStatement,1,40)))
LnHt_%[25]ReportControlLabel LONG !%(REPLACE(%ReportControlStatement,'<13,10>',' '))
#IF(%RptAtH='0')
!WARNING! %ReportControlLabel has AT(,,,0) Height ZERO this will not print
#ENDIF
#ENDFOR
#!!----------------- Report Structure -------------------------
#!!ReportControlIndent 0=TopLevel 1=Control so hunt >0 for TEXT,RESIZE
#!#FOR(%ReportControl)
#!#SET(%RptAtH,EXTRACT(%ReportControlStatement,'AT',4))
#!!%ReportControlIndent At4=%RptAtH %ReportControlType => %ReportControlStatement
#!#ENDFOR
#!-----------------
#ENDAT
#!-------------------------------- Assign Heights ----------------
#AT(%AfterOpeningReport),PRIORITY(9000)
#DECLARE(%RptUse1)
!ReportLineHeightMaxSet Template
#FOR(%ReportControl),WHERE(%ReportControlLabel>'')
#CASE(%ReportControlType)
#OF('DETAIL')
#OROF('HEADER')
#OROF('FOOTER')
#ELSE
#CYCLE
#ENDCASE
#! a Blank AT(,,,Height) means no defined ht so leave it out
#!Looking at %ReportControlLabel Use=%ReportControlUse
#SET(%RptAtH,EXTRACT(%ReportControlStatement,'AT',4))
#IF(~%RptAtH)
!Skipped %ReportControlLabel %ReportControlType has no AT() Height
#CYCLE
#ENDIF
#FIND(%DetailsWithResize,%ReportControlLabel)
#IF(%DetailsWithResize)
!Skipped %ReportControlLabel %ReportControlType has RESIZE controls
#CYCLE
#ENDIF
#FIND(%DetailsWithExclude,%ReportControlLabel)
#IF(%DetailsWithExclude)
!Skipped %ReportControlLabel %ReportControlType is Excluded in Template by user
#CYCLE
#ENDIF
#! #IF(~%ReportControlUse)
#!!%ReportControlLabel %ReportControlType has no USE(ReportControlUse)
#! #CYCLE
#! #ENDIF
#! "%ReportControlUse" does NOT seem to have a value for DETAIL so must Extract it from USE(?xxxx)
#SET(%RptUse1,EXTRACT(%ReportControlStatement,'USE',1))
#IF(~%RptUse1)
!Skipped %ReportControlLabel %ReportControlType has no USE(%ReportControlUse)
#CYCLE
#ENDIF
#!Generate line for %ReportControlLabel Use=%ReportControlUse RptUse1=%RptUse1
LnHt_%[20]ReportControlLabel = %Report$%RptUse1{PROP:Height} ; %Report$%RptUse1{PROP:MaxHeight} = LnHt_%ReportControlLabel
#IF(%RptAtH='0')
!WARNING! %ReportControlLabel has AT(,,,0) Height ZERO this will not print unless Height and MaxHeight are set
#ENDIF
#ENDFOR
#ENDAT
#!============================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment