KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
18KB

  1. /* This file is part of the KDE project
  2. Copyright (C) 1999 David Faure <faure@kde.org>
  3. Copyright (C) 2004 Nicolas GOUTTE <goutte@kde.org>
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. * Boston, MA 02110-1301, USA.
  16. */
  17. #include <dialogui.h>
  18. #include <csvdialog.h>
  19. #include <tqtable.h>
  20. #include <tqcheckbox.h>
  21. #include <tqcursor.h>
  22. #include <tqlineedit.h>
  23. #include <tqcombobox.h>
  24. #include <tqspinbox.h>
  25. #include <tqtextstream.h>
  26. #include <tqbuttongroup.h>
  27. #include <tqpushbutton.h>
  28. #include <tqradiobutton.h>
  29. #include <tqtextcodec.h>
  30. #include <kapplication.h>
  31. #include <tdeconfig.h>
  32. #include <kdebug.h>
  33. #include <klocale.h>
  34. #include <kcombobox.h>
  35. #include <kmessagebox.h>
  36. #include <kcharsets.h>
  37. CSVDialog::CSVDialog(TQWidget* parent, TQByteArray& fileArray, const TQString /*seperator*/)
  38. : KDialogBase(parent, 0, true, TQString(), Ok|Cancel, No, true),
  39. m_adjustRows(false),
  40. m_adjustCols(false),
  41. m_startRow(0),
  42. m_startCol(0),
  43. m_endRow(-1),
  44. m_endCol(-1),
  45. m_textquote('"'),
  46. m_delimiter(","),
  47. m_ignoreDups(false),
  48. m_fileArray(fileArray),
  49. m_dialog(new DialogUI(this)),
  50. m_codec( TQTextCodec::codecForName( "UTF-8" ) )
  51. {
  52. setCaption( i18n( "Import" ) );
  53. kapp->restoreOverrideCursor();
  54. TQStringList encodings;
  55. encodings << i18n( "Descriptive encoding name", "Recommended ( %1 )" ).arg( "UTF-8" );
  56. encodings << i18n( "Descriptive encoding name", "Locale ( %1 )" ).arg( TQTextCodec::codecForLocale()->name() );
  57. encodings += TDEGlobal::charsets()->descriptiveEncodingNames();
  58. // Add a few non-standard encodings, which might be useful for text files
  59. const TQString description(i18n("Descriptive encoding name","Other ( %1 )"));
  60. encodings << description.arg("Apple Roman"); // Apple
  61. encodings << description.arg("IBM 850") << description.arg("IBM 866"); // MS DOS
  62. encodings << description.arg("CP 1258"); // Windows
  63. m_dialog->comboBoxEncoding->insertStringList(encodings);
  64. m_formatList << i18n( "Text" );
  65. m_formatList << i18n( "Number" );
  66. m_formatList << i18n( "Currency" );
  67. m_formatList << i18n( "Date" );
  68. m_formatList << i18n( "Decimal Comma Number" );
  69. m_formatList << i18n( "Decimal Point Number" );
  70. m_dialog->m_formatComboBox->insertStringList( m_formatList );
  71. m_dialog->m_sheet->setReadOnly( true );
  72. loadSettings();
  73. fillTable();
  74. //resize(sizeHint());
  75. resize( 600, 400 ); // Try to show as much as possible of the table view
  76. setMainWidget(m_dialog);
  77. m_dialog->m_sheet->setSelectionMode( TQTable::Multi );
  78. connect(m_dialog->m_formatComboBox, TQT_SIGNAL(activated( const TQString& )),
  79. this, TQT_SLOT(formatChanged( const TQString& )));
  80. connect(m_dialog->m_delimiterBox, TQT_SIGNAL(clicked(int)),
  81. this, TQT_SLOT(delimiterClicked(int)));
  82. connect(m_dialog->m_delimiterEdit, TQT_SIGNAL(returnPressed()),
  83. this, TQT_SLOT(returnPressed()));
  84. connect(m_dialog->m_delimiterEdit, TQT_SIGNAL(textChanged ( const TQString & )),
  85. this, TQT_SLOT(formatChanged ( const TQString & ) ));
  86. connect(m_dialog->m_comboQuote, TQT_SIGNAL(activated(const TQString &)),
  87. this, TQT_SLOT(textquoteSelected(const TQString &)));
  88. connect(m_dialog->m_sheet, TQT_SIGNAL(currentChanged(int, int)),
  89. this, TQT_SLOT(currentCellChanged(int, int)));
  90. connect(m_dialog->m_ignoreDuplicates, TQT_SIGNAL(stateChanged(int)),
  91. this, TQT_SLOT(ignoreDuplicatesChanged(int)));
  92. connect(m_dialog->m_updateButton, TQT_SIGNAL(clicked()),
  93. this, TQT_SLOT(updateClicked()));
  94. connect(m_dialog->comboBoxEncoding, TQT_SIGNAL(textChanged ( const TQString & )),
  95. this, TQT_SLOT(encodingChanged ( const TQString & ) ));
  96. }
  97. CSVDialog::~CSVDialog()
  98. {
  99. saveSettings();
  100. kapp->setOverrideCursor(TQt::waitCursor);
  101. }
  102. void CSVDialog::loadSettings()
  103. {
  104. TDEConfig *config = kapp->config();
  105. config->setGroup("CSVDialog Settings");
  106. m_textquote = config->readEntry("textquote", "\"")[0];
  107. m_delimiter = config->readEntry("delimiter", ",");
  108. m_ignoreDups = config->readBoolEntry("ignoreDups", false);
  109. const TQString codecText = config->readEntry("codec", "");
  110. // update widgets
  111. if (!codecText.isEmpty()) {
  112. m_dialog->comboBoxEncoding->setCurrentText(codecText);
  113. m_codec = getCodec();
  114. }
  115. if (m_delimiter == ",") m_dialog->m_radioComma->setChecked(true);
  116. else if (m_delimiter == "\t") m_dialog->m_radioTab->setChecked(true);
  117. else if (m_delimiter == " ") m_dialog->m_radioSpace->setChecked(true);
  118. else if (m_delimiter == ";") m_dialog->m_radioSemicolon->setChecked(true);
  119. else {
  120. m_dialog->m_radioOther->setChecked(true);
  121. m_dialog->m_delimiterEdit->setText(m_delimiter);
  122. }
  123. m_dialog->m_ignoreDuplicates->setChecked(m_ignoreDups);
  124. m_dialog->m_comboQuote->setCurrentItem(m_textquote == '\'' ? 1
  125. : m_textquote == '"' ? 0 : 2);
  126. }
  127. void CSVDialog::saveSettings()
  128. {
  129. TDEConfig *config = kapp->config();
  130. config->setGroup("CSVDialog Settings");
  131. TQString q = m_textquote;
  132. config->writeEntry("textquote", q);
  133. config->writeEntry("delimiter", m_delimiter);
  134. config->writeEntry("ignoreDups", m_ignoreDups);
  135. config->writeEntry("codec", m_dialog->comboBoxEncoding->currentText());
  136. config->sync();
  137. }
  138. void CSVDialog::fillTable( )
  139. {
  140. int row, column;
  141. bool lastCharDelimiter = false;
  142. enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
  143. S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
  144. TQChar x;
  145. TQString field;
  146. kapp->setOverrideCursor(TQt::waitCursor);
  147. for (row = 0; row < m_dialog->m_sheet->numRows(); ++row)
  148. for (column = 0; column < m_dialog->m_sheet->numCols(); ++column)
  149. m_dialog->m_sheet->clearCell(row, column);
  150. int maxColumn = 1;
  151. row = column = 1;
  152. TQTextStream inputStream(m_fileArray, IO_ReadOnly);
  153. kdDebug(30501) << "Encoding: " << m_codec->name() << endl;
  154. inputStream.setCodec( m_codec );
  155. bool lastCharWasCr = false; // Last character was a Carriage Return
  156. while (!inputStream.atEnd())
  157. {
  158. inputStream >> x; // read one char
  159. // ### TODO: we should perhaps skip all other control characters
  160. if ( x == '\r' )
  161. {
  162. // We have a Carriage Return, assume that its role is the one of a LineFeed
  163. lastCharWasCr = true;
  164. x = '\n'; // Replace by Line Feed
  165. }
  166. else if ( x == '\n' && lastCharWasCr )
  167. {
  168. // The end of line was already handled by the Carriage Return, so do nothing for this character
  169. lastCharWasCr = false;
  170. continue;
  171. }
  172. else if ( x == TQChar( 0xc ) )
  173. {
  174. // We have a FormFeed, skip it
  175. lastCharWasCr = false;
  176. continue;
  177. }
  178. else
  179. {
  180. lastCharWasCr = false;
  181. }
  182. if ( column > maxColumn )
  183. maxColumn = column;
  184. switch (state)
  185. {
  186. case S_START :
  187. if (x == m_textquote)
  188. {
  189. state = S_QUOTED_FIELD;
  190. }
  191. else if (x == m_delimiter)
  192. {
  193. if ((m_ignoreDups == false) || (lastCharDelimiter == false))
  194. ++column;
  195. lastCharDelimiter = true;
  196. }
  197. else if (x == '\n')
  198. {
  199. ++row;
  200. column = 1;
  201. if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
  202. break;
  203. }
  204. else
  205. {
  206. field += x;
  207. state = S_MAYBE_NORMAL_FIELD;
  208. }
  209. break;
  210. case S_QUOTED_FIELD :
  211. if (x == m_textquote)
  212. {
  213. state = S_MAYBE_END_OF_QUOTED_FIELD;
  214. }
  215. else
  216. {
  217. field += x;
  218. }
  219. break;
  220. case S_MAYBE_END_OF_QUOTED_FIELD :
  221. if (x == m_textquote)
  222. {
  223. field += x;
  224. state = S_QUOTED_FIELD;
  225. }
  226. else if (x == m_delimiter || x == '\n')
  227. {
  228. setText(row - m_startRow, column - m_startCol, field);
  229. field = TQString();
  230. if (x == '\n')
  231. {
  232. ++row;
  233. column = 1;
  234. if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
  235. break;
  236. }
  237. else
  238. {
  239. if ((m_ignoreDups == false) || (lastCharDelimiter == false))
  240. ++column;
  241. lastCharDelimiter = true;
  242. }
  243. state = S_START;
  244. }
  245. else
  246. {
  247. state = S_END_OF_QUOTED_FIELD;
  248. }
  249. break;
  250. case S_END_OF_QUOTED_FIELD :
  251. if (x == m_delimiter || x == '\n')
  252. {
  253. setText(row - m_startRow, column - m_startCol, field);
  254. field = TQString();
  255. if (x == '\n')
  256. {
  257. ++row;
  258. column = 1;
  259. if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
  260. break;
  261. }
  262. else
  263. {
  264. if ((m_ignoreDups == false) || (lastCharDelimiter == false))
  265. ++column;
  266. lastCharDelimiter = true;
  267. }
  268. state = S_START;
  269. }
  270. break;
  271. case S_MAYBE_NORMAL_FIELD :
  272. if (x == m_textquote)
  273. {
  274. field = TQString();
  275. state = S_QUOTED_FIELD;
  276. break;
  277. }
  278. case S_NORMAL_FIELD :
  279. if (x == m_delimiter || x == '\n')
  280. {
  281. setText(row - m_startRow, column - m_startCol, field);
  282. field = TQString();
  283. if (x == '\n')
  284. {
  285. ++row;
  286. column = 1;
  287. if ( row > ( m_endRow - m_startRow ) && m_endRow >= 0 )
  288. break;
  289. }
  290. else
  291. {
  292. if ((m_ignoreDups == false) || (lastCharDelimiter == false))
  293. ++column;
  294. lastCharDelimiter = true;
  295. }
  296. state = S_START;
  297. }
  298. else
  299. {
  300. field += x;
  301. }
  302. }
  303. if (x != m_delimiter)
  304. lastCharDelimiter = false;
  305. }
  306. if ( !field.isEmpty() )
  307. {
  308. // the last line of the file had not any line end
  309. setText(row - m_startRow, column - m_startCol, field);
  310. ++row;
  311. field = TQString();
  312. }
  313. m_adjustCols = true;
  314. adjustRows( row - m_startRow );
  315. adjustCols( maxColumn - m_startCol );
  316. m_dialog->m_colEnd->setMaxValue( maxColumn );
  317. if ( m_endCol == -1 )
  318. m_dialog->m_colEnd->setValue( maxColumn );
  319. for (column = 0; column < m_dialog->m_sheet->numCols(); ++column)
  320. {
  321. const TQString header = m_dialog->m_sheet->horizontalHeader()->label(column);
  322. if ( m_formatList.find( header ) == m_formatList.end() )
  323. m_dialog->m_sheet->horizontalHeader()->setLabel(column, i18n("Text"));
  324. m_dialog->m_sheet->adjustColumn(column);
  325. }
  326. fillComboBox();
  327. kapp->restoreOverrideCursor();
  328. }
  329. void CSVDialog::fillComboBox()
  330. {
  331. if ( m_endRow == -1 )
  332. m_dialog->m_rowEnd->setValue( m_dialog->m_sheet->numRows() );
  333. else
  334. m_dialog->m_rowEnd->setValue( m_endRow );
  335. if ( m_endCol == -1 )
  336. m_dialog->m_colEnd->setValue( m_dialog->m_sheet->numCols() );
  337. else
  338. m_dialog->m_colEnd->setValue( m_endCol );
  339. m_dialog->m_rowEnd->setMinValue( 1 );
  340. m_dialog->m_colEnd->setMinValue( 1 );
  341. m_dialog->m_rowEnd->setMaxValue( m_dialog->m_sheet->numRows() );
  342. m_dialog->m_colEnd->setMaxValue( m_dialog->m_sheet->numCols() );
  343. m_dialog->m_rowStart->setMinValue( 1 );
  344. m_dialog->m_colStart->setMinValue( 1 );
  345. m_dialog->m_rowStart->setMaxValue( m_dialog->m_sheet->numRows() );
  346. m_dialog->m_colStart->setMaxValue( m_dialog->m_sheet->numCols() );
  347. }
  348. int CSVDialog::getRows()
  349. {
  350. int rows = m_dialog->m_sheet->numRows();
  351. if ( m_endRow >= 0 )
  352. {
  353. if ( rows > ( m_startRow + m_endRow ) )
  354. rows = m_startRow + m_endRow;
  355. }
  356. return rows;
  357. }
  358. int CSVDialog::getCols()
  359. {
  360. int cols = m_dialog->m_sheet->numCols();
  361. if ( m_endCol >= 0 )
  362. {
  363. if ( cols > ( m_startCol + m_endCol ) )
  364. cols = m_startCol + m_endCol;
  365. }
  366. return cols;
  367. }
  368. int CSVDialog::getHeader(int col)
  369. {
  370. TQString header = m_dialog->m_sheet->horizontalHeader()->label(col);
  371. if (header == i18n("Text"))
  372. return TEXT;
  373. else if (header == i18n("Number"))
  374. return NUMBER;
  375. else if (header == i18n("Currency"))
  376. return CURRENCY;
  377. else if ( header == i18n( "Date" ) )
  378. return DATE;
  379. else if ( header == i18n( "Decimal Comma Number" ) )
  380. return COMMANUMBER;
  381. else if ( header == i18n( "Decimal Point Number" ) )
  382. return POINTNUMBER;
  383. else
  384. return TEXT; // Should not happen
  385. }
  386. TQString CSVDialog::getText(int row, int col)
  387. {
  388. return m_dialog->m_sheet->text( row, col );
  389. }
  390. void CSVDialog::setText(int row, int col, const TQString& text)
  391. {
  392. if ( row < 1 || col < 1 ) // skipped by the user
  393. return;
  394. if ( ( row > ( m_endRow - m_startRow ) && m_endRow > 0 ) || ( col > ( m_endCol - m_startCol ) && m_endCol > 0 ) )
  395. return;
  396. if ( m_dialog->m_sheet->numRows() < row )
  397. {
  398. m_dialog->m_sheet->setNumRows( row + 5000 ); /* We add 5000 at a time to limit recalculations */
  399. m_adjustRows = true;
  400. }
  401. if ( m_dialog->m_sheet->numCols() < col )
  402. {
  403. m_dialog->m_sheet->setNumCols( col );
  404. m_adjustCols = true;
  405. }
  406. m_dialog->m_sheet->setText( row - 1, col - 1, text );
  407. }
  408. /*
  409. * Called after the first fillTable() when number of rows are unknown.
  410. */
  411. void CSVDialog::adjustRows(int iRows)
  412. {
  413. if (m_adjustRows)
  414. {
  415. m_dialog->m_sheet->setNumRows( iRows );
  416. m_adjustRows = false;
  417. }
  418. }
  419. void CSVDialog::adjustCols(int iCols)
  420. {
  421. if (m_adjustCols)
  422. {
  423. m_dialog->m_sheet->setNumCols( iCols );
  424. m_adjustCols = false;
  425. if ( m_endCol == -1 )
  426. {
  427. if ( iCols > ( m_endCol - m_startCol ) )
  428. iCols = m_endCol - m_startCol;
  429. m_dialog->m_sheet->setNumCols( iCols );
  430. }
  431. }
  432. }
  433. void CSVDialog::returnPressed()
  434. {
  435. if (m_dialog->m_delimiterBox->id(m_dialog->m_delimiterBox->selected()) != 4)
  436. return;
  437. m_delimiter = m_dialog->m_delimiterEdit->text();
  438. fillTable();
  439. }
  440. void CSVDialog::textChanged ( const TQString & )
  441. {
  442. m_dialog->m_radioOther->setChecked ( true );
  443. delimiterClicked(4); // other
  444. }
  445. void CSVDialog::formatChanged( const TQString& newValue )
  446. {
  447. //kdDebug(30501) << "CSVDialog::formatChanged:" << newValue << endl;
  448. for ( int i = 0; i < m_dialog->m_sheet->numSelections(); ++i )
  449. {
  450. TQTableSelection select ( m_dialog->m_sheet->selection( i ) );
  451. for ( int j = select.leftCol(); j <= select.rightCol() ; ++j )
  452. {
  453. m_dialog->m_sheet->horizontalHeader()->setLabel( j, newValue );
  454. }
  455. }
  456. }
  457. void CSVDialog::delimiterClicked(int id)
  458. {
  459. switch (id)
  460. {
  461. case 0: // comma
  462. m_delimiter = ",";
  463. break;
  464. case 4: // other
  465. m_delimiter = m_dialog->m_delimiterEdit->text();
  466. break;
  467. case 2: // tab
  468. m_delimiter = "\t";
  469. break;
  470. case 3: // space
  471. m_delimiter = " ";
  472. break;
  473. case 1: // semicolon
  474. m_delimiter = ";";
  475. break;
  476. }
  477. fillTable();
  478. }
  479. void CSVDialog::textquoteSelected(const TQString& mark)
  480. {
  481. if (mark == i18n("None"))
  482. m_textquote = 0;
  483. else
  484. m_textquote = mark[0];
  485. fillTable();
  486. }
  487. void CSVDialog::updateClicked()
  488. {
  489. if ( !checkUpdateRange() )
  490. return;
  491. m_startRow = m_dialog->m_rowStart->value() - 1;
  492. m_endRow = m_dialog->m_rowEnd->value();
  493. m_startCol = m_dialog->m_colStart->value() - 1;
  494. m_endCol = m_dialog->m_colEnd->value();
  495. fillTable();
  496. }
  497. bool CSVDialog::checkUpdateRange()
  498. {
  499. if ( ( m_dialog->m_rowStart->value() > m_dialog->m_rowEnd->value() )
  500. || ( m_dialog->m_colStart->value() > m_dialog->m_colEnd->value() ) )
  501. {
  502. KMessageBox::error( this, i18n( "Please check the ranges you specified. The start value must be lower than the end value." ) );
  503. return false;
  504. }
  505. return true;
  506. }
  507. void CSVDialog::currentCellChanged(int, int col)
  508. {
  509. const TQString header = m_dialog->m_sheet->horizontalHeader()->label(col);
  510. m_dialog->m_formatComboBox->setCurrentText( header );
  511. }
  512. void CSVDialog::ignoreDuplicatesChanged(int)
  513. {
  514. if (m_dialog->m_ignoreDuplicates->isChecked())
  515. m_ignoreDups = true;
  516. else
  517. m_ignoreDups = false;
  518. fillTable();
  519. }
  520. TQTextCodec* CSVDialog::getCodec(void) const
  521. {
  522. const TQString strCodec( TDEGlobal::charsets()->encodingForName( m_dialog->comboBoxEncoding->currentText() ) );
  523. kdDebug(30502) << "Encoding: " << strCodec << endl;
  524. bool ok = false;
  525. TQTextCodec* codec = TQTextCodec::codecForName( strCodec.utf8() );
  526. // If TQTextCodec has not found a valid encoding, so try with KCharsets.
  527. if ( codec )
  528. {
  529. ok = true;
  530. }
  531. else
  532. {
  533. codec = TDEGlobal::charsets()->codecForName( strCodec, ok );
  534. }
  535. // Still nothing?
  536. if ( !codec || !ok )
  537. {
  538. // Default: UTF-8
  539. kdWarning(30502) << "Cannot find encoding:" << strCodec << endl;
  540. // ### TODO: what parent to use?
  541. KMessageBox::error( 0, i18n("Cannot find encoding: %1").arg( strCodec ) );
  542. return 0;
  543. }
  544. return codec;
  545. }
  546. void CSVDialog::encodingChanged ( const TQString & )
  547. {
  548. TQTextCodec* codec = getCodec();
  549. if ( codec )
  550. {
  551. m_codec = codec;
  552. fillTable();
  553. }
  554. }
  555. #include <csvdialog.moc>