csvimportdialog.cpp
00001 /* 00002 This file is part of KAddressBook. 00003 Copyright (C) 2003 Tobias Koenig <tokoe@kde.org> 00004 based on the code of KSpread's CSV Import Dialog 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 00023 #include <tqbuttongroup.h> 00024 #include <tqcheckbox.h> 00025 #include <tqcombobox.h> 00026 #include <tqlabel.h> 00027 #include <tqlayout.h> 00028 #include <tqlineedit.h> 00029 #include <tqpushbutton.h> 00030 #include <tqradiobutton.h> 00031 #include <tqtable.h> 00032 #include <tqtextcodec.h> 00033 #include <tqtooltip.h> 00034 00035 #include <kapplication.h> 00036 #include <kdebug.h> 00037 #include <kdialogbase.h> 00038 #include <kfiledialog.h> 00039 #include <klineedit.h> 00040 #include <klocale.h> 00041 #include <kinputdialog.h> 00042 #include <kmessagebox.h> 00043 #include <kprogress.h> 00044 #include <kstandarddirs.h> 00045 #include <kurlrequester.h> 00046 00047 #include "dateparser.h" 00048 00049 #include "csvimportdialog.h" 00050 00051 enum { Local = 0, Guess = 1, Latin1 = 2, Uni = 3, MSBug = 4, Codec = 5 }; 00052 00053 CSVImportDialog::CSVImportDialog( KABC::AddressBook *ab, TQWidget *parent, 00054 const char * name ) 00055 : KDialogBase( Plain, i18n ( "CSV Import Dialog" ), Ok | Cancel | User1 | 00056 User2, Ok, parent, name, true, true ), 00057 mAdjustRows( false ), 00058 mStartLine( 0 ), 00059 mTextQuote( '"' ), 00060 mDelimiter( "," ), 00061 mAddressBook( ab ) 00062 { 00063 initGUI(); 00064 00065 mTypeMap.insert( i18n( "Undefined" ), Undefined ); 00066 mTypeMap.insert( KABC::Addressee::formattedNameLabel(), FormattedName ); 00067 mTypeMap.insert( KABC::Addressee::familyNameLabel(), FamilyName ); 00068 mTypeMap.insert( KABC::Addressee::givenNameLabel(), GivenName ); 00069 mTypeMap.insert( KABC::Addressee::additionalNameLabel(), AdditionalName ); 00070 mTypeMap.insert( KABC::Addressee::prefixLabel(), Prefix ); 00071 mTypeMap.insert( KABC::Addressee::suffixLabel(), Suffix ); 00072 mTypeMap.insert( KABC::Addressee::nickNameLabel(), NickName ); 00073 mTypeMap.insert( KABC::Addressee::birthdayLabel(), Birthday ); 00074 00075 mTypeMap.insert( KABC::Addressee::homeAddressStreetLabel(), HomeAddressStreet ); 00076 mTypeMap.insert( KABC::Addressee::homeAddressLocalityLabel(), 00077 HomeAddressLocality ); 00078 mTypeMap.insert( KABC::Addressee::homeAddressRegionLabel(), HomeAddressRegion ); 00079 mTypeMap.insert( KABC::Addressee::homeAddressPostalCodeLabel(), 00080 HomeAddressPostalCode ); 00081 mTypeMap.insert( KABC::Addressee::homeAddressCountryLabel(), 00082 HomeAddressCountry ); 00083 mTypeMap.insert( KABC::Addressee::homeAddressLabelLabel(), HomeAddressLabel ); 00084 00085 mTypeMap.insert( KABC::Addressee::businessAddressStreetLabel(), 00086 BusinessAddressStreet ); 00087 mTypeMap.insert( KABC::Addressee::businessAddressLocalityLabel(), 00088 BusinessAddressLocality ); 00089 mTypeMap.insert( KABC::Addressee::businessAddressRegionLabel(), 00090 BusinessAddressRegion ); 00091 mTypeMap.insert( KABC::Addressee::businessAddressPostalCodeLabel(), 00092 BusinessAddressPostalCode ); 00093 mTypeMap.insert( KABC::Addressee::businessAddressCountryLabel(), 00094 BusinessAddressCountry ); 00095 mTypeMap.insert( KABC::Addressee::businessAddressLabelLabel(), 00096 BusinessAddressLabel ); 00097 00098 mTypeMap.insert( KABC::Addressee::homePhoneLabel(), HomePhone ); 00099 mTypeMap.insert( KABC::Addressee::businessPhoneLabel(), BusinessPhone ); 00100 mTypeMap.insert( KABC::Addressee::mobilePhoneLabel(), MobilePhone ); 00101 mTypeMap.insert( KABC::Addressee::homeFaxLabel(), HomeFax ); 00102 mTypeMap.insert( KABC::Addressee::businessFaxLabel(), BusinessFax ); 00103 mTypeMap.insert( KABC::Addressee::carPhoneLabel(), CarPhone ); 00104 mTypeMap.insert( KABC::Addressee::isdnLabel(), Isdn ); 00105 mTypeMap.insert( KABC::Addressee::pagerLabel(), Pager ); 00106 mTypeMap.insert( KABC::Addressee::emailLabel(), Email ); 00107 mTypeMap.insert( KABC::Addressee::mailerLabel(), Mailer ); 00108 mTypeMap.insert( KABC::Addressee::titleLabel(), Title ); 00109 mTypeMap.insert( KABC::Addressee::roleLabel(), Role ); 00110 mTypeMap.insert( KABC::Addressee::organizationLabel(), Organization ); 00111 mTypeMap.insert( KABC::Addressee::noteLabel(), Note ); 00112 mTypeMap.insert( KABC::Addressee::urlLabel(), URL ); 00113 00114 mCustomCounter = mTypeMap.count(); 00115 int count = mCustomCounter; 00116 00117 KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory ); 00118 KABC::Field::List::Iterator it; 00119 for ( it = fields.begin(); it != fields.end(); ++it, ++count ) 00120 mTypeMap.insert( (*it)->label(), count ); 00121 00122 reloadCodecs(); 00123 00124 connect( mDelimiterBox, TQT_SIGNAL( clicked( int ) ), 00125 this, TQT_SLOT( delimiterClicked( int ) ) ); 00126 connect( mDelimiterEdit, TQT_SIGNAL( returnPressed() ), 00127 this, TQT_SLOT( returnPressed() ) ); 00128 connect( mDelimiterEdit, TQT_SIGNAL( textChanged ( const TQString& ) ), 00129 this, TQT_SLOT( textChanged ( const TQString& ) ) ); 00130 connect( mComboLine, TQT_SIGNAL( activated( const TQString& ) ), 00131 this, TQT_SLOT( lineSelected( const TQString& ) ) ); 00132 connect( mComboQuote, TQT_SIGNAL( activated( const TQString& ) ), 00133 this, TQT_SLOT( textquoteSelected( const TQString& ) ) ); 00134 connect( mIgnoreDuplicates, TQT_SIGNAL( stateChanged( int ) ), 00135 this, TQT_SLOT( ignoreDuplicatesChanged( int ) ) ); 00136 connect( mCodecCombo, TQT_SIGNAL( activated( const TQString& ) ), 00137 this, TQT_SLOT( codecChanged() ) ); 00138 00139 connect( mUrlRequester, TQT_SIGNAL( returnPressed( const TQString& ) ), 00140 this, TQT_SLOT( setFile( const TQString& ) ) ); 00141 connect( mUrlRequester, TQT_SIGNAL( urlSelected( const TQString& ) ), 00142 this, TQT_SLOT( setFile( const TQString& ) ) ); 00143 connect( mUrlRequester->lineEdit(), TQT_SIGNAL( textChanged ( const TQString& ) ), 00144 this, TQT_SLOT( urlChanged( const TQString& ) ) ); 00145 00146 connect( this, TQT_SIGNAL( user1Clicked() ), 00147 this, TQT_SLOT( applyTemplate() ) ); 00148 00149 connect( this, TQT_SIGNAL( user2Clicked() ), 00150 this, TQT_SLOT( saveTemplate() ) ); 00151 } 00152 00153 CSVImportDialog::~CSVImportDialog() 00154 { 00155 mCodecs.clear(); 00156 } 00157 00158 KABC::AddresseeList CSVImportDialog::contacts() const 00159 { 00160 DateParser dateParser( mDatePatternEdit->text() ); 00161 KABC::AddresseeList contacts; 00162 00163 KProgressDialog progressDialog( mPage ); 00164 progressDialog.setAutoClose( true ); 00165 progressDialog.progressBar()->setTotalSteps( mTable->numRows() ); 00166 progressDialog.setLabel( i18n( "Importing contacts" ) ); 00167 progressDialog.show(); 00168 00169 kapp->processEvents(); 00170 00171 for ( int row = 1; row < mTable->numRows(); ++row ) { 00172 KABC::Addressee a; 00173 bool emptyRow = true; 00174 KABC::Address addrHome( KABC::Address::Home ); 00175 KABC::Address addrWork( KABC::Address::Work ); 00176 for ( int col = 0; col < mTable->numCols(); ++col ) { 00177 TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0, 00178 col ) ); 00179 if ( !item ) { 00180 kdError() << "ERROR: item cast failed" << endl; 00181 continue; 00182 } 00183 00184 TQString value = mTable->text( row, col ); 00185 if ( 1 == row && static_cast<TQTableItem *>(item)->text() == value ) 00186 // we are looking at a header row, stop now 00187 break; 00188 00189 if ( !value.isEmpty() ) 00190 emptyRow = false; 00191 00192 switch ( posToType( item->currentItem() ) ) { 00193 case Undefined: 00194 continue; 00195 break; 00196 case FormattedName: 00197 a.setFormattedName( value ); 00198 break; 00199 case GivenName: 00200 a.setGivenName( value ); 00201 break; 00202 case FamilyName: 00203 a.setFamilyName( value ); 00204 break; 00205 case AdditionalName: 00206 a.setAdditionalName( value ); 00207 break; 00208 case Prefix: 00209 a.setPrefix( value ); 00210 break; 00211 case Suffix: 00212 a.setSuffix( value ); 00213 break; 00214 case NickName: 00215 a.setNickName( value ); 00216 break; 00217 case Birthday: 00218 a.setBirthday( dateParser.parse( value ) ); 00219 break; 00220 case Email: 00221 if ( !value.isEmpty() ) 00222 a.insertEmail( value, true ); 00223 break; 00224 case Role: 00225 a.setRole( value ); 00226 break; 00227 case Title: 00228 a.setTitle( value ); 00229 break; 00230 case Mailer: 00231 a.setMailer( value ); 00232 break; 00233 case URL: 00234 a.setUrl( KURL( value ) ); 00235 break; 00236 case Organization: 00237 a.setOrganization( value ); 00238 break; 00239 case Note: 00240 a.setNote( a.note() + value + "\n" ); 00241 break; 00242 00243 case HomePhone: 00244 if ( !value.isEmpty() ) { 00245 KABC::PhoneNumber number( value, KABC::PhoneNumber::Home ); 00246 a.insertPhoneNumber( number ); 00247 } 00248 break; 00249 case BusinessPhone: 00250 if ( !value.isEmpty() ) { 00251 KABC::PhoneNumber number( value, KABC::PhoneNumber::Work ); 00252 a.insertPhoneNumber( number ); 00253 } 00254 break; 00255 case MobilePhone: 00256 if ( !value.isEmpty() ) { 00257 KABC::PhoneNumber number( value, KABC::PhoneNumber::Cell ); 00258 a.insertPhoneNumber( number ); 00259 } 00260 break; 00261 case HomeFax: 00262 if ( !value.isEmpty() ) { 00263 KABC::PhoneNumber number( value, KABC::PhoneNumber::Home | 00264 KABC::PhoneNumber::Fax ); 00265 a.insertPhoneNumber( number ); 00266 } 00267 break; 00268 case BusinessFax: 00269 if ( !value.isEmpty() ) { 00270 KABC::PhoneNumber number( value, KABC::PhoneNumber::Work | 00271 KABC::PhoneNumber::Fax ); 00272 a.insertPhoneNumber( number ); 00273 } 00274 break; 00275 case CarPhone: 00276 if ( !value.isEmpty() ) { 00277 KABC::PhoneNumber number( value, KABC::PhoneNumber::Car ); 00278 a.insertPhoneNumber( number ); 00279 } 00280 break; 00281 case Isdn: 00282 if ( !value.isEmpty() ) { 00283 KABC::PhoneNumber number( value, KABC::PhoneNumber::Isdn ); 00284 a.insertPhoneNumber( number ); 00285 } 00286 break; 00287 case Pager: 00288 if ( !value.isEmpty() ) { 00289 KABC::PhoneNumber number( value, KABC::PhoneNumber::Pager ); 00290 a.insertPhoneNumber( number ); 00291 } 00292 break; 00293 00294 case HomeAddressStreet: 00295 addrHome.setStreet( value ); 00296 break; 00297 case HomeAddressLocality: 00298 addrHome.setLocality( value ); 00299 break; 00300 case HomeAddressRegion: 00301 addrHome.setRegion( value ); 00302 break; 00303 case HomeAddressPostalCode: 00304 addrHome.setPostalCode( value ); 00305 break; 00306 case HomeAddressCountry: 00307 addrHome.setCountry( value ); 00308 break; 00309 case HomeAddressLabel: 00310 addrHome.setLabel( value ); 00311 break; 00312 00313 case BusinessAddressStreet: 00314 addrWork.setStreet( value ); 00315 break; 00316 case BusinessAddressLocality: 00317 addrWork.setLocality( value ); 00318 break; 00319 case BusinessAddressRegion: 00320 addrWork.setRegion( value ); 00321 break; 00322 case BusinessAddressPostalCode: 00323 addrWork.setPostalCode( value ); 00324 break; 00325 case BusinessAddressCountry: 00326 addrWork.setCountry( value ); 00327 break; 00328 case BusinessAddressLabel: 00329 addrWork.setLabel( value ); 00330 break; 00331 default: 00332 KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory ); 00333 KABC::Field::List::Iterator it; 00334 00335 int counter = 0; 00336 for ( it = fields.begin(); it != fields.end(); ++it ) { 00337 if ( counter == (int)( posToType( item->currentItem() ) - mCustomCounter ) ) { 00338 (*it)->setValue( a, value ); 00339 break; 00340 } 00341 ++counter; 00342 } 00343 break; 00344 } 00345 } 00346 00347 kapp->processEvents(); 00348 00349 if ( progressDialog.wasCancelled() ) 00350 return KABC::AddresseeList(); 00351 00352 progressDialog.progressBar()->advance( 1 ); 00353 00354 if ( !addrHome.isEmpty() ) 00355 a.insertAddress( addrHome ); 00356 if ( !addrWork.isEmpty() ) 00357 a.insertAddress( addrWork ); 00358 00359 if ( !emptyRow && !a.isEmpty() ) 00360 contacts.append( a ); 00361 } 00362 00363 return contacts; 00364 } 00365 00366 void CSVImportDialog::initGUI() 00367 { 00368 mPage = plainPage(); 00369 00370 TQGridLayout *layout = new TQGridLayout( mPage, 1, 1, marginHint(), 00371 spacingHint() ); 00372 TQHBoxLayout *hbox = new TQHBoxLayout(); 00373 hbox->setSpacing( spacingHint() ); 00374 00375 TQLabel *label = new TQLabel( i18n( "File to import:" ), mPage ); 00376 hbox->addWidget( label ); 00377 00378 mUrlRequester = new KURLRequester( mPage ); 00379 mUrlRequester->setFilter( "*.csv" ); 00380 hbox->addWidget( mUrlRequester ); 00381 00382 layout->addMultiCellLayout( hbox, 0, 0, 0, 4 ); 00383 00384 // Delimiter: comma, semicolon, tab, space, other 00385 mDelimiterBox = new TQButtonGroup( i18n( "Delimiter" ), mPage ); 00386 mDelimiterBox->setColumnLayout( 0, Qt::Vertical ); 00387 mDelimiterBox->layout()->setSpacing( spacingHint() ); 00388 mDelimiterBox->layout()->setMargin( marginHint() ); 00389 TQGridLayout *delimiterLayout = new TQGridLayout( mDelimiterBox->layout() ); 00390 delimiterLayout->setAlignment( TQt::AlignTop ); 00391 layout->addMultiCellWidget( mDelimiterBox, 1, 4, 0, 0 ); 00392 00393 mRadioComma = new TQRadioButton( i18n( "Comma" ), mDelimiterBox ); 00394 mRadioComma->setChecked( true ); 00395 delimiterLayout->addWidget( mRadioComma, 0, 0 ); 00396 00397 mRadioSemicolon = new TQRadioButton( i18n( "Semicolon" ), mDelimiterBox ); 00398 delimiterLayout->addWidget( mRadioSemicolon, 0, 1 ); 00399 00400 mRadioTab = new TQRadioButton( i18n( "Tabulator" ), mDelimiterBox ); 00401 delimiterLayout->addWidget( mRadioTab, 1, 0 ); 00402 00403 mRadioSpace = new TQRadioButton( i18n( "Space" ), mDelimiterBox ); 00404 delimiterLayout->addWidget( mRadioSpace, 1, 1 ); 00405 00406 mRadioOther = new TQRadioButton( i18n( "Other" ), mDelimiterBox ); 00407 delimiterLayout->addWidget( mRadioOther, 0, 2 ); 00408 00409 mDelimiterEdit = new TQLineEdit( mDelimiterBox ); 00410 delimiterLayout->addWidget( mDelimiterEdit, 1, 2 ); 00411 00412 mComboLine = new TQComboBox( false, mPage ); 00413 mComboLine->insertItem( i18n( "1" ) ); 00414 layout->addWidget( mComboLine, 2, 3 ); 00415 00416 mComboQuote = new TQComboBox( false, mPage ); 00417 mComboQuote->insertItem( i18n( "\"" ), 0 ); 00418 mComboQuote->insertItem( i18n( "'" ), 1 ); 00419 mComboQuote->insertItem( i18n( "None" ), 2 ); 00420 layout->addWidget( mComboQuote, 2, 2 ); 00421 00422 mDatePatternEdit = new TQLineEdit( mPage ); 00423 mDatePatternEdit->setText( "Y-M-D" ); // ISO 8601 format as default 00424 TQToolTip::add( mDatePatternEdit, i18n( "<ul><li>y: year with 2 digits</li>" 00425 "<li>Y: year with 4 digits</li>" 00426 "<li>m: month with 1 or 2 digits</li>" 00427 "<li>M: month with 2 digits</li>" 00428 "<li>d: day with 1 or 2 digits</li>" 00429 "<li>D: day with 2 digits</li></ul>" ) ); 00430 layout->addWidget( mDatePatternEdit, 2, 4 ); 00431 00432 label = new TQLabel( i18n( "Start at line:" ), mPage ); 00433 layout->addWidget( label, 1, 3 ); 00434 00435 label = new TQLabel( i18n( "Textquote:" ), mPage ); 00436 layout->addWidget( label, 1, 2 ); 00437 00438 label = new TQLabel( i18n( "Date format:" ), mPage ); 00439 layout->addWidget( label, 1, 4 ); 00440 00441 mIgnoreDuplicates = new TQCheckBox( mPage ); 00442 mIgnoreDuplicates->setText( i18n( "Ignore duplicate delimiters" ) ); 00443 layout->addMultiCellWidget( mIgnoreDuplicates, 3, 3, 2, 4 ); 00444 00445 mCodecCombo = new TQComboBox( mPage ); 00446 layout->addMultiCellWidget( mCodecCombo, 4, 4, 2, 4 ); 00447 00448 mTable = new TQTable( 0, 0, mPage ); 00449 mTable->setSelectionMode( TQTable::NoSelection ); 00450 mTable->horizontalHeader()->hide(); 00451 layout->addMultiCellWidget( mTable, 5, 5, 0, 4 ); 00452 00453 setButtonText( User1, i18n( "Apply Template..." ) ); 00454 setButtonText( User2, i18n( "Save Template..." ) ); 00455 00456 enableButtonOK( false ); 00457 actionButton( User1 )->setEnabled( false ); 00458 actionButton( User2 )->setEnabled( false ); 00459 00460 resize( 400, 300 ); 00461 } 00462 00463 void CSVImportDialog::fillTable() 00464 { 00465 int row, column; 00466 bool lastCharDelimiter = false; 00467 bool ignoreDups = mIgnoreDuplicates->isChecked(); 00468 enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD, 00469 S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START; 00470 00471 TQChar x; 00472 TQString field; 00473 00474 // store previous assignment 00475 mTypeStore.clear(); 00476 for ( column = 0; column < mTable->numCols(); ++column ) { 00477 TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0, 00478 column ) ); 00479 if ( !item || mClearTypeStore ) 00480 mTypeStore.append( typeToPos( Undefined ) ); 00481 else if ( item ) 00482 mTypeStore.append( item->currentItem() ); 00483 } 00484 00485 clearTable(); 00486 00487 row = column = 1; 00488 00489 TQTextStream inputStream( mFileArray, IO_ReadOnly ); 00490 00491 // find the current codec 00492 int code = mCodecCombo->currentItem(); 00493 if ( code == Local ) 00494 inputStream.setEncoding( TQTextStream::Locale ); 00495 else if ( code >= Codec ) 00496 inputStream.setCodec( mCodecs.at( code - Codec ) ); 00497 else if ( code == Uni ) 00498 inputStream.setEncoding( TQTextStream::Unicode ); 00499 else if ( code == MSBug ) 00500 inputStream.setEncoding( TQTextStream::UnicodeReverse ); 00501 else if ( code == Latin1 ) 00502 inputStream.setEncoding( TQTextStream::Latin1 ); 00503 else if ( code == Guess ) { 00504 TQTextCodec* codec = TQTextCodec::codecForContent( mFileArray.data(), mFileArray.size() ); 00505 if ( codec ) { 00506 KMessageBox::information( this, i18n( "Using codec '%1'" ).arg( codec->name() ), i18n( "Encoding" ) ); 00507 inputStream.setCodec( codec ); 00508 } 00509 } 00510 00511 int maxColumn = 0; 00512 while ( !inputStream.atEnd() ) { 00513 inputStream >> x; // read one char 00514 00515 if ( x == '\r' ) inputStream >> x; // eat '\r', to handle DOS/LOSEDOWS files correctly 00516 00517 switch ( state ) { 00518 case S_START : 00519 if ( x == mTextQuote ) { 00520 state = S_QUOTED_FIELD; 00521 } else if ( x == mDelimiter ) { 00522 if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) ) 00523 ++column; 00524 lastCharDelimiter = true; 00525 } else if ( x == '\n' ) { 00526 ++row; 00527 column = 1; 00528 } else { 00529 field += x; 00530 state = S_MAYBE_NORMAL_FIELD; 00531 } 00532 break; 00533 case S_QUOTED_FIELD : 00534 if ( x == mTextQuote ) { 00535 state = S_MAYBE_END_OF_QUOTED_FIELD; 00536 } else if ( x == '\n' && mTextQuote.isNull() ) { 00537 setText( row - mStartLine + 1, column, field ); 00538 field = ""; 00539 if ( x == '\n' ) { 00540 ++row; 00541 column = 1; 00542 } else { 00543 if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) ) 00544 ++column; 00545 lastCharDelimiter = true; 00546 } 00547 state = S_START; 00548 } else { 00549 field += x; 00550 } 00551 break; 00552 case S_MAYBE_END_OF_QUOTED_FIELD : 00553 if ( x == mTextQuote ) { 00554 field += x; 00555 state = S_QUOTED_FIELD; 00556 } else if ( x == mDelimiter || x == '\n' ) { 00557 setText( row - mStartLine + 1, column, field ); 00558 field = ""; 00559 if ( x == '\n' ) { 00560 ++row; 00561 column = 1; 00562 } else { 00563 if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) ) 00564 ++column; 00565 lastCharDelimiter = true; 00566 } 00567 state = S_START; 00568 } else { 00569 state = S_END_OF_QUOTED_FIELD; 00570 } 00571 break; 00572 case S_END_OF_QUOTED_FIELD : 00573 if ( x == mDelimiter || x == '\n' ) { 00574 setText( row - mStartLine + 1, column, field ); 00575 field = ""; 00576 if ( x == '\n' ) { 00577 ++row; 00578 column = 1; 00579 } else { 00580 if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) ) 00581 ++column; 00582 lastCharDelimiter = true; 00583 } 00584 state = S_START; 00585 } else { 00586 state = S_END_OF_QUOTED_FIELD; 00587 } 00588 break; 00589 case S_MAYBE_NORMAL_FIELD : 00590 if ( x == mTextQuote ) { 00591 field = ""; 00592 state = S_QUOTED_FIELD; 00593 break; 00594 } 00595 case S_NORMAL_FIELD : 00596 if ( x == mDelimiter || x == '\n' ) { 00597 setText( row - mStartLine + 1, column, field ); 00598 field = ""; 00599 if ( x == '\n' ) { 00600 ++row; 00601 column = 1; 00602 } else { 00603 if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) ) 00604 ++column; 00605 lastCharDelimiter = true; 00606 } 00607 state = S_START; 00608 } else { 00609 field += x; 00610 } 00611 } 00612 if ( x != mDelimiter ) 00613 lastCharDelimiter = false; 00614 00615 if ( column > maxColumn ) 00616 maxColumn = column; 00617 } 00618 00619 // file with only one line without '\n' 00620 if ( field.length() > 0 ) { 00621 setText( row - mStartLine + 1, column, field ); 00622 ++row; 00623 field = ""; 00624 } 00625 00626 adjustRows( row - mStartLine ); 00627 mTable->setNumCols( maxColumn ); 00628 00629 for ( column = 0; column < mTable->numCols(); ++column ) { 00630 TQComboTableItem *item = new TQComboTableItem( mTable, mTypeMap.keys() ); 00631 mTable->setItem( 0, column, item ); 00632 if ( column < (int)mTypeStore.count() ) 00633 item->setCurrentItem( mTypeStore[ column ] ); 00634 else 00635 item->setCurrentItem( typeToPos( Undefined ) ); 00636 mTable->adjustColumn( column ); 00637 } 00638 00639 resizeColumns(); 00640 } 00641 00642 void CSVImportDialog::clearTable() 00643 { 00644 for ( int row = 0; row < mTable->numRows(); ++row ) 00645 for ( int column = 0; column < mTable->numCols(); ++column ) 00646 mTable->clearCell( row, column ); 00647 } 00648 00649 void CSVImportDialog::fillComboBox() 00650 { 00651 mComboLine->clear(); 00652 for ( int row = 1; row < mTable->numRows() + 1; ++row ) 00653 mComboLine->insertItem( TQString::number( row ), row - 1 ); 00654 } 00655 00656 void CSVImportDialog::reloadCodecs() 00657 { 00658 mCodecCombo->clear(); 00659 00660 mCodecs.clear(); 00661 00662 TQTextCodec *codec; 00663 for ( int i = 0; ( codec = TQTextCodec::codecForIndex( i ) ); i++ ) 00664 mCodecs.append( codec ); 00665 00666 mCodecCombo->insertItem( i18n( "Local (%1)" ).arg( TQTextCodec::codecForLocale()->name() ), Local ); 00667 mCodecCombo->insertItem( i18n( "[guess]" ), Guess ); 00668 mCodecCombo->insertItem( i18n( "Latin1" ), Latin1 ); 00669 mCodecCombo->insertItem( i18n( "Unicode" ), Uni ); 00670 mCodecCombo->insertItem( i18n( "Microsoft Unicode" ), MSBug ); 00671 00672 for ( uint i = 0; i < mCodecs.count(); i++ ) 00673 mCodecCombo->insertItem( mCodecs.at( i )->name(), Codec + i ); 00674 } 00675 00676 void CSVImportDialog::setText( int row, int col, const TQString& text ) 00677 { 00678 if ( row < 1 ) // skipped by the user 00679 return; 00680 00681 if ( mTable->numRows() < row ) { 00682 mTable->setNumRows( row + 5000 ); // We add 5000 at a time to limit recalculations 00683 mAdjustRows = true; 00684 } 00685 00686 if ( mTable->numCols() < col ) 00687 mTable->setNumCols( col + 50 ); // We add 50 at a time to limit recalculation 00688 00689 mTable->setText( row - 1, col - 1, text ); 00690 } 00691 00692 /* 00693 * Called after the first fillTable() when number of rows are unknown. 00694 */ 00695 void CSVImportDialog::adjustRows( int rows ) 00696 { 00697 if ( mAdjustRows ) { 00698 mTable->setNumRows( rows ); 00699 mAdjustRows = false; 00700 } 00701 } 00702 00703 void CSVImportDialog::resizeColumns() 00704 { 00705 TQFontMetrics fm = fontMetrics(); 00706 int width = 0; 00707 00708 TQMap<TQString, uint>::ConstIterator it; 00709 for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it ) { 00710 width = TQMAX( width, fm.width( it.key() ) ); 00711 } 00712 00713 for ( int i = 0; i < mTable->numCols(); ++i ) 00714 mTable->setColumnWidth( i, TQMAX( width + 15, mTable->columnWidth( i ) ) ); 00715 } 00716 00717 void CSVImportDialog::returnPressed() 00718 { 00719 if ( mDelimiterBox->id( mDelimiterBox->selected() ) != 4 ) 00720 return; 00721 00722 mDelimiter = mDelimiterEdit->text(); 00723 fillTable(); 00724 } 00725 00726 void CSVImportDialog::textChanged ( const TQString& ) 00727 { 00728 mRadioOther->setChecked ( true ); 00729 delimiterClicked( 4 ); // other 00730 } 00731 00732 void CSVImportDialog::delimiterClicked( int id ) 00733 { 00734 switch ( id ) { 00735 case 0: // comma 00736 mDelimiter = ","; 00737 break; 00738 case 4: // other 00739 mDelimiter = mDelimiterEdit->text(); 00740 break; 00741 case 2: // tab 00742 mDelimiter = "\t"; 00743 break; 00744 case 3: // space 00745 mDelimiter = " "; 00746 break; 00747 case 1: // semicolon 00748 mDelimiter = ";"; 00749 break; 00750 } 00751 00752 fillTable(); 00753 } 00754 00755 void CSVImportDialog::textquoteSelected( const TQString& mark ) 00756 { 00757 if ( mComboQuote->currentItem() == 2 ) 00758 mTextQuote = 0; 00759 else 00760 mTextQuote = mark[ 0 ]; 00761 00762 fillTable(); 00763 } 00764 00765 void CSVImportDialog::lineSelected( const TQString& line ) 00766 { 00767 mStartLine = line.toInt() - 1; 00768 fillTable(); 00769 } 00770 00771 void CSVImportDialog::slotOk() 00772 { 00773 bool assigned = false; 00774 00775 for ( int column = 0; column < mTable->numCols(); ++column ) { 00776 TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0, 00777 column ) ); 00778 if ( item && posToType( item->currentItem() ) != Undefined ) 00779 assigned = true; 00780 } 00781 00782 if ( assigned ) 00783 KDialogBase::slotOk(); 00784 else 00785 KMessageBox::sorry( this, i18n( "You have to assign at least one column." ) ); 00786 } 00787 00788 void CSVImportDialog::applyTemplate() 00789 { 00790 TQMap<uint,int> columnMap; 00791 TQMap<TQString, TQString> fileMap; 00792 TQStringList templates; 00793 00794 // load all template files 00795 TQStringList list = KGlobal::dirs()->findAllResources( "data" , TQString( kapp->name() ) + 00796 "/csv-templates/*.desktop", true, true ); 00797 00798 for ( TQStringList::iterator it = list.begin(); it != list.end(); ++it ) 00799 { 00800 KSimpleConfig config( *it, true ); 00801 00802 if ( !config.hasGroup( "csv column map" ) ) 00803 continue; 00804 00805 config.setGroup( "Misc" ); 00806 templates.append( config.readEntry( "Name" ) ); 00807 fileMap.insert( config.readEntry( "Name" ), *it ); 00808 } 00809 00810 // let the user chose, what to take 00811 bool ok = false; 00812 TQString tmp; 00813 tmp = KInputDialog::getItem( i18n( "Template Selection" ), 00814 i18n( "Please select a template, that matches the CSV file:" ), 00815 templates, 0, false, &ok, this ); 00816 00817 if ( !ok ) 00818 return; 00819 00820 KSimpleConfig config( fileMap[ tmp ], true ); 00821 config.setGroup( "General" ); 00822 mDatePatternEdit->setText( config.readEntry( "DatePattern", "Y-M-D" ) ); 00823 uint numColumns = config.readUnsignedNumEntry( "Columns" ); 00824 mDelimiterEdit->setText( config.readEntry( "DelimiterOther" ) ); 00825 mDelimiterBox->setButton( config.readNumEntry( "DelimiterType" ) ); 00826 delimiterClicked( config.readNumEntry( "DelimiterType" ) ); 00827 int quoteType = config.readNumEntry( "QuoteType" ); 00828 mComboQuote->setCurrentItem( quoteType ); 00829 textquoteSelected( mComboQuote->currentText() ); 00830 00831 // create the column map 00832 config.setGroup( "csv column map" ); 00833 for ( uint i = 0; i < numColumns; ++i ) { 00834 int col = config.readNumEntry( TQString::number( i ) ); 00835 columnMap.insert( i, col ); 00836 } 00837 00838 // apply the column map 00839 for ( uint column = 0; column < columnMap.count(); ++column ) { 00840 int type = columnMap[ column ]; 00841 TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0, 00842 column ) ); 00843 if ( item ) 00844 item->setCurrentItem( typeToPos( type ) ); 00845 } 00846 } 00847 00848 void CSVImportDialog::saveTemplate() 00849 { 00850 TQString fileName = KFileDialog::getSaveFileName( 00851 locateLocal( "data", TQString( kapp->name() ) + "/csv-templates/" ), 00852 "*.desktop", this ); 00853 00854 if ( fileName.isEmpty() ) 00855 return; 00856 00857 if ( !fileName.contains( ".desktop" ) ) 00858 fileName += ".desktop"; 00859 00860 if( TQFileInfo(fileName).exists() ) { 00861 if(KMessageBox::questionYesNo( this, i18n("Do you want to overwrite file \"%1\"").arg(fileName) ) == KMessageBox::No) 00862 return; 00863 } 00864 TQString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) ); 00865 00866 if ( name.isEmpty() ) 00867 return; 00868 00869 KConfig config( fileName ); 00870 config.setGroup( "General" ); 00871 config.writeEntry( "DatePattern", mDatePatternEdit->text() ); 00872 config.writeEntry( "Columns", mTable->numCols() ); 00873 config.writeEntry( "DelimiterType", mDelimiterBox->id( mDelimiterBox->selected() ) ); 00874 config.writeEntry( "DelimiterOther", mDelimiterEdit->text() ); 00875 config.writeEntry( "QuoteType", mComboQuote->currentItem() ); 00876 00877 config.setGroup( "Misc" ); 00878 config.writeEntry( "Name", name ); 00879 00880 config.setGroup( "csv column map" ); 00881 00882 for ( int column = 0; column < mTable->numCols(); ++column ) { 00883 TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0, 00884 column ) ); 00885 if ( item ) 00886 config.writeEntry( TQString::number( column ), posToType( 00887 item->currentItem() ) ); 00888 else 00889 config.writeEntry( TQString::number( column ), 0 ); 00890 } 00891 00892 config.sync(); 00893 } 00894 00895 TQString CSVImportDialog::getText( int row, int col ) 00896 { 00897 return mTable->text( row, col ); 00898 } 00899 00900 uint CSVImportDialog::posToType( int pos ) const 00901 { 00902 uint counter = 0; 00903 TQMap<TQString, uint>::ConstIterator it; 00904 for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter ) 00905 if ( counter == (uint)pos ) 00906 return it.data(); 00907 00908 return 0; 00909 } 00910 00911 int CSVImportDialog::typeToPos( uint type ) const 00912 { 00913 uint counter = 0; 00914 TQMap<TQString, uint>::ConstIterator it; 00915 for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter ) 00916 if ( it.data() == type ) 00917 return counter; 00918 00919 return -1; 00920 } 00921 00922 void CSVImportDialog::ignoreDuplicatesChanged( int ) 00923 { 00924 fillTable(); 00925 } 00926 00927 void CSVImportDialog::setFile( const TQString &fileName ) 00928 { 00929 if ( fileName.isEmpty() ) 00930 return; 00931 00932 TQFile file( fileName ); 00933 if ( !file.open( IO_ReadOnly ) ) { 00934 KMessageBox::sorry( this, i18n( "Cannot open input file." ) ); 00935 file.close(); 00936 return; 00937 } 00938 00939 mFileArray = file.readAll(); 00940 file.close(); 00941 00942 mClearTypeStore = true; 00943 clearTable(); 00944 mTable->setNumCols( 0 ); 00945 mTable->setNumRows( 0 ); 00946 fillTable(); 00947 mClearTypeStore = false; 00948 00949 fillComboBox(); 00950 } 00951 00952 void CSVImportDialog::urlChanged( const TQString &file ) 00953 { 00954 bool state = !file.isEmpty(); 00955 00956 enableButtonOK( state ); 00957 actionButton( User1 )->setEnabled( state ); 00958 actionButton( User2 )->setEnabled( state ); 00959 } 00960 00961 void CSVImportDialog::codecChanged() 00962 { 00963 fillTable(); 00964 } 00965 00966 #include <csvimportdialog.moc>