*--------------------------------------------------------------------------------------------------------------------------------------------------------
* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!!
*--------------------------------------------------------------------------------------------------------------------------------------------------------
*< FOXBIN2PRG: Version="1.21" SourceFile="_reportlistener.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries)
*
*
DEFINE CLASS _reportlistener AS reportlistener 		&& Adds error handling, session handling, and other common report run-time tasks to ReportListener base class.  Provides the ability to chain a series of reports as well as the means to delegate or share output activities to a chain of Listener-successors.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: addreport		&& Adds to the class's collection of ReportFileNames, optionally associating REPORT FORM clauses and a listener for the specified report.
		*m: allowmodalmessages_assign
		*m: appname_assign
		*m: clearerrors		&& Resets the class's error status.
		*m: getfrxstartupinfo		&& Provides a hook for gathering FRX information during BeforeReport method processing.
		*m: getlasterrormessage		&& Provides information about the last error that occurred.
		*m: invokeoncurrentpass		&& Provides a hook for listeners to evaluate whether they wish to generate output or perform other actions during the current report execution pass.
		*m: issuccessor_assign
		*m: lignoreerrors_assign
		*m: listenertype_assign
		*m: outputtype_assign
		*m: pagelimitinsiderange_assign
		*m: pagelimitquietmode_assign
		*m: pagelimit_assign
		*m: pagetaillimit_assign
		*m: pagetoplimit_assign
		*m: popglobalsets		&& Provides a hook for Listeners to restore global settings not scoped to a data session after saving them with PushGlobalSets.
		*m: prepareerrormessage		&& Organizes common error information values (nError, cMethod, nLine, cName, cMessage, cCodeLine) into a coherent string for presentation to the user.
		*m: pushglobalsets		&& Provides a hook for Listeners to save global settings not scoped to a data session for later restoration with PopGlobalSets.
		*m: quietmode_assign
		*m: removereports		&& Removes report filenames as well as associated clauses and listeners from this Listeners' various collections.
		*m: resetdatasession		&& Sets the DataSessionID to the session where the Listener originated.
		*m: runreports		&& Executes a series of REPORT FORM commands according to the instructions in the ReportFileNames, ReportClauses, and Listeners collections.  Optionally clears collection after run and issues the REPORT FORM commands without OBJECTreferences.
		*m: setcurrentdatasession		&& Sets the DataSessionID to the data session holding report's data tables.
		*m: setfrxdatasession		&& Sets the DataSessionID to the data session in which the Engine has opened a readonly copy of the report file as a table for the Listener's use.
		*m: setfrxdatasessionenvironment		&& Provides a hook for classes to determine the datasession-scoped SETs they wish to add to the private FRX data session.
		*m: setfrxrunstartupconditions		&& Hook method called in BeforeReport, allowing you to set up CommandClauses properties or other attributes required by your class.
		*m: setsuccessordynamicproperties		&& Provides a hook for the Listener to share information changed by the Engine with a succession of Listeners, during the run of a report.
		*m: sharedgdiplusgraphics_assign
		*m: sharedoutputpagecount_assign		&& Provides a readwrite copy of the the Engine's OutputPageCount property which the Listener can share with a succession chain.
		*m: sharedpageheight_assign
		*m: sharedpageno_assign
		*m: sharedpagetotal_assign
		*m: sharedpagewidth_assign
		*m: successor_assign
		*p: appname		&& Localizable application name string for use in user feedback.
		*p: drivingalias		&& Holds the alias of the table or cursor driving the report scope.
		*p: haderror		&& Provides a flag indicating whether an error occurred.
		*p: isrunning		&& Provides a flag to indicate whether a report run is underway.  When IsRunning is true, the class may wish to disallow certain activities or method calls.
		*p: isrunningreports		&& Provides a flag to indicate this ReportListener is running a series of reports using its collection.
		*p: issuccessor		&& Indicates whether this Listener is chained to one or more others to provide output during a report run.  When .T., this Listener was not the object referenced in the REPORT FORM command OBJECT clauses.
		*p: lasterrormessage
		*p: lignoreerrors		&& Provides a flag to determine how this class handles activities subsequent to an error.
		*p: listenerdatasession		&& Saves the DataSessionID in which the Listener originated.
		*p: listeners		&& Collection of ReportListeners associated with each report in this Listener's ReportFileNames collection.
		*p: pagelimit		&& If > 0, represents the highest number of pages allowed in a report run (potentially across multiple reports using NOPAGEEJECT).  Especially useful for ListenerTypes 1 and 3, to avoid GDI resource issues, but can provide abbreviated results for any type
		*p: pagelimitinsiderange		&& If .T., indicates that pageTopLimit and pageTailLimit provide an inside range rather than beginning and end of report contents.  Makes pageTopLimit and pageTailLimit similar to RANGE clause, but active over multiple reports with NOPAGEEJECT.
		*p: pagelimitquietmode		&& Indicates whether the class provides user feedback when the report results are limited because the run exceeded the specified pageLimit.
		*p: pagetaillimit		&& If > 0, represents the lowest number of pages for "tail" section of report run (potentially across multiple reports using NOPAGEEJECT).   Use with pageTopLimit or alone. No user feedback provided.
		*p: pagetoplimit		&& If > 0, represents the highest number of pages for "top" section of report run (potentially across multiple reports using NOPAGEEJECT).   Use with pageTailLimit or alone. No user feedback provided.
		*p: reportclauses		&& Stores REPORT FORM command clauses associated with each report in the ReportFileNames collection.
		*p: reportfilenames		&& Stores the filenames of reports to be managed and executed in a series.
		*p: reportusesprivatedatasession		&& Provides a flag to indicate whether this report shares the data session from which it was executed or maintains a private data session.
		*p: sharedgdiplusgraphics		&& Provides a readwrite copy of the the Engine's GDIPlusGraphics handle which the Listener can share with a succession chain.
		*p: sharedoutputpagecount		&& Provides a readwrite copy of the the Engine's OutputPageCount property which the Listener can share with a succession chain.
		*p: sharedpageheight		&& Shares information gathered by the GetPageHeight method with other Listeners linked in a succession chain.
		*p: sharedpageno		&& Provides a readwrite copy of the the Engine's PageNo property which the Listener can share with a succession chain.
		*p: sharedpagetotal		&& Provides a readwrite copy of the the Engine's PageTotal property which the Listener can share with a succession chain.
		*p: sharedpagewidth		&& Shares information gathered by the GetPageWidth method with other Listeners linked in a succession chain.
		*p: successor		&& An object reference to the next Listener in a succession chain.
		*a: reportpages[1,0]		&& Holds the page count for each report when this Listener runs a collection of reports as a series.
		*p: _memberdata		&& XML Metadata for customizable properties
	*</DefinedPropArrayMethod>

	PROTECTED drivingalias,haderror,isrunning,isrunningreports,lasterrormessage,listenerdatasession,listeners,reportclauses,reportfilenames,reportpages
	*<PropValue>
		AllowModalMessages = (INLIST(_VFP.Startmode, 0, 4))
		appname = ("VFP Report Listener")
		drivingalias = ("")
		FRXDataSession = -1
		Height = 23
		lasterrormessage = ("")
		listenerdatasession = 1
		listeners = .NULL.
		Name = "_reportlistener"
		pagelimit = -1
		pagelimitinsiderange = .F.
		pagelimitquietmode = .F.
		pagetaillimit = -1
		pagetoplimit = -1
		QuietMode = (NOT INLIST(_VFP.Startmode, 0, 4))
		reportclauses = .NULL.
		reportfilenames = .NULL.
		sharedgdiplusgraphics = 0
		sharedoutputpagecount = 0
		sharedpageheight = 0
		sharedpageno = 0
		sharedpagetotal = 0
		sharedpagewidth = 0
		successor = (.NULL.)
		Width = 23
		_memberdata = <VFPData>
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
			<memberdata name="getfrxstartupinfo" type="method" display="getFRXStartupInfo" favorites="False" />
			<memberdata name="getlasterrormessage" type="method" display="get<VFPData><memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="addreport" type="method" display="addReport" favorites="True" />
			<memberdata name="appname" type="property" display="appName" favorites="True" />
			<memberdata name="clearerrors" type="method" display="clearErrors" favorites="True" />
			<memberdata name="drivingalias" type="property" display="drivingAlias" favorites="False" />
		</VFPData>
	*</PropValue>
	
	PROCEDURE addreport		&& Adds to the class's collection of ReportFileNames, optionally associating REPORT FORM clauses and a listener for the specified report.
		LPARAMETERS m.tcFRXName, m.tcClauses, m.toListener
		
		* can this one be done while report is running?
		* Possibly yes because we're always adding to the end.
		
		IF VARTYPE(m.tcFrxName) = "C" AND ;
		   (FILE(m.tcFRXName) OR FILE(FORCEEXT(m.tcFRXName,"FRX")) OR FILE(FORCEEXT(m.tcFRXName,"LBX")))
		
		   * If any is null, create all collections
		   * always add to all three collections
		   * to keep them in synch
		   
		   IF ISNULL(THIS.ReportFileNames) OR ;
		      ISNULL(THIS.ReportClauses) OR ;
		      ISNULL(THIS.Listeners) 
		      * start fresh
		      THIS.ReportFileNames = CREATEOBJECT("Collection")
		      THIS.ReportClauses = CREATEOBJECT("Collection")
		      THIS.Listeners = CREATEOBJECT("Collection")
		      DIME THIS.ReportPages[1]
		   ENDIF
		   
		   THIS.ReportFileNames.Add(m.tcFRXName)
		   
		   DIME THIS.ReportPages[THIS.ReportFileNames.Count]
		   THIS.ReportPages[THIS.ReportFileNames.Count] = 0
		
		   IF VARTYPE(m.tcClauses) = "C"
		      THIS.ReportClauses.Add(m.tcClauses)
		   ELSE
		      THIS.ReportClauses.Add("")   
		   ENDIF
		
		   IF TYPE("toListener.BaseClass") = "C" AND ;
		      UPPER(toListener.BaseClass) == "REPORTLISTENER"
		      THIS.Listeners.Add(toListener)
		   ELSE
		      THIS.Listeners.Add(NULL)      
		   ENDIF
		   
		ELSE
		
		   * TBD: should we error here?   
		   
		ENDIF   
		
		
	ENDPROC

	PROCEDURE AfterBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecno
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.AfterBand(m.nBandObjCode, m.nFRXRecNo)
		ENDIF
		
	ENDPROC

	PROCEDURE AfterReport
		IF NOT THIS.IsSuccessor
		   THIS.SharedPageWidth = THIS.GetPageWidth()
		   THIS.SharedPageHeight = THIS.GetPageHeight()
		ENDIF
		
		IF NOT ISNULL(THIS.Successor)
		   WITH THIS.Successor
		      .FRXDataSession = THIS.FRXDataSession
		      .CurrentDataSession = THIS.CurrentDataSession
		      .TwoPassProcess = THIS.TwoPassProcess
		      .CommandClauses = THIS.CommandClauses
		      .SharedPageHeight = THIS.SharedPageHeight
		      .SharedPageWidth = THIS.SharedPageWidth
		      THIS.SetSuccessorDynamicProperties()
		      .AfterReport()
		   ENDWITH
		ENDIF
		IF NOT THIS.IsSuccessor
		   NODEFAULT
		   DODEFAULT()
		ENDIF   
		
		
	ENDPROC

	PROCEDURE allowmodalmessages_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.AllowModalMessages = m.vNewVal
		ENDIF   
		
		
	ENDPROC

	PROCEDURE appname_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.appname = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE BeforeBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo
		
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.BeforeBand(m.nBandObjCode, m.nFRXRecNo)
		ENDIF
		
	ENDPROC

	PROCEDURE BeforeReport
		THIS.setFRXRunStartupConditions()
		
		THIS.getFRXStartupInfo()
		
		THIS.resetDataSession()
		
		IF NOT THIS.IsSuccessor
		   THIS.sharedPageHeight = THIS.GetPageHeight()
		   THIS.sharedPageWidth = THIS.GetPageWidth()
		ENDIF
		
		IF NOT ISNULL(THIS.Successor)
		   WITH THIS.Successor
		      .AddProperty("sharedGDIPlusGraphics", THIS.sharedGDIPlusGraphics)      
		      .AddProperty("sharedPageHeight", THIS.sharedPageHeight)
		      .AddProperty("sharedPageWidth", THIS.sharedPageWidth)      
		      .AddProperty("sharedOutputPageCount", THIS.sharedOutputPageCount)
		      .AddProperty("sharedPageNo", THIS.sharedPageNo)      
		      .AddProperty("sharedPageTotal", THIS.sharedPageTotal)    
		       THIS.setSuccessorDynamicProperties()        
		      .FRXDataSession = THIS.FRXDataSession
		      .CurrentDataSession = THIS.CurrentDataSession
		      .TwoPassProcess = THIS.TwoPassProcess
		      .CommandClauses = THIS.CommandClauses
		      .BeforeReport()
		   ENDWITH
		ENDIF
		
		
	ENDPROC

	PROCEDURE CancelReport
		IF NOT THIS.IsSuccessor
		   DODEFAULT()
		   NODEFAULT
		ENDIF   
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.CancelReport()
		ENDIF
		
	ENDPROC

	PROCEDURE clearerrors		&& Resets the class's error status.
		THIS.HadError = .F.
		THIS.LastErrorMessage = ""
	ENDPROC

	PROCEDURE ClearStatus
		DODEFAULT()
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.ClearStatus()
		ENDIF   
	ENDPROC

	PROCEDURE Destroy
		STORE NULL TO ;
		  THIS.Successor, ;
		  THIS.Listeners, ;
		  THIS.ReportClauses, ;
		  THIS.ReportFileNames, ;
		  THIS.PreviewContainer, ;
		  THIS.CommandClauses
		
	ENDPROC

	PROCEDURE DoMessage
		LPARAMETERS m.cMessage,m.iParams,m.cTitle
		NODEFAULT
		IF THIS.QuietMode OR ;
		  (THIS.IsRunning AND THIS.CommandClauses.NoDialog)
		   * to emulate the base class behavior, do both checks,
		   * in case the call to DoMessage() occurs
		   * before the baseclass sets QuietMode .T. in response
		   * to NoDialog at the beginning of the report run,
		   * or after the baseclass re-sets Quietmode to .F.
		   * at the end of the report run.
		   RETURN 0
		ELSE
		   IF THIS.AllowModalMessages
		      IF VARTYPE(m.cTitle) = "C"
		         RETURN MESSAGEBOX(TRANS(m.cMessage),VAL(TRANS(m.iParams)),m.cTitle)
		      ELSE
		         RETURN MESSAGEBOX(TRANS(m.cMessage),VAL(TRANS(m.iParams)),THIS.AppName)
		      ENDIF
		   ELSE
		      THIS.DoStatus(m.cMessage)
		      RETURN 0
		   ENDIF
		ENDIF   
	ENDPROC

	PROCEDURE DoStatus
		LPARAMETERS m.cMessage
		NODEFAULT
		IF NOT (THIS.QuietMode OR (THIS.IsRunning AND THIS.CommandClauses.Nodialog))
		   IF THIS.TwoPassProcess AND THIS.CurrentPass = 0
		      WAIT WINDOW NOWAIT OUTPUTCLASS_PREPSTATUS_LOC 
		   ELSE
		      IF VARTYPE(m.cMessage) = "C"
		         DODEFAULT(m.cMessage)      
		      ENDIF
		   ENDIF
		ENDIF
	ENDPROC

	PROCEDURE Error
		LPARAMETERS m.nError, m.cMethod, m.nLine
		LOCAL m.lcOnError,m.lcErrorMsg,m.lcCodeLineMsg
		THIS.HadError = .T.
		IF this.lIgnoreErrors OR _vfp.StartMode>0
			RETURN .F.
		ENDIF
		m.lcOnError=UPPER(ALLTRIM(ON("ERROR")))
		IF NOT EMPTY(m.lcOnError)
			m.lcOnError=STRTRAN(STRTRAN(STRTRAN(lcOnError,"ERROR()","nError"), ;
					"PROGRAM()","cMethod"),"LINENO()","nLine")
			&lcOnError
			RETURN
		ENDIF
		m.lcErrorMsg = THIS.PrepareErrorMessage(m.nError,m.cMethod, m.nLine)
		THIS.LastErrorMessage = m.lcErrorMsg
		
		THIS.DoMessage(m.lcErrorMsg, MB_ICONSTOP )
		
		#IF OUTPUTCLASS_DEBUGGING
		    ERROR m.nError
		#ENDIF    
	ENDPROC

	PROTECTED PROCEDURE getfrxstartupinfo		&& Provides a hook for gathering FRX information during BeforeReport method processing.
		THIS.SetFRXDataSession()
		IF USED("FRX")
		   SELECT FRX
		   LOCATE FOR ObjType = FRX_OBJTYP_DATAENV 
		   THIS.ReportUsesPrivateDataSession = Frx.Environ
		   * could also use 
		   * THIS.CommandClauses.StartDataSession # THIS.CurrentDataSession
		ELSE
		   THIS.ReportUsesPrivateDataSession = .F.   
		ENDIF   
		THIS.SetCurrentDataSession()
		IF THIS.reportUsesPrivateDataSession
		   SET TALK OFF
		ENDIF
		THIS.DrivingAlias = UPPER(ALIAS())
	ENDPROC

	PROCEDURE getlasterrormessage		&& Provides information about the last error that occurred.
		RETURN STRTRAN(THIS.LastErrorMessage, CHR(13), " ")
	ENDPROC

	PROCEDURE IncludePageInOutput
		LPARAMETERS m.nPageNo
		LOCAL m.llInclude
		IF (NOT THIS.isSuccessor) AND ;
		   (THIS.PageLimit > 0) AND ;
		   (THIS.PageNo > THIS.PageLimit)
		   * note that nPageNo and the 
		   * current CommandClauses.RangeFrom and RangeTo values
		   * only refer to the current report, which
		   * is potentially one of a series using NOPAGEEJECT.
		   * THIS.PageNo refers to the overall number of pages that
		   * have been run, across the multiple reports.
		   IF NOT THIS.pageLimitQuietMode 
		      THIS.DoMessage(OUTPUTCLASS_PAGELIMIT_LOC , MB_ICONEXCLAMATION)
		   ENDIF      
		   THIS.LastErrorMessage = OUTPUTCLASS_PAGELIMIT_LOC
		   THIS.CancelReport()
		ELSE
		   DO CASE
		   CASE (THIS.isSuccessor) 
		      * do not try to limit run unless
		      * communicating with native engine
		      m.llInclude = .T.
		   CASE (THIS.PageTopLimit = -1) AND ;
		        (THIS.PageTailLimit = -1)
		      * no limits set        
		      m.llInclude = .T.      
		   CASE THIS.PageTopLimit = -1
		     * only bottom end requested
		      IF THIS.PageLimitInsideRange 
		         m.llInclude = (THIS.PageNo <= THIS.PageTailLimit)      
		      ELSE
		         m.llInclude = (THIS.PageNo >= THIS.PageTailLimit)
		      ENDIF         
		   CASE THIS.PageTailLimit = -1
		     * only top end requested
		      IF THIS.PageLimitInsideRange 
		         m.llInclude = (THIS.PageNo >= THIS.PageTopLimit)      
		      ELSE
		         m.llInclude = (THIS.PageNo <= THIS.PageTopLimit)
		      ENDIF         
		   OTHERWISE
		      * both top and tail requested
		      IF THIS.PageLimitInsideRange
		         m.llInclude = BETWEEN(THIS.PageNo,THIS.PageTopLimit, THIS.PageTailLimit)
		      ELSE
		         m.llInclude = ((THIS.PageNo <= THIS.PageTopLimit) OR ;
		                        (THIS.PageNo >= THIS.PageTailLimit))
		      ENDIF                        
		   ENDCASE      
		ENDIF          
		RETURN (m.llInclude AND DODEFAULT(m.nPageNo))
		
	ENDPROC

	PROCEDURE Init
		THIS.listenerDataSession = SET("DATASESSION")  
		
		IF DODEFAULT() 
		   THIS.AppName = OUTPUTCLASS_APPNAME_LOC
		ELSE
		   RETURN .F.
		ENDIF      
		
		RETURN NOT THIS.HadError
	ENDPROC

	PROCEDURE invokeoncurrentpass		&& Provides a hook for listeners to evaluate whether they wish to generate output or perform other actions during the current report execution pass.
		RETURN .T.
	ENDPROC

	PROCEDURE issuccessor_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.isSuccessor = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE lignoreerrors_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.lIgnoreErrors = m.vNewVal
		ENDIf   
		
		
	ENDPROC

	PROCEDURE listenertype_assign
		LPARAMETERS m.vNewVal
		IF THIS.SupportsListenerType(m.vNewVal) AND ;
		   NOT THIS.IsRunning
		   THIS.ListenerType = m.vNewVal
		ENDIF
		
	ENDPROC

	PROCEDURE LoadReport
		THIS.clearErrors()
		
		THIS.setFRXDataSessionEnvironment()
		
		THIS.resetDataSession()
		   
		IF NOT ISNULL(THIS.Successor)
		   WITH THIS.Successor
		      .AddProperty("IsSuccessor",.T.)
		      .OutputType = THIS.OutputType
		      .PrintJobName = THIS.PrintJobName 
		      .CommandClauses = THIS.CommandClauses
		      .LoadReport()
		   ENDWITH
		ENDIF
		   
	ENDPROC

	PROCEDURE outputtype_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N" AND NOT THIS.IsRunning
		   THIS.OutputType = INT(m.vNewVal)
		   IF THIS.SupportsListenerType(THIS.OutputType) 
		      THIS.ListenerType = THIS.OutputType
		   ENDIF    
		ENDIF
		
	ENDPROC

	PROCEDURE pagelimitinsiderange_assign
		LPARAMETERS m.tVal
		IF VARTYPE(m.tVal) = "L" 
		   THIS.PageLimitInsideRange = m.tVal
		ENDIF
		
		
	ENDPROC

	PROCEDURE pagelimitquietmode_assign
		LPARAMETERS m.tVal
		IF VARTYPE(m.tVal) = "L" 
		   THIS.PageLimitQuietMode = m.tVal
		ENDIF
		
		
	ENDPROC

	PROCEDURE pagelimit_assign
		LPARAMETERS m.tVal
		IF VARTYPE(m.tVal) = "N" AND CEILING(m.tVal) > 0
		   THIS.PageLimit = CEILING(m.tVal)
		ELSE 
		   THIS.PageLimit = -1        
		ENDIF
		
		
	ENDPROC

	PROCEDURE pagetaillimit_assign
		LPARAMETERS m.tVal
		IF VARTYPE(m.tVal) = "N" AND CEILING(m.tVal) > 0
		   THIS.PageTailLimit = CEILING(m.tVal)
		ELSE 
		   THIS.PageTailLimit = -1        
		ENDIF
		
		
	ENDPROC

	PROCEDURE pagetoplimit_assign
		LPARAMETERS m.tVal
		IF VARTYPE(m.tVal) = "N" AND CEILING(m.tVal) > 0
		   THIS.PageTopLimit = CEILING(m.tVal)
		ELSE 
		   THIS.PageTopLimit = -1        
		ENDIF
		
		
	ENDPROC

	PROTECTED PROCEDURE popglobalsets		&& Provides a hook for Listeners to restore global settings not scoped to a data session after saving them with PushGlobalSets.
		* abstract: restore any globals here that aren't session-bound      
		
		
	ENDPROC

	PROCEDURE prepareerrormessage		&& Organizes common error information values (nError, cMethod, nLine, cName, cMessage, cCodeLine) into a coherent string for presentation to the user.
		LPARAMETERS m.nError, m.cMethod, m.nLine, m.cName, m.cMessage, m.cCodeLine
		
		LOCAL m.lcErrorMessage, m.lcCodeLineMsg
		
		IF VARTYPE(cMessage) = "C"
		   m.lcErrorMessage = m.cMessage
		ELSE
		   m.lcErrorMessage = MESSAGE()
		ENDIF
		
		m.lcErrorMessage = m.lcErrorMessage + CHR(13) + CHR(13)
		
		IF VARTYPE(cName) = "C"
		   m.lcErrorMessage = m.lcErrorMessage + m.cName
		ELSE
		   m.lcErrorMessage = m.lcErrorMessage + this.Name
		ENDIF
		
		m.lcErrorMessage = m.lcErrorMessage + CHR(13)+ ;
		 		 OUTPUTCLASS_ERRNOLABEL_LOC +ALLTRIM(STR(m.nError))+CHR(13)+ ;
				 OUTPUTCLASS_ERRPROCLABEL_LOC +LOWER(ALLTRIM(m.cMethod))
				
		IF VARTYPE(m.cCodeLine) = "C"
		   m.lcCodeLineMsg = m.cCodeLine
		ELSE
		   m.lcCodeLineMsg = MESSAGE(1)
		ENDIF		
		
		IF BETWEEN(m.nLine,1,100000) AND NOT m.lcCodeLineMsg="..."
			m.lcErrorMessage= ;
		       m.lcErrorMessage+CHR(13)+ OUTPUTCLASS_ERRLINELABEL_LOC+ ;
			    ALLTRIM(STR(m.nLine))
			IF NOT EMPTY(m.lcCodeLineMsg)
		       m.lcErrorMessage= ;
				   m.lcErrorMessage+CHR(13)+CHR(13)+m.lcCodeLineMsg
			ENDIF
		ENDIF
		
		RETURN m.lcErrorMessage
	ENDPROC

	PROTECTED PROCEDURE pushglobalsets		&& Provides a hook for Listeners to save global settings not scoped to a data session for later restoration with PopGlobalSets.
		  * abstract: set any globals here that aren't session-bound
		
	ENDPROC

	PROCEDURE quietmode_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.quietmode = m.vNewVal
		ENDIF 
	ENDPROC

	PROCEDURE removereports		&& Removes report filenames as well as associated clauses and listeners from this Listeners' various collections.
		IF NOT (THIS.IsRunningReports)
		   THIS.ReportFileNames = NULL
		   THIS.ReportClauses = NULL
		   THIS.Listeners = NULL
		   DIME THIS.ReportPages[1]
		   THIS.ReportPages[1] = 0
		   
		ENDIF   
	ENDPROC

	PROCEDURE Render
		LPARAMETERS m.nFRXRecno, m.nLeft, m.nTop, m.nWidth, m.nHeight, ;
		            m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.Render( m.nFRXRecno, m.nLeft, m.nTop, m.nWidth, m.nHeight, ;
		                          m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage)
		ENDIF
		
		
	ENDPROC

	PROTECTED PROCEDURE resetdatasession		&& Sets the DataSessionID to the session where the Listener originated.
		IF (THIS.listenerDataSession > -1) 
		   TRY
		      SET DATASESSION TO (THIS.listenerDataSession)
		   CATCH WHEN .T.
		      THIS.ResetToDefault("listenerDataSession")
		      SET DATASESSION TO (THIS.listenerDataSession)      
		   ENDTRY
		ENDIF   
		
	ENDPROC

	PROCEDURE runreports		&& Executes a series of REPORT FORM commands according to the instructions in the ReportFileNames, ReportClauses, and Listeners collections.  Optionally clears collection after run and issues the REPORT FORM commands without OBJECTreferences.
		LPARAMETERS m.tlRemoveReportsAfterRun, m.tlOmitListenerReferences
		
		IF NOT ;
		 (THIS.IsRunningReports OR ;
		  ISNULL(THIS.ReportFileNames) OR ;
		  THIS.ReportFileNames.Count = 0)
		
		  LOCAL m.oError, m.liIndex, m.lcClauses, m.loListener
		  m.oError = NULL
		
		  THIS.IsRunningReports = .T.
		
		  TRY 
		  
		    FOR m.liIndex = 1 TO THIS.ReportFileNames.Count
		       * these collections are 
		       * protected properties, we're
		       * taking care of how they match up, 
		       * that FRXs exist, etc.
		       m.lcClauses = UPPER(THIS.ReportClauses[m.liIndex])
		       m.loListener = THIS.Listeners[m.liIndex]
		       DO CASE 
		       CASE " OBJE " $ STRTRAN(" "+m.lcClauses,"CT", " ") OR ;
		            " OBJEC " $ " "+m.lcClauses OR ;
		            m.tlOmitListenerReferences
		          REPORT FORM (THIS.ReportFileNames[m.liIndex]) &lcClauses  
		       CASE ISNULL(loListener)
		          REPORT FORM (THIS.ReportFileNames[m.liIndex]) &lcClauses  OBJECT THIS
		       OTHERWISE
		          REPORT FORM (THIS.ReportFileNames[m.liIndex]) &lcClauses  OBJECT m.loListener
		       ENDCASE
		       IF NOT (" NOWA " $ STRTRAN(" "+m.lcClauses,"IT"," ") OR ;
		               " NOWAI " $ " " + m.lcClauses) 
		          THIS.ReportPages[m.liIndex] = THIS.SharedPageTotal
		          * TBD: make this a two-column array with 
		          * output pages (responsive to RANGE clause)
		          * represented as well?
		       ENDIF
		    ENDFOR
		    
		         
		  CATCH TO m.oError
		     IF NOT (ISNULL(m.oError))
		         THIS.DoMessage(;
		               THIS.PrepareErrorMessage(;
		               m.oError.ErrorNo, ;
		               m.oError.PROCEDURE, ;
		               m.oError.LINENO, ;
		               THIS.AppName, ;
		               m.oError.MESSAGE, ;
		               m.oError.LineContents), ;
		               MB_ICONSTOP )
		        #IF OUTPUTCLASS_DEBUGGING
		         SUSPEND
		        #ENDIF                   
		         EXIT  
		      ENDIF
		  FINALLY
		  
		     THIS.IsRunningReports = .F.  
		     IF m.tlRemoveReportsAfterRun
		       THIS.RemoveReports()
		     ENDIF  
		     STORE NULL TO m.loListener, m.oError
		
		  ENDTRY
		  
		  
		ENDIF   
		
		
		
		
		
	ENDPROC

	PROTECTED PROCEDURE setcurrentdatasession		&& Sets the DataSessionID to the data session holding report's data tables.
		IF (THIS.CurrentDataSession # SET("DATASESSION"))  
		   TRY
		      SET DATASESSION TO (THIS.CurrentDataSession)
		   CATCH WHEN .T.
		      THIS.ResetToDefault("CurrentDataSession")
		      THIS.resetDataSession()
		   ENDTRY
		ENDIF   
		
		
	ENDPROC

	PROTECTED PROCEDURE setfrxdatasession		&& Sets the DataSessionID to the data session in which the Engine has opened a readonly copy of the report file as a table for the Listener's use.
		IF (THIS.FRXDataSession > -1) AND (THIS.FRXDataSession # SET("DATASESSION"))
		
		   TRY
		      SET DATASESSION TO (THIS.FRXDataSession)
		   CATCH WHEN .T.
		      THIS.ResetToDefault("FRXDataSession")
		      THIS.resetDataSession()
		   ENDTRY
		   
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE setfrxdatasessionenvironment		&& Provides a hook for classes to determine the datasession-scoped SETs they wish to add to the private FRX data session.
		THIS.setFRXDataSession()
		SET TALK OFF 
	ENDPROC

	PROTECTED PROCEDURE setfrxrunstartupconditions		&& Hook method called in BeforeReport, allowing you to set up CommandClauses properties or other attributes required by your class.
		IF ISNULL(THIS.CommandClauses)
		   THIS.CommandClauses = CREATEOBJECT("Empty")
		ENDIF
		IF TYPE("THIS.CommandClauses.NoDialog") # "L"
		   ADDPROPERTY(THIS.CommandClauses,"NoDialog",.F.)
		ENDIF      
		
		
		* add anything critical during a run
		* that might not be available, whether
		* because this is a custom attribute
		* or because public methods of ReportListener
		* might be called outside a normal report run.
	ENDPROC

	PROCEDURE setsuccessordynamicproperties		&& Provides a hook for the Listener to share information changed by the Engine with a succession of Listeners, during the run of a report.
		IF NOT THIS.isSuccessor
		   THIS.sharedOutputPageCount = THIS.OutputPageCount
		   THIS.sharedPageTotal = THIS.PageTotal
		   THIS.sharedPageNo = THIS.PageNo
		   THIS.sharedGdiplusGraphics = THIS.GDIPlusGraphics 
		ENDIF
		WITH THIS.Successor
		   .CurrentPass = THIS.CurrentPass
		   .TwoPassProcess = THIS.TwoPassProcess   
		   .sharedOutputPageCount = THIS.sharedOutputPageCount
		   .sharedPageTotal = THIS.sharedPageTotal   
		   .sharedPageNo = THIS.sharedPageNo
		   .sharedGdiplusGraphics  = THIS.sharedGdiplusGraphics
		ENDWITH   
		
	ENDPROC

	PROCEDURE sharedgdiplusgraphics_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.SharedGDIplusGraphics = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE sharedoutputpagecount_assign		&& Provides a readwrite copy of the the Engine's OutputPageCount property which the Listener can share with a succession chain.
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.sharedOutputPageCount = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE sharedpageheight_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.sharedPageHeight = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE sharedpageno_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.sharedPageNo = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE sharedpagetotal_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.sharedPageTotal = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE sharedpagewidth_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N"
		   THIS.sharedPageWidth = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE successor_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND ;
		   (ISNULL(m.vNewVal) OR ;
		   (VARTYPE(m.vNewVal) = "O" AND UPPER(m.vNewVal.BaseClass) == "REPORTLISTENER"))
		   THIS.Successor = m.vNewVal
		ENDIF   
		
		
	ENDPROC

	PROCEDURE UnloadReport
		IF NOT THIS.IsSuccessor
		   THIS.SharedPageWidth = THIS.GetPageWidth()
		   THIS.SharedPageHeight = THIS.GetPageHeight()
		ENDIF
		
		THIS.resetDataSession()
		
		IF NOT ISNULL(THIS.Successor)
		
		   WITH THIS.Successor
		      .FRXDataSession = THIS.FRXDataSession
		      .CurrentDataSession = THIS.CurrentDataSession
		      .TwoPassProcess = THIS.TwoPassProcess
		      .CommandClauses = THIS.CommandClauses
		      .SharedPageHeight = THIS.SharedPageHeight
		      .SharedPageWidth = THIS.SharedPageWidth
		      THIS.SetSuccessorDynamicProperties()      
		      .UnloadReport()
		      .IsSuccessor = .F.
		   ENDWITH
		   
		ENDIF
		
		   
	ENDPROC

	PROCEDURE UpdateStatus
		DODEFAULT()
		IF NOT ISNULL(THIS.Successor)
		   THIS.SetSuccessorDynamicProperties()
		   THIS.Successor.UpdateStatus()
		ENDIF   
	ENDPROC

ENDDEFINE

DEFINE CLASS debuglistener AS utilityreportlistener OF "_reportlistener.vcx" 		&& Provides debugging output to help developers understand what happens during an object-assisted report run.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: dodebug		&& Provides debug information for a ReportListener event or method.
		*m: dodebugcommandclauses		&& Provides debug information for the CommandClauses object and ReportListener member properties.
		*m: verbose_assign
		*p: includeloadandunload		&& Indicates whether the debug information should include values from the LoadReport and UnloadReport events.
		*p: targetalias		&& Holds the target alias during the processing of a detail band.
		*p: verbose		&& Specifies whether the DebugListener should include extended information about parameter values of object type, as well as page, alias, and recno() information for each event or method.
	*</DefinedPropArrayMethod>

	PROTECTED targetalias
	*<PropValue>
		FRXDataSession = -1
		Height = 23
		includeloadandunload = .T.
		Name = "debuglistener"
		targetalias = ("")
		Width = 23
		_memberdata = <VFPData>
			<memberdata name="verbose" type="property" display="verbose" favorites="True" />
			<memberdata name="dodebug" type="method" display="doDebug" favorites="True" />
			<memberdata name="dodebugcommandclauses" type="method" display="doDebugCommandClauses" favorites="False" />
			<memberdata name="targetalias" type="property" display="targetAlias" />
			<memberdata name="includeloadandunload" type="property" display="includeLoadAndUnload" favorites="True" />
		</VFPData>
	*</PropValue>
	
	PROCEDURE AddProperty
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		             m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE AdjustObjectSize
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE AfterBand
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT(m.p1, m.p2)
	ENDPROC

	PROCEDURE AfterReport
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		
		LOCAL m.lcProgram
		m.lcProgram = PROGRAM()
		THIS.DoDebug(m.lcProgram, PCOUNT(), ;
		   m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		THIS.DoDebugCommandClauses(THIS.CommandClauses,m.lcProgram+ " current CommandClauses")
		
		IF NOT (THIS.IncludeLoadAndUnload OR THIS.CommandClauses.NoPageEject)
		   THIS.CloseTargetFile()
		   IF NOT THIS.QuietMode
		      MODI COMM (THIS.TargetFileName) NOWAIT
		   ENDIF   
		ENDIF   
		
		DODEFAULT()
		
	ENDPROC

	PROCEDURE BeforeBand
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT(m.p1, m.p2)
		IF THIS.Verbose
		   THIS.TargetAlias = ""
		   IF m.p1 = FRX_OBJCOD_DETAIL
		      THIS.SetFRXDataSession()
		      GO m.p2 IN FRX
		      IF NOT EMPTY(FRX.Expr)
		         THIS.TargetAlias = UPPER(EVALUATE(FRX.Expr))
		      ENDIF
		      THIS.resetDataSession()
		   ENDIF
		ENDIF   
	ENDPROC

	PROCEDURE BeforeReport
		LPARAMETERS m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		
		   LOCAL m.lcProgram
		   m.lcProgram = PROGRAM()
		   
		   DODEFAULT()
		   
		   IF THIS.Verbose
		   
		      THIS.setCurrentDataSession()
		
		   ENDIF
		   
		  
		   IF THIS.TargetHandle = -1 
		      THIS.OpenTargetFile()
		   ENDIF
		
		  THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		       m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		   
		  THIS.DoDebugCommandClauses(THIS.CommandClauses,m.lcProgram+ " received CommandClauses")
		
		
		   IF THIS.verbose
		      THIS.resetDataSession()
		   ENDIF      
		   
		
	ENDPROC

	PROCEDURE CancelReport
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT()
		
	ENDPROC

	PROCEDURE ClearStatus
		LPARAMETERS m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT()
		
	ENDPROC

	PROCEDURE dodebug		&& Provides debug information for a ReportListener event or method.
		LPARAMETERS m.p0, m.pcount, m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		
		IF THIS.TargetHandle > 0 
		
		   LOCAL m.liIndex, m.loObj, m.lvParam, m.liObjIndex, m.liMembers, laMembers[1]
		
		   FWRITE( THIS.TargetHandle, m.p0 )
		   
		   FOR m.liIndex = 1 TO m.pcount
		   
		      m.lvParam = EVAL("p"+TRANS(liIndex))
		   
		      IF THIS.Verbose AND VARTYPE(m.lvParam) = "O"
		
		         m.liMembers = AMEMBERS(laMembers, m.lvParam) && ,0,"G"
		 
		         FOR m.liIndex = 1 TO m.liMembers
		   
		            IF TYPE("lvParam."+laMembers[m.liIndex]) # "U"
		               FWRITE(THIS.TargetHandle, ;
		                  ",Obj."+laMembers[m.liIndex]+"="+TRANSFORM(EVAL("lvParam."+laMembers[m.liIndex])) )
		            ENDIF
		     
		         ENDFOR
		
		      ELSE
		  
		         FWRITE(THIS.TargetHandle, ","+TRANSF(m.lvParam))
		
		      ENDIF
		
		   ENDFOR
		
		   FPUTS(THIS.TargetHandle, "")
		
		   IF THIS.Verbose
		      THIS.setCurrentDataSession()
		   
		      FWRITE(THIS.TargetHandle, ;
		          "Listener.PageNo=" + IIF(THIS.sharedPageNo=0,;
		                                   TRANSFORM(THIS.PageNo),  ;
		                                   TRANSFORM(THIS.sharedPageNo))+ ", " + ;
		          "_PAGENO="+TRANSFORM(_PAGENO))
		
		      IF (NOT EMPTY(THIS.DrivingAlias)) AND USED(THIS.DrivingAlias)
		         FWRITE(THIS.TargetHandle, ", " + THIS.DrivingAlias + " recno=" + TRANSFORM(RECNO(THIS.Drivingalias)))   
		      ENDIF   
		      
		      IF NOT EMPTY(THIS.TargetAlias)
		      
		         FWRITE(THIS.TargetHandle, ", TargetAlias=" + THIS.TargetAlias + ", targetRecno=" + TRANSFORM(RECNO(THIS.TargetAlias)))         
		      
		      ENDIF
		   
		      FPUTS(THIS.TargetHandle, "")   
		      THIS.resetDataSession()
		   ENDIF
		   
		   FFLUSH(THIS.targetHandle)
		   
		ENDIF   
		
		
		
		
	ENDPROC

	PROTECTED PROCEDURE dodebugcommandclauses		&& Provides debug information for the CommandClauses object and ReportListener member properties.
		LPARAMETERS m.tvCommand, m.tcHeader
		
		IF VARTYPE(m.tvCommand) = "O" AND THIS.TargetHandle > 0 
		   LOCAL m.liIndex, laMembers[1], m.liMembers
		
		   FPUTS( THIS.TargetHandle, REPL("-",30) )   
		   FPUTS( THIS.TargetHandle, "MEMBERS:" )
		   m.liMembers = AMEMBERS(laMembers, THIS) && ,0,"G"
		 
		   FOR m.liIndex = 1 TO m.liMembers
		   
		      IF TYPE("THIS."+laMembers[m.liIndex]) # "U"
		  
		         FPUTS(THIS.TargetHandle, "."+laMembers[m.liIndex]+"="+TRANSFORM(EVAL("THIS."+laMembers[m.liIndex])) )
		      ENDIF
		
		   ENDFOR
		
		   FPUTS( THIS.TargetHandle, REPL("-",30) )   
		   FPUTS( THIS.TargetHandle, tcHeader )
		      
		   m.liMembers = AMEMBERS(laMembers, m.tvCommand)   
		   IF m.liMembers = 0
		      FPUTS(THIS.TargetHandle, "... NO MEMBERS")
		   ELSE
		
		      FOR m.liIndex = 1 TO m.liMembers
		  
		        FPUTS(THIS.TargetHandle, "."+laMembers[m.liIndex]+"="+TRANSFORM(EVAL("tvCommand."+laMembers[m.liIndex])) )
		
		      ENDFOR
		     
		   ENDIF   
		   FPUTS( THIS.TargetHandle, REPL("-",30) )      
		   FFLUSH(THIS.targetHandle)
		
		ENDIF
	ENDPROC

	PROCEDURE DoStatus
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT(m.p1)
		
	ENDPROC

	PROCEDURE EvaluateContents
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE IncludePageInOutput
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		RETURN DODEFAULT(m.p1)
		
	ENDPROC

	PROCEDURE LoadReport
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		
		DODEFAULT()
		
		IF THIS.IncludeLoadandUnload 
		
		  IF THIS.TargetHandle = -1
		     THIS.OpenTargetFile()
		  ENDIF
		  
		  THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		      m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		  THIS.DoDebugCommandClauses(THIS.CommandClauses,PROGRAM()+ " received CommandClauses")
		  
		ENDIF  
		
	ENDPROC

	PROCEDURE OnPreviewClose
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE opentargetfile
		IF WEXIST(JUSTSTEM(THIS.targetFileName))
		   * because of the MODI FILE NOWAIT,
		   * generate a new filename:
		   THIS.targetFileName = SYS(2015)
		ENDIF
		DODEFAULT()   
	ENDPROC

	PROCEDURE OutputPage
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
		
	ENDPROC

	PROCEDURE ReadExpression
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE ReadMethod
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE Render
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT( m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8)
		
	ENDPROC

	PROCEDURE ResetToDefault
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE SaveAsClass
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE SupportsListenerType
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		    m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE UnloadReport
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		
		IF THIS.includeLoadAndUnload 
		   LOCAL lcProgram
		   lcProgram = PROGRAM()
		   THIS.DoDebug(lcProgram, PCOUNT(),;
		        m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		   THIS.DoDebugCommandClauses(THIS.CommandClauses,m.lcProgram+ " current CommandClauses")
		ENDIF   
		
		IF THIS.TargetHandle > 0 AND (NOT THIS.CommandClauses.NoPageEject)
		   THIS.CloseTargetFile()
		   IF NOT THIS.QuietMode
		      MODI FILE (THIS.Targetfilename) NOWAIT
		   ENDIF
		ENDIF   
		
		DODEFAULT()
		
	ENDPROC

	PROCEDURE UpdateStatus
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		DODEFAULT()
		
	ENDPROC

	PROCEDURE verbose_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.Verbose = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE WriteExpression
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

	PROCEDURE WriteMethod
		LPARAMETERS  m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12
		THIS.DoDebug(PROGRAM(), PCOUNT(), ;
		     m.p1, m.p2, m.p3, m.p4, m.p5, m.p6, m.p7, m.p8, m.p9, m.p10, m.p11, m.p12)
		
	ENDPROC

ENDDEFINE

DEFINE CLASS htmllistener AS xmldisplaylistener OF "_reportlistener.vcx" 		&& Applies custom specifications, tuned to HTML production, to its parent class' XML generation process.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: getdefaultuserxsltasstring		&& Supplies default User XSLT document o the getDefaultUserXslt method as a string.
	*</DefinedPropArrayMethod>

	*<PropValue>
		applyusertransform = .T.
		FRXDataSession = -1
		Name = "htmllistener"
		targetfileext = ("HTM")
		_memberdata = <VFPData>
			<memberdata name="copyimagefilestoexternalfilelocation" type="property" display="copyImageFilesToExternalFileLocation" favorites="True" />
			<memberdata name="externalfilelocation" type="property" display="externalFileLocation" favorites="True" />
			<memberdata name="imagefieldinstance" type="property" display="imageFieldInstance" favorites="False" />
			<memberdata name="imagefieldtofile" type="property" display="imageFieldToFile" favorites="False" />
			<memberdata name="imagefilebasename" type="property" display="imageFileBaseName" favorites="True" />
			<memberdata name="imagesrcattr" type="property" display="imageSrcAttr" favorites="True" />
			<memberdata name="jpgclsid" type="property" display="JPGclsid" favorites="False" />
			<memberdata name="oldexternalfilelocation" type="property" display="oldExternalFileLocation" favorites="False" />
			<memberdata name="oldsendgdiplusimage" type="property" display="oldSendGDIPlusImage" favorites="False" />
			<memberdata name="checkreportforgeneralfields" type="method" display="checkReportForGeneralFields" favorites="False" />
			<memberdata name="getdefaultuserxsltasstring" type="method" display="getDefaultUserXsltAsString" favorites="False" />
			<memberdata name="initializefilecopysettings" type="method" display="initializeFileCopySettings" favorites="False"/>
		</VFPData>
	*</PropValue>
	
	PROCEDURE getdefaultuserxslt
		LOCAL m.lcResult
		m.lcResult = THIS.getDefaultUserXSLTAsString()
		m.lcResult = STRTRAN(m.lcResult,"@h","@"+ THIS.HeightAttr)
		m.lcResult = STRTRAN(m.lcResult,"@w","@"+ THIS.WidthAttr)
		m.lcResult = STRTRAN(m.lcResult,"@l","@"+ THIS.LeftAttr)
		m.lcResult = STRTRAN(m.lcResult,"@t","@"+ THIS.TopAttr)
		m.lcResult = STRTRAN(m.lcResult,"@c","@"+ THIS.ContAttr)
		m.lcResult = STRTRAN(m.lcResult,"@idref","@"+ THIS.IdRefAttribute)
		m.lcResult = STRTRAN(m.lcResult,"@id","@"+THIS.IdAttribute)
		m.lcResult = STRTRAN(m.lcResult,"@img","@"+THIS.imageSrcAttr )
		THIS.XSLTProcessorUser = m.lcResult
		
		
		
	ENDPROC

	PROTECTED PROCEDURE getdefaultuserxsltasstring		&& Supplies default User XSLT document o the getDefaultUserXslt method as a string.
		LOCAL m.lcResult
		SET TEXTMERGE TO MEMVAR m.lcResult NOSHOW
		SET TEXTMERGE ON 
		TEXT
		<?xml version="1.0"?>
		<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
		   <xsl:output method="html" version="1.0" encoding="UTF-8" indent="no" doctype-public="-//W3C//DTD HTML 4.0//EN" doctype-system="http://www.w3.org/TR/REC-html40/strict.dtd"/>
		   <xsl:param name="externalFileLocation"/>
		   <!--select="'./whatever/'" or 'http://something/myimages/' or "'./'" or... -->
		   <xsl:param name="copyImageFiles" select="0"/>
		   <xsl:param name="generalFieldDPI" select="96"/>
		   <xsl:param name="fillPatternShade" select="180*3"/>
		   <xsl:param name="fillPatternOffset" select="128"/>
		   <xsl:param name="numberPrecision" select="5"/>
		   <xsl:param name="useTextAreaForStretchingText" select="1"/>
		   <xsl:param name="PageTitlePrefix_LOC" select="''"/>
		    
		    <xsl:variable name="FRUs" select="10000"/>
		   <xsl:variable name="printDPI" select="960"/>
		   <xsl:variable name="FRUsInPixelsat96DPI" select="104.167"/>
		   <xsl:variable name="imagePixelRatio" select="$generalFieldDPI div $printDPI"/>
		   <xsl:variable name="zeros" select="substring('0000000000000000000000000',1,$numberPrecision)"/>
		   <xsl:variable name="thisPageHeight">
		      <xsl:value-of select="number(/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXPrintJob/@pageheight  div $printDPI)"/>
		   </xsl:variable>
		   <xsl:variable name="lineNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[type=6]/name"/>
		   <xsl:variable name="labelNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[type=5]/name"/>
		   <xsl:variable name="fieldNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[type=8]/name"/>
		   <xsl:variable name="shapeNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[type=7]/name"/>
		   <xsl:variable name="pictureNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[type=17]/name"/>
		   <xsl:variable name="detailNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=4]/name"/>
		   <xsl:variable name="detailHeaderNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=9]/name"/>
		   <xsl:variable name="detailFooterNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=10]/name"/>
		   <xsl:variable name="pageHeaderNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=1]/name"/>
		   <xsl:variable name="pageFooterNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=7]/name"/>
		   <xsl:variable name="columnHeaderNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=2]/name"/>
		   <xsl:variable name="columnFooterNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=6]/name"/>
		   <xsl:variable name="groupHeaderNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=3]/name"/>
		   <xsl:variable name="groupFooterNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=5]/name"/>
		   <xsl:variable name="titleNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=0]/name"/>
		   <xsl:variable name="summaryNodeName" select="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutNode[code=8]/name"/>
		   <xsl:key name="Layout" match="/Reports/VFP-Report/VFP-RDL/VFPDataSet/VFPFRXLayoutObject[platform='WINDOWS']" use="concat(frxrecno,../../@id)"/>
		
		
		   <xsl:template match="/">
		      <html>
		         <head>
		            <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"/>
		            <!-- necessary because some versions of MSXML xslt processing don't include the charset as required by the XSLT standard when method="html".  Explicitly including the META creates a doubled meta tag, but we do need the encoding to be specified properly. -->
		
		            <xsl:for-each select="/Reports/VFP-Report">
		               <xsl:call-template name="Styles">
		                  <xsl:with-param name="thisReport" select="position()"/>
		                  <xsl:with-param name="thisReportID" select="./VFP-RDL/@id"/>
		               </xsl:call-template>
		            </xsl:for-each>
		            <!--        <xsl:call-template name="Script"/> avoid security problems: no script -->
		            <title><xsl:value-of select="$PageTitlePrefix_LOC"/><xsl:if test="string-length(/Reports/VFP-Report[1]/VFP-RDL/VFPDataSet/VFPFRXPrintJob/@name) = 0">
		                  <xsl:value-of select="/Reports/VFP-Report[1]/VFP-RDL/@id"/>
		               </xsl:if>
		               <xsl:value-of select="/Reports/VFP-Report[1]/VFP-RDL/VFPDataSet/VFPFRXPrintJob/@name"/>
		            </title>
		         </head>
		         <body>
		            <xsl:for-each select="/Reports/VFP-Report">
		               <xsl:variable name="thisReport" select="position()"/>
		               <xsl:variable name="thisReportID" select="./VFP-RDL/@id"/>
		               <xsl:variable name="thisReportRangeFrom" select="number(./VFP-RDL/VFPDataSet/VFPFRXCommand/@RANGEFROM)"/>
		               <xsl:if test="./Data/*[name()=$titleNodeName] and ./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[bandtype='0' and pagebreak='true']">
		                  <xsl:apply-templates select="./Data/*[name()=$titleNodeName]" mode="titlesummarypage">
		                     <xsl:with-param name="thisReport" select="$thisReport"/>
		                     <xsl:with-param name="thisReportID" select="$thisReportID"/>
		                  </xsl:apply-templates>
		               </xsl:if>
		               <xsl:apply-templates select="./Data/*[name()=$pageHeaderNodeName]" mode="page">
		                  <xsl:with-param name="thisReport" select="$thisReport"/>
		                  <xsl:with-param name="thisReportID" select="$thisReportID"/>
		                  <xsl:with-param name="thisReportRangeFrom" select="$thisReportRangeFrom"/>
		               </xsl:apply-templates>
		               <xsl:if test="./Data/*[name()=$summaryNodeName] and ./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[bandtype='8' and pagebreak='true' and ejectbefor='false']">
		                  <xsl:apply-templates select="./Data/*[name()=$summaryNodeName]" mode="titlesummarypage">
		                     <xsl:with-param name="thisReport" select="$thisReport"/>
		                     <xsl:with-param name="thisReportID" select="$thisReportID"/>
		                  </xsl:apply-templates>
		               </xsl:if>
		            </xsl:for-each>
		         </body>
		      </html>
		   </xsl:template>
		   <xsl:template match="/Reports/VFP-Report/Data/*" mode="titlesummarypage">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:param name="thisReportID"/>
		      <xsl:param name="thisReportRangeFrom" select="1"/>
		      <xsl:variable name="thisBand" select="@id"/>
		      <div>
		         <xsl:attribute name="style"> width:100%;top:<xsl:value-of select="$thisPageHeight * (number( ./@idref) -$thisReportRangeFrom)"/>in;page-break-after:always;page-break-inside:avoid; position:absolute; </xsl:attribute>
		         <xsl:apply-templates select="." mode="band">
		            <xsl:with-param name="thisReport" select="$thisReport"/>
		            <xsl:with-param name="thisReportID" select="$thisReportID"/>
		         </xsl:apply-templates>
		         <xsl:if test="/Reports/VFP-Report[$thisReport]/VFP-RDL/VFPDataSet/VFPFRXLayoutObject[frxrecno=$thisBand and ejectafter='true']">
		            <!-- page footer for this summary page -->
		            <xsl:apply-templates select="/Reports/VFP-Report[$thisReport]/Data/*[name()=$pageFooterNodeName][position()=last()]" mode="band">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		               <xsl:with-param name="thisReportID" select="$thisReportID"/>
		            </xsl:apply-templates>
		         </xsl:if>
		      </div>
		   </xsl:template>
		   <xsl:template match="/Reports/VFP-Report/Data/*" mode="page">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:param name="thisReportID"/>
		      <xsl:param name="thisReportRangeFrom" select="1"/>
		      <xsl:variable name="thisPage" select="@id"/>
		      <div>
		         <xsl:attribute name="style"> width:100%;top:<xsl:value-of select="$thisPageHeight * ($thisPage -$thisReportRangeFrom)"/>in;page-break-after:always;page-break-inside:avoid; position:absolute; </xsl:attribute>
		         <xsl:apply-templates select="." mode="band">
		            <xsl:with-param name="thisReport" select="$thisReport"/>
		            <xsl:with-param name="thisReportID" select="$thisReportID"/>
		         </xsl:apply-templates>
		         <xsl:if test="$thisPage = 1 and /Reports/VFP-Report[$thisReport]/Data/*[name()=$titleNodeName] and /Reports/VFP-Report[$thisReport]/VFP-RDL/VFPDataSet/VFPFRXLayoutObject[bandtype='0' and pagebreak='false']">
		            <xsl:apply-templates select="/Reports/VFP-Report[$thisReport]/Data/*[name()=$titleNodeName]" mode="band">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		               <xsl:with-param name="thisReportID" select="$thisReportID"/>
		            </xsl:apply-templates>
		         </xsl:if>
		         <xsl:apply-templates select="/Reports/VFP-Report/Data/*[( (@id=$thisPage and contains(concat('|',$pageFooterNodeName,'|',$columnHeaderNodeName,'|',$columnFooterNodeName,'|'),concat('|',name(),'|'))) or (@idref=$thisPage and contains(concat('|',$detailHeaderNodeName,'|',$detailFooterNodeName,'|',$detailNodeName,'|',$groupHeaderNodeName,'|',$groupFooterNodeName,'|',$summaryNodeName,'|'),concat('|',name(),'|'))) )]" mode="band">
		            <xsl:with-param name="thisReport" select="$thisReport"/>
		            <xsl:with-param name="thisReportID" select="$thisReportID"/>
		         </xsl:apply-templates>
		      </div>
		   </xsl:template>
		   <xsl:template match="/Reports/VFP-Report/Data/*" mode="band">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:param name="thisReportID"/>
		      <xsl:for-each select="./*">
		         <xsl:variable name="thisID" select="translate(@id,'+','')"/>
		         <!--        <xsl:if test="key('Layout',concat($thisID, $thisReportID))/vpos &gt; key('Layout',preceding-sibling::*/concat(@id,$thisReportID))/vpos"><div style="position=absolute;"/></xsl:if>  -->
		         <xsl:call-template name="Render">
		            <xsl:with-param name="thisID" select="$thisID"/>
		            <xsl:with-param name="thisZ" select="position()"/>
		            <xsl:with-param name="thisPage" select="../@idref"/>
		            <xsl:with-param name="thisReport" select="$thisReport"/>
		            <xsl:with-param name="thisReportID" select="$thisReportID"/>
		         </xsl:call-template>
		      </xsl:for-each>
		   </xsl:template>
		   <xsl:template name="Render">
		      <xsl:param name="thisID"/>
		      <xsl:param name="thisZ"/>
		      <xsl:param name="thisPage"/>
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:param name="thisReportID"/>
		      <xsl:choose>
		         <xsl:when test="name()=$lineNodeName and key('Layout',concat($thisID, $thisReportID))/height &lt;  key('Layout',concat($thisID, $thisReportID))/width">
		            <hr>
		               <xsl:attribute name="class"><xsl:value-of select="concat('FRX',$thisReport,'_',$thisID)"/></xsl:attribute>
		               <xsl:attribute name="style">top:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@t  div $printDPI"/></xsl:call-template>in;left:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@l div $printDPI"/></xsl:call-template>in;height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;z-Index:<xsl:value-of select="$thisZ"/>;</xsl:attribute>
		            </hr>
		         </xsl:when>
		         <xsl:when test="name()=$lineNodeName">
		            <!-- vertical line -->
		            <span>
		               <xsl:attribute name="class"><xsl:value-of select="concat('FRX',$thisReport,'_',$thisID)"/></xsl:attribute>
		               <xsl:attribute name="style">top:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@t  div $printDPI"/></xsl:call-template>in;left:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@l div $printDPI"/></xsl:call-template>in;height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;z-Index:<xsl:value-of select="$thisZ"/>;width:0in;</xsl:attribute>
		            </span>
		         </xsl:when>
		         <xsl:when test="$useTextAreaForStretchingText=1  and name()=$fieldNodeName and key('Layout',concat($thisID, $thisReportID))[stretch='true']">
		            <textarea readonly="readonly" rows="0" cols="0">
		               <xsl:attribute name="style">height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;top:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="(@t  div $printDPI) - .1"/></xsl:call-template>in;left:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@l div $printDPI"/></xsl:call-template>in;
		width:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@w div $printDPI"/></xsl:call-template>in;z-Index:<xsl:value-of select="$thisZ"/>;</xsl:attribute>
		               <xsl:attribute name="class"><xsl:value-of select="concat('FRX',$thisReport,'_',$thisID)"/></xsl:attribute>
		               <xsl:value-of select="."/>
		            </textarea>
		         </xsl:when>
		         <xsl:otherwise>
		            <div>
		               <xsl:attribute name="style">position: absolute;z-Index:<xsl:value-of select="$thisZ"/>;top:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@t  div $printDPI"/></xsl:call-template>in;left:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@l div $printDPI"/></xsl:call-template>in;<xsl:choose><xsl:when test="key('Layout',concat($thisID, $thisReportID))[objtype=5 or objtype=8]">width:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@w div $printDPI"/></xsl:call-template>in;height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;</xsl:when><xsl:when test="name()=$pictureNodeName">
		 width:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@w div $printDPI"/></xsl:call-template>in;height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in; 
		</xsl:when><xsl:otherwise>
		height:<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;
		</xsl:otherwise></xsl:choose></xsl:attribute>
		               <xsl:attribute name="class"><xsl:value-of select="concat('FRX',$thisReport,'_',$thisID)"/></xsl:attribute>
		               <xsl:choose>
		                  <xsl:when test="name()=$shapeNodeName or name()=$lineNodeName">
		                     <!-- nothing -->
		                  </xsl:when>
		                  <xsl:when test="name()=$pictureNodeName">
		                     <img alt="{key('Layout',concat($thisID, $thisReportID))/unpathedimg}">
		                        <xsl:variable name="srcImage">
		                           <xsl:choose>
		                              <xsl:when test="@img and $externalFileLocation">
		                                 <xsl:value-of select="translate(concat($externalFileLocation,@img),'\','/')"/>
		                              </xsl:when>
		                              <xsl:when test="@img">
		                                 <xsl:value-of select="concat('file://',translate(@img,'\','/'))"/>
		                              </xsl:when>
		                              <xsl:when test="$copyImageFiles = '1'">
		                                 <xsl:value-of select="translate(concat($externalFileLocation,key('Layout',concat($thisID, $thisReportID))/unpathedimg),'\','/')"/>
		                              </xsl:when>
		                              <xsl:when test="string-length(./text()) &gt; 0">
		                                 <xsl:value-of select="concat('file://',translate(./text(),'\','/'))"/>
		                              </xsl:when>
		                              <xsl:otherwise>
		                                 <xsl:value-of select="concat('file://',translate(key('Layout',concat($thisID, $thisReportID))/pathedimg,'\','/'))"/>
		                              </xsl:otherwise>
		                           </xsl:choose>
		                        </xsl:variable>
		                        <xsl:attribute name="src"><xsl:value-of select="$srcImage"/></xsl:attribute>
		                        <xsl:attribute name="style"><xsl:variable name="imgGeneral" select="key('Layout',concat($thisID, $thisReportID))"/><xsl:choose><xsl:when test="$imgGeneral/general='0' "><!-- clip top, right, bottom, left -->
		 clip: rect(0in,<xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@w div $printDPI"/></xsl:call-template> in,<xsl:value-of select="@h div $printDPI"/>in,0in);
		 </xsl:when><xsl:when test="$imgGeneral/general='1'"><!-- scale and retain --><xsl:choose><xsl:when test="@h &gt; @w">
		 width:100%;
		 </xsl:when><xsl:otherwise>
		 height:100%;
		 </xsl:otherwise></xsl:choose></xsl:when><xsl:otherwise><!-- stretch to fill frame -->
		 height: <xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@h div $printDPI"/></xsl:call-template>in;    
		width: <xsl:call-template name="setPrecision"><xsl:with-param name="theNumber" select="@w div $printDPI"/></xsl:call-template>in;    
		 </xsl:otherwise></xsl:choose></xsl:attribute>
		                     </img>
		                  </xsl:when>
		                  <xsl:when test="string-length(@href) &gt; 0">
		                     <A href="{@href}">
		                     <xsl:call-template name="replaceText"/>
		                     </A>
		                  </xsl:when>
		                  <xsl:otherwise>
		                     <xsl:call-template name="replaceText"/>
		                  </xsl:otherwise>
		               </xsl:choose>
		            </div>
		         </xsl:otherwise>
		      </xsl:choose>
		      <!-- /xsl:if -->
		   </xsl:template>
		   <xsl:template match="VFPFRXLayoutObject" mode="imagestyles">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:value-of select="concat('.FRX',$thisReport,'_',frxrecno)"/>{
		  position: absolute;
		  overflow: hidden;
		  width: <xsl:call-template name="setPrecision">
		         <xsl:with-param name="theNumber" select="width div $FRUs"/>
		      </xsl:call-template>in;
		  height: <xsl:call-template name="setPrecision">
		         <xsl:with-param name="theNumber" select="height div $FRUs"/>
		      </xsl:call-template>in;
		<!-- <xsl:if test="offset=0">
		left: <xsl:value-of select="hpos div $FRUs"/>in; 
		</xsl:if>
		<xsl:if test="offset=2">
		left: <xsl:value-of select="hpos div $FRUs"/>in; 
		</xsl:if> -->
		  }
		 </xsl:template>
		   <xsl:template match="VFPFRXLayoutObject" mode="shapestyles">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:value-of select="concat('.FRX',$thisReport,'_',frxrecno)"/>{
		   position: absolute ;   font-size:1pt;
		    border: <xsl:value-of select="pensize"/>px <xsl:call-template name="pattern"/>
		      <xsl:call-template name="pencolor"/>;
		    <xsl:if test="(mode=0 and not(fillpat=0)) or (mode=1 and fillpat=1)">
		background-color:<xsl:call-template name="fillcolor"/>;
		</xsl:if>
		width: <xsl:call-template name="setPrecision">
		         <xsl:with-param name="theNumber" select="width div $FRUs"/>
		      </xsl:call-template>in;
		left: <xsl:call-template name="setPrecision">
		         <xsl:with-param name="theNumber" select="hpos div $FRUs"/>
		      </xsl:call-template>in;
		<!--    <xsl:if test="stretch='true'">
		overflow: auto;
		   </xsl:if> -->
		      }
		  </xsl:template>
		   <xsl:template match="VFPFRXLayoutObject" mode="textstyles">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:value-of select="concat('.FRX',$thisReport,'_',frxrecno)"/>{
		  <xsl:call-template name="getTextAlignment"/>
		      <!-- tbd, make vertical-align more dynamic -->  
		   vertical-align: top;
		   font-family: <xsl:value-of select="fontface"/>;
		   font-size: <xsl:value-of select="fontsize"/>pt;
		   border: 0px none;
		  padding: 0px;
		  margin: 0px;
		  <xsl:call-template name="getFontAttributes"/>
		   color:<xsl:call-template name="pencolor"/>;
		<xsl:choose>
		         <xsl:when test="mode=1">
		  background-color:transparent;
		  </xsl:when>
		         <xsl:otherwise>
		  background-color: <xsl:call-template name="fillcolor"/>;
		    </xsl:otherwise>
		      </xsl:choose>
		      <xsl:choose>
		         <xsl:when test="stretch='true' and objtype=8 and $useTextAreaForStretchingText=1">
		     overflow: auto;
		     margin-top:4px;
		    </xsl:when>
		         <xsl:otherwise>
		     overflow:hidden;
		    </xsl:otherwise>
		      </xsl:choose>
		   position: absolute;
		   }
		  </xsl:template>
		   <xsl:template match="VFPFRXLayoutObject" mode="linestyles">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:value-of select="concat('.FRX',$thisReport,'_',frxrecno)"/>{
		   position:absolute;font-size:1pt;
		  border: <xsl:value-of select="pensize"/>px <xsl:call-template name="pattern"/>
		      <xsl:call-template name="pencolor"/>;
		   left: <xsl:value-of select="hpos div $FRUs"/>in;
		      <xsl:choose>
		   <xsl:when test="height &lt; width"> width: <xsl:value-of select="width div $FRUs"/>in;
		  height: <xsl:value-of select="floor(height div $FRUsInPixelsat96DPI)"/>px; margin: 0px;</xsl:when>
		<xsl:otherwise>  height: <xsl:value-of select="height div $FRUs"/>in;
		  width: <xsl:value-of select="floor(width div $FRUsInPixelsat96DPI)"/>px;  </xsl:otherwise>
		</xsl:choose>
		   }
		  </xsl:template>
		   <xsl:template name="pattern">
		      <xsl:choose>
		         <xsl:when test="penpat=0"> none </xsl:when>
		         <xsl:when test="penpat=1"> dotted </xsl:when>
		         <xsl:when test="penpat=2"> dashed </xsl:when>
		         <xsl:otherwise> solid </xsl:otherwise>
		      </xsl:choose>
		   </xsl:template>
		   <xsl:template name="pencolor">
		 #<xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="penred"/>
		      </xsl:call-template>
		      <xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="pengreen"/>
		      </xsl:call-template>
		      <xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="penblue"/>
		      </xsl:call-template>
		   </xsl:template>
		   <xsl:template name="fillcolor">
		    #<xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="fillred"/>
		         <xsl:with-param name="fill" select="1"/>
		      </xsl:call-template>
		      <xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="fillgreen"/>
		         <xsl:with-param name="fill" select="1"/>
		      </xsl:call-template>
		      <xsl:call-template name="getHexColorValue">
		         <xsl:with-param name="theNumber" select="fillblue"/>
		         <xsl:with-param name="fill" select="1"/>
		      </xsl:call-template>
		   </xsl:template>
		   <xsl:template name="getFontAttributes">
		      <xsl:param name="theStyles" select="0"/>
		      <xsl:choose>
		         <xsl:when test="fontbold='true'">font-weight: bold;</xsl:when>
		         <xsl:otherwise>font-weight: normal;</xsl:otherwise>
		      </xsl:choose>
		      <xsl:if test="fontstrikethrough='true' or fontunderline='true'">text-decoration: <xsl:if test="fontstrikethrough='true'">line-through </xsl:if>
		         <xsl:if test="fontunderline='true'">underline</xsl:if>;</xsl:if>
		      <xsl:if test="fontitalic='true'">font-style: italic;</xsl:if>
		   </xsl:template>
		   <xsl:template name="getHexColorValue">
		      <xsl:param name="theNumber" select="-1"/>
		      <xsl:param name="fill" select="0"/>
		      <xsl:variable name="useNumber">
		         <xsl:choose>
		            <xsl:when test="$fill=1 and fillpat &gt; 1 and ((fillred+fillblue+fillgreen) &lt; $fillPatternShade)">
		               <xsl:choose>
		                  <xsl:when test="($fillPatternOffset + $theNumber) &gt; 254">255</xsl:when>
		                  <xsl:otherwise>
		                     <xsl:value-of select="$fillPatternOffset + $theNumber"/>
		                  </xsl:otherwise>
		               </xsl:choose>
		            </xsl:when>
		            <xsl:otherwise>
		               <xsl:value-of select="$theNumber"/>
		            </xsl:otherwise>
		         </xsl:choose>
		      </xsl:variable>
		      <xsl:choose>
		         <xsl:when test="$useNumber=-1 and $fill=1">FF</xsl:when>
		         <xsl:when test="$useNumber=-1">00</xsl:when>
		         <xsl:otherwise>
		            <xsl:call-template name="getHexForNumber">
		               <xsl:with-param name="theNumber" select="floor($useNumber div 16)"/>
		            </xsl:call-template>
		            <xsl:call-template name="getHexForNumber">
		               <xsl:with-param name="theNumber" select="round($useNumber mod 16)"/>
		            </xsl:call-template>
		         </xsl:otherwise>
		      </xsl:choose>
		   </xsl:template>
		   <xsl:template name="setPrecision">
		      <xsl:param name="theNumber" select="-1"/>
		      <xsl:choose>
		         <xsl:when test="$numberPrecision = -1 or not(contains(string($theNumber),'.'))">
		            <xsl:value-of select="$theNumber"/>
		         </xsl:when>
		         <xsl:when test="$numberPrecision &gt; 0">
		            <!--        <xsl:value-of select="concat(string(floor($theNumber)),'.',substring(substring-after(string($theNumber),'.'),1,$numberPrecision))"/>  -->
		            <xsl:value-of select="format-number($theNumber,concat('##0.',$zeros))"/>
		         </xsl:when>
		         <xsl:when test="$numberPrecision=0">
		            <xsl:value-of select="round($theNumber)"/>
		         </xsl:when>
		         <xsl:otherwise>
		            <!-- shouldn't happen-->
		            <xsl:value-of select="$theNumber"/>
		         </xsl:otherwise>
		      </xsl:choose>
		   </xsl:template>
		   <xsl:template name="getHexForNumber">
		      <xsl:param name="theNumber" select="-1"/>
		      <xsl:choose>
		         <xsl:when test="$theNumber=-1">00</xsl:when>
		         <xsl:when test="$theNumber &lt; 10">
		            <xsl:value-of select="$theNumber"/>
		         </xsl:when>
		         <xsl:when test="$theNumber = 10">A</xsl:when>
		         <xsl:when test="$theNumber = 11">B</xsl:when>
		         <xsl:when test="$theNumber = 12">C</xsl:when>
		         <xsl:when test="$theNumber = 13">D</xsl:when>
		         <xsl:when test="$theNumber = 14">E</xsl:when>
		         <xsl:when test="$theNumber = 15">F</xsl:when>
		      </xsl:choose>
		   </xsl:template>
		   <xsl:template name="getTextAlignment">
		  text-align:<xsl:choose>
		         <xsl:when test="objtype=5">
		            <!-- picture field empty for left (default), @I for centered and @J right -->
		            <xsl:choose>
		               <xsl:when test="string-length(picture) = 0">left;</xsl:when>
		               <xsl:when test="contains(picture,'@J')">right;</xsl:when>
		               <xsl:otherwise>center;</xsl:otherwise>
		            </xsl:choose>
		         </xsl:when>
		         <xsl:otherwise>
		            <xsl:choose>
		               <xsl:when test="offset=0">left;</xsl:when>
		               <xsl:when test="offset=1">right;</xsl:when>
		               <xsl:otherwise>center;</xsl:otherwise>
		            </xsl:choose>
		         </xsl:otherwise>
		      </xsl:choose>
		      <!-- don't include direction at all if you want context -->
		      <xsl:if test="mode &lt; 4">
		    direction:<xsl:choose>
		            <xsl:when test="mode &gt; 1">rtl;</xsl:when>
		            <xsl:otherwise>ltr;</xsl:otherwise>
		         </xsl:choose>
		      </xsl:if>
		   </xsl:template>
		   <xsl:template name="Styles">
		      <xsl:param name="thisReport" select="1"/>
		      <xsl:param name="thisReportID"/>
		      <xsl:comment>
		
		    Styles for report # <xsl:value-of select="$thisReport"/>  in this run, 
		    <xsl:value-of select="$thisReportID"/>
		      </xsl:comment>
		      <style type="text/css">
		         <xsl:comment>
		            <xsl:apply-templates select="./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[objtype=6]" mode="linestyles">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		            </xsl:apply-templates>
		            <xsl:apply-templates select="./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[objtype=7]" mode="shapestyles">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		            </xsl:apply-templates>
		            <xsl:apply-templates select="./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[contains('|5|8|',concat('|',./objtype,'|'))]" mode="textstyles">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		            </xsl:apply-templates>
		            <xsl:apply-templates select="./VFP-RDL/VFPDataSet/VFPFRXLayoutObject[objtype=17]" mode="imagestyles">
		               <xsl:with-param name="thisReport" select="$thisReport"/>
		            </xsl:apply-templates>
		         </xsl:comment>
		      </style>
		   </xsl:template>
		   <xsl:template name="replaceText">
		   <xsl:choose>
		  <xsl:when test="$useTextAreaForStretchingText=1">
		  <xsl:value-of select="."/>
		  </xsl:when>
		  <xsl:otherwise>
		  <xsl:call-template name="replaceWhiteSpace">
		  <xsl:with-param name="string" select="."/>
		  </xsl:call-template>
		  </xsl:otherwise>
		</xsl:choose>
		   </xsl:template>
		
		   <xsl:template name="replaceWhiteSpace">
		   <xsl:param name="string" select="."/>
		   <xsl:choose>
		  <xsl:when test="contains($string,'&#xD;')">
		  <xsl:call-template name="replaceWhiteSpace">
		   <xsl:with-param name="string" select="substring-before($string, '&#xD;')"/>
		  </xsl:call-template>   
		  <br />
		  <xsl:call-template name="replaceWhiteSpace">
		   <xsl:with-param name="string" select="substring-after($string, '&#xD;')"/>
		  </xsl:call-template>   
		  </xsl:when>
		  <xsl:otherwise>
		  <xsl:value-of select="$string"/>
		  </xsl:otherwise> 
		</xsl:choose>
		   </xsl:template>
		   
		   <xsl:template name="Script">
		      <script language="JavaScript">
		         <xsl:comment>
		     //TBD
		      </xsl:comment>
		      </script>
		   </xsl:template>
		</xsl:stylesheet>
		
		ENDTEXT
		SET TEXTMERGE OFF
		SET TEXTMERGE TO
		
		RETURN m.lcResult
	ENDPROC

	PROCEDURE Init
		IF DODEFAULT()
		   THIS.AppName = OUTPUTHTML_APPNAME_LOC
		ELSE
		   RETURN .F.   
		ENDIF
		
		RETURN NOT THIS.HadError
	ENDPROC

ENDDEFINE

DEFINE CLASS updatelistener AS _reportlistener OF "_reportlistener.vcx" 		&& Provides user feedback while report output is generated.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: createtherm		&& Creates and configures the "update" feedback window.
		*m: getparentwindowref		&& Provides a window reference for the top form in which the user feedback window should appear.
		*m: getreportscopedriver		&& Adjusts the alias driving CommandClauses.RecordTotal at the beginning of a report  when the DrivingAlias is engaged in one-to-many relationships.
		*m: includeseconds_assign
		*m: initstatustext_assign
		*m: prepassstatustext_assign
		*m: resetuserfeedback		&& Sets user feedback to an initialized state.
		*m: runstatustext_assign
		*m: secondstext_assign
		*m: setthermformcaption		&& Sets the user feedback window title using the ThermFormCaption property.
		*m: synchstatus		&& Compares driving recno with currrently-saved information to evaluate need to update user feedback.
		*m: thermcaption_assign
		*m: thermformcaption_assign
		*m: thermformheight_assign
		*m: thermformwidth_assign
		*m: thermmargin_assign
		*p: currentrecord		&& Holds the current record relative to the recordtotal in scope for the current report run.
		*p: designateddriver		&& Original selected alias for the report.
		*p: drivingaliascurrentrecno		&& Holds the RECNO() value in the cursor driving the report run, to assist in determining when to trigger a change in the user feedback.
		*p: escapereference		&& Holds the name of a public variable used to facilitate interrupting a report run.
		*p: frxbandrecno		&& Holds the RECNO() of the band-describing record in the FRX table this class has determined is optimal for triggering a change in user feedback during a report run.
		*p: includeseconds		&& Indicates whether the default user feedback message should include timing data.
		*p: initstatustext		&& Provides the user message shown when user feedback first appears.
		*p: onescapecommand		&& Saves the user's previous ON ESCAPE command, if any, for restoration after the report run.
		*p: percentdone		&& Calculation of the ratio between the number of records, or pages, already generated to the number of records, or pages, in the total report.
		*p: prepassstatustext		&& User feedback message for use when the report is in a pre-generation pass to calculate _RECORDTOTAL.
		*p: reportstartrundatetime		&& A datetime value indicating when the last report generation run began.
		*p: reportstoprundatetime		&& A datetime value for use at the conclusion of a report run, empty during a report, storing when the last report generation run ended.
		*p: runstatustext		&& Provides a user message shown during the course of a report run.
		*p: secondstext		&& Provides the text message included to describe the time value in the default user feedback message during a report, when IncludeSeconds is .T.
		*p: setescape		&& Saves the state of SET ESCAPE previous to the report run, for later restoration.
		*p: setnotifycursor		&& Saves the state of SET NOTIFY CURSOR previous to the report run, for later restoration.
		*p: successorsys2024		&& Allows UpdateListener to "remember" if it has cancelled a report between the two report passes if it is in a two-pass process report, if it is a Successor.
		*p: thermcaption		&& Holds an evaluated expression for use in the user feedback message shown during a report run. If this expression includes "cMessage", the contents of the argument provided to DoStatus will be included in the result of the evaluation.
		*p: thermform		&& Holds an object reference to the user feedback form.
		*p: thermformcaption		&& Holds the value used to set the title of the user feedback form.
		*p: thermformheight		&& Holds the height of the user feedback form, in pixels.
		*p: thermformwidth		&& Holds the width of the user feedback form, in pixels.
		*p: thermmargin		&& Holds the value (in pixels) used to determine the difference between the size of the user feedback window and the thermometer bar it displays.
	*</DefinedPropArrayMethod>

	PROTECTED currentrecord,designateddriver,drivingaliascurrentrecno,escapereference,frxbandrecno,onescapecommand,percentdone,setescape,setnotifycursor,successorsys2024,thermform
	*<PropValue>
		currentrecord = (0)
		designateddriver = ("")
		drivingaliascurrentrecno = (0)
		escapereference = ("")
		frxbandrecno = (0)
		FRXDataSession = -1
		includeseconds = .T.
		initstatustext = ("")
		Name = "updatelistener"
		onescapecommand = ("")
		percentdone = (0)
		prepassstatustext = ("")
		reportstartrundatetime = (DTOT({}))
		reportstoprundatetime = (DTOT({}))
		runstatustext = ("")
		secondstext = ("secs")
		successorsys2024 = ("N")
		thermcaption = ([cMessage+ " "+ TRANSFORM(INT(THIS.PercentDone*100)) + "%" + IIF(NOT THIS.IncludeSeconds, "" , " "+TRANSFORM(IIF(THIS.IsRunning,DATETIME(), THIS.ReportStopRunDateTime)-THIS.ReportStartRunDateTime)+" " + THIS.SecondsText)])
		thermform = .NULL.
		thermformcaption = ("")
		thermformheight = 40
		thermformwidth = 356
		thermmargin = 5
		_memberdata = <VFPData>
			<memberdata name="percentdone" type="property" display="percentDone" favorites="False" />
			<memberdata name="createtherm" type="method" display="createTherm" favorites="False" />
			<memberdata name="currentrecord" type="property" display="currentRecord" favorites="False" />
			<memberdata name="designateddriver" type="property" display="designatedDriver" favorites="False" />
			<memberdata name="drivingaliascurrentrecno" type="property" display="drivingAliasCurrentRecno" favorites="False" />
			<memberdata name="escapereference" type="property" display="escapeReference" favorites="False" />
			<memberdata name="frxbandrecno" type="property" display="frxBandRecno" favorites="False" />
			<memberdata name="getparentwindowref" type="method" display="getParentWindowRef" favorites="False" />
			<memberdata name="getreportscopedriver" type="method" display="getReportScopeDriver" favorites="False"/>
			<memberdata name="includeseconds" type="property" display="includeSeconds" favorites="True" />
			<memberdata name="initstatustext" type="property" display="initStatusText" favorites="True" />
			<memberdata name="onescapecommand" type="property" display="onEscapeCommand" favorites="False" />
			<memberdata name="prepassstatustext" type="property" display="prepassStatusText" favorites="True" />
			<memberdata name="reportstartrundatetime" type="property" display="reportStartRunDatetime" favorites="True" />
			<memberdata name="reportstoprundatetime" type="property" display="reportStopRunDatetime" favorites="True" />
			<memberdata name="resetuserfeedback" type="method" display="resetUserFeedback" favorites="False" />
			<memberdata name="runstatustext" type="property" display="runStatusText" favorites="True" />
			<memberdata name="secondstext" type="property" display="secondsText" favorites="True" />
			<memberdata name="setescape" type="property" display="setEscape" favorites="False" />
			<memberdata name="setnotifycursor" type="property" display="setNotifyCursor" favorites="False" />
			<memberdata name="setthermformcaption" type="method" display="setThermformCaption" favorites="False" />
			<memberdata name="thermcaption" type="property" display="thermCaption" favorites="True" />
			<memberdata name="thermform" type="property" display="thermForm" favorites="False" />
			<memberdata name="thermformcaption" type="property" display="thermFormCaption" favorites="True" />
			<memberdata name="thermformheight" type="property" display="thermFormHeight" favorites="True" />
			<memberdata name="thermformwidth" type="property" display="thermFormWidth" favorites="True" />
			<memberdata name="thermmargin" type="property" display="thermMargin" favorites="True" />
			<memberdata name="synchstatus" type="method" display="synchStatus" favorites="False" />
			<memberdata name="successorsys2024" type="property" display="successorSys2024" favorites="False" />
		</VFPData>
	*</PropValue>
	
	PROCEDURE AfterBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo
		DODEFAULT(m.nBandObjCode, m.nFRXRecNo)
		THIS.synchStatus(m.nBandObjCode,m.nFRXRecNo)
		THIS.resetDataSession()
		
	ENDPROC

	PROCEDURE AfterReport
		IF SYS(2024) # "Y" 
		   IF THIS.IsRunning OR TYPE("THIS.CommandClauses.RecordTotal") = "N"
		      THIS.CurrentRecord = THIS.CommandClauses.RecordTotal
		   ENDIF   
		   THIS.UpdateStatus() 
		ENDIF
		THIS.IsRunning = .F.
		THIS.ClearStatus() 
		THIS.designatedDriver = ""
		THIS.successorSys2024 = "N"
		THIS.ThermForm = NULL  
		THIS.ReportStopRunDateTime = DATETIME()
		THIS.PopGlobalSets()
		DODEFAULT()
		
		
		
	ENDPROC

	PROCEDURE BeforeBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo
		DODEFAULT(m.nBandObjCode, m.nFRXRecNo)
		IF THIS.successorSys2024 = "Y" AND ;
		   THIS.CurrentPass = LISTENER_FULLPASS
		   * user cancelled during the prepass,
		   * we need to re-cancel.
		   THIS.setCurrentDataSession()
		   IF USED(THIS.designatedDriver)
		      GO BOTTOM IN (THIS.designatedDriver)
		   ENDIF   
		ENDIF   
		
		THIS.synchStatus(m.nBandObjCode,m.nFRXRecNo)
		THIS.resetDataSession()
	ENDPROC

	PROCEDURE BeforeReport
		DODEFAULT()
		* THIS.ResetUserFeedback(.T.)
		THIS.DrivingAliasCurrentRecno = 0
		THIS.IsRunning = .T.
		THIS.resetDataSession()
		
		
		
	ENDPROC

	PROCEDURE CancelReport
		IF THIS.IsRunning AND ;
		   (THIS.QuietMode OR ;
		   (THIS.pageLimit > 0 AND THIS.PageNo > THIS.pageLimit) OR ;
		    (NOT THIS.AllowModalMessages) OR ;
		    THIS.DoMessage(OUTPUTCLASS_REPORT_CANCELQUERY_LOC,;
		                   MB_ICONQUESTION+MB_YESNO) =  IDYES )
		
		   IF THIS.isSuccessor AND NOT EMPTY(THIS.designatedDriver)
		      * make an exception for this Listener
		      * to the rule that Successors don't 
		      * handle cancelling the report, because
		      * this guy's job is to handle user intervention:
		      THIS.successorSys2024 = "Y"
		      THIS.setCurrentDataSession()
		      IF USED(THIS.designatedDriver)
		         GO BOTTOM IN (THIS.designatedDriver)
		      ENDIF   
		      THIS.resetDataSession()
		   ENDIF
		      
		   DODEFAULT() 
		   IF SYS(2024) = "Y"  OR THIS.IsSuccessor
		      THIS.ThermForm = NULL
		      IF (THIS.pageLimit = -1 OR THIS.PageNo <= THIS.pageLimit)
		         THIS.DoMessage(OUTPUTCLASS_REPORT_INCOMPLETE_LOC, ;
		                        MB_ICONEXCLAMATION)
		      ENDIF                        
		   ENDIF
		ELSE
		   NODEFAULT   
		ENDIF
		
	ENDPROC

	PROCEDURE ClearStatus
		NODEFAULT
		IF NOT ISNULL(THIS.ThermForm) 
		   IF THIS.ThermForm.Visible
		      THIS.ThermForm.Visible = .F.
		   ENDIF
		ENDIF
		
		IF NOT ISNULL(THIS.Successor)
		   THIS.Successor.ClearStatus()
		ENDIF
	ENDPROC

	PROTECTED PROCEDURE createtherm		&& Creates and configures the "update" feedback window.
		#define CTLCOLOR_MSGBOX             0
		#define CTLCOLOR_EDIT               1
		#define CTLCOLOR_LISTBOX            2
		#define CTLCOLOR_BTN                3
		#define CTLCOLOR_DLG                4
		#define CTLCOLOR_SCROLLBAR          5
		#define CTLCOLOR_STATIC             6
		#define CTLCOLOR_MAX                7
		#define COLOR_SCROLLBAR             0
		#define COLOR_BACKGROUND            1
		#define COLOR_ACTIVECAPTION         2
		#define COLOR_INACTIVECAPTION       3
		#define COLOR_MENU                  4
		#define COLOR_WINDOW                5
		#define COLOR_WINDOWFRAME           6
		#define COLOR_MENUTEXT              7
		#define COLOR_WINDOWTEXT            8
		#define COLOR_CAPTIONTEXT           9
		#define COLOR_ACTIVEBORDER         10
		#define COLOR_INACTIVEBORDER       11
		#define COLOR_APPWORKSPACE         12
		#define COLOR_HIGHLIGHT            13
		#define COLOR_HIGHLIGHTTEXT        14
		#define COLOR_BTNFACE              15
		#define COLOR_BTNSHADOW            16
		#define COLOR_GRAYTEXT             17
		#define COLOR_BTNTEXT              18
		#define COLOR_INACTIVECAPTIONTEXT  19
		#define COLOR_BTNHIGHLIGHT         20
		#if("4" $ OS())
		#define COLOR_3DDKSHADOW           21
		#define COLOR_3DLIGHT              22
		#define COLOR_INFOTEXT             23
		#define COLOR_INFOBK               24
		#define COLOR_DESKTOP           COLOR_BACKGROUND
		#define COLOR_3DFACE            COLOR_BTNFACE
		#define COLOR_3DSHADOW          COLOR_BTNSHADOW
		#define COLOR_3DHIGHLIGHT       COLOR_BTNHIGHLIGHT
		#define COLOR_3DHILIGHT         COLOR_BTNHIGHLIGHT
		#define COLOR_BTNHILIGHT        COLOR_BTNHIGHLIGHT
		#endif
		
		IF ISNULL(THIS.ThermForm)
		
		  DECLARE INTEGER GetSysColor IN Win32API INTEGER  
		
		  LOCAL m.liThermTop, m.liThermLeft, m.liThermWidth, m.liThermHeight, m.liSession
		  IF TYPE("THIS.CommandClauses.StartDataSession") = "N"
		     m.liSession = SET("DATASESSION")
		     TRY
		        SET DATASESSION TO (THIS.CommandClauses.StartDataSession)
		     CATCH WHEN .T.
		        THIS.resetDataSession()
		     ENDTRY
		  ENDIF
		
		  THIS.ThermForm = CREATEOBJECT("FORM")
		  
		  
		  WITH THIS.ThermForm
		  
		     .ScaleMode = SCALEMODE_PIXELS   
		     .Height = THIS.ThermFormHeight
		     .HalfHeightCaption = .T.
		     .Width = THIS.ThermFormWidth
		     .AutoCenter = .T.
		     .BorderStyle = BORDER_DOUBLE  && fixed dialog
		     .ControlBox = .F.
		     .Closable = (NOT THIS.IsRunning)
		     .MaxButton = .F.
		     .MinButton = .F.
		     .Movable = (NOT THIS.IsRunning)
		     .AlwaysOnTop = .T.
		     .AllowOutput = .F.
		     .AddObject("ThermBack","shape")
		     .AddObject("ThermLabel","label")
		     .AddObject("ThermShape","shape")
		     m.liThermHeight = .Height - (THIS.ThermMargin* 2)
		     m.liThermWidth =  .Width - (THIS.ThermMargin*2)
		  ENDWITH
		  
		  THIS.SetThermFormCaption()    
		  
		  m.liThermTop = THIS.ThermMargin
		  m.liThermLeft = THIS.ThermMargin  
		
		  WITH THIS.ThermForm.ThermBack
		     .Top = m.liThermTop     
		     .Left = m.liThermLeft
		     .Height = m.liThermHeight
		     .Width = m.liThermWidth
		     .Visible = .T.
		     .BorderStyle = BORDER_SINGLE
		     .BackStyle = 0     
		  ENDWITH
		  
		  WITH THIS.ThermForm.ThermLabel
		     .Top = (.Parent.Height - .Height) /2
		     .Autosize = .T.
		     .BackStyle = FILLSTYLE_SOLID      
		     .Caption = ""
		     .Visible = .T.
		     .ForeColor = GetSysColor( COLOR_MENUTEXT )
		  ENDWITH
		  
		  WITH THIS.ThermForm.ThermShape
		     .Top = m.liThermTop +1    
		     .Left = m.liThermLeft+1
		     .Height = m.liThermHeight -2
		     .Width = 0
		     .Visible = .T.
		     .BorderStyle = BORDER_NONE
		     .BackStyle = FILLSTYLE_SOLID         
		     .FillStyle = FILLSTYLE_SOLID    
		     .BackColor = .Parent.BackColor
		     .FillColor = GetSysColor(COLOR_HIGHLIGHT)
		     .DrawMode = DRAWMODE_MERGE_PEN_NOT 
		  ENDWITH
		  
		  IF NOT EMPTY(m.liSession)
		     SET DATASESSION TO (m.liSession)
		  ENDIF
		  
		 
		ENDIF
		
		RETURN NOT ISNULL(THIS.ThermForm)
	ENDPROC

	PROCEDURE Destroy
		STORE NULL TO THIS.thermForm 
		
		DODEFAULT()
	ENDPROC

	PROCEDURE DoStatus
		LPARAMETERS m.cMessage
		LOCAL m.loParentForm, m.lcCaption, m.lcParentFormName
		NODEFAULT
		
		IF (NOT (THIS.QuietMode or ;
		   (THIS.IsRunning AND THIS.CommandClauses.NoDialog)))
		
		   IF EMPTY(m.cMessage) OR ISNULL(m.cMessage)
		      m.cMessage = ""
		   ENDIF
		
		   m.lcCaption = EVALUATE(THIS.ThermCaption)
		   IF ((NOT ISNULL(THIS.ThermForm)) OR (THIS.CreateTherm()) )
		
		      WITH THIS.ThermForm
		      
		         IF THIS.IsRunning
		            .Closable = .F.
		            .Movable = .F.
		         ENDIF
		      
		        .ThermShape.Width = MAX( (((THIS.PercentDone* .ThermBack.Width))-2) ,0)
		        .ThermLabel.Caption = m.lcCaption
		        .ThermLabel.Left = (.Width - .ThermLabel.Width) /2
		      
		        IF NOT .Visible
		        
		           m.loParentForm = THIS.GetParentWindowRef()
		           
		           DO CASE
		           CASE VARTYPE(m.loParentForm) # "O" AND (NOT _SCREEN.Visible)
		              m.lcParentFormName = "MACDESKTOP"
		           CASE VARTYPE(m.loParentForm) # "O"
		              m.lcParentFormName = "SCREEN"              
		           CASE (NOT m.loParentForm.Visible) AND ;
		              (m.loParentForm.DeskTop OR NOT EMPTY(m.loParentForm.MacDesktop) OR ;
		              m.loParentForm.ShowWindow = 2 OR (NOT _SCREEN.Visible))
		              * in many cases, 
		              * they've probably made a programming error,
		              * the parent should be visible according to
		              * the requirements of REPORT FORM ... IN WINDOW
		              * if it's a WINDOW clause they
		              * have no need to show it, might not be an error
		              * Either way, they should see the therm
		              * to know that the report is progressing                
		              m.lcParentFormName = "MACDESKTOP"
		           CASE (NOT m.loParentForm.Visible) 
		              * same comment as above
		              m.lcParentFormName = "SCREEN"
		           OTHERWISE
		              m.lcParentFormName = m.loParentForm.Name
		           ENDCASE
		           
		           SHOW WINDOW (.Name) IN WINDOW (m.lcParentFormName) 
		           .AlwaysOnTop = .T.
		           .AutoCenter = .T.
		           .Visible = .T.
		        
		        ENDIF
		      
		      ENDWITH
		   
		   ENDIF
		  
		ENDIF   
		
		
		
	ENDPROC

	PROCEDURE getfrxstartupinfo
		DODEFAULT()
		
		LOCAL m.llFRXAvailable
		
		m.llFRXAvailable = THIS.getReportScopeDriver() 
		
		IF m.llFRXAvailable
		   THIS.SetFRXDataSession()
		   THIS.FRXBandRecno = 0
		
		   SELECT FRX
		   
		   IF THIS.CommandClauses.Summary
		      * don't use groups unless
		      * we're forced to by Summary.
		      * Group usage will not work if
		      * there's a group on .T. or some
		      * other nonsensical expression that
		      * doesn't change.
		    
		      LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		          Objcode = FRX_OBJCOD_GROUPHEADER
		      DO WHILE NOT EOF()
		         * find the innermost group
		         THIS.FRXBandRecno = RECNO()
		         CONTINUE
		      ENDDO        
		      
		      IF THIS.frxBandRecno = 0
		         * no groups in a Summary report
		         * doesn't make a lot of sense, but
		         * can happen.
		          LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		             Objcode = FRX_OBJCOD_PAGEHEADER
		          IF NOT EOF()
		             THIS.FRXBandRecno = RECNO()
		          ENDIF     
		      ENDIF
		   
		   ENDIF
		      
		   IF THIS.FRXBandRecno = 0
		      * not a Summary report.
		      * look for the appropriate detail
		      * using the report driver
		      LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		                 Objcode = FRX_OBJCOD_DETAIL AND ;
		                 TYPE(Expr) = "C" AND ;
		                 UPPER(EVALUATE(ALLTRIM(Expr))) == UPPER(THIS.DrivingAlias)
		      DO WHILE NOT EOF()
		         THIS.FRXBandRecno = RECNO()
		         CONTINUE && try not to use the first detail band
		      ENDDO
		   
		   ENDIF   
		   
		   IF THIS.frxBandRecno = 0
		      * couldn't match up a band with
		      * the known driver
		      LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		           Objcode = FRX_OBJCOD_DETAIL AND ;
		           EMPTY(Expr)
		      IF NOT EOF()
		         THIS.FRXBandRecno = RECNO()      
		      ELSE
		         IF THIS.FRXBandRecno = 0 
		            LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		                 Objcode = FRX_OBJCOD_DETAIL
		            IF NOT EOF()
		               THIS.FRXBandRecno = RECNO()
		            ENDIF  
		         ENDIF               
		      ENDIF        
		   ENDIF   
		
		ENDIF
		
		THIS.setCurrentDataSession()
		
	ENDPROC

	PROTECTED PROCEDURE getparentwindowref		&& Provides a window reference for the top form in which the user feedback window should appear.
		LOCAL m.loForm, m.loTopForm, m.lcInWindow
		
		* first top form in the list
		* will be the current top form.
		
		ASSERT TYPE("_SCREEN.ActiveForm") # "O"  OR ;
		       INLIST(_SCREEN.ActiveForm.ShowWindow, 0,1,2)
		
		m.loTopForm = NULL
		
		IF TYPE("THIS.CommandClauses.InWindow") = "C"
		   m.lcInWindow = UPPER(ALLTRIM(THIS.CommandClauses.InWindow))
		ENDIF   
		
		IF EMPTY(lcInWindow) AND TYPE("THIS.CommandClauses.Window") = "C"
		   m.lcInWindow = UPPER(ALLTRIM(THIS.CommandClauses.Window))
		ENDIF   
		
		
		IF NOT EMPTY(m.lcInWindow) 
		
		     FOR EACH m.loForm IN _SCREEN.Forms  FOXOBJECT
		        IF m.loForm.ShowWindow = 2  AND ;
		           UPPER(m.loForm.Name) == m.lcInWindow
		           m.loTopForm = m.loForm
		           EXIT
		        ENDIF
		     ENDFOR
		     
		ENDIF
		
		DO CASE
		
		CASE VARTYPE(m.loTopForm) = "O"
		    * already found
		CASE _SCREEN.FormCount = 0 OR ;
		     (TYPE("_SCREEN.ActiveForm") = "O" AND ;
		     _SCREEN.ActiveForm.ShowWindow = 0 )     && ShowWindow In Screen
				     
		     m.loTopForm = _SCREEN
		
		CASE (TYPE("_SCREEN.ActiveForm") = "O" AND ;
		      _SCREEN.ActiveForm.ShowWindow = 2 )    && ShowWindow As Top Form
		
		     m.loTopForm = _SCREEN.ActiveForm
				     
		OTHERWISE 
				                                       
		     FOR EACH m.loForm IN _SCREEN.Forms  FOXOBJECT
		        IF m.loForm.ShowWindow = 2 
		           m.loTopForm = m.loForm
		           EXIT
		        ENDIF
		     ENDFOR
				     
		     IF VARTYPE(m.loTopForm) # "O"
		        m.loTopForm = _SCREEN
		     ENDIF
				          
		ENDCASE
		
		IF VARTYPE(m.loTopForm) # "O" OR ;
		   EMPTY(m.loTopForm.Name)
		   m.loTopForm = NULL
		ENDIF
		
		RETURN m.loTopForm     
		
	ENDPROC

	PROTECTED PROCEDURE getreportscopedriver		&& Adjusts the alias driving CommandClauses.RecordTotal at the beginning of a report  when the DrivingAlias is engaged in one-to-many relationships.
		LOCAL m.liSelect, m.lcAlias, ;
		      m.liSkips,  laSkips[1]
		
		THIS.designatedDriver = THIS.drivingAlias
		* used later if we have to cancel report as
		* a Successor
		
		THIS.setFRXDataSession()
		
		IF USED("frx")
		
		   m.liSelect = SELECT()
		
		   SELECT FRX
		   LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		           Objcode = FRX_OBJCOD_DETAIL AND ;
		           TYPE(Expr) = "C" AND ;
		           UPPER(EVALUATE(ALLTRIM(Expr))) == UPPER(THIS.DrivingAlias)
		   IF FOUND()
		      SELECT (m.liSelect)
		      * if the driver is also a target alias,
		      * don't touch.
		     * otherwise:
		   ELSE 
		
		      LOCATE FOR ObjType = FRX_OBJTYP_BAND AND ;
		              Objcode = FRX_OBJCOD_DETAIL AND ;
		              NOT EMPTY(Expr)
		      IF FOUND()
		         * use the first detail band, on the theory
		         * that people are going to put pre-processing 
		         * calculations before other bands, 
		         * so an early band has the best chance to be
		         * the right driver.
		         THIS.drivingAlias = UPPER(EVALUATE(ALLTRIM(Expr)))
		
		         SELECT (m.liSelect)
		
		      ELSE   
		   
		         * adjust the driver based on any
		         * one to many relationships we can find.
		  
		         SELECT (m.liSelect)
		   
		         THIS.setCurrentDataSession()
		
		         m.lcAlias = THIS.drivingAlias
		
		         m.liSelect = SELECT()
		
		         DO WHILE NOT EMPTY(m.lcAlias)
		
		            SELECT (m.lcAlias)
		            m.liSkips = ALINES(laSkips,SET("SKIP"),",")
		            IF m.liSkips = 0 OR EMPTY(laSkips[1])
		               THIS.drivingAlias = m.lcAlias
		               m.lcAlias = ""
		            ELSE
		               m.lcAlias = laSkips[1]
		               * it doesn't really matter how many lines there
		               * are in the array; this is not going to be perfect
		               * but we can't predict which child 
		               * has the most records.
		            ENDIF
		         ENDDO
		
		         SELECT (m.liSelect)
		   
		      ENDIF   
		    
		   ENDIF  
		ELSE
		   RETURN .F.    
		ENDIF
	ENDPROC

	PROCEDURE includeseconds_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.includeseconds = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE Init
		IF DODEFAULT() 
		   THIS.InitStatusText = OUTPUTCLASS_INITSTATUS_LOC
		   THIS.PrepassStatusText = OUTPUTCLASS_PREPSTATUS_LOC
		   THIS.RunStatusText =  OUTPUTCLASS_RUNSTATUS_LOC
		   THIS.SecondsText = OUTPUTCLASS_TIME_SECONDS_LOC
		   THIS.thermCaption = OUTPUTCLASS_THERMCAPTION_LOC 
		   RETURN (NOT THIS.HadError)
		ELSE
		   RETURN .F.
		ENDIF
		   
	ENDPROC

	PROCEDURE initstatustext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.InitStatusText = m.vNewVal
		ENDIF   
	ENDPROC

	PROCEDURE LoadReport
		IF DODEFAULT()
		   THIS.ResetUserFeedback(.T.)
		   IF NOT (THIS.QuietMode OR ;
		           (TYPE("THIS.CommandClauses.NoDialog") = "L" AND ;
		           THIS.CommandClauses.NoDialog) )
		      THIS.DoStatus(THIS.initStatusText) 
		      * NB: a user can call LoadReport manually,
		      * hence the need for a TYPE() check here.
		   ENDIF   
		   THIS.PushGlobalSets()
		ELSE
		   THIS.ClearStatus()
		   RETURN .F.
		ENDIF
	ENDPROC

	PROCEDURE popglobalsets
		DODEFAULT()
		IF (NOT INLIST(_VFP.StartMode,2,3,5))
		   LOCAL m.lcRef
		   m.lcRef = THIS.EscapeReference
		   IF (NOT EMPTY(m.lcRef)) AND ;
		       TYPE(m.lcRef) = "O"
		      * push occurred earlier
		      STORE NULL TO (m.lcRef)
		      RELEASE &lcRef.
		      THIS.escapeReference = ""
		      m.lcRef = THIS.OnEscapeCommand
		      ON ESCAPE &lcRef
		      POP KEY
		      IF THIS.SetNotifyCursor
		         SET NOTIFY CURSOR ON
		      ENDIF   
		      IF THIS.SetEscape 
		         SET ESCAPE OFF
		      ENDIF   
		   ENDIF   
		ENDIF   
		   
		   
	ENDPROC

	PROCEDURE prepassstatustext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.PrepassStatusText = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE pushglobalsets
		DODEFAULT()
		IF (NOT INLIST(_VFP.StartMode,2,3,5))
		   PUSH KEY CLEAR
		   LOCAL m.lcRef
		   SET MESSAGE TO ""
		   THIS.SetNotifyCursor = (SET("Notify",2) = "ON")
		   IF THIS.SetNotifyCursor
		      SET NOTIFY CURSOR OFF
		   ENDIF   
		   THIS.OnEscapeCommand = ON("ESCAPE")   
		   m.lcRef = SYS(2015)   
		   PUBLIC &lcRef.   
		   STORE THIS TO (m.lcRef)
		   ON ESCAPE &lcRef..CancelReport()      
		   THIS.EscapeReference = m.lcRef   
		   THIS.SetEscape = (SET("ESCAPE")="OFF") 
		   IF THIS.SetEscape
		      SET ESCAPE ON
		   ENDIF   
		ENDIF   
		   
		   
	ENDPROC

	PROTECTED PROCEDURE resetuserfeedback		&& Sets user feedback to an initialized state.
		LPARAMETERS m.tlResetTimes
		THIS.CurrentRecord = 0
		THIS.PercentDone = 0
		IF m.tlResetTimes
		   THIS.ReportStartRunDateTime= DATETIME()
		   THIS.ReportStopRunDateTime= DTOT({})
		ENDIF   
		
	ENDPROC

	PROCEDURE runstatustext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.RunStatusText = m.vNewVal
		ENDIF   
		
		
	ENDPROC

	PROCEDURE secondstext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.SecondsText = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE setfrxrunstartupconditions
		DODEFAULT()
		IF TYPE("THIS.CommandClauses.Summary") # "L"
		   ADDPROPERTY(THIS.CommandClauses,"Summary",.F.)
		ENDIF   
		IF TYPE("THIS.CommandClauses.RecordTotal") # "N"
		   ADDPROPERTY(THIS.CommandClauses,"RecordTotal",0)
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE setthermformcaption		&& Sets the user feedback window title using the ThermFormCaption property.
		IF NOT ISNULL(THIS.ThermForm)
		   IF EMPTY(THIS.ThermFormCaption)
		      
		      IF TYPE("THIS.CommandClauses.File") = "C"
		         LOCAL m.cName
		         IF EMPTY(THIS.PrintJobName)
		            m.cName = PROPER(JUSTFNAME(THIS.CommandClauses.File))
		         ELSE
		            m.cName = THIS.PrintJobName
		         ENDIF   
		         THIS.ThermForm.Caption = ;
		            m.cName + ": " + OUTPUTCLASS_CANCEL_INSTRUCTIONS_LOC
		      ELSE
		         THIS.ThermForm.Caption = ""
		      ENDIF
		   ELSE   
		      THIS.ThermForm.Caption = THIS.ThermFormCaption
		   ENDIF
		ENDIF   
		
		
		
		
		
	ENDPROC

	PROTECTED PROCEDURE synchstatus		&& Compares driving recno with currrently-saved information to evaluate need to update user feedback.
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo
		IF THIS.isRunning AND (NOT THIS.hadError) AND ;
		   THIS.frxBandRecno = m.nFRXRecNo
		   THIS.setCurrentDataSession() 
		   IF THIS.drivingAliasCurrentRecno  # RECNO(THIS.drivingAlias)
		      THIS.currentRecord = THIS.CurrentRecord + 1
		      THIS.drivingAliasCurrentRecno = RECNO(THIS.drivingAlias)
		   ENDIF   
		   IF THIS.currentRecord >= THIS.CommandClauses.RecordTotal
		      IF THIS.CurrentPass = 0 AND THIS.TwoPassProcess
		         THIS.resetUserFeedback() 
		      ELSE
		         THIS.currentRecord = THIS.CommandClauses.RecordTotal
		      ENDIF
		   ENDIF
		   THIS.UpdateStatus()
		   THIS.resetDataSession()
		ENDIF  
		
		
	ENDPROC

	PROCEDURE thermcaption_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   LOCAL m.lcType, m.cMessage
		   m.cMessage = ""
		   TRY 
		    m.lcType = VARTYPE(EVALUATE(m.vNewVal))
		      IF m.lcType = "C"
		        THIS.ThermCaption = m.vNewVal
		    ENDIF
		   CATCH 
		   ENDTRY     
		ENDIF   
		
	ENDPROC

	PROCEDURE thermformcaption_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C"
		   THIS.ThermFormCaption = m.vNewVal
		   THIS.SetThermFormCaption()
		ENDIF   
		
	ENDPROC

	PROCEDURE thermformheight_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND VARTYPE(m.vNewVal) = "N" AND ;
		   BETWEEN(m.vNewVal,30,SYSMETRIC(SYSMETRIC_SCREENHEIGHT )-30)  AND ;
		   INT(m.vNewVal) # THIS.ThermFormHeight
		   THIS.thermformheight = INT(m.vNewVal)
		   IF THIS.ThermMargin > THIS.ThermFormHeight/4
		      THIS.ThermMargin = THIS.ThermFormHeight/4
		   ENDIF   
		   THIS.thermForm = NULL
		ENDIF   
		
	ENDPROC

	PROCEDURE thermformwidth_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND VARTYPE(m.vNewVal) = "N" AND ;
		   BETWEEN(m.vNewVal,100,SYSMETRIC( SYSMETRIC_SCREENWIDTH  )-100) AND ;
		   INT(m.vNewVal) # THIS.ThermFormWidth 
		   THIS.thermformwidth = INT(m.vNewVal)
		   IF THIS.ThermMargin > THIS.ThermFormWidth/4
		      THIS.ThermMargin = THIS.ThermFormWidth/4
		   ENDIF   
		   THIS.ThermForm = NULL
		ENDIF   
		
	ENDPROC

	PROCEDURE thermmargin_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND VARTYPE(m.vNewVal) = "N" AND ;
		   BETWEEN(m.vNewVal,1,MIN(THIS.ThermFormHeight/4,THIS.ThermFormWidth/4)) AND ;
		   INT(m.vNewVal) # THIS.ThermMargin
		   THIS.thermmargin = INT(m.vNewVal)
		   THIS.thermForm = NULL
		ENDIF   
		
	ENDPROC

	PROCEDURE UnloadReport
		IF THIS.IsRunning
		   THIS.IsRunning = .F.
		   THIS.PopGlobalSets()
		ENDIF   
		
		THIS.ReportStopRunDateTime = DATETIME()
		THIS.ClearStatus() 
		THIS.ThermForm = NULL  
		
		DODEFAULT()
		
		THIS.resetDataSession()
		
	ENDPROC

	PROCEDURE UpdateStatus
		NODEFAULT
		* the THIS.IsRunning check here
		* make sure that this code doesn't
		* run if the Engine calls UpdateStatus
		* after we're through
		
		IF THIS.IsRunning
		   IF THIS.CommandClauses.RecordTotal  > 0
		      THIS.PercentDone = (THIS.CurrentRecord/THIS.CommandClauses.RecordTotal)
		 
		      #IF OUTPUTCLASS_DEBUGGING 
		          ? THIS.PercentDone, THIS.CurrentRecord, THIS.CommandClauses.RecordTotal, THIS.PageTotal
		          ? REPL(OUTPUTCLASS_STATUSCHAR_PCT_DONE,INT(THIS.PercentDone* OUTPUTCLASS_ONE_HUNDRED_PCT_MARK))+ ;
		            REPL(OUTPUTCLASS_STATUSCHAR_PCT_NOT_DONE,MAX(FLOOR(OUTPUTCLASS_ONE_HUNDRED_PCT_MARK - ;
		                                                           (OUTPUTCLASS_ONE_HUNDRED_PCT_MARK *THIS.PercentDone)),0) ) 
		       #ENDIF                
		
		   ENDIF   
		   THIS.DoStatus(;
		             IIF(THIS.CurrentPass = LISTENER_PREPASS  AND THIS.TwoPassProcess,;
		                 THIS.PrepassStatusText, ;
		                 THIS.RunStatusText) )
		ENDIF   
		
		
		
		
	ENDPROC

ENDDEFINE

DEFINE CLASS utilityreportlistener AS _reportlistener OF "_reportlistener.vcx" 		&& Adds configuration table handling and output target file handling to _reportListener class
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: closetargetfile		&& Finalizes file output.
		*m: configurationobjtype_access
		*m: createconfigtable		&& Creates a configuration table on demand.
		*m: frxcursor_access
		*m: frxcursor_assign
		*m: getconfigtable		&& Assesses and provides the name of the current configuration table, optionally creating it on disk if it is not available.
		*m: getpathforexternals		&& Determines the location at which the current configuration table and any other required external files will be expected.
		*m: loadfrxcursor_assign
		*m: opentargetfile		&& Initializes a file for output purposes.
		*m: readconfiguration_assign
		*m: setconfiguration		&& Checks the current configuration table for dynamic information in records of appropriate type, and executes these instructions if found.
		*m: targetfileext_assign
		*m: targetfilename_assign
		*m: targethandle_assign
		*m: verifyconfigtable		&& Ascertains that the format and and contents of the configuration meet requirements, adjusting it if necessary.
		*m: verifytargetfile		&& Assures that the nominated filename and its network location are available at the beginning of a file-based report run.
		*p: configurationobjtype		&& Holds the reserved value used to indicate that a configuration table row provides dynamic configuration information at runtime.
		*p: configurationtable		&& Holds the name of the current configuration table.
		*p: frxcursor		&& Holds a reference to an FRXCursor helper object to aid in run-time calculations related to FRX metadata and structure.
		*p: loadfrxcursor		&& Determines whether this class should dynamically load an instance of the helper class FRXCursor when attempting to access a reference to it.
		*p: readconfiguration		&& Indicates the conditions under which SetConfiguration code will run. 0=never, 1 = when the class instance Init runs, 2 = when the class instance runs BeforeReport, 3 = at both Init and BeforeReport.
		*p: targetfileext		&& Provides the default file extension for file output.
		*p: targetfilename		&& Provides the filename to which output will be written.  A unique name is generated for the class instance, which will be overwritten for successive report runs if not adjusted by the user.
		*p: targethandle		&& Provides a low-level file handle, to which output is written directly when the class provides raw data to the file, otherwise reserves the file during the report run so other applications don't write to it .
	*</DefinedPropArrayMethod>

	PROTECTED configurationtable
	*<PropValue>
		configurationobjtype = 1000
		configurationtable = ("")
		frxcursor = (NULL)
		FRXDataSession = -1
		Name = "utilityreportlistener"
		readconfiguration = (0)
		targetfileext = ("TXT")
		targetfilename = (FORCEPATH(SYS(2015),SYS(2023)))
		targethandle = -1
		_memberdata = <VFPData>
			<memberdata name="closetargetfile" type="method" display="closeTargetFile" favorites="False" />
			<memberdata name="configurationobjtype" type="property" display="configurationObjtype" favorites="True" />
			<memberdata name="configurationtable" type="property" display="configurationTable" favorites="False" />
			<memberdata name="createconfigtable" type="method" display="createConfigTable" favorites="True" />
			<memberdata name="frxcursor" type="property" display="frxCursor" favorites="True" />
			<memberdata name="getconfigtable" type="method" display="getConfigTable" favorites="True" />
			<memberdata name="getpathforexternals" type="method" display="getPathForExternals" favorites="True" />
			<memberdata name="loadfrxcursor" type="property" display="loadFrxCursor" favorites="True" />
			<memberdata name="opentargetfile" type="method" display="openTargetFile" favorites="False" />
			<memberdata name="readconfiguration" type="property" display="readConfiguration" favorites="True" />
			<memberdata name="setconfiguration" type="method" display="setConfiguration" favorites="True" />
			<memberdata name="targetfileext" type="property" display="targetFileExt" favorites="True" />
			<memberdata name="targetfilename" type="property" display="targetFileName" favorites="True" />
			<memberdata name="targethandle" type="property" display="targetHandle" favorites="True" />
			<memberdata name="verifyconfigtable" type="property" display="verifyConfigTable" favorites="True" />
			<memberdata name="verifytargetfile" type="method" display="verifyTargetFile" favorites="True" />
		</VFPData>
	*</PropValue>
	
	PROCEDURE BeforeReport
		DODEFAULT()
		
		 IF INLIST(THIS.ReadConfiguration,;
		                  OUTPUTCLASS_READCONFIG_REPORT,;
		                  OUTPUTCLASS_READCONFIG_BOTH)
		     THIS.SetConfiguration()
		ENDIF   
	ENDPROC

	PROTECTED PROCEDURE closetargetfile		&& Finalizes file output.
		   LOCAL laDummy[1]
		     
		   IF THIS.TargetHandle > -1    
		
		      =FCLOSE(THIS.TargetHandle)
		      THIS.TargetHandle = -1
		     
		      IF ADIR(laDummy,THIS.TargetFileName) = 1 AND ;
		         laDummy[1,2] > 0
		         * NB: have to check this as well as
		         * error because some COM errors may not
		         * end up in THIS.HadError.
		          * if continuation, update status rather than
		          * modal message
		         IF THIS.HadError
		            THIS.DoMessage(OUTPUTCLASS_CREATEERRORS_LOC,MB_ICONEXCLAMATION  )
		        ELSE
		            IF THIS.DoMessage( OUTPUTCLASS_SUCCESS_LOC + ;
		                            IIF(SYS(2024)="Y",CHR(13)+OUTPUTCLASS_REPORT_INCOMPLETE_LOC,""),;
		                            MB_ICONINFORMATION + MB_YESNO ) = IDYES
		               _CLIPTEXT = THIS.TargetFileName
		            ENDIF
		         ENDIF
		      ELSE
		         THIS.DoMessage(OUTPUTCLASS_NOCREATE_LOC,MB_ICONSTOP )
		       
		      ENDIF                 
		     
		   ENDIF
		
	ENDPROC

	PROCEDURE configurationobjtype_access
		* readonly property
		   
		RETURN OUTPUTCLASS_OBJTYPE_CONFIG
		
	ENDPROC

	PROCEDURE createconfigtable		&& Creates a configuration table on demand.
		LPARAMETERS m.tcDBF, m.tlOverWrite
		LOCAL m.liSelect, m.lcFile
		
		m.lcFile = FORCEEXT(m.tcDBF,"DBF")
		
		IF FILE(m.lcFile) AND m.tlOverWrite
		   ERASE (m.lcFile) RECYCLE
		   ERASE (FORCEEXT(m.lcFile,"FPT")) RECYCLE
		   ERASE (FORCEEXT(m.lcFile,"CDX")) RECYCLE   
		ENDIF   
		
		m.liSelect = SELECT()
		
		SELECT 0
		
		CREATE TABLE (m.lcFile) FREE ;
		   (objtype i, ;
		    objcode i, ;
		    objname v(60), ;
		    objvalue v(60), ;
		    objinfo m)
		
		IF NOT EMPTY(ALIAS()) && can happen if SAFETY ON and they decide not to overwrite    
		
		   INDEX ON Objtype TAG ObjType
		   INDEX ON ObjCode TAG ObjCode
		   INDEX ON ObjName TAG ObjName
		   INDEX ON ObjValue TAG ObjValue
		   INDEX ON DELETED() TAG OnDeleted    
		   
		   INSERT INTO (ALIAS()) VALUES ;
		      (OUTPUTCLASS_OBJTYPE_CONFIG,0,'DoMessage','"Welcome to the demo run!",64','Sample initialization/config method call')
		   DELETE NEXT 1
		
		   INSERT INTO (ALIAS()) VALUES ;
		     (OUTPUTCLASS_OBJTYPE_CONFIG,0,'TargetFileName','"xxx"','Sample initialization/config property')
		   DELETE NEXT 1
		   USE
		   
		ENDIF   
		
		SELECT (m.liSelect)   
	ENDPROC

	PROCEDURE Destroy
		THIS.CloseTargetFile()
		STORE NULL TO THIS.FRXCursor
		DODEFAULT()
	ENDPROC

	PROCEDURE frxcursor_access
		IF (NOT THIS.IsRunning) AND ;
		   ISNULL(THIS.FRXCursor) AND THIS.LoadFRXCursor
		   LOCAL m.loError
		   DO CASE
		   CASE FILE("_FRXCURSOR.VCX") OR ATC("_FRXCURSOR.VCX",SET("CLASSLIB")) > 0      
		  
		      TRY 
		        THIS.FRXCursor = NEWOBJECT("FRXCursor","_frxcursor.vcx")
		      CATCH TO m.loError
		         THIS.FRXCursor = NULL
		      ENDTRY   
		
		   CASE FILE(_REPORTOUTPUT)
		   
		      TRY 
		        THIS.FRXCursor = NEWOBJECT("FRXCursor","_frxcursor.vcx", _REPORTOUTPUT)
		      CATCH TO m.loError
		         THIS.FRXCursor = NULL
		      ENDTRY   
		
		   
		*!*	   CASE FILE(_REPORTBUILDER)
		*!*	      omit in case of untested version changes
		*!*	      TRY 
		*!*	        THIS.FRXCursor = NEWOBJECT("FRXCursor","frxcursor.vcx", _REPORTBUILDER)
		*!*	      CATCH TO loError
		*!*	         THIS.FRXCursor = NULL
		*!*	      ENDTRY   
		
		   OTHERWISE   
		     * not found
		   ENDCASE
		
		   IF ISNULL(THIS.FRXCursor)
		      THIS.DoMessage(OUTPUTXML_FRXCURSOR_MISSING_LOC,MB_ICONEXCLAMATION )         
		   ENDIF
		
		ENDIF   
		RETURN THIS.frxcursor
		
	ENDPROC

	PROCEDURE frxcursor_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning)
		   THIS.frxcursor = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE getconfigtable		&& Assesses and provides the name of the current configuration table, optionally creating it on disk if it is not available.
		LPARAMETERS m.tlForceExternal
		
		LOCAL m.lcDBF, m.lcPath
		
		m.lcDBF = ""
		
		IF m.tlForceExternal OR FILE(FULLPATH(FORCEEXT(OUTPUTCLASS_EXTERNALDBF,"DBF")))
		   m.lcDBF = FULLPATH(FORCEEXT(OUTPUTCLASS_EXTERNALDBF,"DBF"))
		ELSE
		   m.lcDBF = FORCEEXT(OUTPUTCLASS_INTERNALDBF,"DBF")
		ENDIF
		   
		IF NOT (FILE(m.lcDBF) OR THIS.IsRunning)
		
		      m.lcPath = THIS.GetPathForExternals()
		      * this may be the internal *or* external dbf name;
		      * we could be testing and not yet built into an app,
		      * so accept either, before the next test:
		      m.lcDBF = FORCEPATH(m.lcDBF,m.lcPath) 
		      
		      IF NOT FILE(m.lcDBF)
		         * now force to the external name:
		         m.lcDBF = FORCEEXT(FORCEPATH(OUTPUTCLASS_EXTERNALDBF,m.lcPath),"DBF")
		         * now check again
		         IF NOT FILE(m.lcDBF)
		            THIS.CreateConfigTable(m.lcDBF)
		            IF FILE(m.lcDBF)
		               THIS.DoMessage(OUTPUTCLASS_CONFIGTABLECREATED_LOC)
		            ENDIF
		         ENDIF
		      ENDIF
		
		ENDIF 
		
		IF NOT FILE(m.lcDBF)
		   m.lcDBF = ""
		ENDIF  
		
		THIS.ConfigurationTable = m.lcDBF
		
		RETURN m.lcDBF
	ENDPROC

	PROCEDURE getpathforexternals		&& Determines the location at which the current configuration table and any other required external files will be expected.
		* this is  mostly for standalone use
		* first figure out where to put it
		* with the idea of not littering
		* the disk too much based on CURDIR().
		   
		* For app pieces, look for a container module
		* and put it there.
		* if there isn't one,
		* put it with the VCX
		
		
		      
		LOCAL m.liLevel, m.lcSys16, m.lcPath
		      
		FOR m.liLevel = PROGRAM(-1) TO 1 STEP -1
		   m.lcSys16 = UPPER(SYS(16,m.liLevel))
		   IF INLIST(RIGHT(m.lcSys16,3),"APP","EXE","DLL")
		      m.lcPath = JUSTPATH(m.lcSys16)
		      EXIT
		   ENDIF
		ENDFOR
		      
		IF EMPTY(m.lcPath)
		   m.lcPath = JUSTPATH(THIS.ClassLibrary)
		ENDIF
		IF NOT DIRECTORY(m.lcPath)
		   m.lcPath = ""
		ENDIF
		
		RETURN m.lcPath
	ENDPROC

	PROCEDURE Init
		IF DODEFAULT()
		
		   IF INLIST(THIS.ReadConfiguration,;
		                  OUTPUTCLASS_READCONFIG_INIT,;
		                  OUTPUTCLASS_READCONFIG_BOTH)
		     THIS.SetConfiguration(.T.)
		   ENDIF   
		
		ELSE
		
		   RETURN .F.   
		
		ENDIF
		
		RETURN NOT THIS.HadError
	ENDPROC

	PROCEDURE loadfrxcursor_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L" AND NOT THIS.IsRunning
		   THIS.loadfrxcursor = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE opentargetfile		&& Initializes a file for output purposes.
		   THIS.VerifyTargetFile() 
		
		   THIS.TargetHandle = FCREATE(THIS.TargetFileName)
		 
		   IF THIS.TargetHandle < 0 OR THIS.HadError
		      THIS.HadError = .T.
		      THIS.DoMessage(OUTPUTCLASS_NOFILECREATE_LOC,MB_ICONSTOP )
		   ENDIF
		
		RETURN (NOT THIS.HadError)
		     
	ENDPROC

	PROCEDURE readconfiguration_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "N" AND NOT THIS.IsRunning
		   THIS.ReadConfiguration = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE setconfiguration		&& Checks the current configuration table for dynamic information in records of appropriate type, and executes these instructions if found.
		LPARAMETERS m.tlCalledFromInit
		
		IF NOT THIS.IsRunning 
		   * do some config work, don't change sessions --
		   * at this point we don't have our private session
		   * if we're being called in the Init
		
		   LOCAL m.liSelect, m.lcPEM, m.llOpened, m.lcOrder, m.liType, m.llQuiet
		  
		   m.liSelect = SELECT()
		
		   IF NOT USED("OutputConfig")
		      * if called from Init, 
		      * do this in quietmode
		      * because the caller has no
		      * opportunity to 
		      * turn off the message
		      m.llQuiet = (m.tlCalledFromInit AND NOT THIS.QuietMode)
		      IF m.llQuiet
		         THIS.QuietMode = .T.
		      ENDIF
		      THIS.GetConfigTable()
		      IF m.llQuiet
		         THIS.QuietMode = .F.
		      ENDIF
		      USE (THIS.ConfigurationTable) ALIAS "OutputConfig" IN 0 AGAIN NOUPDATE SHARED
		      m.llOpened = .T.
		   ELSE 
		      m.lcOrder = ORDER("OutputConfig")
		      SET ORDER TO ObjCode
		   ENDIF
		
		   IF (NOT THIS.HadError) AND THIS.VerifyConfigTable("OutputConfig")
		
		      SELECT OutputConfig
		      m.liType = THIS.ConfigurationObjtype
		
		      SCAN FOR ObjType = m.liType AND ;
		            NOT(DELETED() OR ;
		                ObjName == "" OR ;
		                ObjValue =="" )
		          
		         IF PEMSTATUS(THIS,ObjName,5) 
		            m.lcPEM = UPPER(PEMSTATUS(THIS,ObjName,3))
		            DO CASE
		            CASE lcPEM == "PROPERTY"
		               STORE EVAL(ObjValue) TO ("THIS."+ObjName)
		            CASE INLIST("|"+lcPEM+"|","|METHOD|","|EVENT|")
		               EVAL("THIS."+ObjName+"("+ObjValue+")")
		            OTHERWISE
		            ENDCASE
		          ENDIF
		
		      ENDSCAN
		
		      IF m.llOpened
		         USE IN OutputConfig
		      ELSE
		         SET ORDER TO (m.lcOrder) IN OutputConfig   
		      ENDIF   
		
		   ENDIF   
		
		   SELECT (m.liSelect)
		
		ENDIF   
		
	ENDPROC

	PROCEDURE setfrxdatasessionenvironment
		DODEFAULT()
		SET DELETED ON
		SET EXCLUSIVE OFF
		SET TALK OFF
		
	ENDPROC

	PROCEDURE targetfileext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C" AND NOT THIS.IsRunning
		   THIS.targetfileext = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE targetfilename_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C" AND NOT THIS.IsRunning
		   THIS.targetfilename = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE targethandle_assign
		LPARAMETERS m.vNewVal
		* Readonly during report run
		IF VARTYPE(m.vNewVal) = "N" AND NOT THIS.IsRunning
		   THIS.targethandle = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE verifyconfigtable		&& Ascertains that the format and and contents of the configuration meet requirements, adjusting it if necessary.
		LPARAMETERS m.tcAlias, m.tcFailureMsgTable, m.tcFailureMsgIndexes
		
		IF EMPTY(m.tcAlias) OR VARTYPE(m.tcAlias) # "C"
		   RETURN .F.
		ENDIF
		LOCAL m.lcTable, m.lcMessage, m.lcAlias, m.liSelect, ;
		      m.llReturn, m.liTagCount, laRequired[1], laKeys[1], ;
		      m.liFound, m.llExactOff, m.llSafetyOn
		
		m.llReturn = ;
		       TYPE(m.tcAlias+".OBJTYPE") = "N" AND ;
		       TYPE(m.tcAlias+".OBJCODE") = "N" AND ;  
		       TYPE(m.tcAlias+".OBJNAME") = "C" AND ;
		       TYPE(m.tcAlias+".OBJVALUE") = "C" AND ;
		       TYPE(m.tcAlias+".OBJINFO") = "M" 
		       
		* additional fields may be included and order
		* is not significant
		       
		IF NOT m.llReturn
		   
		   m.lcMessage = IIF(EMPTY(m.tcFailureMsgTable),;
		                         OUTPUTCLASS_CONFIGTABLEWRONG_LOC, ;
		                         m.tcFailureMsgTable)  + ;
		               CHR(13)+CHR(13)+ ;
		               DBF(m.tcAlias)
		ENDIF   
		
		
		IF m.llReturn
		
		   IF (SET("EXACT") = "OFF")
		      SET EXACT ON
		      m.llExactOff = .T.
		   ENDIF
		   
		   m.liSelect = SELECT()
		   SELECT (m.tcAlias)
		
		   * check for required keys...
		
		   DIME laRequired[5]
		   laRequired[1] = "OBJTYPE"
		   laRequired[2] = "OBJCODE"
		   laRequired[3] = "OBJNAME"
		   laRequired[4] = "OBJVALUE"
		   laRequired[5] = "DELETED()"   
		
		   IF TAGCOUNT() > 0
		      DIME laKeys[TAGCOUNT()]
		
		      FOR m.liTagCount = 1 TO TAGCOUNT()
		          laKeys[m.liTagCount] = UPPER(KEY(m.liTagCount))
		      ENDFOR
		   
		      FOR m.liTagCount = 1 TO ALEN(laRequired)
		         m.liFound = ASCAN(laKeys,UPPER(laRequired[m.liTagCount]))
		         IF m.liFound = 0
		            m.llReturn = .F.
		            EXIT
		         ENDIF
		      ENDFOR
		   ELSE
		      m.llReturn = .F.
		   ENDIF      
		   
		   IF NOT m.llReturn
		   
		     m.llSafetyOn = (SET("SAFETY") = "ON")
		     SET SAFETY OFF
		   
		     TRY
		         USE (DBF(m.tcAlias)) EXCLU ALIAS (m.tcAlias)
		         INDEX ON Objtype TAG ObjType
		         INDEX ON ObjCode TAG ObjCode
		         INDEX ON ObjName TAG ObjName
		         INDEX ON ObjValue TAG ObjValue
		         INDEX ON DELETED() TAG OnDeleted    
		         m.llReturn = .T.
		      CATCH
		      ENDTRY   
		      
		      IF m.llSafetyOn
		         SET SAFETY OFF
		      ENDIF
		      
		      IF m.llReturn
		         DIME laKeys[TAGCOUNT()]
		
		         FOR m.liTagCount = 1 TO TAGCOUNT()
		             laKeys[m.liTagCount] = UPPER(KEY(m.liTagCount))
		         ENDFOR
		   
		         FOR m.liTagCount = 1 TO ALEN(laRequired)
		            m.liFound = ASCAN(laKeys,UPPER(laRequired[m.liTagCount]))
		            IF m.liFound = 0
		               m.llReturn = .F.
		               EXIT
		            ENDIF
		         ENDFOR
		      ENDIF
		
		      USE (DBF(m.tcAlias)) SHARED ALIAS (m.tcAlias)
		   
		   ENDIF
		   
		   IF NOT m.llReturn
		      m.lcMessage =  IIF(EMPTY(m.tcFailureMsgIndexes),;
		                         OUTPUTCLASS_CONFIGINDEXMISSING_LOC, ;
		                         m.tcFailureMsgTable) + CHR(13) 
		      FOR m.liTagCount = 1 TO ALEN(laRequired)
		          m.lcMessage = m.lcMessage +  CHR(13) + ;
		                      laRequired[m.liTagCount] 
		      ENDFOR
		   ENDIF
		   
		   IF m.llExactOff
		      SET EXACT OFF
		   ENDIF
		   SELECT (m.liSelect) 
		
		ENDIF
		
		IF NOT(m.llReturn)
		   THIS.DoMessage(m.lcMessage,MB_ICONSTOP )
		ENDIF   
		
		RETURN m.llReturn       
		
		   
	ENDPROC

	PROCEDURE verifytargetfile		&& Assures that the nominated filename and its network location are available at the beginning of a file-based report run.
		LOCAL m.lcFile
		
		m.lcFile =  ALLTR(CHRTRAN(THIS.TargetFileName,;
		                       OUTPUTCLASS_FILENAME_CHARS_DISALLOWED,"_"))
		
		IF NOT DIRECTORY(JUSTPATH(m.lcFile))
		   m.lcFile = FULLPATH(ALLTR(m.lcFile))
		ENDIF   
		
		IF DIRECTORY(m.lcFile)
		   * we have to generate a filename
		   m.lcFile = FORCEPATH(SYS(2015), m.lcFile)
		ENDIF
		
		THIS.TargetFileName = m.lcFile   
		
		IF JUSTEXT(THIS.TargetFileName) == "" AND ;
		   RIGHT(THIS.TargetFileName,1) # "."      
		
		   THIS.TargetFileExt = CHRTRAN(THIS.TargetFileExt,;
		                        OUTPUTCLASS_FILENAME_CHARS_DISALLOWED,"_")
		
		   THIS.TargetFileName = FORCEEXT(THIS.TargetFileName, ;
		                                    THIS.TargetFileExt)
		ENDIF                                 
		 
		IF FILE(THIS.TargetFileName)
		   ERASE (THIS.TargetFileName) NORECYCLE
		ENDIF
		
	ENDPROC

ENDDEFINE

DEFINE CLASS xmldisplaylistener AS xmllistener OF "_reportlistener.vcx" 		&& Tunes XML settings suitably for presentation output needs, and adds image-file-publishing capabilities.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: adjustshapeaspectratio		&& Adjust the height and width for the rendered contents of a Picture layout control to give accurate aspect ratio for the current file.
		*m: checkreportforgeneralfields		&& Performs startup chores necessary to save out copies of general fields on disk when a report is rendered.
		*m: copyimagefilestoexternalfilelocation_assign
		*m: externalfilelocation_assign
		*m: imagefilebasename_assign
		*m: imagesrcattr_assign
		*m: initializefilecopysettings		&& Provides required environment settings for non-filebased images to be copied to files at runtime.
		*p: copyimagefilestoexternalfilelocation		&& Indicates whether file-based images should be copied to a  common location from their original locations on your disk, for reference  as image sources in the XML output.
		*p: externalfilelocation		&& Optionally assigns a  UNC or file system path, either relative or absolute, the XML result will assign to any external files, such as images, referenced in the main XML output file.
		*p: formattingchanges		&& Reference in which classes can store information about actions taken to apply changes to object formatting attributes dynamically during a report run.
		*p: imagefieldinstance		&& Keeps count of image instances for use in generating unique filenames for disk versions of non-filebased images in the report.
		*p: imagefieldtofile		&& Holds generated filename for image being rendered to disk from a non-filebased image.
		*p: imagefilebasename		&& Assigns an optional  prefix to be added to generated image file names when image files are saved to disk during the rendering of general fields in a report run
		*p: imagesrcattr		&& Supplies the name of the XML attribute used to show filename copied, or generated for non-filebased images, at runtime.
		*p: jpgclsid		&& Provides the GUID used when accessing GDI+ to request a file to be saved as JPG type.
		*p: oldexternalfilelocation		&& Saves the user's old externalfilelocation information during a report run, if the Listener has to temporarily change it to generate  image files from non-filebased images.
		*p: oldsendgdiplusimage		&& Saves the user's preference for GDIPlus image handle receipt if the Listener has to temporary change this setting to generate image files from non-filebased images.
		*p: utilityimage		&& Utility image object for handling aspect ratio of scale-and-retain filebased images.
	*</DefinedPropArrayMethod>

	PROTECTED imagefieldinstance,imagefieldtofile,jpgclsid,oldexternalfilelocation,oldsendgdiplusimage,utilityimage
	*<PropValue>
		externalfilelocation = ("")
		formattingchanges = (.NULL.)
		FRXDataSession = -1
		imagefieldinstance = 0
		imagefieldtofile = ("")
		imagefilebasename = ("")
		imagesrcattr = ("img")
		includebandswithnoobjects = .T.
		includeformattinginlayoutobjects = .T.
		jpgclsid = (.NULL.)
		Name = "xmldisplaylistener"
		oldexternalfilelocation = ("")
		oldsendgdiplusimage = 0
		utilityimage = .NULL.
		_memberdata = <VFPData>
			<memberdata name="copyimagefilestoexternalfilelocation" type="property" display="copyImageFilesToExternalFileLocation" favorites="True" />
			<memberdata name="externalfilelocation" type="property" display="externalFileLocation" favorites="True" />
			<memberdata name="formattingchanges" type="property" display="formattingChanges" favorites="True"/>
			<memberdata name="imagefieldinstance" type="property" display="imageFieldInstance" favorites="False" />
			<memberdata name="imagefieldtofile" type="property" display="imageFieldToFile" favorites="False" />
			<memberdata name="imagefilebasename" type="property" display="imageFileBaseName" favorites="True" />
			<memberdata name="imagesrcattr" type="property" display="imageSrcAttr" favorites="True" />
			<memberdata name="jpgclsid" type="property" display="JPGclsid" favorites="False" />
			<memberdata name="oldexternalfilelocation" type="property" display="oldExternalFileLocation" favorites="False" />
			<memberdata name="oldsendgdiplusimage" type="property" display="oldSendGDIPlusImage" favorites="False" />
			<memberdata name="checkreportforgeneralfields" type="method" display="checkReportForGeneralFields" favorites="False" />
			<memberdata name="initializefilecopysettings" type="method" display="initializeFileCopySettings" favorites="False"/>
			<memberdata name="utilityimage" type="property" display="utilityImage" favorites="False"/>
			<memberdata name="adjustshapeaspectratio" type="method" display="adjustShapeAspectRatio" favorites="False"/>
		</VFPData>
	*</PropValue>
	
	PROTECTED PROCEDURE adjustshapeaspectratio		&& Adjust the height and width for the rendered contents of a Picture layout control to give accurate aspect ratio for the current file.
		LPARAMETERS m.tnWidth, m.tnHeight
		IF VARTYPE(THIS.utilityImage) = "O"
		   * the image is coming from a file of some sort
		   * and we need to scale-and-retain
		   LOCAL m.llAdjustHeight, m.llAdjustWidth
		   DO CASE
		   CASE (tnWidth < tnHeight) 
		        * use 100% of the width of the space and scale the height        
		        m.llAdjustHeight = .T.
		   CASE (tnWidth > tnHeight) 
		        * use 100% of the height of the space and scale the width
		        m.llAdjustWidth = .T.
		   CASE (m.tnWidth = m.tnHeight) 
		       DO CASE
		       CASE THIS.utilityImage.Height > THIS.utilityImage.Width
		           m.llAdjustWidth = .T.
		       CASE THIS.utilityImage.Height < THIS.utilityImage.Width
		           m.llAdjustHeight = .T.
		       OTHERWISE
		           * both square, don't do adjustment
		       ENDCASE           
		   ENDCASE        
		   If m.llAdjustWidth
		      m.tnWidth = m.tnHeight *  ;
		         (THIS.utilityImage.Width / THIS.utilityImage.Height)
		   ENDIF
		   If m.llAdjustHeight
		      m.tnHeight = m.tnWidth * ;
		         (THIS.utilityImage.Height / THIS.utilityImage.Width)
		   ENDIF
		ENDIF
	ENDPROC

	PROCEDURE BeforeReport
		THIS.oldSendGDIPlusImage = THIS.SendGDIPlusImage     
		THIS.oldExternalFileLocation =  THIS.ExternalFileLocation    
		IF THIS.checkReportForGeneralFields() 
		   THIS.initializeFileCopySettings()
		ENDIF    
		DODEFAULT()
		THIS.resetDataSession()
		
	ENDPROC

	PROTECTED PROCEDURE checkreportforgeneralfields		&& Performs startup chores necessary to save out copies of general fields on disk when a report is rendered.
		LOCAL m.liGeneralFields, m.lcID, m.llOpened
		
		m.liGeneralFields = 0
		
		IF TYPE("THIS.CommandClauses.File") = "C" AND ;
		   (NOT EMPTY(THIS.CommandClauses.File)) AND ;
		   (FILE(THIS.CommandClauses.File)) && NB this is done before setting up dummy CommandClauses
		   
		   THIS.SetFRXDataSession()
		
		   IF USED("FRX") 
		      * this will be true
		      * if we call in BeforeReport,
		      * but that seems to cause a problem,
		      * when we re-assign SendGDIPlusImage
		      * so we're likely to call earlier,
		      * in the LoadReport method
		      SELECT FRX
		   ELSE
		      USE (THIS.CommandClauses.File) SHARED NOUPDATE ALIAS FRX IN 0
		      SELECT FRX
		      m.llOpened = .T.
		   ENDIF   
		
		   COUNT FOR ObjType = FRX_OBJTYP_PICTURE  AND  ;
		           Offset =  FRX_PICTURE_SOURCE_GENERAL TO ;
		             m.liGeneralFields
		
		   IF m.llOpened
		      USE IN FRX         
		   ENDIF
		
		   THIS.ResetDataSession()
		ENDIF  
		
		RETURN ( m.liGeneralFields > 0 )
		
		
		
	ENDPROC

	PROCEDURE contattr_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.ContAttr
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.ContAttr)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE copyimagefilestoexternalfilelocation_assign
		LPARAMETERS m.vNewVal
		IF NOT THIS.IsRunning and VARTYPE(m.vNewVal) = "L"
		   IF NOT (m.vNewVal = THIS.CopyImageFilesToExternalFileLocation)
		      LOCAL m.liIndex
		      THIS.CopyImageFilesToExternalFileLocation = m.vNewVal
		      IF ISNULL(THIS.XSLTParameters) 
		         THIS.XSLTParameters = CREATEOBJECT("Collection")
		      ENDIF
		      IF NOT ISNULL(THIS.XSLTParameters)
		         WITH THIS.XSLTParameters
		            FOR m.liIndex = 1 TO .COUNT
		               IF .GETKEY(m.liIndex) == "copyImageFiles"
		                  .REMOVE(m.liIndex)
		                  EXIT
		               ENDIF
		            NEXT
		            .ADD(IIF(m.vNewVal,"1","0"),"copyImageFiles")
		         ENDWITH   
		      ENDIF
		      
		      IF THIS.CopyImageFilesToExternalFileLocation 
		         IF EMPTY(THIS.ExternalFileLocation) OR ;
		            NOT DIRECTORY(FULLPATH(JUSTPATH(THIS.TargetFileName),THIS.ExternalFileLocation))
		            THIS.ExternalFileLocation = "."
		         ENDIF
		      ENDIF
		
		   ENDIF   
		   
		ENDIF   
		
	ENDPROC

	PROCEDURE Destroy
		STORE NULL TO ;
		   THIS.JPGclsid, ;
		   THIS.FormattingChanges, ;
		   THIS.UtilityImage
		
		DODEFAULT()
		
		
	ENDPROC

	PROCEDURE externalfilelocation_assign
		LPARAMETERS m.vNewVal
		IF THIS.isRunning AND NOT EMPTY(THIS.externalFileLocation)
		   RETURN
		ENDIF   
		IF VARTYPE(m.vNewVal) = "C"
		   
		   IF NOT (ALLTRIM(m.vNewVal) == THIS.externalFileLocation)
		      LOCAL m.liIndex, m.llRemove
		   
		      THIS.externalFileLocation = ALLTRIM(m.vNewVal)
		      IF EMPTY(THIS.externalFileLocation)
		         m.llRemove = .T.
		      ELSE
		         THIS.externalFileLocation = ADDBS(THIS.externalFileLocation)
		      ENDIF
		
		      IF ISNULL(THIS.XSLTParameters) AND NOT m.llRemove
		         THIS.XSLTParameters = CREATEOBJECT("Collection")
		      ENDIF
		
		      IF NOT ISNULL(THIS.XSLTParameters)
		         WITH THIS.XSLTParameters
		            FOR m.liIndex = 1 TO .COUNT
		               IF .GETKEY(m.liIndex) == "externalFileLocation"
		                  .REMOVE(m.liIndex)
		                  EXIT
		               ENDIF
		            NEXT
		            IF NOT (m.llRemove)
		               .ADD(THIS.externalFileLocation,"externalFileLocation")
		            ENDIF
		         ENDWITH   
		      ENDIF
		
		   ENDIF   
		   
		   
		ENDIF   
		
	ENDPROC

	PROCEDURE getrawformattinginfo
		LPARAMETERS m.tnLeft, m.tnTop, m.tnWidth, m.tnHeight, m.tnObjectContinuationType
		LOCAL m.lcInfo
		
		THIS.adjustShapeAspectRatio (@tnWidth, @tnHeight)
		
		m.lcInfo = DODEFAULT(m.tnLeft, m.tnTop, m.tnWidth, m.tnHeight, m.tnObjectContinuationType)
		IF NOT EMPTY(THIS.ImageFieldtoFile) 
		   m.lcInfo = m.lcInfo + " "+THIS.ImageSrcAttr+"='"+THIS.ImageFieldtoFile+"'"
		ENDIF   
		
		RETURN m.lcInfo
	ENDPROC

	PROCEDURE getvfprdlcontents
		LPARAMETERS m.tcNodeName, m.tlAsString
		
		IF VARTYPE(THIS.CommandClauses) = "O"
		
		   ADDPROPERTY(THIS.CommandClauses,;
		             "externalFileLocation", ;
		             THIS.externalFileLocation)
		             
		   ADDPROPERTY(THIS.CommandClauses, ;
		            "copyImageFilesToExternalFileLocation", ;
		            THIS.copyImageFilesToExternalFileLocation) 
		            
		   ADDPROPERTY(THIS.CommandClauses, ;
		            "imageFileBaseName", ;
		            THIS.imageFileBaseName)             
		ENDIF            
		            
		RETURN DODEFAULT(m.tcNodeName, m.tlAsString)                         
	ENDPROC

	PROCEDURE heightattr_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.HeightAttr
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.HeightAttr)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE idattribute_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.IdAttribute
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.IdAttribute)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE idrefattribute_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.IdRefAttribute
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.IdRefAttribute)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE imagefilebasename_assign
		LPARAMETERS m.vNewVal
		THIS.ImageFileBaseName = ALLTR(CHRTRAN(JUSTSTEM(TRANSFORM(m.vNewVal)),;
		                         OUTPUTCLASS_FILENAME_CHARS_DISALLOWED,"_"))
	ENDPROC

	PROCEDURE imagesrcattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal) AND ;
		   (NOT m.vNewVal == THIS.imageSrcAttr)
		   THIS.imageSrcAttr = m.vNewVal
		   THIS.SynchXSLTProcessorUser()
		ENDIF      
		
	ENDPROC

	PROCEDURE Init
		IF DODEFAULT()
		   THIS.AppName = OUTPUTXMLDISPLAY_APPNAME_LOC
		ELSE
		   RETURN .F.   
		ENDIF
		
		RETURN NOT THIS.HadError
	ENDPROC

	PROTECTED PROCEDURE initializefilecopysettings		&& Provides required environment settings for non-filebased images to be copied to files at runtime.
		#define CLSID_JPG 	"{557CF401-1A04-11D3-9A73-0000F81EF32E}"
		
		
		DECLARE INTEGER GdipSaveImageToFile in GDIPLUS.DLL  ;
		       integer image,string filename,string @ CLSID_clsidEncoder,integer encoderParams        
		
		LOCAL m.lcID
		
		IF ISNULL(THIS.Jpgclsid) OR EMPTY(THIS.Jpgclsid)
		   DECLARE integer CLSIDFromString IN ole32 string,string @
		   m.lcID = SPACE(20)
		   CLSIDFromString(STRCONV(CLSID_JPG ,STRCONV_DBCS_UNICODE),@m.lcID)      
		   THIS.Jpgclsid = m.lcID
		   m.lcID = ""
		ENDIF
		
		IF THIS.SendGDIPlusImage < LISTENER_SEND_GDI_IMAGE_HANDLE 
		   THIS.SendGDIPlusImage =   LISTENER_SEND_GDI_IMAGE_HANDLE
		ENDIF   
		
		IF EMPTY(THIS.ExternalFileLocation) OR ;
		   NOT DIRECTORY(FULLPATH(JUSTPATH(THIS.TargetFileName),THIS.ExternalFileLocation))
		   THIS.ExternalFileLocation = "."
		ENDIF
		
		
	ENDPROC

	PROCEDURE leftattr_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.LeftAttr
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.LeftAttr)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE Render
		LPARAMETERS m.nFRXRecNo,m.nLeft,m.nTop,m.nWidth,m.nHeight, ;
		            m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage
		
		LOCAL m.llCopyImage, m.lcFile
		
		IF m.GDIPlusImage > 0 AND ;
		  (ISNULL(THIS.Jpgclsid) OR EMPTY(THIS.Jpgclsid))
		   * we didn't have any general fields but we do have
		   * an image control, referenced as an expression,
		   * they have explicitly turned on SendGDIPlusImage, and
		   * this is the first time we're hitting an image control:
		   THIS.initializeFileCopySettings()
		ENDIF    
		
		THIS.SetFRXDataSession()
		GO m.nFRXRecNo IN FRX
		
		IF THIS.CopyImageFilesToExternalFileLocation 
		    IF (FRX.ObjType = FRX_OBJTYP_PICTURE) AND ;
		       FRX.Offset # FRX_PICTURE_SOURCE_GENERAL AND ;
		       FILE(m.cContentsToBeRendered)
		       * use FILE() function here,
		       * need to find if it is
		       * built into an app
		       m.llCopyImage = .T.
		    ENDIF
		ENDIF   
		
		IF (FRX.ObjType = FRX_OBJTYP_PICTURE) AND ;
		    FRX.General = FRX_PICTUREMODE_SCALE_KEEP_SHAPE AND ;
		    FRX.Offset # FRX_PICTURE_SOURCE_GENERAL AND ;
		    FILE(m.cContentsToBeRendered)
		    * NB: doing this *only* when necessary for re-scaling.
		    * see above for why FILE() function -- this will work
		    * with the image object.
		    THIS.utilityImage = LOADPICTURE(m.cContentsToBeRendered)
		ENDIF    
		
		THIS.SetCurrentDataSession()   
		
		DO CASE
		
		CASE m.llCopyImage
		
		   m.lcFile = FORCEPATH(m.cContentsToBeRendered, ;
		            FULLPATH(THIS.ExternalFileLocation,ADDBS(JUSTPATH(THIS.TargetFileName))))
		            
		   IF EMPTY(SYS(2000,m.lcFile))
		   
		      * used to be:
		      * COPY FILE (cContentsToBeRendered) TO (lcFile)
		      * to handle files built into an app
		      
		      STRTOFILE(FILETOSTR(m.cContentsToBeRendered),m.lcFile)
		    
		   ENDIF   
		   
		   IF EMPTY(SYS(2000,m.lcFile))
		      THIS.ImageFieldToFile = ""
		   ELSE
		      THIS.ImageFieldToFile = JUSTFNAME(m.lcFile)
		   ENDIF
		   
		CASE THIS.SendGDIPlusImage > LISTENER_SEND_GDI_IMAGE_NONE  AND ;
		   m.GDIPlusImage > 0  AND ;
		   GdipSaveImageToFile(m.GDIPlusImage,;
		       STRCONV(FORCEPATH(THIS.ImageFileBaseName + "_"+ ;
		                         TRANSFORM(THIS.ImageFieldInstance+1)+".jpg",;
		                         FULLPATH(THIS.ExternalFileLocation,ADDBS(JUSTPATH(THIS.TargetFileName))))+CHR(0), ;
		               STRCONV_DBCS_UNICODE) ,THIS.JPGCLSID,0) = 0
		
		   THIS.ImageFieldInstance = THIS.ImageFieldInstance + 1       
		
		   THIS.ImageFieldToFile = THIS.ImageFileBaseName +"_"+ ;
		                           TRANSFORM(THIS.ImageFieldInstance)+".jpg"
		
		OTHERWISE
		
		   THIS.ImageFieldToFile = ""
		
		ENDCASE
		
		DODEFAULT(m.nFRXRecNo, m.nLeft,m.nTop,m.nWidth,m.nHeight, ;
		          m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage)
		            
		THIS.ImageFieldToFile = ""
		THIS.UtilityImage = NULL
	ENDPROC

	PROCEDURE resetdocument
		THIS.ResetToDefault("ImageFieldInstance")
		THIS.ResetToDefault("ImageFieldToFile")
		THIS.ResetToDefault("UtilityImage")
		DODEFAULT()
		
		
	ENDPROC

	PROCEDURE setdomformattinginfo
		LPARAMETERS m.toNode, m.tnLeft, m.tnTop, m.tnWidth,m.tnHeight, m.tnObjectContinuationType
		
		THIS.adjustShapeAspectRatio (@m.tnWidth, @m.tnHeight)
		
		DODEFAULT( m.toNode, m.tnLeft, m.tnTop, m.tnWidth,m.tnHeight, m.tnObjectContinuationType)
		
		IF NOT EMPTY(THIS.ImageFieldtoFile) 
		   m.toNode.SetAttribute(THIS.ImageSrcAttr,THIS.ImageFieldtoFile )                  
		ENDIF   
		
	ENDPROC

	PROCEDURE topattr_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.TopAttr
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.TopAttr)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

	PROCEDURE UnloadReport
		DODEFAULT()
		IF NOT (THIS.noPageEject OR ;
		 ((TYPE("THIS.CommandClauses.NoPageEject") = "L") AND ;
		   THIS.CommandClauses.NoPageEject))
		   IF NOT ISNULL(THIS.OldExternalFileLocation)
		      * even if empty
		      THIS.ExternalFileLocation = THIS.OldExternalFileLocation
		      THIS.OldExternalFileLocation = NULL    
		   ENDIF      
		   IF NOT (THIS.OldSendGDIPlusImage = THIS.SendGDIPlusImage)
		      THIS.SendGDIPlusImage = THIS.OldSendGDIPlusImage
		   ENDIF   
		ENDIF   
		THIS.resetDataSession()
		
		
	ENDPROC

	PROCEDURE widthattr_assign
		LPARAMETERS m.vNewVal
		LOCAL m.lcVal
		m.lcVal = THIS.WidthAttr
		DODEFAULT(m.vNewVal)
		IF NOT (m.lcVal == THIS.WidthAttr)
		   THIS.SynchXSLTProcessorUser()
		ENDIF   
	ENDPROC

ENDDEFINE

DEFINE CLASS xmllistener AS utilityreportlistener OF "_reportlistener.vcx" 		&& Provides XML output from a report run.
 	*< CLASSDATA: Baseclass="reportlistener" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "reportlisteners.h"
	*<DefinedPropArrayMethod>
		*m: applyusertransformtooutput		&& Applies a user-specified XSLT to XML output at the conclusion of a report run.
		*m: applyusertransform_assign
		*m: applyxslt		&& Provides generic facilities for applying XSLT to XML.
		*m: contattr_assign
		*m: currentdocument_assign
		*m: getdefaultuserxslt		&& Provides a hook for subclasses to supply their preferred XSLT document for use when XMLListener automatically applies XSLT at the end of a report run.
		*m: getfrxlayoutobjectfieldlist		&& Provides fields list to the getVFPRdlContents method, in a suitable comma-delimited format for inclusion as a macro in SQL SELECT statement from FRX cursor and related Bands and Objects cursors.
		*m: getpathedimageinfo		&& Supplies a fully-pathed filename for an image file included in the report, using the original relative-path information stored in the FRX.
		*m: getrawformattinginfo		&& Formulates the appropriate formatting attributes for an element when XMLListener is streaming XML to a file.
		*m: getvfprdlcontents		&& Translates report definition layout metadata, from the FRX and the current report run, into an XML format useful to other applications.
		*m: heightattr_assign
		*m: idattribute_assign
		*m: idrefattribute_assign
		*m: includebandswithnoobjects_assign
		*m: includebreaksindata_assign
		*m: includedatasourcesinvfprdl_assign
		*m: includeformattinginlayoutobjects_assign
		*m: insertxmlconfigrecords		&& Adds a record to the configuration table describing a default XML node when XML Listener is creating or editing a shared configuration table.
		*m: leftattr_assign
		*m: loadprocessorobject		&& Provides a generic means to load an XSLT processor object from a string representing an XSLT document or a filename.
		*m: nopageeject_assign
		*m: preparefrxcopy		&& Prepares FRX alias or copy for use in creating VFP-RDL contents.
		*m: removefrxcopy		&& Removes FRX copy used in preparing VFP-RDL.
		*m: resetdocument		&& Resets the XML document after a report run.
		*m: resetreport		&& Resets information for a report-level node during the production of an XML document that may span multiple reports.
		*m: setdomformattinginfo		&& Formulates the appropriate formatting attributes for an element when XMLListener is using the DOM to create XML.
		*m: synchxsltprocessoruser		&& Can be called in the assign of any Attribute-setting property to synchronize the XSLT associated with the class witht he new attribute values.
		*m: topattr_assign
		*m: verifyattributenames		&& Hook method called during XML Listener's augmented version of VerifyConfigTable, allowing subclasses to verify attribute names should they decide to store them in the configuration table similar to node names.
		*m: verifyncname		&& Generic method to validate strings as XML-standard NCNames.
		*m: verifynodenames		&& Method called during XML Listener's augmented version of VerifyConfigTable to check all node values in the configuration table for XML validity.
		*m: widthattr_assign
		*m: writeraw		&& Used to write raw XML data to a file.
		*m: xmlmode_assign
		*m: xmlrawconv		&& Used to convert any control characters to entity references when XML Listener is writing raw XML data to a file.
		*m: xmlrawnode		&& Used to formulate the contents of an XML element  node when XML Listener is writing the XML document as raw data to a file.
		*m: xmlrawtag		&& Used to formulate the contents of an XML tag when XML Listener is writing the XML document as raw data to a file.
		*m: xsltparameters_assign
		*m: xsltprocessorrdl_assign
		*m: xsltprocessoruser_assign
		*p: applyusertransform		&& Indicates whether XMLListener should automatically apply an XSLT transform at the conclusion of a report run.
		*p: columnnodes		&& Holds column-level output during a report run.
		*p: contattr		&& Supplies the name of the attribute used to show continuation type for a layout object that can span bands or pages.
		*p: currentband		&& Holds information about the band for which output is currently being generated during a report run.
		*p: currentcolumn		&& Holds information about the column for which output is currently being generated during a report run.
		*p: currentdocument		&& Holds information about the XML document for which output is currently being generated during a report run.
		*p: currentpage		&& Holds information about the page for which output is currently being generated during a report run.
		*p: datanodes		&& Holds non-formatting output (title, detail, group, and summary band objects) during a report run.
		*p: heightattr		&& Supplies the name of the XML attribute used to show height for a layout object.
		*p: idattribute		&& Supplies the name of the XML attribute used to provide the FRX record number for a layout object or page number of a formatting band (column or page) object.
		*p: idrefattribute		&& Supplies the name of the XML attribute used to provide the current page for a layout object or FRX record number of a formatting band (column or page) object.
		*p: includebandswithnoobjects		&& Indicates whether band-level information for bands with no contents should be included in the XML.
		*p: includebreaksindata		&& 0 = provide page band nodes positioned along with other bands in the datastream, wherever they happen to occur, 1 = no pagebreak info, no page header and footer info, 2 = collection of pages with page headers and footers data
		*p: includedatasourcesinvfprdl		&& Indicates whether information about the source tables, relations, indexes, etc should be included in the VFPRDL metadata section of the report XML.
		*p: includeformattinginlayoutobjects		&& Indicates whether formatting information such as positioning attributes should be included in the report XML.
		*p: includepage		&& Evaluates whether the current page is part of the output page set and should be included in the XML document result.
		*p: leftattr		&& Supplies the name of the XML attribute used to show leftmost position for a layout object.
		*p: nopageeject		&& Indicates whether the XML Listener should consider the current report run to be continued.  Can be used without NOPAGEEJECT on the REPORT FORM command.
		*p: pagenodes		&& Holds page-level output during a report run.
		*p: topattr		&& Supplies the name of the XML attribute used to show topmost position for a layout object.
		*p: widthattr		&& Supplies the name of the XML attribute used to show width for a layout object.
		*p: xmlmode		&& 0 = data only, 1 = RDL only, 2 = data and RDL
		*p: xsltparameters		&& Holds an optional parameter collection passed to the ApplyXSLT method when XMLListener automatically  applies a user XSLT transformation at the conclusion of a report run.
		*p: xsltprocessorrdl		&& Holds an RDL-specific processor object, reserved for future use.
		*p: xsltprocessoruser		&& Holds a user-definable processor object which, if filled and available at the end of a run, can be used automatically by XML Listener to transform the raw XML document to requirements.
	*</DefinedPropArrayMethod>

	PROTECTED columnnodes,currentband,currentcolumn,currentpage,datanodes,includepage,pagenodes,verifytargetfile
	*<PropValue>
		columnnodes = (NULL)
		contattr = ("c")
		currentband = (NULL)
		currentcolumn = (NULL)
		currentdocument = (NULL)
		currentpage = (NULL)
		datanodes = (NULL)
		FRXDataSession = -1
		Height = 23
		heightattr = ("h")
		idattribute = ("id")
		idrefattribute = ("idref")
		includebreaksindata = 0
		includepage = .T.
		leftattr = ("l")
		Name = "xmllistener"
		nopageeject = .F.
		pagenodes = (NULL)
		targetfileext = ("XML")
		topattr = ("t")
		Width = 23
		widthattr = ("w")
		xmlmode = 2
		xsltparameters = (NULL)
		xsltprocessorrdl = (NULL)
		xsltprocessoruser = (NULL)
		_memberdata = <VFPData>
			<memberdata name="applyusertransform" type="property" display="applyUserTransform" favorites="True"/>
			<memberdata name="applyusertransformtooutput" type="method" display="applyUserTransformToOutput"/>
			<memberdata name="applyxslt" type="method" display="applyXslt" favorites="True"/>
			<memberdata name="columnnodes" type="property" display="columnNodes"/>
			<memberdata name="contattr" type="property" display="contAttr" favorites="True"/>
			<memberdata name="currentband" type="property" display="currentBand"/>
			<memberdata name="currentcolumn" type="property" display="currentColumn"/>
			<memberdata name="includepage" type="property" display="includePage"/>
			<memberdata name="currentdocument" type="property" display="currentDocument" favorites="True"/>
			<memberdata name="currentpage" type="property" display="currentPage"/>
			<memberdata name="datanodes" type="property" display="dataNodes"/>
			<memberdata name="getdefaultuserxslt" type="method" display="getDefaultUserXslt"/>
			<memberdata name="preparefrxcopy" type="method" display="prepareFrxCopy"/>
			<memberdata name="removefrxcopy" type="method" display="removeFrxCopy"/>
			<memberdata name="getpathedimageinfo" type="method" display="getPathedImageInfo"/>
			<memberdata name="getpathforexternals" type="method" display="getPathForExternals" favorites="True"/>
			<memberdata name="getrawformattinginfo" type="method" display="getRawFormattingInfo"/>
			<memberdata name="getvfprdlcontents" type="method" display="getVFPRdlContents"/>
			<memberdata name="getfrxlayoutobjectfieldlist" type="method" display="getFrxLayoutObjectFieldlist"/>
			<memberdata name="heightattr" type="property" display="heightAttr" favorites="True"/>
			<memberdata name="idattribute" type="property" display="idAttribute" favorites="True"/>
			<memberdata name="idrefattribute" type="property" display="idrefAttribute" favorites="True"/>
			<memberdata name="includebandswithnoobjects" type="property" display="includeBandsWithNoObjects" favorites="True"/>
			<memberdata name="includebreaksindata" type="property" display="includeBreaksInData" favorites="True"/>
			<memberdata name="includedatasourcesinvfprdl" type="property" display="includeDataSourcesInVfpRdl" favorites="True"/>
			<memberdata name="includeformattinginlayoutobjects" type="property" display="includeFormattingInLayoutObjects" favorites="True"/>
			<memberdata name="insertxmlconfigrecords" type="method" display="insertXmlConfigRecords"/>
			<memberdata name="leftattr" type="property" display="leftAttr" favorites="True"/>
			<memberdata name="loadprocessorobject" type="method" display="loadProcessorObject"/>
			<memberdata name="nopageeject" type="property" display="noPageEject" favorites="True"/>
			<memberdata name="pagenodes" type="property" display="pageNodes"/>
			<memberdata name="resetdocument" type="method" display="resetDocument" favorites="True"/>
			<memberdata name="resetreport" display="resetReport" type="method"/>
			<memberdata name="setdomformattinginfo" type="method" display="setDOMFormattingInfo"/>
			<memberdata name="setfrxdatasession" type="method" display="setFRXDataSession"/>
			<memberdata name="synchxsltprocessoruser" type="method" display="synchXsltProcessorUser"/>
			<memberdata name="topattr" type="property" display="topAttr" favorites="True"/>
			<memberdata name="verifyattributenames" type="method" display="verifyAttributeNames"/>
			<memberdata name="verifyncname" type="method" display="verifyNCName" favorites="True"/>
			<memberdata name="verifynodenames" type="method" display="verifyNodeNames"/>
			<memberdata name="widthattr" type="property" display="widthAttr" favorites="True"/>
			<memberdata name="writeraw" type="method" display="writeRaw"/>
			<memberdata name="xmlmode" type="property" display="xmlMode" favorites="True"/>
			<memberdata name="xmlrawconv" type="method" display="xmlRawConv"/>
			<memberdata name="xmlrawnode" type="method" display="xmlRawNode"/>
			<memberdata name="xmlrawtag" type="method" display="xmlRawTag"/>
			<memberdata name="xsltparameters" type="property" display="xsltParameters" favorites="True"/>
			<memberdata name="xsltprocessorrdl" type="property" display="xsltProcessorRdl" favorites="True"/>
			<memberdata name="xsltprocessoruser" type="property" display="xsltProcessorUser" favorites="True"/>
		</VFPData>
	*</PropValue>
	
	PROCEDURE AfterBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo
		
		DODEFAULT(m.nBandObjCode, m.nFRXRecNo)
		
		
		IF THIS.InvokeOnCurrentPass() AND ;
		   THIS.Targethandle > -1 
		  
		   LOCAL m.lcBand, m.loNode, m.lcID, m.lcIDRef, ;
		         m.llFormatBreakBand, m.loObjects, m.llOmitBand
		
		
		   THIS.SetFRXDataSession()   
		   m.lcBand =  IIF(SEEK(OUTPUTXML_OBJTYPE_NODES+FRX_OBJTYP_BAND+ ;
		                    OUTPUTXML_OBJTYPE_BANDOFFSET+nBandObjCode,;
		                    "Nodes","FrxNodes"),;
		                    Nodes.ObjValue, ;
		                    OUTPUTXML_GOOFTAG)
		                    
		   GO m.nFRXRecNo IN FRX
		   IF NOT THIS.IncludeBandsWithNoObjects 
		      m.loObjects = THIS.FRXCursor.GetObjectsInBand(FRX.UniqueID)
		      IF loObjects.Count = 0
		          m.llOmitBand = .T.
		      ENDIF
		      m.loObjects = NULL
		   ENDIF   
		   
		   THIS.SetCurrentDataSession()
		      
		   m.llFormatBreakBand = INLIST(m.nBandObjCode,;
		                    FRX_OBJCOD_PAGEHEADER, ;
		                    FRX_OBJCOD_PAGEFOOTER, ;
		                    FRX_OBJCOD_COLHEADER, ;
		                    FRX_OBJCOD_COLFOOTER)                 
		                    
		   * first evaluate THIS.IncludeBreaksInData 
		   DO CASE
		   CASE m.llOmitBand = .T.
		      * nothing
		   CASE THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_INDATA OR ;
		        NOT m.llFormatBreakBand   
		
		      #IF OUTPUTXML = OUTPUTXML_RAW
		         
		         IF EMPTY(NVL(THIS.CurrentBand,"")) 
		           * see continuation discussion in Render.
		           * our fix there may have left us with
		           * no band here
		           * do nothing
		         ELSE
		         
		            THIS.CurrentBand = NVL(THIS.CurrentBand,"") + ;
		               THIS.XMLRawTag( m.lcBand, .F., m.lcID, m.lcIDRef ) 
		  
		            IF NOT (ISNULL(THIS.CurrentBand) OR EMPTY(THIS.CurrentBand))
		               THIS.WriteRaw(THIS.CurrentBand)
		               THIS.CurrentBand = ""
		            ENDIF
		
		         ENDIF    
		               
		      #ELIF OUTPUTXML = OUTPUTXML_DOTNET                       
		  
		      #ELSE
		         * nothing to do here when using the DOM
		         THIS.CurrentBand = NULL
		      #ENDIF
		
		   CASE THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION
		      * build the collection which will be inserted into the
		      * data before finishing.
		      * but the band output at this point is .F.
		      #IF OUTPUTXML = OUTPUTXML_RAW
		          IF INLIST( m.nBandObjCode, ;
		                 FRX_OBJCOD_PAGEHEADER, ;
		                 FRX_OBJCOD_PAGEFOOTER) 
		             THIS.CurrentPage = NVL(THIS.CurrentPage,"")
		             THIS.CurrentPage = THIS.CurrentPage +  ;
		                               THIS.XMLRawTag( m.lcBand, .F., m.lcID, m.lcIDRef ) 
		             THIS.PageNodes = THIS.PageNodes + THIS.CurrentPage
		             THIS.CurrentPage = NULL
		          ELSE
		             THIS.CurrentColumn = NVL(THIS.CurrentColumn,"")
		             THIS.CurrentColumn = THIS.CurrentColumn + ;
		                                THIS.XMLRawTag( m.lcBand, .F., m.lcID, m.lcIDRef ) 
		             THIS.ColumnNodes = THIS.ColumnNodes + THIS.CurrentColumn
		             THIS.CurrentColumn = NULL
		          ENDIF
		          
		      #ELIF OUTPUTXML = OUTPUTXML_DOTNET 
		      
		      #ELSE
		      
		          * we leave THIS.CurrentBand alone in this case,
		          * to use after the band has finished.
		          IF INLIST( nBandObjCode, ;
		                 FRX_OBJCOD_PAGEHEADER, ;
		                 FRX_OBJCOD_PAGEFOOTER) 
		             THIS.CurrentPage = NULL
		          ELSE
		             THIS.CurrentColumn = NULL
		          ENDIF
		          
		      #ENDIF
		   CASE INLIST( m.nBandObjCode, ;
		                 FRX_OBJCOD_COLHEADER, ;
		                 FRX_OBJCOD_COLFOOTER)   && XMLBREAKS_NONE and column band
		   
		       THIS.CurrentColumn = NULL
		   OTHERWISE  && XMLBREAKS_NONE and page band
		
		       THIS.CurrentPage = NULL
		   
		   ENDCASE
		   
		   THIS.resetDataSession()
		   
		ENDIF
		
		
		IF INLIST(m.nBandObjCode,FRX_OBJCOD_PAGEFOOTER, FRX_OBJCOD_TITLE) 
		   THIS.includePage = .F.
		ENDIF
		
	ENDPROC

	PROCEDURE AfterReport
		
		   THIS.SetFRXDataSession()
		
		
		   IF THIS.TargetHandle > -1 AND NOT (THIS.HadError )
		   
		      #IF OUTPUTXML = OUTPUTXML_RAW
		      
		          LOCAL m.lcNode
		          IF NOT THIS.XMLMode = OUTPUTXML_RDL_ONLY 
		          
		             IF  NOT EMPTY(NVL(THIS.CurrentBand,""))
		                m.lcNode = SUBSTR(THIS.CurrentBand,2,AT(" ", THIS.CurrentBand)-2)
		                THIS.CurrentBand = THIS.CurrentBand + THIS.XMLRawTag(m.lcNode)
		                * write a closing tag
		                THIS.WriteRaw(THIS.CurrentBand)
		             ENDIF
		
		             IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION 
		                IF NOT ISNULL(THIS.PageNodes)
		                    lcNode =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+OUTPUTXML_OBJCODE_PAGES,;
		                                "Nodes","FrxNodes"), ;
		                              Nodes.ObjValue, ;
		                              OUTPUTXML_GOOFTAG)
		                    THIS.WriteRaw(THIS.PageNodes) 
		                    THIS.WriteRaw(THIS.XMLRawTag(m.lcNode))                  
		                ENDIF
		                IF NOT ISNULL(THIS.ColumnNodes)
		                   m.lcNode =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+OUTPUTXML_OBJCODE_COLS,;
		                                "Nodes","FrxNodes"), ;
		                              Nodes.ObjValue, ;
		                              OUTPUTXML_GOOFTAG)
		                    THIS.WriteRaw(THIS.ColumnNodes) 
		                    THIS.WriteRaw(THIS.XMLRawTag(m.lcNode))                                   
		                ENDIF
		             ENDIF          
		          
		             m.lcNode =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+OUTPUTXML_OBJCODE_DATA,;
		                           "Nodes","FrxNodes"), ;
		                         Nodes.ObjValue, ;
		                         OUTPUTXML_GOOFTAG)
		          
		             THIS.WriteRaw( THIS.XMLRawTag(m.lcNode))
		          ENDIF
		
		          m.lcNode =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_REPORTHEADER, ;
		                              "Nodes","ObjType"), ;
		                               Nodes.ObjValue, ;
		                               OUTPUTXML_GOOFTAG)               
		          THIS.WriteRaw( THIS.XMLRawTag(m.lcNode))    
		
		          * check to see if continuation... 
		          IF NOT OUTPUTXML_CONTINUATION
		             THIS.WriteRaw( THIS.XMLRawTag(THIS.CurrentDocument))          
		          ENDIF
		   
		      #ELIF OUTPUTXML = OUTPUTXML_DOTNET     
		         * XMLTextWriter work
		      #ELSE
		          * domwork here        
		          IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION
		             * currently all this is done on entry, but
		             * if not:
		             * append the pages collection
		             * into the report data node --
		             * that's where we should be right now
		             * THIS.DataNodes.AppendChild(THIS.PageNodes)
		             ** IF NOT ISNULL(THIS.ColumnNodes)
		             * THIS.DataNodes.AppendChild(THIS.ColumnNodes)             
		             ** ENDIF
		          ENDIF
		          
		          IF NOT OUTPUTXML_CONTINUATION
		             THIS.CurrentDocument.Save(THIS.TargetFileName)
		          ENDIF
		
		       #ENDIF
		   ENDIF
		   
		   IF OUTPUTXML_CONTINUATION
		      THIS.ResetReport()   
		   ELSE
		      THIS.ResetDocument()         
		      THIS.ApplyUserTransformToOutput()
		      IF THIS.ApplyUserTransform AND (NOT THIS.HadError)
		         * we suppressed this message earlier
		            IF THIS.DoMessage( OUTPUTCLASS_SUCCESS_LOC + ;
		                            IIF(SYS(2024)="Y",CHR(13)+OUTPUTCLASS_REPORT_INCOMPLETE_LOC,""),;
		                            MB_ICONINFORMATION + MB_YESNO ) = IDYES
		               _CLIPTEXT = THIS.TargetFileName
		            ENDIF
		      ENDIF   
		   ENDIF
		
		   THIS.resetDataSession()   
		
		DODEFAULT()
	ENDPROC

	PROTECTED PROCEDURE applyusertransformtooutput		&& Applies a user-specified XSLT to XML output at the conclusion of a report run.
		DO CASE
		CASE THIS.ApplyUserTransform AND NOT ;
		   (ISNULL(THIS.XSLTProcessorUser))
		
		   LOCAL m.lcResult
		
		   *  THIS.SaveTargetFileName is real
		   * THIS.TargetFileName is TMP
		   m.lcResult = THIS.ApplyXSLT(FILETOSTR(THIS.TargetFileName),THIS.XSLTProcessorUser, THIS.XSLTParameters)
		   IF FILE(THIS.SaveTargetFileName)
		      ERASE (THIS.SaveTargetFileName) NORECYCLE
		   ENDIF
		   STRTOFILE(m.lcResult,THIS.SaveTargetFileName)
		   ERASE (THIS.TargetFileName) NORECYCLE
		   THIS.TargetFileName = THIS.SaveTargetFileName
		   RETURN .T.
		CASE THIS.ApplyUserTransform && no processor
		   IF FILE(THIS.SaveTargetFileName)
		      ERASE (THIS.SaveTargetFileName) NORECYCLE
		   ENDIF
		   COPY FILE (THIS.TargetFileName) TO (THIS.SaveTargetFileName)
		   ERASE (THIS.TargetFileName) NORECYCLE
		   THIS.TargetFileName = THIS.SaveTargetFileName
		   RETURN .F.  
		OTHERWISE
		   RETURN .F. 
		ENDCASE
		
		
		
	ENDPROC

	PROCEDURE applyusertransform_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L" AND NOT THIS.IsRunning
		   THIS.ApplyUserTransform = m.vNewVal
		   IF THIS.ApplyUserTransform AND ;
		      (ISNULL(THIS.XSLTProcessorUser))
		      THIS.GetDefaultUserXSLT()
		   ENDIF   
		ENDIF   
		
	ENDPROC

	PROCEDURE applyxslt		&& Provides generic facilities for applying XSLT to XML.
		LPARAMETERS m.tvSource, m.tvProcessor, m.tvParamCollection
		
		LOCAL m.loSource, m.loProcessor, m.lvReturn, m.llSuccess, m.liParam
		
		m.lvReturn = ""
		
		STORE NULL TO m.loSource, m.loProcessor
		
		IF VARTYPE(m.tvSource) = "C" 
		
		   * first param can be filename, string, or object
		   * if filename or string, test existance
		   * and try to load as a dom object
		   DO CASE
		   CASE FILE(m.tvSource)
		      m.loSource = CREATEOBJECT(OUTPUTXML_DOMDOCUMENTOBJECT)
		      m.loSource.Load(m.tvSource)
		   CASE LEN(m.tvSource) > 0
		      m.loSource = CREATEOBJECT(OUTPUTXML_DOMDOCUMENTOBJECT)
		      m.loSource.LoadXML(m.tvSource)
		   ENDCASE
		  
		   IF NOT ISNULL(m.loSource) AND ;
		      LEN(m.loSource.XML) > 0 AND ;
		      EMPTY(m.loSource.parseError.reason)
		      m.llSuccess = .T.
		   ELSE
		      m.loSource = NULL
		*      IF NOT ISNULL(m.loSource)
		*         THIS.LastErrorMessage = loSource.parseError.reason
		*      ENDIF   
		   ENDIF
		ELSE
		   * if object, test nodetypestring availability
		   * and then for document/tree shape.
		   IF VARTYPE(m.tvSource) = "O"
		      TRY 
		         IF INLIST("|"+UPPER(m.tvSource.nodeTypeString)+"|", ;
		                       "|DOCUMENT|","|ELEMENT|") && quick and dirty test for tree shape
		            m.loSource = m.tvSource           
		            m.llSuccess = .T.
		         ENDIF
		      ENDTRY
		   ENDIF   
		
		ENDIF
		
		IF m.llSuccess 
		
		   * for failed transformations, return source XML
		   m.lvReturn = m.loSource.XML
		   
		   * as above
		   * second param can be filename or object
		 
		   * if filename, as above
		   *  if object, test for appropriate interface
		   * figure out if it's a processor factory
		   * or an instance (either dom or stylesheet)
		   *  and error out if we can't figure it out
		   m.llSuccess = .F.
		   IF VARTYPE(m.tvProcessor) = "C"    
		      m.loProcessor = THIS.LoadProcessorObject(m.tvProcessor)
		      IF NOT ISNULL(m.loProcessor)
		         m.loProcessor = m.loProcessor.createProcessor()      
		      ENDIF
		   ELSE
		      IF VARTYPE(m.tvProcessor) = "O" 
		         TRY
		            IF VARTYPE(m.tvProcessor.styleSheet) = "O"
		               m.loProcessor = m.tvProcessor
		               * if the object was a processor object
		               * get a stylesheet instance
		               m.loProcessor = m.loProcessor.createProcessor()
		            ENDIF   
		         CATCH
		            * just want to swallow the errors here because
		            * of the primitive tests being used
		         ENDTRY
		      ENDIF
		   ENDIF
		  
		   IF NOT ISNULL(m.loProcessor) 
		      m.llSuccess = .T.
		   ENDIF
		ENDIF
		
		IF m.llSuccess
		
		   * param collection should be added to the 
		   * instance at this point
		   
		   WITH m.loProcessor
		      IF VARTYPE(m.tvParamCollection) = "O" AND ;
		         UPPER(m.tvParamCollection.BaseClass) == "COLLECTION" AND ;
		         m.tvParamCollection.Count > 0
		
		         FOR m.liParam = 1 TO m.tvParamCollection.Count
		            .AddParameter(m.tvParamCollection.GetKey(m.liParam), ;
		                         m.tvParamCollection.Item(m.liParam))
		         ENDFOR
		      ENDIF   
		      .input = m.loSource
		      .transform()
		      m.lvReturn = .output
		   ENDWITH
		
		ENDIF
		
		
		RETURN m.lvReturn
	ENDPROC

	PROCEDURE BeforeBand
		LPARAMETERS m.nBandObjCode, m.nFRXRecNo, m.tlContinuedBand
		
		DODEFAULT(m.nBandObjCode, m.nFRXRecNo)
		
		IF INLIST(m.nBandObjCode,FRX_OBJCOD_PAGEHEADER, FRX_OBJCOD_TITLE,FRX_OBJCOD_SUMMARY) 
		   THIS.includePage =  THIS.IncludePageInOutput(_PAGENO)
		   *(THIS.PageNo >= THIS.CommandClauses.RangeFrom) AND ;
		   *    ((THIS.CommandClauses.RangeTo = -1) OR (THIS.PageNo <= THIS.CommandClauses.RangeTo))
		   * possibly to be adapted later:
		   * regardless of whether IncludePageInOutput() is used
		   * or the manual evaluation above (commented) is used,
		   * _PAGENO will work for continued reports only if NORESET is not used.
		   * THIS.PageNo/THIS.SharedPageNo will not work whether NORESET is used or not,
		   * for continued reports, 
		   * unless you maintain a private offset.  RANGE is
		   * sensitive to the current REPORT FORM command, not the full
		   * NOPAGEEJECT (chained) run
		   
		ENDIF
		
		
		IF THIS.InvokeOnCurrentPass() AND ;
		  THIS.Targethandle > -1 
		  
		   LOCAL m.lcBand, m.loNode, m.lcID, m.lcIDRef, ;
		         m.llFormatBreakBand, m.lcInterruptedBand,;
		         m.llOmitBand, m.loObjects
		
		   THIS.SetFRXDataSession()   
		
		   GO m.nFRXRecNo IN FRX   
		
		   IF NOT THIS.IncludeBandsWithNoObjects 
		      m.loObjects = THIS.FRXCursor.GetObjectsInBand(FRX.UniqueID)
		      IF m.loObjects.Count = 0
		          m.llOmitBand = .T.
		      ENDIF
		      m.loObjects = NULL
		   ENDIF   
		      
		   m.lcBand =  IIF(SEEK(OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND+ ;
		                    OUTPUTXML_OBJTYPE_BANDOFFSET+nBandObjCode,;
		                    "Nodes","FrxNodes"),;
		                    Nodes.ObjValue, ;
		                    OUTPUTXML_GOOFTAG)
		   THIS.SetCurrentDataSession()
		                    
		   m.llFormatBreakBand = INLIST(nBandObjCode,;
		                    FRX_OBJCOD_PAGEHEADER, ;
		                    FRX_OBJCOD_PAGEFOOTER, ;
		                    FRX_OBJCOD_COLHEADER, ;
		                    FRX_OBJCOD_COLFOOTER)                 
		                    
		   
		   IF m.llFormatBreakBand
		
		      m.lcIDRef =  TRANSFORM(m.nFRXRecNo) && TRANSFORM(IIF(EMPTY(FRX.UniqueID),"",FRX.UniqueID))
		      m.lcID = TRANSFORM(IIF(THIS.sharedPageNo = 0, THIS.PageNo, THIS.sharedPageNo))
		         
		   ELSE
		
		      m.lcID =  TRANSFORM(m.nFRXRecNo) && TRANSFORM(IIF(EMPTY(FRX.UniqueID),"",FRX.UniqueID) )
		      IF m.tlContinuedBand
		         m.lcID = m.lcID + "+"
		      ENDIF
		      m.lcIDRef = TRANSFORM(IIF(THIS.sharedPageNo = 0, THIS.PageNo, THIS.sharedPageNo))
		      
		   ENDIF
		
		   * first evaluate THIS.IncludeBreaksInData 
		   DO CASE
		   CASE m.llOmitBand
		      * do nothing -- TBD checked later.
		   CASE THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_INDATA OR ;
		        NOT m.llFormatBreakBand   
		
		      #IF OUTPUTXML = OUTPUTXML_RAW
		         IF NOT (ISNULL(THIS.CurrentBand) OR EMPTY(THIS.CurrentBand))
		             * a data band has spanned
		             * formatting breaks (pages or columns)
		             * and we haven't otherwise caught it.
		             * This should not happen.
		             m.lcInterruptedBand = SUBSTR(ALLTR(THIS.CurrentBand),2,AT(" ", THIS.CurrentBand)-2)
		             * write a closing tag
		             THIS.WriteRaw(THIS.CurrentBand + THIS.XMLRawTag(m.lcInterruptedBand))
		         ENDIF
		         
		      
		        THIS.CurrentBand =  THIS.XMLRawTag( m.lcBand,.T., m.lcID, m.lcIDRef ) 
		               
		      
		      #ELIF OUTPUTXML = OUTPUTXML_DOTNET                       
		  
		      #ELSE
		
		          m.loNode = THIS.CurrentDocument.CreateElement(m.lcBand)
		          m.loNode.SetAttribute(THIS.idAttribute,m.lcID)
		          m.loNode.SetAttribute(THIS.idrefAttribute,m.lcIDRef)
		          THIS.DataNodes.AppendChild(m.loNode)
		          THIS.CurrentBand = m.loNode                                    
		          m.loNull = NULL
		      #ENDIF
		
		   CASE THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION
		      * build the collection which will be inserted into the
		      * data before finishing.
		      * but the band output at this point is .F.
		      #IF OUTPUTXML = OUTPUTXML_RAW
		          IF INLIST( m.nBandObjCode, ;
		                 FRX_OBJCOD_PAGEHEADER, ;
		                 FRX_OBJCOD_PAGEFOOTER) 
		             THIS.CurrentPage = NVL(THIS.CurrentPage,"")
		             THIS.CurrentPage = THIS.CurrentPage + ;
		                                THIS.XMLRawTag( m.lcBand, .T.,m.lcID, m.lcIDRef ) 
		          ELSE
		             THIS.CurrentColumn = NVL(THIS.CurrentColumn,"")
		             THIS.CurrentColumn = THIS.CurrentColumn + ;
		                                  THIS.XMLRawTag( m.lcBand, .T., m.lcID, m.lcIDRef ) 
		          ENDIF
		          
		         
		      #ELIF OUTPUTXML = OUTPUTXML_DOTNET 
		      
		      #ELSE
		      
		          * we leave THIS.CurrentBand alone in this case,
		          * to use after the band has finished.
		          IF INLIST( m.nBandObjCode, ;
		                 FRX_OBJCOD_PAGEHEADER, ;
		                 FRX_OBJCOD_PAGEFOOTER) 
		             THIS.CurrentPage = THIS.CurrentDocument.CreateElement(m.lcBand)
		             THIS.CurrentPage.SetAttribute(THIS.idAttribute,m.lcID)
		             THIS.CurrentPage.SetAttribute(THIS.idrefAttribute,m.lcIDRef)
		             THIS.PageNodes.AppendChild(THIS.CurrentPage)
		          ELSE
		             THIS.CurrentColumn = THIS.CurrentDocument.CreateElement(m.lcBand)
		             THIS.CurrentColumn.SetAttribute(THIS.idAttribute,m.lcID)
		             THIS.CurrentColumn.SetAttribute(THIS.idrefAttribute,m.lcIDRef)
		             THIS.ColumnNodes.AppendChild(THIS.CurrentColumn)
		          ENDIF
		          
		      #ENDIF
		   CASE INLIST( m.nBandObjCode, ;
		                 FRX_OBJCOD_COLHEADER, ;
		                 FRX_OBJCOD_COLFOOTER)   && XMLBREAKS_NONE and column band
		   
		      THIS.CurrentColumn = "X"
		
		   OTHERWISE  && XMLBREAKS_NONE and page band
		
		      THIS.CurrentPage = "X"
		   
		   ENDCASE
		   
		   THIS.resetDataSession()
		   
		ENDIF
		
		
		
	ENDPROC

	PROCEDURE BeforeReport
		DODEFAULT()
		
		IF (NOT THIS.HadError) 
		   THIS.SetFRXDataSession()
		   IF USED("FRX")
		      LOCAL m.liSelect, m.lcDocument, m.lcReport, ;
		            m.lcRDL, m.lcPage, m.lcCol, m.lcData, m.loNode, m.loParent
		      m.liSelect = SELECT()
		      SELECT FRX
		
		      IF THIS.TargetHandle = -1 AND THIS.ApplyUserTransform
		         THIS.verifyTargetFile()      
		         IF EMPTY(JUSTEXT(THIS.TargetFileName))
		            THIS.TargetFileName = FORCEEXT(THIS.TargetFileName,THIS.TargetFileExt)
		         ENDIF   
		         THIS.AddProperty("SaveTargetFileName",THIS.TargetFileName)
		         THIS.TargetFileName = FORCEEXT(THIS.TargetFileName,"TMP")
		      ENDIF
		         
		      IF (THIS.TargetHandle > -1 OR THIS.OpenTargetFile())  
		         IF NOT USED("Nodes")
		            IF  UPPER(FULLPATH(THIS.ConfigurationTable)) == ;
		                UPPER(FULLPATH(FORCEEXT(OUTPUTCLASS_INTERNALDBF,"DBF")))
		                USE (THIS.ConfigurationTable) AGAIN IN 0  ;
		                   NOUPDATE ALIAS Nodes SHARED
		            ELSE       
		                USE (THIS.ConfigurationTable) AGAIN IN 0  ;
		                   ALIAS Nodes SHARED
		                THIS.VerifyNodeNames()
		                THIs.VerifyAttributeNames()
		            ENDIF
		         ENDIF
		            
		         * create helper object
		         * create band and object cursors   
		         * we may want to evaluate raw mode
		         * as well as THIS.XMLMode to see if these are needed:
		         IF (NOT (THIS.IncludeBandsWithNoObjects AND  ;
		            THIS.XMLMode = OUTPUTXML_DATA_ONLY) ) && OR OUTPUTXML_PERFORMLOCALECONVERSION 
		            THIS.LoadFRXCursor = .T.               
		            IF ISNULL(THIS.FRXCursor) OR ;
		               (NOT THIS.FRXCursor.CreateObjectCursor("FRX", "OBJECTS", .F., .T.)) && force the load and make sure
		                                                         && we have access to runtime
		                                                         && version of the cursor
		               THIS.IncludeBandsWithNoObjects = .T.
		            ELSE
		               SELECT Bands                  
		               IF TAGNO("UniqueID") = 0
		                  INDEX ON UniqueID TAG UniqueID
		               ENDIF
		               SET ORDER TO 0                  
		               SELECT Objects
		               IF TAGNO("UniqueID") = 0
		                  INDEX ON UniqueID TAG UniqueID
		               ENDIF
		               SET ORDER TO 0
		            ENDIF   
		         ENDIF   
		         
		         THIS.IsRunning = .T.               
		         SET ORDER TO 0 IN FRX            
		         m.lcDocument =  IIF(SEEK(OUTPUTXML_OBJTYPE_NODES+ ;
		                              OUTPUTXML_OBJCODE_DOC,;
		                              "Nodes","FrxNodes"), ;
		                              Nodes.ObjValue, ;
		                            OUTPUTXML_GOOFTAG)
		         m.lcReport =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_REPORTHEADER, ;
		                            "Nodes","ObjType"), ;
		                             Nodes.ObjValue, ;
		                             OUTPUTXML_GOOFTAG)               
		  
		         IF NOT THIS.XMLMode = OUTPUTXML_RDL_ONLY 
		            m.lcData =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+ ;
		                                 OUTPUTXML_OBJCODE_DATA, ;
		                                "Nodes","FrxNodes"), ;
		                                 Nodes.ObjValue, ;
		                                 OUTPUTXML_GOOFTAG)
		         ENDIF
		         IF NOT THIS.XMLMode = OUTPUTXML_DATA_ONLY 
		            m.lcRDL = IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+ ;
		                             OUTPUTXML_OBJCODE_RDL, ;
		                             "Nodes","FrxNodes"), ;
		                             Nodes.ObjValue, ;
		                             OUTPUTXML_GOOFTAG)
		         ENDIF
		         IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION   
		            m.lcPage =  IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+ ;
		                                OUTPUTXML_OBJCODE_PAGES, ;
		                                "Nodes","FrxNodes"), ;
		                                Nodes.ObjValue, ;
		                                OUTPUTXML_GOOFTAG)
		   *        GO TOP IN FRX
		   *        IF FRX.VPos > 1
		            m.lcCol = IIF(SEEK( OUTPUTXML_OBJTYPE_NODES+;
		                              OUTPUTXML_OBJCODE_COLS,;
		                              "Nodes","FrxNodes"), ;
		                               Nodes.ObjValue, ;
		                              OUTPUTXML_GOOFTAG)
		   
		    *       ENDIF
		         ENDIF                    
		         #IF OUTPUTXML = OUTPUTXML_RAW
		             IF EMPTY(THIS.CurrentDocument) OR ISNULL(THIS.CurrentDocument)
		                THIS.CurrentDocument = m.lcDocument
		                THIS.WriteRaw( ;
		                     THIS.XMLRawTag( THIS.CurrentDocument,.T.) )   
		             ENDIF
		             THIS.WriteRaw( ;
		                  THIS.XMLRawTag( m.lcReport,.T.) )   
		             * could add FRXname as ID here                
		             IF NOT THIS.XMLMode = OUTPUTXML_DATA_ONLY    
		                * write RDL here
		                THIS.WriteRaw( ;
		                     THIS.XMLRawTag( m.lcRDL,.T.,THIS.xmlRawConv( THIS.CommandClauses.FILE)) )  
		                THIS.WriteRaw( STRCONV(THIS.GetVFPRDLContents(m.lcRDL, .T.),STRCONV_DBCS_UTF8)  )
		                THIS.WriteRaw( ;
		                     THIS.XMLRawTag( m.lcRDL) )   
		             ENDIF
		             IF NOT THIS.XMLMode = OUTPUTXML_RDL_ONLY           
		                THIS.WriteRaw( ;
		                 THIS.XMLRawTag( m.lcData,.T.) )   
		                      
		                IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION   
		                   THIS.PageNodes =  THIS.XMLRawTag( m.lcPage,.T.)  
		 *                 IF NOT EMPTY(lcCol)
		                      THIS.ColumnNodes = THIS.XMLRawTag(m.lcCol,.T.)
		 *                 ENDIF
		                ENDIF
		              ENDIF
		                     
		         #ELIF OUTPUTXML = OUTPUTXML_DOTNET
		             * XMLTextWriter work  
		         #ELSE
		             IF VARTYPE(THIS.CurrentDocument) # "O"
		                THIS.CurrentDocument = CREATEOBJECT(OUTPUTXML_DOMFREETHREADED_DOCUMENTOBJECT)
		                * COMPROP(THIS.CurrentDocument,"UTF8",1)                                
		                THIS.CurrentDocument.DocumentElement = THIS.CurrentDocument.CreateElement(m.lcDocument)
		             ENDIF
		             loNode = THIS.CurrentDocument.CreateElement(m.lcReport)                  
		             * setattribute id using FRXName here                
		             * idref?
		             THIS.CurrentDocument.DocumentElement.AppendChild(m.loNode)
		             m.loParent = loNode             
		             IF NOT THIS.XMLMode = OUTPUTXML_DATA_ONLY    
		                m.loNode = THIS.CurrentDocument.CreateElement(m.lcRDL)
		                m.loNode.SetAttribute(THIS.idAttribute,THIS.CommandClauses.FILE)
		                m.loNode.AppendChild(THIS.GetVFPRDLContents(m.lcRDL))
		                m.loParent.AppendChild(m.loNode)
		             ENDIF
		             IF NOT THIS.XMLMode = OUTPUTXML_RDL_ONLY                        
		                m.loNode = THIS.CurrentDocument.CreateElement(m.lcData)
		                * possibly add DE stuff here                
		                m.loParent.AppendChild(m.loNode)
		                THIS.DataNodes = m.loNode
		                IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION   
		                   THIS.PageNodes = THIS.CurrentDocument.CreateElement(m.lcPage)
		                   THIS.DataNodes.AppendChild(THIS.PageNodes)
		                   THIS.ColumnNodes = THIS.CurrentDocument.CreateElement(m.lcCol)
		                   THIS.DataNodes.AppendChild(THIS.ColumnNodes)                   
		                ENDIF
		             ENDIF
		         #ENDIF
		         IF THIS.XMLMode = OUTPUTXML_RDL_ONLY
		            THIS.AfterReport()
		         ENDIF
		      ENDIF
		          
		      STORE NULL TO m.loNode, m.loParent
		      SELECT (m.liSelect)
		   ELSE
		      THIS.DoMessage(OUTPUTXML_FRXMISSING_LOC,MB_ICONSTOP )
		   ENDIF   
		   THIS.resetDataSession()
		ENDIF
		
		RETURN 
	ENDPROC

	PROCEDURE closetargetfile
		LOCAL m.llResetQuietMode
		m.llResetQuietMode = ((NOT THIS.HadError) AND (NOT THIS.QuietMode) AND (THIS.ApplyUserTransform))
		IF m.llResetQuietMode
		   THIS.QuietMode = .T.
		ENDIF
		DODEFAULT()
		IF m.llResetQuietMode
		   THIS.QuietMode = .F.
		ENDIF      
	ENDPROC

	PROCEDURE contattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.contAttr = m.vNewVal
		ENDIF   
		
		
		
	ENDPROC

	PROCEDURE createconfigtable
		LPARAMETERS m.tcDBF, m.tlOverWrite
		* table is being created from scratch,
		* may be in a VCX in an unknown environment
		* (definitely not in REPORTOUTPUT.APP!)
		
		DODEFAULT(m.tcDBF, m.tlOverWrite)
		
		IF NOT THIS.HadError
		   LOCAL m.liSelect, m.llSafetyOn
		   m.llSafetyOn = (SET("SAFETY") = "ON")
		   SET SAFETY OFF
		   m.liSelect = SELECT()
		   SELECT 0
		   USE (m.tcDBF) EXCLU  
		   INDEX ON ObjType+ObjCode+ ;
		      IIF(ObjType=FRX_OBJTYP_BAND+OUTPUTXML_OBJTYPE_NODES, ;
		          OUTPUTXML_OBJTYPE_BANDOFFSET,0) ;
		      TAG FRXNodes  
		   IF m.llSafetyOn
		      SET SAFETY OFF
		   ENDIF   
		   THIS.InsertXMLConfigRecords()   
		   USE
		   SELECT (m.liSelect)
		ENDIF   
		
	ENDPROC

	PROCEDURE currentdocument_assign
		LPARAMETERS m.vNewVal
		* TBD: evaluate for readonly status during the life of the report run
		THIS.currentdocument = m.vNewVal
		
	ENDPROC

	PROCEDURE Destroy
		THIS.ResetDocument()
		STORE NULL TO ;
		   THIS.ColumnNodes, ;
		   THIS.CurrentBand, ;
		   THIS.CurrentColumn, ;
		   THIS.CurrentDocument, ;
		   THIS.CurrentPage, ;
		   THIS.DataNodes, ;
		   THIS.pageNodes, ;
		   THIS.XSLTProcessorRDL, ;
		   THIS.XSLTProcessorUser, ;
		   THIS.xsltParameters 
		   
		DODEFAULT()
	ENDPROC

	PROCEDURE Error
		LPARAMETERS m.nError, m.cMethod, m.nLine
		
		   DODEFAULT(m.nError,m.cMethod,m.nLine)
		   * we could evaluate errors first, but generally,
		   THIS.CloseTargetFile()
		   THIS.QuietMode = .T.
		   THIS.CancelReport()
		
		
	ENDPROC

	PROCEDURE frxcursor_access
		DODEFAULT()
		IF (NOT THIS.IsRunning) AND ;
		    ISNULL(THIS.FRXCursor) AND THIS.LoadFRXCursor
		    * don't attempt to load later
		    THIS.LoadFRXCursor = .F.               
		ENDIF
		RETURN THIS.FRXCursor    
	ENDPROC

	PROTECTED PROCEDURE getdefaultuserxslt		&& Provides a hook for subclasses to supply their preferred XSLT document for use when XMLListener automatically applies XSLT at the end of a report run.
		** this is an abstract method for use by subclasses
	ENDPROC

	PROTECTED PROCEDURE getfrxlayoutobjectfieldlist		&& Provides fields list to the getVFPRdlContents method, in a suitable comma-delimited format for inclusion as a macro in SQL SELECT statement from FRX cursor and related Bands and Objects cursors.
		LPARAMETERS m.tcAlias
		* frx
		* PLATFORM,UNIQUEID,TIMESTAMP,OBJTYPE,OBJCODE,NAME,EXPR,VPOS,HPOS,HEIGHT,WIDTH,STYLE,
		* PICTURE,ORDER,UNIQUE,COMMENT,ENVIRON,BOXCHAR,FILLCHAR,TAG,TAG2,PENRED,PENGREEN,PENBLUE,
		* FILLRED,FILLGREEN,FILLBLUE,PENSIZE,PENPAT,FILLPAT,FONTFACE,FONTSTYLE,FONTSIZE,MODE,RULER,
		* RULERLINES,GRID,GRIDV,GRIDH,FLOAT,STRETCH,STRETCHTOP,TOP,BOTTOM,SUPTYPE,SUPREST,NOREPEAT,RESETRPT,PAGEBREAK,COLBREAK,RESETPAGE,GENERAL,SPACING,DOUBLE,SWAPHEADER,SWAPFOOTER,EJECTBEFOR,EJECTAFTER,PLAIN,SUMMARY,ADDALIAS,OFFSET,TOPMARGIN,BOTMARGIN,TOTALTYPE,RESETTOTAL,RESOID,CURPOS,SUPALWAYS,SUPOVFLOW,SUPRPCOL,SUPGROUP,SUPVALCHNG,SUPEXPR,USER
		* objects
		* UNIQUEID,OBJTYPE,OBJCODE,EXPR,VPOS,HPOS,HEIGHT,WIDTH,OBJNAME,LOCALE_ID,START_BAND_ID,BAND_OFFSET,END_BAND_ID,BANDLABEL,SELECTED,OBJ_PICT,BAND_SEQ
		* bands
		* UNIQUEID,OBJTYPE,OBJCODE,EXPR,BANDLABEL,START,STOP,HEIGHT,P_START,P_STOP,P_HEIGHT,RESETTOTAL,BAND_SEQ,REL_BAND_ID
		
		
		
		RETURN ;
		       "RECNO() AS FrxRecno, "+m.tcAlias+".PLATFORM, "+m.tcAlias+".NAME,"+m.tcAlias+".EXPR,"+m.tcAlias+".OFFSET,"+m.tcAlias+".VPOS,"+m.tcAlias+".HPOS,"+m.tcAlias+".HEIGHT,"+;
		       ""+m.tcAlias+".OBJTYPE, "+m.tcAlias+".TAG, "+m.tcAlias+".TAG2,"+m.tcAlias+".PENSIZE,"+m.tcAlias+".PENPAT,"+m.tcAlias+".FILLPAT,"+;
		       ""+m.tcAlias+".WIDTH,"+m.tcAlias+".STYLE,"+m.tcAlias+".PICTURE,"+m.tcAlias+".ORDER,"+m.tcAlias+".COMMENT,"+m.tcAlias+".FILLCHAR,"+;       
		       ""+m.tcAlias+".PENRED,"+m.tcAlias+".PENGREEN,"+m.tcAlias+".PENBLUE,"+m.tcAlias+".FILLRED,"+m.tcAlias+".FILLGREEN,"+m.tcAlias+".FILLBLUE,"+;
		       ""+m.tcAlias+".FONTFACE, "+m.tcAlias+".FONTSTYLE,"+m.tcAlias+".FONTSIZE,"+m.tcAlias+".MODE,"+m.tcAlias+".FLOAT,"+m.tcAlias+".STRETCH,"+m.tcAlias+".STRETCHTOP,"+; 
		       "BITTEST( "+m.tcAlias+".FONTSTYLE, 0 ) AS FontBold,"+ ;
		       "BITTEST( "+m.tcAlias+".FONTSTYLE, 1 ) AS FontItalic,"+ ;
		       "BITTEST( "+m.tcAlias+".FONTSTYLE, 3 ) AS FontUnderline,"+ ;
		       "BITTEST( "+m.tcAlias+".FONTSTYLE, 7 ) AS FontStrikeThrough,"+ ;
		       "THIS.GetPathedImageInfo("+m.tcAlias+".ObjType, "+m.tcAlias+".Name, "+m.tcAlias+".Picture, "+m.tcAlias+".Offset) AS UnpathedImg,"+ ;       
		       "THIS.GetPathedImageInfo("+m.tcAlias+".ObjType, "+m.tcAlias+".Name, "+m.tcAlias+".Picture, "+m.tcAlias+".Offset, .T.) AS PathedImg,"+ ;
		       ""+m.tcAlias+".TOP,"+m.tcAlias+".BOTTOM,"+m.tcAlias+".NOREPEAT,"+m.tcAlias+".PAGEBREAK,"+m.tcAlias+".COLBREAK,"+m.tcAlias+".RESETPAGE,"+m.tcAlias+".GENERAL,"+m.tcAlias+".SPACING,"+ ;
		       ""+m.tcAlias+".SWAPHEADER,"+m.tcAlias+".SWAPFOOTER,"+m.tcAlias+".EJECTBEFOR,"+m.tcAlias+".EJECTAFTER,"+m.tcAlias+".TOTALTYPE,"+m.tcAlias+".RESETTOTAL,"+ ;
		       "IIF("+m.tcAlias+".DOUBLE,"+m.tcAlias+".RESOID,1) AS FONTCHARSET,"+m.tcAlias+".SUPALWAYS,"+m.tcAlias+".SUPOVFLOW,"+m.tcAlias+".SUPRPCOL,"+m.tcAlias+".SUPGROUP,"+m.tcAlias+".SUPVALCHNG,"+m.tcAlias+".SUPEXPR,"+m.tcAlias+".USER,"+ ;
		       "OBJECTS.UniqueID AS ObjID, OBJECTS.ObjName, Objects.Locale_ID,"+ ;
		       "OBJECTS.START_BAND_ID,OBJECTS.BAND_OFFSET,OBJECTS.END_BAND_ID,"+ ;
		       "BANDS.UNIQUEID AS BandID,BANDS.OBJCODE AS BandType,Bands.BANDLABEL,Bands.START,"+;
		       "Bands.STOP,Bands.BAND_SEQ,Bands.REL_BAND_ID, ("+m.tcAlias+".ObjType=9 AND (NOT "+m.tcAlias+".Plain)) AS BandStretch"
	ENDPROC

	PROTECTED PROCEDURE getpathedimageinfo		&& Supplies a fully-pathed filename for an image file included in the report, using the original relative-path information stored in the FRX.
		LPARAMETERS m.tObjType, m.tName, m.tPicture, m.tOffset, m.tPathed
		LOCAL m.lcReturn, m.lcFile
		m.lcReturn = ""
		IF m.tObjType =  FRX_OBJTYP_PICTURE  
		   DO CASE
		   CASE m.tOffset = 0 && literal filename
		      m.lcReturn = STRTRAN(m.tPicture,["],[])
		      IF m.tPathed
		         m.lcReturn = FULLPATH(m.tPicture,THIS.CommandClauses.File)      
		      ELSE
		         m.lcReturn = JUSTFNAME(m.tPicture)
		      ENDIF
		   CASE m.tOffset = 1 && general field
		      m.lcReturn = "["+m.tName+"]"
		   CASE m.tOffset = 2 AND TYPE(m.tName)= "O" && imagecontrol
		      m.lcReturn = "["+m.tName+"]"   
		   CASE m.tOffset = 2 AND TYPE(m.tName) = "C" && expression
		      m.lcFile = EVALUATE(m.tName)
		      IF NOT FILE(m.lcFile)
		         m.lcFile = EVALUATE(STRTRAN(m.tName,"()","")) && indirect
		      ENDIF
		      IF FILE(m.lcFile)
		         IF m.tPathed
		            m.lcReturn = FULLPATH(EVALUATE(m.tName))
		         ELSE
		            m.lcReturn = JUSTFNAME(EVALUATE(m.tName))
		         ENDIF
		      ELSE
		         m.lcReturn =  "["+m.tName+"]" 
		      ENDIF
		   OTHERWISE
		      m.lcReturn = "["+m.tName+"]"
		   ENDCASE
		ENDIF
		m.lcReturn = PADR(CHRTRAN(m.lcReturn,"\","/"), OUTPUTXML_CHARFIELD_LIMIT)
		
		RETURN m.lcReturn
		
		
	ENDPROC

	PROTECTED PROCEDURE getrawformattinginfo		&& Formulates the appropriate formatting attributes for an element when XMLListener is streaming XML to a file.
		LPARAMETERS m.tnLeft, m.tnTop, m.tnWidth, m.tnHeight, m.tnObjectContinuationType
		LOCAL m.lcInfo
		
		m.lcInfo = ""
		
		m.lcInfo = m.lcInfo + " "+THIS.contAttr+"='"+TRANSFORM(m.tnObjectContinuationType)+"'"
		
		IF THIS.IncludeFormattingInLayoutObjects
		   m.lcInfo = m.lcInfo + " "+THIS.leftAttr+"='"+ TRANSFORM(m.tnLeft)+"'"
		   m.lcInfo = m.lcInfo + " "+THIS.topAttr+"='"+TRANSFORM(m.tnTop)+"'"
		   m.lcInfo = m.lcInfo + " "+THIS.widthAttr+"='"+TRANSFORM(m.tnWidth)+"'"      
		   m.lcInfo = m.lcInfo + " "+THIS.heightAttr+"='"+TRANSFORM(m.tnHeight)+"'"     
		ENDIF
		RETURN m.lcInfo
	ENDPROC

	PROTECTED PROCEDURE getvfprdlcontents		&& Translates report definition layout metadata, from the FRX and the current report run, into an XML format useful to other applications.
		LPARAMETERS m.tcNodeName, m.tlAsString
		* NB: no push/pop session here, don't bother
		* because we're switching back and forth
		* rapidly and not changing anything important
		LOCAL m.liSelectCurrent, m.liSelectFRX, m.liSession, ;
		      m.liFlds, m.liDBFS, m.liIndex1, m.liIndex2, laFlds[1], ;
		      laDBFS[1], laRels[1], m.lcAlias, m.lcKey, m.llDesc, ;
		      m.lcFilter, m.lcRel, m.liRels, m.lcSkip, m.lcResult, m.llWholePage
		LOCAL m.oXA, m.oXT1, m.oXT2, m.oXT3, m.oXT4, m.oXT5, m.oXML, m.oNode, m.oCommand
		m.liSession = SET("DATASESSION")
		SET DATASESSION TO THIS.FRXDataSession
		m.liSelectFRX = SELECT()
		IF THIS.IncludeDataSourcesInVFPRDL
		   CREATE CURSOR VFPDataSource (the_alias c(200), rpt_driver l, the_dbf m, the_order m, order_desc l, the_filter m, the_skip m )
		   CREATE CURSOR flds (the_alias c(200), the_field m, the_type c(1))
		   CREATE CURSOR rels (the_parent c(200), the_target c(200), the_expr m)
		   SELECT flds
		   INDEX ON the_alias TAG the_alias
		   SELECT rels
		   INDEX ON the_parent TAG the_alias
		   SELECT VFPDataSource
		   SET RELATION TO the_alias INTO flds, the_alias INTO rels
		   SET DATASESSION TO THIS.CurrentDataSession
		   m.liSelectCurrent = SELECT()
		   m.liDBFS = AUSED(laDBFS) 
		   FOR m.liIndex = 1 TO m.liDBFS
		      SET DATASESSION TO THIS.CurrentDataSession
		      m.lcAlias = laDBFs[m.liIndex,1]
		      SELECT (m.lcAlias)
		      m.lcDBF = DBF()
		      m.liFlds = AFIELDS(laFlds)
		      m.lcKey = SET("ORDER")
		      m.llDesc =  (" DESC" $ UPPER(m.lcKey))   
		      m.lcFilter = SET("FILTER")
		      m.lcSkip = SET("SKIP")
		      IF NOT EMPTY(m.lcKey)
		         m.lcKey = STRTRAN(UPPER(m.lcKey),"TAG","")
		         m.liIndex2 = ATC(" OF",m.lcKey)
		         IF m.liIndex2 > 0
		            m.lcKey = LEFT(m.lcKey,m.liIndex2)
		         ENDIF
		         m.lcKey = ALLTR(m.lcKey)
		         m.liTag = TAGNO(m.lcKey)
		         IF m.liTag > 0
		            m.lcKey = KEY(m.liTag)
		         ELSE
		            m.lcKey = ""
		         ENDIF
		      ENDIF    
		      m.liRels = 0
		      STORE "" TO laRels
		      DO WHILE .T.
		         m.lcRel = RELATION(m.liRels + 1)
		         IF EMPTY(m.lcRel)
		            EXIT
		         ELSE
		            m.liRels = m.liRels + 1
		            DIME laRels[m.liRels,3]
		            laRels[m.liRels,1] = TARGET(m.liRels)
		            laRels[m.liRels,2] = m.lcRel
		         ENDIF
		      ENDDO
		      SET DATASESSION TO THIS.FRXDataSession
		      INSERT INTO VFPDataSource VALUES (m.lcAlias, (UPPER(m.lcAlias)==UPPER(THIS.Drivingalias)), m.lcDBF, m.lcKey, m.llDesc, m.lcFilter, m.lcSkip)
		      FOR m.liIndex2 = 1 TO m.liFlds
		         INSERT INTO flds VALUES (m.lcAlias, laFlds[m.liIndex2,1], laFlds[m.liIndex2,2])
		      ENDFOR
		      FOR m.liIndex2 = 1 TO m.liRels
		         INSERT INTO rels VALUES (m.lcAlias, laRels[m.liIndex2,1], laRels[m.liIndex2,2])   
		      ENDFOR
		   ENDFOR
		   SET DATASESSION TO THIS.CurrentDataSession
		   SELECT (m.liSelectCurrent)
		ENDIF
		SET DATASESSION TO THIS.FRXdataSession
		m.lcAlias = THIS.prepareFrxCopy()
		m.lcResult = THIS.getFRXLayoutObjectFieldList(m.lcAlias)
		SELECT &lcResult ;
		   FROM (m.lcAlias) ;
		   LEFT JOIN Bands ON &lcAlias..UniqueID = Bands.UniqueID ;
		   LEFT JOIN Objects ON &lcAlias..UniqueID = Objects.UniqueID ;
		   WHERE NOT DELETED() ;
		   INTO CURSOR VFPFRXLayoutObject READWRITE
		THIS.removeFRXCopy(m.lcAlias)   
		SELECT VFPFRXLayoutObject
		* get rid of compiled data:
		IF TYPE("VFPFRXLayoutObject.Tag") # "U"
		   REPLACE Tag WITH "" ALL FOR NOT INLIST(ObjType,FRX_OBJTYP_VARIABLE,FRX_OBJTYP_BAND,FRX_OBJTYP_DATAENV ,FRX_OBJTYP_DATAOBJ)
		ENDIF
		IF TYPE("VFPFRXLayoutObject.Tag2") # "U"
		   REPLACE Tag2 WITH "" ALL FOR INLIST(ObjType,FRX_OBJTYP_REPORTHEADER,FRX_OBJTYP_DATAENV,FRX_OBJTYP_DATAOBJ)
		ENDIF
		IF TYPE("VFPFRXLayoutObject.Fontface") # "U"
		   REPLACE Fontface WITH ""  ALL FOR INLIST(ObjType,FRX_OBJTYP_DATAENV,FRX_OBJTYP_DATAOBJ)
		ENDIF   
		GO TOP IN VFPFRXLayoutObject
		m.llWholePage = VFPFRXLayoutObject.Top 
		SELECT Nodes.ObjValue AS Name, ;
		       Nodes.ObjType-OUTPUTXML_OBJTYPE_NODES AS Type, ;
		       Nodes.ObjCode AS Code, ;
		       Nodes.ObjInfo AS Info ;
		    FROM Nodes ;   
		    WHERE BETWEEN(ObjType,OUTPUTXML_OBJTYPE_NODES, OUTPUTXML_OBJTYPE_NODES+100) ;
		    AND NOT DELETED() ;
		    INTO CURSOR VFPFRXLayoutNode READWRITE
		m.liFlds = AMEMBERS(laFlds, THIS,0)
		FOR m.liIndex1 = 1 TO m.liFlds
		   IF ATC("attr",laFlds[m.liIndex1]) > 1
		      INSERT INTO VFPFRXLayoutNode VALUES ;
		        (TRANSFORM(EVALUATE("THIS."+laFlds[m.liIndex1])),;
		         0, ;
		         OUTPUTXML_OBJCODE_ATTRIBMEMBER,;
		         laFlds[m.liIndex1]+ " attribute nodename")
		   ENDIF
		ENDFOR   
		m.oXA=CREATEOBJECT("XMLAdapter")
		m.oXA.RespectCursorCP = .T.
		m.oXT4 = m.oXA.AddTableSchema("VFPFRXLayoutObject")
		m.oXT5 = m.oXA.AddTableSchema("VFPFRXLayoutNode")
		m.oXA.RespectNesting=.T.
		IF THIS.IncludeDataSourcesInVFPRDL
		   m.oXT1=oXA.AddTableSchema("VFPDataSource")
		   m.oXT2=oXA.AddTableSchema("flds")
		   m.oXT3=oXA.AddTableSchema("rels")
		   m.oXT1.Nest(m.oXT2)
		   m.oXT1.Nest(m.oXT3)
		ENDIF
		
		m.oXA.XMLSchemaLocation = ""
		m.oXA.ToXML("lcResult")
		#IF OUTPUTXML = OUTPUTXML_DOM
		   m.oXML = CREATEOBJECT(OUTPUTXML_DOMFREETHREADED_DOCUMENTOBJECT)
		#ELSE
		   m.oXML = CREATEOBJECT("Microsoft.XMLDOM")
		#ENDIF      
		m.oXML.LoadXML(m.lcResult)
		m.oNode = m.oXML.SelectSingleNode("/./*")
		IF NOT ISNULL(THIS.CommandClauses)
		   m.oCommand = m.oXML.createElement("VFPFRXCommand")
		   m.liFlds = AMEMBERS(laFlds, THIS.CommandClauses)   
		   FOR m.liIndex1 = 1 TO m.liFlds
		       m.lcKey = EVAL("THIS.CommandClauses."+laFlds[m.liIndex1]) 
		       IF VARTYPE(m.lcKey) = "L"
		          IF m.lcKey
		             m.lcKey = "true"
		          ELSE
		             m.lcKey = "false"
		          ENDIF
		       ENDIF
		       m.oCommand.SetAttribute(laFlds[m.liIndex1], TRANSFORM(m.lcKey))
		   ENDFOR
		   m.oCommand.SetAttribute("OutputType",TRANSFORM(THIS.OutputType))
		   m.oCommand.SetAttribute("appName",THIS.appName)
		   m.oCommand.SetAttribute("targetFileName",THIS.targetFileName)
		   m.oNode.appendChild(m.oCommand)
		ENDIF   
		m.oCommand =  oXML.createElement("VFPFRXPrintJob")
		m.oCommand.SetAttribute("pagewidth", THIS.SharedPageWidth)
		m.oCommand.SetAttribute("pageheight", THIS.SharedPageHeight)
		m.oCommand.SetAttribute("name",THIS.PrintJobName)
		m.oCommand.SetAttribute("pagedesign",IIF(llWholePage,"whole","printable"))
		TRY
		   * if PROMPT was used this will probably work
		   m.oCommand.SetAttribute("printresolution",TRANSFORM(PRTINFO(PRT_YRESOLUTION ,SET("PRINTER",3))))
		   #IF .F. 
		      * OUTPUTXML_RESOLUTIONFIX
		      * try to reset based on current printer FRX information
		      GO TOP IN FRX
		      SELECT FRX
		      m.liIndex = IIF(ALINES(laFlds,Picture,.T.) > 0, ASCAN(laFlds,"YRESOLUTION"),0) 
		      IF m.liIndex = 0
		         m.liIndex = IIF(ALINES(laFlds,Expr,.T.) > 0, ASCAN(laFlds,"YRESOLUTION"),0) 
		      ENDIF
		      IF m.liIndex > 0
		         m.liIndex = VAL(ALLTRIM(SUBSTR(laFlds[liIndex],AT("=",laFlds[m.liIndex])+1)))
		         IF m.liIndex > 0
		            m.oCommand.SetAttribute("printresolution",TRANSFORM(m.liIndex))         
		         ELSE
		            m.oCommand.SetAttribute("printresolution","-1")                  
		         ENDIF
		      ENDIF
		      IF USED("SetPrinter")
		         USE IN SetPrinter
		      ENDIF
		   #ENDIF    
		CATCH WHEN .T.
		   * this can happen when there is no printer
		   m.oCommand.SetAttribute("printresolution","-1") 
		ENDTRY   
		m.oNode.appendChild(m.oCommand)
		IF THIS.IncludeDataSourcesInVFPRDL
		   USE IN VFPDataSource
		   USE IN flds
		   USE IN rels
		ENDIF   
		USE IN VFPFRXLayoutObject
		USE IN VFPFRXLayoutNode
		STORE NULL TO m.oXA, m.oXT1, m.oXT2, m.oXT3, m.oXT4, m.oXT5, m.oXML, m.oCommand
		SELECT (m.liSelectFRX)
		SET DATASESSION TO (m.liSession)
		IF tlAsString
		   RETURN m.oNode.XML
		ELSE
		   RETURN m.oNode
		ENDIF
	ENDPROC

	PROCEDURE heightattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.heightAttr = m.vNewVal
		ENDIF   
		
		
		
	ENDPROC

	PROCEDURE idattribute_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.idattribute = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE idrefattribute_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.idrefattribute = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE includebandswithnoobjects_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L" AND NOT THIS.IsRunning 
		   THIS.IncludeBandsWithNoObjects = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE includebreaksindata_assign
		LPARAMETERS m.vNewVal
		* Readonly during report run
		
		IF VARTYPE(m.vNewVal) = "N" AND ;
		   INLIST(m.vNewVal, ;
		          OUTPUTXML_BREAKS_INDATA,;
		          OUTPUTXML_BREAKS_NONE, ;
		          OUTPUTXML_BREAKS_COLLECTION) AND ;
		   NOT THIS.IsRunning 
		   THIS.IncludeBreaksInData = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE includedatasourcesinvfprdl_assign
		LPARAMETERS m.vNewVal
		
		IF VARTYPE(m.vNewVal) = "L" 
		   THIS.IncludeDataSourcesinVFPRDL = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE includeformattinginlayoutobjects_assign
		LPARAMETERS m.vNewVal
		*TBD: evaluate whether
		* it's okay to do this during a run?
		
		IF VARTYPE(m.vNewVal) = "L"
		   THIS.IncludeFormattingInLayoutObjects = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE Init
		THIS.ReadConfiguration = OUTPUTCLASS_READCONFIG_INIT
		
		IF DODEFAULT()
		   THIS.AppName = OUTPUTXML_APPNAME_LOC
		   THIS.ResetDocument()
		ELSE
		   RETURN .F.   
		ENDIF
		
		IF THIS.ApplyUserTransform
		   THIS.GetDefaultUserXSLT()
		ENDIF   
		RETURN NOT THIS.HadError
	ENDPROC

	PROTECTED PROCEDURE insertxmlconfigrecords		&& Adds a record to the configuration table describing a default XML node when XML Listener is creating or editing a shared configuration table.
		* protected,
		* assumes it is being called with config
		* table already SELECTed.
		
		   DELETE FOR ;
		      BETWEEN(OBJTYPE,OUTPUTXML_OBJTYPE_NODES, OUTPUTXML_OBJTYPE_NODES + 100)
		
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_TITLE,'','Title','Title Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND,;
		      FRX_OBJCOD_PAGEHEADER,'','PH','Page Header Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND,;
		      FRX_OBJCOD_COLHEADER,'','CH','Column Header Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND,;
		      FRX_OBJCOD_GROUPHEADER,'','GH','Group Header Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_DETAIL,'','D','Detail Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_GROUPFOOTER,'','GF','Group Footer Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_COLFOOTER,'','CF','Column Footer Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_PAGEFOOTER,'','PF','Page Footer Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_SUMMARY,'','Summary','Summary Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_DETAILHEADER,'','DH','Detail Header Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_BAND, ;
		      FRX_OBJCOD_DETAILFOOTER,'','DF','Detail Footer Band nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_REPORTHEADER,;
		      FRX_OBJCOD_REPORTHEADER ,'','VFP-Report','Report root nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_LABEL, ;
		      FRX_OBJCOD_OTHER,'','T','Text object nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_FIELD, ;
		      FRX_OBJCOD_OTHER,'','E','Expression object nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_PICTURE,;
		      FRX_OBJCOD_OTHER,'','P','Picture object nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_RECTANGLE,;
		      FRX_OBJCOD_RECTANGLE,'','S','Shape object nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_LINE, ;
		      FRX_OBJCOD_OTHER,'','L','Line object nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_VARIABLE,;
		      FRX_OBJCOD_OTHER,'','V','Variable nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_FONTRES,;
		      FRX_OBJCOD_OTHER,'','FontRes','FontResource nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_DATAENV,;
		      FRX_OBJCOD_OTHER,'','DataEnv','DataEnvironment nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_DATAOBJ,;
		      FRX_OBJCOD_OTHER,'','DE-Cursor','DE-Cursor nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_DATAOBJ, ;
		      FRX_OBJCOD_OTHER+1,'','DE-Relation','DE-Relation nodename')
		     * offset the DE Relation because
		     * this information isn't in ObjType or ObjCode
		     * as distinct from DE-Cursor other than in the Name field
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_GROUP, ;
		      FRX_OBJCOD_OTHER,'','Group','Group selector nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES, ;
		     OUTPUTXML_OBJCODE_DOC,'','Reports','XML Document root nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES,;
		     OUTPUTXML_OBJCODE_DATA,'','Data','Report scope data root nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES, ;
		     OUTPUTXML_OBJCODE_RDL,'','VFP-RDL','RDL layout description root nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES, ;
		     OUTPUTXML_OBJCODE_PAGES,'','Pages','Pages collection root nodename')
		   INSERT INTO (ALIAS()) VALUES ;
		    (OUTPUTXML_OBJTYPE_NODES, ;
		     OUTPUTXML_OBJCODE_COLS,'','Columns','Column collection root nodename')
		
	ENDPROC

	PROCEDURE invokeoncurrentpass
		RETURN (THIS.includePage) AND ;
		       (NOT THIS.XMLMode = OUTPUTXML_RDL_ONLY ) AND ;
		       ((NOT THIS.TwoPassProcess) OR THIS.CurrentPass = LISTENER_FULLPASS)
		        
		
		
	ENDPROC

	PROCEDURE leftattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.leftAttr = m.vNewVal
		ENDIF   
		
		
	ENDPROC

	PROTECTED PROCEDURE loadprocessorobject		&& Provides a generic means to load an XSLT processor object from a string representing an XSLT document or a filename.
		LPARAMETERS m.tcVal
		
		LOCAL m.loReturn
		m.loReturn = NULL
		
		IF VARTYPE(m.tcVal) = "C" AND NOT EMPTY(m.tcVal)
		  LOCAL m.loProcessor, m.loStylesheet
		  m.loProcessor  = CREATEOBJECT(OUTPUTXML_XSLT_PROCESSOROBJECT)
		  m.loStyleSheet = CREATEOBJECT(OUTPUTXML_DOMFREETHREADED_DOCUMENTOBJECT)
		  IF FILE(m.tcVal)
		    m.loStyleSheet.Load(m.tcVal)
		  ELSE
		    * try to load it as a string
		    m.loStyleSheet.LoadXML(m.tcVal)
		  ENDIF
		  IF LEN(m.loStyleSheet.XML) > 0 AND ;
		     EMPTY(m.loStyleSheet.parseError.reason)
		     m.loProcessor.styleSheet = loStyleSheet
		     m.loReturn = m.loProcessor
		*  ELSE     
		*     THIS.LastErrorMessage = loSStyleSheet.parseError.reason
		  ENDIF   
		ENDIF
		
		RETURN loReturn
	ENDPROC

	PROCEDURE nopageeject_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "L" AND NOT THIS.IsRunning
		   THIS.NoPageEject = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROCEDURE opentargetfile
		#IF OUTPUTXML = OUTPUTXML_DOM
		   THIS.VerifyTargetFile() 
		   THIS.TargetHandle = 0
		   RETURN (NOT THIS.HadError)
		#ELSE
		   RETURN DODEFAULT()   
		#ENDIF
		
	ENDPROC

	PROTECTED PROCEDURE preparefrxcopy		&& Prepares FRX alias or copy for use in creating VFP-RDL contents.
		LOCAL m.lcAlias, m.lcFile
		m.lcAlias = "FRX"
		IF EMPTY(SYS(2000,THIS.CommandClauses.File)) AND ;
		   USED("FRX")
		   m.lcFile = FORCEEXT(FORCEPATH("F"+SYS(2015), JUSTPATH(THIS.targetFileName)),"FRX")
		   SELECT 0
		   CREATE CURSOR x (onefield l)
		   * we're in the frxdatasession, this is safe
		   CREATE REPORT (m.lcFile) FROM (DBF("x"))
		   USE IN x
		   SELECT 0
		   USE (m.lcFile) EXCLUSIVE ALIAS (JUSTSTEM(m.lcFile))
		   m.lcAlias = ALIAS()
		   ZAP
		   SELECT FRX
		   SCAN
		     SCATTER MEMVAR MEMO
		     INSERT INTO (m.lcAlias) FROM MEMVAR
		   ENDSCAN
		ENDIF
		RETURN m.lcAlias
	ENDPROC

	PROTECTED PROCEDURE removefrxcopy		&& Removes FRX copy used in preparing VFP-RDL.
		LPARAMETERS m.tcAlias
		LOCAL m.lcFile
		IF m.tcAlias # "FRX"
		   m.lcFile = DBF(m.tcAlias)
		   USE IN (m.tcAlias)
		   ERASE (m.lcFile)
		   ERASE (FORCEEXT(m.lcFile,"FRT"))   
		ENDIF      
		
	ENDPROC

	PROCEDURE Render
		LPARAMETERS m.nFRXRecNo, m.nLeft,m.nTop,m.nWidth,m.nHeight, ;
		            m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage
		
		DODEFAULT(m.nFRXRecNo, m.nLeft,m.nTop,m.nWidth,m.nHeight, ;
		          m.nObjectContinuationType, m.cContentsToBeRendered, m.GDIPlusImage)
		
		IF THIS.InvokeOnCurrentPass() AND ;
		   THIS.Targethandle > -1 
		
		   * also evaluate THIS.IncludeBreaksInData and 
		   * the band for the object in question.
		
		   * If the stars align, create the node for the object here.  For now:
		   
		   LOCAL m.lcNode, m.loNode, m.lcFormattingInfo, m.lcContents,  ;
		         m.llTextType, m.loBandRef, m.liBandRecno, m.lcID
		  
		   THIS.SetFRXDataSession()
		   
		   GO m.nFRXRecNo IN FRX
		   
		
		   m.lcContents = m.cContentsToBeRendered
		   m.llTextType = INLIST(FRX.ObjType,  FRX_OBJTYP_LABEL, FRX_OBJTYP_FIELD)
		   m.lcID = TRANSFORM(m.nFRXRecNo)
		   IF INLIST(m.nObjectContinuationType, ;
		             LISTENER_CONTINUATION_MIDDLE, ;
		             LISTENER_CONTINUATION_END)
		      m.lcID = m.lcID + "+"
		   ENDIF
		
		      
		   IF THIS.IncludeBreaksInData = OUTPUTXML_BREAKS_COLLECTION ;
		      OR (ISNULL(THIS.CurrentPage) AND ISNULL(THIS.CurrentColumn))
		      m.lcNode =   IIF(SEEK(OUTPUTXML_OBJTYPE_NODES+FRX.ObjType,"Nodes","ObjType"), ;
		                     Nodes.ObjValue,;
		                     OUTPUTXML_GOOFTAG)
		                  
		      #IF OUTPUTXML = OUTPUTXML_RAW
		          m.lcFormattingInfo = THIS.GetRawFormattingInfo( m.nLeft, m.nTop, m.nWidth,m.nHeight, m.nObjectContinuationType)
		          IF m.llTextType
		             * build 1515: render gets unicode
		             m.lcContents = STRCONV(TRANSFORM(m.lcContents),STRCONV_UNICODE_UTF8)
		          ELSE
		             m.lcContents = TRANSFORM(m.lcContents)  
		          ENDIF
		          DO CASE
		          CASE NOT ISNULL(THIS.CurrentPage)
		             THIS.CurrentPage = THIS.CurrentPage +  ;
		                 THIS.XMLRawNode( ;
		                 m.lcNode, ;
		                 m.lcContents, ;
		                 m.lcID,.F.,m.lcFormattingInfo) && FRX.UniqueID
		          CASE NOT ISNULL(THIS.CurrentColumn)
		             THIS.CurrentColumn = THIS.CurrentColumn +  ;
		                 THIS.XMLRawNode( ;
		                 m.lcNode, ;
		                 m.lcContents, ;
		                 m.lcID,.F.,m.lcFormattingInfo) 
		          OTHERWISE 
		             * write directly to the stream
		             
		             * First, take care of continuation.
		             IF EMPTY(NVL(THIS.CurrentBand,""))
		                * first object in a continued band
		                 IF ISNULL(THIS.FRXCursor)
		                    m.liBandRecno = 0
		                 ELSE
		                    m.loBandRef =  THIS.FRXCursor.GetBandFor(FRX.UniqueID, .T.)
		                    SELECT FRX
		                    LOCATE FOR UniqueID == loBandRef.UniqueID
		                    IF EOF()
		                       m.liBandRecno = 0
		                    ELSE
		                       m.liBandRecno = RECNO()
		                    ENDIF
		                 ENDIF
		                 IF m.liBandRecno = 0
		                    THIS.CurrentBand = THIS.XMLRawTag(OUTPUTXML_GOOFTAG, .T.,;
		                                  "0",TRANSFORM(IIF(THIS.sharedPageNo = 0, THIS.PageNo, THIS.sharedPageNo)) )
		                 ELSE
		                    THIS.SetCurrentDataSession()
		                    THIS.BeforeBand(FRX_OBJCOD_DETAIL,m.liBandRecno, .T.)
		                    THIS.SetFRXDataSession()
		                 ENDIF                 
		                 THIS.CurrentBand = THIS.CurrentBand + ;
		                      THIS.XMLRawNode( ;
		                       m.lcNode, ;
		                       m.lcContents, ;
		                       m.lcID,.F.,m.lcFormattingInfo) 
		                IF EOF()      
		                   THIS.WriteRaw(THIS.CurrentBand + ;
		                                THIS.XMLRawTag(OUTPUTXML_GOOFTAG))
		                   THIS.CurrentBand = ""               
		                ENDIF
		                GO m.nFRXRecNo IN FRX
		             ELSE      
		                THIS.CurrentBand = THIS.CurrentBand + ;
		                     THIS.XMLRawNode( ;
		                      m.lcNode, ;
		                      m.lcContents, ;
		                      m.lcID,.F., m.lcFormattingInfo) 
		             ENDIF
		             
		          ENDCASE
		     
		      #ELIF OUTPUTXML =  OUTPUTXML_DOTNET
		          * XMLTextWriter work
		      #ELSE      
		          * if continuation type is of type 2 or 3
		          * and we're in a text type object
		          * we have to create a new
		          * continued band node as if a BeforeBand event has occurred.         
		          IF ISNULL(THIS.CurrentBand)
		             * first object in a continued band
		             IF ISNULL(THIS.FRXcursor)
		                m.liBandRecno = 0
		             ELSE
		                m.loBandRef =  THIS.FRXCursor.GetBandFor(FRX.UniqueID, .T.)
		                SELECT FRX
		                LOCATE FOR UniqueID == m.loBandRef.UniqueID
		                m.liBandRecno = RECNO()
		             ENDIF
		             THIS.SetCurrentDataSession()
		             IF EOF()
		                 THIS.BeforeBand(FRX_OBJCOD_DETAIL,1, .T.)
		             ELSE
		                 THIS.BeforeBand(FRX_OBJCOD_DETAIL,m.liBandRecno, .T.)
		             ENDIF
		             THIS.SetFRXDataSession()
		             GO m.nFRXRecNo IN FRX
		          ENDIF
		          m.loNode = THIS.CurrentDocument.CreateElement(m.lcNode)          
		          m.lcContents = TRANSFORM(m.lcContents)            
		          * build 1515: render gets unicode, and is already regionally transformed          
		          * EXCEPT if it's a filename for an image, in which case it's DBCS          
		          IF m.llTextType 
		             m.loNode.Text = CREATEBINARY(m.lcContents)          
		          ELSE
		             m.loNode.Text = m.lcContents          
		          ENDIF             
		          m.loNode.SetAttribute(THIS.IdAttribute,m.lcID)
		          THIS.SetDOMFormattingInfo( m.loNode, m.nLeft, m.nTop, m.nWidth,m.nHeight, m.nObjectContinuationType)
		          DO CASE
		          CASE NOT ISNULL(THIS.CurrentPage)
		             THIS.CurrentPage.AppendChild(m.loNode)          
		          CASE NOT ISNULL(THIS.CurrentColumn)
		             THIS.CurrentColumn.AppendChild(m.loNode)          
		          OTHERWISE
		             THIS.CurrentBand.AppendChild(m.loNode)                     
		          ENDCASE
		          m.loNode = NULL
		       #ENDIF
		   ELSE
		      * otherwise object belongs to a formatting header or footer
		      * and we're not processing them (XMLBREAKS_NONE)
		   ENDIF                   
		   
		   THIS.resetDataSession()
		   
		ENDIF
		
		RETURN  
	ENDPROC

	PROCEDURE resetdocument		&& Resets the XML document after a report run.
		* Do *not* reset 
		* page number/total
		THIS.ResetReport()   
		THIS.CloseTargetFile()
		THIS.NoPageEject = .F.
		IF THIS.HadError
		   THIS.ResetToDefault("QuietMode")
		ENDIF   
		THIS.CurrentDocument = NULL
		
		
		
		
	ENDPROC

	PROTECTED PROCEDURE resetreport		&& Resets information for a report-level node during the production of an XML document that may span multiple reports.
		THIS.IncludePage = .T.
		THIS.IsRunning = .F.
		THIS.DataNodes = NULL
		THIS.PageNodes = NULL
		THIS.ColumnNodes = NULL
		THIS.CurrentBand = NULL
		THIS.CurrentPage = NULL
		THIS.CurrentColumn = NULL
		THIS.FRXCursor = NULL && have to force this,
		                      && because FRXCursor can't be used
		                      && between report runs -- it has
		                      && no notion of switching datasessions
		                      && and won't be able to "find" FRX in
		                      && the next private session for the next run
		
		THIS.ClearStatus()   
		
		
		   
		
	ENDPROC

	PROTECTED PROCEDURE setdomformattinginfo		&& Formulates the appropriate formatting attributes for an element when XMLListener is using the DOM to create XML.
		LPARAMETERS m.toNode, m.tnLeft, m.tnTop, m.tnWidth,m.tnHeight, m.tnObjectContinuationType
		
		m.toNode.SetAttribute(THIS.ContAttr,TRANSFORM(m.tnObjectContinuationType))                  
		IF THIS.IncludeFormattingInLayoutObjects
		   m.toNode.SetAttribute(THIS.LeftAttr,TRANSFORM(m.tnLeft))
		   m.toNode.SetAttribute(THIS.TopAttr,TRANSFORM(m.tnTop))
		   m.toNode.SetAttribute(THIS.WidthAttr,TRANSFORM(m.tnWidth))             
		   m.toNode.SetAttribute(THIS.HeightAttr,TRANSFORM(m.tnHeight))
		ENDIF
		
	ENDPROC

	PROCEDURE setfrxdatasessionenvironment
		DODEFAULT()
		SET EXACT ON
		SET SYSFORMATS ON
		SET CENTURY ON
		SET SAFETY OFF
	ENDPROC

	PROCEDURE setfrxrunstartupconditions
		DODEFAULT()
		IF TYPE("THIS.CommandClauses.File") # "C"
		   ADDPROPERTY(THIS.CommandClauses,"File","")
		ENDIF      
		IF TYPE("THIS.CommandClauses.NoPageEject") # "L"
		   ADDPROPERTY(THIS.CommandClauses,"NoPageEject",.F.)
		ENDIF      
		
	ENDPROC

	PROTECTED PROCEDURE synchxsltprocessoruser		&& Can be called in the assign of any Attribute-setting property to synchronize the XSLT associated with the class witht he new attribute values.
		IF THIS.ApplyUserTransform AND NOT ISNULL(THIS.XSLTProcessorUser)
		   THIS.XSLTProcessorUser = NULL
		   THIS.ApplyUserTransform = .T. && kickstart
		ENDIF 
	ENDPROC

	PROCEDURE targetfileext_assign
		LPARAMETERS m.vNewVal
		IF VARTYPE(m.vNewVal) = "C" AND ;
		   NOT UPPER(ALLTRIM(STRTRAN(m.vNewVal,".",""))) == "TMP"
		   DODEFAULT(m.vNewVal)
		   * this class reserves the extension
		   * TMP for swapping in and out when 
		   * using temporary files and XLSTs transforms
		ENDIF   
	ENDPROC

	PROCEDURE topattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.topAttr = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE verifyattributenames		&& Hook method called during XML Listener's augmented version of VerifyConfigTable, allowing subclasses to verify attribute names should they decide to store them in the configuration table similar to node names.
		* abstract
	ENDPROC

	PROCEDURE verifyconfigtable
		LPARAMETERS m.tcAlias
		
		LOCAL m.llReturn, laRequired[1], m.liIndex, m.liSelect, m.liTag, m.lcTag, m.lcIndex
		
		m.llReturn = DODEFAULT(m.tcAlias)
		
		IF m.llReturn
		   * check for required tagnames (used in SEEKs)
		   
		   m.liSelect = SELECT()
		   SELECT (m.tcAlias)
		   
		   DIME laRequired[2,2]
		   laRequired[1,1] = "OBJTYPE"
		   laRequired[1,2] = "OBJTYPE"
		   laRequired[2,1] = "FRXNODES"
		   laRequired[2,2] = NORMALIZE("OBJTYPE+OBJCODE+IIF(OBJTYPE="+ ;
		                      TRANSFORM(FRX_OBJTYP_BAND+OUTPUTXML_OBJTYPE_NODES)+"," + ;
		                      TRANSFORM(OUTPUTXML_OBJTYPE_BANDOFFSET)+",0)")
		   
		   FOR m.liIndex = 1 TO ALEN(laRequired,1)
		       m.liTag = TAGNO(laRequired[m.liIndex,1])
		       IF m.liTag = 0 OR NOT NORMALIZE(KEY(m.liTag)) == ;
		          laRequired[m.liIndex,2]
		          m.llReturn = .F.
		       ENDIF
		   ENDFOR
		   
		   IF NOT m.llReturn
		   
		      TRY
		         USE (DBF(m.tcAlias)) EXCLU ALIAS (m.tcAlias)
		         FOR m.liIndex = 1 TO ALEN(laRequired,1)
		            m.lcTag = laRequired[m.liIndex,1]
		            m.lcIndex = laRequired[m.liIndex,2]
		            INDEX ON &lcIndex TAG &lcTag
		         ENDFOR
		      
		         m.llReturn = .T.
		      CATCH
		      ENDTRY   
		      
		      
		      IF m.llReturn
		         FOR m.liIndex = 1 TO ALEN(laRequired,1)
		             m.liTag = TAGNO(laRequired[m.liIndex,1])
		             IF m.liTag = 0 OR NOT NORMALIZE(KEY(m.liTag)) == ;
		                laRequired[m.liIndex,2]
		                m.llReturn = .F.
		             ENDIF
		         ENDFOR
		      ENDIF
		      
		      USE (DBF(m.tcAlias)) SHARED ALIAS (m.tcAlias)
		 
		   
		   ENDIF
		  
		   IF NOT m.llReturn
		      m.lcMessage =  OUTPUTXML_CONFIGTAGMISSING_LOC + CHR(13) 
		      FOR m.liIndex = 1 TO ALEN(laRequired,1)
		          m.lcMessage = m.lcMessage + ;
		                     CHR(13) + laRequired[m.liIndex,1] + ;
		                     "=" + laRequired[m.liIndex,2]
		      ENDFOR               
		      THIS.DoMessage(m.lcMessage,MB_ICONSTOP )      
		   ENDIF
		   
		   IF m.llReturn 
		                                  
		     * just do one check, this is in case
		     * a different listener created the config file.
		     * the XML will run just fine without these records,
		     * it will just use its gooftag instead of regular
		     * nodenames if all or any are missing
		
		     IF NOT SEEK( OUTPUTXML_OBJTYPE_NODES+ FRX_OBJTYP_REPORTHEADER, ;
		                               ALIAS(),"ObjType")
		        TRY
		           IF IsReadOnly()
		              USE (DBF(m.tcAlias)) SHARED ALIAS (m.tcAlias)
		           ENDIF
		           THIS.InsertXMLConfigRecords()
		        CATCH
		        ENDTRY   
		                               
		     ENDIF                          
		   
		   ENDIF
		   
		   SELECT (m.liSelect)
		   
		ENDIF
		
		RETURN m.llReturn       
		
		   
	ENDPROC

	PROCEDURE verifyncname		&& Generic method to validate strings as XML-standard NCNames.
		LPARAMETERS m.tcName
		
		LOCAL m.llValid, m.liChar, m.lcChar
		
		DO CASE 
		
		CASE VARTYPE(m.tcName) # "C" OR EMPTY(m.tcName)
		  * invalid
		CASE LEFT(m.tcName,1) # "_" AND NOT ISALPHA(LEFT(m.tcName,1))
		  * invalid
		CASE LEFT(UPPER(m.tcName),3) = "XML"
		  * invalid
		OTHERWISE
		  m.llValid = .T.  
		  FOR m.liChar = 2 TO LEN(tcName)
		     m.lcChar = SUBSTR(m.tcName,m.liChar,1)
		     IF NOT (ISALPHA(m.lcChar) OR ;
		             ISDIGIT(m.lcChar) OR ;
		             INLIST(m.lcChar,".","-","_"))
		        m.llValid = .F.
		        EXIT
		     ENDIF
		  ENDFOR
		ENDCASE
		
		RETURN m.llValid 
	ENDPROC

	PROTECTED PROCEDURE verifynodenames		&& Method called during XML Listener's augmented version of VerifyConfigTable to check all node values in the configuration table for XML validity.
		IF NOT USED("Nodes")
		   RETURN .F.
		ENDIF
		IF ISREADONLY("Nodes")
		   RETURN .T.
		ENDIF   
		   
		LOCAL m.liSelect, m.llSuccess
		m.liSelect = SELECT()
		m.llSuccess = .T.
		SELECT Nodes
		SCAN FOR BETWEEN(ObjType,OUTPUTXML_OBJTYPE_NODES,OUTPUTXML_OBJTYPE_NODES+99)
		   IF NOT THIS.VerifyNCName(ObjValue)
		      DELETE
		      m.llSuccess = .F.
		   ENDIF
		ENDSCAN
		                    
		SELECT (m.liSelect)
		RETURN m.llSuccess                    
	ENDPROC

	PROCEDURE widthattr_assign
		LPARAMETERS m.vNewVal
		IF (NOT THIS.IsRunning) AND THIS.VerifyNCName(m.vNewVal)
		   THIS.widthAttr = m.vNewVal
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE writeraw		&& Used to write raw XML data to a file.
		LPARAMETERS m.tcContents
		
		 FWRITE(THIS.TargetHandle, m.tcContents) 
		
	ENDPROC

	PROCEDURE xmlmode_assign
		LPARAMETERS m.vNewVal
		* Readonly during report run
		IF NOT THIS.IsRunning
		   IF VARTYPE(m.vNewVal) = "N" AND ;
		      INLIST(m.vNewVal,;
		             OUTPUTXML_DATA_ONLY,;
		             OUTPUTXML_RDL_ONLY, ;
		             OUTPUTXML_DATA_RDL)
		      THIS.xmlmode = m.vNewVal
		   ENDIF
		ENDIF   
		
	ENDPROC

	PROTECTED PROCEDURE xmlrawconv		&& Used to convert any control characters to entity references when XML Listener is writing raw XML data to a file.
		LPARAMETERS m.tcValue
		LOCAL m.lcValue, m.liChar
		* must have ampersand as the first STRTRAN()      
		m.lcValue = STRTRAN(m.tcValue, '&', '&amp;' )      
		m.lcValue = STRTRAN(m.lcValue, '<', '&lt;' )
		m.lcValue = STRTRAN(m.lcValue, '>', '&gt;' )
		m.lcValue = STRTRAN(m.lcValue, '"', '&quot;' )
		m.lcValue = STRTRAN(m.lcValue, ['], '&apos;' )
		m.lcValue = CHRTRAN(m.lcValue, CHR(0)+CHR(4), "  ")
		RETURN m.lcValue
		* TBD: make any adjustments 
		* and, if needed, for different element types if needed
		
	ENDPROC

	PROTECTED PROCEDURE xmlrawnode		&& Used to formulate the contents of an XML element  node when XML Listener is writing the XML document as raw data to a file.
		LPARAMETERS m.tcNode,m.tcValue, m.tvID, m.tvIDRef, m.tvFormatting
		LOCAL m.lcValue, m.lcNode
		IF PARAMETERS() < 2
		   m.lcValue = ""
		ELSE
		   m.lcValue = THIS.XMLRawConv(m.tcValue)
		ENDIF
		IF EMPTY(m.lcValue) 
		  m.lcNode = THIS.XMLRawTag(m.tcNode,NULL, m.tvID, m.tvIDRef, m.tvFormatting)
		ELSE
		  m.lcNode = THIS.XMLRawTag(m.tcNode, .T., m.tvID, m.tvIDRef, m.tvFormatting)+m.lcValue+THIS.XMLRawTag(m.tcNode)
		ENDIF
		
		RETURN m.lcNode
		   
	ENDPROC

	PROTECTED PROCEDURE xmlrawtag		&& Used to formulate the contents of an XML tag when XML Listener is writing the XML document as raw data to a file.
		LPARAMETERS m.tcNode, m.tlOpen, m.tcID, m.tcIDRef, m.tvFormatting
		LOCAL m.lcNode
		
		IF ISNULL(m.tlOpen) OR m.tlOpen
		   m.lcNode = "<" + m.tcNode 
		   IF NOT EMPTY(m.tcID)
		      m.lcNode = m.lcNode + " "+THIS.idAttribute+"='"+m.tcID+"'"
		   ENDIF
		   IF NOT EMPTY(m.tcIDRef)
		      m.lcNode = m.lcNode + " "+THIS.idRefAttribute+"='"+m.tcIDRef+"'"
		   ENDIF
		   IF NOT EMPTY(m.tvFormatting)
		      m.lcNode = m.lcNode + " " + m.tvFormatting
		   ENDIF
		   IF ISNULL(m.tlOpen)
		      m.lcNode = m.lcNode +  "/"
		   ENDIF
		   m.lcNode = m.lcNode + ">"
		ELSE
		   m.lcNode = "</"+ m.tcNode + ">"   
		ENDIF
		
		RETURN m.lcNode
	ENDPROC

	PROCEDURE xsltparameters_assign
		LPARAMETERS m.vNewVal
		DO CASE
		CASE VARTYPE(m.vNewVal) = "X"
		  THIS.XSLTParameters= NULL
		CASE VARTYPE(m.vNewVal) = "O" 
		   TRY
		     IF UPPER(m.vNewVal.BaseClass) == "COLLECTION" 
		       THIS.XSLTParameters = m.vNewVal
		     ENDIF
		   CATCH 
		   ENDTRY
		OTHERWISE
		  *
		ENDCASE
		
		
		
		
		
		
	ENDPROC

	PROCEDURE xsltprocessorrdl_assign
		LPARAMETERS m.vNewVal
		DO CASE
		CASE VARTYPE(m.vNewVal) = "X"
		   THIS.XSLTProcessorRDL = NULL
		CASE VARTYPE(m.vNewVal) = "O" 
		   TRY
		     IF VARTYPE(m.vNewVal.stylesheet) = "O"
		       THIS.XSLTProcessorRDL = m.vNewVal
		     ENDIF
		   CATCH 
		   ENDTRY
		CASE VARTYPE(m.vNewVal) = "C" 
		   LOCAL m.loProcessor
		   m.loProcessor = THIS.LoadProcessorObject(m.vNewVal)
		   IF NOT ISNULL(m.loProcessor)
		      THIS.XSLTProcessorRDL = m.loProcessor
		   ENDIF
		ENDCASE
		
		
		
	ENDPROC

	PROCEDURE xsltprocessoruser_assign
		LPARAMETERS m.vNewVal
		DO CASE
		CASE VARTYPE(m.vNewVal) = "X"
		   THIS.XSLTProcessorUser = NULL
		CASE VARTYPE(m.vNewVal) = "O" 
		   TRY
		     IF VARTYPE(m.vNewVal.stylesheet) = "O"
		       THIS.XSLTProcessorUser = m.vNewVal
		     ENDIF
		   CATCH 
		   ENDTRY
		CASE VARTYPE(m.vNewVal) = "C" 
		   LOCAL m.loProcessor
		   m.loProcessor = THIS.LoadProcessorObject(m.vNewVal)
		   IF NOT ISNULL(m.loProcessor)
		      THIS.XSLTProcessorUser = m.loProcessor
		   ENDIF
		ENDCASE
		
		
		
	ENDPROC

ENDDEFINE
