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