LibreOffice Module sc (master)  1
scuiasciiopt.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #undef SC_DLLIMPLEMENTATION
21 
22 #include <svx/txencbox.hxx>
23 
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>
33 
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 
42 #include <unicode/ucsdet.h>
43 
46 
47 using namespace com::sun::star::uno;
48 
49 namespace {
50 
51 // Defines - CSV Import Preserve Options
53 {
54  CSVIO_MergeDelimiters = 0,
55  CSVIO_Separators,
56  CSVIO_TextSeparators,
57  CSVIO_FixedWidth,
58  CSVIO_RemoveSpace,
59  CSVIO_FromRow,
60  CSVIO_Text2ColSkipEmptyCells = CSVIO_FromRow,
61  CSVIO_CharSet,
62  CSVIO_QuotedAsText,
63  CSVIO_DetectSpecialNum,
64  CSVIO_Language,
65  CSVIO_PasteSkipEmptyCells
66 };
67 
68 }
69 
70 const ::std::vector<OUString> CSVImportOptionNames =
71 {
72  "MergeDelimiters",
73  "Separators",
74  "TextSeparators",
75  "FixedWidth",
76  "RemoveSpace",
77  "FromRow",
78  "CharSet",
79  "QuotedFieldAsText",
80  "DetectSpecialNumbers",
81  "Language",
82  "SkipEmptyCells"
83 };
84 constexpr OUStringLiteral aSep_Path = u"Office.Calc/Dialogs/CSVImport";
85 constexpr OUStringLiteral aSep_Path_Clpbrd = u"Office.Calc/Dialogs/ClipboardTextImport";
86 constexpr OUStringLiteral aSep_Path_Text2Col = u"Office.Calc/Dialogs/TextToColumnsImport";
87 
88 namespace {
89 CSVImportOptionsIndex getSkipEmptyCellsIndex( ScImportAsciiCall eCall )
90 {
91  return eCall == SC_TEXTTOCOLUMNS ? CSVIO_Text2ColSkipEmptyCells : CSVIO_PasteSkipEmptyCells;
92 }
93 }
94 
95 static void lcl_FillCombo(weld::ComboBox& rCombo, const OUString& rList, sal_Unicode cSelect)
96 {
97  OUString aStr;
98  if (!rList.isEmpty())
99  {
100  sal_Int32 nIdx {0};
101  do
102  {
103  const OUString sEntry {rList.getToken(0, '\t', nIdx)};
104  rCombo.append_text(sEntry);
105  if (nIdx>0 && static_cast<sal_Unicode>(rList.getToken(0, '\t', nIdx).toInt32()) == cSelect)
106  aStr = sEntry;
107  }
108  while (nIdx>0);
109  }
110 
111  if ( cSelect )
112  {
113  if (aStr.isEmpty())
114  aStr = OUString(cSelect); // Ascii
115 
116  rCombo.set_entry_text(aStr);
117  }
118 }
119 
120 static sal_Unicode lcl_CharFromCombo(const weld::ComboBox& rCombo, const OUString& rList)
121 {
122  sal_Unicode c = 0;
123  OUString aStr = rCombo.get_active_text();
124  if ( !aStr.isEmpty() && !rList.isEmpty() )
125  {
126  sal_Int32 nIdx {0};
127  OUString sToken {rList.getToken(0, '\t', nIdx)};
128  while (nIdx>0)
129  {
130  if ( ScGlobal::GetpTransliteration()->isEqual( aStr, sToken ) )
131  {
132  sal_Int32 nTmpIdx {nIdx};
133  c = static_cast<sal_Unicode>(rList.getToken(0, '\t', nTmpIdx).toInt32());
134  }
135  // Skip to next token at even position
136  sToken = rList.getToken(1, '\t', nIdx);
137  }
138  if (!c)
139  {
140  sal_Unicode cFirst = aStr[0];
141  // #i24235# first try the first character of the string directly
142  if( (aStr.getLength() == 1) || (cFirst < '0') || (cFirst > '9') )
143  c = cFirst;
144  else // keep old behaviour for compatibility (i.e. "39" -> "'")
145  c = static_cast<sal_Unicode>(aStr.toInt32()); // Ascii
146  }
147  }
148  return c;
149 }
150 
151 static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence<OUString>& rNames, ScImportAsciiCall eCall )
152 {
153  sal_Int32 nProperties = 0;
154 
155  switch(eCall)
156  {
157  case SC_IMPORTFILE:
158  rSepPath = aSep_Path;
159  nProperties = 10;
160  break;
161  case SC_PASTETEXT:
162  rSepPath = aSep_Path_Clpbrd;
163  nProperties = 11;
164  break;
165  case SC_TEXTTOCOLUMNS:
166  default:
167  rSepPath = aSep_Path_Text2Col;
168  nProperties = 6;
169  break;
170  }
171  rNames.realloc( nProperties );
172  OUString* pNames = rNames.getArray();
173  pNames[ CSVIO_MergeDelimiters ] = CSVImportOptionNames[ CSVIO_MergeDelimiters ];
174  pNames[ CSVIO_Separators ] = CSVImportOptionNames[ CSVIO_Separators ];
175  pNames[ CSVIO_TextSeparators ] = CSVImportOptionNames[ CSVIO_TextSeparators ];
176  pNames[ CSVIO_FixedWidth ] = CSVImportOptionNames[ CSVIO_FixedWidth ];
177  pNames[ CSVIO_RemoveSpace ] = CSVImportOptionNames[ CSVIO_RemoveSpace ];
178  if (eCall != SC_TEXTTOCOLUMNS)
179  {
180  pNames[ CSVIO_FromRow ] = CSVImportOptionNames[ CSVIO_FromRow ];
181  pNames[ CSVIO_CharSet ] = CSVImportOptionNames[ CSVIO_CharSet ];
182  pNames[ CSVIO_QuotedAsText ] = CSVImportOptionNames[ CSVIO_QuotedAsText ];
183  pNames[ CSVIO_DetectSpecialNum ] = CSVImportOptionNames[ CSVIO_DetectSpecialNum ];
184  pNames[ CSVIO_Language ] = CSVImportOptionNames[ CSVIO_Language ];
185  }
186  if (eCall != SC_IMPORTFILE)
187  {
188  const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
189  assert( nSkipEmptyCells < rNames.getLength());
190  pNames[ nSkipEmptyCells ] = CSVImportOptionNames[ CSVIO_PasteSkipEmptyCells ];
191  }
192 }
193 
194 static void lcl_LoadSeparators( OUString& rFieldSeparators, OUString& rTextSeparators,
195  bool& rMergeDelimiters, bool& rQuotedAsText, bool& rDetectSpecialNum,
196  bool& rFixedWidth, sal_Int32& rFromRow, sal_Int32& rCharSet,
197  sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace, ScImportAsciiCall eCall )
198 {
199  Sequence<Any>aValues;
200  const Any *pProperties;
201  Sequence<OUString> aNames;
202  OUString aSepPath;
203  lcl_CreatePropertiesNames ( aSepPath, aNames, eCall);
204  ScLinkConfigItem aItem( aSepPath );
205  aValues = aItem.GetProperties( aNames );
206  pProperties = aValues.getConstArray();
207 
208  if( pProperties[ CSVIO_MergeDelimiters ].hasValue() )
209  rMergeDelimiters = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_MergeDelimiters ] );
210 
211  if( pProperties[ CSVIO_RemoveSpace ].hasValue() )
212  rRemoveSpace = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_RemoveSpace ] );
213 
214  if( pProperties[ CSVIO_Separators ].hasValue() )
215  pProperties[ CSVIO_Separators ] >>= rFieldSeparators;
216 
217  if( pProperties[ CSVIO_TextSeparators ].hasValue() )
218  pProperties[ CSVIO_TextSeparators ] >>= rTextSeparators;
219 
220  if( pProperties[ CSVIO_FixedWidth ].hasValue() )
221  rFixedWidth = ScUnoHelpFunctions::GetBoolFromAny( pProperties[ CSVIO_FixedWidth ] );
222 
223  if (eCall != SC_TEXTTOCOLUMNS)
224  {
225  if( pProperties[ CSVIO_FromRow ].hasValue() )
226  pProperties[ CSVIO_FromRow ] >>= rFromRow;
227 
228  if( pProperties[ CSVIO_CharSet ].hasValue() )
229  pProperties[ CSVIO_CharSet ] >>= rCharSet;
230 
231  if ( pProperties[ CSVIO_QuotedAsText ].hasValue() )
232  pProperties[ CSVIO_QuotedAsText ] >>= rQuotedAsText;
233 
234  if ( pProperties[ CSVIO_DetectSpecialNum ].hasValue() )
235  pProperties[ CSVIO_DetectSpecialNum ] >>= rDetectSpecialNum;
236 
237  if ( pProperties[ CSVIO_Language ].hasValue() )
238  pProperties[ CSVIO_Language ] >>= rLanguage;
239  }
240  if (eCall != SC_IMPORTFILE)
241  {
242  const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
243  assert( nSkipEmptyCells < aValues.getLength());
244  if ( pProperties[nSkipEmptyCells].hasValue() )
245  rSkipEmptyCells = ScUnoHelpFunctions::GetBoolFromAny( pProperties[nSkipEmptyCells] );
246  }
247 }
248 
249 static void lcl_SaveSeparators(
250  const OUString& sFieldSeparators, const OUString& sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText,
251  bool bDetectSpecialNum, bool bFixedWidth, sal_Int32 nFromRow,
252  sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, ScImportAsciiCall eCall )
253 {
254  Sequence<Any> aValues;
255  Any *pProperties;
256  Sequence<OUString> aNames;
257  OUString aSepPath;
258  lcl_CreatePropertiesNames ( aSepPath, aNames, eCall );
259  ScLinkConfigItem aItem( aSepPath );
260  aValues = aItem.GetProperties( aNames );
261  pProperties = aValues.getArray();
262 
263  pProperties[ CSVIO_MergeDelimiters ] <<= bMergeDelimiters;
264  pProperties[ CSVIO_RemoveSpace ] <<= bRemoveSpace;
265  pProperties[ CSVIO_Separators ] <<= sFieldSeparators;
266  pProperties[ CSVIO_TextSeparators ] <<= sTextSeparators;
267  pProperties[ CSVIO_FixedWidth ] <<= bFixedWidth;
268  if (eCall != SC_TEXTTOCOLUMNS)
269  {
270  pProperties[ CSVIO_FromRow ] <<= nFromRow;
271  pProperties[ CSVIO_CharSet ] <<= nCharSet;
272  pProperties[ CSVIO_QuotedAsText ] <<= bQuotedAsText;
273  pProperties[ CSVIO_DetectSpecialNum ] <<= bDetectSpecialNum;
274  pProperties[ CSVIO_Language ] <<= nLanguage;
275  }
276  if (eCall != SC_IMPORTFILE)
277  {
278  const sal_Int32 nSkipEmptyCells = getSkipEmptyCellsIndex(eCall);
279  assert( nSkipEmptyCells < aValues.getLength());
280  pProperties[ nSkipEmptyCells ] <<= bSkipEmptyCells;
281  }
282 
283  aItem.PutProperties(aNames, aValues);
284 }
285 
286 constexpr OUStringLiteral gaTextSepList(u"" SCSTR_TEXTSEP);
287 
288 ScImportAsciiDlg::ScImportAsciiDlg(weld::Window* pParent, const OUString& aDatName,
289  SvStream* pInStream, ScImportAsciiCall eCall)
290  : GenericDialogController(pParent, "modules/scalc/ui/textimportcsv.ui", "TextImportCsvDialog")
291  , mpDatStream(pInStream)
292  , mnStreamPos(pInStream ? pInStream->Tell() : 0)
293  , mnRowPosCount(0)
294  , mcTextSep(ScAsciiOptions::cDefaultTextSep)
295  , meCall(eCall)
296  , mbDetectSpaceSep(eCall != SC_TEXTTOCOLUMNS)
297  , mxFtCharSet(m_xBuilder->weld_label("textcharset"))
298  , mxLbCharSet(new SvxTextEncodingBox(m_xBuilder->weld_combo_box("charset")))
299  , mxFtCustomLang(m_xBuilder->weld_label("textlanguage"))
300  , mxLbCustomLang(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
301  , mxFtRow(m_xBuilder->weld_label("textfromrow"))
302  , mxNfRow(m_xBuilder->weld_spin_button("fromrow"))
303  , mxRbFixed(m_xBuilder->weld_radio_button("tofixedwidth"))
304  , mxRbSeparated(m_xBuilder->weld_radio_button("toseparatedby"))
305  , mxCkbTab(m_xBuilder->weld_check_button("tab"))
306  , mxCkbSemicolon(m_xBuilder->weld_check_button("semicolon"))
307  , mxCkbComma(m_xBuilder->weld_check_button("comma"))
308  , mxCkbRemoveSpace(m_xBuilder->weld_check_button("removespace"))
309  , mxCkbSpace(m_xBuilder->weld_check_button("space"))
310  , mxCkbOther(m_xBuilder->weld_check_button("other"))
311  , mxEdOther(m_xBuilder->weld_entry("inputother"))
312  , mxCkbAsOnce(m_xBuilder->weld_check_button("mergedelimiters"))
313  , mxFtTextSep(m_xBuilder->weld_label("texttextdelimiter"))
314  , mxCbTextSep(m_xBuilder->weld_combo_box("textdelimiter"))
315  , mxCkbQuotedAsText(m_xBuilder->weld_check_button("quotedfieldastext"))
316  , mxCkbDetectNumber(m_xBuilder->weld_check_button("detectspecialnumbers"))
317  , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button("skipemptycells"))
318  , mxFtType(m_xBuilder->weld_label("textcolumntype"))
319  , mxLbType(m_xBuilder->weld_combo_box("columntype"))
320  , mxAltTitle(m_xBuilder->weld_label("textalttitle"))
321  , mxTableBox(new ScCsvTableBox(*m_xBuilder))
322 {
323  OUString aName = m_xDialog->get_title();
324  switch (meCall)
325  {
326  case SC_TEXTTOCOLUMNS:
327  m_xDialog->set_title(mxAltTitle->get_label());
328  break;
329  case SC_IMPORTFILE:
331  {
332  aName += " - [" + aDatName + "]";
333  m_xDialog->set_title(aName);
334  }
335  break;
336  default:
337  break;
338  }
339 
340  // To be able to prefill the correct values based on the file extension
341  bool bIsTSV = (aDatName.endsWithIgnoreAsciiCase(".tsv") || aDatName.endsWithIgnoreAsciiCase(".tab"));
342 
343  // Default options are set in officecfg/registry/schema/org/openoffice/Office/Calc.xcs
344  OUString sFieldSeparators(",;\t");
345  OUString sTextSeparators(mcTextSep);
346  bool bMergeDelimiters = false;
347  bool bFixedWidth = false;
348  bool bQuotedFieldAsText = false;
349  bool bDetectSpecialNum = true;
350  bool bSkipEmptyCells = true;
351  bool bRemoveSpace = false;
352  sal_Int32 nFromRow = 1;
353  sal_Int32 nCharSet = -1;
354  sal_Int32 nLanguage = 0;
355  lcl_LoadSeparators (sFieldSeparators, sTextSeparators, bMergeDelimiters,
356  bQuotedFieldAsText, bDetectSpecialNum, bFixedWidth, nFromRow,
357  nCharSet, nLanguage, bSkipEmptyCells, bRemoveSpace, meCall);
358  // load from saved settings
359  maFieldSeparators = sFieldSeparators;
360 
361  if( bMergeDelimiters && !bIsTSV )
362  mxCkbAsOnce->set_active(true);
363  if (bQuotedFieldAsText)
364  mxCkbQuotedAsText->set_active(true);
365  if (bRemoveSpace)
366  mxCkbRemoveSpace->set_active(true);
367  if (bDetectSpecialNum)
368  mxCkbDetectNumber->set_active(true);
369  if (bSkipEmptyCells)
370  mxCkbSkipEmptyCells->set_active(true);
371  if (bFixedWidth && !bIsTSV)
372  mxRbFixed->set_active(true);
373  if (nFromRow != 1)
374  mxNfRow->set_value(nFromRow);
375 
376  if ( bIsTSV )
377  mxCkbTab->set_active(true);
378  else
379  SetSeparators(); // Set Separators in the dialog from maFieldSeparators (empty are not set)
380 
381  // Get Separators from the dialog (empty are set from default)
383 
384  // Clipboard is always Unicode, else detect.
385  rtl_TextEncoding ePreselectUnicode = (meCall == SC_IMPORTFILE ?
386  RTL_TEXTENCODING_DONTKNOW : RTL_TEXTENCODING_UNICODE);
387  // Sniff for Unicode / not
388  if( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW && mpDatStream )
389  {
390  mpDatStream->Seek( 0 );
391  constexpr size_t buffsize = 4096;
392  sal_Int8 bytes[buffsize] = { 0 };
393  sal_Int32 nRead = mpDatStream->ReadBytes( bytes, buffsize );
394  mpDatStream->Seek( 0 );
395 
396  if ( nRead > 0 )
397  {
398  UErrorCode uerr = U_ZERO_ERROR;
399  UCharsetDetector* ucd = ucsdet_open( &uerr );
400  ucsdet_setText( ucd, reinterpret_cast<const char*>(bytes), nRead, &uerr );
401 
402  if ( const UCharsetMatch* match = ucsdet_detect(ucd, &uerr) )
403  {
404  const char* pEncodingName = ucsdet_getName( match, &uerr );
405 
406  if ( U_SUCCESS(uerr) && !strcmp("UTF-8", pEncodingName) )
407  {
408  ePreselectUnicode = RTL_TEXTENCODING_UTF8; // UTF-8
409  mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UTF8 );
410  }
411  else if ( U_SUCCESS(uerr) && !strcmp("UTF-16LE", pEncodingName) )
412  {
413  ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16LE
414  mpDatStream->SetEndian( SvStreamEndian::LITTLE );
415  mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
416  }
417  else if ( U_SUCCESS(uerr) && !strcmp("UTF-16BE", pEncodingName) )
418  {
419  ePreselectUnicode = RTL_TEXTENCODING_UNICODE; // UTF-16BE
420  mpDatStream->SetEndian( SvStreamEndian::BIG );
421  mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_UNICODE );
422  }
423  else // other
424  mpDatStream->StartReadingUnicodeText( RTL_TEXTENCODING_DONTKNOW );
425  }
426 
427  ucsdet_close( ucd );
428  }
429 
431  }
432 
433  mxNfRow->connect_value_changed( LINK( this, ScImportAsciiDlg, FirstRowHdl ) );
434 
435  // *** Separator characters ***
437  mxCbTextSep->set_entry_text(sTextSeparators);
438 
439  Link<weld::Toggleable&,void> aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl );
440  mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) );
441  mxCkbTab->connect_toggled( aSeparatorClickHdl );
442  mxCkbSemicolon->connect_toggled( aSeparatorClickHdl );
443  mxCkbComma->connect_toggled( aSeparatorClickHdl );
444  mxCkbAsOnce->connect_toggled( aSeparatorClickHdl );
445  mxCkbQuotedAsText->connect_toggled( aSeparatorClickHdl );
446  mxCkbDetectNumber->connect_toggled( aSeparatorClickHdl );
447  mxCkbSkipEmptyCells->connect_toggled( aSeparatorClickHdl );
448  mxCkbSpace->connect_toggled( aSeparatorClickHdl );
449  mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl );
450  mxCkbOther->connect_toggled( aSeparatorClickHdl );
451  mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl));
452 
453  // *** text encoding ListBox ***
454  // all encodings allowed, including Unicode, but subsets are excluded
455  mxLbCharSet->FillFromTextEncodingTable( true );
456  // Insert one "SYSTEM" entry for compatibility in AsciiOptions and system
457  // independent document linkage.
458  mxLbCharSet->InsertTextEncoding( RTL_TEXTENCODING_DONTKNOW, ScResId( SCSTR_CHARSET_USER ) );
459  if ( ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW )
460  {
461  rtl_TextEncoding eSystemEncoding = osl_getThreadTextEncoding();
462  // Prefer UTF-8, as UTF-16 would have already been detected from the stream.
463  // This gives a better chance that the file is going to be opened correctly.
464  if ( ( eSystemEncoding == RTL_TEXTENCODING_UNICODE ) && mpDatStream )
465  eSystemEncoding = RTL_TEXTENCODING_UTF8;
466  mxLbCharSet->SelectTextEncoding( eSystemEncoding );
467  }
468  else
469  {
470  mxLbCharSet->SelectTextEncoding( ePreselectUnicode );
471  }
472 
473  if (nCharSet >= 0 && ePreselectUnicode == RTL_TEXTENCODING_DONTKNOW)
474  mxLbCharSet->set_active(nCharSet);
475 
477  mxLbCharSet->connect_changed( LINK( this, ScImportAsciiDlg, CharSetHdl ) );
478 
479  mxLbCustomLang->SetLanguageList(
480  SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false, false);
481  mxLbCustomLang->InsertLanguage(LANGUAGE_SYSTEM);
482  mxLbCustomLang->set_active_id(static_cast<LanguageType>(nLanguage));
483 
484  // *** column type ListBox ***
485  OUString aColumnUser( ScResId( SCSTR_COLUMN_USER ) );
486  for (sal_Int32 nIdx {0}; nIdx>=0; )
487  {
488  mxLbType->append_text(aColumnUser.getToken(0, ';', nIdx));
489  }
490 
491  mxLbType->connect_changed( LINK( this, ScImportAsciiDlg, LbColTypeHdl ) );
492  mxFtType->set_sensitive(false);
493  mxLbType->set_sensitive(false);
494 
495  // *** table box preview ***
496  mxTableBox->Init();
497  mxTableBox->SetUpdateTextHdl( LINK( this, ScImportAsciiDlg, UpdateTextHdl ) );
498  mxTableBox->InitTypes( *mxLbType );
499  mxTableBox->SetColTypeHdl( LINK( this, ScImportAsciiDlg, ColTypeHdl ) );
500 
501  mxRbSeparated->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
502  mxRbFixed->connect_toggled( LINK( this, ScImportAsciiDlg, RbSepFixHdl ) );
503 
505  RbSepFix();
506 
507  UpdateVertical();
508 
509  mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
510 
511  if (meCall == SC_TEXTTOCOLUMNS)
512  {
513  mxFtCharSet->set_sensitive(false);
514  mxLbCharSet->set_sensitive(false);
515  mxFtCustomLang->set_sensitive(false);
516  mxLbCustomLang->set_active_id(LANGUAGE_SYSTEM);
517  mxLbCustomLang->set_sensitive(false);
518  mxFtRow->set_sensitive(false);
519  mxNfRow->set_sensitive(false);
520 
521  // Quoted field as text option is not used for text-to-columns mode.
522  mxCkbQuotedAsText->set_active(false);
523  mxCkbQuotedAsText->set_sensitive(false);
524 
525  // Always detect special numbers for text-to-columns mode.
526  mxCkbDetectNumber->set_active(true);
527  mxCkbDetectNumber->set_sensitive(false);
528  }
529  if (meCall == SC_IMPORTFILE)
530  {
531  //Empty cells in imported file are empty
532  mxCkbSkipEmptyCells->set_active(false);
533  mxCkbSkipEmptyCells->hide();
534  }
535  m_xDialog->SetInstallLOKNotifierHdl(LINK(this, ScImportAsciiDlg, InstallLOKNotifierHdl));
536 }
537 
538 IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
539 {
540  return GetpApp();
541 }
542 
544 {
545 }
546 
547 bool ScImportAsciiDlg::GetLine( sal_uLong nLine, OUString &rText, sal_Unicode& rcDetectSep )
548 {
549  if (nLine >= ASCIIDLG_MAXROWS || !mpDatStream)
550  return false;
551 
552  bool bRet = true;
553  bool bFixed = mxRbFixed->get_active();
554 
555  if (!mpRowPosArray)
556  mpRowPosArray.reset( new sal_uLong[ASCIIDLG_MAXROWS + 2] );
557 
558  if (!mnRowPosCount) // complete re-fresh
559  {
560  memset( mpRowPosArray.get(), 0, sizeof(mpRowPosArray[0]) * (ASCIIDLG_MAXROWS+2));
561 
562  Seek(0);
564 
567  }
568 
569  if (nLine >= mnRowPosCount)
570  {
571  // need to work out some more line information
572  do
573  {
575  {
576  bRet = false;
577  break;
578  }
579  rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators,
580  mcTextSep, rcDetectSep);
583  } while (nLine >= mnRowPosCount && mpDatStream->good());
584  if (mpDatStream->eof() &&
586  {
587  // the very end, not even an empty line read
588  bRet = false;
589  --mnRowPosCount;
590  }
591  }
592  else
593  {
594  Seek( mpRowPosArray[nLine]);
595  rText = ReadCsvLine(*mpDatStream, !bFixed, maFieldSeparators, mcTextSep, rcDetectSep);
597  }
598 
599  // If the file content isn't unicode, ReadUniStringLine
600  // may try to seek beyond the file's end and cause a CANTSEEK error
601  // (depending on the stream type). The error code has to be cleared,
602  // or further read operations (including non-unicode) will fail.
605 
607 
608  return bRet;
609 }
610 
612 {
613  rOpt.SetCharSet( meCharSet );
615  rOpt.SetLanguage(mxLbCustomLang->get_active_id());
616  rOpt.SetFixedLen( mxRbFixed->get_active() );
617  rOpt.SetStartRow( mxNfRow->get_value() );
618  mxTableBox->FillColumnData( rOpt );
619  if( mxRbSeparated->get_active() )
620  {
621  rOpt.SetFieldSeps( GetSeparators() );
622  rOpt.SetMergeSeps( mxCkbAsOnce->get_active() );
623  rOpt.SetRemoveSpace( mxCkbRemoveSpace->get_active() );
625  }
626 
627  rOpt.SetQuotedAsText(mxCkbQuotedAsText->get_active());
628  rOpt.SetDetectSpecialNumber(mxCkbDetectNumber->get_active());
629  rOpt.SetSkipEmptyCells(mxCkbSkipEmptyCells->get_active());
630 }
631 
633 {
634  lcl_SaveSeparators( maFieldSeparators, mxCbTextSep->get_active_text(), mxCkbAsOnce->get_active(),
635  mxCkbQuotedAsText->get_active(), mxCkbDetectNumber->get_active(),
636  mxRbFixed->get_active(),
637  mxNfRow->get_value(),
638  mxLbCharSet->get_active(),
639  static_cast<sal_uInt16>(mxLbCustomLang->get_active_id()),
640  mxCkbSkipEmptyCells->get_active(), mxCkbRemoveSpace->get_active(), meCall );
641 }
642 
644 {
645  OString sString(OUStringToOString(maFieldSeparators,
646  RTL_TEXTENCODING_MS_1252));
647  const char *aSep = sString.getStr();
648  sal_Int32 len = maFieldSeparators.getLength();
649  for (int i = 0; i < len; ++i)
650  {
651  switch( aSep[i] )
652  {
653  case '\t': mxCkbTab->set_active(true); break;
654  case ';': mxCkbSemicolon->set_active(true); break;
655  case ',': mxCkbComma->set_active(true); break;
656  case ' ': mxCkbSpace->set_active(true); break;
657  default:
658  mxCkbOther->set_active(true);
659  mxEdOther->set_text(mxEdOther->get_text() + OUStringChar(aSep[i]));
660  }
661  }
662 }
663 
665 {
666  meCharSet = mxLbCharSet->GetSelectTextEncoding();
667  mbCharSetSystem = (meCharSet == RTL_TEXTENCODING_DONTKNOW);
668  if( mbCharSetSystem )
669  meCharSet = osl_getThreadTextEncoding();
670 }
671 
673 {
674  OUString aSepChars;
675  if( mxCkbTab->get_active() )
676  aSepChars += "\t";
677  if( mxCkbSemicolon->get_active() )
678  aSepChars += ";";
679  if( mxCkbComma->get_active() )
680  aSepChars += ",";
681  if( mxCkbSpace->get_active() )
682  aSepChars += " ";
683  if( mxCkbOther->get_active() )
684  aSepChars += mxEdOther->get_text();
685  return aSepChars;
686 }
687 
689 {
690  bool bEnable = mxRbSeparated->get_active();
691  mxCkbTab->set_sensitive( bEnable );
692  mxCkbSemicolon->set_sensitive( bEnable );
693  mxCkbComma->set_sensitive( bEnable );
694  mxCkbSpace->set_sensitive( bEnable );
695  mxCkbRemoveSpace->set_sensitive( bEnable );
696  mxCkbOther->set_sensitive( bEnable );
697  mxEdOther->set_sensitive( bEnable );
698  mxCkbAsOnce->set_sensitive( bEnable );
699  mxFtTextSep->set_sensitive( bEnable );
700  mxCbTextSep->set_sensitive( bEnable );
701 }
702 
704 {
705  mnRowPosCount = 0;
706  if (mpDatStream)
708 }
709 
711 {
712  weld::WaitObject aWaitObj(m_xDialog.get());
713  if( mxRbFixed->get_active() )
714  mxTableBox->SetFixedWidthMode();
715  else
716  mxTableBox->SetSeparatorsMode();
718 }
719 
720 IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable&, rButton, void)
721 {
722  if (!rButton.get_active())
723  return;
724  RbSepFix();
725 }
726 
727 IMPL_LINK(ScImportAsciiDlg, SeparatorClickHdl, weld::Toggleable&, rCtrl, void)
728 {
729  SeparatorHdl(&rCtrl);
730 }
731 
732 IMPL_LINK( ScImportAsciiDlg, SeparatorComboBoxHdl, weld::ComboBox&, rCtrl, void )
733 {
734  SeparatorHdl(&rCtrl);
735 }
736 
737 IMPL_LINK( ScImportAsciiDlg, SeparatorEditHdl, weld::Entry&, rEdit, void )
738 {
739  SeparatorHdl(&rEdit);
740 }
741 
743 {
744  OSL_ENSURE( pCtrl, "ScImportAsciiDlg::SeparatorHdl - missing sender" );
745  OSL_ENSURE( !mxRbFixed->get_active(), "ScImportAsciiDlg::SeparatorHdl - not allowed in fixed width" );
746 
747  /* #i41550# First update state of the controls. The GetSeparators()
748  function needs final state of the check boxes. */
749  if (pCtrl == mxCkbOther.get() && mxCkbOther->get_active())
750  mxEdOther->grab_focus();
751  else if (pCtrl == mxEdOther.get())
752  mxCkbOther->set_active(!mxEdOther->get_text().isEmpty());
753 
754  OUString aOldFldSeps( maFieldSeparators);
756  sal_Unicode cOldSep = mcTextSep;
758  // Any separator changed may result in completely different lines due to
759  // embedded line breaks.
760  if (cOldSep != mcTextSep || aOldFldSeps != maFieldSeparators)
761  UpdateVertical();
762 
763  mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
764 }
765 
767 {
768  if (mxLbCharSet->get_active() != -1)
769  {
770  weld::WaitObject aWaitObj(m_xDialog.get());
771  rtl_TextEncoding eOldCharSet = meCharSet;
772  SetSelectedCharSet();
773  // switching char-set invalidates 8bit -> String conversions
774  if (eOldCharSet != meCharSet)
775  UpdateVertical();
776 
777  mxTableBox->GetGrid().Execute( CSVCMD_NEWCELLTEXTS );
778  }
779 }
780 
781 IMPL_LINK(ScImportAsciiDlg, FirstRowHdl, weld::SpinButton&, rNumField, void)
782 {
783  mxTableBox->GetGrid().Execute( CSVCMD_SETFIRSTIMPORTLINE, rNumField.get_value() - 1);
784 }
785 
786 IMPL_LINK(ScImportAsciiDlg, LbColTypeHdl, weld::ComboBox&, rListBox, void)
787 {
788  if (&rListBox == mxLbType.get())
789  mxTableBox->GetGrid().Execute(CSVCMD_SETCOLUMNTYPE, rListBox.get_active());
790 }
791 
793 {
794  // Checking the separator can only be done once for the very first time
795  // when the dialog wasn't already presented to the user.
796  // As a side effect this has the benefit that the check is only done on the
797  // first set of visible lines.
798  sal_Unicode cDetectSep = (mbDetectSpaceSep && !mxRbFixed->get_active() && !mxCkbSpace->get_active() ? 0 : 0xffff);
799 
800  sal_Int32 nBaseLine = mxTableBox->GetGrid().GetFirstVisLine();
801  sal_Int32 nRead = mxTableBox->GetGrid().GetVisLineCount();
802  // If mnRowPosCount==0, this is an initializing call, read ahead for row
803  // count and resulting scroll bar size and position to be able to scroll at
804  // all. When adding lines, read only the amount of next lines to be
805  // displayed.
806  if (!mnRowPosCount || nRead > CSV_PREVIEW_LINES)
807  nRead = CSV_PREVIEW_LINES;
808 
809  sal_Int32 i;
810  for (i = 0; i < nRead; i++)
811  {
812  if (!GetLine( nBaseLine + i, maPreviewLine[i], cDetectSep))
813  break;
814  }
815  for (; i < CSV_PREVIEW_LINES; i++)
816  maPreviewLine[i].clear();
817 
818  if (mbDetectSpaceSep)
819  {
820  mbDetectSpaceSep = false;
821  if (cDetectSep == ' ')
822  {
823  // Expect space to be appended by now so all subsequent
824  // GetLine()/ReadCsvLine() actually used it.
825  assert(maFieldSeparators.endsWith(" "));
826  // Preselect Space in UI.
827  mxCkbSpace->set_active(true);
828  }
829  }
830 
831  mxTableBox->GetGrid().Execute( CSVCMD_SETLINECOUNT, mnRowPosCount);
832  bool bMergeSep = mxCkbAsOnce->get_active();
833  bool bRemoveSpace = mxCkbRemoveSpace->get_active();
834  mxTableBox->SetUniStrings( maPreviewLine, maFieldSeparators, mcTextSep, bMergeSep, bRemoveSpace );
835 }
836 
837 IMPL_LINK( ScImportAsciiDlg, ColTypeHdl, ScCsvTableBox&, rTableBox, void )
838 {
839  sal_Int32 nType = rTableBox.GetSelColumnType();
840  sal_Int32 nTypeCount = mxLbType->get_count();
841  bool bEmpty = (nType == CSV_TYPE_MULTI);
842  bool bEnable = ((0 <= nType) && (nType < nTypeCount)) || bEmpty;
843 
844  mxFtType->set_sensitive( bEnable );
845  mxLbType->set_sensitive( bEnable );
846 
847  if (bEmpty)
848  mxLbType->set_active(-1);
849  else if (bEnable)
850  mxLbType->set_active(nType);
851 }
852 
853 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::shared_ptr< weld::Dialog > m_xDialog
std::unique_ptr< weld::ComboBox > mxLbType
OUString maFieldSeparators
virtual ~ScImportAsciiDlg() override
std::unique_ptr< weld::RadioButton > mxRbFixed
std::unique_ptr< weld::CheckButton > mxCkbOther
static SC_DLLPUBLIC::utl::TransliterationWrapper * GetpTransliteration()
Definition: global.cxx:975
void SeparatorHdl(const weld::Widget *)
sal_Int32 nProperties
const ::std::vector< OUString > CSVImportOptionNames
std::unique_ptr< weld::ComboBox > mxCbTextSep
signed char sal_Int8
void SetSeparators()
Set separators in ui from maFieldSeparators.
sal_Unicode mcTextSep
std::vector< sal_uInt8 > bytes
IMPL_LINK_NOARG(ScImportAsciiDlg, CharSetHdl, weld::ComboBox &, void)
bool Seek(sal_uLong nPos)
sal_uIntPtr sal_uLong
static void lcl_FillCombo(weld::ComboBox &rCombo, const OUString &rList, sal_Unicode cSelect)
Update cell texts with current split settings. [-].
Definition: csvcontrol.hxx:184
void SetCharSetSystem(bool bSet)
Definition: asciiopt.hxx:71
sal_uInt64 Seek(sal_uInt64 nPos)
std::unique_ptr< weld::SpinButton > mxNfRow
static void lcl_CreatePropertiesNames(OUString &rSepPath, Sequence< OUString > &rNames, ScImportAsciiCall eCall)
void SetupSeparatorCtrls()
Enables or disables all separator checkboxes and edit fields.
bool GetLine(sal_uLong nLine, OUString &rText, sal_Unicode &rcDetectSep)
void SetSkipEmptyCells(bool bSet)
Definition: asciiopt.hxx:78
std::unique_ptr< weld::CheckButton > mxCkbQuotedAsText
std::unique_ptr< weld::RadioButton > mxRbSeparated
sal_uLong mnStreamPos
SvStream * mpDatStream
#define ERRCODE_IO_CANTSEEK
void SetStartRow(sal_Int32 nRow)
Definition: asciiopt.hxx:80
constexpr OUStringLiteral aSep_Path_Clpbrd
std::unique_ptr< weld::CheckButton > mxCkbSpace
bool match(const sal_Unicode *pWild, const sal_Unicode *pStr, const sal_Unicode cEscape)
constexpr OUStringLiteral gaTextSepList(u""SCSTR_TEXTSEP)
void SetCharSet(rtl_TextEncoding eNew)
Definition: asciiopt.hxx:70
sal_uInt16 sal_Unicode
static void lcl_LoadSeparators(OUString &rFieldSeparators, OUString &rTextSeparators, bool &rMergeDelimiters, bool &rQuotedAsText, bool &rDetectSpecialNum, bool &rFixedWidth, sal_Int32 &rFromRow, sal_Int32 &rCharSet, sal_Int32 &rLanguage, bool &rSkipEmptyCells, bool &rRemoveSpace, ScImportAsciiCall eCall)
void SetDetectSpecialNumber(bool bSet)
Definition: asciiopt.hxx:77
ErrCode GetError() const
bool eof() const
void StartReadingUnicodeText(rtl_TextEncoding eReadBomCharSet)
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
IMPL_STATIC_LINK_NOARG(ScImportAsciiDlg, InstallLOKNotifierHdl, void *, vcl::ILibreOfficeKitNotifier *)
const SCSIZE ASCIIDLG_MAXROWS
TODO make dynamic.
Application * GetpApp()
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::unique_ptr< weld::CheckButton > mxCkbSkipEmptyCells
const SCROW MAXROWCOUNT
Definition: address.hxx:62
std::unique_ptr< weld::CheckButton > mxCkbSemicolon
constexpr OUStringLiteral aSep_Path_Text2Col
std::unique_ptr< weld::CheckButton > mxCkbComma
OUString GetSeparators() const
Returns all separator characters in a string.
Send selected column type to external controls. [-].
Definition: csvcontrol.hxx:186
std::unique_ptr< weld::Label > mxFtCharSet
Whether to detect a possible space separator.
std::unique_ptr< weld::Label > mxFtRow
void SetQuotedAsText(bool bSet)
Definition: asciiopt.hxx:76
rtl_TextEncoding meCharSet
int i
std::unique_ptr< ScCsvTableBox > mxTableBox
void SetLanguage(LanguageType e)
Definition: asciiopt.hxx:81
#define LANGUAGE_SYSTEM
void SetRemoveSpace(bool bSet)
Definition: asciiopt.hxx:75
void SetSelectedCharSet()
Sets the selected char set data to meCharSet and mbCharSetSystem.
std::unique_ptr< weld::CheckButton > mxCkbTab
bool mbCharSetSystem
Selected char set.
float u
std::unique_ptr< weld::CheckButton > mxCkbDetectNumber
static void lcl_SaveSeparators(const OUString &sFieldSeparators, const OUString &sTextSeparators, bool bMergeDelimiters, bool bQuotedAsText, bool bDetectSpecialNum, bool bFixedWidth, sal_Int32 nFromRow, sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, ScImportAsciiCall eCall)
std::unique_ptr< SvxTextEncodingBox > mxLbCharSet
void SetFixedLen(bool bSet)
Definition: asciiopt.hxx:72
void SetFieldSeps(const OUString &rStr)
Definition: asciiopt.hxx:73
void GetOptions(ScAsciiOptions &rOpt)
void PutProperties(const css::uno::Sequence< OUString > &rNames, const css::uno::Sequence< css::uno::Any > &rValues)
Definition: optutil.hxx:53
The control in the CSV import dialog that contains a ruler and a data grid to visualize and modify th...
Definition: csvtablebox.hxx:43
std::size_t ReadBytes(void *pData, std::size_t nSize)
static void EmbeddedNullTreatment(OUString &rStr)
Definition: impex.cxx:1572
Move to make passed position visible (for mouse tracking). [position].
Definition: csvcontrol.hxx:182
std::unique_ptr< weld::Label > mxFtTextSep
#define SCSTR_TEXTSEP
Definition: strings.hxx:13
OUString ScResId(std::string_view aId)
Definition: scdll.cxx:89
std::unique_ptr< sal_uLong[]> mpRowPosArray
weld::Entry & rEdit
std::unique_ptr< weld::Entry > mxEdOther
const sal_Int32 CSV_TYPE_MULTI
Multi selection with different types.
Definition: csvcontrol.hxx:49
static sal_Unicode lcl_CharFromCombo(const weld::ComboBox &rCombo, const OUString &rList)
void SetEndian(SvStreamEndian SvStreamEndian)
OUString aName
std::unique_ptr< weld::Label > mxAltTitle
ScImportAsciiCall
How ScImportAsciiDlg is called.
Definition: asciiopt.hxx:100
void SetStreamCharSet(rtl_TextEncoding eCharSet)
rtl_TextEncoding GetStreamCharSet() const
Reference< XExecutableDialog > m_xDialog
std::unique_ptr< weld::CheckButton > mxCkbAsOnce
sal_uLong mnRowPosCount
sal_uInt64 Tell() const
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:400
IMPL_LINK(ScImportAsciiDlg, RbSepFixHdl, weld::Toggleable &, rButton, void)
Change character pixel width. [width in pixel].
Definition: csvcontrol.hxx:171
bool good() const
ScImportAsciiCall meCall
Is System char set selected?
OUString ReadCsvLine(SvStream &rStream, bool bEmbeddedLineBreak, OUString &rFieldSeparators, sal_Unicode cFieldQuote, sal_Unicode &rcDetectSep)
Read a CSV (comma separated values) data line using ReadUniOrByteStringLine().
Definition: impex.cxx:2452
void append_text(const OUString &rStr)
constexpr OUStringLiteral aSep_Path
virtual void ResetError()
std::unique_ptr< weld::CheckButton > mxCkbRemoveSpace
void SetMergeSeps(bool bSet)
Definition: asciiopt.hxx:74
static bool GetBoolFromAny(const css::uno::Any &aAny)
Definition: miscuno.cxx:138
std::unique_ptr< SvxLanguageBox > mxLbCustomLang
css::uno::Sequence< css::uno::Any > GetProperties(const css::uno::Sequence< OUString > &rNames)
Definition: optutil.hxx:51
ScImportAsciiDlg(weld::Window *pParent, const OUString &aDatName, SvStream *pInStream, ScImportAsciiCall eCall)
aStr
virtual void set_entry_text(const OUString &rStr)=0
void SetTextSep(sal_Unicode c)
Definition: asciiopt.hxx:79
std::unique_ptr< weld::Label > mxFtCustomLang
const sal_Int32 CSV_PREVIEW_LINES
TODO make string array dynamic.
Definition: csvcontrol.hxx:42
virtual OUString get_active_text() const =0
CSVImportOptionsIndex
std::unique_ptr< weld::Label > mxFtType