1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
25 #include <scresid.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>
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
57 constexpr sal_uInt32 kMaxEmbeddedLinefeeds
= 500;
59 using namespace com::sun::star::uno
;
63 // Defines - CSV Import Preserve Options
64 // For usage of index order see lcl_CreatePropertiesNames() below.
65 enum CSVImportOptionsIndex
67 CSVIO_MergeDelimiters
= 0,
72 CSVIO_EvaluateFormulas
,
74 // Settings for *all* dialog invocations above.
75 // Settings not for SC_TEXTTOCOLUMNS below.
77 CSVIO_Text2ColSkipEmptyCells
= CSVIO_FromRow
,
80 CSVIO_DetectSpecialNum
,
81 CSVIO_DetectScientificNum
,
83 // Plus one not for SC_IMPORTFILE.
84 CSVIO_PasteSkipEmptyCells
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
,
103 u
"TextSeparators"_ustr
,
106 u
"EvaluateFormulas"_ustr
,
107 u
"SeparatorType"_ustr
,
110 u
"QuotedFieldAsText"_ustr
,
111 u
"DetectSpecialNumbers"_ustr
,
112 u
"DetectScientificNumbers"_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";
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
)
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
)
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
)
155 OUString aStr
= rCombo
.get_active_text();
156 if ( !aStr
.isEmpty() && !rList
.empty() )
159 OUString sToken
{o3tl::getToken(rList
, 0, '\t', nIdx
)};
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
);
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') )
176 else // keep old behaviour for compatibility (i.e. "39" -> "'")
177 c
= static_cast<sal_Unicode
>(aStr
.toInt32()); // Ascii
183 static void lcl_CreatePropertiesNames ( OUString
& rSepPath
, Sequence
<OUString
>& rNames
, ScImportAsciiCall eCall
)
185 sal_Int32 nProperties
= 0;
190 rSepPath
= aSep_Path
;
194 rSepPath
= aSep_Path_Clpbrd
;
197 case SC_TEXTTOCOLUMNS
:
199 rSepPath
= aSep_Path_Text2Col
;
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
;
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
;
305 Sequence
<OUString
> aNames
;
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
)
344 , mcTextSep(ScAsciiOptions::cDefaultTextSep
)
345 , meDetectedCharSet(RTL_TEXTENCODING_DONTKNOW
)
346 , mbCharSetDetect(true)
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();
384 case SC_TEXTTOCOLUMNS
:
385 m_xDialog
->set_title(mxAltTitle
->get_label());
388 if (!comphelper::LibreOfficeKit::isActive())
390 aName
+= OUString::Concat(" - [") + aDatName
+ "]";
391 m_xDialog
->set_title(aName
);
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
,
422 maFieldSeparators
= sFieldSeparators
;
424 if( bMergeDelimiters
&& !bIsTSV
)
425 mxCkbAsOnce
->set_active(true);
426 if (bQuotedFieldAsText
)
427 mxCkbQuotedAsText
->set_active(true);
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);
441 mxCkbSkipEmptyCells
->set_active(true);
442 if (eSepType
== SeparatorType::FIXED
)
446 eSepType
= SeparatorType::SEPARATOR
;
447 mxRbSeparated
->set_active(true);
450 mxRbFixed
->set_active(true);
452 else if (eSepType
== SeparatorType::SEPARATOR
)
453 mxRbSeparated
->set_active(true);
455 mxRbDetectSep
->set_active(true);
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
;
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
);
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 ***
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
) );
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
*)
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
)
609 bool bFixed
= mxRbFixed
->get_active();
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));
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())
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
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
);
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
)
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
);
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;
724 mxCkbOther
->set_active(true);
725 mxEdOther
->set_text(OUStringChar(cSep
));
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;
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
);
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
));
763 mxFtDetectedCharSet
->set_label(SvxTextEncodingTable::GetTextString(meCharSet
));
765 if (eOldCharSet
!= meCharSet
)
766 DetectCsvSeparators();
771 OUString
ScImportAsciiDlg::GetSeparators() const
774 if( mxCkbTab
->get_active() )
776 if( mxCkbSemicolon
->get_active() )
778 if( mxCkbComma
->get_active() )
780 if( mxCkbSpace
->get_active() )
782 if( mxCkbOther
->get_active() )
783 aSepChars
+= mxEdOther
->get_text();
787 OUString
ScImportAsciiDlg::GetActiveSeparators() const
789 if (mxRbSeparated
->get_active())
790 return GetSeparators();
792 if (mxRbDetectSep
->get_active())
793 return maDetectedFieldSeps
;
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
);
815 if (maDetectedFieldSeps
.isEmpty())
816 aSepName
+= ScResId(SCSTR_NONE
);
819 for (int idx
= 0; idx
< maDetectedFieldSeps
.getLength(); idx
++)
824 if (maDetectedFieldSeps
[idx
] == u
' ')
825 aSepName
+= ScResId(SCSTR_FIELDSEP_SPACE
);
826 else if (maDetectedFieldSeps
[idx
] == u
'\t')
827 aSepName
+= ScResId(SCSTR_FIELDSEP_TAB
);
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()
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();
858 mxTableBox
->Refresh();
861 mxTableBox
->SetFixedWidthMode();
863 SetupSeparatorCtrls();
866 IMPL_LINK(ScImportAsciiDlg
, RbSepFixHdl
, weld::Toggleable
&, rButton
, void)
868 if (!rButton
.get_active())
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);
898 mxCkbDetectScientificNumber
->set_sensitive(true);
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
)
930 mxTableBox
->Refresh();
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
)
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
976 if (!mnRowPosCount
|| nRead
> CSV_PREVIEW_LINES
)
977 nRead
= CSV_PREVIEW_LINES
;
980 for (i
= 0; i
< nRead
; i
++)
982 if (!GetLine( nBaseLine
+ i
, maPreviewLine
[i
], cDetectSep
))
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
);
1005 mxLbType
->set_active(-1);
1007 mxLbType
->set_active(nType
);
1010 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */