diff --git a/src/CompilerTests/Applications/R870/Prg/R870.prg b/src/CompilerTests/Applications/R870/Prg/R870.prg
index 41628efb1f..71091580d3 100644
--- a/src/CompilerTests/Applications/R870/Prg/R870.prg
+++ b/src/CompilerTests/Applications/R870/Prg/R870.prg
@@ -9,12 +9,13 @@ c := "a" +;
? c
? StackFrame{true}:GetFileLineNumber()
-xAssert(StackFrame{true}:GetFileLineNumber() == 12)
-PROC xAssert(l AS LOGIC) AS VOID
- IF l
+xAssert(StackFrame{true}:GetFileLineNumber() , 12)
+
+PROC xAssert(nResult AS INT, nExpected AS INT) AS VOID
+ IF nExpected == nResult
? "Assertion passed"
ELSE
- THROW Exception{"Incorrect result"}
+ THROW Exception{ System.String.Format("Incorrect result, expected {0}, returned {1}", nExpected, nResult)}
END IF
RETURN
diff --git a/src/CompilerTests/Applications/RuntimeTests/Prg/RuntimeTests.prg b/src/CompilerTests/Applications/RuntimeTests/Prg/RuntimeTests.prg
index 84769fad68..94b8ac6354 100644
--- a/src/CompilerTests/Applications/RuntimeTests/Prg/RuntimeTests.prg
+++ b/src/CompilerTests/Applications/RuntimeTests/Prg/RuntimeTests.prg
@@ -17,7 +17,7 @@ FUNCTION Start() AS INT
"C427", "C433", "C434", "C435", "C437", "C441", "C444", "C445", "C446", "C448", ;
"C450", "C452", "C457", "C460", /*"C475",*/ "C478", "C479", "C484", "C499", "C504", ;
"C505", "C506", "C507", "C508", "C509", "C515", "C519", "C520", "C521", "C527", ;
- "C528", "C536", "C538", /*"C541",*/ "C542", "C543", "C548", "C552", "C557", "C558", ;
+ "C528", "C536", "C538", /*"C541",*/ "C542", "C543", "C552", "C557", "C558", ;
"C560", "C564", "C567", "C573", "C578", "C579", "C582", "C588", "C590", "C591", ;
"C599", "C602", "C604", "C606", "C607", "C609", "C610", "C611", "C612", "C613", "C615", ;
"C616", "C617", "C618", "C621", "C628", "C629", "C630", "C631", "C632", "C635", ;
@@ -42,7 +42,7 @@ FUNCTION Start() AS INT
"R711", "R712", "R725", "R729", "R730", "R732","R735", "R736","R741","R742","R743",;
"R750", "R751", "R752", "R753", "R754", "R755","R756", "R757","R759","R763", "R765",;
"R771", "R772", "R773", "R774", "R776", "R777", "R779","R780","R782", "R784", "R787", ;
- "R788", "R789", "R790", "R791", "R793", "R795", "R796", "R799", "R800","R803", ;
+ /*"R788", */"R789", "R790", "R791", "R793", "R795", "R796", "R799", "R800","R803", ;
"R820", "R821", /*"R822",*/ "R823", "R824", "R827", "R828", "R829", "R830", "R831","R832",;
"R835", "R836", "R839", "R840", "R842", "R848", "R849", "R850", "R855", "R856", "R858",;
"R861", "R862", "R863", "R864", "R865", "R868", "R870", "R871", "R872", "R873", ;
@@ -62,10 +62,13 @@ FUNCTION Start() AS INT
aTests:Remove("C418")
aTests:Remove("C479")
aTests:Remove("C521")
+
+ // uses StackFrame to get the line of code, but automated tests are running in release mode an this is not available
+ aTests:Remove("R870")
#endif
- #warning Removed C729 because it uses macrocompiler substitution which fails in the the tests
- aTests:Remove("C729")
+ aTests:Remove("C729") // Removed C729 because it uses macrocompiler substitution which fails in the the tests
+ aTests:Add( "C548" ) // Added C548 at the end because for some reason causes several tests following it to fail
// TODO Must fail: "C135"
diff --git a/src/CompilerTests/Automated/CompilerTests.prg b/src/CompilerTests/Automated/CompilerTests.prg
index 80807dd452..f1ec22a665 100644
--- a/src/CompilerTests/Automated/CompilerTests.prg
+++ b/src/CompilerTests/Automated/CompilerTests.prg
@@ -41,9 +41,8 @@ FUNCTION Start() AS VOID
gcCompilerFilename := "C:\Program Files (x86)\XSharp\Bin\xsc.exe"
cProjectFile := "C:\xSharp\Dev\src\CompilerTests\xSharp Tests30.viproj"
- gcRuntimeFolder := "C:\xSharp\Dev\Artifacts\Release\net46"
-// gcNetCoreRuntimeFolder := "C:\xSharp\Dev\Artifacts\Release"
- gcNetCoreRuntimeFolder := "C:\Program Files (x86)\XSharp\Redist\net8.0"
+ gcRuntimeFolder := "C:\xSharp\Dev\src\CompilerTests\Runtime"
+ gcNetCoreRuntimeFolder := gcRuntimeFolder + "Net8"
cTestTheFixedOnes := "TRUE"
cConfigName := "DEBUG"
cLogFilename := "C:\xSharp\Dev\src\CompilerTests\Automated\log.txt"
@@ -53,7 +52,7 @@ FUNCTION Start() AS VOID
gcCompilerFilename := Environment.GetEnvironmentVariable("XSCOMPILER")?:Trim()
cProjectFile := Environment.GetEnvironmentVariable("XSTESTPROJECT")?:Trim()
gcRuntimeFolder := Environment.GetEnvironmentVariable("XSRUNTIMEFOLDER")?:Trim()
- gcNetCoreRuntimeFolder := Environment.GetEnvironmentVariable("XSRUNTIMEFOLDER")?:Trim()
+ gcNetCoreRuntimeFolder := gcRuntimeFolder + "Net8"
cTestTheFixedOnes := Environment.GetEnvironmentVariable("XSFIXEDTESTS")?:Trim()
cConfigName := Environment.GetEnvironmentVariable("XSCONFIG")?:Trim()
cLogFilename := Environment.GetEnvironmentVariable("XSLOGFILE")?:Trim()
diff --git a/src/CompilerTests/Runtime/XSharp.Harbour.dll b/src/CompilerTests/Runtime/XSharp.Harbour.dll
index b68c11c164..46e56143a0 100644
Binary files a/src/CompilerTests/Runtime/XSharp.Harbour.dll and b/src/CompilerTests/Runtime/XSharp.Harbour.dll differ
diff --git a/src/CompilerTests/Runtime/XSharp.RT.dll b/src/CompilerTests/Runtime/XSharp.RT.dll
index f5ca1556f6..cd5b670f46 100644
Binary files a/src/CompilerTests/Runtime/XSharp.RT.dll and b/src/CompilerTests/Runtime/XSharp.RT.dll differ
diff --git a/src/CompilerTests/Runtime/XSharp.VFP.dll b/src/CompilerTests/Runtime/XSharp.VFP.dll
index cfb01a5a13..77df38c6c0 100644
Binary files a/src/CompilerTests/Runtime/XSharp.VFP.dll and b/src/CompilerTests/Runtime/XSharp.VFP.dll differ
diff --git a/src/CompilerTests/Runtime/XSharp.VO.dll b/src/CompilerTests/Runtime/XSharp.VO.dll
index a0b1db32e3..0943da8254 100644
Binary files a/src/CompilerTests/Runtime/XSharp.VO.dll and b/src/CompilerTests/Runtime/XSharp.VO.dll differ
diff --git a/src/CompilerTests/Runtime/XSharp.XPP.dll b/src/CompilerTests/Runtime/XSharp.XPP.dll
index f38f112fbd..ca49aaa6c5 100644
Binary files a/src/CompilerTests/Runtime/XSharp.XPP.dll and b/src/CompilerTests/Runtime/XSharp.XPP.dll differ
diff --git a/src/CompilerTests/xSharp Tests30.viproj b/src/CompilerTests/xSharp Tests30.viproj
index 7990b0ba15..e88badaca2 100644
--- a/src/CompilerTests/xSharp Tests30.viproj
+++ b/src/CompilerTests/xSharp Tests30.viproj
@@ -146894,7 +146894,7 @@ AppConfig = Release,22222222-2222-2222-2222-222222222222
Optimize=0
ENDApplication = D002 - Primary Constructor VO Dialect
-ApplicationGroup = B9CFE839-D401-428D-94E9-E9D21E9F772D
+ApplicationGroup = 70E3DAB7-5F3E-45A2-B617-437A91064B55
; ************** APPLICATION D003 - Primary Constructor NetCore VO Dialect *************
Application = D003 - Primary Constructor NetCore VO Dialect
IDEVersion = 1.06
diff --git a/src/Runtime/XSharp.Core/Language/Generic.resx b/src/Runtime/XSharp.Core/Language/Generic.resx
index b2cb96f518..b15d5862c3 100644
--- a/src/Runtime/XSharp.Core/Language/Generic.resx
+++ b/src/Runtime/XSharp.Core/Language/Generic.resx
@@ -2831,19 +2831,11 @@
Keysort on {0} is not supported for collections ordered by {1}
-
-
-
-
-
-
-
-
-
-
-
-
+
+ XML Parse error: {0}
+
+
+ Unable to infer XML schema.
+
-
-
\ No newline at end of file
diff --git a/src/Runtime/XSharp.Data/FoxPro/Enums.prg b/src/Runtime/XSharp.Data/FoxPro/Enums.prg
index d797a7995b..a678899c5e 100644
--- a/src/Runtime/XSharp.Data/FoxPro/Enums.prg
+++ b/src/Runtime/XSharp.Data/FoxPro/Enums.prg
@@ -43,4 +43,6 @@ ENUM XSharp.VFPErrors
MEMBER VFP_INVALID_PARAMETER
MEMBER VFP_FOR_CONDITION_MUST_BE_LOGIC
MEMBER VFP_INVALID_DATE_SETTING
+ MEMBER VFP_XML_PARSE_ERROR
+ MEMBER VFP_XML_NOSCHEMA
END ENUM
diff --git a/src/Runtime/XSharp.RT.Debugger/Xsharp.RT.Debugger.Next.xsproj b/src/Runtime/XSharp.RT.Debugger/Xsharp.RT.Debugger.Next.xsproj
index 0faf852603..50c22ab7dd 100644
--- a/src/Runtime/XSharp.RT.Debugger/Xsharp.RT.Debugger.Next.xsproj
+++ b/src/Runtime/XSharp.RT.Debugger/Xsharp.RT.Debugger.Next.xsproj
@@ -1,7 +1,7 @@
- XSharp
+ XSharp.Debugger
XSharp.RT.Debugger
true
Core
diff --git a/src/Runtime/XSharp.RT/XSharp.RT.Next.xsproj b/src/Runtime/XSharp.RT/XSharp.RT.Next.xsproj
index 765a533400..a28499a198 100644
--- a/src/Runtime/XSharp.RT/XSharp.RT.Next.xsproj
+++ b/src/Runtime/XSharp.RT/XSharp.RT.Next.xsproj
@@ -2,7 +2,7 @@
Library
- XSharp
+ XSharp.RT
XSharp.RT
VO
True
diff --git a/src/Runtime/XSharp.VFP.Tests/OOPTests.prg b/src/Runtime/XSharp.VFP.Tests/OOPTests.prg
new file mode 100644
index 0000000000..ae31a131d3
--- /dev/null
+++ b/src/Runtime/XSharp.VFP.Tests/OOPTests.prg
@@ -0,0 +1,39 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+
+using System
+using XUnit
+
+begin namespace XSharp.VFP.Tests
+ class OOPTests
+ static constructor
+ XSharp.RuntimeState.Dialect := XSharpDialect.FoxPro
+ end constructor
+
+ [Fact, Trait("Category", "OOP")];
+ method NumericPropertyIsUsableInArithmetic as void
+ local oCircle := Circle{} as Circle
+ oCircle:Ratio := 5
+ Assert.Equal(31.4, (real8)oCircle:Perimeter(), 2)
+ end method
+
+ [Fact, Trait("Category", "OOP")];
+ method NumericPropertyRoundTripsThroughLateBoundAccess as void
+ local oCircle := Circle{} as Usual
+ oCircle:Ratio := 5
+ Assert.Equal(10, (int)(oCircle:Ratio * 2))
+ end method
+ end class
+
+ define class Circle as Custom
+ ratio = 0
+
+ procedure Perimeter()
+ return 2 * 3.14 * this.ratio
+ endproc
+ enddefine
+end namespace
diff --git a/src/Runtime/XSharp.VFP.Tests/XSharp.VFP.Tests.xsproj b/src/Runtime/XSharp.VFP.Tests/XSharp.VFP.Tests.xsproj
index 37948e305d..55f3f9c0ea 100644
--- a/src/Runtime/XSharp.VFP.Tests/XSharp.VFP.Tests.xsproj
+++ b/src/Runtime/XSharp.VFP.Tests/XSharp.VFP.Tests.xsproj
@@ -104,6 +104,7 @@
+
@@ -122,6 +123,7 @@
+
diff --git a/src/Runtime/XSharp.VFP.Tests/XmlToCursorTests.prg b/src/Runtime/XSharp.VFP.Tests/XmlToCursorTests.prg
new file mode 100644
index 0000000000..ab36183d1a
--- /dev/null
+++ b/src/Runtime/XSharp.VFP.Tests/XmlToCursorTests.prg
@@ -0,0 +1,554 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+USING System
+USING System.IO
+USING XUnit
+
+BEGIN NAMESPACE XSharp.VFP.Tests
+
+ CLASS XmlToCursorTests
+
+ STATIC CONSTRUCTOR
+ XSharp.RuntimeState.Dialect := XSharpDialect.FoxPro
+ END CONSTRUCTOR
+
+ #region helpers
+ PRIVATE METHOD Stru(cAlias AS STRING) AS STRING
+ LOCAL aStru AS ARRAY
+ LOCAL cResult AS STRING
+ LOCAL n AS DWORD
+ DbSelectArea(cAlias)
+ aStru := DbStruct()
+ cResult := ""
+ FOR n := 1 UPTO ALen(aStru)
+ cResult += Upper(AllTrim(aStru[n, 1])) + " " + Upper(aStru[n, 2]) + ;
+ "(" + AllTrim(Str(aStru[n, 3])) + "," + AllTrim(Str(aStru[n, 4])) + ") "
+ NEXT
+ RETURN AllTrim(cResult)
+ END METHOD
+
+ PRIVATE METHOD Close(cAlias AS STRING) AS VOID
+ IF Used(cAlias)
+ DbCloseArea(cAlias)
+ ENDIF
+ END METHOD
+
+ PRIVATE METHOD Doc(cRows AS STRING) AS STRING
+ RETURN "" + cRows + ""
+ END METHOD
+
+ PRIVATE METHOD OneCol(cValues AS STRING) AS STRING
+ LOCAL cResult AS STRING
+ cResult := ""
+ FOREACH cValue AS STRING IN cValues:Split(c'|')
+ cResult += "" + cValue + ""
+ NEXT
+ RETURN SELF:Doc(cResult)
+ END METHOD
+
+ PRIVATE METHOD Infer(cValues AS STRING) AS STRING
+ LOCAL cAlias AS STRING
+ LOCAL cResult AS STRING
+ cAlias := "inf" + Right(Guid.NewGuid():ToString("N"), 6)
+ XmlToCursor(SELF:OneCol(cValues), cAlias, 0)
+ cResult := SELF:Stru(cAlias)
+ SELF:Close(cAlias)
+ RETURN cResult
+ END METHOD
+
+ // The XML VFP 9 itself produces for a cursor holding one row of each
+ // interesting type plus one empty row. It is embedded verbatim rather than
+ // regenerated with CursorToXml(): our own writer emits every column as
+ // xs:string, so a round trip through it would prove nothing about the
+ // types XmlToCursor() is supposed to rebuild.
+ PRIVATE METHOD TypesXml(lSchema AS LOGIC) AS STRING
+ IF lSchema
+ RETURN SELF:VfpXmlWithSchema()
+ ENDIF
+ RETURN SELF:VfpXmlNoSchema()
+ END METHOD
+
+ // Captured verbatim from VFP 9: one row of each type plus one empty row, with the inline schema
+ PRIVATE METHOD VfpXmlWithSchema() AS STRING
+ LOCAL oSb AS System.Text.StringBuilder
+ oSb := System.Text.StringBuilder{}
+ oSb:AppendLine("")
+ oSb:AppendLine("")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" hello")
+ oSb:AppendLine(" 123.45")
+ oSb:AppendLine(" true")
+ oSb:AppendLine(" 2024-01-15")
+ oSb:AppendLine(" 2024-01-15T10:30:45")
+ oSb:AppendLine(" 99.9999")
+ oSb:AppendLine(" 42")
+ oSb:AppendLine(" memo text")
+ oSb:AppendLine(" 3.142")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.00")
+ oSb:AppendLine(" false")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.0000")
+ oSb:AppendLine(" 0")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.000")
+ oSb:AppendLine(" ")
+ oSb:AppendLine("")
+ RETURN oSb:ToString()
+ END METHOD
+
+ // The same cursor, written by VFP 9 without a schema
+ PRIVATE METHOD VfpXmlNoSchema() AS STRING
+ LOCAL oSb AS System.Text.StringBuilder
+ oSb := System.Text.StringBuilder{}
+ oSb:AppendLine("")
+ oSb:AppendLine("")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" hello")
+ oSb:AppendLine(" 123.45")
+ oSb:AppendLine(" true")
+ oSb:AppendLine(" 2024-01-15")
+ oSb:AppendLine(" 2024-01-15T10:30:45")
+ oSb:AppendLine(" 99.9999")
+ oSb:AppendLine(" 42")
+ oSb:AppendLine(" memo text")
+ oSb:AppendLine(" 3.142")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.00")
+ oSb:AppendLine(" false")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.0000")
+ oSb:AppendLine(" 0")
+ oSb:AppendLine(" ")
+ oSb:AppendLine(" 0.000")
+ oSb:AppendLine(" ")
+ oSb:AppendLine("")
+ RETURN oSb:ToString()
+ END METHOD
+ #endregion
+
+ #region round trip through the inline schema
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD SchemaRebuildsEveryFieldType AS VOID
+ VAR cXml := SELF:TypesXml(TRUE)
+ SELF:Close("dst")
+ Assert.Equal(2, (INT) XmlToCursor(cXml, "dst", 0))
+ Assert.Equal("CCHAR C(10,0) NNUM N(8,2) LLOG L(1,0) DDATE D(8,0) TTIME T(8,0) " + ;
+ "YCUR N(20,4) IINT I(4,0) MMEMO M(4,0) BDBL B(8,0)", SELF:Stru("dst"))
+ SELF:Close("dst")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD Flag2048RebuildsCurrency AS VOID
+ VAR cXml := SELF:TypesXml(TRUE)
+ SELF:Close("dst")
+ XmlToCursor(cXml, "dst", 2048)
+ DbSelectArea("dst")
+ Assert.Equal("Y", DbFieldInfo(DBS_TYPE, FieldPos("ycur")))
+ SELF:Close("dst")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD SchemaRoundTripKeepsValues AS VOID
+ VAR cXml := SELF:TypesXml(TRUE)
+ SELF:Close("dst")
+ XmlToCursor(cXml, "dst", 0)
+ DbSelectArea("dst")
+ DbGoTop()
+ Assert.Equal("hello", AllTrim(dst->cchar))
+ Assert.Equal(123.45, (REAL8) dst->nnum, 2)
+ Assert.True(dst->llog)
+ Assert.Equal(2024, (INT) Year(dst->ddate))
+ Assert.Equal(42, (INT) dst->iint)
+ Assert.Equal("memo text", AllTrim(dst->mmemo))
+ SELF:Close("dst")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD SchemaHandlesTheEmptyDateVfpWrites AS VOID
+ VAR cXml := SELF:TypesXml(TRUE)
+ SELF:Close("dst")
+ Assert.Equal(2, (INT) XmlToCursor(cXml, "dst", 0))
+ DbSelectArea("dst")
+ DbGoBottom()
+ Assert.True(Empty(dst->ddate))
+ SELF:Close("dst")
+ END METHOD
+
+ #endregion
+ #region inference when there is no schema
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD NoSchemaInfersFromTheValues AS VOID
+ VAR cXml := SELF:TypesXml(FALSE)
+ SELF:Close("dst")
+ Assert.Equal(2, (INT) XmlToCursor(cXml, "dst", 0))
+ Assert.Equal("CCHAR C(5,0) NNUM N(6,2) LLOG L(1,0) DDATE C(10,0) TTIME C(19,0) " + ;
+ "YCUR N(7,4) IINT N(2,0) MMEMO C(9,0) BDBL N(5,3)", SELF:Stru("dst"))
+ SELF:Close("dst")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD WidthIsTheLongestValueSeen AS VOID
+ Assert.Equal("V C(10,0)", SELF:Infer("abc|abcdefghij"))
+ Assert.Equal("V N(7,3)", SELF:Infer("1|333.456"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD BooleanValuesBecomeLogical AS VOID
+ Assert.Equal("V L(1,0)", SELF:Infer("true|false"))
+ Assert.Equal("V L(1,0)", SELF:Infer("TRUE|FALSE"))
+ Assert.Equal("V L(1,0)", SELF:Infer("1|0"))
+ Assert.Equal("V L(1,0)", SELF:Infer("1|true"))
+ Assert.Equal("V C(4,0)", SELF:Infer("true|xyz"))
+ Assert.Equal("V N(1,0)", SELF:Infer("1|2"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD DatesAndDateTimesAreInferred AS VOID
+ Assert.Equal("V D(8,0)", SELF:Infer("2024-01-15|2024-02-20"))
+ Assert.Equal("V T(8,0)", SELF:Infer("2024-01-15T10:30:45"))
+ Assert.Equal("V C(19,0)", SELF:Infer("2024-01-15|2024-01-15T10:30:45"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD LongTextBecomesMemo AS VOID
+ Assert.Equal("V C(254,0)", SELF:Infer(Replicate("y", 254)))
+ Assert.Equal("V M(4,0)", SELF:Infer(Replicate("z", 255)))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD EmptyColumnBecomesCharacterOfOne AS VOID
+ Assert.Equal("V C(1,0)", SELF:Infer("|"))
+ END METHOD
+
+ #endregion
+
+ #region inference quirks reproduced from VFP on purpose
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD SignedNumbersAreInferredAsCharacter AS VOID
+ Assert.Equal("V C(2,0)", SELF:Infer("-5"))
+ Assert.Equal("V C(2,0)", SELF:Infer("+5"))
+ Assert.Equal("V C(7,0)", SELF:Infer("-123.45"))
+ Assert.Equal("V C(4,0)", SELF:Infer("-5|1234"))
+ Assert.Equal("V N(4,0)", SELF:Infer("1234"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD ExponentsAreInferredAsCharacter AS VOID
+ Assert.Equal("V C(3,0)", SELF:Infer("1e3|25"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD LeadingAndTrailingDotsAreStillNumeric AS VOID
+ Assert.Equal("V N(2,1)", SELF:Infer(".5|.7"))
+ Assert.Equal("V N(2,0)", SELF:Infer("5.|7."))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AnEmptyValueRulesOutLogicalButNotNumeric AS VOID
+ Assert.Equal("V N(1,0)", SELF:Infer("1|0|"))
+ Assert.Equal("V C(4,0)", SELF:Infer("true|"))
+ Assert.Equal("V N(4,1)", SELF:Infer("12.5|"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AnEmptyValueRulesOutDate AS VOID
+ Assert.Equal("V C(10,0)", SELF:Infer("2024-01-15|"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD InferredWidthCanOverflow AS VOID
+ Assert.Equal("V N(5,1)", SELF:Infer("99999|0.1"))
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD LeadingZerosAreLost AS VOID
+ Assert.Equal("V N(3,0)", SELF:Infer("007|12"))
+ Assert.Equal("V C(3,0)", SELF:Infer("007|ab"))
+ END METHOD
+
+ #endregion
+ #region cursor name, work area and return value
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD MissingNameCreatesXmlResult AS VOID
+ SELF:Close("XMLRESULT")
+ Assert.Equal(1, (INT) XmlToCursor(SELF:Doc("1")))
+ Assert.True(Used("XMLRESULT"))
+ Assert.Equal("XMLRESULT", Alias())
+ SELF:Close("XMLRESULT")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD EmptyNameCreatesXmlResult AS VOID
+ SELF:Close("XMLRESULT")
+ XmlToCursor(SELF:Doc("1"), "", 0)
+ Assert.True(Used("XMLRESULT"))
+ SELF:Close("XMLRESULT")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD TheNewCursorBecomesTheCurrentArea AS VOID
+ SELF:Close("parked")
+ SELF:Close("landed")
+ CREATE CURSOR parked (zz C(3))
+ DbSelectArea("parked")
+ XmlToCursor(SELF:Doc("1"), "landed", 0)
+ Assert.Equal("LANDED", Alias())
+ Assert.Equal(1, (INT) RecNo())
+ SELF:Close("landed")
+ SELF:Close("parked")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AnExistingCursorIsReplaced AS VOID
+ SELF:Close("reuse")
+ CREATE CURSOR reuse (zz C(3))
+ INSERT INTO reuse VALUES ("aaa")
+ XmlToCursor(SELF:Doc("1"), "reuse", 0)
+ // "1" on its own is Logical, not Numeric: it is inside {0,1,true,false}
+ Assert.Equal("A L(1,0)", SELF:Stru("reuse"))
+ SELF:Close("reuse")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD EmptyDocumentCreatesNoCursor AS VOID
+ SELF:Close("nothing")
+ SELF:Close("norecords")
+ // the return is RecCount() of the current area, so it has to be a known one
+ CREATE CURSOR norecords (zz C(1))
+ DbSelectArea("norecords")
+ Assert.Equal(0, (INT) XmlToCursor("", "nothing", 0))
+ Assert.False(Used("nothing"))
+ SELF:Close("norecords")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD ReturnIsRecCountOfTheCurrentArea AS VOID
+ SELF:Close("target")
+ SELF:Close("parked")
+ CREATE CURSOR target (cchar C(20))
+ INSERT INTO target VALUES ("pre1")
+ INSERT INTO target VALUES ("pre2")
+ CREATE CURSOR parked (zz C(3))
+ DbSelectArea("parked")
+ Assert.Equal(0, (INT) XmlToCursor(SELF:Doc("ab"), "target", 8192))
+ Assert.Equal("PARKED", Alias())
+ Assert.Equal(4, (INT) RecCount("target"))
+ SELF:Close("target")
+ SELF:Close("parked")
+ END METHOD
+
+ #endregion
+
+ #region append mode
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendAddsRowsAndKeepsTheStructure AS VOID
+ SELF:Close("tgt")
+ CREATE CURSOR tgt (cchar C(20), nnum N(10,2), zzz C(5))
+ INSERT INTO tgt VALUES ("preexisting", 9.99, "keep")
+ DbSelectArea("tgt")
+ Assert.Equal(3, (INT) XmlToCursor(SELF:TypesXml(TRUE), "tgt", 8192))
+ Assert.Equal("CCHAR C(20,0) NNUM N(10,2) ZZZ C(5,0)", SELF:Stru("tgt"))
+ DbSelectArea("tgt")
+ DbGoTop()
+ Assert.Equal("preexisting", AllTrim(tgt->cchar))
+ DbSkip(1)
+ Assert.Equal("hello", AllTrim(tgt->cchar))
+ Assert.Equal(123.45, (REAL8) tgt->nnum, 2)
+ Assert.Equal("", AllTrim(tgt->zzz))
+ SELF:Close("tgt")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendMatchesFieldNamesCaseInsensitively AS VOID
+ SELF:Close("tgt6")
+ CREATE CURSOR tgt6 (myfield C(5))
+ DbSelectArea("tgt6")
+ XmlToCursor(SELF:Doc("up"), "tgt6", 8192)
+ DbSelectArea("tgt6")
+ DbGoTop()
+ Assert.Equal("up", AllTrim(tgt6->myfield))
+ SELF:Close("tgt6")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendWithNoMatchingColumnStillAddsBlankRows AS VOID
+ SELF:Close("tgt2")
+ CREATE CURSOR tgt2 (qqq C(5))
+ DbSelectArea("tgt2")
+ Assert.Equal(2, (INT) XmlToCursor(SELF:Doc("12"), "tgt2", 8192))
+ Assert.Equal(2, (INT) RecCount("tgt2"))
+ SELF:Close("tgt2")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendCoercesToTheTargetFieldType AS VOID
+ SELF:Close("tgt4")
+ CREATE CURSOR tgt4 (ddate D, llog L, nnum N(6,2), cchar C(4))
+ DbSelectArea("tgt4")
+ XmlToCursor(SELF:Doc("2024-01-15true" + ;
+ "12.3456abcdefgh"), "tgt4", 8192)
+ DbSelectArea("tgt4")
+ DbGoTop()
+ Assert.Equal(2024, (INT) Year(tgt4->ddate))
+ Assert.Equal(1, (INT) Month(tgt4->ddate))
+ Assert.True(tgt4->llog)
+ Assert.Equal(12.35, (REAL8) tgt4->nnum, 2) // rounded to the field scale
+ Assert.Equal("abcd", tgt4->cchar) // truncated to the field width
+ SELF:Close("tgt4")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendWithEmptyNameUsesTheCurrentArea AS VOID
+ SELF:Close("cur3")
+ CREATE CURSOR cur3 (cchar C(20))
+ DbSelectArea("cur3")
+ Assert.Equal(2, (INT) XmlToCursor(SELF:Doc("ab"), "", 8192))
+ Assert.Equal(2, (INT) RecCount("cur3"))
+ SELF:Close("cur3")
+ END METHOD
+
+ #endregion
+
+ #region source flavours
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD Flag512ReadsFromAFile AS VOID
+ VAR cFile := Path.Combine(Path.GetTempPath(), "X2CIN_" + Guid.NewGuid():ToString("N") + ".xml")
+ File.WriteAllText(cFile, SELF:TypesXml(TRUE))
+ SELF:Close("fromfile")
+ Assert.Equal(2, (INT) XmlToCursor(cFile, "fromfile", 512))
+ SELF:Close("fromfile")
+ File.Delete(cFile)
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AttributeFormatIsRead AS VOID
+ SELF:Close("attr")
+ Assert.Equal(1, (INT) XmlToCursor(SELF:Doc(""), "attr", 0))
+ Assert.Equal("CC C(2,0) NN N(3,1)", SELF:Stru("attr"))
+ SELF:Close("attr")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AttributesAreDroppedWhenTheRowAlsoHasElements AS VOID
+ SELF:Close("mix")
+ XmlToCursor(SELF:Doc("x"), "mix", 0)
+ Assert.Equal("B C(1,0)", SELF:Stru("mix"))
+ SELF:Close("mix")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD Flag65536CreatesVarChar AS VOID
+ SELF:Close("vc")
+ XmlToCursor(SELF:TypesXml(TRUE), "vc", 65536)
+ DbSelectArea("vc")
+ Assert.Equal("V", DbFieldInfo(DBS_TYPE, FieldPos("cchar")))
+ SELF:Close("vc")
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD EntitiesAndAccentsSurvive AS VOID
+ SELF:Close("ent")
+ XmlToCursor(SELF:Doc("a&bniño"), "ent", 0)
+ DbSelectArea("ent")
+ DbGoTop()
+ Assert.Equal("a&b", AllTrim(ent->a))
+ Assert.Equal("ni" + Chr(241) + "o", AllTrim(ent->b))
+ SELF:Close("ent")
+ END METHOD
+
+ #endregion
+
+ #region errors
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD InvalidXmlThrows AS VOID
+ Assert.ThrowsAny({ => XmlToCursor("this is not xml", "bad1", 0) })
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD MissingFileThrows AS VOID
+ VAR cFile := Path.Combine(Path.GetTempPath(), "X2CMISSING_" + Guid.NewGuid():ToString("N") + ".xml")
+ Assert.ThrowsAny({ => XmlToCursor(cFile, "bad2", 512) })
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD DocumentWithoutARowLevelThrows AS VOID
+ Assert.ThrowsAny({ => XmlToCursor("12", "bad3", 0) })
+ END METHOD
+
+ [Fact, Trait("Category", "XmlToCursor")];
+ METHOD AppendToAnUnknownAliasThrows AS VOID
+ Assert.ThrowsAny({ => XmlToCursor(SELF:Doc("1"), "nosuchalias", 8192) })
+ END METHOD
+
+ #endregion
+
+ END CLASS
+
+END NAMESPACE
diff --git a/src/Runtime/XSharp.VFP.UI/VfpUIProvider.prg b/src/Runtime/XSharp.VFP.UI/VfpUIProvider.prg
index 8b92ab9972..16dfdb88a0 100644
--- a/src/Runtime/XSharp.VFP.UI/VfpUIProvider.prg
+++ b/src/Runtime/XSharp.VFP.UI/VfpUIProvider.prg
@@ -54,46 +54,40 @@ BEGIN NAMESPACE XSharp.VFP.UI
PUBLIC METHOD SysMetric(nScreenElement AS LONG) AS LONG
SWITCH nScreenElement
- CASE 1 // SYSMETRIC_SCREENWIDTH
- LOCAL hDC := VfpWin32UI.GetDC(IntPtr.Zero) AS IntPtr
- LOCAL nRes := VfpWin32UI.GetDeviceCaps(hDC, VfpWin32UI.DESKTOP_HORZRES) AS LONG
- VfpWin32UI.ReleaseDC(IntPtr.Zero, hDC)
- RETURN nRes
- CASE 2 // SYSMETRIC_SCREENHEIGHT
- LOCAL hDC := VfpWin32UI.GetDC(IntPtr.Zero) AS IntPtr
- LOCAL nRes := VfpWin32UI.GetDeviceCaps(hDC, VfpWin32UI.DESKTOP_VERTRES) AS LONG
- VfpWin32UI.ReleaseDC(IntPtr.Zero, hDC)
- RETURN nRes
- CASE 3; RETURN SystemInformation.MinimizedWindowSpacingSize.Width
- CASE 4; RETURN SystemInformation.MinimizedWindowSpacingSize.Height
- CASE 5; RETURN SystemInformation.VerticalScrollBarWidth
- CASE 6; RETURN SystemInformation.VerticalScrollBarArrowHeight
- CASE 7; RETURN SystemInformation.HorizontalScrollBarArrowWidth
- CASE 8; RETURN SystemInformation.HorizontalScrollBarHeight
- CASE 9; RETURN SystemInformation.CaptionHeight
- CASE 10; RETURN SystemInformation.FixedFrameBorderSize.Width
- CASE 11; RETURN SystemInformation.FixedFrameBorderSize.Height
- CASE 12; RETURN SystemInformation.FrameBorderSize.Width
- CASE 13; RETURN SystemInformation.FrameBorderSize.Height
- CASE 14; RETURN SystemInformation.HorizontalScrollBarThumbWidth
- CASE 15; RETURN SystemInformation.VerticalScrollBarWidth
- CASE 16; RETURN SystemInformation.IconSize.Width
- CASE 17; RETURN SystemInformation.IconSize.Height
- CASE 18; RETURN 0
- CASE 19; RETURN 0
- CASE 20; RETURN SystemInformation.MenuHeight
- CASE 21; RETURN SystemInformation.MaxWindowTrackSize.Width
- CASE 22; RETURN SystemInformation.MaxWindowTrackSize.Height
- CASE 23; RETURN SystemInformation.KanjiWindowHeight
- CASE 24; RETURN SystemInformation.MinWindowTrackSize.Width
- CASE 25; RETURN SystemInformation.MinWindowTrackSize.Height
- CASE 26; RETURN SystemInformation.MinimumWindowSize.Width
- CASE 27; RETURN SystemInformation.MinimumWindowSize.Height
- CASE 30; RETURN iif(SystemInformation.MousePresent == TRUE, 1, 0)
- CASE 31; RETURN iif(SystemInformation.DebugOS == TRUE , 1, 0)
- CASE 32; RETURN iif(SystemInformation.MouseButtonsSwapped == TRUE, 1, 0)
- CASE 33; RETURN SystemInformation.ToolWindowCaptionButtonSize.Width
- CASE 34; RETURN SystemInformation.ToolWindowCaptionHeight
+ CASE 1; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXSCREEN)
+ CASE 2; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYSCREEN)
+ CASE 3; RETURN SystemInformation.MinimizedWindowSpacingSize.Width
+ CASE 4; RETURN SystemInformation.MinimizedWindowSpacingSize.Height
+ CASE 5; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXVSCROLL)
+ CASE 6; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYVSCROLL)
+ CASE 7; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXHSCROLL)
+ CASE 8; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYHSCROLL)
+ CASE 9; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYCAPTION)
+ CASE 10; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXBORDER)
+ CASE 11; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYBORDER)
+ CASE 12; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXDLGFRAME)
+ CASE 13; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYDLGFRAME)
+ CASE 14; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXHTHUMB)
+ CASE 15; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYVTHUMB)
+ CASE 16; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXICON)
+ CASE 17; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYICON)
+ CASE 18; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXCURSOR)
+ CASE 19; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYCURSOR)
+ CASE 20; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYMENU)
+ CASE 21; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXFULLSCREEN)
+ CASE 22; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYFULLSCREEN)
+ CASE 23; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYKANJIWINDOW)
+ CASE 24; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXMINTRACK)
+ CASE 25; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYMINTRACK)
+ CASE 26; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXMIN)
+ CASE 27; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYMIN)
+ CASE 28; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXSIZE)
+ CASE 29; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYSIZE)
+ CASE 30; RETURN IIF(VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_MOUSEPRESENT) != 0, 1, 0)
+ CASE 31; RETURN IIF(VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_DEBUG) != 0, 1, 0)
+ CASE 32; RETURN IIF(VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_SWAPBUTTON) != 0, 1, 0)
+ CASE 33; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CXSMSIZE)
+ CASE 34; RETURN VfpWin32UI.GetSystemMetrics(VfpWin32UI.SM_CYSMSIZE)
END SWITCH
RETURN 0
END METHOD
diff --git a/src/Runtime/XSharp.VFP.UI/Win32UI.prg b/src/Runtime/XSharp.VFP.UI/Win32UI.prg
index 6a996afc49..239f7fd3f5 100644
--- a/src/Runtime/XSharp.VFP.UI/Win32UI.prg
+++ b/src/Runtime/XSharp.VFP.UI/Win32UI.prg
@@ -14,6 +14,41 @@ BEGIN NAMESPACE XSharp.VFP.UI
PUBLIC CONST DESKTOP_HORZRES := 117 AS INT
PUBLIC CONST DESKTOP_VERTRES := 118 AS INT
PUBLIC CONST PD_PRINTSETUP := 0x40U AS DWORD
+
+ // SYSMETRIC(): the VFP help points at GetSystemMetrics()
+ PUBLIC CONST SM_CXSCREEN := 0 AS INT
+ PUBLIC CONST SM_CYSCREEN := 1 AS INT
+ PUBLIC CONST SM_CXVSCROLL := 2 AS INT
+ PUBLIC CONST SM_CYHSCROLL := 3 AS INT
+ PUBLIC CONST SM_CYCAPTION := 4 AS INT
+ PUBLIC CONST SM_CXBORDER := 5 AS INT
+ PUBLIC CONST SM_CYBORDER := 6 AS INT
+ PUBLIC CONST SM_CXDLGFRAME := 7 AS INT
+ PUBLIC CONST SM_CYDLGFRAME := 8 AS INT
+ PUBLIC CONST SM_CYVTHUMB := 9 AS INT
+ PUBLIC CONST SM_CXHTHUMB := 10 AS INT
+ PUBLIC CONST SM_CXICON := 11 AS INT
+ PUBLIC CONST SM_CYICON := 12 AS INT
+ PUBLIC CONST SM_CXCURSOR := 13 AS INT
+ PUBLIC CONST SM_CYCURSOR := 14 AS INT
+ PUBLIC CONST SM_CYMENU := 15 AS INT
+ PUBLIC CONST SM_CXFULLSCREEN := 16 AS INT
+ PUBLIC CONST SM_CYFULLSCREEN := 17 AS INT
+ PUBLIC CONST SM_CYKANJIWINDOW := 18 AS INT
+ PUBLIC CONST SM_MOUSEPRESENT := 19 AS INT
+ PUBLIC CONST SM_CYVSCROLL := 20 AS INT
+ PUBLIC CONST SM_CXHSCROLL := 21 AS INT
+ PUBLIC CONST SM_DEBUG := 22 AS INT
+ PUBLIC CONST SM_SWAPBUTTON := 23 AS INT
+ PUBLIC CONST SM_CXMIN := 28 AS INT
+ PUBLIC CONST SM_CYMIN := 29 AS INT
+ PUBLIC CONST SM_CXSIZE := 30 AS INT
+ PUBLIC CONST SM_CYSIZE := 31 AS INT
+ PUBLIC CONST SM_CXMINTRACK := 34 AS INT
+ PUBLIC CONST SM_CYMINTRACK := 35 AS INT
+ PUBLIC CONST SM_CXSMSIZE := 52 AS INT
+ PUBLIC CONST SM_CYSMSIZE := 53 AS INT
+
[StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)];
PUBLIC STRUCT PRINTDLGW
PUBLIC lStructSize AS DWORD
@@ -84,6 +119,9 @@ BEGIN NAMESPACE XSharp.VFP.UI
[DllImport("gdi32.dll", CharSet := CharSet.Auto, SetLastError := TRUE, ExactSpelling := TRUE)];
STATIC EXTERN METHOD GetDeviceCaps(hDC AS IntPtr, nIndex AS INT) AS INT
+ [DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := TRUE, ExactSpelling := TRUE)];
+ STATIC EXTERN METHOD GetSystemMetrics(nIndex AS INT) AS INT
+
[DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := TRUE, ExactSpelling := TRUE)];
STATIC EXTERN METHOD GetDC(hWnd AS IntPtr) AS IntPtr
diff --git a/src/Runtime/XSharp.VFP/Cursors/Xml.prg b/src/Runtime/XSharp.VFP/Cursors/Xml.prg
index dd79c6006f..1518894f5a 100644
--- a/src/Runtime/XSharp.VFP/Cursors/Xml.prg
+++ b/src/Runtime/XSharp.VFP/Cursors/Xml.prg
@@ -8,16 +8,13 @@ using System
using System.IO
using System.Data
using System.Text
+using System.Collections.Generic
using System.Xml
using XSharp.RDD
+using XSharp.RDD.Enums
using XSharp.RDD.Support
using System.Globalization
-// nFlags values for CursorToXml(). Only the ones implemented are listed here:
-DEFINE C2X_FLAG_CONTINUOUS := 1 // Produce unformatted XML as one continuous string
-DEFINE C2X_FLAG_TOFILE := 512 // Send the output to the file name in cOutput
-
-
///
[FoxProFunction("CURSORTOXML", FoxFunctionCategory.General, FoxEngine.RuntimeCore, FoxFunctionStatus.Partial, FoxCriticality.Medium)];
FUNCTION CursorToXML (uArea, cOutput, nOutputFormat, nFlags, nRecords, cSchemaName, cSchemaLocation, cNameSpace ) AS USUAL CLIPPER
@@ -56,12 +53,12 @@ FUNCTION CursorToXML (uArea, cOutput, nOutputFormat, nFlags, nRecords, cSchemaNa
var nOldArea := RuntimeState.CurrentWorkarea
RuntimeState.CurrentWorkarea := nArea
try
- cXml := __FoxCursorToXmlString(nRecs, cSchema, cNs, nFlagsInt)
+ cXml := FoxXml.WriteToString(nRecs, cSchema, cNs, nFlagsInt)
finally
RuntimeState.CurrentWorkarea := nOldArea
end try
- if _AND(nFlagsInt, C2X_FLAG_TOFILE) != 0
+ if _AND(nFlagsInt, FoxXml.C2X_FLAG_TOFILE) != 0
File.WriteAllText(cTarget, cXml, UTF8Encoding{FALSE})
return (long) FileInfo{cTarget}:Length
endif
@@ -70,153 +67,702 @@ FUNCTION CursorToXML (uArea, cOutput, nOutputFormat, nFlags, nRecords, cSchemaNa
XSharp.MemVar.Put(cTarget, cXml)
RETURN Encoding.UTF8:GetByteCount(cXml)
-/// Builds the XML for the cursor in the current work area.
-INTERNAL FUNCTION __FoxCursorToXmlString(nRecs as long, cSchema as string, cNs as string, nFlags as long) as string
- local nMode as XmlWriteMode
- var oData := __FoxCursorDataTable(nRecs)
+///
+[FoxProFunction("XMLTOCURSOR", FoxFunctionCategory.General, FoxEngine.RuntimeCore, FoxFunctionStatus.Partial, FoxCriticality.High)];
+FUNCTION XmlToCursor( eExpression, cCursorName, nFlags ) AS USUAL CLIPPER
+ local cAlias as string
+ local nFlagsInt as long
- // VFP always names the root element VFPData, whatever the output format is
- var oSet := DataSet{"VFPData"}
- if !String.IsNullOrEmpty(cNs)
- oSet:Namespace := cNs
+ if !IsString(eExpression)
+ throw Error.VoDbError(EG_ARG, EDB_PARAM, __FUNCTION__, nameof(eExpression), 1,