include/rtl/textenc.h                                    |    1 
 offapi/UnoApi_offapi.mk                                  |    1 
 offapi/com/sun/star/sheet/SensitivityReport.idl          |   55 ++
 officecfg/registry/schema/org/openoffice/Office/Calc.xcs |    7 
 sc/inc/globstr.hrc                                       |   14 
 sc/inc/miscuno.hxx                                       |    4 
 sc/inc/scabstdlg.hxx                                     |    3 
 sc/inc/strings.hrc                                       |    2 
 sc/qa/extras/scsolverobj.cxx                             |   28 -
 sc/qa/uitest/csv_dialog/tdf117868.py                     |   15 
 sc/qa/uitest/textToColumns/tdf143008.py                  |    3 
 sc/qa/uitest/textToColumns/tdf51700.py                   |    3 
 sc/qa/uitest/textToColumns/tdf69981.py                   |    3 
 sc/qa/uitest/textToColumns/tdf73006.py                   |    3 
 sc/qa/uitest/textToColumns/tdf82398.py                   |    3 
 sc/qa/uitest/textToColumns/tdf85979.py                   |    3 
 sc/qa/uitest/textToColumns/tdf89907.py                   |    3 
 sc/qa/uitest/textToColumns/tdf92423.py                   |    3 
 sc/qa/uitest/textToColumns/textToColumns.py              |   18 
 sc/source/ui/attrdlg/scdlgfact.cxx                       |    5 
 sc/source/ui/attrdlg/scdlgfact.hxx                       |    3 
 sc/source/ui/dbgui/asciiopt.cxx                          |   31 +
 sc/source/ui/dbgui/csvtablebox.cxx                       |   36 -
 sc/source/ui/dbgui/scuiasciiopt.cxx                      |  338 +++++++--------
 sc/source/ui/docshell/docsh.cxx                          |    2 
 sc/source/ui/inc/asciiopt.hxx                            |    2 
 sc/source/ui/inc/csvtablebox.hxx                         |    2 
 sc/source/ui/inc/optsolver.hxx                           |    4 
 sc/source/ui/inc/scuiasciiopt.hxx                        |   14 
 sc/source/ui/miscdlgs/optsolver.cxx                      |  152 ++++++
 sc/source/ui/unoobj/filtuno.cxx                          |   12 
 sc/source/ui/view/cellsh2.cxx                            |    1 
 sc/uiconfig/scalc/ui/textimportcsv.ui                    |   45 +
 sccomp/inc/strings.hrc                                   |    1 
 sccomp/source/solver/LpsolveSolver.cxx                   |  118 +++++
 sccomp/source/solver/SolverComponent.cxx                 |   24 -
 sccomp/source/solver/SolverComponent.hxx                 |   14 
 sfx2/source/doc/objstor.cxx                              |   25 -
 38 files changed, 739 insertions(+), 262 deletions(-)

New commits:
commit 565b619d57a3b98b0826c4b49dee6606f9ae70e0
Author:     Gabriel Masei <[email protected]>
AuthorDate: Tue Apr 9 13:07:18 2024 +0300
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Oct 9 14:49:11 2024 +0200

    tdf#160582 Preserve settings saving in csv import dialog
    
    Also, improve detection algorithm by replacing the limit
    of 20 lines with a time limit of 500ms.
    
    Change-Id: Iac519b6ebe675b91ce84b900646d9d320ea9ddc1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165905
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Jenkins

diff --git a/include/rtl/textenc.h b/include/rtl/textenc.h
index af4a16e5c422..10d69e734a4f 100644
--- a/include/rtl/textenc.h
+++ b/include/rtl/textenc.h
@@ -141,6 +141,7 @@ typedef sal_uInt16 rtl_TextEncoding;
  */
 
 #define RTL_TEXTENCODING_USER_START             (RTL_TEXTENC_CAST( 0x8000 ))
+#define RTL_TEXTENCODING_USER_DETECTED          (RTL_TEXTENCODING_USER_START + 
0)
 #define RTL_TEXTENCODING_USER_END               (RTL_TEXTENC_CAST( 0xEFFF ))
 
 #define RTL_TEXTENCODING_UCS4                   (RTL_TEXTENC_CAST( 0xFFFE ))
diff --git a/officecfg/registry/schema/org/openoffice/Office/Calc.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
index cb557431362b..770b31e7a5fd 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Calc.xcs
@@ -1012,6 +1012,13 @@
           </info>
           <value>false</value>
         </prop>
+        <prop oor:name="SeparatorType" oor:type="xs:short" 
oor:nillable="false">
+          <info>
+            <desc>Fixed width, separator or detected separator</desc>
+            <label>SeparatorType</label>
+          </info>
+          <value>2</value>
+        </prop>
         <prop oor:name="QuotedFieldAsText" oor:type="xs:boolean" 
oor:nillable="false">
           <info>
             <desc>If true, quoted field is always imported as text with no 
exception.</desc>
diff --git a/sc/inc/miscuno.hxx b/sc/inc/miscuno.hxx
index 6ac2a925090b..71cd9390de49 100644
--- a/sc/inc/miscuno.hxx
+++ b/sc/inc/miscuno.hxx
@@ -152,8 +152,8 @@ public:
         const css::uno::Reference<css::beans::XPropertySet>& xProp,
         const OUString& rName, const OUString& rDefault );
 
-    SC_DLLPUBLIC static bool GetBoolFromAny( const css::uno::Any& aAny );
-    static sal_Int16        GetInt16FromAny( const css::uno::Any& aAny );
+    SC_DLLPUBLIC static bool        GetBoolFromAny( const css::uno::Any& aAny 
);
+    SC_DLLPUBLIC static sal_Int16   GetInt16FromAny( const css::uno::Any& aAny 
);
     static sal_Int32        GetInt32FromAny( const css::uno::Any& aAny );
     static sal_Int32        GetEnumFromAny( const css::uno::Any& aAny );
 
diff --git a/sc/inc/scabstdlg.hxx b/sc/inc/scabstdlg.hxx
index 158501225a58..957c9605a116 100644
--- a/sc/inc/scabstdlg.hxx
+++ b/sc/inc/scabstdlg.hxx
@@ -419,8 +419,7 @@ public:
     virtual     VclPtr<AbstractScImportAsciiDlg> 
CreateScImportAsciiDlg(weld::Window* pParent,
                                                                     const 
OUString& aDatName,
                                                                     SvStream* 
pInStream,
-                                                                    
ScImportAsciiCall eCall,
-                                                                    
ScAsciiOptions* aOptions = nullptr) = 0;
+                                                                    
ScImportAsciiCall eCall) = 0;
 
     virtual     VclPtr<AbstractScTextImportOptionsDlg> 
CreateScTextImportOptionsDlg(weld::Window* pParent) = 0;
 
diff --git a/sc/inc/strings.hrc b/sc/inc/strings.hrc
index 3e23a800974e..faaa5e64998b 100644
--- a/sc/inc/strings.hrc
+++ b/sc/inc/strings.hrc
@@ -85,6 +85,8 @@
 #define SCSTR_FIELDSEP_SPACE                        
NC_("SCSTR_FIELDSEP_SPACE", "space")
 #define SCSTR_UNDO_GRAFFILTER                       
NC_("SCSTR_UNDO_GRAFFILTER", "Image Filter")
 #define STR_CAPTION_DEFAULT_TEXT                    
NC_("STR_CAPTION_DEFAULT_TEXT", "Text")
+#define SCSTR_DETECTED                              NC_("SCSTR_DETECTED", 
"Detected (%1)")
+#define SCSTR_AUTOMATIC                             NC_("SCSTR_AUTOMATIC", 
"Automatic")
 // Select tables dialog title
 #define STR_DLG_SELECTTABLES_TITLE                  
NC_("STR_DLG_SELECTTABLES_TITLE", "Select Sheets")
 #define STR_DLG_SELECTTABLE_TITLE                   
NC_("STR_DLG_SELECTTABLE_TITLE", "Go to Sheet")
diff --git a/sc/qa/uitest/csv_dialog/tdf117868.py 
b/sc/qa/uitest/csv_dialog/tdf117868.py
index d5306117e372..405a18f12acc 100644
--- a/sc/qa/uitest/csv_dialog/tdf117868.py
+++ b/sc/qa/uitest/csv_dialog/tdf117868.py
@@ -17,21 +17,12 @@ class Td117868(UITestCase):
 
         with load_csv_file(self, "tdf117868.csv", False) as xDialog:
             # Set text delimiter in case it's changed by another test
-            xSeparatedBy = xDialog.getChild("toseparatedby")
-            xSeparatedBy.executeAction("CLICK", tuple())
-
-            # Without the fix in place, this test would have failed with
-            # AssertionError: 'true' != 'false'
-            self.assertEqual('true', 
get_state_as_dict(xDialog.getChild("other"))['Selected'])
-            self.assertEqual('false', 
get_state_as_dict(xDialog.getChild("tab"))['Selected'])
-            self.assertEqual('false', 
get_state_as_dict(xDialog.getChild("comma"))['Selected'])
-            self.assertEqual('false', 
get_state_as_dict(xDialog.getChild("semicolon"))['Selected'])
+            self.assertEqual('Detected (|)', 
get_state_as_dict(xDialog.getChild("todetectseparator"))['Text'])
+            xDetected = xDialog.getChild("todetectseparator")
+            xDetected.executeAction("CLICK", tuple())
 
             self.assertEqual('1', 
get_state_as_dict(xDialog.getChild("fromrow"))['Text'])
 
-            xInputOther = xDialog.getChild("inputother")
-            self.assertEqual("|", get_state_as_dict(xInputOther)['Text'])
-
         document = self.ui_test.get_component()
 
         self.assertEqual("LETTER", get_cell_by_position(document, 0, 0, 
1).getString())
diff --git a/sc/qa/uitest/textToColumns/tdf143008.py 
b/sc/qa/uitest/textToColumns/tdf143008.py
index 087a7b5a8fd5..a14c671d26f6 100644
--- a/sc/qa/uitest/textToColumns/tdf143008.py
+++ b/sc/qa/uitest/textToColumns/tdf143008.py
@@ -22,6 +22,9 @@ class tdf143008(UITestCase):
             enter_text_to_cell(gridwin, "A1", "22/06/2021 10:02 PM")
 
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xGrid = xDialog.getChild("csvgrid")
                 xColumnType = xDialog.getChild("columntype")
 
diff --git a/sc/qa/uitest/textToColumns/tdf51700.py 
b/sc/qa/uitest/textToColumns/tdf51700.py
index 98c33d8f3104..b4fdf9b24462 100644
--- a/sc/qa/uitest/textToColumns/tdf51700.py
+++ b/sc/qa/uitest/textToColumns/tdf51700.py
@@ -28,6 +28,9 @@ class tdf51700(UITestCase):
             self.xUITest.executeCommand(".uno:SelectColumn")
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xcomma = xDialog.getChild("comma")
                 if (get_state_as_dict(xcomma)["Selected"]) == "false":
                     xcomma.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/textToColumns/tdf69981.py 
b/sc/qa/uitest/textToColumns/tdf69981.py
index 87c09e8ffcbf..16a1e9eb305e 100644
--- a/sc/qa/uitest/textToColumns/tdf69981.py
+++ b/sc/qa/uitest/textToColumns/tdf69981.py
@@ -23,6 +23,9 @@ class tdf69981(UITestCase):
             gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": 
"A2:A7"}))
             #Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xtab = xDialog.getChild("tab")
                 xcomma = xDialog.getChild("comma")
                 xtab.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/textToColumns/tdf73006.py 
b/sc/qa/uitest/textToColumns/tdf73006.py
index 1ac519f73c4b..0ffa5e69731e 100644
--- a/sc/qa/uitest/textToColumns/tdf73006.py
+++ b/sc/qa/uitest/textToColumns/tdf73006.py
@@ -26,6 +26,9 @@ class tdf73006(UITestCase):
             self.xUITest.executeCommand(".uno:SelectColumn")
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xspace = xDialog.getChild("space")
                 if (get_state_as_dict(xspace)["Selected"]) == "false":
                     xspace.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/textToColumns/tdf82398.py 
b/sc/qa/uitest/textToColumns/tdf82398.py
index b0722fcbd14c..2ddf0b60d654 100644
--- a/sc/qa/uitest/textToColumns/tdf82398.py
+++ b/sc/qa/uitest/textToColumns/tdf82398.py
@@ -29,6 +29,9 @@ class tdf82398(UITestCase):
             self.xUITest.executeCommand(".uno:NumberFormatDate")
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xother = xDialog.getChild("other")
                 xinputother = xDialog.getChild("inputother")
 
diff --git a/sc/qa/uitest/textToColumns/tdf85979.py 
b/sc/qa/uitest/textToColumns/tdf85979.py
index ca5808947e70..a3be674b0d69 100644
--- a/sc/qa/uitest/textToColumns/tdf85979.py
+++ b/sc/qa/uitest/textToColumns/tdf85979.py
@@ -23,6 +23,9 @@ class tdf85979(UITestCase):
             gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": 
"C1:C5"}))
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xspace = xDialog.getChild("space")
                 if (get_state_as_dict(xspace)["Selected"]) == "false":
                     xspace.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/textToColumns/tdf89907.py 
b/sc/qa/uitest/textToColumns/tdf89907.py
index ef5f164f9261..a7ea4c3c0207 100644
--- a/sc/qa/uitest/textToColumns/tdf89907.py
+++ b/sc/qa/uitest/textToColumns/tdf89907.py
@@ -37,6 +37,9 @@ class tdf89907(UITestCase):
 
                 # Data - Text to Columns
                 with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                    xSeparatedBy = xDialog.getChild("toseparatedby")
+                    xSeparatedBy.executeAction("CLICK", tuple())
+
                     xother = xDialog.getChild("other")
                     xinputother = xDialog.getChild("inputother")
                     if (get_state_as_dict(xother)["Selected"]) == "false":
diff --git a/sc/qa/uitest/textToColumns/tdf92423.py 
b/sc/qa/uitest/textToColumns/tdf92423.py
index 99486bb2f00e..ce9cadf3f348 100644
--- a/sc/qa/uitest/textToColumns/tdf92423.py
+++ b/sc/qa/uitest/textToColumns/tdf92423.py
@@ -39,6 +39,9 @@ class tdf92423(UITestCase):
             self.assertEqual(gridWinState["MarkedArea"], "Sheet1.A7:Sheet1.A9")
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xSemicolon = xDialog.getChild("semicolon")  #check semicolon 
checkbox
                 if (get_state_as_dict(xSemicolon)["Selected"]) == "false":
                     xSemicolon.executeAction("CLICK", tuple())
diff --git a/sc/qa/uitest/textToColumns/textToColumns.py 
b/sc/qa/uitest/textToColumns/textToColumns.py
index c67e879d1835..678a75b04c77 100644
--- a/sc/qa/uitest/textToColumns/textToColumns.py
+++ b/sc/qa/uitest/textToColumns/textToColumns.py
@@ -26,6 +26,9 @@ class CalcTextToColumns(UITestCase):
             #Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
                 #Untag Tab as separator and tag other. Put a dot into the 
input field next to the other checkbox
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xother = xDialog.getChild("other")
                 xinputother = xDialog.getChild("inputother")
 
@@ -85,6 +88,9 @@ class CalcTextToColumns(UITestCase):
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
                 # Untag Tab as separator and tag comma.
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xComma = xDialog.getChild("comma")
                 if (get_state_as_dict(xComma)["Selected"]) == "false":
                     xComma.executeAction("CLICK", tuple())
@@ -142,6 +148,9 @@ class CalcTextToColumns(UITestCase):
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
                 # Untag comma as separator and tag Semicolon
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xSemicolon = xDialog.getChild("semicolon")
                 if (get_state_as_dict(xSemicolon)["Selected"]) == "false":
                     xSemicolon.executeAction("CLICK", tuple())
@@ -199,6 +208,9 @@ class CalcTextToColumns(UITestCase):
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
                 # Untag comma as separator and tag Semicolon
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xSpace = xDialog.getChild("space")
                 if (get_state_as_dict(xSpace)["Selected"]) == "false":
                     xSpace.executeAction("CLICK", tuple())
@@ -257,6 +269,9 @@ class CalcTextToColumns(UITestCase):
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
                 # Untag comma as separator and tag Semicolon
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xother = xDialog.getChild("other")
                 xinputother = xDialog.getChild("inputother")
                 if (get_state_as_dict(xother)["Selected"]) == "false":
@@ -315,6 +330,9 @@ class CalcTextToColumns(UITestCase):
             gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": 
"A1:A5"}))
             # Data - Text to Columns
             with 
self.ui_test.execute_dialog_through_command(".uno:TextToColumns", 
close_button="") as xDialog:
+                xSeparatedBy = xDialog.getChild("toseparatedby")
+                xSeparatedBy.executeAction("CLICK", tuple())
+
                 xspace = xDialog.getChild("space")
                 xother = xDialog.getChild("other")
                 xinputother = xDialog.getChild("inputother")
diff --git a/sc/source/ui/attrdlg/scdlgfact.cxx 
b/sc/source/ui/attrdlg/scdlgfact.cxx
index 47e81dc7c09f..af95b1e025fa 100644
--- a/sc/source/ui/attrdlg/scdlgfact.cxx
+++ b/sc/source/ui/attrdlg/scdlgfact.cxx
@@ -1073,10 +1073,9 @@ bool 
AbstractScSelEntryDlg_Impl::StartExecuteAsync(VclAbstractDialog::AsyncConte
 // =========================Factories  for createdialog ===================
 VclPtr<AbstractScImportAsciiDlg> 
ScAbstractDialogFactory_Impl::CreateScImportAsciiDlg(weld::Window* pParent,
                                                     const OUString& aDatName,
-                                                    SvStream* pInStream, 
ScImportAsciiCall eCall,
-                                                    ScAsciiOptions* aOptions)
+                                                    SvStream* pInStream, 
ScImportAsciiCall eCall)
 {
-    return 
VclPtr<AbstractScImportAsciiDlg_Impl>::Create(std::make_shared<ScImportAsciiDlg>(pParent,
 aDatName,pInStream, eCall, aOptions));
+    return 
VclPtr<AbstractScImportAsciiDlg_Impl>::Create(std::make_shared<ScImportAsciiDlg>(pParent,
 aDatName,pInStream, eCall));
 }
 
 VclPtr<AbstractScTextImportOptionsDlg> 
ScAbstractDialogFactory_Impl::CreateScTextImportOptionsDlg(weld::Window* 
pParent)
diff --git a/sc/source/ui/attrdlg/scdlgfact.hxx 
b/sc/source/ui/attrdlg/scdlgfact.hxx
index 0f8077240a73..d7dab5e4c4b8 100644
--- a/sc/source/ui/attrdlg/scdlgfact.hxx
+++ b/sc/source/ui/attrdlg/scdlgfact.hxx
@@ -666,8 +666,7 @@ public:
     virtual VclPtr<AbstractScImportAsciiDlg> 
CreateScImportAsciiDlg(weld::Window* pParent,
                                                                     const 
OUString& aDatName,
                                                                     SvStream* 
pInStream,
-                                                                    
ScImportAsciiCall eCall,
-                                                                    
ScAsciiOptions* aOptions = nullptr) override;
+                                                                    
ScImportAsciiCall eCall) override;
 
     virtual VclPtr<AbstractScTextImportOptionsDlg> 
CreateScTextImportOptionsDlg(weld::Window* pParent) override;
 
diff --git a/sc/source/ui/dbgui/asciiopt.cxx b/sc/source/ui/dbgui/asciiopt.cxx
index c9a4d881baed..4c470793f9ac 100644
--- a/sc/source/ui/dbgui/asciiopt.cxx
+++ b/sc/source/ui/dbgui/asciiopt.cxx
@@ -22,9 +22,11 @@
 #include <comphelper/string.hxx>
 #include <osl/thread.h>
 #include <o3tl/string_view.hxx>
+#include <sfx2/objsh.hxx>
 
 constexpr std::u16string_view pStrFix = u"FIX";
 constexpr std::u16string_view pStrMrg = u"MRG";
+constexpr std::u16string_view pStrDet = u"DETECT";
 
 ScAsciiOptions::ScAsciiOptions() :
     bFixedLen       ( false ),
@@ -86,9 +88,10 @@ static OUString lcl_decodeSepString( std::u16string_view 
rSepNums, bool & o_bMer
 // The options string must not contain semicolons (because of the pick list),
 // use comma as separator.
 
-void ScAsciiOptions::ReadFromString( std::u16string_view rString )
+void ScAsciiOptions::ReadFromString( std::u16string_view rString, SvStream* 
pStream4Detect )
 {
     sal_Int32 nPos = rString.empty() ? -1 : 0;
+    bool bDetectSep = false;
 
     // Token 0: Field separator.
     if ( nPos >= 0 )
@@ -96,9 +99,14 @@ void ScAsciiOptions::ReadFromString( std::u16string_view 
rString )
         bFixedLen = bMergeFieldSeps = false;
 
         const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', 
nPos);
-        if ( aToken == pStrFix )
-            bFixedLen = true;
-        aFieldSeps = lcl_decodeSepString( aToken, bMergeFieldSeps);
+        if ( aToken == pStrDet)
+            bDetectSep = true;
+        else
+        {
+            if ( aToken == pStrFix )
+                bFixedLen = true;
+            aFieldSeps = lcl_decodeSepString( aToken, bMergeFieldSeps);
+        }
     }
 
     // Token 1: Text separator.
@@ -111,9 +119,22 @@ void ScAsciiOptions::ReadFromString( std::u16string_view 
rString )
     // Token 2: Text encoding.
     if ( nPos >= 0 )
     {
-        eCharSet = ScGlobal::GetCharsetValue( o3tl::getToken(rString, 0, ',', 
nPos) );
+        const std::u16string_view aToken = o3tl::getToken(rString, 0, ',', 
nPos);
+        SvStreamEndian endian;
+        bool bDetectCharSet = aToken == pStrDet;
+        if ( bDetectCharSet && pStream4Detect )
+        {
+            SfxObjectShell::DetectCharSet(*pStream4Detect, eCharSet, endian);
+            if (eCharSet == RTL_TEXTENCODING_UNICODE)
+                pStream4Detect->SetEndian(endian);
+        }
+        else if (!bDetectCharSet)
+            eCharSet = ScGlobal::GetCharsetValue( aToken );
     }
 
+    if (bDetectSep && pStream4Detect)
+        SfxObjectShell::DetectCsvSeparators(*pStream4Detect, eCharSet, 
aFieldSeps, cTextSep);
+
     // Token 3: Number of start row.
     if ( nPos >= 0 )
     {
diff --git a/sc/source/ui/dbgui/csvtablebox.cxx 
b/sc/source/ui/dbgui/csvtablebox.cxx
index 2a3a16c0c550..ab8231013009 100644
--- a/sc/source/ui/dbgui/csvtablebox.cxx
+++ b/sc/source/ui/dbgui/csvtablebox.cxx
@@ -57,6 +57,26 @@ ScCsvTableBox::~ScCsvTableBox()
 
 // common table box handling --------------------------------------------------
 
+void ScCsvTableBox::Refresh()
+{
+    mxGrid->DisableRepaint();
+    mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
+    if (mbFixedMode)
+    {
+        mxGrid->Execute( CSVCMD_SETPOSCOUNT, mnFixedWidth );
+        mxGrid->SetSplits( mxRuler->GetSplits() );
+        mxGrid->SetColumnStates( std::vector(maFixColStates) );
+    }
+    else
+    {
+        mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
+        mxGrid->Execute( CSVCMD_NEWCELLTEXTS );
+        mxGrid->SetColumnStates( std::vector(maSepColStates) );
+    }
+    InitControls();
+    mxGrid->EnableRepaint();
+}
+
 void ScCsvTableBox::SetSeparatorsMode()
 {
     if( !mbFixedMode )
@@ -68,13 +88,7 @@ void ScCsvTableBox::SetSeparatorsMode()
     // switch to separators mode
     mbFixedMode = false;
     // reset and reinitialize controls
-    mxGrid->DisableRepaint();
-    mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
-    mxGrid->Execute( CSVCMD_SETPOSCOUNT, 1 );
-    mxGrid->Execute( CSVCMD_NEWCELLTEXTS );
-    mxGrid->SetColumnStates( std::vector(maSepColStates) );
-    InitControls();
-    mxGrid->EnableRepaint();
+    Refresh();
 }
 
 void ScCsvTableBox::SetFixedWidthMode()
@@ -87,13 +101,7 @@ void ScCsvTableBox::SetFixedWidthMode()
     // switch to fixed width mode
     mbFixedMode = true;
     // reset and reinitialize controls
-    mxGrid->DisableRepaint();
-    mxGrid->Execute( CSVCMD_SETLINEOFFSET, 0 );
-    mxGrid->Execute( CSVCMD_SETPOSCOUNT, mnFixedWidth );
-    mxGrid->SetSplits( mxRuler->GetSplits() );
-    mxGrid->SetColumnStates( std::vector(maFixColStates) );
-    InitControls();
-    mxGrid->EnableRepaint();
+    Refresh();
 }
 
 void ScCsvTableBox::Init()
diff --git a/sc/source/ui/dbgui/scuiasciiopt.cxx 
b/sc/source/ui/dbgui/scuiasciiopt.cxx
index 304424806aa5..2789d33096c8 100644
--- a/sc/source/ui/dbgui/scuiasciiopt.cxx
+++ b/sc/source/ui/dbgui/scuiasciiopt.cxx
@@ -41,6 +41,8 @@
 #include <o3tl/string_view.hxx>
 
 #include <unicode/ucsdet.h>
+#include <sfx2/objsh.hxx>
+#include <svx/txenctab.hxx>
 
 //! TODO make dynamic
 const SCSIZE ASCIIDLG_MAXROWS                = MAXROWCOUNT;
@@ -67,6 +69,7 @@ enum CSVImportOptionsIndex
     CSVIO_FixedWidth,
     CSVIO_RemoveSpace,
     CSVIO_EvaluateFormulas,
+    CSVIO_SeparatorType,
     // Settings for *all* dialog invocations above.
     // Settings not for SC_TEXTTOCOLUMNS below.
     CSVIO_FromRow,
@@ -80,6 +83,13 @@ enum CSVImportOptionsIndex
     CSVIO_PasteSkipEmptyCells
 };
 
+enum SeparatorType
+{
+    FIXED,
+    SEPARATOR,
+    DETECT_SEPARATOR
+};
+
 }
 
 // Config items for all three paths are defined in
@@ -93,6 +103,7 @@ const ::std::vector<OUString> CSVImportOptionNames =
     u"FixedWidth"_ustr,
     u"RemoveSpace"_ustr,
     u"EvaluateFormulas"_ustr,
+    u"SeparatorType"_ustr,
     u"FromRow"_ustr,
     u"CharSet"_ustr,
     u"QuotedFieldAsText"_ustr,
@@ -176,16 +187,16 @@ static void lcl_CreatePropertiesNames ( OUString& 
rSepPath, Sequence<OUString>&
     {
         case SC_IMPORTFILE:
             rSepPath = aSep_Path;
-            nProperties = 12;
+            nProperties = 13;
             break;
         case SC_PASTETEXT:
             rSepPath = aSep_Path_Clpbrd;
-            nProperties = 13;
+            nProperties = 14;
             break;
         case SC_TEXTTOCOLUMNS:
         default:
             rSepPath = aSep_Path_Text2Col;
-            nProperties = 7;
+            nProperties = 8;
             break;
     }
     rNames.realloc( nProperties );
@@ -196,6 +207,7 @@ static void lcl_CreatePropertiesNames ( OUString& rSepPath, 
Sequence<OUString>&
     pNames[ CSVIO_FixedWidth ] =        CSVImportOptionNames[ CSVIO_FixedWidth 
];
     pNames[ CSVIO_RemoveSpace ] =       CSVImportOptionNames[ 
CSVIO_RemoveSpace ];
     pNames[ CSVIO_EvaluateFormulas ] =  CSVImportOptionNames[ 
CSVIO_EvaluateFormulas ];
+    pNames[ CSVIO_SeparatorType ] =     CSVImportOptionNames[ 
CSVIO_SeparatorType ];
     if (eCall != SC_TEXTTOCOLUMNS)
     {
         pNames[ CSVIO_FromRow ] =       CSVImportOptionNames[ CSVIO_FromRow ];
@@ -215,9 +227,9 @@ static void lcl_CreatePropertiesNames ( OUString& rSepPath, 
Sequence<OUString>&
 
 static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& 
rTextSeparators,
                              bool& rMergeDelimiters, bool& rQuotedAsText, 
bool& rDetectSpecialNum, bool& rDetectScientificNum,
-                             bool& rFixedWidth, sal_Int32& rFromRow, 
sal_Int32& rCharSet,
+                             SeparatorType& rSepType, sal_Int32& rFromRow, 
sal_Int32& rCharSet,
                              sal_Int32& rLanguage, bool& rSkipEmptyCells, 
bool& rRemoveSpace,
-                             bool& rEvaluateFormulas, ScImportAsciiCall eCall )
+                             bool& rEvaluateFormulas, ScImportAsciiCall eCall, 
bool& rBeforeDetection )
 {
     Sequence<Any>aValues;
     const Any *pProperties;
@@ -240,8 +252,14 @@ static void lcl_LoadSeparators( OUString& 
rFieldSeparators, OUString& rTextSepar
     if( pProperties[ CSVIO_TextSeparators ].hasValue() )
         pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
 
-    if( pProperties[ CSVIO_FixedWidth ].hasValue() )
-        rFixedWidth = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ 
CSVIO_FixedWidth ] );
+    rBeforeDetection = true;
+    if( pProperties[ CSVIO_SeparatorType ].hasValue() )
+    {
+        rBeforeDetection = false;
+        rSepType = 
static_cast<SeparatorType>(ScUnoHelpFunctions::GetInt16FromAny( pProperties[ 
CSVIO_SeparatorType ] ));
+    }
+    else if( pProperties[ CSVIO_FixedWidth ].hasValue() )
+        rSepType = (ScUnoHelpFunctions::GetBoolFromAny( pProperties[ 
CSVIO_FixedWidth ] ) ? SeparatorType::FIXED : SeparatorType::DETECT_SEPARATOR);
 
     if( pProperties[ CSVIO_EvaluateFormulas ].hasValue() )
         rEvaluateFormulas = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ 
CSVIO_EvaluateFormulas ] );
@@ -277,7 +295,7 @@ static void lcl_LoadSeparators( OUString& rFieldSeparators, 
OUString& rTextSepar
 
 static void lcl_SaveSeparators(
     const OUString& sFieldSeparators, const OUString& sTextSeparators, bool 
bMergeDelimiters, bool bQuotedAsText,
-    bool bDetectSpecialNum, bool bDetectScientificNum, bool bFixedWidth, 
sal_Int32 nFromRow,
+    bool bDetectSpecialNum, bool bDetectScientificNum, SeparatorType rSepType, 
sal_Int32 nFromRow,
     sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool 
bRemoveSpace, bool bEvaluateFormulas,
     ScImportAsciiCall eCall )
 {
@@ -294,8 +312,8 @@ static void lcl_SaveSeparators(
     pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
     pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
     pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
-    pProperties[ CSVIO_FixedWidth ] <<= bFixedWidth;
     pProperties[ CSVIO_EvaluateFormulas ] <<= bEvaluateFormulas;
+    pProperties[ CSVIO_SeparatorType ] <<= static_cast<sal_Int16>(rSepType);
     if (eCall != SC_TEXTTOCOLUMNS)
     {
         pProperties[ CSVIO_FromRow ] <<= nFromRow;
@@ -316,21 +334,24 @@ static void lcl_SaveSeparators(
 }
 
 ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, std::u16string_view 
aDatName,
-                                   SvStream* pInStream, ScImportAsciiCall 
eCall,
-                                   const ScAsciiOptions* aOptions)
+                                   SvStream* pInStream, ScImportAsciiCall 
eCall)
     : GenericDialogController(pParent, 
u"modules/scalc/ui/textimportcsv.ui"_ustr, u"TextImportCsvDialog"_ustr)
     , mpDatStream(pInStream)
     , mnStreamPos(pInStream ? pInStream->Tell() : 0)
+    , mnStreamInitPos(mnStreamPos)
     , mnRowPosCount(0)
     , mcTextSep(ScAsciiOptions::cDefaultTextSep)
+    , meDetectedCharSet(RTL_TEXTENCODING_DONTKNOW)
+    , mbCharSetDetect(true)
     , meCall(eCall)
-    , mbDetectSep(eCall != SC_TEXTTOCOLUMNS)
     , mxFtCharSet(m_xBuilder->weld_label(u"textcharset"_ustr))
     , mxLbCharSet(new 
SvxTextEncodingBox(m_xBuilder->weld_combo_box(u"charset"_ustr)))
+    , mxFtDetectedCharSet(m_xBuilder->weld_label(u"textdetectedcharset"_ustr))
     , mxFtCustomLang(m_xBuilder->weld_label(u"textlanguage"_ustr))
     , mxLbCustomLang(new 
SvxLanguageBox(m_xBuilder->weld_combo_box(u"language"_ustr)))
     , mxFtRow(m_xBuilder->weld_label(u"textfromrow"_ustr))
     , mxNfRow(m_xBuilder->weld_spin_button(u"fromrow"_ustr))
+    , mxRbDetectSep(m_xBuilder->weld_radio_button(u"todetectseparator"_ustr))
     , mxRbFixed(m_xBuilder->weld_radio_button(u"tofixedwidth"_ustr))
     , mxRbSeparated(m_xBuilder->weld_radio_button(u"toseparatedby"_ustr))
     , mxCkbTab(m_xBuilder->weld_check_button(u"tab"_ustr))
@@ -376,40 +397,23 @@ ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, 
std::u16string_view aD
     OUString sFieldSeparators(u",;     "_ustr);
     OUString sTextSeparators(mcTextSep);
     bool bMergeDelimiters = false;
-    bool bFixedWidth = false;
+    SeparatorType eSepType = DETECT_SEPARATOR;
     bool bQuotedFieldAsText = false;
     bool bDetectSpecialNum = true;
     bool bDetectScientificNum = true;
     bool bEvaluateFormulas = (meCall != SC_IMPORTFILE);
     bool bSkipEmptyCells = true;
     bool bRemoveSpace = false;
+    bool bBeforeDetection = false;
     sal_Int32 nFromRow = 1;
     sal_Int32 nCharSet = -1;
     sal_Int32 nLanguage = 0;
 
-    if (aOptions)
-    {
-        if (!aOptions->GetFieldSeps().isEmpty())
-            sFieldSeparators = aOptions->GetFieldSeps();
-        if (aOptions->GetTextSep())
-            sTextSeparators = OUStringChar(aOptions->GetTextSep());
-        bMergeDelimiters = aOptions->IsMergeSeps();
-        bFixedWidth = aOptions->IsFixedLen();
-        bQuotedFieldAsText = aOptions->IsQuotedAsText();
-        bDetectSpecialNum = aOptions->IsDetectSpecialNumber();
-        bDetectScientificNum = aOptions->IsDetectScientificNumber();
-        bEvaluateFormulas = aOptions->IsEvaluateFormulas();
-        bSkipEmptyCells = aOptions->IsSkipEmptyCells();
-        bRemoveSpace = aOptions->IsRemoveSpace();
-        nFromRow = aOptions->GetStartRow();
-        nCharSet = aOptions->GetCharSet();
-        nLanguage = static_cast<sal_uInt16>(aOptions->GetLanguage());
-    }
-    else
-        lcl_LoadSeparators (sFieldSeparators, sTextSeparators, 
bMergeDelimiters,
-                         bQuotedFieldAsText, bDetectSpecialNum, 
bDetectScientificNum, bFixedWidth, nFromRow,
-                         nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, 
bEvaluateFormulas, meCall);
-    // load from saved settings
+    lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
+                         bQuotedFieldAsText, bDetectSpecialNum, 
bDetectScientificNum, eSepType, nFromRow,
+                         nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, 
bEvaluateFormulas, meCall,
+                         bBeforeDetection);
+
     maFieldSeparators = sFieldSeparators;
 
     if( bMergeDelimiters && !bIsTSV )
@@ -430,95 +434,48 @@ ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, 
std::u16string_view aD
         mxCkbEvaluateFormulas->set_active(true);
     if (bSkipEmptyCells)
         mxCkbSkipEmptyCells->set_active(true);
-    if (bFixedWidth && !bIsTSV)
-        mxRbFixed->set_active(true);
-    if (nFromRow != 1)
-        mxNfRow->set_value(nFromRow);
-
-    // Clipboard is always Unicode, else detect.
-    rtl_TextEncoding ePreselectUnicode = (aOptions ? aOptions->GetCharSet() : 
(meCall == SC_IMPORTFILE ?
-            RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE));
-    // Sniff for Unicode / not
-    if( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW && mpDatStream )
+    if (eSepType == SeparatorType::FIXED)
     {
-        mpDatStream->Seek( 0 );
-        constexpr size_t buffsize = 4096;
-        sal_Int8 bytes[buffsize] = { 0 };
-        sal_Int32 nRead = mpDatStream->ReadBytes( bytes, buffsize );
-        mpDatStream->Seek( 0 );
-
-        if ( nRead > 0 )
+        if (bIsTSV)
         {
-            UErrorCode uerr = U_ZERO_ERROR;
-            UCharsetDetector* ucd = ucsdet_open( &uerr );
-            ucsdet_setText( ucd, reinterpret_cast<const char*>(bytes), nRead, 
&uerr );
-
-            if ( const UCharsetMatch* match = ucsdet_detect(ucd, &uerr) )
-            {
-                const char* pEncodingName = ucsdet_getName( match, &uerr );
-
-                if ( U_SUCCESS(uerr) && !strcmp("UTF-8", pEncodingName) )
-                {
-                    ePreselectUnicode = RTL_TEXTENCODING_UTF8; // UTF-8
-                    mpDatStream->StartReadingUnicodeText( 
RTL_TEXTENCODING_UTF8 );
-                }
-                else if ( U_SUCCESS(uerr) && !strcmp("UTF-16LE", 
pEncodingName) )
-                {
-                    ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16LE
-                    mpDatStream->SetEndian( SvStreamEndian::LITTLE );
-                    mpDatStream->StartReadingUnicodeText( 
RTL_TEXTENCODING_UNICODE );
-                }
-                else if ( U_SUCCESS(uerr) && !strcmp("UTF-16BE", 
pEncodingName) )
-                {
-                    ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16BE
-                    mpDatStream->SetEndian( SvStreamEndian::BIG );
-                    mpDatStream->StartReadingUnicodeText( 
RTL_TEXTENCODING_UNICODE );
-                }
-                else // other
-                    mpDatStream->StartReadingUnicodeText( 
RTL_TEXTENCODING_DONTKNOW );
-            }
-
-            ucsdet_close( ucd );
+            eSepType = SeparatorType::SEPARATOR;
+            mxRbSeparated->set_active(true);
         }
+        else
+            mxRbFixed->set_active(true);
+    }
+    else if (eSepType == SeparatorType::SEPARATOR)
+        mxRbSeparated->set_active(true);
+    else
+        mxRbDetectSep->set_active(true);
+    if (nFromRow != 1)
+        mxNfRow->set_value(nFromRow);
 
-        mnStreamPos = mpDatStream->Tell();
+    // Clipboard is always Unicode, else rely on default/config.
+    rtl_TextEncoding ePreselectUnicode = (meCall == SC_IMPORTFILE ?
+            RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
+
+    // Detect character set only once and then use it for "Detect" option.
+    SvStreamEndian eEndian;
+    SfxObjectShell::DetectCharSet(*mpDatStream, meDetectedCharSet, eEndian);
+    if (meDetectedCharSet == RTL_TEXTENCODING_UNICODE)
+        mpDatStream->SetEndian(eEndian);
+    else if ( meDetectedCharSet == RTL_TEXTENCODING_DONTKNOW )
+    {
+        meDetectedCharSet = osl_getThreadTextEncoding();
+        // Prefer UTF-8, as UTF-16 would have already been detected from the 
stream.
+        // This gives a better chance that the file is going to be opened 
correctly.
+        if ( meDetectedCharSet == RTL_TEXTENCODING_UNICODE && mpDatStream )
+            meDetectedCharSet = RTL_TEXTENCODING_UTF8;
     }
 
-    if (aOptions && !maFieldSeparators.isEmpty())
-        SetSeparators(0);
-    else if (bIsTSV)
+    if (bIsTSV)
         SetSeparators('        ');
     else
-    {
-        // Some MS-Excel convention is the first line containing the field
-        // separator as "sep=|" (without quotes and any field separator
-        // character). The second possibility seems to be it is present *with*
-        // quotes so it shows up as cell content *including* the separator and
-        // can be preserved during round trips. Check for an exact match of
-        // any such and set separator.
-        /* TODO: it is debatable whether the unquoted form should rather be
-         * treated special to actually include the separator in the field data.
-         * Currently it does not. */
-        sal_Unicode cSep = 0;
-        OUString aLine;
-        // Try to read one more character, if more than 7 it can't be an exact
-        // match of any.
-        mpDatStream->ReadUniOrByteStringLine( aLine, 
mpDatStream->GetStreamCharSet(), 8);
-        mpDatStream->Seek(mnStreamPos);
-        if (aLine.getLength() == 8)
-            ;   // nothing
-        else if (aLine.getLength() == 5 && 
aLine.startsWithIgnoreAsciiCase("sep="))
-            cSep = aLine[4];
-        else if (aLine.getLength() == 7 && aLine[6] == '"' && 
aLine.startsWithIgnoreAsciiCase("\"sep="))
-            cSep = aLine[5];
-
-        // Set Separators in the dialog from maFieldSeparators (empty are not
-        // set) or an optionally defined by file content field separator.
-        SetSeparators(cSep);
-    }
+        SetSeparators(0);
 
     // Get Separators from the dialog (empty are set from default)
-    maFieldSeparators = GetSeparators();
+    maFieldSeparators = GetActiveSeparators();
 
     mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl 
) );
 
@@ -551,22 +508,15 @@ ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, 
std::u16string_view aD
     // Insert one "SYSTEM" entry for compatibility in AsciiOptions and system
     // independent document linkage.
     mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_DONTKNOW, ScResId( 
SCSTR_CHARSET_USER ) );
-    if ( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW )
-    {
-        rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
-        // Prefer UTF-8, as UTF-16 would have already been detected from the 
stream.
-        // This gives a better chance that the file is going to be opened 
correctly.
-        if ( ( eSystemEncoding == RTL_TEXTENCODING_UNICODE ) && mpDatStream )
-            eSystemEncoding = RTL_TEXTENCODING_UTF8;
-        mxLbCharSet->SelectTextEncoding( eSystemEncoding );
-    }
-    else
-    {
-        mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
-    }
+    // Insert one for detecting charset.
+    mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_USER_DETECTED, "- " + 
ScResId( SCSTR_AUTOMATIC ) + " -" );
 
-    if (nCharSet >= 0 && ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW)
+    if (ePreselectUnicode != RTL_TEXTENCODING_DONTKNOW)
+        mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
+    else if (nCharSet >= 0 && !bBeforeDetection)
         mxLbCharSet->set_active(nCharSet);
+    else
+        mxLbCharSet->SelectTextEncoding(RTL_TEXTENCODING_USER_DETECTED);
 
     SetSelectedCharSet();
     mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
@@ -592,10 +542,10 @@ ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, 
std::u16string_view aD
     mxTableBox->InitTypes( *mxLbType );
     mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
 
+    mxRbDetectSep->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl 
) );
     mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl 
) );
     mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
 
-    SetupSeparatorCtrls();
     RbSepFix();
 
     UpdateVertical();
@@ -715,9 +665,9 @@ void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
     rOpt.SetFixedLen( mxRbFixed->get_active() );
     rOpt.SetStartRow( mxNfRow->get_value() );
     mxTableBox->FillColumnData( rOpt );
-    if( mxRbSeparated->get_active() )
+    if( mxRbSeparated->get_active() || mxRbDetectSep->get_active())
     {
-        rOpt.SetFieldSeps( GetSeparators() );
+        rOpt.SetFieldSeps( GetActiveSeparators() );
         rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
         rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
         rOpt.SetTextSep( lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP ) );
@@ -732,9 +682,9 @@ void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
 
 void ScImportAsciiDlg::SaveParameters()
 {
-    lcl_SaveSeparators( maFieldSeparators, mxCbTextSep->get_active_text(), 
mxCkbAsOnce->get_active(),
+    lcl_SaveSeparators( GetSeparators(), mxCbTextSep->get_active_text(), 
mxCkbAsOnce->get_active(),
                      mxCkbQuotedAsText->get_active(), 
mxCkbDetectNumber->get_active(), mxCkbDetectScientificNumber->get_active(),
-                     mxRbFixed->get_active(),
+                     mxRbFixed->get_active() ? FIXED : 
(mxRbDetectSep->get_active() ? DETECT_SEPARATOR : SEPARATOR),
                      mxNfRow->get_value(),
                      mxLbCharSet->get_active(),
                      static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
@@ -788,10 +738,27 @@ void ScImportAsciiDlg::SetSeparators( sal_Unicode cSep )
 
 void ScImportAsciiDlg::SetSelectedCharSet()
 {
+    rtl_TextEncoding eOldCharSet = meCharSet;
     meCharSet = mxLbCharSet->GetSelectTextEncoding();
+    mbCharSetDetect = (meCharSet == RTL_TEXTENCODING_USER_DETECTED);
     mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
-    if( mbCharSetSystem )
+    if (mbCharSetDetect)
+    {
+        meCharSet = meDetectedCharSet;
+        
mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
+    }
+    else if( mbCharSetSystem )
+    {
         meCharSet = osl_getThreadTextEncoding();
+        
mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
+    }
+    else
+        
mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
+
+    if (eOldCharSet != meCharSet)
+        DetectCsvSeparators();
+
+    RbSepFix();
 }
 
 OUString ScImportAsciiDlg::GetSeparators() const
@@ -810,6 +777,17 @@ OUString ScImportAsciiDlg::GetSeparators() const
     return aSepChars;
 }
 
+OUString ScImportAsciiDlg::GetActiveSeparators() const
+{
+    if (mxRbSeparated->get_active())
+        return GetSeparators();
+
+    if (mxRbDetectSep->get_active())
+        return maDetectedFieldSeps;
+
+    return OUString();
+}
+
 void ScImportAsciiDlg::SetupSeparatorCtrls()
 {
     bool bEnable = mxRbSeparated->get_active();
@@ -817,12 +795,41 @@ void ScImportAsciiDlg::SetupSeparatorCtrls()
     mxCkbSemicolon->set_sensitive( bEnable );
     mxCkbComma->set_sensitive( bEnable );
     mxCkbSpace->set_sensitive( bEnable );
-    mxCkbRemoveSpace->set_sensitive( bEnable );
     mxCkbOther->set_sensitive( bEnable );
     mxEdOther->set_sensitive( bEnable );
+
+    bEnable = bEnable || mxRbDetectSep->get_active();
+    mxCkbRemoveSpace->set_sensitive( bEnable );
     mxCkbAsOnce->set_sensitive( bEnable );
     mxFtTextSep->set_sensitive( bEnable );
     mxCbTextSep->set_sensitive( bEnable );
+
+    OUString aSepName;
+    if (maDetectedFieldSeps.isEmpty())
+        aSepName += ScResId(SCSTR_NONE);
+    else
+    {
+        for (int idx = 0; idx < maDetectedFieldSeps.getLength(); idx ++)
+        {
+            if (idx > 0)
+                aSepName += u" ";
+
+            if (maDetectedFieldSeps[idx] == u' ')
+                aSepName += ScResId(SCSTR_FIELDSEP_SPACE);
+            else if (maDetectedFieldSeps[idx] == u'    ')
+                aSepName += ScResId(SCSTR_FIELDSEP_TAB);
+            else
+                aSepName += OUStringChar(maDetectedFieldSeps[idx]);
+        }
+    }
+    mxRbDetectSep->set_label(ScResId(SCSTR_DETECTED).replaceFirst( "%1", 
aSepName));
+}
+
+void ScImportAsciiDlg::DetectCsvSeparators()
+{
+    mpDatStream->Seek(mnStreamInitPos);
+    SfxObjectShell::DetectCsvSeparators(*mpDatStream, meCharSet, 
maDetectedFieldSeps, mcTextSep);
+    mpDatStream->Seek(mnStreamPos);
 }
 
 void ScImportAsciiDlg::UpdateVertical()
@@ -835,10 +842,17 @@ void ScImportAsciiDlg::UpdateVertical()
 void ScImportAsciiDlg::RbSepFix()
 {
     weld::WaitObject aWaitObj(m_xDialog.get());
-    if( mxRbFixed->get_active() )
-        mxTableBox->SetFixedWidthMode();
+    if (mxRbSeparated->get_active() || mxRbDetectSep->get_active())
+    {
+        maFieldSeparators = GetActiveSeparators();
+        if (mxTableBox->IsFixedWidthMode())
+            mxTableBox->SetSeparatorsMode();
+        else
+            mxTableBox->Refresh();
+    }
     else
-        mxTableBox->SetSeparatorsMode();
+        mxTableBox->SetFixedWidthMode();
+
     SetupSeparatorCtrls();
 }
 
@@ -892,13 +906,26 @@ void ScImportAsciiDlg::SeparatorHdl(const weld::Widget* 
pCtrl)
         mxCkbOther->set_active(!mxEdOther->get_text().isEmpty());
 
     OUString aOldFldSeps( maFieldSeparators);
-    maFieldSeparators = GetSeparators();
     sal_Unicode cOldSep = mcTextSep;
     mcTextSep = lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP );
     // Any separator changed may result in completely different lines due to
     // embedded line breaks.
-    if (cOldSep != mcTextSep || aOldFldSeps != maFieldSeparators)
-        UpdateVertical();
+    if (cOldSep != mcTextSep)
+    {
+        DetectCsvSeparators();
+
+        SetupSeparatorCtrls();
+
+        maFieldSeparators = GetActiveSeparators();
+        if (aOldFldSeps != maFieldSeparators)
+        {
+            UpdateVertical();
+            mxTableBox->Refresh();
+            return;
+        }
+    }
+    else
+        maFieldSeparators = GetActiveSeparators();
 
     mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
 }
@@ -931,14 +958,7 @@ IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, 
rListBox, void)
 
 IMPL_LINK_NOARG(ScImportAsciiDlg, UpdateTextHdl, ScCsvTableBox&, void)
 {
-    // Checking the separator can only be done once for the very first time
-    // when the dialog wasn't already presented to the user.
-    // As a side effect this has the benefit that the check is only done on the
-    // first set of visible lines.
-    mbDetectSep = (mbDetectSep && !mxRbFixed->get_active()
-                && (!mxCkbTab->get_active() || !mxCkbSemicolon->get_active()
-                    || !mxCkbComma->get_active() || 
!mxCkbSpace->get_active()));
-    sal_Unicode cDetectSep = (mbDetectSep ? 0 : 0xffff);
+    sal_Unicode cDetectSep = 0xffff;
 
     sal_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
     sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
@@ -958,24 +978,6 @@ IMPL_LINK_NOARG(ScImportAsciiDlg, UpdateTextHdl, 
ScCsvTableBox&, void)
     for (; i < CSV_PREVIEW_LINES; i++)
         maPreviewLine[i].clear();
 
-    if (mbDetectSep)
-    {
-        mbDetectSep = false;
-        if (cDetectSep)
-        {
-            // Expect separator to be appended by now so all subsequent
-            // GetLine()/ReadCsvLine() actually used it.
-            assert(maFieldSeparators.endsWith(OUStringChar(cDetectSep)));
-            // Preselect separator in UI.
-            switch (cDetectSep)
-            {
-                case ' ':  mxCkbTab->set_active(true);         break;
-                case ';':   mxCkbSemicolon->set_active(true);   break;
-                case ',':   mxCkbComma->set_active(true);       break;
-                case ' ':   mxCkbSpace->set_active(true);       break;
-            }
-        }
-    }
 
     mxTableBox->GetGrid().Execute( CSVCMD_SETLINECOUNT, mnRowPosCount);
     bool bMergeSep = mxCkbAsOnce->get_active();
diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
index 4b8e5f041c8d..7ef50cc7a8d3 100644
--- a/sc/source/ui/docshell/docsh.cxx
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -1354,7 +1354,7 @@ bool ScDocShell::ConvertFrom( SfxMedium& rMedium )
 
             if ( const SfxStringItem* pOptionsItem = 
rMedium.GetItemSet().GetItemIfSet( SID_FILE_FILTEROPTIONS ) )
             {
-                aOptions.ReadFromString( pOptionsItem->GetValue() );
+                aOptions.ReadFromString( pOptionsItem->GetValue(), 
rMedium.GetInStream() );
                 bOptInit = true;
             }
 
diff --git a/sc/source/ui/inc/asciiopt.hxx b/sc/source/ui/inc/asciiopt.hxx
index 6028b8825d94..af97d4b0f2ba 100644
--- a/sc/source/ui/inc/asciiopt.hxx
+++ b/sc/source/ui/inc/asciiopt.hxx
@@ -52,7 +52,7 @@ public:
 
     static const sal_Unicode cDefaultTextSep = '"';
 
-    void            ReadFromString( std::u16string_view rString );
+    void            ReadFromString( std::u16string_view rString, SvStream* 
pStream4Detect = nullptr );
     OUString        WriteToString() const;
 
     rtl_TextEncoding    GetCharSet() const      { return eCharSet; }
diff --git a/sc/source/ui/inc/csvtablebox.hxx b/sc/source/ui/inc/csvtablebox.hxx
index e2392a478f4c..9d626493fd54 100644
--- a/sc/source/ui/inc/csvtablebox.hxx
+++ b/sc/source/ui/inc/csvtablebox.hxx
@@ -72,10 +72,12 @@ public:
 
     // common table box handling ----------------------------------------------
 public:
+    void                        Refresh();
     /** Sets the control to separators mode. */
     void                        SetSeparatorsMode();
     /** Sets the control to fixed width mode. */
     void                        SetFixedWidthMode();
+    bool                        IsFixedWidthMode(){ return mbFixedMode; }
 
     ScCsvRuler& GetRuler() { return *mxRuler; }
     ScCsvGrid& GetGrid() { return *mxGrid; }
diff --git a/sc/source/ui/inc/scuiasciiopt.hxx 
b/sc/source/ui/inc/scuiasciiopt.hxx
index ee8ca78b221d..5b19b8c4d3ee 100644
--- a/sc/source/ui/inc/scuiasciiopt.hxx
+++ b/sc/source/ui/inc/scuiasciiopt.hxx
@@ -31,29 +31,34 @@ class SvxTextEncodingBox;
 
 class ScImportAsciiDlg : public weld::GenericDialogController
 {
-    SvStream*                   mpDatStream;
+    SvStream*                       mpDatStream;
     sal_uLong                       mnStreamPos;
+    sal_uLong                       mnStreamInitPos;
     std::unique_ptr<sal_uLong[]>    mpRowPosArray;
     sal_uLong                       mnRowPosCount;
 
     OUString               maPreviewLine[ CSV_PREVIEW_LINES ];
 
     OUString                    maFieldSeparators;  // selected field 
separators
+    OUString                    maDetectedFieldSeps; // detected field seps
     sal_Unicode                 mcTextSep;
 
     rtl_TextEncoding            meCharSet;          /// Selected char set.
+    rtl_TextEncoding            meDetectedCharSet;  /// This is computed only 
once at initialization, so store it.
     bool                        mbCharSetSystem;    /// Is System char set 
selected?
+    bool                        mbCharSetDetect;    /// Should we autodetect 
character set ?
     ScImportAsciiCall           meCall;             /// How the dialog is 
called (see asciiopt.hxx)
-    bool                        mbDetectSep;        /// Whether to detect a 
possible separator.
 
     std::unique_ptr<weld::Label> mxFtCharSet;
     std::unique_ptr<SvxTextEncodingBox> mxLbCharSet;
+    std::unique_ptr<weld::Label> mxFtDetectedCharSet;
     std::unique_ptr<weld::Label> mxFtCustomLang;
     std::unique_ptr<SvxLanguageBox> mxLbCustomLang;
 
     std::unique_ptr<weld::Label> mxFtRow;
     std::unique_ptr<weld::SpinButton> mxNfRow;
 
+    std::unique_ptr<weld::RadioButton> mxRbDetectSep;
     std::unique_ptr<weld::RadioButton> mxRbFixed;
     std::unique_ptr<weld::RadioButton> mxRbSeparated;
 
@@ -83,8 +88,7 @@ class ScImportAsciiDlg : public weld::GenericDialogController
 public:
                                 ScImportAsciiDlg(
                                     weld::Window* pParent, std::u16string_view 
aDatName,
-                                    SvStream* pInStream, ScImportAsciiCall 
eCall,
-                                    const ScAsciiOptions* aOptions = nullptr );
+                                    SvStream* pInStream, ScImportAsciiCall 
eCall);
                                 virtual ~ScImportAsciiDlg() override;
 
     void                        GetOptions( ScAsciiOptions& rOpt );
@@ -98,6 +102,8 @@ private:
     void                        SetSeparators( sal_Unicode cSep );
     /** Returns all separator characters in a string. */
     OUString                    GetSeparators() const;
+    OUString                    GetActiveSeparators() const;
+    void                        DetectCsvSeparators();
 
     /** Enables or disables all separator checkboxes and edit fields. */
     void                        SetupSeparatorCtrls();
diff --git a/sc/source/ui/unoobj/filtuno.cxx b/sc/source/ui/unoobj/filtuno.cxx
index e5ee8de17d29..575d66147b64 100644
--- a/sc/source/ui/unoobj/filtuno.cxx
+++ b/sc/source/ui/unoobj/filtuno.cxx
@@ -179,25 +179,15 @@ sal_Int16 SAL_CALL ScFilterOptionsObj::execute()
     {
         //  ascii import is special...
 
-        ScAsciiOptions aInOptions, *pInOptions = nullptr;
         INetURLObject aURL( aFileName );
         // tdf#132421 - don't URL encode filename for the import ASCII dialog 
title
         OUString 
aPrivDatName(aURL.GetLastName(INetURLObject::DecodeMechanism::Unambiguous));
         std::unique_ptr<SvStream> pInStream;
         if ( xInputStream.is() )
-        {
             pInStream = utl::UcbStreamHelper::CreateStream( xInputStream );
 
-            if (aFilterOptions.isEmpty())
-                aFilterOptions = "DETECT,34,DETECT,,,,,,,,,,,,";
-            SfxObjectShell::DetectCsvFilterOptions(*pInStream, aFilterOptions);
-
-            aInOptions.ReadFromString(aFilterOptions);
-            pInOptions = &aInOptions;
-        }
-
         ScopedVclPtr<AbstractScImportAsciiDlg> 
pDlg(pFact->CreateScImportAsciiDlg(Application::GetFrameWeld(xDialogParent), 
aPrivDatName,
-                                                                               
   pInStream.get(), SC_IMPORTFILE, pInOptions));
+                                                                               
   pInStream.get(), SC_IMPORTFILE));
         if ( pDlg->Execute() == RET_OK )
         {
             ScAsciiOptions aOptions;
diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx
index 0323ca8c219f..bb7c4cf831b3 100644
--- a/sc/source/ui/view/cellsh2.cxx
+++ b/sc/source/ui/view/cellsh2.cxx
@@ -1040,6 +1040,7 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq )
                     ScImportExport::SetNoEndianSwap( aStream );
                     aExport.ExportStream( aStream, OUString(), 
SotClipboardFormatId::STRING );
 
+                    aStream.Seek(0);
                     ScAbstractDialogFactory* pFact = 
ScAbstractDialogFactory::Create();
                     ScopedVclPtr<AbstractScImportAsciiDlg> 
pDlg(pFact->CreateScImportAsciiDlg(
                             pTabViewShell->GetFrameWeld(), OUString(), 
&aStream, SC_TEXTTOCOLUMNS));
diff --git a/sc/uiconfig/scalc/ui/textimportcsv.ui 
b/sc/uiconfig/scalc/ui/textimportcsv.ui
index a7ab406a563c..0f841c5e4910 100644
--- a/sc/uiconfig/scalc/ui/textimportcsv.ui
+++ b/sc/uiconfig/scalc/ui/textimportcsv.ui
@@ -137,7 +137,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">0</property>
-                        <property name="top-attach">1</property>
+                        <property name="top-attach">2</property>
                       </packing>
                     </child>
                     <child>
@@ -151,7 +151,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">0</property>
-                        <property name="top-attach">2</property>
+                        <property name="top-attach">3</property>
                       </packing>
                     </child>
                     <child>
@@ -169,6 +169,18 @@
                         <property name="top-attach">0</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkLabel" id="textdetectedcharset">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="no" 
context="textimportcsv|textcharset"></property>
+                      </object>
+                      <packing>
+                        <property name="left-attach">1</property>
+                        <property name="top-attach">1</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkComboBoxText" id="language">
                         <property name="visible">True</property>
@@ -181,7 +193,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">1</property>
-                        <property name="top-attach">1</property>
+                        <property name="top-attach">2</property>
                       </packing>
                     </child>
                     <child>
@@ -200,7 +212,7 @@
                       </object>
                       <packing>
                         <property name="left-attach">1</property>
-                        <property name="top-attach">2</property>
+                        <property name="top-attach">3</property>
                       </packing>
                     </child>
                   </object>
@@ -252,7 +264,7 @@
                             <property name="receives-default">False</property>
                             <property name="use-underline">True</property>
                             <property name="draw-indicator">True</property>
-                            <property name="group">toseparatedby</property>
+                            <property name="group">todetectseparator</property>
                             <child internal-child="accessible">
                               <object class="AtkObject" 
id="tofixedwidth-atkobject">
                                 <property 
name="AtkObject::accessible-description" translatable="yes" 
context="textimportcsv|extended_tip|tofixedwidth">Separates fixed-width data 
(equal number of characters) into columns.</property>
@@ -272,8 +284,8 @@
                             <property name="can-focus">True</property>
                             <property name="receives-default">False</property>
                             <property name="use-underline">True</property>
-                            <property name="active">True</property>
                             <property name="draw-indicator">True</property>
+                            <property name="group">todetectseparator</property>
                             <child internal-child="accessible">
                               <object class="AtkObject" 
id="toseparatedby-atkobject">
                                 <property 
name="AtkObject::accessible-description" translatable="yes" 
context="textimportcsv|extended_tip|toseparatedby">Select the separator used in 
your data.</property>
@@ -286,6 +298,27 @@
                             <property name="position">1</property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkRadioButton" 
id="todetectseparator">
+                            <property name="label" translatable="yes" 
context="textimportcsv|todetectseparator">Detected</property>
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="receives-default">False</property>
+                            <property name="use-underline">True</property>
+                            <property name="active">True</property>
+                            <property name="draw-indicator">True</property>
+                            <child internal-child="accessible">
+                              <object class="AtkObject" 
id="todetectseparator-atkobject">
+                                <property 
name="AtkObject::accessible-description" translatable="yes" 
context="textimportcsv|extended_tip|todetectseparator">Use detected 
separator.</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
diff --git a/sfx2/source/doc/objstor.cxx b/sfx2/source/doc/objstor.cxx
index 17f34ec5ad88..7a9f94da4337 100644
--- a/sfx2/source/doc/objstor.cxx
+++ b/sfx2/source/doc/objstor.cxx
@@ -119,7 +119,6 @@
 #include "objstor.hxx"
 #include "exoticfileloadexception.hxx"
 #include <unicode/ucsdet.h>
-#include <unicode/ucnv.h>
 #include <o3tl/string_view.hxx>
 
 using namespace ::com::sun::star;
@@ -960,7 +959,7 @@ void SfxObjectShell::DetectCsvSeparators(SvStream& stream, 
rtl_TextEncoding& eCh
     std::vector<std::unordered_map<sal_Unicode, sal_uInt32>> aLinesCharsCount;
     std::unordered_map<sal_Unicode, sal_uInt32> aCharsCount;
     std::unordered_map<sal_Unicode, std::pair<sal_uInt32, sal_uInt32>> aStats;
-    constexpr sal_uInt32 nMaxLinesToProcess = 20;
+    constexpr sal_uInt32 nTimeout = 500; // Timeout for detection in ms
     sal_uInt32 nLinesCount = 0;
     OUString sInitSeps;
     OUString sCommonSeps = u", ;:| \/"_ustr;//Sorted by importance
@@ -970,17 +969,18 @@ void SfxObjectShell::DetectCsvSeparators(SvStream& 
stream, rtl_TextEncoding& eCh
     sal_uInt32 nMaxLinesSameChar = 0;
     sal_uInt32 nMinDiffs = 0xFFFFFFFF;
     sal_uInt64 nInitPos = stream.Tell();
+    sal_uInt64 nStartTime = tools::Time::GetSystemTicks();
 
     if (!cStringDelimiter)
         cStringDelimiter = '\"';
 
     for (sal_Int32 nComSepIdx = sCommonSeps.getLength() - 1; nComSepIdx >= 0; 
nComSepIdx --)
         usetCommonSeps.insert(sCommonSeps[nComSepIdx]);
-    aLinesCharsCount.reserve(nMaxLinesToProcess);
+    aLinesCharsCount.reserve(128);
     separators = "";
 
     stream.StartReadingUnicodeText(eCharSet);
-    while (stream.ReadUniOrByteStringLine(sLine, eCharSet) && 
aLinesCharsCount.size() < nMaxLinesToProcess)
+    while (stream.ReadUniOrByteStringLine(sLine, eCharSet) && 
(tools::Time::GetSystemTicks() - nStartTime < nTimeout))
     {
         if (sLine.isEmpty())
             continue;
@@ -1034,7 +1034,7 @@ void SfxObjectShell::DetectCsvSeparators(SvStream& 
stream, rtl_TextEncoding& eCh
         {
             auto aCurStats = aStats.find(aCurLineChar->first);
             if (aCurStats == aStats.cend())
-                aStats.insert(std::pair<sal_Unicode, std::pair<sal_uInt32, 
sal_uInt32>>(aCurLineChar->first, std::pair<sal_uInt32, sal_uInt32>(1, 1)));
+                aCurStats = aStats.insert(std::pair<sal_Unicode, 
std::pair<sal_uInt32, sal_uInt32>>(aCurLineChar->first, std::pair<sal_uInt32, 
sal_uInt32>(1, 1))).first;
             else
             {
                 aCurStats->second.first ++;// Increment number of lines that 
contain the current character
@@ -1048,17 +1048,19 @@ void SfxObjectShell::DetectCsvSeparators(SvStream& 
stream, rtl_TextEncoding& eCh
                 }
                 if (aPrevLineChar == aLinesCharsCount.cend())
                     aCurStats->second.second ++;// Increment number of 
different number of occurrences.
-
-                // Update the maximum of number of lines that contain the same 
character. This is a global value.
-                if (nMaxLinesSameChar < aCurStats->second.first)
-                    nMaxLinesSameChar = aCurStats->second.first;
             }
+
+            // Update the maximum of number of lines that contain the same 
character. This is a global value.
+            if (nMaxLinesSameChar < aCurStats->second.first)
+                nMaxLinesSameChar = aCurStats->second.first;
         }
 
         aLinesCharsCount.emplace_back();
         aLinesCharsCount[aLinesCharsCount.size() - 1].swap(aCharsCount);
     }
 
+    SAL_INFO("sfx.doc", "" << nLinesCount << " lines processed in " << 
tools::Time::GetSystemTicks() - nStartTime << " ms while detecting separator.");
+
     // Compute the global minimum of different number of occurrences.
     // But only for characters which occur in a maximum number of lines 
(previously computed).
     for (auto it=aStats.cbegin(); it != aStats.cend(); it++)
@@ -1086,8 +1088,6 @@ void SfxObjectShell::DetectCsvSeparators(SvStream& 
stream, rtl_TextEncoding& eCh
             }
         }
 
-        if (nInitSepIdx >= 0)
-            break;
     }
 
     stream.Seek(nInitPos);
@@ -1133,9 +1133,9 @@ void SfxObjectShell::DetectCsvFilterOptions(SvStream& 
stream, OUString& aFilterO
 
 
     //Detect separators
-    aFilterOptions = "";
     if (aSeps == aDetect)
     {
+        aFilterOptions = "";
         OUString separators;
         DetectCsvSeparators(stream, eCharSet, separators, 
static_cast<sal_Unicode>(o3tl::toInt32(aDelimiter)));
 
@@ -1198,7 +1198,6 @@ ErrCode SfxObjectShell::HandleFilter( SfxMedium* pMedium, 
SfxObjectShell const *
     // FilterOptions should not be detected here (the detection is done before 
entering
     // interactive state). For now this is focused on CSV files.
     DetectFilterOptions(pMedium);
-    //::sleep(30);
 
     if ( !pData && (bTiledRendering || !pOptions) )
     {
commit 2f1dcf01d713f786ed1bfdc2ba3b6c9e06fb8ecf
Author:     Rafael Lima <[email protected]>
AuthorDate: Fri Sep 20 20:47:12 2024 +0200
Commit:     Mike Kaganski <[email protected]>
CommitDate: Wed Oct 9 14:49:07 2024 +0200

    tdf#157519 Implement Sensitivity Report in LpSolve solver
    
    This patch implements sensitivity analysis when using the LpSolve solver 
engine.
    
    It also adds the infrastructure needed for future implementations in other 
solver engines via the css::sheet::SensitivityReport struct.
    
    Change-Id: I74c2ed9c6201a0b2ffc29ef612d2b778d11a3bef
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173642
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 6ff00937bd81..2a21d9acc791 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -3467,6 +3467,7 @@ $(eval $(call 
gb_UnoApi_add_idlfiles,offapi,com/sun/star/sheet,\
        RangeSelectionEvent \
        ReferenceFlags \
        ResultEvent \
+       SensitivityReport \
        SheetLinkMode \
        SingleReference \
        SolverConstraint \
diff --git a/offapi/com/sun/star/sheet/SensitivityReport.idl 
b/offapi/com/sun/star/sheet/SensitivityReport.idl
new file mode 100644
index 000000000000..7c074b64f583
--- /dev/null
+++ b/offapi/com/sun/star/sheet/SensitivityReport.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+
+module com {  module sun {  module star {  module sheet {
+
+
+/** Stores all the information related to the sensitivity report of a linear 
programming model
+ *
+ *  @since LibreOffice 25.2
+ */
+struct SensitivityReport
+{
+    // Indicates whether a sensitivity report was successfully generated
+    boolean HasReport;
+
+    // Coefficients of the objective function
+    sequence<double> ObjCoefficients;
+
+    // Reduced costs of the variables in the objective function
+    sequence<double> ObjReducedCosts;
+
+    // Allowable decrease in the coefficients of the objective function
+    sequence<double> ObjAllowableDecreases;
+
+    // Allowable increase in the coefficients of the objective function
+    sequence<double> ObjAllowableIncreases;
+
+    // Value of the constraint at the solution
+    sequence<double> ConstrValues;
+
+    // Right-hand side of the constraints
+    sequence<double> ConstrRHS;
+
+    // Shadow prices of constraints
+    sequence<double> ConstrShadowPrices;
+
+    // Allowable decrease in the constraint resources
+    sequence<double> ConstrAllowableDecreases;
+
+    // Allowable increase in the constraint resources
+    sequence<double> ConstrAllowableIncreases;
+};
+
+
+}; }; }; };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/globstr.hrc b/sc/inc/globstr.hrc
index 76f1859c51c4..e2174c4e5c80 100644
--- a/sc/inc/globstr.hrc
+++ b/sc/inc/globstr.hrc
@@ -575,6 +575,20 @@
 #define STR_UNDO_THEME_CHANGE                   NC_("STR_UNDO_THEME_CHANGE", 
"Theme Change")
 #define STR_UNDO_THEME_COLOR_CHANGE             
NC_("STR_UNDO_THEME_COLOR_CHANGE", "Theme Color Change")
 #define STR_ERR_INSERT_CELLS                    NC_("STR_ERR_INSERT_CELLS", 
"Failed to insert cells")
+#define STR_SOLVER_ENGINE                       NC_("STR_SOLVER_ENGINE", 
"Solver Engine:")
+#define STR_SENSITIVITY                         NC_("STR_SENSITIVITY", 
"Sensitivity")
+#define STR_SENSITIVITY_TITLE                   NC_("STR_SENSITIVITY_TITLE", 
"Sensitivity Report")
+#define STR_SENSITIVITY_OBJCELL                 NC_("STR_SENSITIVITY_OBJCELL", 
"Objective Cell")
+#define STR_SENSITIVITY_VARCELLS                
NC_("STR_SENSITIVITY_VARCELLS", "Variable Cells")
+#define STR_SENSITIVITY_CONSTRAINTS             
NC_("STR_SENSITIVITY_CONSTRAINTS", "Constraints")
+#define STR_SENSITIVITY_CELL                    NC_("STR_SENSITIVITY_CELL", 
"Cell")
+#define STR_SENSITIVITY_FINALVALUE              NC_("STR_SENSITIVITY_CELL", 
"Final Value")
+#define STR_SENSITIVITY_REDUCED                 NC_("STR_SENSITIVITY_CELL", 
"Reduced Cost")
+#define STR_SENSITIVITY_OBJCOEFF                NC_("STR_SENSITIVITY_CELL", 
"Objective Coefficient")
+#define STR_SENSITIVITY_DECREASE                NC_("STR_SENSITIVITY_CELL", 
"Allowable Decrease")
+#define STR_SENSITIVITY_INCREASE                NC_("STR_SENSITIVITY_CELL", 
"Allowable Increase")
+#define STR_SENSITIVITY_SHADOWPRICE             
NC_("STR_SENSITIVITY_SHADOWPRICE", "Shadow Price")
+#define STR_SENSITIVITY_RHS                     NC_("STR_SENSITIVITY_RHS", 
"Constraint R.H. Side")
 
 #endif
 
diff --git a/sc/qa/extras/scsolverobj.cxx b/sc/qa/extras/scsolverobj.cxx
index d6feccb3a5d3..0537b6e4f4ff 100644
--- a/sc/qa/extras/scsolverobj.cxx
+++ b/sc/qa/extras/scsolverobj.cxx
@@ -148,14 +148,16 @@ void ScSolverSettingsObj::testXSolverSettings()
     uno::Sequence<beans::PropertyValue> aEngProps = 
xSolverModel->getEngineOptions();
     CPPUNIT_ASSERT_EQUAL(OUString("EpsilonLevel"), aEngProps[0].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(0)), 
aEngProps[0].Value);
-    CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps[1].Name);
+    CPPUNIT_ASSERT_EQUAL(u"GenSensitivityReport"_ustr, aEngProps[1].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps[1].Value);
-    CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps[2].Name);
-    CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[2].Value);
-    CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps[3].Name);
+    CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps[2].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps[2].Value);
+    CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps[3].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[3].Value);
-    CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps[4].Name);
-    CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), 
aEngProps[4].Value);
+    CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps[4].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps[4].Value);
+    CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps[5].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), 
aEngProps[5].Value);
 
     // Save file and reload to check if solver settings are still there
     saveAndReload(u"calc8"_ustr);
@@ -191,14 +193,16 @@ void ScSolverSettingsObj::testXSolverSettings()
     uno::Sequence<beans::PropertyValue> aEngProps2 = 
xSolverModel2->getEngineOptions();
     CPPUNIT_ASSERT_EQUAL(OUString("EpsilonLevel"), aEngProps2[0].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(0)), 
aEngProps2[0].Value);
-    CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps2[1].Name);
+    CPPUNIT_ASSERT_EQUAL(u"GenSensitivityReport"_ustr, aEngProps2[1].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps2[1].Value);
-    CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps2[2].Name);
-    CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[2].Value);
-    CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps2[3].Name);
+    CPPUNIT_ASSERT_EQUAL(u"Integer"_ustr, aEngProps2[2].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(false), aEngProps2[2].Value);
+    CPPUNIT_ASSERT_EQUAL(u"LimitBBDepth"_ustr, aEngProps2[3].Name);
     CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[3].Value);
-    CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps2[4].Name);
-    CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), 
aEngProps2[4].Value);
+    CPPUNIT_ASSERT_EQUAL(u"NonNegative"_ustr, aEngProps2[4].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(true), aEngProps2[4].Value);
+    CPPUNIT_ASSERT_EQUAL(u"Timeout"_ustr, aEngProps2[5].Name);
+    CPPUNIT_ASSERT_EQUAL(uno::Any(static_cast<sal_Int32>(10)), 
aEngProps2[5].Value);
 }
 
 void ScSolverSettingsObj::setUp()
diff --git a/sc/source/ui/inc/optsolver.hxx b/sc/source/ui/inc/optsolver.hxx
index 4f28a59d5fcd..35570f7d09d4 100644
--- a/sc/source/ui/inc/optsolver.hxx
+++ b/sc/source/ui/inc/optsolver.hxx
@@ -24,6 +24,7 @@
 #include "docsh.hxx"
 #include <SolverSettings.hxx>
 #include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/table/CellAddress.hpp>
 
 #include <string_view>
 #include <vector>
@@ -157,6 +158,9 @@ private:
 
     static sc::ConstraintOperator OperatorIndexToConstraintOperator(sal_Int32 
nIndex);
 
+    // Return the string representation of a css::table::CellAddress
+    OUString GetCellStrAddress(css::table::CellAddress aUnoAddress);
+
     DECL_LINK( BtnHdl, weld::Button&, void );
     DECL_LINK( DelBtnHdl, weld::Button&, void );
     DECL_LINK( GetEditFocusHdl, formula::RefEdit&, void );
diff --git a/sc/source/ui/miscdlgs/optsolver.cxx 
b/sc/source/ui/miscdlgs/optsolver.cxx
index a406e361c998..40e7a87fa3e1 100644
--- a/sc/source/ui/miscdlgs/optsolver.cxx
+++ b/sc/source/ui/miscdlgs/optsolver.cxx
@@ -38,11 +38,14 @@
 #include <comphelper/sequence.hxx>
 #include <optsolver.hxx>
 #include <table.hxx>
+#include <TableFillingAndNavigationTools.hxx>
 
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
 #include <com/sun/star/sheet/SolverConstraint.hpp>
 #include <com/sun/star/sheet/SolverConstraintOperator.hpp>
 #include <com/sun/star/sheet/XSolverDescription.hpp>
 #include <com/sun/star/sheet/XSolver.hpp>
+#include <com/sun/star/sheet/SensitivityReport.hpp>
 
 using namespace com::sun::star;
 
@@ -916,6 +919,14 @@ bool ScOptSolverDlg::FindTimeout( sal_Int32& rTimeout )
     return bFound;
 }
 
+OUString ScOptSolverDlg::GetCellStrAddress(css::table::CellAddress aUnoAddress)
+{
+    ScAddress aScAddr;
+    ScUnoConversion::FillScAddress(aScAddr, aUnoAddress);
+    ScRange aRange(aScAddr);
+    return aRange.Format(mrDoc, ScRefFlags::RANGE_ABS);
+}
+
 bool ScOptSolverDlg::CallSolver()       // return true -> close dialog after 
calling
 {
     // show progress dialog
@@ -1189,6 +1200,147 @@ bool ScOptSolverDlg::CallSolver()       // return true 
-> close dialog after cal
         mpDocShell->UnlockPaint();
     }
 
+    // Generate sensitivity report if user wants it
+    uno::Reference<css::beans::XPropertySetInfo> xInfo = 
xOptProp->getPropertySetInfo();
+    bool bUserWantsReport = false;
+    if (xInfo->hasPropertyByName("GenSensitivityReport"))
+        xOptProp->getPropertyValue("GenSensitivityReport") >>= 
bUserWantsReport;
+
+    if (bSuccess && bUserWantsReport)
+    {
+        // Retrieve the sensitivity analysis report
+        css::sheet::SensitivityReport aSensitivity;
+        bool bHasReportObj = xOptProp->getPropertyValue("SensitivityReport") 
>>= aSensitivity;
+
+        if (bHasReportObj && aSensitivity.HasReport)
+        {
+            // Define the Tab name where the sensitivity analysis will be 
written to
+            OUString sNewTabName;
+            SCTAB nNewTab;
+            mrDoc.GetName(mnCurTab, sNewTabName);
+            sNewTabName += "_" + ScResId(STR_SENSITIVITY);
+            // Chech if the new Tab name exists
+            if (mrDoc.GetTable(sNewTabName, nNewTab))
+            {
+                // Add numbers to the end of the Tab name to make it unique
+                SCTAB i = 1;
+                OUString aName;
+                do
+                {
+                    i++;
+                    aName = sNewTabName + "_" + 
OUString::number(static_cast<sal_Int32>(i));
+                }
+                while(mrDoc.GetTable(aName, nNewTab));
+                sNewTabName = aName;
+            }
+
+            // Insert new sheet to the document and start writing the report
+            ScDocFunc &rFunc = mpDocShell->GetDocFunc();
+            rFunc.InsertTable(mnCurTab + 1, sNewTabName, false, false);
+            SCTAB nReportTab;
+            mrDoc.GetTable(sNewTabName, nReportTab);
+
+            // Used to input data in the new sheet
+            ScAddress aOutputAddress(0, 0, nReportTab);
+            ScAddress::Details mAddressDetails(mrDoc, aOutputAddress);
+            AddressWalkerWriter aOutput(aOutputAddress, mpDocShell, mrDoc,
+                                        
formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH, 
mAddressDetails.eConv));
+            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_TITLE));
+            aOutput.newLine();
+            aOutput.writeString(ScResId(STR_SOLVER_ENGINE) + " " + maEngine);
+            aOutput.newLine();
+            aOutput.newLine();
+
+            // Objective cell section
+            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_OBJCELL));
+            aOutput.newLine();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
+            aOutput.newLine();
+            aOutput.writeString(GetCellStrAddress(xSolver->getObjective()));
+            aOutput.nextColumn();
+            aOutput.writeValue(xSolver->getResultValue());
+            aOutput.newLine();
+            aOutput.newLine();
+
+            // Variable cell section
+            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_VARCELLS));
+            aOutput.newLine();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_REDUCED));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_OBJCOEFF));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
+            aOutput.newLine();
+
+            uno::Sequence<double> aSolution = xSolver->getSolution();
+            uno::Sequence<double> aObjCoefficients = 
aSensitivity.ObjCoefficients;
+            uno::Sequence<double> aObjReducedCosts = 
aSensitivity.ObjReducedCosts;
+            uno::Sequence<double> aObjAllowableDecreases = 
aSensitivity.ObjAllowableDecreases;
+            uno::Sequence<double> aObjAllowableIncreases = 
aSensitivity.ObjAllowableIncreases;
+            for (sal_Int32 i = 0; i < aVariables.getLength(); i++)
+            {
+                aOutput.writeString(GetCellStrAddress(aVariables[i]));
+                aOutput.nextColumn();
+                aOutput.writeValue(aSolution[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aObjReducedCosts[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aObjCoefficients[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aObjAllowableDecreases[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aObjAllowableIncreases[i]);
+                aOutput.newLine();
+            }
+            aOutput.newLine();
+
+            // Constraints section
+            aOutput.writeBoldString(ScResId(STR_SENSITIVITY_CONSTRAINTS));
+            aOutput.newLine();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_CELL));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_FINALVALUE));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_SHADOWPRICE));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_RHS));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_DECREASE));
+            aOutput.nextColumn();
+            aOutput.writeString(ScResId(STR_SENSITIVITY_INCREASE));
+            aOutput.newLine();
+
+            uno::Sequence<double> aConstrValues = aSensitivity.ConstrValues;
+            uno::Sequence<double> aConstrRHS = aSensitivity.ConstrRHS;
+            uno::Sequence<double> aConstrShadowPrices = 
aSensitivity.ConstrShadowPrices;
+            uno::Sequence<double> aConstrAllowableDecreases = 
aSensitivity.ConstrAllowableDecreases;
+            uno::Sequence<double> aConstrAllowableIncreases = 
aSensitivity.ConstrAllowableIncreases;
+            for (sal_Int32 i = 0; i < aConstraints.getLength(); i++)
+            {
+                aOutput.writeString(GetCellStrAddress(aConstraints[i].Left));
+                aOutput.nextColumn();
+                aOutput.writeValue(aConstrValues[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aConstrShadowPrices[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aConstrRHS[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aConstrAllowableDecreases[i]);
+                aOutput.nextColumn();
+                aOutput.writeValue(aConstrAllowableIncreases[i]);
+                aOutput.newLine();
+            }
+        }
+    }
+
     return bClose;
 }
 
diff --git a/sccomp/inc/strings.hrc b/sccomp/inc/strings.hrc
index b1b8506c3908..c7826cc228ac 100644
--- a/sccomp/inc/strings.hrc
+++ b/sccomp/inc/strings.hrc
@@ -30,6 +30,7 @@
 #define RID_PROPERTY_EPSILONLEVEL   NC_("RID_PROPERTY_EPSILONLEVEL", "Epsilon 
level (0-3)")
 #define RID_PROPERTY_LIMITBBDEPTH   NC_("RID_PROPERTY_LIMITBBDEPTH", "Limit 
branch-and-bound depth")
 #define RID_PROPERTY_ALGORITHM      NC_("RID_PROPERTY_ALGORITHM", "Swarm 
algorithm (0 - Differential Evolution, 1 - Particle Swarm Optimization)")
+#define RID_PROPERTY_SENSITIVITY    NC_("RID_PROPERTY_SENSITIVITY", "Generate 
sensitivity report")
 #define RID_ERROR_NONLINEAR         NC_("RID_ERROR_NONLINEAR", "The model is 
not linear.")
 #define RID_ERROR_EPSILONLEVEL      NC_("RID_ERROR_EPSILONLEVEL", "The epsilon 
level is invalid.")
 #define RID_ERROR_INFEASIBLE        NC_("RID_ERROR_INFEASIBLE", "The model is 
infeasible. Check limiting conditions.")
diff --git a/sccomp/source/solver/LpsolveSolver.cxx 
b/sccomp/source/solver/LpsolveSolver.cxx
index e80bd87e1a41..70a9239c5083 100644
--- a/sccomp/source/solver/LpsolveSolver.cxx
+++ b/sccomp/source/solver/LpsolveSolver.cxx
@@ -37,6 +37,7 @@
  ************************************************************************/
 
 #include <sal/config.h>
+#include <sal/log.hxx>
 
 #undef LANGUAGE_NONE
 #if defined _WIN32
@@ -107,6 +108,10 @@ void SAL_CALL LpsolveSolver::solve()
     size_t nVariables = aVariableCells.size();
     size_t nVar = 0;
 
+    // Store all RHS values
+    sal_uInt32 nConstraints = maConstraints.size();
+    m_aConstrRHS.realloc(nConstraints);
+
     // collect all dependent cells
 
     ScSolverCellHashMap aCellsHash;
@@ -197,6 +202,9 @@ void SAL_CALL LpsolveSolver::solve()
 
     set_add_rowmode(lp, TRUE);
 
+    sal_uInt32 nConstrCount(0);
+    double* pConstrRHS = m_aConstrRHS.getArray();
+
     for (const auto& rConstr : maConstraints)
     {
         // integer constraints are set later
@@ -237,6 +245,9 @@ void SAL_CALL LpsolveSolver::solve()
             else
                 fRightValue += fDirectValue;
 
+            // Remember the RHS value used for sensitivity analysis later
+            pConstrRHS[nConstrCount] = fRightValue;
+
             int nConstrType = LE;
             switch ( eOp )
             {
@@ -247,6 +258,7 @@ void SAL_CALL LpsolveSolver::solve()
                     OSL_FAIL( "unexpected enum type" );
             }
             add_constraint( lp, pValues.get(), nConstrType, fRightValue );
+            nConstrCount++;
         }
     }
 
@@ -311,6 +323,112 @@ void SAL_CALL LpsolveSolver::solve()
         std::copy_n(pResultVar, nVariables, maSolution.getArray());
 
         mfResultValue = get_objective( lp );
+
+        // Initially set to false because getting the report might fail
+        m_aSensitivityReport.HasReport = false;
+
+        // Get sensitivity report if the user set SensitivityReport parameter 
to true
+        if (mbGenSensitivity)
+        {
+            // Get sensitivity data about the objective function
+            // LpSolve returns an interval for the coefficients of the 
objective function
+            // instead of returning an allowable increase/decrease (which is 
what we want to show
+            // in the sensitivity report; so we these from/till values are 
converted into increase
+            // and decrease values later)
+            REAL* pObjFrom = nullptr;
+            REAL* pObjTill = nullptr;
+            bool bHasObjReport = false;
+            bHasObjReport = get_ptr_sensitivity_obj(lp, &pObjFrom, &pObjTill);
+
+            // Get sensitivity data about constraints
+            // Similarly to the objective function, the sensitivity values 
returned for the
+            // constraints are in the form from/till and are later converted 
to increase and
+            // decrease values later
+            REAL* pConstrValue = nullptr;
+            REAL* pConstrDual = nullptr;
+            REAL* pConstrFrom = nullptr;
+            REAL* pConstrTill = nullptr;
+            bool bHasConstrReport = false;
+            bHasConstrReport = get_ptr_sensitivity_rhs(lp, &pConstrDual, 
&pConstrFrom, &pConstrTill);
+
+            // When successfull, store sensitivity data in the solver component
+            if (bHasObjReport && bHasConstrReport)
+            {
+                m_aSensitivityReport.HasReport = true;
+                m_aObjDecrease.realloc(nVariables);
+                m_aObjIncrease.realloc(nVariables);
+                double* pObjDecrease = m_aObjDecrease.getArray();
+                double* pObjIncrease = m_aObjIncrease.getArray();
+                for (size_t i = 0; i < nVariables; i++)
+                {
+                    // Allowed decrease. Note that the indices of rObjCoeff 
are offset by 1
+                    // because of the objective function
+                    if (static_cast<bool>(is_infinite(lp, pObjFrom[i])))
+                        pObjDecrease[i] = get_infinite(lp);
+                    else
+                        pObjDecrease[i] = rObjCoeff[i + 1] - pObjFrom[i];
+
+                    // Allowed increase
+                    if (static_cast<bool>(is_infinite(lp, pObjTill[i])))
+                        pObjIncrease[i] = get_infinite(lp);
+                    else
+                        pObjIncrease[i] = pObjTill[i] - rObjCoeff[i + 1];
+                }
+
+                // Save objective coefficients for the sensitivity report
+                double* pObjCoefficients(new double[nVariables]);
+                for (size_t i = 0; i < nVariables; i++)
+                    pObjCoefficients[i] = rObjCoeff[i + 1];
+                m_aObjCoefficients.realloc(nVariables);
+                std::copy_n(pObjCoefficients, nVariables, 
m_aObjCoefficients.getArray());
+
+                // The reduced costs are in pConstrDual after the constraints
+                double* pObjRedCost(new double[nVariables]);
+                for (size_t i = 0; i < nVariables; i++)
+                    pObjRedCost[i] = pConstrDual[nConstraints + i];
+                m_aObjRedCost.realloc(nVariables);
+                std::copy_n(pObjRedCost, nVariables, m_aObjRedCost.getArray());
+
+                // Final value of constraints
+                get_ptr_constraints(lp, &pConstrValue);
+                m_aConstrValue.realloc(nConstraints);
+                std::copy_n(pConstrValue, nConstraints, 
m_aConstrValue.getArray());
+
+                // The RHS contains information for each constraint
+                m_aConstrDual.realloc(nConstraints);
+                m_aConstrDecrease.realloc(nConstraints);
+                m_aConstrIncrease.realloc(nConstraints);
+                std::copy_n(pConstrDual, nConstraints, 
m_aConstrDual.getArray());
+                double* pConstrDecrease = m_aConstrDecrease.getArray();
+                double* pConstrIncrease = m_aConstrIncrease.getArray();
+
+                for (sal_uInt32 i = 0; i < nConstraints; i++)
+                {
+                    // Allowed decrease
+                    pConstrDecrease[i] = m_aConstrRHS[i] - pConstrFrom[i];
+                    if (static_cast<bool>(is_infinite(lp, pConstrFrom[i]))
+                        && maConstraints[i].Operator == 
sheet::SolverConstraintOperator_LESS_EQUAL)
+                        pConstrDecrease[i] = m_aConstrRHS[i] - 
m_aConstrValue[i];
+
+                    // Allowed increase
+                    pConstrIncrease[i] = pConstrTill[i] - m_aConstrRHS[i];
+                    if (static_cast<bool>(is_infinite(lp, pConstrTill[i]))
+                        && maConstraints[i].Operator == 
sheet::SolverConstraintOperator_GREATER_EQUAL)
+                        pConstrIncrease[i] = m_aConstrValue[i] - 
m_aConstrRHS[i];
+                }
+
+                // Set all values of the SensitivityReport object
+                m_aSensitivityReport.ObjCoefficients = m_aObjCoefficients;
+                m_aSensitivityReport.ObjReducedCosts = m_aObjRedCost;
+                m_aSensitivityReport.ObjAllowableDecreases = m_aObjDecrease;
+                m_aSensitivityReport.ObjAllowableIncreases = m_aObjIncrease;
+                m_aSensitivityReport.ConstrValues = m_aConstrValue;
+                m_aSensitivityReport.ConstrRHS = m_aConstrRHS;
+                m_aSensitivityReport.ConstrShadowPrices = m_aConstrDual;
+                m_aSensitivityReport.ConstrAllowableDecreases = 
m_aConstrDecrease;
+                m_aSensitivityReport.ConstrAllowableIncreases = 
m_aConstrIncrease;
+            }
+        }
     }
     else if ( nResult == INFEASIBLE )
         maStatus = SolverComponent::GetResourceString( RID_ERROR_INFEASIBLE );
diff --git a/sccomp/source/solver/SolverComponent.cxx 
b/sccomp/source/solver/SolverComponent.cxx
index b7038090e56c..a06b65f4c46a 100644
--- a/sccomp/source/solver/SolverComponent.cxx
+++ b/sccomp/source/solver/SolverComponent.cxx
@@ -37,6 +37,8 @@ constexpr OUStringLiteral STR_INTEGER = u"Integer";
 constexpr OUStringLiteral STR_TIMEOUT = u"Timeout";
 constexpr OUStringLiteral STR_EPSILONLEVEL = u"EpsilonLevel";
 constexpr OUStringLiteral STR_LIMITBBDEPTH = u"LimitBBDepth";
+constexpr OUStringLiteral STR_GEN_SENSITIVITY = u"GenSensitivityReport";
+constexpr OUStringLiteral STR_SENSITIVITY_REPORT = u"SensitivityReport";
 
 
 //  Resources from tools are used for translated strings
@@ -64,7 +66,9 @@ namespace
         PROP_INTEGER,
         PROP_TIMEOUT,
         PROP_EPSILONLEVEL,
-        PROP_LIMITBBDEPTH
+        PROP_LIMITBBDEPTH,
+        PROP_GEN_SENSITIVITY,
+        PROP_SENSITIVITY_REPORT
     };
 }
 
@@ -95,15 +99,20 @@ SolverComponent::SolverComponent() :
     mnTimeout( 100 ),
     mnEpsilonLevel( 0 ),
     mbLimitBBDepth( true ),
+    mbGenSensitivity(false),
     mbSuccess( false ),
     mfResultValue( 0.0 )
 {
     // for XPropertySet implementation:
-    registerProperty( STR_NONNEGATIVE,  PROP_NONNEGATIVE,  0, &mbNonNegative,  
cppu::UnoType<decltype(mbNonNegative)>::get()  );
-    registerProperty( STR_INTEGER,      PROP_INTEGER,      0, &mbInteger,      
cppu::UnoType<decltype(mbInteger)>::get()      );
-    registerProperty( STR_TIMEOUT,      PROP_TIMEOUT,      0, &mnTimeout,      
cppu::UnoType<decltype(mnTimeout)>::get()      );
-    registerProperty( STR_EPSILONLEVEL, PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, 
cppu::UnoType<decltype(mnEpsilonLevel)>::get() );
-    registerProperty( STR_LIMITBBDEPTH, PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, 
cppu::UnoType<decltype(mbLimitBBDepth)>::get() );
+    registerProperty(STR_NONNEGATIVE,  PROP_NONNEGATIVE,  0, &mbNonNegative,   
 cppu::UnoType<decltype(mbNonNegative)>::get());
+    registerProperty(STR_INTEGER,      PROP_INTEGER,      0, &mbInteger,       
 cppu::UnoType<decltype(mbInteger)>::get());
+    registerProperty(STR_TIMEOUT,      PROP_TIMEOUT,      0, &mnTimeout,       
 cppu::UnoType<decltype(mnTimeout)>::get());
+    registerProperty(STR_EPSILONLEVEL, PROP_EPSILONLEVEL, 0, &mnEpsilonLevel,  
 cppu::UnoType<decltype(mnEpsilonLevel)>::get());
+    registerProperty(STR_LIMITBBDEPTH, PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth,  
 cppu::UnoType<decltype(mbLimitBBDepth)>::get());
+    registerProperty(STR_GEN_SENSITIVITY, PROP_GEN_SENSITIVITY, 0, 
&mbGenSensitivity, cppu::UnoType<decltype(mbGenSensitivity)>::get());
+
+    // Sensitivity report
+    registerProperty(STR_SENSITIVITY_REPORT, PROP_SENSITIVITY_REPORT, 0, 
&m_aSensitivityReport, cppu::UnoType<decltype(m_aSensitivityReport)>::get());
 }
 
 SolverComponent::~SolverComponent()
@@ -158,6 +167,9 @@ OUString SAL_CALL SolverComponent::getPropertyDescription( 
const OUString& rProp
         case PROP_LIMITBBDEPTH:
             pResId = RID_PROPERTY_LIMITBBDEPTH;
             break;
+        case PROP_GEN_SENSITIVITY:
+            pResId = RID_PROPERTY_SENSITIVITY;
+            break;
         default:
             {
                 // unknown - leave empty
diff --git a/sccomp/source/solver/SolverComponent.hxx 
b/sccomp/source/solver/SolverComponent.hxx
index 7b5ff1dd49f0..543eeedea282 100644
--- a/sccomp/source/solver/SolverComponent.hxx
+++ b/sccomp/source/solver/SolverComponent.hxx
@@ -21,6 +21,7 @@
 
 #include <com/sun/star/sheet/XSolver.hpp>
 #include <com/sun/star/sheet/XSolverDescription.hpp>
+#include <com/sun/star/sheet/SensitivityReport.hpp>
 #include <com/sun/star/table/CellAddress.hpp>
 #include <com/sun/star/lang/XServiceInfo.hpp>
 #include <cppuhelper/implbase.hxx>
@@ -76,12 +77,25 @@ protected:
     sal_Int32                                               mnTimeout;
     sal_Int32                                               mnEpsilonLevel;
     bool                                                    mbLimitBBDepth;
+    bool                                                    mbGenSensitivity;
     // results
     bool                                                    mbSuccess;
     double                                                  mfResultValue;
     css::uno::Sequence< double >                            maSolution;
     OUString                                                maStatus;
 
+    // Sensitivity report
+    css::uno::Sequence<double> m_aObjCoefficients;
+    css::uno::Sequence<double> m_aObjDecrease;
+    css::uno::Sequence<double> m_aObjIncrease;
+    css::uno::Sequence<double> m_aObjRedCost;
+    css::uno::Sequence<double> m_aConstrValue;
+    css::uno::Sequence<double> m_aConstrRHS;
+    css::uno::Sequence<double> m_aConstrDual;
+    css::uno::Sequence<double> m_aConstrIncrease;
+    css::uno::Sequence<double> m_aConstrDecrease;
+    css::sheet::SensitivityReport m_aSensitivityReport;
+
     static OUString GetResourceString(TranslateId aId);
     static css::uno::Reference<css::table::XCell> GetCell(
             const css::uno::Reference<css::sheet::XSpreadsheetDocument>& xDoc,

Reply via email to