*--------------------------------------------------------------------------------------------------------------------------------------------------------
* (EN) AUTOGENERATED - ATTENTION!! - NOT INTENDED FOR EXECUTION!! USE ONLY FOR MERGING CHANGES AND STORING WITH SCM TOOLS!!
*--------------------------------------------------------------------------------------------------------------------------------------------------------
*< FOXBIN2PRG: Version="1.21" SourceFile="_frxcursor.vcx" CPID="1252" /> (Solo para binarios VFP 9 / Only for VFP 9 binaries)
*
*
DEFINE CLASS frxcursor AS custom 		&& Provides methods to translate and manipulate various values in label  and report tables (LBXs and FRXs)
 	*< CLASSDATA: Baseclass="custom" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "_frxcursor.h"
	*<DefinedPropArrayMethod>
		*m: binstringtoint		&& Returns a numeric equivalent of a given binary number given in byte string form.
		*m: bintoint		&& Returns the integer form of binary data. Parameter: cBinary
		*m: charsettolocale		&& Converts a given Font Charset to a candidate locale Id for use with the STRCONV() function. Parameters: nCharSet
		*m: createbandcursor		&& Creates a cursor with alias "bands" containing records of information for each band in the report. Assumes source alias is "FRX" unless specified. Parameters: [cFrxAlias]
		*m: createcalcresetoncursor		&& Creates a cursor with alias "reset_on" that contains records of information for each prompt in the Calculation Reset combobox. Parameters: [cFrxAlias]
		*m: createdefaultprintenvcursor		&& Creates a one-row cursor with the same structure as the FRX. Default parameters are "frx", "defPrnEnv". Parameters: [ cFrxAlias [, cDestinationAlias]]
		*m: creategroupcursor		&& Creates a cursor with the alias "groups" containing records of information about each data group in the specified report cursor, default "frx". Parameters: [cFrxAlias]
		*m: createobjcursorrecord		&& Called from .createObjectCursor(). Parameter: cDestinationAlias
		*m: createobjectcursor		&& Creates a cursor (default alias: "objects") of records for each object record in the report alias. Parameters: [ cFrxAlias [, cDestinationAlias [, iFilterMode ]]]
		*m: createvariablecursor		&& Creates a cursor with the alias "vars" containing records of information for each report variable in the report. Parameters: [cFrxAlias]
		*m: frutopixels		&& Returns the pixel value of a given measurement in FRUs.
		*m: getbandfor		&& Returns a SCATTER NAME band object for the specified object ID. Calls .createObjectCursor() if necessary. Parameters: cObjectID [, lStart]
		*m: getfrutextheight		&& Reutrns the height of a given string in FRUs. Parameters: cText, cTypeface, iSize [, cStyle ]
		*m: getfrutextwidth		&& Returns the width of a given string in FRUs. Parameters: cText, cTypeface, iSize [, cStyle ]
		*m: getfrxtimestamp		&& Returns a FOX system file timestamp from a datetime value, any data type. Parameter: [vDateTime]
		*m: getmetadatadomdoc		&& Returns a reference to an MSXml.DomDocument with the metadata xml loaded. Assumes FRX is located on desired record.
		*m: getobjectsinband		&& Returns a collection of UNIQUEIDs (or RECNOs) for each object in a given band. Calls .createObjectCursor() if necessary. Parameters: cBandId [, lRecnos]
		*m: getreportattribute		&& Returns the value of a given report/header attribute. The FRX cursor must be open. Parameters: cToken [, iAlternate]
		*m: getselectedobjectcount		&& Returns the number of selected objects in the frx cursor. Parameter: [cFrxAlias]
		*m: gettargettypetext		&& Returns a readable string version of a target Type+Code. Parameters: iObjectType, iObjectCode
		*m: gettimestampstring		&& Returns a readable string version of a Fox system timestamp, using current date settings. Parameter: iStamp
		*m: getunitvaluefromfru		&& Returns a given unit value for a given value in FRUs, depending on the units. Parameters: nFruValue, iUnits
		*m: gorec		&& Restores record pointer with bounds checking. Parameters: i, cAlias
		*m: hasband		&& Returns .T. if the report has the specified band type. Calls .createBandCursor() if necessary. Parameters: iObjCode
		*m: hasdetailheader		&& Returns .T. if the specified detail band has an associated detail header band. Calls .createBandCursor() if necessary. Parameters: cUniqueId
		*m: hasprotectionflag		&& Returns .T. if the given binary data contains a specific bit set. Parameters: cBytes, iBit
		*m: insertband		&& Inserts a band record into an frx cursor. Assumes that the FRX is currently selected and that the record pointer is located appropriately. Parameter: iObjCode
		*m: insertdataenvrecord		&& Inserts a data environment object record into an FRX cursor. Assumes that the record pointer is appropriately located. Parameters: ID, NAME, EXPR, CODE
		*m: insertdetailband		&& Inserts a detail band into the frx cursor. Assumes: the record pointer is located appropriately. Parameters: none
		*m: insertdetailheaderfooter		&& Inserts detail header and footer bands into the frx cursor. Assumes: record pointer is located on the detail band record. Parameters: none
		*m: insertsummaryband		&& Inserts a summary band into the frx cursor. Parameters: lBreakToNewPage, lPageHeader, lPageFooter
		*m: inserttitleband		&& Inserts a title band into the frx cursor. Parameter: lBreakToNewPage
		*m: inttobin		&& Returns a binary form of an integer. Parameter: iNumber
		*m: inttobinstring		&& Returns a string of bytes, the binary version of a given integer.
		*m: pixelstofru		&& Returns the FRU value of a given measurement in pixels.
		*m: popprintenv		&& Restores the printer environment from a previously saved cursor. See .PushPrintEnvToCursor() method. Assumes: previously saved cursor is currently selected.
		*m: pushprintenvtocursor		&& Saves the current printer environment to a cursor. Parameter: cSavedInAlias
		*m: setcolumncount		&& Adds or removes columns (and column header/footer records) from the FRX cursor. Assumes: the frx cursor is selected. Parameters: iColumns
		*m: stripquotes		&& Returns a string with embraced string delimiters removed. Parameters: cString
		*m: synchobjectpositions		&& Updates VPOS values in the FRX cursor for each object, based on which band the object starts in, and the current height of each band as expressed in the bands cursor. Assumes: band and object cursors exist; current alias is frx cursor; no recno restore.
		*p: screendpi		&& Contains the working DPI of the ReportDesigner.  Currently hard-coded to 96.
		*p: _memberdata		&& XML Metadata for customizable properties
	*</DefinedPropArrayMethod>

	*<PropValue>
		Name = "frxcursor"
		screendpi = 96
		_memberdata = <VFPData>
			<memberdata name="getmetadatadomdoc" type="Method" display="GetMetadataDomDoc" favorites="false" override="false" script=""/>
			<memberdata name="createvariablecursor" type="Method" display="CreateVariableCursor"/>
			<memberdata name="binstringtoint" type="Method" display="BinStringToInt"/>
			<memberdata name="bintoint" type="Method" display="BinToInt"/>
			<memberdata name="charsettolocale" type="Method" display="CharsetToLocale"/>
			<memberdata name="createbandcursor" type="Method" display="CreateBandCursor"/>
			<memberdata name="createcalcresetoncursor" type="Method" display="CreateCalcResetOnCursor"/>
			<memberdata name="createdefaultprintenvcursor" type="Method" display="CreateDefaultPrintEnvCursor"/>
			<memberdata name="creategroupcursor" type="Method" display="CreateGroupCursor"/>
			<memberdata name="createobjcursorrecord" type="Method" display="CreateObjCursorRecord"/>
			<memberdata name="createobjectcursor" type="Method" display="CreateObjectCursor"/>
			<memberdata name="frutopixels" type="Method" display="FruToPixels"/>
			<memberdata name="getbandfor" type="Method" display="GetBandFor"/>
			<memberdata name="getfrutextheight" type="Method" display="GetFruTextHeight"/>
			<memberdata name="getfrutextwidth" type="Method" display="GetFruTextWidth"/>
			<memberdata name="getfrxtimestamp" type="Method" display="GetFrxTimeStamp"/>
			<memberdata name="getobjectsinband" type="Method" display="GetObjectsInBand"/>
			<memberdata name="getreportattribute" type="Method" display="GetReportAttribute"/>
			<memberdata name="getselectedobjectcount" type="Method" display="GetSelectedObjectCount"/>
			<memberdata name="gettargettypetext" type="Method" display="GetTargetTypeText"/>
			<memberdata name="gettimestampstring" type="Method" display="GetTimeStampString"/>
			<memberdata name="getunitvaluefromfru" type="Method" display="GetUnitValueFromFru"/>
			<memberdata name="gorec" type="Method" display="GoRec"/>
			<memberdata name="hasband" type="Method" display="HasBand"/>
			<memberdata name="hasdetailheader" type="Method" display="HasDetailHeader"/>
			<memberdata name="hasprotectionflag" type="Method" display="HasProtectionFlag"/>
			<memberdata name="insertband" type="Method" display="InsertBand"/>
			<memberdata name="insertdataenvrecord" type="Method" display="InsertDataEnvRecord"/>
			<memberdata name="insertdetailband" type="Method" display="InsertDetailBand"/>
			<memberdata name="insertdetailheaderfooter" type="Method" display="InsertDetailHeaderFooter"/>
			<memberdata name="insertsummaryband" type="Method" display="InsertSummaryBand"/>
			<memberdata name="inserttitleband" type="Method" display="InsertTitleBand"/>
			<memberdata name="inttobin" type="Method" display="IntToBin"/>
			<memberdata name="inttobinstring" type="Method" display="IntToBinString"/>
			<memberdata name="pixelstofru" type="Method" display="PixelsToFru"/>
			<memberdata name="popprintenv" type="Method" display="PopPrintEnv"/>
			<memberdata name="pushprintenvtocursor" type="Method" display="PushPrintEnvToCursor"/>
			<memberdata name="screendpi" type="Property" display="ScreenDPI"/>
			<memberdata name="setcolumncount" type="Method" display="SetColumnCount"/>
			<memberdata name="stripquotes" type="Method" display="StripQuotes"/>
			<memberdata name="synchobjectpositions" type="Method" display="SynchObjectPositions"/>
			<memberdata name="runtimemode" type="Property" display="RuntimeMode"/>
		</VFPData>
	*</PropValue>
	
	PROCEDURE binstringtoint		&& Returns a numeric equivalent of a given binary number given in byte string form.
		*=======================================================
		* BinstringToInt( char )
		*
		* Returns a numeric equivalent of a binary data in string
		* form.
		*
		* BinChar & Integer conversion, based on code by RS
		*=======================================================
		lparameter cByte
		
		local iReturn, i, b
		iReturn = 0
		
		for m.i = len( m.cByte ) to 1 step -1
			b = asc( substr( m.cByte, m.i, 1 ))
		
			iReturn = (m.iReturn*256) + m.b
		endfor
		return m.iReturn
		
	ENDPROC

	PROCEDURE bintoint		&& Returns the integer form of binary data. Parameter: cBinary
		*=======================================================
		* BinToInt( cBytes )
		*
		* Returns an integer form of binary data
		*=======================================================
		LPARAMETERS tcBinary
		LOCAL lcInteger,lnInteger,lnCount,lnStrLen
		
		IF EMPTY(tcBinary)
			RETURN 0
		ENDIF
		lnStrLen=LEN(tcBinary)
		lnInteger=0
		FOR lnCount = 0 TO (lnStrLen-1)
			IF SUBSTR(tcBinary,lnStrLen-lnCount,1)=="1"
				lnInteger=lnInteger+2^lnCount
			ENDIF
		ENDFOR
		RETURN INT(lnInteger)
		
		
	ENDPROC

	PROCEDURE charsettolocale		&& Converts a given Font Charset to a candidate locale Id for use with the STRCONV() function. Parameters: nCharSet
		*=======================================================
		* CharsetToLocale( nCharset )
		*
		* We need to map a given Font Charset to a candidate locale
		* for use with the STRCONV() function.
		*
		*----------------------------------------------------------
		* References: Q224804, Q232625
		* also http://www.science.co.il/Language-Locale-Codes.asp
		* 
		* Charset------------------- CodePage---- Locale (hex / dec)
		*   0   Western              1252                   1033
		* 178   Arabic               1256, 864     0x0401 / 1025 (saudi)
		* 186   Baltic               1257,         0x0425 / 1061
		* 134   Chinese Simplified    936          0x0804 / 2052
		* 136   Chinese Traditional   950          0x0404 / 1028
		* 238   Central European     1250,         0x0405 / 1029  (Czech)
		* 204   Cyrillic             1251, 855     0x0419 / 1049  (Russian)
		* 161   Greek                1253, 869     0x0408 / 1032
		* 177   Hebrew               1255          0x040d / 1037
		* 128   Japanese              932          0x0411 / 1041
		* 129   Korean               1361, 949     0x0412 / 1042
		* 162   Turkish              1254, 857     0x041f / 1055
		* 163   Vietnamese           1258          0x042a / 1066
		*=======================================================
		lparameter nCharset
		
		do case
		case inlist( m.nCharset, -1, 1 )
			* Charset not assigned or system default 
			* Map to default locale:
			return 0
		
		case inlist( m.nCharset, 0 )
			* 0 = Western
			return 1033
		
		case inlist( m.nCharset, 2, 77, 255 )
			* These are generic. Map to default locale?
			return 0
			
		case m.nCharset = 178
			* 178   Arabic               1256, 864     0x0401 / 1025 (saudi)
			return 1025
		
		case m.nCharset = 186
			* 186   Baltic               1257,         0x0425 / 1061
			return 1061
		
		case m.nCharset = 134
			* 134   Chinese Simplified    936          0x0804 / 2052
			return 2052
		
		case m.nCharset = 136
			* 136   Chinese Traditional   950          0x0404 / 1028
			return 1028
		
		case m.nCharset = 238
			* 238   Central European     1250,         0x0405 / 1029  (Czech)
			return 1029
		
		case m.nCharset = 204
			* 204   Cyrillic             1251, 855     0x0419 / 1049  (Russian)
			return 1049
		
		case m.nCharset = 161
			* 161   Greek                1253, 869     0x0408 / 1032
			return 1032
		
		case m.nCharset = 177
			* 177   Hebrew               1255          0x040d / 1037
			return 1037
		
		case m.nCharset = 128
			* 128   Japanese              932          0x0411 / 1041
			return 1041
		
		case m.nCharset = 129
			* 129   Korean               1361, 949     0x0412 / 1042
			return 1042
		
		case m.nCharset = 162
			* 162   Turkish              1254, 857     0x041f / 1055
			return 1055
		
		case m.nCharset = 163
			* 163   Vietnamese           1258          0x042a / 1066
			return 1066
		
		otherwise
			return 0
		
		endcase
		return 0
		
	ENDPROC

	PROCEDURE createbandcursor		&& Creates a cursor with alias "bands" containing records of information for each band in the report. Assumes source alias is "FRX" unless specified. Parameters: [cFrxAlias]
		*=======================================================
		* CreateBandCursor()
		*
		* Creats a cursor with the alias "bands" containing records
		* of information for each band in the report.
		*
		* Returns .T. if successfully created.
		*=======================================================
		lparameter tcFrxAlias
		
		if empty( m.tcFrxAlias )
			tcFrxAlias = 'frx'
		endif
		if not used(m.tcFrxAlias)
			return .F.
		endif
		
		local curSel, curRec
		curSel = select()
		curRec = recno(m.tcFrxAlias)
		
		*-------------------------------------------------------
		* create the bands cursor:
		*-------------------------------------------------------
		if used("bands")
			use in bands
		endif
		select 0
		create cursor bands ( ;
			UNIQUEID c(10), ;
			OBJTYPE N(2,0), ;
			OBJCODE n(3,0), ;
			EXPR M, ;
			BANDLABEL c(35), ;
			START n(9,3), ;
			STOP n(9,3), ;
			HEIGHT n(9,3), ;
			P_START i, ;
			P_STOP i, ;
			P_HEIGHT i, ;
			R_START i, ;
			R_STOP i, ;
			RESETTOTAL i, ;
			BAND_SEQ i, ;
			REL_BAND_ID c(10), ;
			REC_NO i )
		
		*-------------------------------------------------------
		* Initialise stuff:
		*-------------------------------------------------------
		local nStart, iStart, iDetailCount
		local cSuffix, iBandCount, oRec
		local oGroup, oHeader
		local lcTitleId, lcSummaryId
		
		nStart  = 0.000 
		iStart  = 0
		oGroup  = newobject("Collection")
		oHeader = newobject("Collection")
		iBandCount   = 0
		iDetailCount = 0
		store "" to lcTitleId, lcSummaryId
		
		*-------------------------------------------------------
		* Scan through the band records, building up the information
		* and then insert a new record into the bands cursor:
		*-------------------------------------------------------
		select (m.tcFrxAlias)
		scan for	OBJTYPE = FRX_OBJTYP_BAND ;
					and not deleted()  and ;
					PLATFORM = FRX_PLATFORM_WINDOWS
		
			*----------------------------------------------
			* Initialise the record buffer
			*----------------------------------------------
			select bands
			scatter memo blank name oRec
			select (m.tcFrxAlias)
		
			iBandCount     = m.iBandCount + 1
			oRec.BAND_SEQ  = m.iBandCount
		
			*----------------------------------------------
			* Transfer over some field values:
			*----------------------------------------------
			oRec.UNIQUEID   = UNIQUEID
			oRec.REC_NO     = recno()
			oRec.OBJTYPE    = OBJTYPE
			oRec.OBJCODE    = OBJCODE
			oRec.EXPR       = EXPR
			oRec.BANDLABEL  = THIS.getTargetTypeText( OBJTYPE, OBJCODE )
		
			*----------------------------------------------
			* Save for later:
			*----------------------------------------------
			if OBJCODE = FRX_OBJCOD_TITLE
				lcTitleID = UNIQUEID
			endif
			if OBJCODE = FRX_OBJCOD_SUMMARY
				lcSummaryID = UNIQUEID
			endif
		
			*----------------------------------------------
			* Determine related header/footer IDs:
			*----------------------------------------------
			do case
			case inlist( OBJCODE, FRX_OBJCOD_PAGEHEADER, FRX_OBJCOD_COLHEADER, FRX_OBJCOD_GROUPHEADER, FRX_OBJCOD_DETAILHEADER )
				*-------------------------------------
				* Push the header ID on the stack:
				*-------------------------------------
				oHeader.Add( UNIQUEID )		
		
			case inlist( OBJCODE, FRX_OBJCOD_PAGEFOOTER, FRX_OBJCOD_COLFOOTER, FRX_OBJCOD_GROUPFOOTER, FRX_OBJCOD_DETAILFOOTER )	
				*-------------------------------------
				* Pop the related header ID off the stack and 
				* store it:
				*-------------------------------------
				oRec.REL_BAND_ID = oHeader.Item( oHeader.Count )
				oHeader.Remove( oHeader.Count )
				
			endcase
		
			*----------------------------------------------
			* Some bands need extra info in their labels:
			*----------------------------------------------
			do case 
			case OBJCODE = FRX_OBJCOD_GROUPHEADER
				cSuffix = ":" + trim(EXPR) 		
				oGroup.Add( m.cSuffix )
				oRec.RESETTOTAL = FRX_RESETTOTAL_GROUP_OFFSET + oGroup.Count
				
			case OBJCODE = FRX_OBJCOD_DETAIL
				iDetailCount = m.iDetailCount + 1
				oRec.RESETTOTAL = FRX_RESETTOTAL_DETAIL_OFFSET + m.iDetailCount
				cSuffix = " " + transform( m.iDetailCount ) ;
						+ iif(not empty( trim(EXPR)), ":" + trim(EXPR), "")
		
			case OBJCODE = FRX_OBJCOD_GROUPFOOTER
				cSuffix = oGroup.Item( oGroup.Count )
				oGroup.Remove( oGroup.Count )
		
			otherwise
				cSuffix = ""
		
			endcase
			oRec.BANDLABEL = oRec.BANDLABEL + m.cSuffix
		
			*----------------------------------------------
			* Band dimensions:
			*----------------------------------------------
			oRec.HEIGHT    = HEIGHT
			oRec.P_HEIGHT  = THIS.FruToPixels( HEIGHT )
		
			oRec.START	   = m.nStart	
			oRec.STOP	   = m.nStart + oRec.HEIGHT   + BAND_SEPARATOR_HEIGHT_FRUS
			nStart         = oRec.STOP
		
			oRec.P_START   = m.iStart
			oRec.P_STOP    = m.iStart + oRec.P_HEIGHT + BAND_SEPARATOR_HEIGHT_PIXELS - 1
			iStart         = oRec.P_STOP + 1
		
			*----------------------------------------------
			* Run-time Band dimensions:
			*----------------------------------------------
			oRec.R_START   = oRec.P_START - 18
			oRec.R_STOP    = oRec.P_STOP - 2  && per RICHSTA
		*	oRec.R_STOP    = oRec.P_STOP - 3  && empirically
			
			*----------------------------------------------
			* Add the record to the cursor:
			*----------------------------------------------
			insert into bands from name oRec
		
		endscan
		
		*----------------------------------------------
		* Retro-fit the Headers' relative Footer band id:
		*----------------------------------------------
		select bands
		local iRec, cFooterId, cBandId 
		scan
			if not empty( REL_BAND_ID )
				iRec = recno()
				m.cFooterId = UNIQUEID
				m.cHeaderId = REL_BAND_ID
				locate for UNIQUEID = m.cHeaderId
				if found()
					replace REL_BAND_ID with m.cFooterId
				endif
				go m.iRec
			endif
		endscan
		
		if not empty( m.lcTitleId ) and not empty( m.lcSummaryId )
			*----------------------------------------------
			* Match up relative band ids:
			*----------------------------------------------
			locate for UNIQUEID = m.lcTitleID
			replace REL_BAND_ID with m.lcSummaryId
				
			locate for UNIQUEID = m.lcSummaryID
			replace REL_BAND_ID with m.lcTitleId
		
		endif
		
		go top in bands
		
		release oGroup, oHeader
		THIS.goRec(m.curRec, m.tcFrxAlias)
		select (m.curSel)		
		return .T.
		
	ENDPROC

	PROCEDURE createcalcresetoncursor		&& Creates a cursor with alias "reset_on" that contains records of information for each prompt in the Calculation Reset combobox. Parameters: [cFrxAlias]
		*=======================================================
		* CreateCalcResetOnCursor()
		*
		* Creates a cursor with the alias "reset_on" containing records
		* of information for each prompt in the Calculation Reset combobox.
		*
		* Returns .T. if successfully created.
		*=======================================================
		lparameter tcFrxAlias
		
		if empty( m.tcFrxAlias )
			tcFrxAlias = 'frx'
		endif
		if not used(m.tcFrxAlias)
			return .F.
		endif
		
		local curSel, curRec, iGroupCount, iDetailCount, iNum
		curSel = select()
		
		
		*-------------------------------------------------------
		* create the reset_on cursor:
		*-------------------------------------------------------
		if used("reset_on")
			use in reset_on
		endif
		create cursor reset_on (;
			UNIQUEID c(10), ;
			OBJCODE n(2,0) , ;
			PROMPT_TEXT c(30), ;
			RESETTOTAL i )
		
		*-----------------------------------------------------------	
		* Add in the report/Page resets:
		*-----------------------------------------------------------	
		insert into reset_on values ( "", 0, ENDOFREPORT_LOC, FRX_RESETTOTAL_ENDOFREPORT )
		insert into reset_on values ( "", 0, ENDOFPAGE_LOC,   FRX_RESETTOTAL_ENDOFPAGE )
		
		select (m.tcFrxAlias)
		curRec = recno()
		
		*-----------------------------------------------------------	
		* Add in the column reset if there are multi-cols:
		*-----------------------------------------------------------	
		locate for OBJTYPE = FRX_OBJTYP_REPORTHEADER ;
			   and PLATFORM = FRX_PLATFORM_WINDOWS
		
		if VPOS > 1
			insert into reset_on values ( "", 0, ENDOFCOLUMN_LOC, FRX_RESETTOTAL_ENDOFCOLUMN )
		else
			insert into reset_on values ( "", 0, "\"+ENDOFCOLUMN_LOC, FRX_RESETTOTAL_ENDOFCOLUMN )
		endif
			
		*-----------------------------------------------------------	
		* Only add in the group reset if there are groups:
		*-----------------------------------------------------------	
		count for OBJTYPE = FRX_OBJTYP_BAND ;
		      and OBJCODE = FRX_OBJCOD_GROUPHEADER ;
			  and PLATFORM = FRX_PLATFORM_WINDOWS ;
		      to iGroupCount
		
		if m.iGroupCount > 0
			iNum = 1
			insert into reset_on values ( "", 0, "\-", 1001 )
		
			scan for OBJTYPE = FRX_OBJTYP_BAND ;
			     and OBJCODE = FRX_OBJCOD_GROUPHEADER ;
		         and PLATFORM = FRX_PLATFORM_WINDOWS
			
				insert into reset_on values ( ;
					&tcFrxAlias..UNIQUEID, ;
					&tcFrxAlias..OBJCODE, ;
					GROUP_BY_LOC + trim(&tcFrxAlias..EXPR), ;
					FRX_RESETTOTAL_GROUP_OFFSET + m.iNum )
				iNum = m.iNum + 1
			endscan
		endif	
		
		*-----------------------------------------------------------	
		* Only add in the detail reset if this is a multi-detail report:
		*-----------------------------------------------------------	
		count for OBJTYPE = FRX_OBJTYP_BAND ;
		      and OBJCODE = FRX_OBJCOD_DETAIL ;
			  and PLATFORM = FRX_PLATFORM_WINDOWS ;
		      to iDetailCount
		
		if m.iDetailCount > 1
			iNum = 1
			insert into reset_on values ( "", 0, "\-", 1002 )
		
			scan for OBJTYPE = FRX_OBJTYP_BAND ;
			     and OBJCODE = FRX_OBJCOD_DETAIL ;
			     and PLATFORM = FRX_PLATFORM_WINDOWS
			     
				insert into reset_on values ( ;
					&tcFrxAlias..UNIQUEID, ;
					&tcFrxAlias..OBJCODE, ;
					DETAIL_LOC + transform(m.iNum), ;
					FRX_RESETTOTAL_DETAIL_OFFSET + m.iNum  )	
				iNum = m.iNum + 1
			endscan
		endif	
		THIS.goRec(m.curRec, m.tcFrxAlias)
		select (m.curSel)
		return .t.
		
	ENDPROC

	PROCEDURE createdefaultprintenvcursor		&& Creates a one-row cursor with the same structure as the FRX. Default parameters are "frx", "defPrnEnv". Parameters: [ cFrxAlias [, cDestinationAlias]]
		*=======================================================
		* CreateDefaultPrintEnvCursor( <frxAlias>, <dataenvAlias> )
		*
		* Creates a one-row cursor with the same structure as the
		* FRX. Default parameters are "frx", "defPrnEnv".
		*=======================================================
		lparameters lcFrxAlias, lcPEAlias
		local curSel
		curSel = select()
		
		if empty( m.lcFrxAlias )
			lcFrxAlias = 'frx'
		endif
		
		if empty( m.lcPEAlias )
			lcPEAlias = 'defPrnEnv'
		endif
		
		select * ;
			from (m.lcFrxAlias) ;
			where OBJTYPE = FRX_OBJTYP_REPORTHEADER ;
		      and PLATFORM = FRX_PLATFORM_WINDOWS ;
			into cursor (m.lcPEAlias) ;
			readwrite
		
		=sys(1037,2)
		
		select (m.curSel)
		return
		
	ENDPROC

	PROCEDURE creategroupcursor		&& Creates a cursor with the alias "groups" containing records of information about each data group in the specified report cursor, default "frx". Parameters: [cFrxAlias]
		*=======================================================
		* CreateGroupCursor()
		*
		* Creats a cursor with the alias "groups" containing records
		* of information for each data group in the report.
		* Used mainly by PanelGrouping class.
		*
		* Returns .T. if successfully created.
		*=======================================================
		lparameter tcFrxAlias
		
		if empty( m.tcFrxAlias )
			tcFrxAlias = 'frx'
		endif
		if not used(m.tcFrxAlias)
			return .F.
		endif
		
		curSel = select()
		
		*-------------------------------------------------------
		* get the current report measurement units and tell our 
		* converter to use them:
		*-------------------------------------------------------
		local iUnits
		iUnits     = THIS.getReportAttribute("UNITS")
		
		*-------------------------------------------------------
		* create the groups cursor:
		*-------------------------------------------------------
		if used("groups")
			use in groups
		endif
		select 0
		create cursor groups ( ;
			UNIQUEID c(10), ;
			EXPR M, ;
			PAGINATE i, ;
			REPRINT l, ;
			THRESH n(9,3), ;
			FOOTER_ID c(10) )
		
		*-------------------------------------------------------
		* Initialise stuff:
		*-------------------------------------------------------
		local iPaginate, iCurRec, nThreshold, lisMultiCol
		iPaginate   = 0
		iCurRec     = recno(m.tcFrxAlias)
		lIsMultiCol = THIS.getReportAttribute("MULTICOLUMN")
		
		*-------------------------------------------------------
		* Scan through the group header records, building up the information
		* and then insert a new record into the groups cursor:
		*-------------------------------------------------------
		select (m.tcFrxAlias)
		scan for OBJTYPE = FRX_OBJTYP_BAND and ;
				 OBJCODE = FRX_OBJCOD_GROUPHEADER and ;
				 not deleted() and ;
				 PLATFORM = FRX_PLATFORM_WINDOWS
		
			*-------------------------------------
			* Pagination: try:
			*
			* 1  ( ) Normal               (none set)
			* 2  ( ) Start on new column  (COLBREAK)
			* 3  ( ) Start on new page    (PAGEBREAK)
			* 4  ( ) Start on new page 1  (PAGEBREAK+RESETPAGE)
			*-------------------------------------
			do case
			case PAGEBREAK and RESETPAGE
				iPaginate = 4
			case PAGEBREAK 
				iPaginate = 3
			case COLBREAK
				if m.lIsMultiCol
					iPaginate = 2
				else
					*--------------------------------
					* Even though this option used to be set to "new column",
					* the report only has one column, so reset to default:
					*--------------------------------
					iPaginate = 1
				endif
			otherwise
				iPaginate = 1
			endcase
		
			*-------------------------------------
			* Calculate threshold in local units:
			*-------------------------------------
			nThreshold = THIS.getUnitValueFromFRU( WIDTH, m.iUnits )
		
			insert into groups values ( ;
				&tcFrxAlias..UNIQUEID, ;
				&tcFrxAlias..EXPR, ;
				m.iPaginate, ;
				&tcFrxAlias..NOREPEAT, ;
				m.nThreshold, ;
				"" )
			
		endscan
		
		*-------------------------------------------------------
		* Now grab the group footer IDs as well. Note of course
		* that they are in reverse order to the headers (nesting):
		*-------------------------------------------------------
		go bottom in groups
		scan for OBJTYPE = FRX_OBJTYP_BAND and ;
				 OBJCODE = FRX_OBJCOD_GROUPFOOTER and ;
				 not deleted() and ;
				 PLATFORM = FRX_PLATFORM_WINDOWS
		
			select groups
			replace FOOTER_ID with &tcFrxAlias..UNIQUEID
			skip -1		
			select (m.tcFrxAlias)
			
		endscan
		
		*-------------------------------------------------------
		* Restore the record pointer in FRX (important)
		*-------------------------------------------------------
		THIS.goRec(m.icurRec, m.tcFrxAlias)
		
		select (m.curSel)		
		return .T.
		
	ENDPROC

	PROCEDURE createobjcursorrecord		&& Called from .createObjectCursor(). Parameter: cDestinationAlias
		*=======================================================
		* createObjCursorRecord()
		*
		* Assumes: current alias is the source
		*=======================================================
		lparameters tcDestAlias, tlRuntimeMode
		
		local srcAlias, lIsGroup, oRec
		srcAlias = alias()
		lIsGroup = .F.
		
		select (m.tcDestAlias)
		scatter memo blank name oRec
		
		select (m.srcAlias)
		
		oRec.REC_NO = recno()
		
		do case 
		case OBJTYPE = FRX_OBJTYP_LABEL
			oRec.CTYPE     = TARGET_TEXT_LABEL_LOC
		*	oRec.OBJNAME   = TARGET_TEXT_LABEL_LOC + ": " + trim(EXPR)
			oRec.OBJNAME   = THIS.stripQuotes(trim(EXPR))
			oRec.OBJ_PICT  = "pslabel.bmp"
			oRec.LOCALE_ID = THIS.CharsetToLocale( RESOID )	
			oRec.TYPE_SEQ  = 1
					
		case OBJTYPE = FRX_OBJTYP_FIELD
			oRec.CTYPE     = TARGET_FIELD_LOC
		*	oRec.OBJNAME   = TARGET_FIELD_LOC + ": " + trim(EXPR)
			oRec.OBJNAME   = THIS.stripQuotes(trim(EXPR))
			oRec.OBJ_PICT  = "pseditbx.bmp"
			oRec.LOCALE_ID = THIS.CharsetToLocale( RESOID )	
			oRec.TYPE_SEQ  = 2
		
		case OBJTYPE = FRX_OBJTYP_LINE
			oRec.CTYPE     = TARGET_LINE_LOC
			oRec.OBJNAME   = TARGET_LINE_LOC
			oRec.OBJ_PICT  = "psline.bmp"
			oRec.TYPE_SEQ  = 3
			
		case OBJTYPE = FRX_OBJTYP_RECTANGLE
			oRec.CTYPE    = TARGET_BOX_LOC
			oRec.OBJNAME  = TARGET_BOX_LOC
			oRec.OBJ_PICT = "pshape.bmp"
			oRec.TYPE_SEQ  = 4
			
		case OBJTYPE = FRX_OBJTYP_PICTURE
			oRec.CTYPE    = TARGET_PICTURE_LOC
			if not empty( NAME )
		*		oRec.OBJNAME  = TARGET_PICTURE_LOC + ": " + trim(NAME)
				oRec.OBJNAME  = THIS.stripQuotes(trim(NAME))
			else
		*		oRec.OBJNAME  = TARGET_PICTURE_LOC + ": " + trim(PICTURE)
				oRec.OBJNAME  = THIS.stripQuotes(trim(PICTURE))
			endif		
			oRec.OBJ_PICT = "psolebnd.bmp"
			oRec.TYPE_SEQ  = 5
			
		case OBJTYPE = FRX_OBJTYP_GROUP
			oRec.CTYPE    = TARGET_GROUPED_LOC
			oRec.OBJNAME  = TARGET_GROUPED_LOC + " (" + trans(HPOS)+")"
			oRec.OBJ_PICT = "group2.bmp"
			lIsGroup = .T.
			oRec.TYPE_SEQ  = 6
			
		endcase
		
		oRec.UNIQUEID = UNIQUEID
		oRec.OBJTYPE  = OBJTYPE
		oRec.OBJCODE  = OBJCODE
		oRec.EXPR     = EXPR
		oRec.VPOS     = VPOS
		oRec.HPOS     = HPOS
		oRec.HEIGHT   = HEIGHT
		oRec.P_START  = THIS.FruToPixels( VPOS )
		oRec.P_HEIGHT = THIS.FruToPixels( HEIGHT )
		oRec.P_STOP   = (oRec.P_START + oRec.P_HEIGHT - 1)
		oRec.WIDTH    = WIDTH
		oRec.SELECTED = CURPOS
		
		if not m.lIsGroup
		
			if m.tlRuntimeMode
		
				*--------------------------------------------------------
				* Determine the object's location, start and end bands,
				* as determined by the report run-time engine, using the 
				* two adjusted lookup cursors we created earlier:
				*--------------------------------------------------------
		
				*-------------------------------------------
				* Scan bottom up for start points: 
				*-------------------------------------------
				select bands
				go bottom
				do while .T.
				
					if R_START <= oRec.P_START
						oRec.START_BAND_ID = UNIQUEID
						oRec.BANDLABEL     = trim(BANDLABEL)
						oRec.BAND_OFFSET   = oRec.P_START - P_START
						oRec.BAND_SEQ      = BAND_SEQ
					
						exit
					endif			
					
					if recno()=1
						exit
					endif
		
					skip -1 in bands
				enddo
		
				*-------------------------------------------
				* scan top down for end points: 
				*-------------------------------------------
				select bands
				go top
		
				*-------------------------------------------
				* This difference between object types 
				* was determined through empirical testing.
				* It may have some flaws.
				*-------------------------------------------
				do case
				case inlist(oRec.OBJTYPE, FRX_OBJTYP_LABEL ) 
					locate for R_STOP > oRec.P_STOP
		
				otherwise
					locate for R_STOP >= oRec.P_STOP
		
				endcase
						
				if found()
					*-------------------------------------------
					* an object cannot end in a band above 
					* that in which it starts:
					*-------------------------------------------
					if BAND_SEQ >= oRec.BAND_SEQ
						oRec.END_BAND_ID   = UNIQUEID
					else
						oRec.END_BAND_ID   = oRec.START_BAND_ID
					endif
				else
					*---------------------------------------------------
					* The object runs off the bottom of the band array.
					* Force the object to end in the Page Footer:
					*---------------------------------------------------
					locate for OBJCODE = FRX_OBJCOD_PAGEFOOTER
					if found()
						oRec.END_BAND_ID   = UNIQUEID
					endif
				endif
		
				*---------------------------------------------------
				* Show both start and end bands against the object:
				*---------------------------------------------------
				if oRec.END_BAND_ID <> oRec.START_BAND_ID
					oRec.BANDLABEL = oRec.BANDLABEL + "..." + trim(BANDLABEL)
				endif
		
				*---------------------------------------------------
				* If an object starts and ends in two different bands...
				*---------------------------------------------------
				if oRec.START_BAND_ID <> oRec.END_BAND_ID
		
					select bands
					locate for UNIQUEID = oRec.START_BAND_ID
		
					if empty( REL_BAND_ID ) or REL_BAND_ID # oRec.END_BAND_ID
		
						*---------------------------------------------------
						* ...that are not a matched Header-Footer pair,
						* then the object is forced into the PageHeader band.
						*---------------------------------------------------
						locate for OBJTYPE = FRX_OBJTYP_BAND ;
							   and OBJCODE = FRX_OBJCOD_PAGEHEADER
		
						oRec.START_BAND_ID = UNIQUEID
						do case
						case inlist(oRec.OBJTYPE, FRX_OBJTYP_RECTANGLE )
							oRec.BANDLABEL     = TARGET_UNPREDICTABLE_LOC + " (" + trim(oRec.BANDLABEL) + ")"
						otherwise
							oRec.BANDLABEL     = TARGET_FORCED_PAGEHEADER_LOC + " (" + trim(oRec.BANDLABEL) + ")"
						endcase
						oRec.BAND_SEQ      = 99
						oRec.END_BAND_ID   = ""
						oRec.BAND_OFFSET   = oRec.P_START - P_START && real start, not the run-time one
				
					endif
				endif		
			else
				*--------------------------------------------------------
				* Determine the object's location, start and end bands,
				* as determined by the Report Designer:
				*--------------------------------------------------------
				select bands
				locate for P_START <= oRec.P_START ;
					   and P_STOP  >= oRec.P_START
		
				if found()
					oRec.START_BAND_ID = UNIQUEID
					oRec.BAND_OFFSET   = oRec.P_START - P_START
					oRec.BANDLABEL     = trim(BANDLABEL)
					oRec.BAND_SEQ      = BAND_SEQ
				endif	
		
				select bands
				locate for P_START <= oRec.P_STOP ;
					   and P_STOP  >= oRec.P_STOP
		
				if found()
					oRec.END_BAND_ID = UNIQUEID
		
					if oRec.END_BAND_ID <> oRec.START_BAND_ID
						oRec.BANDLABEL = oRec.BANDLABEL + " - " + trim(BANDLABEL)
					endif
		
				endif	
		
			endif
		endif
		
		insert into (m.tcDestAlias) from name oRec
		
		select (m.srcAlias)
		return
		
	ENDPROC

	PROCEDURE createobjectcursor		&& Creates a cursor (default alias: "objects") of records for each object record in the report alias. Parameters: [ cFrxAlias [, cDestinationAlias [, iFilterMode ]]]
		*=======================================================
		* CreateObjectCursor()
		*
		* Creates a cursor (alias: objects) of records for each 
		* object in the report.
		* Returns .T. if successful.
		* Calls .createBandCursor() if necessary.
		*=======================================================
		lparameter tcFrxAlias, tcDestAlias, tiFilter, tlRuntimeMode
		
		*-------------------------------------------------------
		* Ensure parameters are initialised appropriately:
		*-------------------------------------------------------
		if vartype( m.tiFilter ) = "L"
			tiFilter = OBJCSR_ALL_OBJECTS_IGNORE_GROUPS
		endif	
		if empty( m.tcDestAlias )
			tcDestAlias = 'objects'
		endif
		if empty( m.tcFrxAlias )
			tcFrxAlias = 'frx'
		endif
		
		local curSel, curRec
		curSel = select()
			
		if not used(m.tcFrxAlias)
			return .F.
		else
			curRec = recno(m.tcFrxAlias)
		endif
		
		if not used('bands')
			if not THIS.createBandCursor(m.tcFrxAlias)
				select (m.curSel)
				return .F.
			endif
		endif
		
		*-------------------------------------------------------
		* create the objects cursor:
		*-------------------------------------------------------
		if used( m.tcDestAlias )
			use in (m.tcDestAlias)
		endif
		select 0
		create cursor &tcDestAlias ( ;
			UNIQUEID c(10), ;
			OBJTYPE N(2,0), ;
			OBJCODE n(3,0), ;
			EXPR M, ;
			VPOS n(9,3), ;
			HPOS n(9,3), ;
			HEIGHT n(9,3),;
			WIDTH n(9,3),;
			OBJNAME c(50),;
			LOCALE_ID i, ;
			P_START i, ;
			P_STOP i, ;
			P_HEIGHT i, ;	
			BAND_OFFSET i, ;
			START_BAND_ID c(10),;
			END_BAND_ID c(10), ;
			BANDLABEL c(75), ;
			SELECTED l, ;
			OBJ_PICT c(12), ;
			BAND_SEQ i, ;
			REC_NO i, ;
			TYPE_SEQ i, ;
			CTYPE c(10) )
		
		*-------------------------------------------------------
		* Select object records from the FRX:
		*-------------------------------------------------------
		do case
		case m.tiFilter = OBJCSR_ALL_OBJECTS_IGNORE_GROUPS
			*-------------------------------------------------
			* all object records (ignoring grouped items):
			*-------------------------------------------------
			select (m.tcFrxAlias)
			scan for 	inlist( OBJTYPE, FRX_OBJTYP_LABEL, FRX_OBJTYP_FIELD, ;
							FRX_OBJTYP_LINE, FRX_OBJTYP_RECTANGLE, FRX_OBJTYP_PICTURE ) and ;
				  		not deleted()  and ;
						PLATFORM = FRX_PLATFORM_WINDOWS
		
				THIS.createObjCursorRecord( tcDestAlias, m.tlRuntimeMode )
			endscan
		
		case m.tiFilter = OBJCSR_SHOW_ALL_OBJECTS
			*-------------------------------------------------
			* All objects+grouped objects in report:
			* Show groups as separate objects
			* do not expand grouped objects
			*-------------------------------------------------
			select (m.tcFrxAlias)
			scan for	inlist( OBJTYPE, FRX_OBJTYP_LABEL, FRX_OBJTYP_FIELD, ;
								FRX_OBJTYP_LINE, FRX_OBJTYP_RECTANGLE, FRX_OBJTYP_PICTURE ) and ;
						not deleted()  and ;
						PLATFORM = FRX_PLATFORM_WINDOWS
		
				THIS.createObjCursorRecord( tcDestAlias, m.tlRuntimeMode )
		
			endscan
		
			*-------------------------------------------------
			* Now remove the grouped ones 
			*-------------------------------------------------
			local iGrpStart, iGrpCount
			scan for OBJTYPE = FRX_OBJTYP_GROUP
		
				iGrpStart = VPOS
				iGrpCount = HPOS
		
				select (m.tcDestAlias)
				go m.iGrpStart
				delete next m.iGrpCount		
		
				select (m.tcFrxAlias)		
			endscan	
		
			*-------------------------------------------------
			* Now add the groups themselves
			*-------------------------------------------------
			scan for OBJTYPE = FRX_OBJTYP_GROUP
				THIS.createObjCursorRecord( tcDestAlias, m.tlRuntimeMode )
			endscan	
		
		
		case m.tiFilter = OBJCSR_FILTER_ON_SELECTED
			*-------------------------------------------------
			* All currently selected object records:
			* do not show grouped objects
			* Show groups as separate objects
			*-------------------------------------------------
			select (m.tcFrxAlias)
			scan for	inlist( OBJTYPE, FRX_OBJTYP_LABEL, FRX_OBJTYP_FIELD, ;
							FRX_OBJTYP_LINE, FRX_OBJTYP_RECTANGLE, FRX_OBJTYP_PICTURE ) and ;
						not deleted() and ;
						CURPOS  and ;
						PLATFORM = FRX_PLATFORM_WINDOWS				
		
				THIS.createObjCursorRecord( tcDestAlias, m.tlRuntimeMode )
		
			endscan
		
			*-------------------------------------------------
			* Now add any selected grouped items themselves:
			*-------------------------------------------------
			scan for 	OBJTYPE = FRX_OBJTYP_GROUP and ;
						CURPOS and ;
						PLATFORM = FRX_PLATFORM_WINDOWS
						
				THIS.createObjCursorRecord( tcDestAlias, m.tlRuntimeMode )
		
			endscan	
			
		endcase
		
		go top in (m.tcDestAlias)
		
		THIS.goRec(m.curRec, m.tcFrxAlias)
		select (m.curSel)
		return .T.
		
	ENDPROC

	PROCEDURE createvariablecursor		&& Creates a cursor with the alias "vars" containing records of information for each report variable in the report. Parameters: [cFrxAlias]
		*=======================================================
		* CreateVariableCursor()
		*
		* Creats a cursor with the alias "vars" containing records
		* of information for each report variable in the report.
		* Used mostly by panelVariables class
		*
		* Returns .T. if successfully created.
		*=======================================================
		lparameter tcFrxAlias
		
		if empty( m.tcFrxAlias )
			tcFrxAlias = 'frx'
		endif
		if not used(m.tcFrxAlias)
			return .F.
		endif
		
		curSel = select()
		
		*-------------------------------------------------------
		* This is needed later on:
		*-------------------------------------------------------
		if not used("reset_on")
			THIS.createCalcResetOnCursor()
		endif
		
		*-------------------------------------------------------
		* create the vars cursor:
		*-------------------------------------------------------
		if used("vars")
			use in vars
		endif
		select 0
		create cursor vars ( ;
			UNIQUEID       C(10), ;
			VARNAME        M, ;
			VALUE_TO_STORE M, ;
			INITIAL_VALUE  M, ;
			RELEASE_VAR    L, ;
			CALC_TYPE      N(2,0), ;
			RESET_ON       N(2,0), ;
			REC_NO		   I )
		
		*-------------------------------------------------------
		* Initialise stuff:
		*-------------------------------------------------------
		local iCurRec, iResetOn
		iCurRec   = recno(m.tcFrxAlias)
		
		*-------------------------------------------------------
		* Scan through the variable records, building up the information
		* and then insert a new record into the vars cursor:
		*-------------------------------------------------------
		select (m.tcFrxAlias)
		scan for OBJTYPE = FRX_OBJTYP_VARIABLE and ;
				 not deleted() and ;
				 PLATFORM = FRX_PLATFORM_WINDOWS
		
			select reset_on
			locate for RESETTOTAL = &tcFrxAlias..RESETTOTAL
			
			insert into vars values ( ;
				&tcFrxAlias..UNIQUEID, ;
				&tcFrxAlias..NAME, ;
				&tcFrxAlias..EXPR, ;
				&tcFrxAlias..TAG, ;
				&tcFrxAlias..UNIQUE, ;
				&tcFrxAlias..TOTALTYPE+1, ;
				recno("reset_on"), ;
				recno(m.tcFrxAlias) )
			
		endscan
		
		*-------------------------------------------------------
		* Restore the record pointer in FRX (important)
		*-------------------------------------------------------
		THIS.goRec(m.icurRec, m.tcFrxAlias)
		
		select (m.curSel)		
		return .T.
		
	ENDPROC

	PROCEDURE frutopixels		&& Returns the pixel value of a given measurement in FRUs.
		*=======================================================
		* FruToPixels( num )
		*
		* returns the value in pixels of a given FRU measurement.
		*=======================================================
		lparameter nFRU
		
		local iPixels
		*------------------------------------
		* Fixed for SP1: 
		* use the .screenDPI property instead of 
		* the hard-coded 96 dpi value. The ReportBuilder
		* client will reset .screenDPI to 96 so that it
		* is correct for design-time report designer use.
		*------------------------------------
		*iPixels = round((m.nFRU * 96)/10000,3)
		iPixels = round((m.nFRU * this.screenDPI)/10000,3)
		return int( m.iPixels )
		
	ENDPROC

	PROCEDURE getbandfor		&& Returns a SCATTER NAME band object for the specified object ID. Calls .createObjectCursor() if necessary. Parameters: cObjectID [, lStart]
		*=======================================================
		* GetBandFor( UNIQUEID [, lStart ] )
		*
		* Returns a SCATTER NAME band object for the specified
		* object.
		* Calls .createObjectCursor() if necessary.
		*=======================================================
		lparameters cObjectId, lStart 
		
		if not used('frx')
			return null
		endif
		
		if parameters() < 2
			lStart = .T.
		endif
		
		if not used('objects')
			THIS.createObjectCursor()
		endif
		
		local curSel, iRec, oBand
		curSel = select()
		oBand  = null
		
		select objects
		locate for UNIQUEID = m.cObjectId
		if found()
			select bands
			if m.lStart
				locate for UNIQUEID = objects.START_BAND_ID
			else
				locate for UNIQUEID = objects.END_BAND_ID
			endif
		endif			
		if not found()
			go bottom
		endif
		
		*scatter fields UNIQUEID, OBJCODE, BANDLABEL, RESETTOTAL, REC_NO ;
		*	name oBand
		scatter name oBand
		
		select (m.curSel)
		return oBand
		
	ENDPROC

	PROCEDURE getfrutextheight		&& Reutrns the height of a given string in FRUs. Parameters: cText, cTypeface, iSize [, cStyle ]
		*=======================================================
		* GetFruTextHeight( cText, cTypeface, iSize [, cStyle ] )
		*
		* Returns the height of a given string in FRUs
		*=======================================================
		lparameters cText, cTypeFace, iSize, cStyle
		
		if parameters() < 4
			cStyle = "N"
		endif
		
		*-------------------------------------------------------
		* obtain text height in pixels:
		*-------------------------------------------------------
		local iHeight
		iHeight = fontmetric(1, cTypeFace, iSize, cStyle )
		
		*-------------------------------------------------------
		* convert pixels to FRU:
		*-------------------------------------------------------
		*------------------------------------
		* Fixed for SP1: 
		* use the .screenDPI property instead of 
		* the hard-coded 96 dpi value. The ReportBuilder
		* client will reset .screenDPI to 96 so that it
		* is correct for design-time report designer use.
		*------------------------------------
		return this.pixelsToFru( m.iHeight )	
		
	ENDPROC

	PROCEDURE getfrutextwidth		&& Returns the width of a given string in FRUs. Parameters: cText, cTypeface, iSize [, cStyle ]
		*=======================================================
		* GetFruTextWidth( cText, cTypeface, iSize [, cStyle ] )
		*
		* Returns the width of a given string in FRUs
		*=======================================================
		lparameters cText, cTypeFace, iSize, cStyle
		
		if parameters() < 4
			cStyle = "N"
		endif
			
		*-------------------------------------------------------
		* obtain text width in pixels. Remember that txtwidth() returns
		* number of equivalent average characters, so must multiply by
		* the overall average character width:
		*-------------------------------------------------------
		local iWidth
		
		iWidth = txtwidth( cText,  cTypeFace, iSize, cStyle ) 
		iWidth = m.iWidth * fontmetric(6, cTypeFace, iSize, cStyle ) 
		
		*-------------------------------------------------------
		* convert pixels to FRU using screen dpi:
		*-------------------------------------------------------
		*------------------------------------
		* Fixed for SP1: 
		* use the .screenDPI property instead of 
		* the hard-coded 96 dpi value. The ReportBuilder
		* client will reset .screenDPI to 96 so that it
		* is correct for design-time report designer use.
		*------------------------------------
		return this.pixelsToFru( m.iWidth )	
		
	ENDPROC

	PROCEDURE getfrxtimestamp		&& Returns a FOX system file timestamp from a datetime value, any data type. Parameter: [vDateTime]
		#define DEBUGGING .F.
		*=======================================================
		* GetFrxTimeStamp( vDateTime )
		*
		* Returns a FOX system file timestamp 
		* from a date time value, any data type
		*
		* Calls: intToBin(), binToInt() from browser.scx code
		*=======================================================
		lparameter tvDateTime
		
		*-------------------------------------------------------
		* Default to current datetime
		*-------------------------------------------------------
		LOCAL ltDateTime, lvFoxTimeStamp, lvTemp
		ltDateTime = CTOT(TRANSFORM(tvDateTime))
		IF EMPTY(ltDateTime)
		   ltDateTime = DATETIME()
		ENDIF
		
		#IF DEBUGGING
		   ACTI SCREEN
		   CLEAR
		   ? ltDateTime
		#ENDIF
		
		*-------------------------------------------------------
		* bits 4-0, seconds in two-second increments
		*-------------------------------------------------------
		lvTemp = SEC(ltDateTime) / 2
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),5),5,"0")
		
		#IF DEBUGGING
		?  lvTemp
		?  lvFoxTimeStamp
		?
		#ENDIF
		
		*-------------------------------------------------------
		* bits 10-5, minutes
		*-------------------------------------------------------
		lvTemp = MINUTE(ltDateTime)
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),6),6,"0") + lvFoxTimeStamp
		
		#IF DEBUGGING
		?  lvTemp
		?  lvFoxTimeStamp
		?
		#ENDIF
		
		*-------------------------------------------------------
		* bits 15-11, hours
		*-------------------------------------------------------
		lvTemp = HOUR(ltDateTime)
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),5),5,"0") + lvFoxTimeStamp
		
		#IF DEBUGGING
		?  lvTemp
		?  lvFoxTimeStamp
		?
		#ENDIF
		
		*-------------------------------------------------------
		* bits 20-16, days
		*-------------------------------------------------------
		lvTemp = DAY(ltDateTime)
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),5),5,"0") + lvFoxTimeStamp
		
		#IF DEBUGGING
		?  lvTemp
		?  lvFoxTimeStamp
		?
		#ENDIF
		
		*-------------------------------------------------------
		* bits 24-21, months
		*-------------------------------------------------------
		lvTemp = MONTH(ltDateTime)
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),4),4,"0") + lvFoxTimeStamp
		
		#IF DEBUGGING
		?  lvTemp
		?  lvFoxTimeStamp
		?
		#ENDIF
		
		*-------------------------------------------------------
		* bits 31-25, years with a 1980 offset
		*-------------------------------------------------------
		lvTemp = YEAR(ltDateTime)-1980
		lvFoxTimeStamp = PADL(RIGHT(THIS.IntToBin(lvTemp),7),7,"0") + lvFoxTimeStamp
		
		#IF DEBUGGING
		?  lvTemp
		?  LEN(lvFoxTimeStamp)
		?  lvFoxTimeStamp
		?
		#ENDIF
		lvFoxTimeStamp = THIS.BinToInt(lvFoxTimeStamp)
		
		RETURN lvFoxTimeStamp
		
	ENDPROC

	PROCEDURE getmetadatadomdoc		&& Returns a reference to an MSXml.DomDocument with the metadata xml loaded. Assumes FRX is located on desired record.
		*=======================================================
		* GetMetadataDomDoc( <frx> )
		*
		* Returns a reference to an MSXml.DomDocument with the 
		* XML stored in the STYLE column of the current record in 
		* the frx cursor. Default alias is "frx". 
		*
		* Check for null in case of errors.
		*
		* If the STYLE field is empty, a default XML document is
		* returned.
		*=======================================================
		lparameter lcFrxAlias
		
		local curSel, cXml, oDom
		
		if empty( m.lcFrxAlias )
			lcFrxAlias = 'frx'
		endif
		if not used(m.lcFrxAlias) 
			return .null.
		endif
		
		curSel = select()
		select (m.lcFrxAlias)
		
		*------------------------------------------------------
		* Extract the metadata XML from STYLE field of frx cursor 
		* record, and put default xml in if its empty:
		*------------------------------------------------------
		cXml = STYLE
		
		if empty( m.cXml )
			text noshow to m.cXml 
		<VFPData>
			<reportdata name="" type="R" script="" execute="" execwhen="" class="" classlib="" declass="" declasslib=""/> 
		</VFPData> 	
			endtext
			replace STYLE with m.cXml
		endif
		
		select (m.curSel)
		
		*------------------------------------------------------
		* Create a DomDocument and load up the xml.
		*------------------------------------------------------
		oDom = createobject("MSXml.DomDocument")
		oDom.loadXML( m.cXml )
		return oDom
	ENDPROC

	PROCEDURE getobjectsinband		&& Returns a collection of UNIQUEIDs (or RECNOs) for each object in a given band. Calls .createObjectCursor() if necessary. Parameters: cBandId [, lRecnos]
		*=======================================================
		* GetObjectsInBand( UNIQUEID [, lRecnos ] )
		*
		* returns a Collection of UNIQUEID values for each object
		* in a given Band.
		* Calls .createObjectCursor() if necessary.
		*=======================================================
		lparameter cBandId, lRecnos
		
		local oBandObjects, curSel
		curSel = select()
		
		oBandObjects = newobject("Collection")
		if not used("objects")
			THIS.createObjectCursor()
		endif
		
		select objects
		scan
			if objects.START_BAND_ID = m.cBandId
				*-------------------------------------------------------
				* the object is defined as starting in this band
				*-------------------------------------------------------
				if m.lRecnos
					oBandObjects.Add( objects.REC_NO )
				else
					oBandObjects.Add( objects.UNIQUEID )
				endif
			endif			
		endscan
		select (m.curSel)
		return oBandObjects
		
	ENDPROC

	PROCEDURE getreportattribute		&& Returns the value of a given report/header attribute. The FRX cursor must be open. Parameters: cToken [, iAlternate]
		*=======================================================
		* GetReportAttribute( cToken [, iAlt] )
		*
		* Returns the value of a given report (or header) attribute.
		* The FRX cursor must be open.
		*=======================================================
		lparameters cKey, iAlt 
		if parameters()<2
			iAlt = 0
		endif
		
		if not used('frx')
			return .F.
		endif
		
		local curSel, curRec, vRetVal
		curSel = select()
		select frx
		curRec = recno()
		
		locate for	OBJTYPE = FRX_OBJTYP_REPORTHEADER and ;
					PLATFORM = FRX_PLATFORM_WINDOWS
		
		do case
		case upper(m.cKey) = "UNITS"
			if m.iAlt = 0
				vRetVal = RULER
			else
				do case
				case RULER = FRX_RULER_FRUS
					vRetVal = UNITS_FRU_LOC
				case RULER = FRX_RULER_INCHES
					vRetVal = UNITS_INCHES_LOC
				case RULER = FRX_RULER_METRIC
					vRetVal = UNITS_METRIC_LOC
				case RULER = FRX_RULER_PIXELS
					vRetVal = UNITS_PIXELS_LOC
				case RULER = FRX_RULER_CHARACTERS
					vRetVal = UNITS_CHARACTERS_LOC
				otherwise
					vRetVal = "?"
				endcase
			endif
		
		case upper(m.cKey) = "MULTICOLUMN"
			*-------------------------------------------------------
			* If a report has multiple columns,
			* then the column width is not the default:
			*-------------------------------------------------------
			*vRetVal = (WIDTH <> -1)						
		
			*-------------------------------------------------------
			* could also check for existence of ColumnHeader:
			*-------------------------------------------------------
			*vRetVal = THIS.hasBand(OBJCODE_COLHEADER)
		
			*-------------------------------------------------------
			* or just how many columns?
			*-------------------------------------------------------
			vRetVal = (VPOS > 1)
			
		case upper(m.cKey) = "COLUMNCOUNT"
			vRetVal = VPOS
		
		case upper(m.cKey) = "PROTECTION"
			vRetVal = ORDER
		
		case upper(m.cKey) = "SNAKED_COLUMNS"
			vRetVal = BOTTOM
			
		*case upper(m.cKey) = "DEFAULT FONT"
		
		endcase
		
		THIS.goRec( m.curRec, 'frx')
		select (m.curSel)
		return m.vRetVal
		
	ENDPROC

	PROCEDURE getselectedobjectcount		&& Returns the number of selected objects in the frx cursor. Parameter: [cFrxAlias]
		*=======================================================
		* getSelectedObjectCount( <frx> )
		*
		*=======================================================
		lparameter lcFrxAlias
		if empty( m.lcFrxAlias )
			lcFrxAlias = "frx"
		endif
		local cursel, curRec, selCount
		curSel = select()
		select (m.lcFrxAlias)
		curRec = recno()
		count for	CURPOS and ;
					PLATFORM = FRX_PLATFORM_WINDOWS and ;
					OBJTYPE <> 1 ;
			to m.selCount
		
		go m.curRec
		
		select (m.curSel)
		return m.selCount
		
	ENDPROC

	PROCEDURE gettargettypetext		&& Returns a readable string version of a target Type+Code. Parameters: iObjectType, iObjectCode
		*=======================================================
		* GetTargetTypeText( OBJTYPE, OBJCODE ) 
		*
		* Returns a readable string version of a target Type/Code
		*=======================================================
		lparameters nObjType, nObjCode 
		
		if parameters() = 0
			nObjType = -1
			nObjCode = -1 
		endif
		
		do case
		case m.nObjType = FRX_OBJTYPE_MULTISELECT
			return TARGET_MULTISELECT_LOC
		
		case m.nObjType = FRX_OBJTYP_COMMENT
			return TARGET_REPORT_COMMENT_LOC
				
		case m.nObjType = FRX_OBJTYP_REPORTHEADER
			return TARGET_REPORT_GLOBAL_LOC
			
		case m.nObjType = 2
			return TARGET_WORKAREA_LOC
			
		case m.nObjType = 3
			return TARGET_INDEX_LOC
			
		case m.nObjType = 4
			return TARGET_RELATION_LOC
			
		case m.nObjType = FRX_OBJTYP_LABEL
			return TARGET_TEXT_LABEL_LOC
			
		case m.nObjType = FRX_OBJTYP_LINE
			return TARGET_LINE_LOC
			
		case m.nObjType = FRX_OBJTYP_RECTANGLE
			return TARGET_BOX_LOC
			
		case m.nObjType = FRX_OBJTYP_FIELD
			return TARGET_FIELD_LOC
			
		case m.nObjType = FRX_OBJTYP_BAND
			do case
			case m.nObjCode = FRX_OBJCOD_TITLE
				return TARGET_TITLE_LOC
				
			case m.nObjCode = FRX_OBJCOD_PAGEHEADER
				return TARGET_PAGE_HEADER_LOC
				
			case m.nObjCode = FRX_OBJCOD_COLHEADER
				return TARGET_COL_HEADER_LOC
				
			case m.nObjCode = FRX_OBJCOD_GROUPHEADER
				return TARGET_GROUP_HEADER_LOC
				
			case m.nObjCode = FRX_OBJCOD_DETAIL
				return TARGET_DETAIL_LOC
				
			case m.nObjCode = FRX_OBJCOD_GROUPFOOTER
				return TARGET_GROUP_FOOTER_LOC
				
			case m.nObjCode = FRX_OBJCOD_COLFOOTER
				return TARGET_COL_FOOTER_LOC
				
			case m.nObjCode = FRX_OBJCOD_PAGEFOOTER
				return TARGET_PAGE_FOOTER_LOC
				
			case m.nObjCode = FRX_OBJCOD_SUMMARY
				return TARGET_SUMMARY_LOC
				
			case m.nObjCode = FRX_OBJCOD_DETAILHEADER
				return TARGET_DETAIL_HEADER_LOC
				
			case m.nObjCode = FRX_OBJCOD_DETAILFOOTER
				return TARGET_DETAIL_FOOTER_LOC
				
		 	otherwise
				return TARGET_UNKNOWN_BAND_LOC
				
			endcase
			
		case m.nObjType = FRX_OBJTYP_GROUP
			return TARGET_GROUPED_LOC
			
		case m.nObjType = FRX_OBJTYP_PICTURE
			return TARGET_PICTURE_LOC
			
		case m.nObjType = FRX_OBJTYP_VARIABLE
			return TARGET_VARIABLE_LOC
			
		case m.nObjType = FRX_OBJTYP_PDRIVER
			return TARGET_PDRIVER_LOC
			
		case m.nObjType = FRX_OBJTYP_FONTRES
			return TARGET_FONTRESO_LOC
			
		case m.nObjType = FRX_OBJTYP_DATAENV
			return TARGET_DATAENV_LOC
			
		case m.nObjType = FRX_OBJTYP_DATAOBJ
			return TARGET_CURSOR_LOC
			
		otherwise
			return TARGET_UNKNOWN_LOC
		endcase
		
	ENDPROC

	PROCEDURE gettimestampstring		&& Returns a readable string version of a Fox system timestamp, using current date settings. Parameter: iStamp
		*=======================================================
		* GetTimeStampString( iStamp )
		*
		* Returns a readable string version of a Fox system 
		* timestamp, using current date settings
		*=======================================================
		lparameter tiStamp 
		
		IF EMPTY(tiStamp) OR TYPE("tiStamp") # "N"  
		   RETURN ""
		ENDIF
		LOCAL lnYearoffset,lcYear,lcMonth,;
		      lcDay,lcHour,lcMinute,lcSecond
		
		*-------------------------------------------------------
		* lnYearoffset = INT(tiStamp/2^25)   && bits 31-25
		*-------------------------------------------------------
		lnYearoffset = BITRSHIFT(tiStamp,25)
		lcYear = STR(1980 + lnYearoffset)
		
		*-------------------------------------------------------
		* lcMonth = STR(INT(tiStamp/2^21) % 2^4)  && bits 24-21
		*-------------------------------------------------------
		lcMonth = STR(BITRSHIFT(tiStamp,21) % 2^4)
		
		*-------------------------------------------------------
		* lcDay = STR(INT(tiStamp/2^16) % 2^5)    && bits 20-16
		*-------------------------------------------------------
		lcDay = STR(BITRSHIFT(tiStamp,16) % 2^5)
		
		*-------------------------------------------------------
		* lcHour = STR(INT(tiStamp/2^11) % 2^5)   && bits 15-11
		*-------------------------------------------------------
		lcHour = STR(BITRSHIFT(tiStamp,11) % 2^5)
		
		*-------------------------------------------------------
		* lcMinute = STR(INT(tiStamp/2^5) % 2^6)  && bits 10-5
		*-------------------------------------------------------
		lcMinute = STR(BITRSHIFT(tiStamp,5) % 2^6)
		
		*-------------------------------------------------------
		* lcSecond = STR(INT(tiStamp%2^5) * 2)    && bits 4-0 (two-second increments)   
		*-------------------------------------------------------
		lcSecond = STR(BITLSHIFT(tiStamp%2^5,1))
		
		RETURN TTOC({^&lcYear./&lcMonth./&lcDay. &lcHour.:&lcMinute.:&lcSecond.})	
		
	ENDPROC

	PROCEDURE getunitvaluefromfru		&& Returns a given unit value for a given value in FRUs, depending on the units. Parameters: nFruValue, iUnits
		*-------------------------------------------------
		* Return a given unit value for a given value in FRUs,
		* depending on the Units:
		*-------------------------------------------------
		lparameter nFruValue, iUnits
		
		do case
		case inlist( m.iUnits, FRX_RULER_INCHES, FRX_RULER_OFF)
			return (m.nFruValue / 10000)
		
		case m.iUnits = FRX_RULER_METRIC
			return (m.nFruValue / (0.3937 * 10000))
		
		case m.iUnits = FRX_RULER_CHARACTERS
			*---------------------------------------------------------
			* 1 char = 833.33 FRU
			* 1 char = 1/12 inch
			*---------------------------------------------------------
			return (m.nFruValue / 833.33)
		
		*case THIS.units = FRX_RULER_PIXELS 
		otherwise
			*---------------------------------------------------------
			* Designer fixed at 96 dpi
			*---------------------------------------------------------
			return (m.nFruValue * 96 / 10000 )
		
		endcase
	ENDPROC

	PROCEDURE gorec		&& Restores record pointer with bounds checking. Parameters: i, cAlias
		*=======================================================
		* GoRec( i, cAlias )
		*
		* restores record pointer with bounds checking
		*=======================================================
		lparameter iRec, cAlias
		if parameters() < 2
			cAlias = alias()
		endif
		if between( iRec, 1, reccount(m.cAlias) )
			go m.iRec in (m.cAlias)
		endif
		return
		
	ENDPROC

	PROCEDURE hasband		&& Returns .T. if the report has the specified band type. Calls .createBandCursor() if necessary. Parameters: iObjCode
		*=======================================================
		* HasBand( objCode )
		*
		* Returns .T. if the report has the specified type of 
		* band record.
		* Calls .createBandCursor() if necessary.
		*=======================================================
		lparameter iObjCode 
		
		if not used('bands')
			THIS.createBandCursor()
		endif
		
		local curSel, lRetVal
		curSel = select()
		lRetVal = .F.
		
		select bands
		locate for OBJCODE = m.iObjCode
		if found()
			lRetVal = .T.
		endif
		
		select (m.curSel)
		return m.lRetVal	
		
	ENDPROC

	PROCEDURE hasdetailheader		&& Returns .T. if the specified detail band has an associated detail header band. Calls .createBandCursor() if necessary. Parameters: cUniqueId
		*=======================================================
		* HasDetailHeader( UNIQUED )
		*
		* Returns .T. if the specifed Detail band has an associated 
		* "detail header" band.
		*
		* Calls .createBandCursor() if necessary.
		*=======================================================
		lparameter cUniqueId 
		if not used('bands')
			THIS.createBandCursor()
		endif
		local curSel, lRetval
		curSel = select()
		lRetVal = .F.
		
		select bands
		locate for UNIQUEID = m.cUniqueId
		skip -1
		lRetVal = (OBJTYPE = FRX_OBJTYP_BAND and OBJCODE = FRX_OBJCOD_DETAILHEADER)
		select (m.curSel)
		return m.lRetVal
		
		
	ENDPROC

	PROCEDURE hasprotectionflag		&& Returns .T. if the given binary data contains a specific bit set. Parameters: cBytes, iBit
		*=======================================================
		* HasProtectionFlag( cBytes, iBit )
		*
		* Returns .T. if the given binary data contains a specific 
		* bit set to true.
		*
		* Uses: BinstringToInt()
		*=======================================================
		lparameters cBinstring, iFlagBit 
		
		local iProtFlags
		iProtFlags = THIS.BinstringToInt( m.cBinstring )
		return bittest( m.iProtFlags, m.iFlagBit )
		
	ENDPROC

	PROCEDURE Init
		*=======================================================
		* Init()
		*=======================================================
		*---------------------------------
		* Determine the screen DPI:
		*---------------------------------
		#define LOGPIXELSX 88
		
		declare integer GetDeviceCaps in WIN32API integer HDC, integer item
		declare integer GetDC         in WIN32API integer hWnd
		*declare integer DeleteDC      in WIN32API integer HDC
		declare integer ReleaseDC     in WIN32API integer hWnd, integer HDC
		
		local hdc, screenDPI
		hdc    = GetDC(0)
		THIS.screenDPI = GetDeviceCaps( m.hdc, LOGPIXELSX )
		ReleaseDC( 0, m.hdc )
		*DeleteDC( m.hdc )
		
		*------------------------------------
		* Fixed for SP1: 
		* The ReportBuilder client will 
		* reset .ScreenDPI to 96 so that it is correct 
		* for design-time report designer use, even if
		* at run-time the app is running on higher-res 
		* screens.
		*------------------------------------
		
		
	ENDPROC

	PROCEDURE insertband		&& Inserts a band record into an frx cursor. Assumes that the FRX is currently selected and that the record pointer is located appropriately. Parameter: iObjCode
		*=======================================================
		* InsertBand()
		*
		* Inserts a band into an FRX. 
		* Assumes that the record pointer is located appropriately.
		*=======================================================
		lparameters liObjCode 
		
		insert blank
		replace ;
			PLATFORM 	with FRX_PLATFORM_WINDOWS, ;
			UNIQUEID 	with sys(2015), ;
			OBJTYPE  	with FRX_OBJTYP_BAND, ;
			OBJCODE  	with m.liObjCode, ;
			NOREPEAT	with .F., ;
			PAGEBREAK	with .F., ;
			COLBREAK	with .F., ;
			RESETPAGE	with .F., ;
			PLAIN		with .F., ;
			CURPOS		with .F.
		return
		
	ENDPROC

	PROCEDURE insertdataenvrecord		&& Inserts a data environment object record into an FRX cursor. Assumes that the record pointer is appropriately located. Parameters: ID, NAME, EXPR, CODE
		*=======================================================
		* InsertDataEnvRecord( ID, NAME, EXPR, CODE )
		*
		* Inserts a data-environment object record into an FRX. 
		* Assumes that the record pointer is located appropriately.
		*=======================================================
		lparameters liObjType, lcName, lcExpr, lcMethods
		
		insert blank
		replace ;
			PLATFORM 	with FRX_PLATFORM_WINDOWS, ;
			OBJTYPE  	with m.liObjType, ;
			NAME		with m.lcName, ;
			EXPR		with m.lcExpr, ;
			TAG         with m.lcMethods, ;
			ENVIRON		with .F., ;
			CURPOS		with .F.
		
		return
		
	ENDPROC

	PROCEDURE insertdetailband		&& Inserts a detail band into the frx cursor. Assumes: the record pointer is located appropriately. Parameters: none
		*=======================================================
		* InsertDetailBand()
		*
		* Inserts a detail band into an FRX. 
		* Assumes that the record pointer is located appropriately.
		*=======================================================
		THIS.insertBand( FRX_OBJCOD_DETAIL )
		return
		
	ENDPROC

	PROCEDURE insertdetailheaderfooter		&& Inserts detail header and footer bands into the frx cursor. Assumes: record pointer is located on the detail band record. Parameters: none
		*=======================================================
		* InsertDetailHeaderFooter()
		*
		* Inserts Detail Header and Footer bands into the FRX.
		* Assumes: we are positioned on the detail band:
		*=======================================================
		insert blank
		replace ;
			PLATFORM with FRX_PLATFORM_WINDOWS, ;
			UNIQUEID with sys(2015), ;
			OBJTYPE  with FRX_OBJTYP_BAND, ;
			OBJCODE  with FRX_OBJCOD_DETAILFOOTER
		skip -1
		insert before blank
		replace ;
			PLATFORM with FRX_PLATFORM_WINDOWS, ;
			UNIQUEID with sys(2015), ;
			OBJTYPE  with FRX_OBJTYP_BAND, ;
			OBJCODE  with FRX_OBJCOD_DETAILHEADER
		skip					
		return
		
	ENDPROC

	PROCEDURE insertsummaryband		&& Inserts a summary band into the frx cursor. Parameters: lBreakToNewPage, lPageHeader, lPageFooter
		*=======================================================
		* InsertSummaryBand( lNewPage, lPageHeader, lPageFooter )
		*
		* Inserts a Summary band into an FRX
		
		*=======================================================
		
		lparameter lNewPage, lPageHeader, lPageFooter
		
		locate for OBJTYPE = FRX_OBJTYP_BAND and ;
			 	   OBJCODE = FRX_OBJCOD_PAGEFOOTER
		
		THIS.insertBand( FRX_OBJCOD_SUMMARY )
		replace ;
			HEIGHT    	with 5000.000, ;
			PAGEBREAK   with m.lNewPage, ;
			EJECTBEFOR  with m.lPageHeader, ;
			EJECTAFTER  with m.lPageFooter
		return
		
	ENDPROC

	PROCEDURE inserttitleband		&& Inserts a title band into the frx cursor. Parameter: lBreakToNewPage
		*=======================================================
		* InsertTitleBand( lNewPage )
		*
		* Inserts a Title band record into the FRX
		*=======================================================
		lparameter lNewPage
		
		go top
		*-------------------------------------------------------
		* will be inserted as the second record (as is appropriate)
		*-------------------------------------------------------
		THIS.insertBand( FRX_OBJCOD_TITLE )
		replace ;
			HEIGHT    with 5000.000, ;
			PAGEBREAK with m.lNewPage
		return
		
	ENDPROC

	PROCEDURE inttobin		&& Returns a binary form of an integer. Parameter: iNumber
		*=======================================================
		* IntToBin( int )
		*
		* Returns a binary form of an integer
		*=======================================================
		LPARAMETERS tnInteger
		LOCAL lnInteger,lcBinary,lnDivisor,lnCount
		
		IF EMPTY(tnInteger)
			RETURN "0"
		ENDIF
		lnInteger=INT(tnInteger)
		lcBinary=""
		FOR lnCount = 31 TO 0 STEP -1
			lnDivisor=2^lnCount
			IF lnDivisor>lnInteger
				lcBinary=lcBinary+"0"
				LOOP
			ENDIF
			lcBinary=lcBinary+IIF((lnInteger/lnDivisor)>0,"1","0")
			lnInteger=INT(lnInteger-lnDivisor)
		ENDFOR
		RETURN lcBinary
		
	ENDPROC

	PROCEDURE inttobinstring		&& Returns a string of bytes, the binary version of a given integer.
		*=======================================================
		* InttoBinString( int )
		*
		* Returns a string of bytes, the binary version of a 
		* given integer.
		*
		* BinChar & Integer conversion, based on code by RS
		*=======================================================
		lparameter i
		
		local cBytes, b
		cBytes = ""
		
		do while m.i <> 0 
			b = m.i % 256
			i = floor( m.i/256 )
			cBytes = m.cBytes + chr(m.b)			
		enddo	
		return m.cBytes
		
	ENDPROC

	PROCEDURE pixelstofru		&& Returns the FRU value of a given measurement in pixels.
		*=======================================================
		* PixelsToFru( int )
		*
		* return a value in FRUs of a given pixel dimension
		*=======================================================
		lparameter nPix 
		*------------------------------------
		* Fixed for SP1: 
		* use the .screenDPI property instead of 
		* the hard-coded 96 dpi value. The ReportBuilder
		* client will reset .screenDPI to 96 so that it
		* is correct for design-time report designer use.
		*------------------------------------
		*return round(( nPix * 10000 )/96 , 3 )
		return round(( nPix * 10000 )/this.screenDPI , 3 )
		
	ENDPROC

	PROCEDURE popprintenv		&& Restores the printer environment from a previously saved cursor. See .PushPrintEnvToCursor() method. Assumes: previously saved cursor is currently selected.
		*=======================================================
		* popPrintEnv()
		*
		* assumes the FRX file to pop from is selected.
		*=======================================================
		result = sys(1037,3)
		return
		
	ENDPROC

	PROCEDURE pushprintenvtocursor		&& Saves the current printer environment to a cursor. Parameter: cSavedInAlias
		*=======================================================
		* pushPrintEnvToCursor( <alias> )
		*=======================================================
		lparameter cRegisterAlias
		local curSel
		curSel = select()
		
		select * ;
			from frx ;
			where .F. ;
			into cursor (m.cRegisterAlias) ;
			readwrite
		
		append Blank
		
		result = sys(1037,2)
		
		select (m.curSel)
		return
		
	ENDPROC

	PROCEDURE setcolumncount		&& Adds or removes columns (and column header/footer records) from the FRX cursor. Assumes: the frx cursor is selected. Parameters: iColumns
		*=======================================================
		* SetColumnCount( iCols )
		*
		* Adds or subtracts columns (and column header/footer
		* records from the FRX.
		* Assumes: the current workarea contains the FRX cursor.
		* Calls: .createObjectCursor() if necessary.
		*=======================================================
		lparameter iCols
		
		local lHasColBands, curRec, cFrxAlias
		cFrxAlias = alias()
		curRec = recno( m.cFrxAlias )
		
		locate for 	OBJTYPE = FRX_OBJTYP_REPORTHEADER and ;
					PLATFORM = FRX_PLATFORM_WINDOWS
		
		replace VPOS with m.iCols
		
		locate for	OBJTYPE = FRX_OBJTYP_BAND and ;
					OBJCODE = FRX_OBJCOD_COLHEADER and ;
					PLATFORM = FRX_PLATFORM_WINDOWS
					
		lHasColBands = found()
		
		THIS.goRec(m.curRec, m.cFrxAlias)
		
		if (m.lHasColBands and m.iCols = 1) or ;
		   (not m.lHasColBands and m.iCols > 1)
		
			THIS.createObjectCursor()
		
			do case 
			case (m.lHasColBands and m.iCols = 1)
				*-------------------------------------------------------
				* Remove the column header/footer bands:
				*-------------------------------------------------------
				locate for OBJTYPE = FRX_OBJTYP_BAND ;
				       and OBJCODE = FRX_OBJCOD_COLHEADER ;
					   and PLATFORM = FRX_PLATFORM_WINDOWS
		
				if found()
					delete
				endif
				locate for OBJTYPE = FRX_OBJTYP_BAND ;
				       and OBJCODE = FRX_OBJCOD_COLFOOTER ;
					   and PLATFORM = FRX_PLATFORM_WINDOWS
		
				if found()
					delete
				endif
					
			case (not m.lHasColBands and m.iCols > 1)
				*-------------------------------------------------------
				* Insert the column header/footer bands:
				*-------------------------------------------------------
				locate for OBJTYPE = FRX_OBJTYP_BAND ;
					   and OBJCODE = FRX_OBJCOD_PAGEHEADER ;
		 			   and PLATFORM = FRX_PLATFORM_WINDOWS
		
				insert blank
				replace ;
					PLATFORM 	with FRX_PLATFORM_WINDOWS, ;
					UNIQUEID 	with sys(2015), ;
					OBJTYPE  	with FRX_OBJTYP_BAND, ;
					OBJCODE  	with FRX_OBJCOD_COLHEADER, ;
					NOREPEAT	with .F., ;
					PAGEBREAK	with .F., ;
					COLBREAK	with .F., ;
					RESETPAGE	with .F., ;
					PLAIN		with .F., ;
					CURPOS		with .F.
		
				locate for OBJTYPE = FRX_OBJTYP_BAND ;
					   and OBJCODE = FRX_OBJCOD_PAGEFOOTER ;
					   and PLATFORM = FRX_PLATFORM_WINDOWS
		
				insert before blank
				replace ;
					PLATFORM 	with FRX_PLATFORM_WINDOWS, ;
					UNIQUEID 	with sys(2015), ;
					OBJTYPE  	with FRX_OBJTYP_BAND, ;
					OBJCODE  	with FRX_OBJCOD_COLFOOTER, ;
					NOREPEAT	with .F., ;
					PAGEBREAK	with .F., ;
					COLBREAK	with .F., ;
					RESETPAGE	with .F., ;
					PLAIN		with .F., ;
					CURPOS		with .F.
		
			endcase
		
			*-------------------------------------------------------
			* Refresh the band cursor:
			*-------------------------------------------------------
			THIS.createBandCursor()
			
			*-------------------------------------------------------
			* Using the offsets in object cursor and the 
			* new band heights in band cursor, re-sync the 
			* objects' positions:
			*-------------------------------------------------------
			THIS.synchObjectPositions()
		
			THIS.goRec(m.curRec, m.cFrxAlias)
		
		endif
		return
		
	ENDPROC

	PROCEDURE stripquotes		&& Returns a string with embraced string delimiters removed. Parameters: cString
		lparameter lcValue
		
		do case
		case left( m.lcValue, 1 ) == ["] and right( m.lcValue, 1 ) == ["]
			return substr( m.lcValue, 2, len( m.lcValue ) - 2 )
		
		case left( m.lcValue, 1 ) == ['] and right( m.lcValue, 1 ) == [']
			return substr( m.lcValue, 2, len( m.lcValue ) - 2 )
		
		otherwise
			return m.lcValue
		
		endcase
		
	ENDPROC

	PROCEDURE synchobjectpositions		&& Updates VPOS values in the FRX cursor for each object, based on which band the object starts in, and the current height of each band as expressed in the bands cursor. Assumes: band and object cursors exist; current alias is frx cursor; no recno restore.
		*=======================================================
		* SynchObjectPositions()
		*
		* Updates the VPOS values in the FRX for each object,
		* based on which band the object starts in, and the current
		* height of each band as expressed in the bands cursor.
		*
		* Assumes: 
		*   - bands and objects cursors have been prepared,
		*   - current alias is an FRX cursor
		*=======================================================
		local curSel, curRec, cUID
		curSel = select()
		curRec = recno()
		
		*-------------------------------------------------------
		* Scan through the FRX cursor, 
		* updating the VPOS to maintain the 
		* same relative offset from the band it belongs to.
		*-------------------------------------------------------
		scan for 	inlist( OBJTYPE, ;
						FRX_OBJTYP_LABEL, FRX_OBJTYP_FIELD, FRX_OBJTYP_LINE, ;
						FRX_OBJTYP_RECTANGLE, FRX_OBJTYP_PICTURE ) and ;
					not deleted() and ;
					PLATFORM = FRX_PLATFORM_WINDOWS
		
			cUID = UNIQUEID
		
			*-------------------------------------------------------
			* for each object, restore the relative positions:
			*-------------------------------------------------------
			select objects
			locate for UNIQUEID = cUID
			select bands
			locate for UNIQUEID = objects.START_BAND_ID
		
			select (curSel)
			if found("bands") and found("objects")
		
				*-------------------------------------------------------
				* The bands and objects cursors use pixel-based math, so
				* convert back to FRUs:
				*-------------------------------------------------------
				replace VPOS with ;
					THIS.pixelsToFru( bands.P_START + objects.BAND_OFFSET	)
			endif
					
		endscan
		THIS.GoRec( m.curRec, alias() )
		return
		
	ENDPROC

ENDDEFINE

DEFINE CLASS frxdevicehelper AS custom 
	*<ClassComment>
		*This class knows how to read printer environment information. Usage:
		*
		*   x = NEWOBJECT( "frxDeviceHelper" )
		*   IF x.LoadDeviceInfo( cDRIVER, cDEVICE, frx.TAG2 )
		*       ? x.DpiX
		*       ? x.DpiY
		*       :
		*      etc
	*</ClassComment>
 	*< CLASSDATA: Baseclass="custom" Timestamp="" Scale="Pixels" Uniqueid="" />

	#INCLUDE "_frxcursor.h"
	*<DefinedPropArrayMethod>
		*m: loaddeviceinfo		&& Parses out print device parameters  into member properties given specific device information: Parameters: cDriver, cDevice, cDevMode. Assumes current VFP default printer if no parameters specified.
		*m: loadfromfrx		&& Loads printer device parameters from an FRX cursor header record. Restores recno and selected alias. Params: [ cFrxAlias ] (assumes "frx" if none specified.)
		*m: loadfromhdc
		*m: loadfromstrings		&& Loads printer device parameters from string variables. Params: cExpr, cTag
		*p: actualx		&& Physical width of page in device units
		*p: actualy		&& Physical length of page in device units
		*p: dpix		&& Logical pixels-per-inch in the X dimension
		*p: dpiy		&& Logical pixels-per-inch in the Y dimension
		*p: errormessage		&& Occurs when the Valid event returns false (.F.), and provides a means to display an error message.
		*p: mmx		&& Horizontal page size in millimeters
		*p: mmy		&& Vertical page size in millimeters
		*p: offsetx		&& Physical printable page left margin
		*p: offsety		&& Physical printable page top margin
		*p: orientation		&& 0=Portrait, 1=Landscape
		*p: printablex		&& Horizontal page width in pixels
		*p: printabley		&& Vertical page length in pixels
		*p: _memberdata		&& XML Metadata for customizable properties
	*</DefinedPropArrayMethod>

	*<PropValue>
		actualx = 0
		actualy = 0
		dpix = 0
		dpiy = 0
		errormessage = ("")
		mmx = 0
		mmy = 0
		Name = "frxdevicehelper"
		offsetx = 0
		offsety = 0
		orientation = 0
		printablex = 0
		printabley = 0
		_memberdata = <VFPData>
			<memberdata name="loaddeviceinfo" type="Method" display="LoadDeviceInfo" favorites="false" override="false" script=""/>
			<memberdata name="loadfromfrx" type="Method" display="LoadFromFrx" favorites="false" override="false" script=""/>
			<memberdata name="loadfromstrings" type="Method" display="LoadFromStrings" favorites="false" override="false" script=""/>
			<memberdata name="loadfromhdc" type="Method" display="LoadFromHDC" favorites="false" override="false" script=""/>
			<memberdata name="dpix" type="Property" display="DpiX" favorites="false" override="false" script=""/>
			<memberdata name="dpiy" type="Property" display="DpiY" favorites="false" override="false" script=""/>
			<memberdata name="offsetx" type="Property" display="OffsetX" favorites="false" override="false" script=""/>
			<memberdata name="offsety" type="Property" display="OffsetY" favorites="false" override="false" script=""/>
			<memberdata name="actualx" type="Property" display="ActualX" favorites="false" override="false" script=""/>
			<memberdata name="actualy" type="Property" display="ActualY" favorites="false" override="false" script=""/>
			<memberdata name="mmx" type="Property" display="mmX" favorites="false" override="false" script=""/>
			<memberdata name="mmy" type="Property" display="mmY" favorites="false" override="false" script=""/>
			<memberdata name="printablex" type="Property" display="PrintableX" favorites="false" override="false" script=""/>
			<memberdata name="printabley" type="Property" display="PrintableY" favorites="false" override="false" script=""/>
			<memberdata name="orientation" type="Property" display="Orientation" favorites="false" override="false" script=""/>
		</VFPData>
	*</PropValue>
	
	PROCEDURE Init
		declare integer GetLastError ;
			in win32api
			
		declare integer SetLastError ;
			in win32api ;
			integer i
		
		declare integer CreateIC ;
			in gdi32 ;
			string lpszDriver, ;
			string lpszDevice, ;
			string lpszOutput, ;
			string lpdvmInit
			
		declare integer GetDeviceCaps ;
			in gdi32 ;
			integer hdc, ;
			integer nIndex 	
		
		declare integer DeleteDC ;
			in gdi32 ;
			integer hdc
				
		return .T.
		
	ENDPROC

	PROCEDURE loaddeviceinfo		&& Parses out print device parameters  into member properties given specific device information: Parameters: cDriver, cDevice, cDevMode. Assumes current VFP default printer if no parameters specified.
		lparameters lcDriver, lcDevice, lcDEVMODE
		
		THIS.errorMessage = ""
		
		local lRetVal
		lRetVal = .T.
		
		if empty( m.lcDevice )
			*---------------------------------------
			* Use default printer settings:
			*---------------------------------------
			lcDriver  = ""
			lcDevice  = set("PRINTER",3)
			lcDEVMODE = ""
		endif
		
		try 
			local iHDC, vResult
			iHDC = CreateIC( m.lcDriver, m.lcDevice, chr(0), m.lcDEVMODE )
			if m.iHDC = 0
				error CREATE_IC_FAILURE_LOC
			else
				THIS.LoadFromHDC( m.iHDC )
				vResult = DeleteDC( m.iHDC )
			endif
			
		catch to oErr
			THIS.errorMessage = oErr.message
			lRetVal = .F.
		endtry
		return m.lRetVal
	ENDPROC

	PROCEDURE loadfromfrx		&& Loads printer device parameters from an FRX cursor header record. Restores recno and selected alias. Params: [ cFrxAlias ] (assumes "frx" if none specified.)
		lparameter lcFrxAlias
		
		THIS.errorMessage = ""
		
		local curSel, curRec, lRetVal
		store .F. to lRetVal
		
		if empty(m.lcFrxAlias)
			lcFrxAlias = "frx"
		endif
		if not used( m.lcFrxAlias )
			return m.lRetVal
		endif
		
		*--------------------------------
		* Save current data/cursor state:
		*--------------------------------
		curSel = select()
		select (m.lcFrxAlias)
		curRec = recno()
		
		locate for PLATFORM="WINDOWS" and OBJTYPE = 1 and OBJCODE = 53
		if found()
		
			*---------------------------------------------------
			* Get the device info settings:
			*---------------------------------------------------
			lRetVal = THIS.LoadFromStrings( EXPR, TAG2 )
		
		endif
		*---------------------------------------------------
		* Restore data/cursor state:
		*---------------------------------------------------
		if m.curRec <= reccount()
			go m.curRec
		endif
		select (m.curSel)
		return m.lRetVal
		
	ENDPROC

	PROCEDURE loadfromhdc
		lparameters tiHDC
		
		#define HORZSIZE          4  && Horizontal size in millimeters 
		#define VERTSIZE          6  && Vertical size in millimeters 
		
		#define HORZRES           8  && Printable page width  / Horizontal width in pixels 
		#define VERTRES          10  && Printable page length / Vertical height in pixels 
		
		#define LOGPIXELSX       88  && DPI / Logical pixels/inch in X dimension
		#define LOGPIXELSY       90  && DPI / Logical pixels/inch in Y dimension
		
		#define PHYSICALWIDTH   110  && Actual page width  / Physical Width in device units
		#define PHYSICALHEIGHT  111  && Actual page length / Physical Height in device units
		
		#define PHYSICALOFFSETX 112  && Printable page left margin / Physical Printable Area x margin 
		#define PHYSICALOFFSETY 113  && Printable page top margin  / Physical Printable Area y margin
		
		with THIS
			.dpiX        = GetDeviceCaps( m.tiHDC, LOGPIXELSX )
			.dpiY        = GetDeviceCaps( m.tiHDC, LOGPIXELSY )
			.OffsetX     = GetDeviceCaps( m.tiHDC, PHYSICALOFFSETX )
			.OffsetY     = GetDeviceCaps( m.tiHDC, PHYSICALOFFSETY )
			.PrintableX  = GetDeviceCaps( m.tiHDC, HORZRES )
			.PrintableY  = GetDeviceCaps( m.tiHDC, VERTRES )
			.mmX         = GetDeviceCaps( m.tiHDC, HORZSIZE )
			.mmY         = GetDeviceCaps( m.tiHDC, VERTSIZE )
			.ActualX     = GetDeviceCaps( m.tiHDC, PHYSICALWIDTH )
			.ActualY     = GetDeviceCaps( m.tiHDC, PHYSICALHEIGHT )
		endwith
		return
	ENDPROC

	PROCEDURE loadfromstrings		&& Loads printer device parameters from string variables. Params: cExpr, cTag
		lparameters tcEXPR, tcTAG2
		
		local i, cLine, cDriver, lDriver, cDevice, lDevice
		
		*---------------------------------------------------
		* Read DRIVER and DEVICE values from EXPR	
		*---------------------------------------------------
		for i = 1 to memlines(m.tcEXPR)
			cLine = mline(m.tcEXPR,m.i)
			do case
			case m.cLine = "DRIVER=" and not m.lDriver
				cDriver = substr(m.cLine,8)
				lDriver = .T.
		
			case m.cLine = "DEVICE=" and not m.lDevice
				cDevice = substr(m.cLine,8)
				lDevice = .T.
				
			endcase		
			if m.lDevice and m.lDriver
				exit
			endif
		endfor
		
		*---------------------------------------------------
		* Get the device info settings:
		*---------------------------------------------------
		return THIS.LoadDeviceInfo( m.cDriver, m.cDevice, m.tcTAG2 )
		
	ENDPROC

ENDDEFINE
