summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/dbgui/scuiasciiopt.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/ui/dbgui/scuiasciiopt.cxx')
-rw-r--r--sc/source/ui/dbgui/scuiasciiopt.cxx975
1 files changed, 975 insertions, 0 deletions
diff --git a/sc/source/ui/dbgui/scuiasciiopt.cxx b/sc/source/ui/dbgui/scuiasciiopt.cxx
new file mode 100644
index 0000000000..601323a658
--- /dev/null
+++ b/sc/source/ui/dbgui/scuiasciiopt.cxx
@@ -0,0 +1,975 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#undef SC_DLLIMPLEMENTATION
+
+#include <svx/txencbox.hxx>
+
+#include <global.hxx>
+#include <scresid.hxx>
+#include <impex.hxx>
+#include <scuiasciiopt.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <csvtablebox.hxx>
+#include <osl/thread.h>
+#include <unotools/transliterationwrapper.hxx>
+
+#include <optutil.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <miscuno.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <unicode/ucsdet.h>
+
+//! TODO make dynamic
+const SCSIZE ASCIIDLG_MAXROWS = MAXROWCOUNT;
+
+// Maximum number of source lines to concatenate while generating the preview
+// for one logical line. This may result in a wrong preview if the actual
+// number of embedded line feeds is greater, but a number too high would take
+// too much time (loop excessively if unlimited and large data) if none of the
+// selected separators are actually used in data but a field at start of line
+// is quoted.
+constexpr sal_uInt32 kMaxEmbeddedLinefeeds = 500;
+
+using namespace com::sun::star::uno;
+
+namespace {
+
+// Defines - CSV Import Preserve Options
+// For usage of index order see lcl_CreatePropertiesNames() below.
+enum CSVImportOptionsIndex
+{
+ CSVIO_MergeDelimiters = 0,
+ CSVIO_Separators,
+ CSVIO_TextSeparators,
+ CSVIO_FixedWidth,
+ CSVIO_RemoveSpace,
+ CSVIO_EvaluateFormulas,
+ // Settings for *all* dialog invocations above.
+ // Settings not for SC_TEXTTOCOLUMNS below.
+ CSVIO_FromRow,
+ CSVIO_Text2ColSkipEmptyCells = CSVIO_FromRow,
+ CSVIO_CharSet,
+ CSVIO_QuotedAsText,
+ CSVIO_DetectSpecialNum,
+ CSVIO_DetectScientificNum,
+ CSVIO_Language,
+ // Plus one not for SC_IMPORTFILE.
+ CSVIO_PasteSkipEmptyCells
+};
+
+}
+
+// Config items for all three paths are defined in
+// officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+// If not, options are neither loaded nor saved.
+const ::std::vector<OUString> CSVImportOptionNames =
+{
+ "MergeDelimiters",
+ "Separators",
+ "TextSeparators",
+ "FixedWidth",
+ "RemoveSpace",
+ "EvaluateFormulas",
+ "FromRow",
+ "CharSet",
+ "QuotedFieldAsText",
+ "DetectSpecialNumbers",
+ "DetectScientificNumbers",
+ "Language",
+ "SkipEmptyCells"
+};
+constexpr OUStringLiteral aSep_Path = u"Office.Calc/Dialogs/CSVImport";
+constexpr OUStringLiteral aSep_Path_Clpbrd = u"Office.Calc/Dialogs/ClipboardTextImport";
+constexpr OUStringLiteral aSep_Path_Text2Col = u"Office.Calc/Dialogs/TextToColumnsImport";
+
+namespace {
+CSVImportOptionsIndex getSkipEmptyCellsIndex( ScImportAsciiCall eCall )
+{
+ return eCall == SC_TEXTTOCOLUMNS ? CSVIO_Text2ColSkipEmptyCells : CSVIO_PasteSkipEmptyCells;
+}
+}
+
+static void lcl_FillCombo(weld::ComboBox& rCombo, std::u16string_view rList, sal_Unicode cSelect)
+{
+ OUString aStr;
+ if (!rList.empty())
+ {
+ sal_Int32 nIdx {0};
+ do
+ {
+ const OUString sEntry {o3tl::getToken(rList, 0, '\t', nIdx)};
+ rCombo.append_text(sEntry);
+ if (nIdx>0 && static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nIdx))) == cSelect)
+ aStr = sEntry;
+ }
+ while (nIdx>0);
+ }
+
+ if ( cSelect )
+ {
+ if (aStr.isEmpty())
+ aStr = OUString(cSelect); // Ascii
+
+ rCombo.set_entry_text(aStr);
+ }
+}
+
+static sal_Unicode lcl_CharFromCombo(const weld::ComboBox& rCombo, std::u16string_view rList)
+{
+ sal_Unicode c = 0;
+ OUString aStr = rCombo.get_active_text();
+ if ( !aStr.isEmpty() && !rList.empty() )
+ {
+ sal_Int32 nIdx {0};
+ OUString sToken {o3tl::getToken(rList, 0, '\t', nIdx)};
+ while (nIdx>0)
+ {
+ if ( ScGlobal::GetTransliteration().isEqual( aStr, sToken ) )
+ {
+ sal_Int32 nTmpIdx {nIdx};
+ c = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nTmpIdx)));
+ }
+ // Skip to next token at even position
+ sToken = o3tl::getToken(rList, 1, '\t', nIdx);
+ }
+ if (!c)
+ {
+ sal_Unicode cFirst = aStr[0];
+ // #i24235# first try the first character of the string directly
+ if( (aStr.getLength() == 1) || (cFirst < '0') || (cFirst > '9') )
+ c = cFirst;
+ else // keep old behaviour for compatibility (i.e. "39" -> "'")
+ c = static_cast<sal_Unicode>(aStr.toInt32()); // Ascii
+ }
+ }
+ return c;
+}
+
+static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence<OUString>& rNames, ScImportAsciiCall eCall )
+{
+ sal_Int32 nProperties = 0;
+
+ switch(eCall)
+ {
+ case SC_IMPORTFILE:
+ rSepPath = aSep_Path;
+ nProperties = 12;
+ break;
+ case SC_PASTETEXT:
+ rSepPath = aSep_Path_Clpbrd;
+ nProperties = 13;
+ break;
+ case SC_TEXTTOCOLUMNS:
+ default:
+ rSepPath = aSep_Path_Text2Col;
+ nProperties = 7;
+ break;
+ }
+ rNames.realloc( nProperties );
+ OUString* pNames = rNames.getArray();
+ pNames[ CSVIO_MergeDelimiters ] = CSVImportOptionNames[ CSVIO_MergeDelimiters ];
+ pNames[ CSVIO_Separators ] = CSVImportOptionNames[ CSVIO_Separators ];
+ pNames[ CSVIO_TextSeparators ] = CSVImportOptionNames[ CSVIO_TextSeparators ];
+ pNames[ CSVIO_FixedWidth ] = CSVImportOptionNames[ CSVIO_FixedWidth ];
+ pNames[ CSVIO_RemoveSpace ] = CSVImportOptionNames[ CSVIO_RemoveSpace ];
+ pNames[ CSVIO_EvaluateFormulas ] = CSVImportOptionNames[ CSVIO_EvaluateFormulas ];
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pNames[ CSVIO_FromRow ] = CSVImportOptionNames[ CSVIO_FromRow ];
+ pNames[ CSVIO_CharSet ] = CSVImportOptionNames[ CSVIO_CharSet ];
+ pNames[ CSVIO_QuotedAsText ] = CSVImportOptionNames[ CSVIO_QuotedAsText ];
+ pNames[ CSVIO_DetectSpecialNum ] = CSVImportOptionNames[ CSVIO_DetectSpecialNum ];
+ pNames[ CSVIO_DetectScientificNum ] = CSVImportOptionNames[ CSVIO_DetectScientificNum ];
+ pNames[ CSVIO_Language ] = CSVImportOptionNames[ CSVIO_Language ];
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < rNames.getLength());
+ pNames[ nSkipEmptyCells ] = CSVImportOptionNames[ CSVIO_PasteSkipEmptyCells ];
+ }
+}
+
+static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& rTextSeparators,
+ bool& rMergeDelimiters, bool& rQuotedAsText, bool& rDetectSpecialNum, bool& rDetectScientificNum,
+ bool& rFixedWidth, sal_Int32& rFromRow, sal_Int32& rCharSet,
+ sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace,
+ bool& rEvaluateFormulas, ScImportAsciiCall eCall )
+{
+ Sequence<Any>aValues;
+ const Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall);
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getConstArray();
+
+ if( pProperties[ CSVIO_MergeDelimiters ].hasValue() )
+ rMergeDelimiters = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_MergeDelimiters ] );
+
+ if( pProperties[ CSVIO_RemoveSpace ].hasValue() )
+ rRemoveSpace = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_RemoveSpace ] );
+
+ if( pProperties[ CSVIO_Separators ].hasValue() )
+ pProperties[ CSVIO_Separators ] >>= rFieldSeparators;
+
+ if( pProperties[ CSVIO_TextSeparators ].hasValue() )
+ pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
+
+ if( pProperties[ CSVIO_FixedWidth ].hasValue() )
+ rFixedWidth = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_FixedWidth ] );
+
+ if( pProperties[ CSVIO_EvaluateFormulas ].hasValue() )
+ rEvaluateFormulas = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_EvaluateFormulas ] );
+
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ if( pProperties[ CSVIO_FromRow ].hasValue() )
+ pProperties[ CSVIO_FromRow ] >>= rFromRow;
+
+ if( pProperties[ CSVIO_CharSet ].hasValue() )
+ pProperties[ CSVIO_CharSet ] >>= rCharSet;
+
+ if ( pProperties[ CSVIO_QuotedAsText ].hasValue() )
+ pProperties[ CSVIO_QuotedAsText ] >>= rQuotedAsText;
+
+ if ( pProperties[ CSVIO_DetectSpecialNum ].hasValue() )
+ pProperties[ CSVIO_DetectSpecialNum ] >>= rDetectSpecialNum;
+
+ if ( pProperties[ CSVIO_DetectScientificNum ].hasValue() )
+ pProperties[ CSVIO_DetectScientificNum ] >>= rDetectScientificNum;
+
+ if ( pProperties[ CSVIO_Language ].hasValue() )
+ pProperties[ CSVIO_Language ] >>= rLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ if ( pProperties[nSkipEmptyCells].hasValue() )
+ rSkipEmptyCells = ScUnoHelpFunctions::GetBoolFromAny( pProperties[nSkipEmptyCells] );
+ }
+}
+
+static void lcl_SaveSeparators(
+ const OUString& sFieldSeparators, const OUString& sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText,
+ bool bDetectSpecialNum, bool bDetectScientificNum, bool bFixedWidth, sal_Int32 nFromRow,
+ sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, bool bEvaluateFormulas,
+ ScImportAsciiCall eCall )
+{
+ Sequence<Any> aValues;
+ Any *pProperties;
+ Sequence<OUString> aNames;
+ OUString aSepPath;
+ lcl_CreatePropertiesNames ( aSepPath, aNames, eCall );
+ ScLinkConfigItem aItem( aSepPath );
+ aValues = aItem.GetProperties( aNames );
+ pProperties = aValues.getArray();
+
+ pProperties[ CSVIO_MergeDelimiters ] <<= bMergeDelimiters;
+ pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
+ pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
+ pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
+ pProperties[ CSVIO_FixedWidth ] <<= bFixedWidth;
+ pProperties[ CSVIO_EvaluateFormulas ] <<= bEvaluateFormulas;
+ if (eCall != SC_TEXTTOCOLUMNS)
+ {
+ pProperties[ CSVIO_FromRow ] <<= nFromRow;
+ pProperties[ CSVIO_CharSet ] <<= nCharSet;
+ pProperties[ CSVIO_QuotedAsText ] <<= bQuotedAsText;
+ pProperties[ CSVIO_DetectSpecialNum ] <<= bDetectSpecialNum;
+ pProperties[ CSVIO_DetectScientificNum ] <<= bDetectScientificNum;
+ pProperties[ CSVIO_Language ] <<= nLanguage;
+ }
+ if (eCall != SC_IMPORTFILE)
+ {
+ const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
+ assert( nSkipEmptyCells < aValues.getLength());
+ pProperties[ nSkipEmptyCells ] <<= bSkipEmptyCells;
+ }
+
+ aItem.PutProperties(aNames, aValues);
+}
+
+ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, std::u16string_view aDatName,
+ SvStream* pInStream, ScImportAsciiCall eCall)
+ : GenericDialogController(pParent, "modules/scalc/ui/textimportcsv.ui", "TextImportCsvDialog")
+ , mpDatStream(pInStream)
+ , mnStreamPos(pInStream ? pInStream->Tell() : 0)
+ , mnRowPosCount(0)
+ , mcTextSep(ScAsciiOptions::cDefaultTextSep)
+ , meCall(eCall)
+ , mbDetectSep(eCall != SC_TEXTTOCOLUMNS)
+ , mxFtCharSet(m_xBuilder->weld_label("textcharset"))
+ , mxLbCharSet(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charset")))
+ , mxFtCustomLang(m_xBuilder->weld_label("textlanguage"))
+ , mxLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
+ , mxFtRow(m_xBuilder->weld_label("textfromrow"))
+ , mxNfRow(m_xBuilder->weld_spin_button("fromrow"))
+ , mxRbFixed(m_xBuilder->weld_radio_button("tofixedwidth"))
+ , mxRbSeparated(m_xBuilder->weld_radio_button("toseparatedby"))
+ , mxCkbTab(m_xBuilder->weld_check_button("tab"))
+ , mxCkbSemicolon(m_xBuilder->weld_check_button("semicolon"))
+ , mxCkbComma(m_xBuilder->weld_check_button("comma"))
+ , mxCkbRemoveSpace(m_xBuilder->weld_check_button("removespace"))
+ , mxCkbSpace(m_xBuilder->weld_check_button("space"))
+ , mxCkbOther(m_xBuilder->weld_check_button("other"))
+ , mxEdOther(m_xBuilder->weld_entry("inputother"))
+ , mxCkbAsOnce(m_xBuilder->weld_check_button("mergedelimiters"))
+ , mxFtTextSep(m_xBuilder->weld_label("texttextdelimiter"))
+ , mxCbTextSep(m_xBuilder->weld_combo_box("textdelimiter"))
+ , mxCkbQuotedAsText(m_xBuilder->weld_check_button("quotedfieldastext"))
+ , mxCkbDetectNumber(m_xBuilder->weld_check_button("detectspecialnumbers"))
+ , mxCkbDetectScientificNumber(m_xBuilder->weld_check_button("detectscientificnumbers"))
+ , mxCkbEvaluateFormulas(m_xBuilder->weld_check_button("evaluateformulas"))
+ , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button("skipemptycells"))
+ , mxLbType(m_xBuilder->weld_combo_box("columntype"))
+ , mxAltTitle(m_xBuilder->weld_label("textalttitle"))
+ , mxTableBox(new ScCsvTableBox(*m_xBuilder))
+{
+ OUString aName = m_xDialog->get_title();
+ switch (meCall)
+ {
+ case SC_TEXTTOCOLUMNS:
+ m_xDialog->set_title(mxAltTitle->get_label());
+ break;
+ case SC_IMPORTFILE:
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ aName += OUString::Concat(" - [") + aDatName + "]";
+ m_xDialog->set_title(aName);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // To be able to prefill the correct values based on the file extension
+ bool bIsTSV = (o3tl::endsWithIgnoreAsciiCase(aDatName, ".tsv") || o3tl::endsWithIgnoreAsciiCase(aDatName, ".tab"));
+
+ // Default options are set in officecfg/registry/schema/org/openoffice/Office/Calc.xcs
+ OUString sFieldSeparators(",;\t");
+ OUString sTextSeparators(mcTextSep);
+ bool bMergeDelimiters = false;
+ bool bFixedWidth = false;
+ bool bQuotedFieldAsText = false;
+ bool bDetectSpecialNum = true;
+ bool bDetectScientificNum = true;
+ bool bEvaluateFormulas = (meCall != SC_IMPORTFILE);
+ bool bSkipEmptyCells = true;
+ bool bRemoveSpace = false;
+ sal_Int32 nFromRow = 1;
+ sal_Int32 nCharSet = -1;
+ sal_Int32 nLanguage = 0;
+ lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
+ bQuotedFieldAsText, bDetectSpecialNum, bDetectScientificNum, bFixedWidth, nFromRow,
+ nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, bEvaluateFormulas, meCall);
+ // load from saved settings
+ maFieldSeparators = sFieldSeparators;
+
+ if( bMergeDelimiters && !bIsTSV )
+ mxCkbAsOnce->set_active(true);
+ if (bQuotedFieldAsText)
+ mxCkbQuotedAsText->set_active(true);
+ if (bRemoveSpace)
+ mxCkbRemoveSpace->set_active(true);
+ if (bDetectSpecialNum)
+ {
+ mxCkbDetectNumber->set_active(true);
+ bDetectScientificNum = true;
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ if (bDetectScientificNum)
+ mxCkbDetectScientificNumber->set_active(true);
+ if (bEvaluateFormulas)
+ 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 = (meCall == SC_IMPORTFILE ?
+ RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
+ // Sniff for Unicode / not
+ if( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW && mpDatStream )
+ {
+ 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 )
+ {
+ 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 );
+ }
+
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ if (bIsTSV)
+ SetSeparators('\t');
+ 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);
+ }
+
+ // Get Separators from the dialog (empty are set from default)
+ maFieldSeparators = GetSeparators();
+
+ mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl ) );
+
+ // *** Separator characters ***
+ lcl_FillCombo( *mxCbTextSep, SCSTR_TEXTSEP, mcTextSep );
+ mxCbTextSep->set_entry_text(sTextSeparators);
+ // tdf#69207 - use selected text delimiter to parse the provided data
+ mcTextSep = lcl_CharFromCombo(*mxCbTextSep, SCSTR_TEXTSEP);
+
+ Link<weld::Toggleable&,void> aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl );
+ Link<weld::Toggleable&,void> aOtherOptionsClickHdl =LINK( this, ScImportAsciiDlg, OtherOptionsClickHdl );
+ mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) );
+ mxCkbTab->connect_toggled( aSeparatorClickHdl );
+ mxCkbSemicolon->connect_toggled( aSeparatorClickHdl );
+ mxCkbComma->connect_toggled( aSeparatorClickHdl );
+ mxCkbAsOnce->connect_toggled( aSeparatorClickHdl );
+ mxCkbSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl );
+ mxCkbOther->connect_toggled( aSeparatorClickHdl );
+ mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl));
+ mxCkbQuotedAsText->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbDetectNumber->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbDetectScientificNumber->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbEvaluateFormulas->connect_toggled( aOtherOptionsClickHdl );
+ mxCkbSkipEmptyCells->connect_toggled( aOtherOptionsClickHdl );
+
+ // *** text encoding ListBox ***
+ // all encodings allowed, including Unicode, but subsets are excluded
+ mxLbCharSet->FillFromTextEncodingTable( true );
+ // 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 );
+ }
+
+ if (nCharSet >= 0 && ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW)
+ mxLbCharSet->set_active(nCharSet);
+
+ SetSelectedCharSet();
+ mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
+
+ mxLbCustomLang->SetLanguageList(
+ SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false);
+ mxLbCustomLang->InsertLanguage(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_active_id(static_cast<LanguageType>(nLanguage));
+
+ // *** column type ListBox ***
+ OUString aColumnUser( ScResId( SCSTR_COLUMN_USER ) );
+ for (sal_Int32 nIdx {0}; nIdx>=0; )
+ {
+ mxLbType->append_text(aColumnUser.getToken(0, ';', nIdx));
+ }
+
+ mxLbType->connect_changed( LINK( this, ScImportAsciiDlg, LbColTypeHdl ) );
+ mxLbType->set_sensitive(false);
+
+ // *** table box preview ***
+ mxTableBox->Init();
+ mxTableBox->SetUpdateTextHdl( LINK( this, ScImportAsciiDlg, UpdateTextHdl ) );
+ mxTableBox->InitTypes( *mxLbType );
+ mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
+
+ mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+ mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
+
+ SetupSeparatorCtrls();
+ RbSepFix();
+
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+
+ if (meCall == SC_TEXTTOCOLUMNS)
+ {
+ mxFtCharSet->set_sensitive(false);
+ mxLbCharSet->set_sensitive(false);
+ mxFtCustomLang->set_sensitive(false);
+ mxLbCustomLang->set_active_id(LANGUAGE_SYSTEM);
+ mxLbCustomLang->set_sensitive(false);
+ mxFtRow->set_sensitive(false);
+ mxNfRow->set_sensitive(false);
+
+ // Quoted field as text option is not used for text-to-columns mode.
+ mxCkbQuotedAsText->set_active(false);
+ mxCkbQuotedAsText->set_sensitive(false);
+
+ // Always detect special numbers for text-to-columns mode.
+ mxCkbDetectNumber->set_active(true);
+ mxCkbDetectNumber->set_sensitive(false);
+ mxCkbDetectScientificNumber->set_active(true);
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ if (meCall == SC_IMPORTFILE)
+ {
+ //Empty cells in imported file are empty
+ mxCkbSkipEmptyCells->set_active(false);
+ mxCkbSkipEmptyCells->hide();
+ }
+ m_xDialog->SetInstallLOKNotifierHdl(LINK(this, ScImportAsciiDlg, InstallLOKNotifierHdl));
+}
+
+IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
+{
+ return GetpApp();
+}
+
+ScImportAsciiDlg::~ScImportAsciiDlg()
+{
+}
+
+bool ScImportAsciiDlg::GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep )
+{
+ if (nLine >= ASCIIDLG_MAXROWS || !mpDatStream)
+ return false;
+
+ bool bRet = true;
+ bool bFixed = mxRbFixed->get_active();
+
+ if (!mpRowPosArray)
+ mpRowPosArray.reset( new sal_uLong[ASCIIDLG_MAXROWS + 2] );
+
+ if (!mnRowPosCount) // complete re-fresh
+ {
+ memset( mpRowPosArray.get(), 0, sizeof(mpRowPosArray[0]) * (ASCIIDLG_MAXROWS+2));
+
+ Seek(0);
+ mpDatStream->StartReadingUnicodeText( mpDatStream->GetStreamCharSet() );
+
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[mnRowPosCount] = mnStreamPos;
+ }
+
+ if (nLine >= mnRowPosCount)
+ {
+ // need to work out some more line information
+ do
+ {
+ if (!Seek(mpRowPosArray[mnRowPosCount]) || !mpDatStream->good())
+ {
+ bRet = false;
+ break;
+ }
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators,
+ mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ mpRowPosArray[++mnRowPosCount] = mnStreamPos;
+ } while (nLine >= mnRowPosCount && mpDatStream->good());
+ if (mpDatStream->eof() &&
+ mnStreamPos == mpRowPosArray[mnRowPosCount-1])
+ {
+ // the very end, not even an empty line read
+ bRet = false;
+ --mnRowPosCount;
+ }
+ }
+ else
+ {
+ Seek( mpRowPosArray[nLine]);
+ rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators, mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
+ mnStreamPos = mpDatStream->Tell();
+ }
+
+ // If the file content isn't unicode, ReadUniStringLine
+ // may try to seek beyond the file's end and cause a CANTSEEK error
+ // (depending on the stream type). The error code has to be cleared,
+ // or further read operations (including non-unicode) will fail.
+ if ( mpDatStream->GetError() == ERRCODE_IO_CANTSEEK )
+ mpDatStream->ResetError();
+
+ ScImportExport::EmbeddedNullTreatment( rText);
+
+ return bRet;
+}
+
+void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
+{
+ rOpt.SetCharSet( meCharSet );
+ rOpt.SetCharSetSystem( mbCharSetSystem );
+ rOpt.SetLanguage(mxLbCustomLang->get_active_id());
+ rOpt.SetFixedLen( mxRbFixed->get_active() );
+ rOpt.SetStartRow( mxNfRow->get_value() );
+ mxTableBox->FillColumnData( rOpt );
+ if( mxRbSeparated->get_active() )
+ {
+ rOpt.SetFieldSeps( GetSeparators() );
+ rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
+ rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
+ rOpt.SetTextSep( lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP ) );
+ }
+
+ rOpt.SetQuotedAsText(mxCkbQuotedAsText->get_active());
+ rOpt.SetDetectSpecialNumber(mxCkbDetectNumber->get_active());
+ rOpt.SetDetectScientificNumber(mxCkbDetectScientificNumber->get_active());
+ rOpt.SetEvaluateFormulas(mxCkbEvaluateFormulas->get_active());
+ rOpt.SetSkipEmptyCells(mxCkbSkipEmptyCells->get_active());
+}
+
+void ScImportAsciiDlg::SaveParameters()
+{
+ lcl_SaveSeparators( maFieldSeparators, mxCbTextSep->get_active_text(), mxCkbAsOnce->get_active(),
+ mxCkbQuotedAsText->get_active(), mxCkbDetectNumber->get_active(), mxCkbDetectScientificNumber->get_active(),
+ mxRbFixed->get_active(),
+ mxNfRow->get_value(),
+ mxLbCharSet->get_active(),
+ static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
+ mxCkbSkipEmptyCells->get_active(), mxCkbRemoveSpace->get_active(),
+ mxCkbEvaluateFormulas->get_active(), meCall );
+}
+
+void ScImportAsciiDlg::SetSeparators( sal_Unicode cSep )
+{
+ if (cSep)
+ {
+ // Exclusively set a separator, maFieldSeparators needs not be
+ // modified, it's obtained by GetSeparators() after this call.
+ constexpr sal_Unicode aSeps[] = { '\t', ';', ',', ' ' };
+ for (const sal_Unicode c : aSeps)
+ {
+ const bool bSet = (c == cSep);
+ switch (c)
+ {
+ case '\t': mxCkbTab->set_active(bSet); break;
+ case ';': mxCkbSemicolon->set_active(bSet); break;
+ case ',': mxCkbComma->set_active(bSet); break;
+ case ' ': mxCkbSpace->set_active(bSet); break;
+ }
+ if (bSet)
+ cSep = 0;
+ }
+ if (cSep)
+ {
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(OUStringChar(cSep));
+ }
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < maFieldSeparators.getLength(); ++i)
+ {
+ switch (maFieldSeparators[i])
+ {
+ case '\t': mxCkbTab->set_active(true); break;
+ case ';': mxCkbSemicolon->set_active(true); break;
+ case ',': mxCkbComma->set_active(true); break;
+ case ' ': mxCkbSpace->set_active(true); break;
+ default:
+ mxCkbOther->set_active(true);
+ mxEdOther->set_text(mxEdOther->get_text() + OUStringChar(maFieldSeparators[i]));
+ }
+ }
+ }
+}
+
+void ScImportAsciiDlg::SetSelectedCharSet()
+{
+ meCharSet = mxLbCharSet->GetSelectTextEncoding();
+ mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
+ if( mbCharSetSystem )
+ meCharSet = osl_getThreadTextEncoding();
+}
+
+OUString ScImportAsciiDlg::GetSeparators() const
+{
+ OUString aSepChars;
+ if( mxCkbTab->get_active() )
+ aSepChars += "\t";
+ if( mxCkbSemicolon->get_active() )
+ aSepChars += ";";
+ if( mxCkbComma->get_active() )
+ aSepChars += ",";
+ if( mxCkbSpace->get_active() )
+ aSepChars += " ";
+ if( mxCkbOther->get_active() )
+ aSepChars += mxEdOther->get_text();
+ return aSepChars;
+}
+
+void ScImportAsciiDlg::SetupSeparatorCtrls()
+{
+ bool bEnable = mxRbSeparated->get_active();
+ mxCkbTab->set_sensitive( bEnable );
+ mxCkbSemicolon->set_sensitive( bEnable );
+ mxCkbComma->set_sensitive( bEnable );
+ mxCkbSpace->set_sensitive( bEnable );
+ mxCkbRemoveSpace->set_sensitive( bEnable );
+ mxCkbOther->set_sensitive( bEnable );
+ mxEdOther->set_sensitive( bEnable );
+ mxCkbAsOnce->set_sensitive( bEnable );
+ mxFtTextSep->set_sensitive( bEnable );
+ mxCbTextSep->set_sensitive( bEnable );
+}
+
+void ScImportAsciiDlg::UpdateVertical()
+{
+ mnRowPosCount = 0;
+ if (mpDatStream)
+ mpDatStream->SetStreamCharSet(meCharSet);
+}
+
+void ScImportAsciiDlg::RbSepFix()
+{
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ if( mxRbFixed->get_active() )
+ mxTableBox->SetFixedWidthMode();
+ else
+ mxTableBox->SetSeparatorsMode();
+ SetupSeparatorCtrls();
+}
+
+IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ RbSepFix();
+}
+
+IMPL_LINK(ScImportAsciiDlg, SeparatorClickHdl, weld::Toggleable&, rCtrl, void)
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorComboBoxHdl, weld::ComboBox&, rCtrl, void )
+{
+ SeparatorHdl(&rCtrl);
+}
+
+IMPL_LINK( ScImportAsciiDlg, SeparatorEditHdl, weld::Entry&, rEdit, void )
+{
+ SeparatorHdl(&rEdit);
+}
+
+IMPL_LINK(ScImportAsciiDlg, OtherOptionsClickHdl, weld::Toggleable&, rCtrl, void)
+{
+ if (&rCtrl == mxCkbDetectNumber.get())
+ {
+ if (mxCkbDetectNumber->get_active())
+ {
+ mxCkbDetectScientificNumber->set_active(true);
+ mxCkbDetectScientificNumber->set_sensitive(false);
+ }
+ else
+ mxCkbDetectScientificNumber->set_sensitive(true);
+ return;
+ }
+}
+
+void ScImportAsciiDlg::SeparatorHdl(const weld::Widget* pCtrl)
+{
+ OSL_ENSURE( pCtrl, "ScImportAsciiDlg::SeparatorHdl - missing sender" );
+ OSL_ENSURE( !mxRbFixed->get_active(), "ScImportAsciiDlg::SeparatorHdl - not allowed in fixed width" );
+
+ /* #i41550# First update state of the controls. The GetSeparators()
+ function needs final state of the check boxes. */
+ if (pCtrl == mxCkbOther.get() && mxCkbOther->get_active())
+ mxEdOther->grab_focus();
+ else if (pCtrl == mxEdOther.get())
+ 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();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+}
+
+IMPL_LINK_NOARG(ScImportAsciiDlg, CharSetHdl, weld::ComboBox&, void)
+{
+ if (mxLbCharSet->get_active() != -1)
+ {
+ weld::WaitObject aWaitObj(m_xDialog.get());
+ rtl_TextEncoding eOldCharSet = meCharSet;
+ SetSelectedCharSet();
+ // switching char-set invalidates 8bit -> String conversions
+ if (eOldCharSet != meCharSet)
+ UpdateVertical();
+
+ mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
+ }
+}
+
+IMPL_LINK(ScImportAsciiDlg, FirstRowHdl, weld::SpinButton&, rNumField, void)
+{
+ mxTableBox->GetGrid().Execute( CSVCMD_SETFIRSTIMPORTLINE, rNumField.get_value() - 1);
+}
+
+IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, rListBox, void)
+{
+ if (&rListBox == mxLbType.get())
+ mxTableBox->GetGrid().Execute(CSVCMD_SETCOLUMNTYPE, rListBox.get_active());
+}
+
+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_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
+ sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
+ // If mnRowPosCount==0, this is an initializing call, read ahead for row
+ // count and resulting scroll bar size and position to be able to scroll at
+ // all. When adding lines, read only the amount of next lines to be
+ // displayed.
+ if (!mnRowPosCount || nRead > CSV_PREVIEW_LINES)
+ nRead = CSV_PREVIEW_LINES;
+
+ sal_Int32 i;
+ for (i = 0; i < nRead; i++)
+ {
+ if (!GetLine( nBaseLine + i, maPreviewLine[i], cDetectSep))
+ break;
+ }
+ 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 '\t': 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();
+ bool bRemoveSpace = mxCkbRemoveSpace->get_active();
+ mxTableBox->SetUniStrings( maPreviewLine, maFieldSeparators, mcTextSep, bMergeSep, bRemoveSpace );
+}
+
+IMPL_LINK( ScImportAsciiDlg, ColTypeHdl, ScCsvTableBox&, rTableBox, void )
+{
+ sal_Int32 nType = rTableBox.GetSelColumnType();
+ sal_Int32 nTypeCount = mxLbType->get_count();
+ bool bEmpty = (nType == CSV_TYPE_MULTI);
+ bool bEnable = ((0 <= nType) && (nType < nTypeCount)) || bEmpty;
+
+ mxLbType->set_sensitive( bEnable );
+
+ if (bEmpty)
+ mxLbType->set_active(-1);
+ else if (bEnable)
+ mxLbType->set_active(nType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */