<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Calc" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
REM ===			The ScriptForge library and its associated libraries are part of the LibreOffice project.				===
REM	===						The SFDocuments library is one of the associated libraries.									===
REM ===					Full documentation is available on https://help.libreoffice.org/								===
REM =======================================================================================================================

Option Compatible
Option ClassModule

Option Explicit

&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
&apos;&apos;&apos;	SF_Calc
&apos;&apos;&apos;	=======
&apos;&apos;&apos;
&apos;&apos;&apos;		The SFDocuments library gathers a number of methods and properties making easy
&apos;&apos;&apos;		managing and manipulating LibreOffice documents
&apos;&apos;&apos;
&apos;&apos;&apos;		Some methods are generic for all types of documents: they are combined in the SF_Document module.
&apos;&apos;&apos;		Specific properties and methods are implemented in the concerned subclass(es) SF_Calc, SF_Base, ...
&apos;&apos;&apos;
&apos;&apos;&apos;		To workaround the absence of class inheritance in LibreOffice Basic, some redundancy is necessary
&apos;&apos;&apos;		Each subclass MUST implement also the generic methods and properties, even if they only call
&apos;&apos;&apos;		the parent methods and properties.
&apos;&apos;&apos;		They should also duplicate some generic private members as a subset of their own set of members
&apos;&apos;&apos;
&apos;&apos;&apos;		The SF_Calc module is focused on :
&apos;&apos;&apos;			- management (copy, insert, move, ...) of sheets within a Calc document
&apos;&apos;&apos;			- exchange of data between Basic data structures and Calc ranges of values
&apos;&apos;&apos;
&apos;&apos;&apos;		The current module is closely related to the &quot;UI&quot; service of the ScriptForge library
&apos;&apos;&apos;
&apos;&apos;&apos;		Service invocation examples:
&apos;&apos;&apos;		1) From the UI service
&apos;&apos;&apos;			Dim ui As Object, oDoc As Object
&apos;&apos;&apos;			Set ui = CreateScriptService(&quot;UI&quot;)
&apos;&apos;&apos;			Set oDoc = ui.CreateDocument(&quot;Calc&quot;, ...)
&apos;&apos;&apos;				&apos; or Set oDoc = ui.OpenDocument(&quot;C:\Me\MyFile.ods&quot;)
&apos;&apos;&apos;		2) Directly if the document is already opened
&apos;&apos;&apos;			Dim oDoc As Object
&apos;&apos;&apos;			Set oDoc = CreateScriptService(&quot;SFDocuments.Calc&quot;, &quot;Untitled 1&quot;)	&apos;	Default = ActiveWindow
&apos;&apos;&apos;				&apos; or Set oDoc = CreateScriptService(&quot;SFDocuments.Calc&quot;, &quot;Untitled 1&quot;)	&apos;	Untitled 1 is presumed a Calc document
&apos;&apos;&apos;			&apos; The substring &quot;SFDocuments.&quot; in the service name is optional
&apos;&apos;&apos;
&apos;&apos;&apos;		Definitions: 
&apos;&apos;&apos;			Many methods require a &quot;Sheet&quot; or a &quot;Range&quot; as argument. (NB: a single cell is considered as a special case of a Range)
&apos;&apos;&apos;			Usually, within a specific Calc instance, sheets and ranges are given as a string: &quot;SheetX&quot; and &quot;D2:F6&quot;
&apos;&apos;&apos;			Multiple ranges are not supported in this context.
&apos;&apos;&apos;			Additionally, the .Sheet and .Range methods return a reference that may be used
&apos;&apos;&apos;			as argument of a method called from another instance of the Calc service
&apos;&apos;&apos;			Example:
&apos;&apos;&apos;				Dim oDocA As Object	:	Set oDocA = ui.OpenDocument(&quot;C:\FileA.ods&quot;, Hidden := True, ReadOnly := True)
&apos;&apos;&apos;				Dim oDocB As Object	:	Set oDocB = ui.OpenDocument(&quot;C:\FileB.ods&quot;)
&apos;&apos;&apos;				oDocB.CopyToRange(oDocA.Range(&quot;SheetX.D4:F8&quot;), &quot;D2:F6&quot;)	&apos; CopyToRange(source, target)
&apos;&apos;&apos;
&apos;&apos;&apos;			Sheet: the sheet name as a string or an object produced by .Sheet()
&apos;&apos;&apos;						&quot;~&quot; = current sheet
&apos;&apos;&apos;			Range: a string designating a set of contiguous cells located in a sheet of the current instance
&apos;&apos;&apos;						&quot;~&quot; = current selection (if multiple selections, its 1st component)
&apos;&apos;&apos;					or an object produced by .Range()
&apos;&apos;&apos;				The sheet name is optional (default = active sheet). Surrounding quotes and $ signs are optional
&apos;&apos;&apos;					~.~, ~							The current selection in the active sheet
&apos;&apos;&apos;					&apos;$SheetX&apos;.D2 or $D$2			A single cell
&apos;&apos;&apos;					&apos;$SheetX&apos;.D2:F6, D2:D10			Multiple cells
&apos;&apos;&apos;					&apos;$SheetX&apos;.A:A or 3:5			All cells in the same column or row up to the last active cell
&apos;&apos;&apos;					SheetX.*						All cells up to the last active cell
&apos;&apos;&apos;					myRange							A range name at spreadsheet level
&apos;&apos;&apos;					~.yourRange, SheetX.someRange	A range name at sheet level
&apos;&apos;&apos;					myDoc.Range(&quot;SheetX.D2:F6&quot;)
&apos;&apos;&apos;											A range within the sheet SheetX in file associated with the myDoc Calc instance
&apos;&apos;&apos;
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;

REM ================================================================== EXCEPTIONS

Private Const UNKNOWNFILEERROR		=	&quot;UNKNOWNFILEERROR&quot;
Private Const BASEDOCUMENTOPENERROR	=	&quot;BASEDOCUMENTOPENERROR&quot;
Private Const CALCADDRESSERROR		=	&quot;CALCADDRESSERROR&quot;
Private Const DUPLICATESHEETERROR	=	&quot;DUPLICATESHEETERROR&quot;
Private Const OFFSETADDRESSERROR	=	&quot;OFFSETADDRESSERROR&quot;
Private Const CALCFORMNOTFOUNDERROR	=	&quot;CALCFORMNOTFOUNDERROR&quot;

REM ============================================================= PRIVATE MEMBERS

Private [Me]					As Object
Private [_Parent]				As Object
Private [_Super]				As Object		&apos;	Document superclass, which the current instance is a subclass of
Private ObjectType				As String		&apos;	Must be CALC
Private ServiceName				As String

&apos;	Window component
Private _Component				As Object		&apos;	com.sun.star.lang.XComponent

Type _Address
	ObjectType					As String		&apos;	Must be &quot;SF_CalcReference&quot;
	ServiceName					As String		&apos;	Must be &quot;SFDocuments.CalcReference&quot;
	RawAddress					As String
	Component					As Object		&apos;	com.sun.star.lang.XComponent
	SheetName					As String
	SheetIndex					As Integer
	RangeName					As String
	Height						As Long
	Width						As Long
	XSpreadSheet				As Object		&apos;	com.sun.star.sheet.XSpreadsheet
	XCellRange					As Object		&apos;	com.sun.star.table.XCellRange
End Type

REM ============================================================ MODULE CONSTANTS

Private Const cstSHEET			= 1
Private Const cstRANGE			= 2

Private Const MAXCOLS			= 2^10					&apos;	Max number of columns in a sheet
Private Const MAXROWS			= 2^20					&apos;	Max number of rows in a sheet

Private Const CALCREFERENCE		= &quot;SF_CalcReference&quot;	&apos;	Object type of _Address
Private Const SERVICEREFERENCE	= &quot;SFDocuments.CalcReference&quot;
														&apos;	Service name of _Address (used in Python)

Private Const ISCALCFORM		= 2						&apos;	Form is stored in a Calc document

REM ====================================================== CONSTRUCTOR/DESTRUCTOR

REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
	Set [Me] = Nothing
	Set [_Parent] = Nothing
	Set [_Super] = Nothing
	ObjectType = &quot;CALC&quot;
	ServiceName = &quot;SFDocuments.Calc&quot;
	Set _Component = Nothing
End Sub		&apos;	SFDocuments.SF_Calc Constructor

REM -----------------------------------------------------------------------------
Private Sub Class_Terminate()
	Call Class_Initialize()
End Sub		&apos;	SFDocuments.SF_Calc Destructor

REM -----------------------------------------------------------------------------
Public Function Dispose() As Variant
	If Not IsNull([_Super]) Then Set [_Super] = [_Super].Dispose()
	Call Class_Terminate()
	Set Dispose = Nothing
End Function	&apos;	SFDocuments.SF_Calc Explicit Destructor

REM ================================================================== PROPERTIES

REM -----------------------------------------------------------------------------
Property Get CurrentSelection() As Variant
&apos;&apos;&apos;	Returns as a string the currently selected range or as an array the list of the currently selected ranges
	CurrentSelection = _PropertyGet(&quot;CurrentSelection&quot;)
End Property	&apos;	SFDocuments.SF_Calc.CurrentSelection (get)

REM -----------------------------------------------------------------------------
Property Let CurrentSelection(Optional ByVal pvSelection As Variant)
&apos;&apos;&apos;	Set the selection to a single or a multiple range
&apos;&apos;&apos;	The argument is a string or an array of strings

Dim sRange As String			&apos;	A single selection
Dim oCellRanges	As Object		&apos;	com.sun.star.sheet.SheetCellRanges
Dim vRangeAddresses As Variant	&apos;	Array of com.sun.star.table.CellRangeAddress
Dim i As Long
Const cstThisSub = &quot;SFDocuments.Calc.setCurrentSelection&quot;
Const cstSubArgs = &quot;Selection&quot;

	On Local Error GoTo Catch

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If IsArray(pvSelection) Then
			If Not ScriptForge.SF_Utils._ValidateArray(pvSelection, &quot;pvSelection&quot;, 1, V_STRING, True)  Then GoTo Finally
		Else
			If Not ScriptForge.SF_Utils._Validate(pvSelection, &quot;pvSelection&quot;, V_STRING) Then GoTo Finally
		End If
	End If

Try:
	If IsArray(pvSelection) Then
		Set oCellRanges = _Component.createInstance(&quot;com.sun.star.sheet.SheetCellRanges&quot;)
		vRangeAddresses = Array()
		ReDim vRangeAddresses(0 To UBound(pvSelection))
		For i = 0 To UBound(pvSelection)
			vRangeAddresses(i) = Range(pvSelection(i)).XCellRange.RangeAddress
		Next i
		oCellRanges.addRangeAddresses(vRangeAddresses, False)
		_Component.CurrentController.select(oCellRanges)
	Else
		_Component.CurrentController.select(_ParseAddress(pvSelection).XCellRange)
	End If

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Property
Catch:
	GoTo Finally
End Property	&apos;	SFDocuments.SF_Calc.CurrentSelection (let)

REM -----------------------------------------------------------------------------
Property Get Height(Optional ByVal RangeName As Variant) As Long
&apos;&apos;&apos;	Returns the height in # of rows of the given range
	Height = _PropertyGet(&quot;Height&quot;, RangeName)
End Property	&apos;	SFDocuments.SF_Calc.Height

REM -----------------------------------------------------------------------------
Property Get LastCell(Optional ByVal SheetName As Variant) As String
&apos;&apos;&apos;	Returns the last used cell in a given sheet
	LastCell = _PropertyGet(&quot;LastCell&quot;, SheetName)
End Property	&apos;	SFDocuments.SF_Calc.LastCell

REM -----------------------------------------------------------------------------
Property Get LastColumn(Optional ByVal SheetName As Variant) As Long
&apos;&apos;&apos;	Returns the last used column in a given sheet
	LastColumn = _PropertyGet(&quot;LastColumn&quot;, SheetName)
End Property	&apos;	SFDocuments.SF_Calc.LastColumn

REM -----------------------------------------------------------------------------
Property Get LastRow(Optional ByVal SheetName As Variant) As Long
&apos;&apos;&apos;	Returns the last used column in a given sheet
	LastRow = _PropertyGet(&quot;LastRow&quot;, SheetName)
End Property	&apos;	SFDocuments.SF_Calc.LastRow

REM -----------------------------------------------------------------------------
Property Get Range(Optional ByVal RangeName As Variant) As Variant
&apos;&apos;&apos;	Returns a (internal) range object
	Range = _PropertyGet(&quot;Range&quot;, RangeName)
End Property	&apos;	SFDocuments.SF_Calc.Range

REM -----------------------------------------------------------------------------
Property Get Sheet(Optional ByVal SheetName As Variant) As Variant
&apos;&apos;&apos;	Returns a (internal) sheet object
	Sheet = _PropertyGet(&quot;Sheet&quot;, SheetName)
End Property	&apos;	SFDocuments.SF_Calc.Sheet

REM -----------------------------------------------------------------------------
Property Get Sheets() As Variant
&apos;&apos;&apos;	Returns an array listing the existing sheet names
	Sheets = _PropertyGet(&quot;Sheets&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Sheets

REM -----------------------------------------------------------------------------
Property Get Width(Optional ByVal RangeName As Variant) As Long
&apos;&apos;&apos;	Returns the width in # of columns of the given range
	Width = _PropertyGet(&quot;Width&quot;, RangeName)
End Property	&apos;	SFDocuments.SF_Calc.Width

REM -----------------------------------------------------------------------------
Property Get XCellRange(Optional ByVal RangeName As Variant) As Variant
&apos;&apos;&apos;	Returns a UNO object of type com.sun.star.Table.CellRange
	XCellRange = _PropertyGet(&quot;XCellRange&quot;, RangeName)
End Property	&apos;	SFDocuments.SF_Calc.XCellRange

REM -----------------------------------------------------------------------------
Property Get XSpreadsheet(Optional ByVal SheetName As Variant) As Variant
&apos;&apos;&apos;	Returns a UNO object of type com.sun.star.sheet.XSpreadsheet
	XSpreadsheet = _PropertyGet(&quot;XSpreadsheet&quot;, SheetName)
End Property	&apos;	SFDocuments.SF_Calc.XSpreadsheet

REM ===================================================================== METHODS

REM -----------------------------------------------------------------------------
Public Function Activate(Optional ByVal SheetName As Variant) As Boolean
&apos;&apos;&apos; Make the current document or the given sheet active
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: Default = the Calc document as a whole
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the document or the sheet could be made active
&apos;&apos;&apos;		Otherwise, there is no change in the actual user interface
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.Activate(&quot;SheetX&quot;)

Dim bActive As Boolean				&apos;	Return value
Dim oSheet As Object				&apos;	Reference to sheet
Const cstThisSub = &quot;SFDocuments.Calc.Activate&quot;
Const cstSubArgs = &quot;[SheetName]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bActive = False

Check:
	If IsMissing(SheetName) Or IsEmpty(SheetName) Then SheetName = &quot;&quot;
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , , True) Then GoTo Finally
	End If

Try:
	&apos;	Sheet activation, to do only when meaningful, precedes document activation
	If Len(SheetName) &gt; 0 Then
		With _Component
			Set oSheet = .getSheets.getByName(SheetName)
			Set .CurrentController.ActiveSheet = oSheet
		End With
	End If
	bActive = [_Super].Activate()

Finally:
	Activate = bActive
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.Activate

REM -----------------------------------------------------------------------------
Public Sub ClearAll(Optional ByVal Range As Variant) As String
&apos;&apos;&apos;	Clear entirely the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the cell or the range as a string that should be cleared
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.ClearAll(&quot;SheetX&quot;)		&apos; Clears the used area of the sheet

Dim lClear As Long				&apos;	The elements to clear
Dim oRange As Object			&apos;	Alias of Range
Const cstThisSub = &quot;SFDocuments.Calc.ClearAll&quot;
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	With com.sun.star.sheet.CellFlags
		lClear = 0 _
					+ .VALUE _
					+ .DATETIME _
					+ .STRING _
					+ .ANNOTATION _
					+ .FORMULA _
					+ .HARDATTR _
					+ .STYLES _
					+ .OBJECTS _
					+ .EDITATTR _
					+ .FORMATTED
		Set oRange = _ParseAddress(Range)
		oRange.XCellRange.clearContents(lClear)
	End With

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Sub
Catch:
	GoTo Finally
End Sub	&apos;	SF_Documents.SF_Calc.ClearAll

REM -----------------------------------------------------------------------------
Public Sub ClearFormats(Optional ByVal Range As Variant) As String
&apos;&apos;&apos;	Clear all the formatting elements of the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the cell or the range as a string that should be cleared
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.ClearFormats(&quot;SheetX:A1:E100&quot;)		&apos; Clear the formats of the given range

Dim lClear As Long				&apos;	The elements to clear
Dim oRange As Object			&apos;	Alias of Range
Const cstThisSub = &quot;SFDocuments.Calc.ClearFormats&quot;
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	With com.sun.star.sheet.CellFlags
		lClear = 0 _
					+ .HARDATTR _
					+ .STYLES _
					+ .EDITATTR _
					+ .FORMATTED
		Set oRange = _ParseAddress(Range)
		oRange.XCellRange.clearContents(lClear)
	End With

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Sub
Catch:
	GoTo Finally
End Sub	&apos;	SF_Documents.SF_Calc.ClearFormats

REM -----------------------------------------------------------------------------
Public Sub ClearValues(Optional ByVal Range As Variant) As String
&apos;&apos;&apos;	Clear values and formulas in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the cell or the range as a string that should be cleared
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.ClearValues(&quot;SheetX:*&quot;)		&apos; Clears the used area of the sheet

Dim lClear As Long				&apos;	The elements to clear
Dim oRange As Object			&apos;	Alias of Range
Const cstThisSub = &quot;SFDocuments.Calc.ClearValues&quot;
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	With com.sun.star.sheet.CellFlags
		lClear = 0 _
					+ .VALUE _
					+ .DATETIME _
					+ .STRING _
					+ .FORMULA
		Set oRange = _ParseAddress(Range)
		oRange.XCellRange.clearContents(lClear)
	End With

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Sub
Catch:
	GoTo Finally
End Sub	&apos;	SF_Documents.SF_Calc.ClearValues

REM -----------------------------------------------------------------------------
Public Function CopySheet(Optional ByVal SheetName As Variant _
								, Optional ByVal NewName As Variant _
								, Optional ByVal BeforeSheet As Variant _
								) As Boolean
&apos;&apos;&apos; Copy a specified sheet before an existing sheet or at the end of the list of sheets
&apos;&apos;&apos;	The sheet to copy may be inside any open Calc document
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: The name of the sheet to copy or its reference
&apos;&apos;&apos;		NewName: Must not exist
&apos;&apos;&apos;		BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be copied successfully
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		DUPLICATESHEETERROR		A sheet with the given name exists already
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.CopySheet(&quot;SheetX&quot;, &quot;SheetY&quot;)
&apos;&apos;&apos;			&apos;	Copy within the same document
&apos;&apos;&apos;		Dim oDocA As Object		:	Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
&apos;&apos;&apos;		Dim oDocB As Object		:	Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
&apos;&apos;&apos;		oDocB.CopySheet(oDocA.Sheet(&quot;SheetX&quot;), &quot;SheetY&quot;)
&apos;&apos;&apos;			&apos;	Copy from 1 file to another and put the new sheet at the end

Dim bCopy As Boolean				&apos;	Return value
Dim oSheets As Object				&apos;	com.sun.star.sheet.XSpreadsheets
Dim vSheets As Variant				&apos;	List of existing sheets
Dim lSheetIndex As Long				&apos;	Index of a sheet
Dim oSheet As Object				&apos;	Alias of SheetName as reference
Dim lRandom As Long					&apos;	Output of random number generator
Dim sRandom							&apos;	Random sheet name
Const cstThisSub = &quot;SFDocuments.Calc.CopySheet&quot;
Const cstSubArgs = &quot;SheetName, NewName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCopy = False

Check:
	If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True, , , True) Then GoTo Finally
		If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
		If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
	End If

Try:
	&apos;	Determine the index of the sheet before which to insert the copy
	Set oSheets = _Component.getSheets
	vSheets = oSheets.getElementNames()
	If VarType(BeforeSheet) = V_STRING Then
		lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
	Else
		lSheetIndex = BeforeSheet - 1
		If lSheetIndex &lt; 0 Then lSheetIndex = 0
		If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
	End If

	&apos;	Copy sheet inside the same document OR import from another document
	If VarType(SheetName) = V_STRING Then
		_Component.getSheets.copyByName(SheetName, NewName, lSheetIndex)
	Else
		Set oSheet = SheetName
		With oSheet
			&apos;	If a sheet with same name as input exists in the target sheet, rename it first with a random name
			sRandom = &quot;&quot;
			If ScriptForge.SF_Array.Contains(vSheets, .SheetName) Then
				lRandom = ScriptForge.SF_Session.ExecuteCalcFunction(&quot;RANDBETWEEN&quot;, 1, 9999999)
				sRandom = &quot;SF_&quot; &amp; Right(&quot;0000000&quot; &amp; lRandom, 7)
				oSheets.getByName(.SheetName).setName(sRandom)
			End If
			&apos;	Import i.o. Copy
			oSheets.importSheet(oSheet.Component, .SheetName, lSheetIndex)
			&apos;	Rename to new sheet name
			oSheets.getByName(.SheetName).setName(NewName)
			&apos;	Reset random name
			If Len(sRandom) &gt; 0 Then oSheets.getByName(srandom).setName(.SheetName)
		End With
	End If
	bCopy = True

Finally:
	CopySheet = bCopy
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchDuplicate:
	ScriptForge.SF_Exception.RaiseFatal(DUPLICATESHEETERROR, &quot;NewName&quot;, NewName, &quot;Document&quot;, [_Super]._FileIdent())
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.CopySheet

REM -----------------------------------------------------------------------------
Public Function CopySheetFromFile(Optional ByVal FileName As Variant _
								, Optional ByVal SheetName As Variant _
								, Optional ByVal NewName As Variant _
								, Optional ByVal BeforeSheet As Variant _
								) As Boolean
&apos;&apos;&apos; Copy a specified sheet before an existing sheet or at the end of the list of sheets
&apos;&apos;&apos;	The sheet to copy is located inside any closed Calc document
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
&apos;&apos;&apos;			The file must not be protected with a password
&apos;&apos;&apos;		SheetName: The name of the sheet to copy
&apos;&apos;&apos;		NewName: Must not exist
&apos;&apos;&apos;		BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be created
&apos;&apos;&apos;		The created sheet is blank when the input file is not a Calc file
&apos;&apos;&apos;		The created sheet contains an error message when the input sheet was not found
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		DUPLICATESHEETERROR		A sheet with the given name exists already
&apos;&apos;&apos;		UNKNOWNFILEERROR		The input file is unknown
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.CopySheetFromFile(&quot;C:\MyFile.ods&quot;, &quot;SheetX&quot;, &quot;SheetY&quot;, 3)

Dim bCopy As Boolean				&apos;	Return value
Dim oSheet As Object				&apos;	com.sun.star.sheet.XSpreadsheet
Dim sFileName As String				&apos;	URL alias of FileName
Dim FSO As Object					&apos;	SF_FileSystem
Const cstThisSub = &quot;SFDocuments.Calc.CopySheetFromFile&quot;
Const cstSubArgs = &quot;FileName, SheetName, NewName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bCopy = False

Check:
	If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(SheetName, &quot;SheetName&quot;, V_STRING) Then GoTo Finally
		If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
		If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
	End If

Try:
	Set FSO = ScriptForge.SF_FileSystem
	&apos;	Does the input file exist ?
	If Not FSO.FileExists(FileName) Then GoTo CatchNotExists
	sFileName = FSO._ConvertToUrl(FileName)

	&apos;	Insert a blank new sheet and import sheet from file va link setting and deletion
	If Not InsertSheet(Newname, BeforeSheet) Then GoTo Finally
	Set oSheet = _Component.getSheets.getByName(NewName)
	With oSheet
		.link(sFileName,SheetName, &quot;&quot;, &quot;&quot;, com.sun.star.sheet.SheetLinkMode.NORMAL)
		.LinkMode = com.sun.star.sheet.SheetLinkMode.NONE
    	.LinkURL = &quot;&quot;
	End With
	bCopy = True

Finally:
	CopySheetFromFile = bCopy
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotExists:
	ScriptForge.SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.CopySheetFromFile

REM -----------------------------------------------------------------------------
Public Function CopyToCell(Optional ByVal SourceRange As Variant _
								, Optional ByVal DestinationCell As Variant _
								) As String
&apos;&apos;&apos; Copy a specified source range to a destination range or cell
&apos;&apos;&apos;	The source range may belong to another open document
&apos;&apos;&apos;	The method imitates the behaviour of a Copy/Paste from a range to a single cell
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SourceRange: the source range as a string if it belongs to the same document
&apos;&apos;&apos;			or as a reference if it belongs to another open Calc document
&apos;&apos;&apos;		DestinationCell: the destination of the copied range of cells, as a string
&apos;&apos;&apos;			If given as a range of cells, the destination will be reduced to its top-left cell
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the modified range of cells
&apos;&apos;&apos;		The modified area depends only on the size of the source area
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.CopyToCell(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5&quot;)
&apos;&apos;&apos;			&apos;	Copy within the same document
&apos;&apos;&apos;		Dim oDocA As Object		:	Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
&apos;&apos;&apos;		Dim oDocB As Object		:	Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
&apos;&apos;&apos;		oDocB.CopyToCell(oDocA.Range(&quot;SheetX.A1:F10&quot;), &quot;SheetY.C5&quot;)
&apos;&apos;&apos;			&apos;	Copy from 1 file to another

Dim sCopy As String				&apos;	Return value
Dim oSource As Object			&apos;	Alias of SourceRange to avoid &quot;Object variable not set&quot; run-time error
Dim oSourceAddress As Object	&apos;	com.sun.star.table.CellRangeAddress
Dim oDestRange As Object		&apos;	Destination as a range
Dim oDestAddress As Object		&apos;	com.sun.star.table.CellRangeAddress
Dim oDestCell As Object			&apos;	com.sun.star.table.CellAddress
Dim oSelect As Object			&apos;	Current selection in source
Dim oClipboard As Object		&apos;	com.sun.star.datatransfer.XTransferable

Const cstThisSub = &quot;SFDocuments.Calc.CopyToCell&quot;
Const cstSubArgs = &quot;SourceRange, DestinationCell&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sCopy = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(SourceRange, &quot;SourceRange&quot;, Array(V_STRING, ScriptForge.V_OBJECT), , , CALCREFERENCE) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	If VarType(SourceRange) = V_STRING Then		&apos;	Same document - Use UNO copyRange method
		Set oSourceAddress = _ParseAddress(SourceRange).XCellRange.RangeAddress
		Set oDestRange = _ParseAddress(DestinationCell)
		Set oDestAddress = oDestRange.XCellRange.RangeAddress
		Set oDestCell = New com.sun.star.table.CellAddress
		With oDestAddress
			oDestCell.Sheet = .Sheet
			oDestCell.Column = .StartColumn
			oDestCell.Row = .StartRow
		End With
		oDestRange.XSpreadsheet.copyRange(oDestCell, oSourceAddress)
	Else			&apos;	Use clipboard to copy - current selection in Source should be preserved
		Set oSource = SourceRange
		With oSource
			&apos;	Keep current selection in source document
			Set oSelect = .Component.CurrentController.getSelection()
			&apos;	Select, copy the source range and paste in the top-left cell of the destination
			.Component.CurrentController.select(.XCellRange)
			Set oClipboard = .Component.CurrentController.getTransferable()
			_Component.CurrentController.select(_Offset(DestinationCell, 0, 0, 1, 1).XCellRange)
			_Component.CurrentController.insertTransferable(oClipBoard)
			&apos;	Restore previous selection in Source
			_RestoreSelections(.Component, oSelect)
			Set oSourceAddress = .XCellRange.RangeAddress
		End With
	End If

	With oSourceAddress
		sCopy = _Offset(DestinationCell, 0, 0, .EndRow - .StartRow + 1, .EndColumn - .StartColumn + 1).RangeName
	End With

Finally:
	CopyToCell = sCopy
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.CopyToCell

REM -----------------------------------------------------------------------------
Public Function CopyToRange(Optional ByVal SourceRange As Variant _
								, Optional ByVal DestinationRange As Variant _
								) As String
&apos;&apos;&apos; Copy downwards and/or rightwards a specified source range to a destination range
&apos;&apos;&apos;	The source range may belong to another open document
&apos;&apos;&apos;	The method imitates the behaviour of a Copy/Paste from a range to a larger range
&apos;&apos;&apos;		If the height (resp. width) of the destination area is &gt; 1 row (resp. column)
&apos;&apos;&apos;		then the height (resp. width) of the source must be &lt;= the height (resp. width)
&apos;&apos;&apos;		of the destination. Otherwise nothing happens
&apos;&apos;&apos;		If the height (resp.width) of the destination is = 1 then the destination
&apos;&apos;&apos;		is expanded downwards (resp. rightwards) up to the height (resp. width)
&apos;&apos;&apos;		of the source range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SourceRange: the source range as a string if it belongs to the same document
&apos;&apos;&apos;			or as a reference if it belongs to another open Calc document
&apos;&apos;&apos;		DestinationRange: the destination of the copied range of cells, as a string
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the modified range of cells
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.CopyToRange(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5:J5&quot;)
&apos;&apos;&apos;			&apos;	Copy within the same document
&apos;&apos;&apos;			&apos;	Returned range: $SheetY.$C$5:$J$14
&apos;&apos;&apos;		Dim oDocA As Object		:	Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
&apos;&apos;&apos;		Dim oDocB As Object		:	Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
&apos;&apos;&apos;		oDocB.CopyToRange(oDocA.Range(&quot;SheetX.A1:F10&quot;), &quot;SheetY.C5:J5&quot;)
&apos;&apos;&apos;			&apos;	Copy from 1 file to another

Dim sCopy As String				&apos;	Return value
Dim oSource As Object			&apos;	Alias of SourceRange to avoid &quot;Object variable not set&quot; run-time error
Dim oDestRange As Object		&apos;	Destination as a range
Dim oDestCell As Object			&apos;	com.sun.star.table.CellAddress
Dim oSelect As Object			&apos;	Current selection in source
Dim oClipboard As Object		&apos;	com.sun.star.datatransfer.XTransferable
Dim bSameDocument As Boolean	&apos;	True when source in same document as destination
Dim lHeight As Long				&apos;	Height of destination
Dim lWidth As Long				&apos;	Width of destination

Const cstThisSub = &quot;SFDocuments.Calc.CopyToRange&quot;
Const cstSubArgs = &quot;SourceRange, DestinationRange&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sCopy = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(SourceRange, &quot;SourceRange&quot;, Array(V_STRING, ScriptForge.V_OBJECT), , , CALCREFERENCE) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DestinationRange, &quot;DestinationRange&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	&apos;	Copy done via clipboard

	&apos;	Check Height/Width destination = 1 or &gt; Height/Width of source
	bSameDocument = ( VarType(SourceRange) = V_STRING )
	If bSameDocument Then Set oSource = _ParseAddress(SourceRange) Else Set oSource = SourceRange
	Set oDestRange = _ParseAddress(DestinationRange)
	With oDestRange
		lHeight = .Height
		lWidth = .Width
		If lHeight = 1 Then
			lHeight = oSource.Height		&apos;	Future height
		ElseIf lHeight &lt; oSource.Height Then
			GoTo Finally
		End If
		If lWidth = 1 Then
			lWidth = oSource.Width	&apos;	Future width
		ElseIf lWidth &lt; oSource.Width Then
			GoTo Finally
		End If
	End With

	With oSource
		&apos;	Store actual selection in source
		Set oSelect = .Component.CurrentController.getSelection()
		&apos;	Select, copy the source range and paste in the destination
		.Component.CurrentController.select(.XCellRange)
		Set oClipboard = .Component.CurrentController.getTransferable()
		_Component.CurrentController.select(oDestRange.XCellRange)
		_Component.CurrentController.insertTransferable(oClipBoard)
		&apos;	Restore selection in source
		_RestoreSelections(.Component, oSelect)
	End With
	
	sCopy = _Offset(oDestRange, 0, 0, lHeight, lWidth).RangeName

Finally:
	CopyToRange = sCopy
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.CopyToRange

REM -----------------------------------------------------------------------------
Public Function DAvg(Optional ByVal Range As Variant) As Double
&apos;&apos;&apos;	Get the average of the numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the values from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The average of the numeric values as a double
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.DAvg(&quot;~.A1:A1000&quot;)

Try:
	DAvg = _DFunction(&quot;DAvg&quot;, Range)

Finally:
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc.DAvg

REM -----------------------------------------------------------------------------
Public Function DCount(Optional ByVal Range As Variant) As Long
&apos;&apos;&apos;	Get the number of numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the values from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The number of numeric values as a Long
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.DCount(&quot;~.A1:A1000&quot;)

Try:
	DCount = _DFunction(&quot;DCount&quot;, Range)

Finally:
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc.DCount

REM -----------------------------------------------------------------------------
Public Function DMax(Optional ByVal Range As Variant) As Double
&apos;&apos;&apos;	Get the greatest of the numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the values from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The greatest of the numeric values as a double
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.DMax(&quot;~.A1:A1000&quot;)

Try:
	DMax = _DFunction(&quot;DMax&quot;, Range)

Finally:
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc.DMax

REM -----------------------------------------------------------------------------
Public Function DMin(Optional ByVal Range As Variant) As Double
&apos;&apos;&apos;	Get the smallest of the numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the values from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The smallest of the numeric values as a double
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.DMin(&quot;~.A1:A1000&quot;)

Try:
	DMin = _DFunction(&quot;DMin&quot;, Range)

Finally:
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc.DMin

REM -----------------------------------------------------------------------------
Public Function DSum(Optional ByVal Range As Variant) As Double
&apos;&apos;&apos;	Get sum of the numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the values from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The sum of the numeric values as a double
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.DSum(&quot;~.A1:A1000&quot;)

Try:
	DSum = _DFunction(&quot;DSum&quot;, Range)

Finally:
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc.DSum

REM -----------------------------------------------------------------------------
Public Function Forms(Optional ByVal SheetName As Variant _
							, Optional ByVal Form As Variant _
							) As Variant
&apos;&apos;&apos;	Return either
&apos;&apos;&apos;		- the list of the Forms contained in the given sheet
&apos;&apos;&apos;		- a SFDocuments.Form object based on its name or its index
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: the name of the sheet containing the requested form or forms
&apos;&apos;&apos;		Form: a form stored in the document given by its name or its index
&apos;&apos;&apos;			When absent, the list of available forms is returned
&apos;&apos;&apos;			To get the first (unique ?) form stored in the form document, set Form = 0
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		CALCFORMNOTFOUNDERROR		Form not found
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A zero-based array of strings if Form is absent
&apos;&apos;&apos;		An instance of the SF_Form class if Form exists
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;			Dim myForm As Object, myList As Variant
&apos;&apos;&apos;				myList = oDoc.Forms(&quot;ThisSheet&quot;)
&apos;&apos;&apos;				Set myForm = oDoc.Forms(&quot;ThisSheet&quot;, 0)

Dim oForm As Object					&apos;	The new Form class instance
Dim oMainForm As Object				&apos;	com.sun.star.comp.sdb.Content
Dim oXForm As Object				&apos;	com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
Dim vFormNames As Variant			&apos;	Array of form names
Dim oForms As Object				&apos;	Forms collection
Const cstDrawPage = -1				&apos;	There is no DrawPages collection in Calc sheets

Const cstThisSub = &quot;SFDocuments.Calc.Forms&quot;
Const cstSubArgs = &quot;SheetName, [Form=&quot;&quot;&quot;&quot;]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	If IsMissing(Form) Or IsEmpty(Form) Then Form = &quot;&quot;
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
	End If

Try:
	&apos;	Start from the Calc sheet and go down to forms
	Set oForms = _Component.getSheets.getByName(SheetName).DrawPage.Forms
	vFormNames = oForms.getElementNames()

	If Len(Form) = 0 Then	&apos;	Return the list of valid form names
		Forms = vFormNames
	Else
		If VarType(Form) = V_STRING Then	&apos;	Find the form by name
		If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, V_STRING, vFormNames) Then GoTo Finally
			Set oXForm = oForms.getByName(Form)
		Else								&apos;	Find the form by index
			If Form &lt; 0 Or Form &gt;= oForms.Count Then GoTo CatchNotFound
			Set oXForm = oForms.getByIndex(Form)
		End If
		&apos;	Create the new Form class instance
		Set oForm = SF_Register._NewForm(oXForm)
		With oForm
			Set .[_Parent] = [Me]
			._SheetName = SheetName
			._FormType = ISCALCFORM
			Set ._Component = _Component
			._Initialize()
		End With
		Set Forms = oForm
	End If

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
CatchNotFound:
	ScriptForge.SF_Exception.RaiseFatal(CALCFORMNOTFOUNDERROR, Form, _FileIdent())
End Function	&apos;	SFDocuments.SF_Calc.Forms

REM -----------------------------------------------------------------------------
Function GetColumnName(Optional ByVal ColumnNumber As Variant) As String
&apos;&apos;&apos;	Convert a column number (range 1, 2,..1024) into its letter counterpart (range &apos;A&apos;, &apos;B&apos;,..&apos;AMJ&apos;).
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		ColumnNumber: the column number, must be in the interval 1 ... 1024
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		a string representation of the column name, in range &apos;A&apos;..&apos;AMJ&apos;
&apos;&apos;&apos;		If ColumnNumber is not in the allowed range, returns a zero-length string
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		MsgBox oDoc.GetColumnName(1022)	&apos;	&quot;AMH&quot;
&apos;&apos;&apos;	Adapted from a Python function by sundar nataraj 
&apos;&apos;&apos;	http://stackoverflow.com/questions/23861680/convert-spreadsheet-number-to-column-letter

Dim sCol As String			&apos;	Return value
Const cstThisSub = &quot;SFDocuments.Calc.GetColumnName&quot;
Const cstSubArgs = &quot;ColumnNumber&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sCol = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not SF_Utils._Validate(ColumnNumber, &quot;ColumnNumber&quot;, V_NUMERIC) Then GoTo Finally
	End If

Try:
	If (ColumnNumber &gt; 0) And (ColumnNumber &lt;= MAXCOLS) Then sCol = _GetColumnName(ColumnNumber)

Finally:
	GetColumnName = sCol
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Calc.GetColumnName

REM -----------------------------------------------------------------------------
Public Function GetFormula(Optional ByVal Range As Variant) As Variant
&apos;&apos;&apos;	Get the formula(e) stored in the given range of cells
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the formula from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A scalar, a zero-based 1D array or a zero-based 2D array of strings
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.GetFormula(&quot;~.A1:A1000&quot;)

Dim vGet As Variant					&apos;	Return value
Dim oAddress As Object				&apos;	Alias of Range
Dim vDataArray As Variant			&apos;	DataArray compatible with .DataArray UNO property
Const cstThisSub = &quot;SFDocuments.Calc.GetFormula&quot;
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	vGet = Empty

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	&apos;	Get the data
	Set oAddress = _ParseAddress(Range)
	vDataArray = oAddress.XCellRange.getFormulaArray()

	&apos;	Convert the data array to scalar, vector or array
	vGet = _ConvertFromDataArray(vDataArray)

Finally:
	GetFormula = vGet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.GetFormula

REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant _
								, Optional ObjectName As Variant _
								) As Variant
&apos;&apos;&apos;	Return the actual value of the given property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		PropertyName: the name of the property as a string
&apos;&apos;&apos;		ObjectName: a sheet or range name
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The actual value of the property
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		ARGUMENTERROR		The property does not exist

Const cstThisSub = &quot;SFDocuments.Calc.GetProperty&quot;
Const cstSubArgs = &quot;&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	GetProperty = Null

Check:
	If IsMissing(ObjectName) Or IsEMpty(ObjectName) Then ObjectName = &quot;&quot;
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
		If Not ScriptForge.SF_Utils._Validate(ObjectName, &quot;ObjectName&quot;, V_STRING) Then GoTo Catch
	End If

Try:
	&apos;	Superclass or subclass property ?
	If ScriptForge.SF_Array.Contains([_Super].Properties(), PropertyName) Then
		GetProperty = [_Super].GetProperty(PropertyName)
	ElseIf Len(ObjectName) = 0 Then
		GetProperty = _PropertyGet(PropertyName)
	Else
		GetProperty = _PropertyGet(PropertyName, ObjectName)
	End If

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Calc.GetProperty

REM -----------------------------------------------------------------------------
Public Function GetValue(Optional ByVal Range As Variant) As Variant
&apos;&apos;&apos;	Get the value(s) stored in the given range of cells
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to get the value from
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A scalar, a zero-based 1D array or a zero-based 2D array of strings and doubles
&apos;&apos;&apos;		To convert doubles to dates, use the CDate builtin function
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		Val = oDoc.GetValue(&quot;~.A1:A1000&quot;)

Dim vGet As Variant					&apos;	Return value
Dim oAddress As Object				&apos;	Alias of Range
Dim vDataArray As Variant			&apos;	DataArray compatible with .DataArray UNO property
Const cstThisSub = &quot;SFDocuments.Calc.GetValue&quot;
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	vGet = Empty

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	&apos;	Get the data
	Set oAddress = _ParseAddress(Range)
	vDataArray = oAddress.XCellRange.getDataArray()

	&apos;	Convert the data array to scalar, vector or array
	vGet = _ConvertFromDataArray(vDataArray)

Finally:
	GetValue = vGet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.GetValue

REM -----------------------------------------------------------------------------
Public Function ImportFromCSVFile(Optional ByVal FileName As Variant _
								, Optional ByVal DestinationCell As Variant _
								, Optional ByVal FilterOptions As Variant _
								) As String
&apos;&apos;&apos; Import the content of a CSV-formatted text file starting from a given cell
&apos;&apos;&apos;	Beforehand the destination area will be cleared from any content and format
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
&apos;&apos;&apos;		DestinationCell: the destination of the copied range of cells, as a string
&apos;&apos;&apos;			If given as range, the destination will be reduced to its top-left cell
&apos;&apos;&apos;		FilterOptions: The arguments of the CSV input filter.
&apos;&apos;&apos;			Read https://wiki.openoffice.org/wiki/Documentation/DevGuide/Spreadsheets/Filter_Options#Filter_Options_for_the_CSV_Filter
&apos;&apos;&apos;			Default:	input file encoding is UTF8
&apos;&apos;&apos;						separator = comma, semi-colon or tabulation
&apos;&apos;&apos;						string delimiter = double quote
&apos;&apos;&apos;						all lines are included
&apos;&apos;&apos;						quoted strings are formatted as texts
&apos;&apos;&apos;						special numbers are detected
&apos;&apos;&apos;						all columns are presumed texts
&apos;&apos;&apos;						language = english/US =&gt; decimal separator is &quot;.&quot;, thousands separator = &quot;,&quot;
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the modified range of cells
&apos;&apos;&apos;		The modified area depends only on the content of the source file
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		DOCUMENTOPENERROR			The csv file could not be opened
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.ImportFromCSVFile(&quot;C:\Temp\myCsvFile.csv&quot;, &quot;SheetY.C5&quot;)

Dim sImport As String			&apos;	Return value
Dim oUI As Object				&apos;	UI service
Dim oSource As Object			&apos;	New Calc document with csv loaded
Dim oSelect As Object			&apos;	Current selection in destination

Const cstFilter = &quot;Text - txt - csv (StarCalc)&quot;
Const cstFilterOptions = &quot;9/44/59/MRG,34,76,1,,1033,true,true&quot;
Const cstThisSub = &quot;SFDocuments.Calc.ImportFromCSVFile&quot;
Const cstSubArgs = &quot;FileName, DestinationCell, [FilterOptions]=&quot;&quot;9/44/59/MRG,34,76,1,,1033,true,true&quot;&quot;&quot;

&apos;	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sImport = &quot;&quot;

Check:
	If IsMissing(FilterOptions) Or IsEmpty(FilterOptions) Then FilterOptions = cstFilterOptions
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	&apos;	Input file is loaded in an empty worksheet. Data are copied to destination cell
	Set oUI = CreateScriptService(&quot;UI&quot;)
	Set oSource = oUI.OpenDocument(FileName _
						, ReadOnly := True _
						, Hidden := True _
						, FilterName := cstFilter _
						, FilterOptions := FilterOptions _
						)
	&apos;	Remember current selection and restore it after copy
	Set oSelect = _Component.CurrentController.getSelection()
	sImport = CopyToCell(oSource.Range(&quot;*&quot;), DestinationCell)
	_RestoreSelections(_Component, oSelect)

Finally:
	If Not IsNull(oSource) Then oSource.CloseDocument(False)
	ImportFromCSVFile = sImport
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.ImportFromCSVFile

REM -----------------------------------------------------------------------------
Public Sub ImportFromDatabase(Optional ByVal FileName As Variant _
								, Optional ByVal RegistrationName As Variant _
								, Optional ByVal DestinationCell As Variant _
								, Optional ByVal SQLCommand As Variant _
								, Optional ByVal DirectSQL As Variant _
								)
&apos;&apos;&apos; Import the content of a database table, query or resultset, i.e. the result of a SELECT SQL command,
&apos;&apos;&apos;	starting from a given cell
&apos;&apos;&apos;	Beforehand the destination area will be cleared from any content and format
&apos;&apos;&apos;	The modified area depends only on the content of the source data
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
&apos;&apos;&apos;		RegistrationName: the name of a registered database
&apos;&apos;&apos;			It is ignored if FileName &lt;&gt; &quot;&quot;
&apos;&apos;&apos;		DestinationCell: the destination of the copied range of cells, as a string
&apos;&apos;&apos;			If given as a range of cells, the destination will be reduced to its top-left cell
&apos;&apos;&apos;		SQLCommand: either a table or query name (without square brackets)
&apos;&apos;&apos;			or a full SQL commands where table and fieldnames are preferably surrounded with square brackets
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		Implemented as a Sub because the doImport UNO method does not return any error
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		BASEDOCUMENTOPENERROR			The database file could not be opened
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.ImportFromDatabase(&quot;C:\Temp\myDbFile.odb&quot;, , &quot;SheetY.C5&quot;, &quot;SELECT * FROM [Employees] ORDER BY [LastName]&quot;)

Dim oDBContext As Object		&apos;	com.sun.star.sdb.DatabaseContext
Dim oDatabase As Object			&apos;	SFDatabases.Database service
Dim lCommandType As Long		&apos;	A com.sun.star.sheet.DataImportMode.xxx constant
Dim oQuery As Object			&apos;	com.sun.star.ucb.XContent
Dim bDirect As Boolean			&apos;	Alias of DirectSQL
Dim oDestRange As Object		&apos;	Destination as a range
Dim oDestAddress As Object		&apos;	com.sun.star.table.CellRangeAddress
Dim oDestCell As Object			&apos;	com.sun.star.table.XCell
Dim oSelect As Object			&apos;	Current selection in destination
Dim vImportOptions As Variant	&apos;	Array of PropertyValues

Const cstThisSub = &quot;SFDocuments.Calc.ImportFromDatabase&quot;
Const cstSubArgs = &quot;[FileName=&quot;&quot;&quot;&quot;], [RegistrationName=&quot;&quot;&quot;&quot;], DestinationCell, SQLCommand, [DirectSQL=False]&quot;

&apos;	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	
	If IsMissing(FileName) Or IsEmpty(FileName) Then FileName = &quot;&quot;
	If IsMissing(RegistrationName) Or IsEmpty(RegistrationName) Then RegistrationName = &quot;&quot;
	If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;, , True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
	End If

	&apos;	Check the existence of FileName
	If Len(FileName) = 0 Then	&apos;	FileName has precedence over RegistrationName
		If Len(RegistrationName) = 0 Then GoTo CatchError
		Set oDBContext = ScriptForge.SF_Utils._GetUNOService(&quot;DatabaseContext&quot;)
		If Not oDBContext.hasRegisteredDatabase(RegistrationName) Then GoTo CatchError
		FileName = ScriptForge.SF_FileSystem._ConvertFromUrl(oDBContext.getDatabaseLocation(RegistrationName))
	End If	
	If Not ScriptForge.SF_FileSystem.FileExists(FileName) Then GoTo CatchError
	
Try:
	&apos;	Check command type
	Set oDatabase = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , True)	&apos;	Read-only
	If IsNull(oDatabase) Then GoTo CatchError
	With oDatabase
		If ScriptForge.SF_Array.Contains(.Tables, SQLCommand) Then
			bDirect = True
			lCommandType = com.sun.star.sheet.DataImportMode.TABLE
		ElseIf ScriptForge.SF_Array.Contains(.Queries, SQLCommand) Then
			Set oQuery = .XConnection.Queries.getByName(SQLCommand)
			bDirect = Not oQuery.EscapeProcessing
			lCommandType = com.sun.star.sheet.DataImportMode.QUERY
		Else
			bDirect = DirectSQL
			lCommandType = com.sun.star.sheet.DataImportMode.SQL
			SQLCommand = ._ReplaceSquareBrackets(SQLCommand)
		End If
		.CloseDatabase()
		Set oDatabase = oDatabase.Dispose()
	End With

	&apos;	Determine the destination cell as the top-left coordinates of the given range
	Set oDestRange = _ParseAddress(DestinationCell)
	Set oDestAddress = oDestRange.XCellRange.RangeAddress
	Set oDestCell = oDestRange.XSpreadsheet.getCellByPosition(oDestAddress.StartColumn, oDestAddress.StartRow)

	&apos;	Remember current selection
	Set oSelect = _Component.CurrentController.getSelection()
	&apos;	Import arguments
	vImportOptions = Array(_
			ScriptForge.SF_Utils._MakePropertyValue(&quot;DatabaseName&quot;, ScriptForge.SF_FileSystem._ConvertToUrl(FileName)) _
			, ScriptForge.SF_Utils._MakePropertyValue(&quot;SourceObject&quot;, SQLCommand) _
			, ScriptForge.SF_Utils._MakePropertyValue(&quot;SourceType&quot;, lCommandType) _
			, ScriptForge.SF_Utils._MakePropertyValue(&quot;IsNative&quot;, bDirect) _
			)
	oDestCell.doImport(vImportOptions)
	&apos;	Restore selection after import_
	_RestoreSelections(_Component, oSelect)

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Sub
Catch:
	GoTo Finally
CatchError:
	SF_Exception.RaiseFatal(BASEDOCUMENTOPENERROR, &quot;FileName&quot;, FileName, &quot;RegistrationName&quot;, RegistrationName)
	GoTo Finally
End Sub		    &apos;   SFDocuments.SF_Calc.ImportFromDatabase

REM -----------------------------------------------------------------------------
Public Function InsertSheet(Optional ByVal SheetName As Variant _
								, Optional ByVal BeforeSheet As Variant _
								) As Boolean
&apos;&apos;&apos; Insert a new empty sheet before an existing sheet or at the end of the list of sheets
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: The name of the new sheet
&apos;&apos;&apos;		BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be inserted successfully
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.InsertSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)

Dim bInsert As Boolean				&apos;	Return value
Dim vSheets As Variant				&apos;	List of existing sheets
Dim lSheetIndex As Long				&apos;	Index of a sheet
Const cstThisSub = &quot;SFDocuments.Calc.InsertSheet&quot;
Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bInsert = False

Check:
	If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, True) Then GoTo Finally
		If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
	End If
	vSheets = _Component.getSheets.getElementNames()

Try:
	If VarType(BeforeSheet) = V_STRING Then
		lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
	Else
		lSheetIndex = BeforeSheet - 1
		If lSheetIndex &lt; 0 Then lSheetIndex = 0
		If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
	End If
	_Component.getSheets.insertNewByName(SheetName, lSheetIndex)
	bInsert = True

Finally:
	InsertSheet = binsert
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.InsertSheet

REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos;	Return the list of public methods of the Calc service as an array

	Methods = Array( _
					&quot;Activate&quot; _
					, &quot;ClearAll&quot; _
					, &quot;ClearFormats&quot; _
					, &quot;ClearValues&quot; _
					, &quot;CloseDocument&quot; _
					, &quot;CopySheet&quot; _
					, &quot;CopySheetFromFile&quot; _
					, &quot;CopyToCell&quot; _
					, &quot;CopyToRange&quot; _
					, &quot;DAvg&quot; _
					, &quot;DCount&quot; _
					, &quot;DMax&quot; _
					, &quot;DMin&quot; _
					, &quot;DSum&quot; _
					, &quot;GetColumnName&quot; _
					, &quot;GetFormula&quot; _
					, &quot;GetValue&quot; _
					, &quot;ImportFromCSVFile&quot; _
					, &quot;ImportFromDatabase&quot; _
					, &quot;InsertSheet&quot; _
					, &quot;MoveRange&quot; _
					, &quot;MoveSheet&quot; _
					, &quot;Offset&quot; _
					, &quot;RemoveSheet&quot; _
					, &quot;RenameSheet&quot; _
					, &quot;RunCommand&quot; _
					, &quot;Save&quot; _
					, &quot;SaveAs&quot; _
					, &quot;SaveCopyAs&quot; _
					, &quot;SetArray&quot; _
					, &quot;SetCellStyle&quot; _
					, &quot;SetFormula&quot; _
					, &quot;SetValue&quot; _
					, &quot;SortRange&quot; _
					)

End Function	&apos;	SFDocuments.SF_Calc.Methods

REM -----------------------------------------------------------------------------
Public Function MoveRange(Optional ByVal Source As Variant _
								, Optional ByVal Destination As Variant _
								) As String
&apos;&apos;&apos; Move a specified source range to a destination range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Source: the source range of cells as a string
&apos;&apos;&apos;		Destination: the destination of the moved range of cells, as a string
&apos;&apos;&apos;			If given as a range of cells, the destination will be reduced to its top-left cell
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the modified range of cells
&apos;&apos;&apos;		The modified area depends only on the size of the source area
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.MoveRange(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5&quot;)

Dim sMove As String				&apos;	Return value
Dim oSource As Object			&apos;	Alias of Source to avoid &quot;Object variable not set&quot; run-time error
Dim oSourceAddress As Object	&apos;	com.sun.star.table.CellRangeAddress
Dim oDestRange As Object		&apos;	Destination as a range
Dim oDestAddress As Object		&apos;	com.sun.star.table.CellRangeAddress
Dim oDestCell As Object			&apos;	com.sun.star.table.CellAddress
Dim oSelect As Object			&apos;	Current selection in source
Dim oClipboard As Object		&apos;	com.sun.star.datatransfer.XTransferable
Dim oCellRanges As Object		&apos;	com.sun.star.sheet.SheetCellRanges
Dim vRangeAddresses As Variant	&apos;	Array of com.sun.star.table.CellRangeAddress
Dim i As Long

Const cstThisSub = &quot;SFDocuments.Calc.MoveRange&quot;
Const cstSubArgs = &quot;Source, Destination&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sMove = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _Validate(Source, &quot;Source&quot;, V_STRING) Then GoTo Finally
		If Not _Validate(Destination, &quot;Destination&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	Set oSourceAddress = _ParseAddress(Source).XCellRange.RangeAddress
	Set oDestRange = _ParseAddress(Destination)
	Set oDestAddress = oDestRange.XCellRange.RangeAddress
	Set oDestCell = New com.sun.star.table.CellAddress
	With oDestAddress
		oDestCell.Sheet = .Sheet
		oDestCell.Column = .StartColumn
		oDestCell.Row = .StartRow
	End With
	oDestRange.XSpreadsheet.moveRange(oDestCell, oSourceAddress)

	With oSourceAddress
		sMove = _Offset(Destination, 0, 0, .EndRow - .StartRow + 1, .EndColumn - .StartColumn + 1).RangeName
	End With

Finally:
	MoveRange = sMove
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.MoveRange

REM -----------------------------------------------------------------------------
Public Function MoveSheet(Optional ByVal SheetName As Variant _
								, Optional ByVal BeforeSheet As Variant _
								) As Boolean
&apos;&apos;&apos; Move a sheet before an existing sheet or at the end of the list of sheets
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: The name of the sheet to move
&apos;&apos;&apos;		BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to move the sheet
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be moved successfully
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.MoveSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)

Dim bMove As Boolean				&apos;	Return value
Dim vSheets As Variant				&apos;	List of existing sheets
Dim lSheetIndex As Long				&apos;	Index of a sheet
Const cstThisSub = &quot;SFDocuments.Calc.MoveSheet&quot;
Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bMove = False

Check:
	If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
		If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
	End If
	vSheets = _Component.getSheets.getElementNames()

Try:
	If VarType(BeforeSheet) = V_STRING Then
		lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
	Else
		lSheetIndex = BeforeSheet - 1
		If lSheetIndex &lt; 0 Then lSheetIndex = 0
		If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
	End If
	_Component.getSheets.MoveByName(SheetName, lSheetIndex)
	bMove = True

Finally:
	MoveSheet = bMove
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.MoveSheet

REM -----------------------------------------------------------------------------
Public Function Offset(Optional ByRef Range As Variant _
								, Optional ByVal Rows As Variant _
								, Optional ByVal Columns As Variant _
								, Optional ByVal Height As Variant _
								, Optional ByVal Width As Variant _
								) As String
&apos;&apos;&apos;	Returns a new range offset by a certain number of rows and columns from a given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range, as a string, from which the function searches for the new range
&apos;&apos;&apos;		Rows : the number of rows by which the reference was corrected up (negative value) or down.
&apos;&apos;&apos;				Use 0 (default) to stay in the same row.
&apos;&apos;&apos;		Columns : the number of columns by which the reference was corrected to the left (negative value) or to the right.
&apos;&apos;&apos;				Use 0 (default) to stay in the same column
&apos;&apos;&apos;		Height : the vertical height for an area that starts at the new reference position.
&apos;&apos;&apos;				Default = no vertical resizing
&apos;&apos;&apos;		Width : the horizontal width for an area that starts at the new reference position.
&apos;&apos;&apos;				Default - no horizontal resizing
&apos;&apos;&apos;		Arguments Rows and Columns must not lead to zero or negative start row or column.
&apos;&apos;&apos;		Arguments Height and Width must not lead to zero or negative count of rows or columns.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A new range as a string
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		OFFSETADDRESSERROR		The computed range of cells falls beyond the sheet boundaries
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.Offset(&quot;A1&quot;, 2, 2)	&apos;	&quot;&apos;SheetX&apos;.$C$3&quot; (A1 moved by two rows and two columns down)
&apos;&apos;&apos;		oDoc.Offset(&quot;A1&quot;, 2, 2, 5, 6)	&apos;	&quot;&apos;SheetX&apos;.$C$3:$H$7&quot;

Dim sOffset As String				&apos;	Return value
Dim oAddress As Object				&apos;	Alias of Range
Const cstThisSub = &quot;SFDocuments.Calc.Offset&quot;
Const cstSubArgs = &quot;Range, [Rows=0], [Columns=0], [Height], [Width]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sOffset = &quot;&quot;

Check:
	If IsMissing(Rows) Or IsEmpty(Rows) Then Rows = 0
	If IsMissing(Columns) Or IsEmpty(Columns) Then Columns = 0
	If IsMissing(Height) Or IsEmpty(Height) Then Height = 0
	If IsMissing(Width) Or IsEmpty(Width) Then Width = 0
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Rows, &quot;Rows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Columns, &quot;Columns&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Height, &quot;Height&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Width, &quot;Width&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
	End If

Try:
	&apos;	Define the new range string
	Set oAddress = _Offset(Range, Rows, Columns, Height, Width)
	sOffset = oAddress.RangeName

Finally:
	Offset = sOffset
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.Offset

REM -----------------------------------------------------------------------------
Public Function Properties() As Variant
&apos;&apos;&apos;	Return the list or properties of the Calc class as an array

	Properties = Array( _
					&quot;CurrentSelection&quot; _
					, &quot;CustomProperties&quot; _
					, &quot;Description&quot; _
					, &quot;DocumentProperties&quot; _
					, &quot;DocumentType&quot; _
					, &quot;Height&quot; _
					, &quot;IsBase&quot; _
					, &quot;IsCalc&quot; _
					, &quot;IsDraw&quot; _
					, &quot;IsImpress&quot; _
					, &quot;IsMath&quot; _
					, &quot;IsWriter&quot; _
					, &quot;Keywords&quot; _
					, &quot;LastCell&quot; _
					, &quot;LastColumn&quot; _
					, &quot;LastRow&quot; _
					, &quot;Range&quot; _
					, &quot;Readonly&quot; _
					, &quot;Sheet&quot; _
					, &quot;Sheets&quot; _
					, &quot;Subject&quot; _
					, &quot;Title&quot; _
					, &quot;Width&quot; _
					, &quot;XCellRange&quot; _
					, &quot;XComponent&quot; _
					, &quot;XSpreadsheet&quot; _
					)

End Function	&apos;	SFDocuments.SF_Calc.Properties

REM -----------------------------------------------------------------------------
Public Function RemoveSheet(Optional ByVal SheetName As Variant) As Boolean
&apos;&apos;&apos; Remove an existing sheet from the document
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: The name of the sheet to remove
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be removed successfully
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.RemoveSheet(&quot;SheetX&quot;)

Dim bRemove As Boolean				&apos;	Return value
Const cstThisSub = &quot;SFDocuments.Calc.RemoveSheet&quot;
Const cstSubArgs = &quot;SheetName&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bRemove = False

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
	End If

Try:
	 _Component.getSheets.RemoveByName(SheetName)
	bRemove = True

Finally:
	RemoveSheet = bRemove
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.RemoveSheet

REM -----------------------------------------------------------------------------
Public Function RenameSheet(Optional ByVal SheetName As Variant _
								, Optional ByVal NewName As Variant _
								) As Boolean
&apos;&apos;&apos; Rename a specified sheet
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		SheetName: The name of the sheet to rename
&apos;&apos;&apos;		NewName: Must not exist
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if the sheet could be renamed successfully
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		DUPLICATESHEETERROR		A sheet with the given name exists already
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.RenameSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)

Dim bRename As Boolean				&apos;	Return value
Const cstThisSub = &quot;SFDocuments.Calc.RenameSheet&quot;
Const cstSubArgs = &quot;SheetName, NewName&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bRename = False

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive(True) Then GoTo Finally
		If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
		If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
	End If

Try:
	_Component.getSheets.getByName(SheetName).setName(NewName)
	bRename = True

Finally:
	RenameSheet = bRename
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function    &apos;   SFDocuments.SF_Calc.RenameSheet

REM -----------------------------------------------------------------------------
Public Function SetArray(Optional ByVal TargetCell As Variant _
								, Optional ByRef Value As Variant _
								) As String
&apos;&apos;&apos;	Set the given (array of) values starting from the target cell
&apos;&apos;&apos;	The updated area expands itself from the target cell or from the top-left corner of the given range
&apos;&apos;&apos;	as far as determined by the size of the input Value.
&apos;&apos;&apos;	Vectors are always expanded vertically
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		TargetCell : the cell or the range as a string that should receive a new value
&apos;&apos;&apos;		Value: a scalar, a vector or an array with the new values
&apos;&apos;&apos;			The new values should be strings, numeric values or dates. Other types empty the corresponding cell
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the updated range
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		OFFSETADDRESSERROR		The computed range of cells falls beyond the sheet boundaries
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.SetArray(&quot;SheetX.A1&quot;, SF_Array.RangeInit(1, 1000))

Dim sSet As String					&apos;	Return value
Dim oSet As Object					&apos;	_Address alias of sSet
Dim vDataArray As Variant			&apos;	DataArray compatible with .DataArray UNO property
Const cstThisSub = &quot;SFDocuments.Calc.SetArray&quot;
Const cstSubArgs = &quot;TargetCell, Value&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sSet = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(TargetCell, &quot;TargetCell&quot;, V_STRING) Then GoTo Finally
		If IsArray(Value) Then
			If Not ScriptForge.SF_Utils._ValidateArray(Value, &quot;Value&quot;) Then GoTo Finally
		Else
			If Not ScriptForge.SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Finally
		End If
	End If

Try:
	&apos;	Convert argument to data array and derive new range from its size
	vDataArray = _ConvertToDataArray(Value)
	If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
	Set oSet = _Offset(TargetCell, 0, 0, plHeight := UBound(vDataArray) + 1, plWidth := UBound(vDataArray(0)) + 1)	&apos;	+1 : vDataArray is zero-based
	With oSet
		.XCellRange.setDataArray(vDataArray)
		sSet = .RangeName
	End With

Finally:
	SetArray = sSet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.SetArray

REM -----------------------------------------------------------------------------
Public Function SetCellStyle(Optional ByVal TargetRange As Variant _
								, Optional ByVal Style As Variant _
								) As String
&apos;&apos;&apos;	Apply the given cell style in the given range
&apos;&apos;&apos;	The full range is updated and the remainder of the sheet is left untouched
&apos;&apos;&apos;	If the cell style does not exist, an error is raised
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		TargetRange : the range as a string that should receive a new cell style
&apos;&apos;&apos;		Style: the style name as a string
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the updated range
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.SetCellStyle(&quot;A1:F1&quot;, &quot;Heading 2&quot;)

Dim sSet As String					&apos;	Return value
Dim oAddress As _Address				&apos;	Alias of TargetRange
Dim oStyleFamilies As Object		&apos;	com.sun.star.container.XNameAccess
Dim vStyles As Variant				&apos;	Array of existing cell styles
Const cstStyle = &quot;CellStyles&quot;
Const cstThisSub = &quot;SFDocuments.Calc.SetCellStyle&quot;
Const cstSubArgs = &quot;TargetRange, Style&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sSet = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
		Set oStyleFamilies = _Component.StyleFamilies
		If oStyleFamilies.hasByName(cstStyle) Then vStyles = oStyleFamilies.getByName(cstStyle).getElementNames() Else vStyles = Array()
		If Not ScriptForge.SF_Utils._Validate(Style, &quot;Style&quot;, V_STRING, vStyles) Then GoTo Finally
	End If

Try:
	Set oAddress = _ParseAddress(TargetRange)
	With oAddress
		.XCellRange.CellStyle = Style
		sSet = .RangeName
	End With

Finally:
	SetCellStyle = sSet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.SetCellStyle

REM -----------------------------------------------------------------------------
Public Function SetFormula(Optional ByVal TargetRange As Variant _
								, Optional ByRef Formula As Variant _
								) As String
&apos;&apos;&apos;	Set the given (array of) formulae in the given range
&apos;&apos;&apos;	The full range is updated and the remainder of the sheet is left untouched
&apos;&apos;&apos;	If the given formula is a string:
&apos;&apos;&apos;		the unique formula is pasted across the whole range with adjustment of the relative references
&apos;&apos;&apos;	Otherwise
&apos;&apos;&apos;		If the size of Formula &lt; the size of Range, then the other cells are emptied
&apos;&apos;&apos;		If the size of Formula &gt; the size of Range, then Formula is only partially copied
&apos;&apos;&apos;		Vectors are always expanded vertically, except if the range has a height of exactly 1 row
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		TargetRange : the range as a string that should receive a new Formula
&apos;&apos;&apos;		Formula: a scalar, a vector or an array with the new formula(e) as strings  for each cell of the range.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the updated range
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.SetFormula(&quot;A1&quot;, &quot;=A2&quot;)
&apos;&apos;&apos;		oDoc.SetFormula(&quot;A1:F1&quot;, Array(&quot;=A2&quot;, &quot;=B2&quot;, &quot;=C2+10&quot;))		&apos;	Horizontal vector, partially empty
&apos;&apos;&apos;		oDoc.SetFormula(&quot;A1:D2&quot;, &quot;=E1&quot;)		&apos;	D2 contains the formula &quot;=H2&quot;

Dim sSet As String					&apos;	Return value
Dim oAddress As Object				&apos;	Alias of TargetRange
Dim vDataArray As Variant			&apos;	DataArray compatible with .DataArray UNO property
Const cstThisSub = &quot;SFDocuments.Calc.SetFormula&quot;
Const cstSubArgs = &quot;TargetRange, Formula&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sSet = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
		If IsArray(Formula) Then
			If Not ScriptForge.SF_Utils._ValidateArray(Formula, &quot;Formula&quot;, 0, V_STRING) Then GoTo Finally
		Else
			If Not ScriptForge.SF_Utils._Validate(Formula, &quot;Formula&quot;, V_STRING) Then GoTo Finally
		End If
	End If

Try:
	Set oAddress = _ParseAddress(TargetRange)
	With oAddress
		If IsArray(Formula) Then
			&apos;	Convert to data array and limit its size to the size of the initial range
			vDataArray = _ConvertToDataArray(Formula, .Height - 1, .Width - 1)
			If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
			.XCellRange.setFormulaArray(vDataArray)
		Else
			With .XCellRange
				&apos;	Store formula in top-left cell and paste it along the whole range
				.getCellByPosition(0, 0).setFormula(Formula)
				.fillSeries(com.sun.star.sheet.FillDirection.TO_BOTTOM, com.sun.star.sheet.FillMode.SIMPLE, 0, 0, 0)
				.fillSeries(com.sun.star.sheet.FillDirection.TO_RIGHT, com.sun.star.sheet.FillMode.SIMPLE, 0, 0, 0)
			End With
		End If
		sSet = .RangeName
	End With

Finally:
	SetFormula = sSet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.SetFormula

REM -----------------------------------------------------------------------------
Private Function SetProperty(Optional ByVal psProperty As String _
								, Optional ByVal pvValue As Variant _
								) As Boolean
&apos;&apos;&apos;	Set the new value of the named property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psProperty: the name of the property
&apos;&apos;&apos;		pvValue: the new value of the given property
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True if successful

Dim bSet As Boolean							&apos;	Return value
Static oSession As Object					&apos;	Alias of SF_Session
Dim cstThisSub As String
Const cstSubArgs = &quot;Value&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	bSet = False

	cstThisSub = &quot;SFDocuments.Calc.set&quot; &amp; psProperty
	If IsMissing(pvValue) Then pvValue = Empty
	&apos;ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)	&apos;	Validation done in Property Lets

	If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
	bSet = True
	Select Case UCase(psProperty)
		Case UCase(&quot;CurrentSelection&quot;)
			CurrentSelection = pvValue
		Case UCase(&quot;CustomProperties&quot;)
			CustomProperties = pvValue
		Case UCase(&quot;Description&quot;)
			Description = pvValue
		Case UCase(&quot;Keywords&quot;)
			Keywords = pvValue
		Case UCase(&quot;Subject&quot;)
			Subject = pvValue
		Case UCase(&quot;Title&quot;)
			Title = pvValue
		Case Else
			bSet = False
	End Select

Finally:
	SetProperty = bSet
	&apos;ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Calc.SetProperty

REM -----------------------------------------------------------------------------
Public Function SetValue(Optional ByVal TargetRange As Variant _
								, Optional ByRef Value As Variant _
								) As String
&apos;&apos;&apos;	Set the given value in the given range
&apos;&apos;&apos;	The full range is updated and the remainder of the sheet is left untouched
&apos;&apos;&apos;	If the size of Value &lt; the size of Range, then the other cells are emptied
&apos;&apos;&apos;	If the size of Value &gt; the size of Range, then Value is only partially copied
&apos;&apos;&apos;	Vectors are always expanded vertically, except if the range has a height of exactly 1 row
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		TargetRange : the range as a string that should receive a new value
&apos;&apos;&apos;		Value: a scalar, a vector or an array with the new values for each cell of the range.
&apos;&apos;&apos;			The new values should be strings, numeric values or dates. Other types empty the corresponding cell
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A string representing the updated range
&apos;&apos;&apos;	Examples:
&apos;&apos;&apos;		oDoc.SetValue(&quot;A1&quot;, 2)
&apos;&apos;&apos;		oDoc.SetValue(&quot;A1:F1&quot;, Array(1, 2, 3))		&apos;	Horizontal vector, partially empty
&apos;&apos;&apos;		oDoc.SetValue(&quot;A1:D2&quot;, SF_Array.AppendRow(Array(1, 2, 3, 4), Array(5, 6, 7, 8)))

Dim sSet As String					&apos;	Return value
Dim oAddress As Object				&apos;	Alias of TargetRange
Dim vDataArray As Variant			&apos;	DataArray compatible with .DataArray UNO property
Const cstThisSub = &quot;SFDocuments.Calc.SetValue&quot;
Const cstSubArgs = &quot;TargetRange, Value&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sSet = &quot;&quot;

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
		If IsArray(Value) Then
			If Not ScriptForge.SF_Utils._ValidateArray(Value, &quot;Value&quot;) Then GoTo Finally
		Else
			If Not ScriptForge.SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Finally
		End If
	End If

Try:
	Set oAddress = _ParseAddress(TargetRange)
	With oAddress
		&apos;	Convert to data array and limit its size to the size of the initial range
		vDataArray = _ConvertToDataArray(Value, .Height - 1, .Width - 1)
		If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
		.XCellRange.setDataArray(vDataArray)
		sSet = .RangeName
	End With

Finally:
	SetValue = sSet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.SetValue

REM -----------------------------------------------------------------------------
Public Function SortRange(Optional ByVal Range As Variant _
								, Optional ByVal SortKeys As Variant _
								, Optional ByVal SortOrder As Variant _
								, Optional ByVal DestinationCell As Variant _
								, Optional ByVal ContainsHeader As Variant _
								, Optional ByVal CaseSensitive As Variant _
								, Optional ByVal SortColumns As Variant _
								) As Variant
&apos;&apos;&apos;	Sort the given range on maximum 3 columns/rows. The sorting order may vary by column/row
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range: the range to sort as a string
&apos;&apos;&apos;		SortKeys: a scalar (if 1 column/row) or an array of column/row numbers starting from 1
&apos;&apos;&apos;		SortOrder: a scalar or an array of strings: &quot;ASC&quot; or &quot;DESC&quot;
&apos;&apos;&apos;			Each item is paired with the corresponding item in SortKeys
&apos;&apos;&apos;			If the SortOrder array is shorter than SortKeys, the remaining keys are sorted
&apos;&apos;&apos;				in ascending order
&apos;&apos;&apos;		DestinationCell: the destination of the sorted range of cells, as a string
&apos;&apos;&apos;			If given as range, the destination will be reduced to its top-left cell
&apos;&apos;&apos;			By default, Range is overwritten with its sorted content
&apos;&apos;&apos;		ContainsHeader: when True, the first row/column is not sorted. Default = False
&apos;&apos;&apos;		CaseSensitive: only for string comparisons, default = False
&apos;&apos;&apos;		SortColumns: when True, the columns are sorted from left to right
&apos;&apos;&apos;			Default = False: rows are sorted from top to bottom.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The modified range of cells as a string
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		oDoc.SortRange(&quot;A2:J200&quot;, Array(1, 3), , Array(&quot;ASC&quot;, &quot;DESC&quot;), CaseSensitive := True)
&apos;&apos;&apos;			&apos;	Sort on columns A (ascending) and C (descending)

Dim sSort As String					&apos;	Return value
Dim oRangeAddress As _Address		&apos;	Parsed range
Dim oRange As Object				&apos;	com.sun.star.table.XCellRange
Dim oDestRange As Object			&apos;	Destination as a range
Dim oDestAddress As Object			&apos;	com.sun.star.table.CellRangeAddress
Dim oDestCell As Object				&apos;	com.sun.star.table.CellAddress
Dim vSortDescriptor As Variant		&apos;	Array of com.sun.star.beans.PropertyValue
Dim vSortFields As Variant			&apos;	Array of com.sun.star.table.TableSortField
Dim sOrder As String				&apos;	Item in SortOrder
Dim i As Long
Const cstThisSub = &quot;SFDocuments.Calc.SortRange&quot;
Const cstSubArgs = &quot;Range, SortKeys, [TargetRange=&quot;&quot;&quot;&quot;], [SortOrder=&quot;&quot;ASC&quot;&quot;], [DestinationCell=&quot;&quot;&quot;&quot;], [ContainsHeader=False], [CaseSensitive=False], [SortColumns=False]&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	sSort = &quot;&quot;

Check:
	If IsMissing(SortKeys) Or IsEmpty(SortKeys) Then
		SortKeys = Array(1)
	ElseIf Not IsArray(SortKeys) Then
		SortKeys = Array(SortKeys)
	End If
	If IsMissing(DestinationCell) Or IsEmpty(DestinationCell) Then DestinationCell = &quot;&quot;
	If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then
		SortOrder = Array(&quot;ASC&quot;)
	ElseIf Not IsArray(SortOrder) Then
		SortOrder = Array(SortOrder)
	End If
	If IsMissing(ContainsHeader) Or IsEmpty(ContainsHeader) Then ContainsHeader = False
	If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
	If IsMissing(SortColumns) Or IsEmpty(SortColumns) Then SortColumns = False
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
		If Not ScriptForge.SF_Utils._ValidateArray(SortKeys, &quot;SortKeys&quot;, 1, V_NUMERIC, True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
		If Not ScriptForge.SF_Utils._ValidateArray(SortOrder, &quot;SortOrder&quot;, 1, V_STRING, True) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(ContainsHeader, &quot;ContainsHeader&quot;, V_BOOLEAN) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(CaseSensitive, &quot;CaseSensitive&quot;, V_BOOLEAN) Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(SortColumns, &quot;SortColumns&quot;, V_BOOLEAN) Then GoTo Finally
	End If
	Set oRangeAddress = _ParseAddress(Range)
	If Len(DestinationCell) &gt; 0 Then Set oDestRange = _ParseAddress(DestinationCell)

Try:
	&apos;	Initialize the sort descriptor
	Set oRange = oRangeAddress.XCellRange
	vSortDescriptor = oRange.createSortDescriptor
	ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;IsSortColumns&quot;, SortColumns)
	ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;ContainsHeader&quot;, ContainsHeader)
	ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;BindFormatsToContent&quot;, True)
	If Len(DestinationCell) = 0 Then
		ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;CopyOutputData&quot;, False)
	Else
		Set oDestAddress = oDestRange.XCellRange.RangeAddress
		Set oDestCell = New com.sun.star.table.CellAddress
		With oDestAddress
			oDestCell.Sheet = .Sheet
			oDestCell.Column = .StartColumn
			oDestCell.Row = .StartRow
		End With
		ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;CopyOutputData&quot;, true)
		ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;OutputPosition&quot;, oDestCell)
	End If
	ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;IsUserListEnabled&quot;, False)

	&apos;	Define the sorting keys
	vSortFields = Array()
	ReDim vSortFields(0 To UBound(SortKeys))
	For i = 0 To UBound(SortKeys)
		vSortFields(i) = New com.sun.star.table.TableSortField
		If i &gt; UBound(SortOrder) Then sOrder = &quot;&quot; Else sOrder = SortOrder(i)
		If Len(sOrder) = 0 Then sOrder = &quot;ASC&quot;
		With vSortFields(i)
			.Field = SortKeys(i) - 1
			.IsAscending = ( UCase(sOrder) = &quot;ASC&quot; )
			.IsCaseSensitive = CaseSensitive
		End With
	Next i

	&apos;	Associate the keys and the descriptor, and sort
	ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;SortFields&quot;, vSortFields)
	oRange.sort(vSortDescriptor)

	&apos;	Compute the changed area
	If Len(DestinationCell) = 0 Then
		sSort = oRangeAddress.RangeName
	Else
		With oRangeAddress
			sSort = _Offset(oDestRange, 0, 0, .Height, .Width).RangeName
		End With
	End If

Finally:
	SortRange = sSort
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc.SortRange

REM ======================================================= SUPERCLASS PROPERTIES

REM -----------------------------------------------------------------------------
Property Get CustomProperties() As Variant
	CustomProperties = [_Super].GetProperty(&quot;CustomProperties&quot;)
End Property	&apos;	SFDocuments.SF_Calc.CustomProperties

REM -----------------------------------------------------------------------------
Property Let CustomProperties(Optional ByVal pvCustomProperties As Variant)
	[_Super].CustomProperties = pvCustomProperties
End Property	&apos;	SFDocuments.SF_Calc.CustomProperties

REM -----------------------------------------------------------------------------
Property Get Description() As Variant
	Description = [_Super].GetProperty(&quot;Description&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Description

REM -----------------------------------------------------------------------------
Property Let Description(Optional ByVal pvDescription As Variant)
	[_Super].Description = pvDescription
End Property	&apos;	SFDocuments.SF_Calc.Description

REM -----------------------------------------------------------------------------
Property Get DocumentProperties() As Variant
	DocumentProperties = [_Super].GetProperty(&quot;DocumentProperties&quot;)
End Property	&apos;	SFDocuments.SF_Calc.DocumentProperties

REM -----------------------------------------------------------------------------
Property Get DocumentType() As String
	DocumentType = [_Super].GetProperty(&quot;DocumentType&quot;)
End Property	&apos;	SFDocuments.SF_Calc.DocumentType

REM -----------------------------------------------------------------------------
Property Get IsBase() As Boolean
	IsBase = [_Super].GetProperty(&quot;IsBase&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsBase

REM -----------------------------------------------------------------------------
Property Get IsCalc() As Boolean
	IsCalc = [_Super].GetProperty(&quot;IsCalc&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsCalc

REM -----------------------------------------------------------------------------
Property Get IsDraw() As Boolean
	IsDraw = [_Super].GetProperty(&quot;IsDraw&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsDraw

REM -----------------------------------------------------------------------------
Property Get IsImpress() As Boolean
	IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsImpress

REM -----------------------------------------------------------------------------
Property Get IsMath() As Boolean
	IsMath = [_Super].GetProperty(&quot;IsMath&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsMath

REM -----------------------------------------------------------------------------
Property Get IsWriter() As Boolean
	IsWriter = [_Super].GetProperty(&quot;IsWriter&quot;)
End Property	&apos;	SFDocuments.SF_Calc.IsWriter

REM -----------------------------------------------------------------------------
Property Get Keywords() As Variant
	Keywords = [_Super].GetProperty(&quot;Keywords&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Keywords

REM -----------------------------------------------------------------------------
Property Let Keywords(Optional ByVal pvKeywords As Variant)
	[_Super].Keywords = pvKeywords
End Property	&apos;	SFDocuments.SF_Calc.Keywords

REM -----------------------------------------------------------------------------
Property Get Readonly() As Variant
	Readonly = [_Super].GetProperty(&quot;Readonly&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Readonly

REM -----------------------------------------------------------------------------
Property Get Subject() As Variant
	Subject = [_Super].GetProperty(&quot;Subject&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Subject

REM -----------------------------------------------------------------------------
Property Let Subject(Optional ByVal pvSubject As Variant)
	[_Super].Subject = pvSubject
End Property	&apos;	SFDocuments.SF_Calc.Subject

REM -----------------------------------------------------------------------------
Property Get Title() As Variant
	Title = [_Super].GetProperty(&quot;Title&quot;)
End Property	&apos;	SFDocuments.SF_Calc.Title

REM -----------------------------------------------------------------------------
Property Let Title(Optional ByVal pvTitle As Variant)
	[_Super].Title = pvTitle
End Property	&apos;	SFDocuments.SF_Calc.Title

REM -----------------------------------------------------------------------------
Property Get XComponent() As Variant
	XComponent = [_Super].GetProperty(&quot;XComponent&quot;)
End Property	&apos;	SFDocuments.SF_Calc.XComponent

REM ========================================================== SUPERCLASS METHODS

REM -----------------------------------------------------------------------------
&apos;Public Function Activate() As Boolean
&apos;	Activate = [_Super].Activate()
&apos;End Function    &apos;   SFDocuments.SF_Calc.Activate

REM -----------------------------------------------------------------------------
Public Function CloseDocument(Optional ByVal SaveAsk As Variant) As Boolean
	CloseDocument = [_Super].CloseDocument(SaveAsk)
End Function   &apos;   SFDocuments.SF_Calc.CloseDocument

REM -----------------------------------------------------------------------------
Public Sub RunCommand(Optional ByVal Command As Variant)
	[_Super].RunCommand(Command)
End Sub		  &apos;   SFDocuments.SF_Calc.RunCommand

REM -----------------------------------------------------------------------------
Public Function Save() As Boolean
	Save = [_Super].Save()
End Function   &apos;   SFDocuments.SF_Calc.Save

REM -----------------------------------------------------------------------------
Public Function SaveAs(Optional ByVal FileName As Variant _
							, Optional ByVal Overwrite As Variant _
							, Optional ByVal Password As Variant _
							, Optional ByVal FilterName As Variant _
							, Optional ByVal FilterOptions As Variant _
							) As Boolean
	SaveAs = [_Super].SaveAs(FileName, Overwrite, Password, FilterName, FilterOptions)
End Function   &apos;   SFDocuments.SF_Calc.SaveAs

REM -----------------------------------------------------------------------------
Public Function SaveCopyAs(Optional ByVal FileName As Variant _
							, Optional ByVal Overwrite As Variant _
							, Optional ByVal Password As Variant _
							, Optional ByVal FilterName As Variant _
							, Optional ByVal FilterOptions As Variant _
							) As Boolean
	SaveCopyAs = [_Super].SaveCopyAs(FileName, Overwrite, Password, FilterName, FilterOptions)
End Function   &apos;   SFDocuments.SF_Calc.SaveCopyAs

REM =========================================================== PRIVATE FUNCTIONS

REM -----------------------------------------------------------------------------
Public Function _ConvertFromDataArray(ByRef pvDataArray As Variant) As Variant
&apos;&apos;&apos;	Convert a data array to a scalar, a vector or a 2D array
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvDataArray: an array as returned by the XCellRange.getDataArray or .getFormulaArray methods
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A scalar, a zero-based 1D array or a zero-based 2D array of strings and/or doubles
&apos;&apos;&apos;		To convert doubles to dates, use the CDate builtin function

Dim vArray As Variant				&apos;	Return value
Dim lMax1 As Long					&apos;	UBound of pvDataArray
Dim lMax2 As Long					&apos;	UBound of pvDataArray items
Dim i As Long
Dim j As Long

	vArray = Empty

Try:
	&apos;	Convert the data array to scalar, vector or array
	lMax1 = UBound(pvDataArray)
	If lMax1 &gt;= 0 Then
		lMax2 = UBound(pvDataArray(0))
		If lMax2 &gt;= 0 Then
			If lMax1 + lMax2 &gt; 0 Then vArray = Array()
			Select Case True
				Case lMax1 = 0 And lMax2 = 0			&apos;	Scalar
					vArray = pvDataArray(0)(0)
				Case lMax1 &gt; 0 And lMax2 = 0			&apos;	Vertical vector
					ReDim vArray(0 To lMax1)
					For i = 0 To lMax1
						vArray(i) = pvDataArray(i)(0)
					Next i
				Case lMax1 = 0 And lMax2 &gt; 0			&apos;	Horizontal vector
					ReDim vArray(0 To lMax2)
					For j = 0 To lMax2
						vArray(j) = pvDataArray(0)(j)
					Next j
				Case Else								&apos;	Array
					ReDim vArray(0 To lMax1, 0 To lMax2)
					For i = 0 To lMax1
						For j = 0 To lMax2
							vArray(i, j) = pvDataArray(i)(j)
						Next j
					Next i			
			End Select
		End If
	End If

Finally:
	_ConvertFromDataArray = vArray
End Function	&apos;	SF_Documents.SF_Calc._ConvertFromDataArray

REM -----------------------------------------------------------------------------
Private Function _ConvertToCellValue(ByVal pvItem As Variant) As Variant
&apos;&apos;&apos;	Convert the argument to a valid Calc cell content

Dim vCell As Variant		&apos;	Return value

Try:
	Select Case ScriptForge.SF_Utils._VarTypeExt(pvItem)
		Case V_STRING					:		vCell = pvItem
		Case V_DATE						:		vCell = CDbl(pvItem)
		Case ScriptForge.V_NUMERIC		:		vCell = CDbl(pvItem)
		Case ScriptForge.V_BOOLEAN		:		vCell = CDbl(Iif(pvItem, 1, 0))
		Case Else						:		vCell = &quot;&quot;
	End Select

Finally:
	_ConvertToCellValue = vCell
	Exit Function
End Function	&apos;	SF_Documents.SF_Calc._ConvertToCellValue

REM -----------------------------------------------------------------------------
Private Function _ConvertToDataArray(ByRef pvArray As Variant _
								, Optional ByVal plRows As Long _
								, Optional ByVal plColumns As Long _
								) As Variant
&apos;&apos;&apos;	Create a 2-dimensions nested array (compatible with the ranges .DataArray property)
&apos;&apos;&apos;	from a scalar, a 1D array or a 2D array
&apos;&apos;&apos;	Input may be a 1D array of arrays, typically when call issued by a Python script
&apos;&apos;&apos;	Array items are converted to (possibly empty) strings or doubles
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvArray: the input scalar or array. If array, must be 1 or 2D otherwise it is ignored.
&apos;&apos;&apos;		plRows, plColumns: the upper bounds of the data array
&apos;&apos;&apos;			If bigger than input array, fill with zero-length strings
&apos;&apos;&apos;			If smaller than input array, truncate
&apos;&apos;&apos;			If plRows = 0 and the input array is a vector, the data array is aligned horizontally
&apos;&apos;&apos;			They are either both present or both absent
&apos;&apos;&apos;				When absent
&apos;&apos;&apos;					The size of the output is fully determined by the input array
&apos;&apos;&apos;					Vectors are aligned vertically
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A data array compatible with ranges .DataArray property
&apos;&apos;&apos;		The output is always an array of nested arrays

Dim vDataArray() As Variant			&apos;	Return value
Dim vVector() As Variant			&apos;	A temporary 1D array
Dim vItem As Variant				&apos;	A single input item
Dim iDims As Integer				&apos;	Number of dimensions of the input argument
Dim lMin1 As Long					&apos;	Lower bound (1) of input array
Dim lMax1 As Long					&apos;	Upper bound (1)
Dim lMin2 As Long					&apos;	Lower bound (2)
Dim lMax2 As Long					&apos;	Upper bound (2)
Dim lRows As Long					&apos;	Upper bound of vDataArray
Dim lCols As Long					&apos;	Upper bound of vVector
Dim bHorizontal As Boolean			&apos;	Horizontal vector
Dim bDataArray As Boolean			&apos;	Input array is already an array of arrays
Dim i As Long
Dim j As Long

Const cstEmpty = &quot;&quot;		&apos;	Empty cell

	If IsMissing(plRows) Or IsEmpty(plRows) Then plRows = -1
	If IsMissing(plColumns) Or IsEmpty(plColumns) Then plColumns = -1

	vDataArray = Array()

Try:
	&apos;	Check the input argument and know its boundaries
	iDims = ScriptForge.SF_Array.CountDims(pvArray)
	If iDims = 0 Or iDims &gt; 2 Then Exit Function
	lMin1 = 0		:	lMax1 = 0	&apos;	Default values
	lMin2 = 0		:	lMax2 = 0
	Select Case iDims
		Case -1													&apos;	Scalar value
		Case 1
			bHorizontal = ( plRows = 0 And plColumns &gt; 0 )
			bDataArray = IsArray(pvArray(0))
			If Not bDataArray Then
				If Not bHorizontal Then
					lMin1 = LBound(pvArray)	:	lMax1 = UBound(pvArray)
				Else
					lMin2 = LBound(pvArray)	:	lMax2 = UBound(pvArray)
				End If
			Else
				iDims = 2
				lMin1 = LBound(pvArray)	:	lMax1 = UBound(pvArray)
				lMin2 = LBound(pvArray(0))	:	lMax2 = UBound(pvArray(0))
			End If
		Case 2
			lMin1 = LBound(pvArray, 1)	:	lMax1 = UBound(pvArray, 1)
			lMin2 = LBound(pvArray, 2)	:	lMax2 = UBound(pvArray, 2)
	End Select

	&apos;	Set the output dimensions accordingly
	If plRows &gt;= 0 Then		&apos;	Dimensions of output are imposed
		lRows = plRows
		lCols = plColumns
	Else					&apos;	Dimensions of output determined by input argument
		lRows = 0		:	lCols = 0	&apos;	Default values
		Select Case iDims
			Case -1													&apos;	Scalar value
			Case 1													&apos;	Vectors are aligned vertically
				lRows = lMax1 - lMin1
			Case 2
				lRows = lMax1 - lMin1
				lCols = lMax2 - lMin2
		End Select
	End If
	ReDim vDataArray(0 To lRows)
	
	&apos;	Feed the output array row by row, each row being a vector
	For i = 0 To lRows
		ReDim vVector(0 To lCols)
		For j = 0 To lCols
			If i &gt; lMax1 - lMin1 Then
				vVector(j) = cstEmpty
			ElseIf j &gt; lMax2 - lMin2 Then
				vVector(j) = cstEmpty
			Else
				Select Case iDims
					Case -1		:	vItem = _ConvertToCellValue(pvArray)
					Case 1
						If bHorizontal Then
							vItem = _ConvertToCellValue(pvArray(j + lMin2))
						Else
							vItem = _ConvertToCellValue(pvArray(i + lMin1))
						End If
					Case 2
						If bDataArray Then
							vItem = _ConvertToCellValue(pvArray(i + lMin1)(j + lMin2))
						Else
							vItem = _ConvertToCellValue(pvArray(i + lMin1, j + lMin2))
						End If
				End Select
				vVector(j) = vItem
			End If
			vDataArray(i) = vVector
		Next j
	Next i

Finally:
	_ConvertToDataArray = vDataArray
	Exit Function	
End Function	&apos;	SF_Documents.SF_Calc._ConvertToDataArray

REM -----------------------------------------------------------------------------
Private Function _DFunction(ByVal psFunction As String _
								, Optional ByVal Range As Variant _
								) As Double
&apos;&apos;&apos;	Apply the given function on all the numeric values stored in the given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		Range : the range as a string where to apply the function on
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The resulting value as a double

Dim dblGet As Double				&apos;	Return value
Dim oAddress As Object				&apos;	Alias of Range
Dim vFunction As Variant			&apos;	com.sun.star.sheet.GeneralFunction.XXX
Dim cstThisSub As String	:	cstThisSub = &quot;SFDocuments.Calc.&quot; &amp; psFunction
Const cstSubArgs = &quot;Range&quot;

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
	dblGet = 0

Check:
	If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
		If Not _IsStillAlive() Then GoTo Finally
		If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
	End If

Try:
	&apos;	Get the data
	Set oAddress = _ParseAddress(Range)
	Select Case psFunction
		Case &quot;DAvg&quot;		:	vFunction = com.sun.star.sheet.GeneralFunction.AVERAGE
		Case &quot;DCount&quot;	:	vFunction = com.sun.star.sheet.GeneralFunction.COUNTNUMS
		Case &quot;DMax&quot;		:	vFunction = com.sun.star.sheet.GeneralFunction.MAX
		Case &quot;DMin&quot;		:	vFunction = com.sun.star.sheet.GeneralFunction.MIN
		Case &quot;DSum&quot;		:	vFunction = com.sun.star.sheet.GeneralFunction.SUM
		Case Else		:	GoTo Finally
	End Select
	dblGet = oAddress.XCellRange.computeFunction(vFunction)

Finally:
	_DFunction = dblGet
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc._DFunction

REM -----------------------------------------------------------------------------
Private Function _FileIdent() As String
&apos;&apos;&apos;	Returns a file identification from the information that is currently available
&apos;&apos;&apos;	Useful e.g. for display in error messages

	_FileIdent = [_Super]._FileIdent()

End Function	&apos;	SFDocuments.SF_Calc._FileIdent

REM -----------------------------------------------------------------------------
Function _GetColumnName(ByVal plColumnNumber As Long) As String
&apos;&apos;&apos;	Convert a column number (range 1, 2,..1024) into its letter counterpart (range &apos;A&apos;, &apos;B&apos;,..&apos;AMJ&apos;).
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		ColumnNumber: the column number, must be in the interval 1 ... 1024
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		a string representation of the column name, in range &apos;A&apos;..&apos;AMJ&apos;
&apos;&apos;&apos;	Adapted from a Python function by sundar nataraj 
&apos;&apos;&apos;	http://stackoverflow.com/questions/23861680/convert-spreadsheet-number-to-column-letter

Dim sCol As String			&apos;	Return value
Dim lDiv As Long			&apos;	Intermediate result
Dim lMod As Long			&apos;	Result of modulo 26 operation

Try:
	lDiv = plColumnNumber
	Do While lDiv &gt; 0
		lMod = (lDiv - 1) Mod 26
		sCol = Chr(65 + lMod) + sCol
		lDiv = Int((lDiv - lMod)/26)
	Loop

Finally:
	_GetColumnName = sCol
End Function	&apos;	SFDocuments.SF_Calc._GetColumnName

REM -----------------------------------------------------------------------------
Private Function _IsStillAlive(Optional ByVal pbForUpdate As Boolean _
									, Optional ByVal pbError As Boolean _
									) As Boolean
&apos;&apos;&apos;	Returns True if the document has not been closed manually or incidentally since the last use
&apos;&apos;&apos;	If dead the actual instance is disposed. The execution is cancelled when pbError = True (default)
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pbForUpdate: if True (default = False), check additionally if document is open for editing
&apos;&apos;&apos;		pbError: if True (default), raise a fatal error

Dim bAlive As Boolean			&apos;	Return value

	If IsMissing(pbForUpdate) Then pbForUpdate = False
	If IsMissing(pbError) Then pbError = True

Try:
	bAlive = [_Super]._IsStillAlive(pbForUpdate, pbError)

Finally:
	_IsStillAlive = bAlive
	Exit Function
End Function	&apos;	SFDocuments.SF_Calc._IsStillAlive

REM -----------------------------------------------------------------------------
Private Function _LastCell(ByRef poSheet As Object) As Variant
&apos;&apos;&apos;	Returns in an array the coordinates of the last used cell in the given sheet

Dim oCursor As Object				&apos;	Cursor on the cell
Dim oRange As Object				&apos;	The used range
Dim vCoordinates(0 To 1) As Long	&apos;	Return value: (0) = Column, (1) = Row

Try:
	Set oCursor = poSheet.createCursorByRange(poSheet.getCellRangeByName(&quot;A1&quot;))
	oCursor.gotoEndOfUsedArea(True)
	Set oRange = poSheet.getCellRangeByName(oCursor.AbsoluteName)

	vCoordinates(0) = oRange.RangeAddress.EndColumn + 1
	vCoordinates(1) = oRange.RangeAddress.EndRow + 1

Finally:
	_LastCell = vCoordinates
End Function	&apos;	SFDocuments.SF_Calc._LastCell

REM -----------------------------------------------------------------------------
Public Function _Offset(ByRef pvRange As Variant _
								, ByVal plRows As Long _
								, ByVal plColumns As Long _
								, ByVal plHeight As Long _
								, ByVal plWidth As Long _
								) As Object
&apos;&apos;&apos;	Returns a new range offset by a certain number of rows and columns from a given range
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvRange : the range, as a string or an object, from which the function searches for the new range
&apos;&apos;&apos;		plRows : the number of rows by which the reference was corrected up (negative value) or down.
&apos;&apos;&apos;		plColumns : the number of columns by which the reference was corrected to the left (negative value) or to the right.
&apos;&apos;&apos;		plHeight : the vertical height for an area that starts at the new reference position.
&apos;&apos;&apos;		plWidth : the horizontal width for an area that starts at the new reference position.
&apos;&apos;&apos;		Arguments Rows and Columns must not lead to zero or negative start row or column.
&apos;&apos;&apos;		Arguments Height and Width must not lead to zero or negative count of rows or columns.
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A new range as object of type _Address
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		OFFSETADDRESSERROR		The computed range of cells falls beyond the sheet boundaries

Dim oOffset As Object				&apos;	Return value
Dim oAddress As Object				&apos;	Alias of Range
Dim oSheet As Object				&apos;	com.sun.star.sheet.XSpreadsheet
Dim oRange As Object				&apos;	com.sun.star.table.XCellRange
Dim oNewRange As Object				&apos;	com.sun.star.table.XCellRange
Dim lLeft As Long					&apos;	New range coordinates
Dim lTop As Long
Dim lRight As Long
Dim lBottom As Long

	Set oOffset = Nothing

Check:
	If plHeight &lt; 0 Or plWidth &lt; 0 Then GoTo CatchAddress

Try:
	If VarType(pvRange) = V_STRING Then Set oAddress = _ParseAddress(pvRange) Else Set oAddress = pvRange
	Set oSheet = oAddress.XSpreadSheet
	Set oRange = oAddress.XCellRange.RangeAddress


	&apos;	Compute and validate new coordinates
	With oRange
		lLeft = .StartColumn + plColumns
		lTop = .StartRow + plRows
		lRight = lLeft + Iif(plWidth = 0, .EndColumn - .StartColumn, plWidth - 1)
		lBottom = lTop + Iif(plHeight = 0, .EndRow - .StartRow, plHeight - 1)
		If lLeft &lt; 0 Or lRight &lt; 0 Or lTop &lt; 0 Or lBottom &lt; 0 _
			Or lLeft &gt; MAXCOLS Or lRight &gt; MAXCOLS _
			Or lTop &gt; MAXROWS Or lBottom &gt; MAXROWS _
				Then GoTo CatchAddress
		Set oNewRange = oSheet.getCellRangeByPosition(lLeft, lTop, lRight, lBottom)
	End With

	&apos;	Define the new range address
	Set oOffset = New _Address
	With oOffset
		.ObjectType = CALCREFERENCE
		.ServiceName = SERVICEREFERENCE
		.RawAddress = oNewRange.AbsoluteName
		.Component = _Component
		.XSpreadsheet = oNewRange.Spreadsheet
		.SheetName = .XSpreadsheet.Name
		.SheetIndex = .XSpreadsheet.RangeAddress.Sheet
		.RangeName = .RawAddress
		.XCellRange = oNewRange
		.Height = oNewRange.RangeAddress.EndRow - oNewRange.RangeAddress.StartRow + 1
		.Width = oNewRange.RangeAddress.EndColumn - oNewRange.RangeAddress.StartColumn + 1
	End With

Finally:
	Set _Offset = oOffset
	Exit Function
Catch:
	GoTo Finally
CatchAddress:
	ScriptForge.SF_Exception.RaiseFatal(OFFSETADDRESSERROR, &quot;Range&quot;, oAddress.RawAddress _
				, &quot;Rows&quot;, plRows, &quot;Columns&quot;, plColumns, &quot;Height&quot;, plHeight, &quot;Width&quot;, plWidth _
				, &quot;Document&quot;, [_Super]._FileIdent())
	GoTo Finally
End Function	&apos;	SF_Documents.SF_Calc._Offset

REM -----------------------------------------------------------------------------
Private Function _ParseAddress(ByVal psAddress As String) As Object
&apos;&apos;&apos;	Parse and validate a sheet or range reference
&apos;&apos;&apos;	Syntax to parse:
&apos;&apos;&apos;		[Sheet].[Range]
&apos;&apos;&apos;			Sheet 	=&gt; [&apos;][$]sheet[&apos;] or document named range or ~
&apos;&apos;&apos;			Range	=&gt; A1:D10, A1, A:D, 10:10 ($ ignored), or sheet named range or ~
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		An object of type _Address
&apos;&apos;&apos;	Exceptions:
&apos;&apos;&apos;		CALCADDRESSERROR		&apos;	Address could not be parsed to a valid address

Dim oAddress As _Address			&apos;	Return value
Dim lStart As Long					&apos;	Position of found regex
Dim sSheet As String				&apos;	Sheet component
Dim sRange As String				&apos;	Range component
Dim oSheets As Object				&apos;	com.sun.star.sheet.XSpreadsheets
Dim oNamedRanges As Object			&apos;	com.sun.star.sheet.XNamedRanges
Dim oRangeAddress As Object			&apos;	Alias for rangeaddress
Dim vLastCell As Variant			&apos;	Result of _LastCell() method
Dim oSelect As Object				&apos;	Current selection

	With oAddress
		sSheet = &quot;&quot;				:	sRange = &quot;&quot;
		.SheetName = &quot;&quot;			:	.RangeName = &quot;&quot;

		.ObjectType = CALCREFERENCE
		.ServiceName = SERVICEREFERENCE
		.RawAddress = psAddress
		Set .XSpreadSheet = Nothing	:	Set .XCellRange = Nothing
		
		&apos;	Split in sheet and range components - Check presence of surrounding single quotes or dot
		If Left(psAddress, 1) = &quot;&apos;&quot; Then
			lStart = 1
			sSheet = ScriptForge.SF_String.FindRegex(psAddress, &quot;^&apos;[^\[\]*?:\/\\]+&apos;&quot;)
			If lStart = 0 Then GoTo CatchAddress	&apos;	Invalid sheet name
			If Len(psAddress) &gt; Len(sSheet) + 1 Then
				If Mid(psAddress, Len(sSheet) + 1, 1) = &quot;.&quot; then sRange = Mid(psAddress, Len(sSheet) + 2)
			End If
			sSheet = Replace(Replace(sSheet, &quot;$&quot;, &quot;&quot;), &quot;&apos;&quot;, &quot;&quot;)
		ElseIf InStr(psAddress, &quot;.&quot;) &gt; 0 Then
			sSheet = Replace(Split(psAddress, &quot;.&quot;)(0), &quot;$&quot;, &quot;&quot;)
			sRange = Replace(Split(psAddress, &quot;.&quot;)(1), &quot;$&quot;, &quot;&quot;)
		Else
			sSheet = psAddress
		End If

		&apos;	Resolve sheet part: either a document named range, or the active sheet or a real sheet
		Set oSheets = _Component.getSheets()
		Set oNamedRanges = _Component.NamedRanges
		If oSheets.hasByName(sSheet) Then
		ElseIf sSheet = &quot;~&quot; And Len(sRange) &gt; 0 Then
			sSheet = _Component.CurrentController.ActiveSheet.Name
		ElseIf oNamedRanges.hasByName(sSheet) Then
			.XCellRange = oNamedRanges.getByName(sSheet).ReferredCells
			sSheet = oSheets.getByIndex(oNamedRanges.getByName(sSheet).ReferencePosition.Sheet).Name
		Else
			sRange = sSheet
			sSheet = _Component.CurrentController.ActiveSheet.Name
		End If
		.SheetName = sSheet
		.XSpreadSheet = oSheets.getByName(sSheet)
		.SheetIndex = .XSpreadSheet.RangeAddress.Sheet

		&apos;	Resolve range part - either a sheet named range or the current selection or a real range or &quot;&quot;
		If IsNull(.XCellRange) Then
			Set oNamedRanges = .XSpreadSheet.NamedRanges
			If sRange = &quot;~&quot; Then
				Set oSelect = _Component.CurrentController.getSelection()
				If oSelect.supportsService(&quot;com.sun.star.sheet.SheetCellRanges&quot;) Then	&apos;	Multiple selections
					Set .XCellRange = oSelect.getByIndex(0)
				Else
					Set .XCellRange = oSelect
				End If
			ElseIf sRange = &quot;*&quot; Or sRange = &quot;&quot; Then
				vLastCell = _LastCell(.XSpreadSheet)
				sRange = &quot;A1:&quot; &amp; _GetColumnName(vLastCell(0)) &amp; CStr(vLastCell(1))
				Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
			ElseIf oNamedRanges.hasByName(sRange) Then
				.XCellRange = oNamedRanges.getByName(sRange).ReferredCells
			Else
				On Local Error GoTo CatchError
				Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
				&apos;	If range reaches the limits of the sheets, reduce it up to the used area
				Set oRangeAddress = .XCellRange.RangeAddress
				If oRangeAddress.StartColumn = 0 And oRangeAddress.EndColumn = MAXCOLS - 1 Then
					vLastCell = _LastCell(.XSpreadSheet)
					sRange = &quot;A&quot; &amp; CStr(oRangeAddress.StartRow + 1) &amp; &quot;:&quot; _
									&amp; _GetColumnName(vLastCell(0)) &amp; CStr(oRangeAddress.EndRow + 1)
					Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
				ElseIf oRangeAddress.StartRow = 0 And oRangeAddress.EndRow = MAXROWS - 1 Then
					vLastCell = _LastCell(.XSpreadSheet)
					sRange = _GetColumnName(oRangeAddress.StartColumn + 1) &amp; &quot;1&quot; &amp; &quot;:&quot; _
									&amp; _GetColumnName(oRangeAddress.EndColumn + 1) &amp; CStr(_LastCell(.XSpreadSheet)(1))
					Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
				End If
			End If
		End If
		If IsNull(.XCellRange) Then GoTo CatchAddress

		Set oRangeAddress = .XCellRange.RangeAddress
		.RangeName = _RangeToString(oRangeAddress)
		.Height = oRangeAddress.EndRow - oRangeAddress.StartRow + 1
		.Width = oRangeAddress.EndColumn - oRangeAddress.StartColumn + 1

		&apos;	Remember the current component in case of use outside the current instance
		Set .Component = _Component

	End With

Finally:
	Set _ParseAddress = oAddress
	Exit Function
CatchError:
	ScriptForge.SF_Exception.Clear()
CatchAddress:
	ScriptForge.SF_Exception.RaiseFatal(CALCADDRESSERROR, &quot;Range&quot;, psAddress _
				, &quot;Document&quot;, [_Super]._FileIdent())
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Calc._ParseAddress

REM -----------------------------------------------------------------------------
Private Function _PropertyGet(Optional ByVal psProperty As String _
								, Optional ByVal pvArg As Variant _
								) As Variant
&apos;&apos;&apos;	Return the value of the named property
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		psProperty: the name of the property

Dim oProperties As Object			&apos;	Document or Custom properties
Dim vLastCell As Variant			&apos;	Coordinates of last used cell in a sheet
Dim oSelect As Object				&apos;	Current selection
Dim vRanges As Variant				&apos;	List of selected ranges
Dim i As Long
Dim cstThisSub As String
Const cstSubArgs = &quot;&quot;

	_PropertyGet = False

	cstThisSub = &quot;SFDocuments.SF_Calc.get&quot; &amp; psProperty
	ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
	If Not _IsStillAlive() Then GoTo Finally

	Select Case psProperty
		Case &quot;CurrentSelection&quot;
			Set oSelect = _Component.CurrentController.getSelection()
			If IsNull(oSelect) Then
				_PropertyGet = Array()
			ElseIf oSelect.supportsService(&quot;com.sun.star.sheet.SheetCellRanges&quot;) Then	&apos;	Multiple selections
				vRanges = Array()
				For i = 0 To oSelect.Count - 1
					vRanges = ScriptForge.SF_Array.Append(vRanges, oSelect.getByIndex(i).AbsoluteName)
				Next i
				_PropertyGet = vRanges
			Else
				_PropertyGet = oSelect.AbsoluteName
			End If
		Case &quot;Height&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				_PropertyGet = 0
			Else
				If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
				_PropertyGet = _ParseAddress(pvArg).Height
			End If
		Case &quot;LastCell&quot;, &quot;LastColumn&quot;, &quot;LastRow&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then	&apos;	Avoid errors when instance is watched in Basic IDE
				_PropertyGet = -1
			Else
				If Not _ValidateSheet(pvArg, &quot;SheetName&quot;, , True) Then GoTo Finally
				vLastCell = _LastCell(_Component.getSheets.getByName(pvArg))
				If psProperty = &quot;LastRow&quot; Then
					_PropertyGet = vLastCell(1)
				ElseIf psProperty = &quot;LastColumn&quot; Then
					_PropertyGet = vLastCell(0)
				Else
					_PropertyGet = GetColumnName(vLastCell(0)) &amp; CStr(vLastCell(1))
				End If
			End If
		Case &quot;Range&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				Set _PropertyGet = Nothing
			Else
				If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
				Set _PropertyGet = _ParseAddress(pvArg)
			End If
		Case &quot;Sheet&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				Set _PropertyGet = Nothing
			Else
				If Not _ValidateSheet(pvArg, &quot;SheetName&quot;, , True) Then GoTo Finally
				Set _PropertyGet = _ParseAddress(pvArg)
			End If
		Case &quot;Sheets&quot;
			_PropertyGet = _Component.getSheets.getElementNames()
		Case &quot;Width&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				_PropertyGet = 0
			Else
				If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
				_PropertyGet = _ParseAddress(pvArg).Width
			End If
		Case &quot;XCellRange&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				Set _PropertyGet = Nothing
			Else
				If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
				Set _PropertyGet = _ParseAddress(pvArg).XCellRange
			End If
		Case &quot;XSpreadsheet&quot;
			If IsMissing(pvArg) Or IsEmpty(pvArg) Then
				Set _PropertyGet = Nothing
			Else
				If Not _ValidateSheet(pvArg, &quot;SheetName&quot;, , True) Then GoTo Finally
				Set _PropertyGet = _Component.getSheets.getByName(pvArg)
			End If			
		Case Else
			_PropertyGet = Null
	End Select

Finally:
	ScriptForge.SF_Utils._ExitFunction(cstThisSub)
	Exit Function
End Function	&apos;	SFDocuments.SF_Calc._PropertyGet

REM -----------------------------------------------------------------------------
Private Function _RangeToString(ByRef poAddress As Object) As String
&apos;&apos;&apos;	Converts a range address to its A1 notation)

	With poAddress
		_RangeToString = _GetColumnName(.StartColumn + 1) &amp; CStr(.StartRow + 1) &amp; &quot;:&quot; _
									&amp; _GetColumnName(.EndColumn + 1) &amp; CStr(.EndRow + 1)
	End With

End Function	&apos;	SFDocuments.SF_Calc._RangeToString

REM -----------------------------------------------------------------------------
Private Function _Repr() As String
&apos;&apos;&apos;	Convert the SF_Calc instance to a readable string, typically for debugging purposes (DebugPrint ...)
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;	Return:
&apos;&apos;&apos;		&quot;[DOCUMENT]: Type/File&quot;

	_Repr = &quot;[Calc]: &quot; &amp; [_Super]._FileIdent()

End Function	&apos;	SFDocuments.SF_Calc._Repr

REM -----------------------------------------------------------------------------
Private Sub _RestoreSelections(ByRef pvComponent As Variant _
									, ByRef pvSelection As Variant _
									)
&apos;&apos;&apos;	Set the selection to a single or a multiple range
&apos;&apos;&apos;		Does not work well when multiple selections and macro terminating in Basic IDE
&apos;&apos;&apos;	Called by the CopyToCell and CopyToRange methods
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvComponent: should work for foreign instances as well
&apos;&apos;&apos;		pvSelection: the stored selection done previously by Component.CurrentController.getSelection()

Dim oCellRanges	As Object		&apos;	com.sun.star.sheet.SheetCellRanges
Dim vRangeAddresses As Variant	&apos;	Array of com.sun.star.table.CellRangeAddress
Dim i As Long

Try:
	If IsArray(pvSelection) Then
		Set oCellRanges = pvComponent.createInstance(&quot;com.sun.star.sheet.SheetCellRanges&quot;)
		vRangeAddresses = Array()
		ReDim vRangeAddresses(0 To UBound(pvSelection))
		For i = 0 To UBound(pvSelection)
			vRangeAddresses(i) = pvSelection.getByIndex(i).RangeAddress
		Next i
		oCellRanges.addRangeAddresses(vRangeAddresses, False)
		pvComponent.CurrentController.select(oCellRanges)
	Else
		pvComponent.CurrentController.select(pvSelection)
	End If

Finally:
	Exit Sub
End Sub	&apos;	SFDocuments.SF_Calc._RestoreSelections

REM -----------------------------------------------------------------------------
Private Function _ValidateSheet(Optional ByRef pvSheetName As Variant _
									, Optional ByVal psArgName As String _
									, Optional ByVal pvNew As Variant _
									, Optional ByVal pvActive As Variant _
									, Optional ByVal pvOptional as Variant _
									, Optional ByVal pvNumeric As Variant _
									, Optional ByVal pvReference As Variant _
									) As Boolean
&apos;&apos;&apos;	Sheet designation validation function similar to the SF_Utils._ValidateXXX functions
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvSheetName: string or numeric position
&apos;&apos;&apos;		pvArgName: the name of the variable to be used in the error message
&apos;&apos;&apos;		pvNew: if True, sheet must not exist (default = False)
&apos;&apos;&apos;		pvActive: if True, the shortcut &quot;~&quot; is accepted (default = False)
&apos;&apos;&apos;		pvOptional: if True, a zero-length string is accepted (default = False)
&apos;&apos;&apos;		pvNumeric: if True, the sheet position is accepted (default = False)
&apos;&apos;&apos;		pvReference: if True, a sheet reference is acceptable (default = False)
&apos;&apos;&apos;			pvNumeric and pvReference must not both be = True
&apos;&apos;&apos;	Returns
&apos;&apos;&apos;		True if valid. SheetName is reset to current value if = &quot;~&quot;
&apos;&apos;&apos;	Exceptions
&apos;&apos;&apos;		DUPLICATESHEETERROR		A sheet with the given name exists already

Dim vSheets As Variant				&apos;	List of sheets
Dim vTypes As Variant				&apos;	Array of accepted variable types
Dim bValid As Boolean				&apos;	Return value

Check:
	If IsMissing(pvNew) Or IsEmpty(pvNew) Then pvNew = False
	If IsMissing(pvActive) Or IsEmpty(pvActive) Then pvActive = False
	If IsMissing(pvOptional) Or IsEmpty(pvOptional) Then pvOptional = False
	If IsMissing(pvNumeric) Or IsEmpty(pvNumeric) Then pvNumeric = False
	If IsMissing(pvReference) Or IsEmpty(pvReference) Then pvReference = False

	&apos;	Define the acceptable variable types
	If pvNumeric Then
		vTypes = Array(V_STRING, V_NUMERIC)
	ElseIf pvReference Then
		vTypes = Array(V_STRING, ScriptForge.V_OBJECT)
	Else
		vTypes = V_STRING
	End If
	If Not ScriptForge.SF_Utils._Validate(pvSheetName, psArgName, vTypes, , , Iif(pvReference, CALCREFERENCE, &quot;&quot;)) Then GoTo Finally
	bValid = False

Try:
	If VarType(pvSheetName) = V_STRING Then
		If pvOptional And Len(pvSheetName) = 0 Then
		ElseIf pvActive And pvSheetName = &quot;~&quot; Then
			pvSheetName = _Component.CurrentController.ActiveSheet.Name
		Else
			vSheets = _Component.getSheets.getElementNames()
			If pvNew Then
				If ScriptForge.SF_Array.Contains(vSheets, pvSheetName) Then GoTo CatchDuplicate
			Else
				If Not ScriptForge.SF_Utils._Validate(pvSheetName, psArgName, V_STRING, vSheets) Then GoTo Finally
			End If
		End If
	End If
	bValid = True
	
Finally:
	_ValidateSheet = bValid
	Exit Function
CatchDuplicate:
	ScriptForge.SF_Exception.RaiseFatal(DUPLICATESHEETERROR, psArgName, pvSheetName, &quot;Document&quot;, [_Super]._FileIdent())
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Calc._ValidateSheet

REM ============================================ END OF SFDOCUMENTS.SF_CALC
</script:module>