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