tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / dbgui / scuiasciiopt.cxx
blobc44bf2ad222d7f535c97ef8f5ed7cdc20207e7a0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #undef SC_DLLIMPLEMENTATION
22 #include <svx/txencbox.hxx>
24 #include <global.hxx>
25 #include <scresid.hxx>
26 #include <impex.hxx>
27 #include <scuiasciiopt.hxx>
28 #include <strings.hrc>
29 #include <strings.hxx>
30 #include <csvtablebox.hxx>
31 #include <osl/thread.h>
32 #include <unotools/transliterationwrapper.hxx>
34 #include <optutil.hxx>
35 #include <com/sun/star/uno/Any.hxx>
36 #include <com/sun/star/uno/Sequence.hxx>
37 #include <miscuno.hxx>
38 #include <osl/diagnose.h>
39 #include <vcl/svapp.hxx>
40 #include <comphelper/lok.hxx>
41 #include <o3tl/string_view.hxx>
43 #include <unicode/ucsdet.h>
44 #include <sfx2/objsh.hxx>
45 #include <svx/txenctab.hxx>
46 #include <unotools/viewoptions.hxx>
48 //! TODO make dynamic
49 const SCSIZE ASCIIDLG_MAXROWS = MAXROWCOUNT;
51 // Maximum number of source lines to concatenate while generating the preview
52 // for one logical line. This may result in a wrong preview if the actual
53 // number of embedded line feeds is greater, but a number too high would take
54 // too much time (loop excessively if unlimited and large data) if none of the
55 // selected separators are actually used in data but a field at start of line
56 // is quoted.
57 constexpr sal_uInt32 kMaxEmbeddedLinefeeds = 500;
59 using namespace com::sun::star::uno;
61 namespace {
63 // Defines - CSV Import Preserve Options
64 // For usage of index order see lcl_CreatePropertiesNames() below.
65 enum CSVImportOptionsIndex
67 CSVIO_MergeDelimiters = 0,
68 CSVIO_Separators,
69 CSVIO_TextSeparators,
70 CSVIO_FixedWidth,
71 CSVIO_RemoveSpace,
72 CSVIO_EvaluateFormulas,
73 CSVIO_SeparatorType,
74 // Settings for *all* dialog invocations above.
75 // Settings not for SC_TEXTTOCOLUMNS below.
76 CSVIO_FromRow,
77 CSVIO_Text2ColSkipEmptyCells = CSVIO_FromRow,
78 CSVIO_CharSet,
79 CSVIO_QuotedAsText,
80 CSVIO_DetectSpecialNum,
81 CSVIO_DetectScientificNum,
82 CSVIO_Language,
83 // Plus one not for SC_IMPORTFILE.
84 CSVIO_PasteSkipEmptyCells
87 enum SeparatorType
89 FIXED,
90 SEPARATOR,
91 DETECT_SEPARATOR
96 // Config items for all three paths are defined in
97 // officecfg/registry/schema/org/openoffice/Office/Calc.xcs
98 // If not, options are neither loaded nor saved.
99 const ::std::vector<OUString> CSVImportOptionNames =
101 u"MergeDelimiters"_ustr,
102 u"Separators"_ustr,
103 u"TextSeparators"_ustr,
104 u"FixedWidth"_ustr,
105 u"RemoveSpace"_ustr,
106 u"EvaluateFormulas"_ustr,
107 u"SeparatorType"_ustr,
108 u"FromRow"_ustr,
109 u"CharSet"_ustr,
110 u"QuotedFieldAsText"_ustr,
111 u"DetectSpecialNumbers"_ustr,
112 u"DetectScientificNumbers"_ustr,
113 u"Language"_ustr,
114 u"SkipEmptyCells"_ustr
116 constexpr OUStringLiteral aSep_Path = u"Office.Calc/Dialogs/CSVImport";
117 constexpr OUStringLiteral aSep_Path_Clpbrd = u"Office.Calc/Dialogs/ClipboardTextImport";
118 constexpr OUStringLiteral aSep_Path_Text2Col = u"Office.Calc/Dialogs/TextToColumnsImport";
120 namespace {
121 CSVImportOptionsIndex getSkipEmptyCellsIndex( ScImportAsciiCall eCall )
123 return eCall == SC_TEXTTOCOLUMNS ? CSVIO_Text2ColSkipEmptyCells : CSVIO_PasteSkipEmptyCells;
127 static void lcl_FillCombo(weld::ComboBox& rCombo, std::u16string_view rList, sal_Unicode cSelect)
129 OUString aStr;
130 if (!rList.empty())
132 sal_Int32 nIdx {0};
135 const OUString sEntry {o3tl::getToken(rList, 0, '\t', nIdx)};
136 rCombo.append_text(sEntry);
137 if (nIdx>0 && static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nIdx))) == cSelect)
138 aStr = sEntry;
140 while (nIdx>0);
143 if ( cSelect )
145 if (aStr.isEmpty())
146 aStr = OUString(cSelect); // Ascii
148 rCombo.set_entry_text(aStr);
152 static sal_Unicode lcl_CharFromCombo(const weld::ComboBox& rCombo, std::u16string_view rList)
154 sal_Unicode c = 0;
155 OUString aStr = rCombo.get_active_text();
156 if ( !aStr.isEmpty() && !rList.empty() )
158 sal_Int32 nIdx {0};
159 OUString sToken {o3tl::getToken(rList, 0, '\t', nIdx)};
160 while (nIdx>0)
162 if ( ScGlobal::GetTransliteration().isEqual( aStr, sToken ) )
164 sal_Int32 nTmpIdx {nIdx};
165 c = static_cast<sal_Unicode>(o3tl::toInt32(o3tl::getToken(rList, 0, '\t', nTmpIdx)));
167 // Skip to next token at even position
168 sToken = o3tl::getToken(rList, 1, '\t', nIdx);
170 if (!c)
172 sal_Unicode cFirst = aStr[0];
173 // #i24235# first try the first character of the string directly
174 if( (aStr.getLength() == 1) || (cFirst < '0') || (cFirst > '9') )
175 c = cFirst;
176 else // keep old behaviour for compatibility (i.e. "39" -> "'")
177 c = static_cast<sal_Unicode>(aStr.toInt32()); // Ascii
180 return c;
183 static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence<OUString>& rNames, ScImportAsciiCall eCall )
185 sal_Int32 nProperties = 0;
187 switch(eCall)
189 case SC_IMPORTFILE:
190 rSepPath = aSep_Path;
191 nProperties = 13;
192 break;
193 case SC_PASTETEXT:
194 rSepPath = aSep_Path_Clpbrd;
195 nProperties = 14;
196 break;
197 case SC_TEXTTOCOLUMNS:
198 default:
199 rSepPath = aSep_Path_Text2Col;
200 nProperties = 8;
201 break;
203 rNames.realloc( nProperties );
204 OUString* pNames = rNames.getArray();
205 pNames[ CSVIO_MergeDelimiters ] = CSVImportOptionNames[ CSVIO_MergeDelimiters ];
206 pNames[ CSVIO_Separators ] = CSVImportOptionNames[ CSVIO_Separators ];
207 pNames[ CSVIO_TextSeparators ] = CSVImportOptionNames[ CSVIO_TextSeparators ];
208 pNames[ CSVIO_FixedWidth ] = CSVImportOptionNames[ CSVIO_FixedWidth ];
209 pNames[ CSVIO_RemoveSpace ] = CSVImportOptionNames[ CSVIO_RemoveSpace ];
210 pNames[ CSVIO_EvaluateFormulas ] = CSVImportOptionNames[ CSVIO_EvaluateFormulas ];
211 pNames[ CSVIO_SeparatorType ] = CSVImportOptionNames[ CSVIO_SeparatorType ];
212 if (eCall != SC_TEXTTOCOLUMNS)
214 pNames[ CSVIO_FromRow ] = CSVImportOptionNames[ CSVIO_FromRow ];
215 pNames[ CSVIO_CharSet ] = CSVImportOptionNames[ CSVIO_CharSet ];
216 pNames[ CSVIO_QuotedAsText ] = CSVImportOptionNames[ CSVIO_QuotedAsText ];
217 pNames[ CSVIO_DetectSpecialNum ] = CSVImportOptionNames[ CSVIO_DetectSpecialNum ];
218 pNames[ CSVIO_DetectScientificNum ] = CSVImportOptionNames[ CSVIO_DetectScientificNum ];
219 pNames[ CSVIO_Language ] = CSVImportOptionNames[ CSVIO_Language ];
221 if (eCall != SC_IMPORTFILE)
223 const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
224 assert( nSkipEmptyCells < rNames.getLength());
225 pNames[ nSkipEmptyCells ] = CSVImportOptionNames[ CSVIO_PasteSkipEmptyCells ];
229 static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& rTextSeparators,
230 bool& rMergeDelimiters, bool& rQuotedAsText, bool& rDetectSpecialNum, bool& rDetectScientificNum,
231 SeparatorType& rSepType, sal_Int32& rFromRow, sal_Int32& rCharSet,
232 sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace,
233 bool& rEvaluateFormulas, ScImportAsciiCall eCall, bool& rBeforeDetection )
235 Sequence<Any>aValues;
236 const Any *pProperties;
237 Sequence<OUString> aNames;
238 OUString aSepPath;
239 lcl_CreatePropertiesNames ( aSepPath, aNames, eCall);
240 ScLinkConfigItem aItem( aSepPath );
241 aValues = aItem.GetProperties( aNames );
242 pProperties = aValues.getConstArray();
244 if( pProperties[ CSVIO_MergeDelimiters ].hasValue() )
245 rMergeDelimiters = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_MergeDelimiters ] );
247 if( pProperties[ CSVIO_RemoveSpace ].hasValue() )
248 rRemoveSpace = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_RemoveSpace ] );
250 if( pProperties[ CSVIO_Separators ].hasValue() )
251 pProperties[ CSVIO_Separators ] >>= rFieldSeparators;
253 if( pProperties[ CSVIO_TextSeparators ].hasValue() )
254 pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
256 rBeforeDetection = true;
257 if( pProperties[ CSVIO_SeparatorType ].hasValue() )
259 rBeforeDetection = false;
260 rSepType = static_cast<SeparatorType>(ScUnoHelpFunctions::GetInt16FromAny( pProperties[ CSVIO_SeparatorType ] ));
262 else if( pProperties[ CSVIO_FixedWidth ].hasValue() )
263 rSepType = (ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_FixedWidth ] ) ? SeparatorType::FIXED : SeparatorType::DETECT_SEPARATOR);
265 if( pProperties[ CSVIO_EvaluateFormulas ].hasValue() )
266 rEvaluateFormulas = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_EvaluateFormulas ] );
268 if (eCall != SC_TEXTTOCOLUMNS)
270 if( pProperties[ CSVIO_FromRow ].hasValue() )
271 pProperties[ CSVIO_FromRow ] >>= rFromRow;
273 if( pProperties[ CSVIO_CharSet ].hasValue() )
274 pProperties[ CSVIO_CharSet ] >>= rCharSet;
276 if ( pProperties[ CSVIO_QuotedAsText ].hasValue() )
277 pProperties[ CSVIO_QuotedAsText ] >>= rQuotedAsText;
279 if ( pProperties[ CSVIO_DetectSpecialNum ].hasValue() )
280 pProperties[ CSVIO_DetectSpecialNum ] >>= rDetectSpecialNum;
282 if ( pProperties[ CSVIO_DetectScientificNum ].hasValue() )
283 pProperties[ CSVIO_DetectScientificNum ] >>= rDetectScientificNum;
285 if ( pProperties[ CSVIO_Language ].hasValue() )
286 pProperties[ CSVIO_Language ] >>= rLanguage;
288 if (eCall != SC_IMPORTFILE)
290 const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
291 assert( nSkipEmptyCells < aValues.getLength());
292 if ( pProperties[nSkipEmptyCells].hasValue() )
293 rSkipEmptyCells = ScUnoHelpFunctions::GetBoolFromAny( pProperties[nSkipEmptyCells] );
297 static void lcl_SaveSeparators(
298 const OUString& sFieldSeparators, const OUString& sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText,
299 bool bDetectSpecialNum, bool bDetectScientificNum, SeparatorType rSepType, sal_Int32 nFromRow,
300 sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, bool bEvaluateFormulas,
301 ScImportAsciiCall eCall )
303 Sequence<Any> aValues;
304 Any *pProperties;
305 Sequence<OUString> aNames;
306 OUString aSepPath;
307 lcl_CreatePropertiesNames ( aSepPath, aNames, eCall );
308 ScLinkConfigItem aItem( aSepPath );
309 aValues = aItem.GetProperties( aNames );
310 pProperties = aValues.getArray();
312 pProperties[ CSVIO_MergeDelimiters ] <<= bMergeDelimiters;
313 pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
314 pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
315 pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
316 pProperties[ CSVIO_EvaluateFormulas ] <<= bEvaluateFormulas;
317 pProperties[ CSVIO_SeparatorType ] <<= static_cast<sal_Int16>(rSepType);
318 if (eCall != SC_TEXTTOCOLUMNS)
320 pProperties[ CSVIO_FromRow ] <<= nFromRow;
321 pProperties[ CSVIO_CharSet ] <<= nCharSet;
322 pProperties[ CSVIO_QuotedAsText ] <<= bQuotedAsText;
323 pProperties[ CSVIO_DetectSpecialNum ] <<= bDetectSpecialNum;
324 pProperties[ CSVIO_DetectScientificNum ] <<= bDetectScientificNum;
325 pProperties[ CSVIO_Language ] <<= nLanguage;
327 if (eCall != SC_IMPORTFILE)
329 const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
330 assert( nSkipEmptyCells < aValues.getLength());
331 pProperties[ nSkipEmptyCells ] <<= bSkipEmptyCells;
334 aItem.PutProperties(aNames, aValues);
337 ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, std::u16string_view aDatName,
338 SvStream* pInStream, ScImportAsciiCall eCall)
339 : GenericDialogController(pParent, u"modules/scalc/ui/textimportcsv.ui"_ustr, u"TextImportCsvDialog"_ustr)
340 , mpDatStream(pInStream)
341 , mnStreamPos(pInStream ? pInStream->Tell() : 0)
342 , mnStreamInitPos(mnStreamPos)
343 , mnRowPosCount(0)
344 , mcTextSep(ScAsciiOptions::cDefaultTextSep)
345 , meDetectedCharSet(RTL_TEXTENCODING_DONTKNOW)
346 , mbCharSetDetect(true)
347 , meCall(eCall)
348 , mxFtCharSet(m_xBuilder->weld_label(u"textcharset"_ustr))
349 , mxLbCharSet(new SvxTextEncodingBox(m_xBuilder->weld_combo_box(u"charset"_ustr)))
350 , mxFtDetectedCharSet(m_xBuilder->weld_label(u"textdetectedcharset"_ustr))
351 , mxFtCustomLang(m_xBuilder->weld_label(u"textlanguage"_ustr))
352 , mxLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box(u"language"_ustr)))
353 , mxFtRow(m_xBuilder->weld_label(u"textfromrow"_ustr))
354 , mxNfRow(m_xBuilder->weld_spin_button(u"fromrow"_ustr))
355 , mxRbDetectSep(m_xBuilder->weld_radio_button(u"todetectseparator"_ustr))
356 , mxRbFixed(m_xBuilder->weld_radio_button(u"tofixedwidth"_ustr))
357 , mxRbSeparated(m_xBuilder->weld_radio_button(u"toseparatedby"_ustr))
358 , mxCkbTab(m_xBuilder->weld_check_button(u"tab"_ustr))
359 , mxCkbSemicolon(m_xBuilder->weld_check_button(u"semicolon"_ustr))
360 , mxCkbComma(m_xBuilder->weld_check_button(u"comma"_ustr))
361 , mxCkbRemoveSpace(m_xBuilder->weld_check_button(u"removespace"_ustr))
362 , mxCkbSpace(m_xBuilder->weld_check_button(u"space"_ustr))
363 , mxCkbOther(m_xBuilder->weld_check_button(u"other"_ustr))
364 , mxEdOther(m_xBuilder->weld_entry(u"inputother"_ustr))
365 , mxCkbAsOnce(m_xBuilder->weld_check_button(u"mergedelimiters"_ustr))
366 , mxFtTextSep(m_xBuilder->weld_label(u"texttextdelimiter"_ustr))
367 , mxCbTextSep(m_xBuilder->weld_combo_box(u"textdelimiter"_ustr))
368 , mxCkbQuotedAsText(m_xBuilder->weld_check_button(u"quotedfieldastext"_ustr))
369 , mxCkbDetectNumber(m_xBuilder->weld_check_button(u"detectspecialnumbers"_ustr))
370 , mxCkbDetectScientificNumber(m_xBuilder->weld_check_button(u"detectscientificnumbers"_ustr))
371 , mxCkbEvaluateFormulas(m_xBuilder->weld_check_button(u"evaluateformulas"_ustr))
372 , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button(u"skipemptycells"_ustr))
373 , mxLbType(m_xBuilder->weld_combo_box(u"columntype"_ustr))
374 , mxAltTitle(m_xBuilder->weld_label(u"textalttitle"_ustr))
375 , mxTableBox(new ScCsvTableBox(*m_xBuilder))
377 SvtViewOptions aDlgOpt(EViewType::Dialog, "TextImportCsvDialog");
378 if (aDlgOpt.Exists())
379 m_xDialog->set_window_state(aDlgOpt.GetWindowState());
381 OUString aName = m_xDialog->get_title();
382 switch (meCall)
384 case SC_TEXTTOCOLUMNS:
385 m_xDialog->set_title(mxAltTitle->get_label());
386 break;
387 case SC_IMPORTFILE:
388 if (!comphelper::LibreOfficeKit::isActive())
390 aName += OUString::Concat(" - [") + aDatName + "]";
391 m_xDialog->set_title(aName);
393 break;
394 default:
395 break;
398 // To be able to prefill the correct values based on the file extension
399 bool bIsTSV = (o3tl::endsWithIgnoreAsciiCase(aDatName, ".tsv") || o3tl::endsWithIgnoreAsciiCase(aDatName, ".tab"));
401 // Default options are set in officecfg/registry/schema/org/openoffice/Office/Calc.xcs
402 OUString sFieldSeparators(u",;\t"_ustr);
403 OUString sTextSeparators(mcTextSep);
404 bool bMergeDelimiters = false;
405 SeparatorType eSepType = DETECT_SEPARATOR;
406 bool bQuotedFieldAsText = false;
407 bool bDetectSpecialNum = true;
408 bool bDetectScientificNum = true;
409 bool bEvaluateFormulas = (meCall != SC_IMPORTFILE);
410 bool bSkipEmptyCells = true;
411 bool bRemoveSpace = false;
412 bool bBeforeDetection = false;
413 sal_Int32 nFromRow = 1;
414 sal_Int32 nCharSet = -1;
415 sal_Int32 nLanguage = 0;
417 lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
418 bQuotedFieldAsText, bDetectSpecialNum, bDetectScientificNum, eSepType, nFromRow,
419 nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, bEvaluateFormulas, meCall,
420 bBeforeDetection);
422 maFieldSeparators = sFieldSeparators;
424 if( bMergeDelimiters && !bIsTSV )
425 mxCkbAsOnce->set_active(true);
426 if (bQuotedFieldAsText)
427 mxCkbQuotedAsText->set_active(true);
428 if (bRemoveSpace)
429 mxCkbRemoveSpace->set_active(true);
430 if (bDetectSpecialNum)
432 mxCkbDetectNumber->set_active(true);
433 bDetectScientificNum = true;
434 mxCkbDetectScientificNumber->set_sensitive(false);
436 if (bDetectScientificNum)
437 mxCkbDetectScientificNumber->set_active(true);
438 if (bEvaluateFormulas)
439 mxCkbEvaluateFormulas->set_active(true);
440 if (bSkipEmptyCells)
441 mxCkbSkipEmptyCells->set_active(true);
442 if (eSepType == SeparatorType::FIXED)
444 if (bIsTSV)
446 eSepType = SeparatorType::SEPARATOR;
447 mxRbSeparated->set_active(true);
449 else
450 mxRbFixed->set_active(true);
452 else if (eSepType == SeparatorType::SEPARATOR)
453 mxRbSeparated->set_active(true);
454 else
455 mxRbDetectSep->set_active(true);
456 if (nFromRow != 1)
457 mxNfRow->set_value(nFromRow);
459 // Clipboard is always Unicode, else rely on default/config.
460 rtl_TextEncoding ePreselectUnicode = (meCall == SC_IMPORTFILE ?
461 RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
463 // Detect character set only once and then use it for "Detect" option.
464 SvStreamEndian eEndian;
465 SfxObjectShell::DetectCharSet(*mpDatStream, meDetectedCharSet, eEndian);
466 if (meDetectedCharSet == RTL_TEXTENCODING_UNICODE)
467 mpDatStream->SetEndian(eEndian);
468 else if ( meDetectedCharSet == RTL_TEXTENCODING_DONTKNOW )
470 meDetectedCharSet = osl_getThreadTextEncoding();
471 // Prefer UTF-8, as UTF-16 would have already been detected from the stream.
472 // This gives a better chance that the file is going to be opened correctly.
473 if ( meDetectedCharSet == RTL_TEXTENCODING_UNICODE && mpDatStream )
474 meDetectedCharSet = RTL_TEXTENCODING_UTF8;
477 if (bIsTSV)
478 SetSeparators('\t');
479 else
480 SetSeparators(0);
482 // Get Separators from the dialog (empty are set from default)
483 maFieldSeparators = GetActiveSeparators();
485 mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl ) );
487 // *** Separator characters ***
488 lcl_FillCombo( *mxCbTextSep, SCSTR_TEXTSEP, mcTextSep );
489 mxCbTextSep->set_entry_text(sTextSeparators);
490 // tdf#69207 - use selected text delimiter to parse the provided data
491 mcTextSep = lcl_CharFromCombo(*mxCbTextSep, SCSTR_TEXTSEP);
493 Link<weld::Toggleable&,void> aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl );
494 Link<weld::Toggleable&,void> aOtherOptionsClickHdl =LINK( this, ScImportAsciiDlg, OtherOptionsClickHdl );
495 mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) );
496 mxCkbTab->connect_toggled( aSeparatorClickHdl );
497 mxCkbSemicolon->connect_toggled( aSeparatorClickHdl );
498 mxCkbComma->connect_toggled( aSeparatorClickHdl );
499 mxCkbAsOnce->connect_toggled( aSeparatorClickHdl );
500 mxCkbSpace->connect_toggled( aSeparatorClickHdl );
501 mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl );
502 mxCkbOther->connect_toggled( aSeparatorClickHdl );
503 mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl));
504 mxCkbQuotedAsText->connect_toggled( aOtherOptionsClickHdl );
505 mxCkbDetectNumber->connect_toggled( aOtherOptionsClickHdl );
506 mxCkbDetectScientificNumber->connect_toggled( aOtherOptionsClickHdl );
507 mxCkbEvaluateFormulas->connect_toggled( aOtherOptionsClickHdl );
508 mxCkbSkipEmptyCells->connect_toggled( aOtherOptionsClickHdl );
510 // *** text encoding ListBox ***
511 // all encodings allowed, including Unicode, but subsets are excluded
512 mxLbCharSet->FillFromTextEncodingTable( true );
513 // Insert one "SYSTEM" entry for compatibility in AsciiOptions and system
514 // independent document linkage.
515 mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_DONTKNOW, ScResId( SCSTR_CHARSET_USER ) );
516 // Insert one for detecting charset.
517 mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_USER_DETECTED, "- " + ScResId( SCSTR_AUTOMATIC ) + " -" );
519 if (ePreselectUnicode != RTL_TEXTENCODING_DONTKNOW)
520 mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
521 else if (nCharSet >= 0 && !bBeforeDetection)
522 mxLbCharSet->set_active(nCharSet);
523 else
524 mxLbCharSet->SelectTextEncoding(RTL_TEXTENCODING_USER_DETECTED);
526 SetSelectedCharSet();
527 mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
529 mxLbCustomLang->SetLanguageList(
530 SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false);
531 mxLbCustomLang->InsertLanguage(LANGUAGE_SYSTEM);
532 mxLbCustomLang->set_active_id(static_cast<LanguageType>(nLanguage));
534 // *** column type ListBox ***
535 OUString aColumnUser( ScResId( SCSTR_COLUMN_USER ) );
536 for (sal_Int32 nIdx {0}; nIdx>=0; )
538 mxLbType->append_text(aColumnUser.getToken(0, ';', nIdx));
541 mxLbType->connect_changed( LINK( this, ScImportAsciiDlg, LbColTypeHdl ) );
542 mxLbType->set_sensitive(false);
544 // *** table box preview ***
545 mxTableBox->Init();
546 mxTableBox->SetUpdateTextHdl( LINK( this, ScImportAsciiDlg, UpdateTextHdl ) );
547 mxTableBox->InitTypes( *mxLbType );
548 mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
550 mxRbDetectSep->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
551 mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
552 mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
554 RbSepFix();
556 UpdateVertical();
558 mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
560 if (meCall == SC_TEXTTOCOLUMNS)
562 mxFtCharSet->set_sensitive(false);
563 mxLbCharSet->set_sensitive(false);
564 mxFtCustomLang->set_sensitive(false);
565 mxLbCustomLang->set_active_id(LANGUAGE_SYSTEM);
566 mxLbCustomLang->set_sensitive(false);
567 mxFtRow->set_sensitive(false);
568 mxNfRow->set_sensitive(false);
570 // Quoted field as text option is not used for text-to-columns mode.
571 mxCkbQuotedAsText->set_active(false);
572 mxCkbQuotedAsText->set_sensitive(false);
574 // Always detect special numbers for text-to-columns mode.
575 mxCkbDetectNumber->set_active(true);
576 mxCkbDetectNumber->set_sensitive(false);
577 mxCkbDetectScientificNumber->set_active(true);
578 mxCkbDetectScientificNumber->set_sensitive(false);
580 if (meCall == SC_IMPORTFILE)
582 //Empty cells in imported file are empty
583 mxCkbSkipEmptyCells->set_active(false);
584 mxCkbSkipEmptyCells->hide();
587 if (comphelper::LibreOfficeKit::isActive())
588 m_xBuilder->weld_button(u"cancel"_ustr)->hide();
589 m_xDialog->SetInstallLOKNotifierHdl(LINK(this, ScImportAsciiDlg, InstallLOKNotifierHdl));
592 IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
594 return GetpApp();
597 ScImportAsciiDlg::~ScImportAsciiDlg()
599 SvtViewOptions aDlgOpt(EViewType::Dialog, "TextImportCsvDialog");
600 aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::PosSize));
603 bool ScImportAsciiDlg::GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep )
605 if (nLine >= ASCIIDLG_MAXROWS || !mpDatStream)
606 return false;
608 bool bRet = true;
609 bool bFixed = mxRbFixed->get_active();
611 if (!mpRowPosArray)
612 mpRowPosArray.reset( new sal_uLong[ASCIIDLG_MAXROWS + 2] );
614 if (!mnRowPosCount) // complete re-fresh
616 memset( mpRowPosArray.get(), 0, sizeof(mpRowPosArray[0]) * (ASCIIDLG_MAXROWS+2));
618 Seek(0);
619 mpDatStream->StartReadingUnicodeText( mpDatStream->GetStreamCharSet() );
621 mnStreamPos = mpDatStream->Tell();
622 mpRowPosArray[mnRowPosCount] = mnStreamPos;
625 if (nLine >= mnRowPosCount)
627 // need to work out some more line information
630 if (!Seek(mpRowPosArray[mnRowPosCount]) || !mpDatStream->good())
632 bRet = false;
633 break;
635 rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators,
636 mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
637 mnStreamPos = mpDatStream->Tell();
638 mpRowPosArray[++mnRowPosCount] = mnStreamPos;
639 } while (nLine >= mnRowPosCount && mpDatStream->good());
640 if (mpDatStream->eof() && mnRowPosCount &&
641 mnStreamPos == mpRowPosArray[mnRowPosCount-1])
643 // the very end, not even an empty line read
644 bRet = false;
645 --mnRowPosCount;
648 else
650 Seek( mpRowPosArray[nLine]);
651 rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators, mcTextSep, rcDetectSep, kMaxEmbeddedLinefeeds);
652 mnStreamPos = mpDatStream->Tell();
655 // If the file content isn't unicode, ReadUniStringLine
656 // may try to seek beyond the file's end and cause a CANTSEEK error
657 // (depending on the stream type). The error code has to be cleared,
658 // or further read operations (including non-unicode) will fail.
659 if ( mpDatStream->GetError() == ERRCODE_IO_CANTSEEK )
660 mpDatStream->ResetError();
662 ScImportExport::EmbeddedNullTreatment( rText);
664 return bRet;
667 void ScImportAsciiDlg::GetOptions( ScAsciiOptions& rOpt )
669 rOpt.SetCharSet( meCharSet );
670 rOpt.SetCharSetSystem( mbCharSetSystem );
671 rOpt.SetLanguage(mxLbCustomLang->get_active_id());
672 rOpt.SetFixedLen( mxRbFixed->get_active() );
673 rOpt.SetStartRow( mxNfRow->get_value() );
674 mxTableBox->FillColumnData( rOpt );
675 if( mxRbSeparated->get_active() || mxRbDetectSep->get_active())
677 rOpt.SetFieldSeps( GetActiveSeparators() );
678 rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
679 rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
680 rOpt.SetTextSep( lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP ) );
683 rOpt.SetQuotedAsText(mxCkbQuotedAsText->get_active());
684 rOpt.SetDetectSpecialNumber(mxCkbDetectNumber->get_active());
685 rOpt.SetDetectScientificNumber(mxCkbDetectScientificNumber->get_active());
686 rOpt.SetEvaluateFormulas(mxCkbEvaluateFormulas->get_active());
687 rOpt.SetSkipEmptyCells(mxCkbSkipEmptyCells->get_active());
690 void ScImportAsciiDlg::SaveParameters()
692 lcl_SaveSeparators( GetSeparators(), mxCbTextSep->get_active_text(), mxCkbAsOnce->get_active(),
693 mxCkbQuotedAsText->get_active(), mxCkbDetectNumber->get_active(), mxCkbDetectScientificNumber->get_active(),
694 mxRbFixed->get_active() ? FIXED : (mxRbDetectSep->get_active() ? DETECT_SEPARATOR : SEPARATOR),
695 mxNfRow->get_value(),
696 mxLbCharSet->get_active(),
697 static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
698 mxCkbSkipEmptyCells->get_active(), mxCkbRemoveSpace->get_active(),
699 mxCkbEvaluateFormulas->get_active(), meCall );
702 void ScImportAsciiDlg::SetSeparators( sal_Unicode cSep )
704 if (cSep)
706 // Exclusively set a separator, maFieldSeparators needs not be
707 // modified, it's obtained by GetSeparators() after this call.
708 static constexpr sal_Unicode aSeps[] = { '\t', ';', ',', ' ' };
709 for (const sal_Unicode c : aSeps)
711 const bool bSet = (c == cSep);
712 switch (c)
714 case '\t': mxCkbTab->set_active(bSet); break;
715 case ';': mxCkbSemicolon->set_active(bSet); break;
716 case ',': mxCkbComma->set_active(bSet); break;
717 case ' ': mxCkbSpace->set_active(bSet); break;
719 if (bSet)
720 cSep = 0;
722 if (cSep)
724 mxCkbOther->set_active(true);
725 mxEdOther->set_text(OUStringChar(cSep));
728 else
730 for (sal_Int32 i = 0; i < maFieldSeparators.getLength(); ++i)
732 switch (maFieldSeparators[i])
734 case '\t': mxCkbTab->set_active(true); break;
735 case ';': mxCkbSemicolon->set_active(true); break;
736 case ',': mxCkbComma->set_active(true); break;
737 case ' ': mxCkbSpace->set_active(true); break;
738 default:
739 mxCkbOther->set_active(true);
740 mxEdOther->set_text(mxEdOther->get_text() + OUStringChar(maFieldSeparators[i]));
746 void ScImportAsciiDlg::SetSelectedCharSet()
748 rtl_TextEncoding eOldCharSet = meCharSet;
749 meCharSet = mxLbCharSet->GetSelectTextEncoding();
750 mbCharSetDetect = (meCharSet == RTL_TEXTENCODING_USER_DETECTED);
751 mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
752 if (mbCharSetDetect)
754 meCharSet = meDetectedCharSet;
755 mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
757 else if( mbCharSetSystem )
759 meCharSet = osl_getThreadTextEncoding();
760 mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
762 else
763 mxFtDetectedCharSet->set_label(SvxTextEncodingTable::GetTextString(meCharSet));
765 if (eOldCharSet != meCharSet)
766 DetectCsvSeparators();
768 RbSepFix();
771 OUString ScImportAsciiDlg::GetSeparators() const
773 OUString aSepChars;
774 if( mxCkbTab->get_active() )
775 aSepChars += "\t";
776 if( mxCkbSemicolon->get_active() )
777 aSepChars += ";";
778 if( mxCkbComma->get_active() )
779 aSepChars += ",";
780 if( mxCkbSpace->get_active() )
781 aSepChars += " ";
782 if( mxCkbOther->get_active() )
783 aSepChars += mxEdOther->get_text();
784 return aSepChars;
787 OUString ScImportAsciiDlg::GetActiveSeparators() const
789 if (mxRbSeparated->get_active())
790 return GetSeparators();
792 if (mxRbDetectSep->get_active())
793 return maDetectedFieldSeps;
795 return OUString();
798 void ScImportAsciiDlg::SetupSeparatorCtrls()
800 bool bEnable = mxRbSeparated->get_active();
801 mxCkbTab->set_sensitive( bEnable );
802 mxCkbSemicolon->set_sensitive( bEnable );
803 mxCkbComma->set_sensitive( bEnable );
804 mxCkbSpace->set_sensitive( bEnable );
805 mxCkbOther->set_sensitive( bEnable );
806 mxEdOther->set_sensitive( bEnable );
808 bEnable = bEnable || mxRbDetectSep->get_active();
809 mxCkbRemoveSpace->set_sensitive( bEnable );
810 mxCkbAsOnce->set_sensitive( bEnable );
811 mxFtTextSep->set_sensitive( bEnable );
812 mxCbTextSep->set_sensitive( bEnable );
814 OUString aSepName;
815 if (maDetectedFieldSeps.isEmpty())
816 aSepName += ScResId(SCSTR_NONE);
817 else
819 for (int idx = 0; idx < maDetectedFieldSeps.getLength(); idx ++)
821 if (idx > 0)
822 aSepName += u" ";
824 if (maDetectedFieldSeps[idx] == u' ')
825 aSepName += ScResId(SCSTR_FIELDSEP_SPACE);
826 else if (maDetectedFieldSeps[idx] == u'\t')
827 aSepName += ScResId(SCSTR_FIELDSEP_TAB);
828 else
829 aSepName += OUStringChar(maDetectedFieldSeps[idx]);
832 mxRbDetectSep->set_label(ScResId(SCSTR_DETECTED).replaceFirst( "%1", aSepName));
835 void ScImportAsciiDlg::DetectCsvSeparators()
837 mpDatStream->Seek(mnStreamInitPos);
838 SfxObjectShell::DetectCsvSeparators(*mpDatStream, meCharSet, maDetectedFieldSeps, mcTextSep);
839 mpDatStream->Seek(mnStreamPos);
842 void ScImportAsciiDlg::UpdateVertical()
844 mnRowPosCount = 0;
845 if (mpDatStream)
846 mpDatStream->SetStreamCharSet(meCharSet);
849 void ScImportAsciiDlg::RbSepFix()
851 weld::WaitObject aWaitObj(m_xDialog.get());
852 if (mxRbSeparated->get_active() || mxRbDetectSep->get_active())
854 maFieldSeparators = GetActiveSeparators();
855 if (mxTableBox->IsFixedWidthMode())
856 mxTableBox->SetSeparatorsMode();
857 else
858 mxTableBox->Refresh();
860 else
861 mxTableBox->SetFixedWidthMode();
863 SetupSeparatorCtrls();
866 IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable&, rButton, void)
868 if (!rButton.get_active())
869 return;
870 RbSepFix();
873 IMPL_LINK(ScImportAsciiDlg, SeparatorClickHdl, weld::Toggleable&, rCtrl, void)
875 SeparatorHdl(&rCtrl);
878 IMPL_LINK( ScImportAsciiDlg, SeparatorComboBoxHdl, weld::ComboBox&, rCtrl, void )
880 SeparatorHdl(&rCtrl);
883 IMPL_LINK( ScImportAsciiDlg, SeparatorEditHdl, weld::Entry&, rEdit, void )
885 SeparatorHdl(&rEdit);
888 IMPL_LINK(ScImportAsciiDlg, OtherOptionsClickHdl, weld::Toggleable&, rCtrl, void)
890 if (&rCtrl == mxCkbDetectNumber.get())
892 if (mxCkbDetectNumber->get_active())
894 mxCkbDetectScientificNumber->set_active(true);
895 mxCkbDetectScientificNumber->set_sensitive(false);
897 else
898 mxCkbDetectScientificNumber->set_sensitive(true);
899 return;
903 void ScImportAsciiDlg::SeparatorHdl(const weld::Widget* pCtrl)
905 OSL_ENSURE( pCtrl, "ScImportAsciiDlg::SeparatorHdl - missing sender" );
906 OSL_ENSURE( !mxRbFixed->get_active(), "ScImportAsciiDlg::SeparatorHdl - not allowed in fixed width" );
908 /* #i41550# First update state of the controls. The GetSeparators()
909 function needs final state of the check boxes. */
910 if (pCtrl == mxCkbOther.get() && mxCkbOther->get_active())
911 mxEdOther->grab_focus();
912 else if (pCtrl == mxEdOther.get())
913 mxCkbOther->set_active(!mxEdOther->get_text().isEmpty());
915 OUString aOldFldSeps( maFieldSeparators);
916 sal_Unicode cOldSep = mcTextSep;
917 mcTextSep = lcl_CharFromCombo( *mxCbTextSep, SCSTR_TEXTSEP );
918 // Any separator changed may result in completely different lines due to
919 // embedded line breaks.
920 if (cOldSep != mcTextSep)
922 DetectCsvSeparators();
924 SetupSeparatorCtrls();
926 maFieldSeparators = GetActiveSeparators();
927 if (aOldFldSeps != maFieldSeparators)
929 UpdateVertical();
930 mxTableBox->Refresh();
931 return;
934 else
935 maFieldSeparators = GetActiveSeparators();
937 mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
940 IMPL_LINK_NOARG(ScImportAsciiDlg, CharSetHdl, weld::ComboBox&, void)
942 if (mxLbCharSet->get_active() != -1)
944 weld::WaitObject aWaitObj(m_xDialog.get());
945 rtl_TextEncoding eOldCharSet = meCharSet;
946 SetSelectedCharSet();
947 // switching char-set invalidates 8bit -> String conversions
948 if (eOldCharSet != meCharSet)
949 UpdateVertical();
951 mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
955 IMPL_LINK(ScImportAsciiDlg, FirstRowHdl, weld::SpinButton&, rNumField, void)
957 mxTableBox->GetGrid().Execute( CSVCMD_SETFIRSTIMPORTLINE, rNumField.get_value() - 1);
960 IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, rListBox, void)
962 if (&rListBox == mxLbType.get())
963 mxTableBox->GetGrid().Execute(CSVCMD_SETCOLUMNTYPE, rListBox.get_active());
966 IMPL_LINK_NOARG(ScImportAsciiDlg, UpdateTextHdl, ScCsvTableBox&, void)
968 sal_Unicode cDetectSep = 0xffff;
970 sal_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
971 sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
972 // If mnRowPosCount==0, this is an initializing call, read ahead for row
973 // count and resulting scroll bar size and position to be able to scroll at
974 // all. When adding lines, read only the amount of next lines to be
975 // displayed.
976 if (!mnRowPosCount || nRead > CSV_PREVIEW_LINES)
977 nRead = CSV_PREVIEW_LINES;
979 sal_Int32 i;
980 for (i = 0; i < nRead; i++)
982 if (!GetLine( nBaseLine + i, maPreviewLine[i], cDetectSep))
983 break;
985 for (; i < CSV_PREVIEW_LINES; i++)
986 maPreviewLine[i].clear();
989 mxTableBox->GetGrid().Execute( CSVCMD_SETLINECOUNT, mnRowPosCount);
990 bool bMergeSep = mxCkbAsOnce->get_active();
991 bool bRemoveSpace = mxCkbRemoveSpace->get_active();
992 mxTableBox->SetUniStrings( maPreviewLine, maFieldSeparators, mcTextSep, bMergeSep, bRemoveSpace );
995 IMPL_LINK( ScImportAsciiDlg, ColTypeHdl, ScCsvTableBox&, rTableBox, void )
997 sal_Int32 nType = rTableBox.GetSelColumnType();
998 sal_Int32 nTypeCount = mxLbType->get_count();
999 bool bEmpty = (nType == CSV_TYPE_MULTI);
1000 bool bEnable = ((0 <= nType) && (nType < nTypeCount)) || bEmpty;
1002 mxLbType->set_sensitive( bEnable );
1004 if (bEmpty)
1005 mxLbType->set_active(-1);
1006 else if (bEnable)
1007 mxLbType->set_active(nType);
1010 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */