kaddressbook

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