TDE personal information management applications
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.

967 lines
30KB

  1. /*
  2. This file is part of KAddressBook.
  3. Copyright (C) 2003 Tobias Koenig <tokoe@kde.org>
  4. based on the code of KSpread's CSV Import Dialog
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Library General Public
  7. License as published by the Free Software Foundation; either
  8. version 2 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Library General Public License for more details.
  13. You should have received a copy of the GNU Library General Public License
  14. along with this library; see the file COPYING.LIB. If not, write to
  15. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. #include <tqbuttongroup.h>
  19. #include <tqcheckbox.h>
  20. #include <tqcombobox.h>
  21. #include <tqlabel.h>
  22. #include <tqlayout.h>
  23. #include <tqlineedit.h>
  24. #include <tqpushbutton.h>
  25. #include <tqradiobutton.h>
  26. #include <tqtable.h>
  27. #include <tqtextcodec.h>
  28. #include <tqtooltip.h>
  29. #include <kapplication.h>
  30. #include <kdebug.h>
  31. #include <kdialogbase.h>
  32. #include <kfiledialog.h>
  33. #include <klineedit.h>
  34. #include <klocale.h>
  35. #include <kinputdialog.h>
  36. #include <kmessagebox.h>
  37. #include <kprogress.h>
  38. #include <kstandarddirs.h>
  39. #include <kurlrequester.h>
  40. #include "dateparser.h"
  41. #include "csvimportdialog.h"
  42. enum { Local = 0, Guess = 1, Latin1 = 2, Uni = 3, MSBug = 4, Codec = 5 };
  43. CSVImportDialog::CSVImportDialog( KABC::AddressBook *ab, TQWidget *parent,
  44. const char * name )
  45. : KDialogBase( Plain, i18n ( "CSV Import Dialog" ), Ok | Cancel | User1 |
  46. User2, Ok, parent, name, true, true ),
  47. mAdjustRows( false ),
  48. mStartLine( 0 ),
  49. mTextQuote( '"' ),
  50. mDelimiter( "," ),
  51. mAddressBook( ab )
  52. {
  53. initGUI();
  54. mTypeMap.insert( i18n( "Undefined" ), Undefined );
  55. mTypeMap.insert( KABC::Addressee::formattedNameLabel(), FormattedName );
  56. mTypeMap.insert( KABC::Addressee::familyNameLabel(), FamilyName );
  57. mTypeMap.insert( KABC::Addressee::givenNameLabel(), GivenName );
  58. mTypeMap.insert( KABC::Addressee::additionalNameLabel(), AdditionalName );
  59. mTypeMap.insert( KABC::Addressee::prefixLabel(), Prefix );
  60. mTypeMap.insert( KABC::Addressee::suffixLabel(), Suffix );
  61. mTypeMap.insert( KABC::Addressee::nickNameLabel(), NickName );
  62. mTypeMap.insert( KABC::Addressee::birthdayLabel(), Birthday );
  63. mTypeMap.insert( KABC::Addressee::homeAddressStreetLabel(), HomeAddressStreet );
  64. mTypeMap.insert( KABC::Addressee::homeAddressLocalityLabel(),
  65. HomeAddressLocality );
  66. mTypeMap.insert( KABC::Addressee::homeAddressRegionLabel(), HomeAddressRegion );
  67. mTypeMap.insert( KABC::Addressee::homeAddressPostalCodeLabel(),
  68. HomeAddressPostalCode );
  69. mTypeMap.insert( KABC::Addressee::homeAddressCountryLabel(),
  70. HomeAddressCountry );
  71. mTypeMap.insert( KABC::Addressee::homeAddressLabelLabel(), HomeAddressLabel );
  72. mTypeMap.insert( KABC::Addressee::businessAddressStreetLabel(),
  73. BusinessAddressStreet );
  74. mTypeMap.insert( KABC::Addressee::businessAddressLocalityLabel(),
  75. BusinessAddressLocality );
  76. mTypeMap.insert( KABC::Addressee::businessAddressRegionLabel(),
  77. BusinessAddressRegion );
  78. mTypeMap.insert( KABC::Addressee::businessAddressPostalCodeLabel(),
  79. BusinessAddressPostalCode );
  80. mTypeMap.insert( KABC::Addressee::businessAddressCountryLabel(),
  81. BusinessAddressCountry );
  82. mTypeMap.insert( KABC::Addressee::businessAddressLabelLabel(),
  83. BusinessAddressLabel );
  84. mTypeMap.insert( KABC::Addressee::homePhoneLabel(), HomePhone );
  85. mTypeMap.insert( KABC::Addressee::businessPhoneLabel(), BusinessPhone );
  86. mTypeMap.insert( KABC::Addressee::mobilePhoneLabel(), MobilePhone );
  87. mTypeMap.insert( KABC::Addressee::homeFaxLabel(), HomeFax );
  88. mTypeMap.insert( KABC::Addressee::businessFaxLabel(), BusinessFax );
  89. mTypeMap.insert( KABC::Addressee::carPhoneLabel(), CarPhone );
  90. mTypeMap.insert( KABC::Addressee::isdnLabel(), Isdn );
  91. mTypeMap.insert( KABC::Addressee::pagerLabel(), Pager );
  92. mTypeMap.insert( KABC::Addressee::emailLabel(), Email );
  93. mTypeMap.insert( KABC::Addressee::mailerLabel(), Mailer );
  94. mTypeMap.insert( KABC::Addressee::titleLabel(), Title );
  95. mTypeMap.insert( KABC::Addressee::roleLabel(), Role );
  96. mTypeMap.insert( KABC::Addressee::organizationLabel(), Organization );
  97. mTypeMap.insert( KABC::Addressee::noteLabel(), Note );
  98. mTypeMap.insert( KABC::Addressee::urlLabel(), URL );
  99. mCustomCounter = mTypeMap.count();
  100. int count = mCustomCounter;
  101. KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory );
  102. KABC::Field::List::Iterator it;
  103. for ( it = fields.begin(); it != fields.end(); ++it, ++count )
  104. mTypeMap.insert( (*it)->label(), count );
  105. reloadCodecs();
  106. connect( mDelimiterBox, TQT_SIGNAL( clicked( int ) ),
  107. this, TQT_SLOT( delimiterClicked( int ) ) );
  108. connect( mDelimiterEdit, TQT_SIGNAL( returnPressed() ),
  109. this, TQT_SLOT( returnPressed() ) );
  110. connect( mDelimiterEdit, TQT_SIGNAL( textChanged ( const TQString& ) ),
  111. this, TQT_SLOT( textChanged ( const TQString& ) ) );
  112. connect( mComboLine, TQT_SIGNAL( activated( const TQString& ) ),
  113. this, TQT_SLOT( lineSelected( const TQString& ) ) );
  114. connect( mComboQuote, TQT_SIGNAL( activated( const TQString& ) ),
  115. this, TQT_SLOT( textquoteSelected( const TQString& ) ) );
  116. connect( mIgnoreDuplicates, TQT_SIGNAL( stateChanged( int ) ),
  117. this, TQT_SLOT( ignoreDuplicatesChanged( int ) ) );
  118. connect( mCodecCombo, TQT_SIGNAL( activated( const TQString& ) ),
  119. this, TQT_SLOT( codecChanged() ) );
  120. connect( mUrlRequester, TQT_SIGNAL( returnPressed( const TQString& ) ),
  121. this, TQT_SLOT( setFile( const TQString& ) ) );
  122. connect( mUrlRequester, TQT_SIGNAL( urlSelected( const TQString& ) ),
  123. this, TQT_SLOT( setFile( const TQString& ) ) );
  124. connect( mUrlRequester->lineEdit(), TQT_SIGNAL( textChanged ( const TQString& ) ),
  125. this, TQT_SLOT( urlChanged( const TQString& ) ) );
  126. connect( this, TQT_SIGNAL( user1Clicked() ),
  127. this, TQT_SLOT( applyTemplate() ) );
  128. connect( this, TQT_SIGNAL( user2Clicked() ),
  129. this, TQT_SLOT( saveTemplate() ) );
  130. }
  131. CSVImportDialog::~CSVImportDialog()
  132. {
  133. mCodecs.clear();
  134. }
  135. KABC::AddresseeList CSVImportDialog::contacts() const
  136. {
  137. DateParser dateParser( mDatePatternEdit->text() );
  138. KABC::AddresseeList contacts;
  139. KProgressDialog progressDialog( mPage );
  140. progressDialog.setAutoClose( true );
  141. progressDialog.progressBar()->setTotalSteps( mTable->numRows() );
  142. progressDialog.setLabel( i18n( "Importing contacts" ) );
  143. progressDialog.show();
  144. kapp->processEvents();
  145. for ( int row = 1; row < mTable->numRows(); ++row ) {
  146. KABC::Addressee a;
  147. bool emptyRow = true;
  148. KABC::Address addrHome( KABC::Address::Home );
  149. KABC::Address addrWork( KABC::Address::Work );
  150. for ( int col = 0; col < mTable->numCols(); ++col ) {
  151. TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
  152. col ) );
  153. if ( !item ) {
  154. kdError() << "ERROR: item cast failed" << endl;
  155. continue;
  156. }
  157. TQString value = mTable->text( row, col );
  158. if ( 1 == row && static_cast<TQTableItem *>(item)->text() == value )
  159. // we are looking at a header row, stop now
  160. break;
  161. if ( !value.isEmpty() )
  162. emptyRow = false;
  163. switch ( posToType( item->currentItem() ) ) {
  164. case Undefined:
  165. continue;
  166. break;
  167. case FormattedName:
  168. a.setFormattedName( value );
  169. break;
  170. case GivenName:
  171. a.setGivenName( value );
  172. break;
  173. case FamilyName:
  174. a.setFamilyName( value );
  175. break;
  176. case AdditionalName:
  177. a.setAdditionalName( value );
  178. break;
  179. case Prefix:
  180. a.setPrefix( value );
  181. break;
  182. case Suffix:
  183. a.setSuffix( value );
  184. break;
  185. case NickName:
  186. a.setNickName( value );
  187. break;
  188. case Birthday:
  189. a.setBirthday( dateParser.parse( value ) );
  190. break;
  191. case Email:
  192. if ( !value.isEmpty() )
  193. a.insertEmail( value, true );
  194. break;
  195. case Role:
  196. a.setRole( value );
  197. break;
  198. case Title:
  199. a.setTitle( value );
  200. break;
  201. case Mailer:
  202. a.setMailer( value );
  203. break;
  204. case URL:
  205. a.setUrl( KURL( value ) );
  206. break;
  207. case Organization:
  208. a.setOrganization( value );
  209. break;
  210. case Note:
  211. a.setNote( a.note() + value + "\n" );
  212. break;
  213. case HomePhone:
  214. if ( !value.isEmpty() ) {
  215. KABC::PhoneNumber number( value, KABC::PhoneNumber::Home );
  216. a.insertPhoneNumber( number );
  217. }
  218. break;
  219. case BusinessPhone:
  220. if ( !value.isEmpty() ) {
  221. KABC::PhoneNumber number( value, KABC::PhoneNumber::Work );
  222. a.insertPhoneNumber( number );
  223. }
  224. break;
  225. case MobilePhone:
  226. if ( !value.isEmpty() ) {
  227. KABC::PhoneNumber number( value, KABC::PhoneNumber::Cell );
  228. a.insertPhoneNumber( number );
  229. }
  230. break;
  231. case HomeFax:
  232. if ( !value.isEmpty() ) {
  233. KABC::PhoneNumber number( value, KABC::PhoneNumber::Home |
  234. KABC::PhoneNumber::Fax );
  235. a.insertPhoneNumber( number );
  236. }
  237. break;
  238. case BusinessFax:
  239. if ( !value.isEmpty() ) {
  240. KABC::PhoneNumber number( value, KABC::PhoneNumber::Work |
  241. KABC::PhoneNumber::Fax );
  242. a.insertPhoneNumber( number );
  243. }
  244. break;
  245. case CarPhone:
  246. if ( !value.isEmpty() ) {
  247. KABC::PhoneNumber number( value, KABC::PhoneNumber::Car );
  248. a.insertPhoneNumber( number );
  249. }
  250. break;
  251. case Isdn:
  252. if ( !value.isEmpty() ) {
  253. KABC::PhoneNumber number( value, KABC::PhoneNumber::Isdn );
  254. a.insertPhoneNumber( number );
  255. }
  256. break;
  257. case Pager:
  258. if ( !value.isEmpty() ) {
  259. KABC::PhoneNumber number( value, KABC::PhoneNumber::Pager );
  260. a.insertPhoneNumber( number );
  261. }
  262. break;
  263. case HomeAddressStreet:
  264. addrHome.setStreet( value );
  265. break;
  266. case HomeAddressLocality:
  267. addrHome.setLocality( value );
  268. break;
  269. case HomeAddressRegion:
  270. addrHome.setRegion( value );
  271. break;
  272. case HomeAddressPostalCode:
  273. addrHome.setPostalCode( value );
  274. break;
  275. case HomeAddressCountry:
  276. addrHome.setCountry( value );
  277. break;
  278. case HomeAddressLabel:
  279. addrHome.setLabel( value );
  280. break;
  281. case BusinessAddressStreet:
  282. addrWork.setStreet( value );
  283. break;
  284. case BusinessAddressLocality:
  285. addrWork.setLocality( value );
  286. break;
  287. case BusinessAddressRegion:
  288. addrWork.setRegion( value );
  289. break;
  290. case BusinessAddressPostalCode:
  291. addrWork.setPostalCode( value );
  292. break;
  293. case BusinessAddressCountry:
  294. addrWork.setCountry( value );
  295. break;
  296. case BusinessAddressLabel:
  297. addrWork.setLabel( value );
  298. break;
  299. default:
  300. KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory );
  301. KABC::Field::List::Iterator it;
  302. int counter = 0;
  303. for ( it = fields.begin(); it != fields.end(); ++it ) {
  304. if ( counter == (int)( posToType( item->currentItem() ) - mCustomCounter ) ) {
  305. (*it)->setValue( a, value );
  306. break;
  307. }
  308. ++counter;
  309. }
  310. break;
  311. }
  312. }
  313. kapp->processEvents();
  314. if ( progressDialog.wasCancelled() )
  315. return KABC::AddresseeList();
  316. progressDialog.progressBar()->advance( 1 );
  317. if ( !addrHome.isEmpty() )
  318. a.insertAddress( addrHome );
  319. if ( !addrWork.isEmpty() )
  320. a.insertAddress( addrWork );
  321. if ( !emptyRow && !a.isEmpty() )
  322. contacts.append( a );
  323. }
  324. return contacts;
  325. }
  326. void CSVImportDialog::initGUI()
  327. {
  328. mPage = plainPage();
  329. TQGridLayout *tqlayout = new TQGridLayout( mPage, 1, 1, marginHint(),
  330. spacingHint() );
  331. TQHBoxLayout *hbox = new TQHBoxLayout();
  332. hbox->setSpacing( spacingHint() );
  333. TQLabel *label = new TQLabel( i18n( "File to import:" ), mPage );
  334. hbox->addWidget( label );
  335. mUrlRequester = new KURLRequester( mPage );
  336. mUrlRequester->setFilter( "*.csv" );
  337. hbox->addWidget( mUrlRequester );
  338. tqlayout->addMultiCellLayout( hbox, 0, 0, 0, 4 );
  339. // Delimiter: comma, semicolon, tab, space, other
  340. mDelimiterBox = new TQButtonGroup( i18n( "Delimiter" ), mPage );
  341. mDelimiterBox->setColumnLayout( 0, Qt::Vertical );
  342. mDelimiterBox->tqlayout()->setSpacing( spacingHint() );
  343. mDelimiterBox->tqlayout()->setMargin( marginHint() );
  344. TQGridLayout *delimiterLayout = new TQGridLayout( mDelimiterBox->tqlayout() );
  345. delimiterLayout->tqsetAlignment( TQt::AlignTop );
  346. tqlayout->addMultiCellWidget( mDelimiterBox, 1, 4, 0, 0 );
  347. mRadioComma = new TQRadioButton( i18n( "Comma" ), mDelimiterBox );
  348. mRadioComma->setChecked( true );
  349. delimiterLayout->addWidget( mRadioComma, 0, 0 );
  350. mRadioSemicolon = new TQRadioButton( i18n( "Semicolon" ), mDelimiterBox );
  351. delimiterLayout->addWidget( mRadioSemicolon, 0, 1 );
  352. mRadioTab = new TQRadioButton( i18n( "Tabulator" ), mDelimiterBox );
  353. delimiterLayout->addWidget( mRadioTab, 1, 0 );
  354. mRadioSpace = new TQRadioButton( i18n( "Space" ), mDelimiterBox );
  355. delimiterLayout->addWidget( mRadioSpace, 1, 1 );
  356. mRadioOther = new TQRadioButton( i18n( "Other" ), mDelimiterBox );
  357. delimiterLayout->addWidget( mRadioOther, 0, 2 );
  358. mDelimiterEdit = new TQLineEdit( mDelimiterBox );
  359. delimiterLayout->addWidget( mDelimiterEdit, 1, 2 );
  360. mComboLine = new TQComboBox( false, mPage );
  361. mComboLine->insertItem( i18n( "1" ) );
  362. tqlayout->addWidget( mComboLine, 2, 3 );
  363. mComboQuote = new TQComboBox( false, mPage );
  364. mComboQuote->insertItem( i18n( "\"" ), 0 );
  365. mComboQuote->insertItem( i18n( "'" ), 1 );
  366. mComboQuote->insertItem( i18n( "None" ), 2 );
  367. tqlayout->addWidget( mComboQuote, 2, 2 );
  368. mDatePatternEdit = new TQLineEdit( mPage );
  369. mDatePatternEdit->setText( "Y-M-D" ); // ISO 8601 format as default
  370. TQToolTip::add( mDatePatternEdit, i18n( "<ul><li>y: year with 2 digits</li>"
  371. "<li>Y: year with 4 digits</li>"
  372. "<li>m: month with 1 or 2 digits</li>"
  373. "<li>M: month with 2 digits</li>"
  374. "<li>d: day with 1 or 2 digits</li>"
  375. "<li>D: day with 2 digits</li></ul>" ) );
  376. tqlayout->addWidget( mDatePatternEdit, 2, 4 );
  377. label = new TQLabel( i18n( "Start at line:" ), mPage );
  378. tqlayout->addWidget( label, 1, 3 );
  379. label = new TQLabel( i18n( "Textquote:" ), mPage );
  380. tqlayout->addWidget( label, 1, 2 );
  381. label = new TQLabel( i18n( "Date format:" ), mPage );
  382. tqlayout->addWidget( label, 1, 4 );
  383. mIgnoreDuplicates = new TQCheckBox( mPage );
  384. mIgnoreDuplicates->setText( i18n( "Ignore duplicate delimiters" ) );
  385. tqlayout->addMultiCellWidget( mIgnoreDuplicates, 3, 3, 2, 4 );
  386. mCodecCombo = new TQComboBox( mPage );
  387. tqlayout->addMultiCellWidget( mCodecCombo, 4, 4, 2, 4 );
  388. mTable = new TQTable( 0, 0, mPage );
  389. mTable->setSelectionMode( TQTable::NoSelection );
  390. mTable->horizontalHeader()->hide();
  391. tqlayout->addMultiCellWidget( mTable, 5, 5, 0, 4 );
  392. setButtonText( User1, i18n( "Apply Template..." ) );
  393. setButtonText( User2, i18n( "Save Template..." ) );
  394. enableButtonOK( false );
  395. actionButton( User1 )->setEnabled( false );
  396. actionButton( User2 )->setEnabled( false );
  397. resize( 400, 300 );
  398. }
  399. void CSVImportDialog::fillTable()
  400. {
  401. int row, column;
  402. bool lastCharDelimiter = false;
  403. bool ignoreDups = mIgnoreDuplicates->isChecked();
  404. enum { S_START, S_TQUOTED_FIELD, S_MAYBE_END_OF_TQUOTED_FIELD, S_END_OF_TQUOTED_FIELD,
  405. S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
  406. TQChar x;
  407. TQString field;
  408. // store previous assignment
  409. mTypeStore.clear();
  410. for ( column = 0; column < mTable->numCols(); ++column ) {
  411. TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
  412. column ) );
  413. if ( !item || mClearTypeStore )
  414. mTypeStore.append( typeToPos( Undefined ) );
  415. else if ( item )
  416. mTypeStore.append( item->currentItem() );
  417. }
  418. clearTable();
  419. row = column = 1;
  420. TQTextStream inputStream( mFileArray, IO_ReadOnly );
  421. // find the current codec
  422. int code = mCodecCombo->currentItem();
  423. if ( code == Local )
  424. inputStream.setEncoding( TQTextStream::Locale );
  425. else if ( code >= Codec )
  426. inputStream.setCodec( mCodecs.at( code - Codec ) );
  427. else if ( code == Uni )
  428. inputStream.setEncoding( TQTextStream::Unicode );
  429. else if ( code == MSBug )
  430. inputStream.setEncoding( TQTextStream::UnicodeReverse );
  431. else if ( code == Latin1 )
  432. inputStream.setEncoding( TQTextStream::Latin1 );
  433. else if ( code == Guess ) {
  434. TQTextCodec* codec = TQTextCodec::codecForContent( mFileArray.data(), mFileArray.size() );
  435. if ( codec ) {
  436. KMessageBox::information( this, i18n( "Using codec '%1'" ).tqarg( codec->name() ), i18n( "Encoding" ) );
  437. inputStream.setCodec( codec );
  438. }
  439. }
  440. int maxColumn = 0;
  441. while ( !inputStream.atEnd() ) {
  442. inputStream >> x; // read one char
  443. if ( x == '\r' ) inputStream >> x; // eat '\r', to handle DOS/LOSEDOWS files correctly
  444. switch ( state ) {
  445. case S_START :
  446. if ( x == mTextQuote ) {
  447. state = S_TQUOTED_FIELD;
  448. } else if ( x == mDelimiter ) {
  449. if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
  450. ++column;
  451. lastCharDelimiter = true;
  452. } else if ( x == '\n' ) {
  453. ++row;
  454. column = 1;
  455. } else {
  456. field += x;
  457. state = S_MAYBE_NORMAL_FIELD;
  458. }
  459. break;
  460. case S_TQUOTED_FIELD :
  461. if ( x == mTextQuote ) {
  462. state = S_MAYBE_END_OF_TQUOTED_FIELD;
  463. } else if ( x == '\n' && mTextQuote.isNull() ) {
  464. setText( row - mStartLine + 1, column, field );
  465. field = "";
  466. if ( x == '\n' ) {
  467. ++row;
  468. column = 1;
  469. } else {
  470. if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
  471. ++column;
  472. lastCharDelimiter = true;
  473. }
  474. state = S_START;
  475. } else {
  476. field += x;
  477. }
  478. break;
  479. case S_MAYBE_END_OF_TQUOTED_FIELD :
  480. if ( x == mTextQuote ) {
  481. field += x;
  482. state = S_TQUOTED_FIELD;
  483. } else if ( x == mDelimiter || x == '\n' ) {
  484. setText( row - mStartLine + 1, column, field );
  485. field = "";
  486. if ( x == '\n' ) {
  487. ++row;
  488. column = 1;
  489. } else {
  490. if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
  491. ++column;
  492. lastCharDelimiter = true;
  493. }
  494. state = S_START;
  495. } else {
  496. state = S_END_OF_TQUOTED_FIELD;
  497. }
  498. break;
  499. case S_END_OF_TQUOTED_FIELD :
  500. if ( x == mDelimiter || x == '\n' ) {
  501. setText( row - mStartLine + 1, column, field );
  502. field = "";
  503. if ( x == '\n' ) {
  504. ++row;
  505. column = 1;
  506. } else {
  507. if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
  508. ++column;
  509. lastCharDelimiter = true;
  510. }
  511. state = S_START;
  512. } else {
  513. state = S_END_OF_TQUOTED_FIELD;
  514. }
  515. break;
  516. case S_MAYBE_NORMAL_FIELD :
  517. if ( x == mTextQuote ) {
  518. field = "";
  519. state = S_TQUOTED_FIELD;
  520. break;
  521. }
  522. case S_NORMAL_FIELD :
  523. if ( x == mDelimiter || x == '\n' ) {
  524. setText( row - mStartLine + 1, column, field );
  525. field = "";
  526. if ( x == '\n' ) {
  527. ++row;
  528. column = 1;
  529. } else {
  530. if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
  531. ++column;
  532. lastCharDelimiter = true;
  533. }
  534. state = S_START;
  535. } else {
  536. field += x;
  537. }
  538. }
  539. if ( x != mDelimiter )
  540. lastCharDelimiter = false;
  541. if ( column > maxColumn )
  542. maxColumn = column;
  543. }
  544. // file with only one line without '\n'
  545. if ( field.length() > 0 ) {
  546. setText( row - mStartLine + 1, column, field );
  547. ++row;
  548. field = "";
  549. }
  550. adjustRows( row - mStartLine );
  551. mTable->setNumCols( maxColumn );
  552. for ( column = 0; column < mTable->numCols(); ++column ) {
  553. TQComboTableItem *item = new TQComboTableItem( mTable, mTypeMap.keys() );
  554. mTable->setItem( 0, column, item );
  555. if ( column < (int)mTypeStore.count() )
  556. item->setCurrentItem( mTypeStore[ column ] );
  557. else
  558. item->setCurrentItem( typeToPos( Undefined ) );
  559. mTable->adjustColumn( column );
  560. }
  561. resizeColumns();
  562. }
  563. void CSVImportDialog::clearTable()
  564. {
  565. for ( int row = 0; row < mTable->numRows(); ++row )
  566. for ( int column = 0; column < mTable->numCols(); ++column )
  567. mTable->clearCell( row, column );
  568. }
  569. void CSVImportDialog::fillComboBox()
  570. {
  571. mComboLine->clear();
  572. for ( int row = 1; row < mTable->numRows() + 1; ++row )
  573. mComboLine->insertItem( TQString::number( row ), row - 1 );
  574. }
  575. void CSVImportDialog::reloadCodecs()
  576. {
  577. mCodecCombo->clear();
  578. mCodecs.clear();
  579. TQTextCodec *codec;
  580. for ( int i = 0; ( codec = TQTextCodec::codecForIndex( i ) ); i++ )
  581. mCodecs.append( codec );
  582. mCodecCombo->insertItem( i18n( "Local (%1)" ).tqarg( TQTextCodec::codecForLocale()->name() ), Local );
  583. mCodecCombo->insertItem( i18n( "[guess]" ), Guess );
  584. mCodecCombo->insertItem( i18n( "Latin1" ), Latin1 );
  585. mCodecCombo->insertItem( i18n( "Unicode" ), Uni );
  586. mCodecCombo->insertItem( i18n( "Microsoft Unicode" ), MSBug );
  587. for ( uint i = 0; i < mCodecs.count(); i++ )
  588. mCodecCombo->insertItem( mCodecs.at( i )->name(), Codec + i );
  589. }
  590. void CSVImportDialog::setText( int row, int col, const TQString& text )
  591. {
  592. if ( row < 1 ) // skipped by the user
  593. return;
  594. if ( mTable->numRows() < row ) {
  595. mTable->setNumRows( row + 5000 ); // We add 5000 at a time to limit recalculations
  596. mAdjustRows = true;
  597. }
  598. if ( mTable->numCols() < col )
  599. mTable->setNumCols( col + 50 ); // We add 50 at a time to limit recalculation
  600. mTable->setText( row - 1, col - 1, text );
  601. }
  602. /*
  603. * Called after the first fillTable() when number of rows are unknown.
  604. */
  605. void CSVImportDialog::adjustRows( int rows )
  606. {
  607. if ( mAdjustRows ) {
  608. mTable->setNumRows( rows );
  609. mAdjustRows = false;
  610. }
  611. }
  612. void CSVImportDialog::resizeColumns()
  613. {
  614. TQFontMetrics fm = fontMetrics();
  615. int width = 0;
  616. TQMap<TQString, uint>::ConstIterator it;
  617. for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it ) {
  618. width = TQMAX( width, fm.width( it.key() ) );
  619. }
  620. for ( int i = 0; i < mTable->numCols(); ++i )
  621. mTable->setColumnWidth( i, TQMAX( width + 15, mTable->columnWidth( i ) ) );
  622. }
  623. void CSVImportDialog::returnPressed()
  624. {
  625. if ( mDelimiterBox->id( mDelimiterBox->selected() ) != 4 )
  626. return;
  627. mDelimiter = mDelimiterEdit->text();
  628. fillTable();
  629. }
  630. void CSVImportDialog::textChanged ( const TQString& )
  631. {
  632. mRadioOther->setChecked ( true );
  633. delimiterClicked( 4 ); // other
  634. }
  635. void CSVImportDialog::delimiterClicked( int id )
  636. {
  637. switch ( id ) {
  638. case 0: // comma
  639. mDelimiter = ",";
  640. break;
  641. case 4: // other
  642. mDelimiter = mDelimiterEdit->text();
  643. break;
  644. case 2: // tab
  645. mDelimiter = "\t";
  646. break;
  647. case 3: // space
  648. mDelimiter = " ";
  649. break;
  650. case 1: // semicolon
  651. mDelimiter = ";";
  652. break;
  653. }
  654. fillTable();
  655. }
  656. void CSVImportDialog::textquoteSelected( const TQString& mark )
  657. {
  658. if ( mComboQuote->currentItem() == 2 )
  659. mTextQuote = 0;
  660. else
  661. mTextQuote = mark[ 0 ];
  662. fillTable();
  663. }
  664. void CSVImportDialog::lineSelected( const TQString& line )
  665. {
  666. mStartLine = line.toInt() - 1;
  667. fillTable();
  668. }
  669. void CSVImportDialog::slotOk()
  670. {
  671. bool assigned = false;
  672. for ( int column = 0; column < mTable->numCols(); ++column ) {
  673. TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
  674. column ) );
  675. if ( item && posToType( item->currentItem() ) != Undefined )
  676. assigned = true;
  677. }
  678. if ( assigned )
  679. KDialogBase::slotOk();
  680. else
  681. KMessageBox::sorry( this, i18n( "You have to assign at least one column." ) );
  682. }
  683. void CSVImportDialog::applyTemplate()
  684. {
  685. TQMap<uint,int> columnMap;
  686. TQMap<TQString, TQString> fileMap;
  687. TQStringList templates;
  688. // load all template files
  689. TQStringList list = KGlobal::dirs()->findAllResources( "data" , TQString( kapp->name() ) +
  690. "/csv-templates/*.desktop", true, true );
  691. for ( TQStringList::iterator it = list.begin(); it != list.end(); ++it )
  692. {
  693. KSimpleConfig config( *it, true );
  694. if ( !config.hasGroup( "csv column map" ) )
  695. continue;
  696. config.setGroup( "Misc" );
  697. templates.append( config.readEntry( "Name" ) );
  698. fileMap.insert( config.readEntry( "Name" ), *it );
  699. }
  700. // let the user chose, what to take
  701. bool ok = false;
  702. TQString tmp;
  703. tmp = KInputDialog::getItem( i18n( "Template Selection" ),
  704. i18n( "Please select a template, that matches the CSV file:" ),
  705. templates, 0, false, &ok, this );
  706. if ( !ok )
  707. return;
  708. KSimpleConfig config( fileMap[ tmp ], true );
  709. config.setGroup( "General" );
  710. mDatePatternEdit->setText( config.readEntry( "DatePattern", "Y-M-D" ) );
  711. uint numColumns = config.readUnsignedNumEntry( "Columns" );
  712. mDelimiterEdit->setText( config.readEntry( "DelimiterOther" ) );
  713. mDelimiterBox->setButton( config.readNumEntry( "DelimiterType" ) );
  714. delimiterClicked( config.readNumEntry( "DelimiterType" ) );
  715. int quoteType = config.readNumEntry( "QuoteType" );
  716. mComboQuote->setCurrentItem( quoteType );
  717. textquoteSelected( mComboQuote->currentText() );
  718. // create the column map
  719. config.setGroup( "csv column map" );
  720. for ( uint i = 0; i < numColumns; ++i ) {
  721. int col = config.readNumEntry( TQString::number( i ) );
  722. columnMap.insert( i, col );
  723. }
  724. // apply the column map
  725. for ( uint column = 0; column < columnMap.count(); ++column ) {
  726. int type = columnMap[ column ];
  727. TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
  728. column ) );
  729. if ( item )
  730. item->setCurrentItem( typeToPos( type ) );
  731. }
  732. }
  733. void CSVImportDialog::saveTemplate()
  734. {
  735. TQString fileName = KFileDialog::getSaveFileName(
  736. locateLocal( "data", TQString( kapp->name() ) + "/csv-templates/" ),
  737. "*.desktop", this );
  738. if ( fileName.isEmpty() )
  739. return;
  740. if ( !fileName.contains( ".desktop" ) )
  741. fileName += ".desktop";
  742. if( TQFileInfo(fileName).exists() ) {
  743. if(KMessageBox::questionYesNo( this, i18n("Do you want to overwrite file \"%1\"").tqarg(fileName) ) == KMessageBox::No)
  744. return;
  745. }
  746. TQString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) );
  747. if ( name.isEmpty() )
  748. return;
  749. KConfig config( fileName );
  750. config.setGroup( "General" );
  751. config.writeEntry( "DatePattern", mDatePatternEdit->text() );
  752. config.writeEntry( "Columns", mTable->numCols() );
  753. config.writeEntry( "DelimiterType", mDelimiterBox->id( mDelimiterBox->selected() ) );
  754. config.writeEntry( "DelimiterOther", mDelimiterEdit->text() );
  755. config.writeEntry( "QuoteType", mComboQuote->currentItem() );
  756. config.setGroup( "Misc" );
  757. config.writeEntry( "Name", name );
  758. config.setGroup( "csv column map" );
  759. for ( int column = 0; column < mTable->numCols(); ++column ) {
  760. TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
  761. column ) );
  762. if ( item )
  763. config.writeEntry( TQString::number( column ), posToType(
  764. item->currentItem() ) );
  765. else
  766. config.writeEntry( TQString::number( column ), 0 );
  767. }
  768. config.sync();
  769. }
  770. TQString CSVImportDialog::getText( int row, int col )
  771. {
  772. return mTable->text( row, col );
  773. }
  774. uint CSVImportDialog::posToType( int pos ) const
  775. {
  776. uint counter = 0;
  777. TQMap<TQString, uint>::ConstIterator it;
  778. for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
  779. if ( counter == (uint)pos )
  780. return it.data();
  781. return 0;
  782. }
  783. int CSVImportDialog::typeToPos( uint type ) const
  784. {
  785. uint counter = 0;
  786. TQMap<TQString, uint>::ConstIterator it;
  787. for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
  788. if ( it.data() == type )
  789. return counter;
  790. return -1;
  791. }
  792. void CSVImportDialog::ignoreDuplicatesChanged( int )
  793. {
  794. fillTable();
  795. }
  796. void CSVImportDialog::setFile( const TQString &fileName )
  797. {
  798. if ( fileName.isEmpty() )
  799. return;
  800. TQFile file( fileName );
  801. if ( !file.open( IO_ReadOnly ) ) {
  802. KMessageBox::sorry( this, i18n( "Cannot open input file." ) );
  803. file.close();
  804. return;
  805. }
  806. mFileArray = file.readAll();
  807. file.close();
  808. mClearTypeStore = true;
  809. clearTable();
  810. mTable->setNumCols( 0 );
  811. mTable->setNumRows( 0 );
  812. fillTable();
  813. mClearTypeStore = false;
  814. fillComboBox();
  815. }
  816. void CSVImportDialog::urlChanged( const TQString &file )
  817. {
  818. bool state = !file.isEmpty();
  819. enableButtonOK( state );
  820. actionButton( User1 )->setEnabled( state );
  821. actionButton( User2 )->setEnabled( state );
  822. }
  823. void CSVImportDialog::codecChanged()
  824. {
  825. fillTable();
  826. }
  827. #include <csvimportdialog.moc>