kaddressbook Library API Documentation

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., 59 Temple Place - Suite 330,
00019    Boston, MA 02111-1307, USA.
00020 */
00021 
00022 
00023 #include <qbuttongroup.h>
00024 #include <qcheckbox.h>
00025 #include <qcombobox.h>
00026 #include <qlabel.h>
00027 #include <qlayout.h>
00028 #include <qlineedit.h>
00029 #include <qpushbutton.h>
00030 #include <qradiobutton.h>
00031 #include <qtable.h>
00032 #include <qtextcodec.h>
00033 #include <qtooltip.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, QWidget *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, SIGNAL( clicked( int ) ),
00125            this, SLOT( delimiterClicked( int ) ) );
00126   connect( mDelimiterEdit, SIGNAL( returnPressed() ),
00127            this, SLOT( returnPressed() ) );
00128   connect( mDelimiterEdit, SIGNAL( textChanged ( const QString& ) ),
00129            this, SLOT( textChanged ( const QString& ) ) );
00130   connect( mComboLine, SIGNAL( activated( const QString& ) ),
00131            this, SLOT( lineSelected( const QString& ) ) );
00132   connect( mComboQuote, SIGNAL( activated( const QString& ) ),
00133            this, SLOT( textquoteSelected( const QString& ) ) );
00134   connect( mIgnoreDuplicates, SIGNAL( stateChanged( int ) ),
00135            this, SLOT( ignoreDuplicatesChanged( int ) ) );
00136   connect( mCodecCombo, SIGNAL( activated( const QString& ) ),
00137            this, SLOT( codecChanged() ) );
00138 
00139   connect( mUrlRequester, SIGNAL( returnPressed( const QString& ) ),
00140            this, SLOT( setFile( const QString& ) ) );
00141   connect( mUrlRequester, SIGNAL( urlSelected( const QString& ) ),
00142            this, SLOT( setFile( const QString& ) ) );
00143   connect( mUrlRequester->lineEdit(), SIGNAL( textChanged ( const QString& ) ),
00144            this, SLOT( urlChanged( const QString& ) ) );
00145 
00146   connect( this, SIGNAL( user1Clicked() ),
00147            this, SLOT( applyTemplate() ) );
00148 
00149   connect( this, SIGNAL( user2Clicked() ),
00150            this, 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       QComboTableItem *item = static_cast<QComboTableItem*>( mTable->item( 0,
00178                                                              col ) );
00179       if ( !item ) {
00180         kdError() << "ERROR: item cast failed" << endl;
00181         continue;
00182       }
00183 
00184       QString value = mTable->text( row, col );
00185       if ( !value.isEmpty() )
00186         emptyRow = false;
00187 
00188       switch ( posToType( item->currentItem() ) ) {
00189         case Undefined:
00190           continue;
00191           break;
00192         case FormattedName:
00193           a.setFormattedName( value );
00194           break;
00195         case GivenName:
00196           a.setGivenName( value );
00197           break;
00198         case FamilyName:
00199           a.setFamilyName( value );
00200           break;
00201         case AdditionalName:
00202           a.setAdditionalName( value );
00203           break;
00204         case Prefix:
00205           a.setPrefix( value );
00206           break;
00207         case Suffix:
00208           a.setSuffix( value );
00209           break;
00210         case NickName:
00211           a.setNickName( value );
00212           break;
00213         case Birthday:
00214           a.setBirthday( dateParser.parse( value ) );
00215           break;
00216         case Email:
00217           if ( !value.isEmpty() )
00218             a.insertEmail( value, true );
00219           break;
00220         case Role:
00221           a.setRole( value );
00222           break;
00223         case Title:
00224           a.setTitle( value );
00225           break;
00226         case Mailer:
00227           a.setMailer( value );
00228           break;
00229         case URL:
00230           a.setUrl( KURL( value ) );
00231           break;
00232         case Organization:
00233           a.setOrganization( value );
00234           break;
00235         case Note:
00236           a.setNote( value );
00237           break;
00238 
00239         case HomePhone:
00240           if ( !value.isEmpty() ) {
00241             KABC::PhoneNumber number( value, KABC::PhoneNumber::Home );
00242             a.insertPhoneNumber( number );
00243           }
00244           break;
00245         case BusinessPhone:
00246           if ( !value.isEmpty() ) {
00247             KABC::PhoneNumber number( value, KABC::PhoneNumber::Work );
00248             a.insertPhoneNumber( number );
00249           }
00250           break;
00251         case MobilePhone:
00252           if ( !value.isEmpty() ) {
00253             KABC::PhoneNumber number( value, KABC::PhoneNumber::Cell );
00254             a.insertPhoneNumber( number );
00255           }
00256           break;
00257         case HomeFax:
00258           if ( !value.isEmpty() ) {
00259             KABC::PhoneNumber number( value, KABC::PhoneNumber::Home |
00260                                              KABC::PhoneNumber::Fax );
00261             a.insertPhoneNumber( number );
00262           }
00263           break;
00264         case BusinessFax:
00265           if ( !value.isEmpty() ) {
00266             KABC::PhoneNumber number( value, KABC::PhoneNumber::Work |
00267                                              KABC::PhoneNumber::Fax );
00268             a.insertPhoneNumber( number );
00269           }
00270           break;
00271         case CarPhone:
00272           if ( !value.isEmpty() ) {
00273             KABC::PhoneNumber number( value, KABC::PhoneNumber::Car );
00274             a.insertPhoneNumber( number );
00275           }
00276           break;
00277         case Isdn:
00278           if ( !value.isEmpty() ) {
00279             KABC::PhoneNumber number( value, KABC::PhoneNumber::Isdn );
00280             a.insertPhoneNumber( number );
00281           }
00282           break;
00283         case Pager:
00284           if ( !value.isEmpty() ) {
00285             KABC::PhoneNumber number( value, KABC::PhoneNumber::Pager );
00286             a.insertPhoneNumber( number );
00287           }
00288           break;
00289 
00290         case HomeAddressStreet:
00291           addrHome.setStreet( value );
00292           break;
00293         case HomeAddressLocality:
00294           addrHome.setLocality( value );
00295           break;
00296         case HomeAddressRegion:
00297           addrHome.setRegion( value );
00298           break;
00299         case HomeAddressPostalCode:
00300           addrHome.setPostalCode( value );
00301           break;
00302         case HomeAddressCountry:
00303           addrHome.setCountry( value );
00304           break;
00305         case HomeAddressLabel:
00306           addrHome.setLabel( value );
00307           break;
00308 
00309         case BusinessAddressStreet:
00310           addrWork.setStreet( value );
00311           break;
00312         case BusinessAddressLocality:
00313           addrWork.setLocality( value );
00314           break;
00315         case BusinessAddressRegion:
00316           addrWork.setRegion( value );
00317           break;
00318         case BusinessAddressPostalCode:
00319           addrWork.setPostalCode( value );
00320           break;
00321         case BusinessAddressCountry:
00322           addrWork.setCountry( value );
00323           break;
00324         case BusinessAddressLabel:
00325           addrWork.setLabel( value );
00326           break;
00327         default:
00328           KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory );
00329           KABC::Field::List::Iterator it;
00330 
00331           int counter = 0;
00332           for ( it = fields.begin(); it != fields.end(); ++it ) {
00333             if ( counter == (int)( posToType( item->currentItem() ) - mCustomCounter ) ) {
00334               (*it)->setValue( a, value );
00335               continue;
00336             }
00337             ++counter;
00338           }
00339           break;
00340       }
00341     }
00342 
00343     kapp->processEvents();
00344 
00345     if ( progressDialog.wasCancelled() )
00346       return KABC::AddresseeList();
00347 
00348     progressDialog.progressBar()->advance( 1 );
00349 
00350     if ( !addrHome.isEmpty() )
00351       a.insertAddress( addrHome );
00352     if ( !addrWork.isEmpty() )
00353       a.insertAddress( addrWork );
00354 
00355     if ( !emptyRow && !a.isEmpty() )
00356       contacts.append( a );
00357   }
00358 
00359   return contacts;
00360 }
00361 
00362 void CSVImportDialog::initGUI()
00363 {
00364   mPage = plainPage();
00365 
00366   QGridLayout *layout = new QGridLayout( mPage, 1, 1, marginHint(),
00367                                          spacingHint() );
00368   QHBoxLayout *hbox = new QHBoxLayout();
00369   hbox->setSpacing( spacingHint() );
00370 
00371   QLabel *label = new QLabel( i18n( "File to import:" ), mPage );
00372   hbox->addWidget( label );
00373 
00374   mUrlRequester = new KURLRequester( mPage );
00375   mUrlRequester->setFilter( "*.csv" );
00376   hbox->addWidget( mUrlRequester );
00377 
00378   layout->addMultiCellLayout( hbox, 0, 0, 0, 4 );
00379 
00380   // Delimiter: comma, semicolon, tab, space, other
00381   mDelimiterBox = new QButtonGroup( i18n( "Delimiter" ), mPage );
00382   mDelimiterBox->setColumnLayout( 0, Qt::Vertical );
00383   mDelimiterBox->layout()->setSpacing( spacingHint() );
00384   mDelimiterBox->layout()->setMargin( marginHint() );
00385   QGridLayout *delimiterLayout = new QGridLayout( mDelimiterBox->layout() );
00386   delimiterLayout->setAlignment( Qt::AlignTop );
00387   layout->addMultiCellWidget( mDelimiterBox, 1, 4, 0, 0 );
00388 
00389   mRadioComma = new QRadioButton( i18n( "Comma" ), mDelimiterBox );
00390   mRadioComma->setChecked( true );
00391   delimiterLayout->addWidget( mRadioComma, 0, 0 );
00392 
00393   mRadioSemicolon = new QRadioButton( i18n( "Semicolon" ), mDelimiterBox );
00394   delimiterLayout->addWidget( mRadioSemicolon, 0, 1 );
00395 
00396   mRadioTab = new QRadioButton( i18n( "Tabulator" ), mDelimiterBox );
00397   delimiterLayout->addWidget( mRadioTab, 1, 0 );
00398 
00399   mRadioSpace = new QRadioButton( i18n( "Space" ), mDelimiterBox );
00400   delimiterLayout->addWidget( mRadioSpace, 1, 1 );
00401 
00402   mRadioOther = new QRadioButton( i18n( "Other" ), mDelimiterBox );
00403   delimiterLayout->addWidget( mRadioOther, 0, 2 );
00404 
00405   mDelimiterEdit = new QLineEdit( mDelimiterBox );
00406   delimiterLayout->addWidget( mDelimiterEdit, 1, 2 );
00407 
00408   mComboLine = new QComboBox( false, mPage );
00409   mComboLine->insertItem( i18n( "1" ) );
00410   layout->addWidget( mComboLine, 2, 3 );
00411 
00412   mComboQuote = new QComboBox( false, mPage );
00413   mComboQuote->insertItem( i18n( "\"" ), 0 );
00414   mComboQuote->insertItem( i18n( "'" ), 1 );
00415   mComboQuote->insertItem( i18n( "None" ), 2 );
00416   layout->addWidget( mComboQuote, 2, 2 );
00417 
00418   mDatePatternEdit = new QLineEdit( mPage );
00419   mDatePatternEdit->setText( "Y-M-D" ); // ISO 8601 format as default
00420   QToolTip::add( mDatePatternEdit, i18n( "<ul><li>y: year with 2 digits</li>"
00421                                          "<li>Y: year with 4 digits</li>"
00422                                          "<li>m: month with 1 or 2 digits</li>"
00423                                          "<li>M: month with 2 digits</li>"
00424                                          "<li>d: day with 1 or 2 digits</li>"
00425                                          "<li>D: day with 2 digits</li></ul>" ) );
00426   layout->addWidget( mDatePatternEdit, 2, 4 );
00427 
00428   label = new QLabel( i18n( "Start at line:" ), mPage );
00429   layout->addWidget( label, 1, 3 );
00430 
00431   label = new QLabel( i18n( "Textquote:" ), mPage );
00432   layout->addWidget( label, 1, 2 );
00433 
00434   label = new QLabel( i18n( "Date format:" ), mPage );
00435   layout->addWidget( label, 1, 4 );
00436 
00437   mIgnoreDuplicates = new QCheckBox( mPage );
00438   mIgnoreDuplicates->setText( i18n( "Ignore duplicate delimiters" ) );
00439   layout->addMultiCellWidget( mIgnoreDuplicates, 3, 3, 2, 4 );
00440 
00441   mCodecCombo = new QComboBox( mPage );
00442   layout->addMultiCellWidget( mCodecCombo, 4, 4, 2, 4 );
00443 
00444   mTable = new QTable( 0, 0, mPage );
00445   mTable->setSelectionMode( QTable::NoSelection );
00446   mTable->horizontalHeader()->hide();
00447   layout->addMultiCellWidget( mTable, 5, 5, 0, 4 );
00448 
00449   setButtonText( User1, i18n( "Apply Template..." ) );
00450   setButtonText( User2, i18n( "Save Template..." ) );
00451 
00452   enableButtonOK( false );
00453   actionButton( User1 )->setEnabled( false );
00454   actionButton( User2 )->setEnabled( false );
00455 
00456   resize( 400, 300 );
00457 }
00458 
00459 void CSVImportDialog::fillTable()
00460 {
00461   int row, column;
00462   bool lastCharDelimiter = false;
00463   bool ignoreDups = mIgnoreDuplicates->isChecked();
00464   enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
00465          S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
00466 
00467   QChar x;
00468   QString field;
00469 
00470   // store previous assignment
00471   mTypeStore.clear();
00472   for ( column = 0; column < mTable->numCols(); ++column ) {
00473     QComboTableItem *item = static_cast<QComboTableItem*>( mTable->item( 0,
00474                                                            column ) );
00475     if ( !item || mClearTypeStore )
00476       mTypeStore.append( typeToPos( Undefined ) );
00477     else if ( item )
00478       mTypeStore.append( item->currentItem() );
00479   }
00480 
00481   clearTable();
00482 
00483   row = column = 1;
00484   mData = QString( mFileArray );
00485 
00486   QTextStream inputStream( mData, IO_ReadOnly );
00487 
00488   // find the current codec
00489   int code = mCodecCombo->currentItem();
00490   if ( code >= Codec )
00491     inputStream.setCodec( mCodecs.at( code - Codec ) );
00492   else if ( code == Uni )
00493     inputStream.setEncoding( QTextStream::Unicode );
00494   else if ( code == MSBug )
00495     inputStream.setEncoding( QTextStream::UnicodeReverse );
00496   else if ( code == Latin1 )
00497     inputStream.setEncoding( QTextStream::Latin1 );
00498   else if ( code == Guess ) {
00499     QTextCodec* codec = QTextCodec::codecForContent( mFileArray.data(), mFileArray.size() );
00500     if ( codec ) {
00501       KMessageBox::information( this, i18n( "Using codec '%1'" ).arg( codec->name() ), i18n( "Encoding" ) );
00502       inputStream.setCodec( codec );
00503     }
00504   }
00505 
00506   int maxColumn = 0;
00507   while ( !inputStream.atEnd() ) {
00508     inputStream >> x; // read one char
00509 
00510     if ( x == '\r' ) inputStream >> x; // eat '\r', to handle DOS/LOSEDOWS files correctly
00511 
00512     switch ( state ) {
00513      case S_START :
00514       if ( x == mTextQuote ) {
00515         state = S_QUOTED_FIELD;
00516       } else if ( x == mDelimiter ) {
00517         if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
00518           ++column;
00519         lastCharDelimiter = true;
00520       } else if ( x == '\n' ) {
00521         ++row;
00522         column = 1;
00523       } else {
00524         field += x;
00525         state = S_MAYBE_NORMAL_FIELD;
00526       }
00527       break;
00528      case S_QUOTED_FIELD :
00529       if ( x == mTextQuote ) {
00530         state = S_MAYBE_END_OF_QUOTED_FIELD;
00531       } else if ( x == '\n' ) {
00532         setText( row - mStartLine + 1, column, field );
00533         field = "";
00534         if ( x == '\n' ) {
00535           ++row;
00536           column = 1;
00537         } else {
00538           if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
00539             ++column;
00540           lastCharDelimiter = true;
00541         }
00542         state = S_START;
00543       } else {
00544         field += x;
00545       }
00546       break;
00547      case S_MAYBE_END_OF_QUOTED_FIELD :
00548       if ( x == mTextQuote ) {
00549         field += x;
00550         state = S_QUOTED_FIELD;
00551       } else if ( x == mDelimiter || x == '\n' ) {
00552         setText( row - mStartLine + 1, column, field );
00553         field = "";
00554         if ( x == '\n' ) {
00555           ++row;
00556           column = 1;
00557         } else {
00558           if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
00559             ++column;
00560           lastCharDelimiter = true;
00561         }
00562         state = S_START;
00563       } else {
00564         state = S_END_OF_QUOTED_FIELD;
00565       }
00566       break;
00567      case S_END_OF_QUOTED_FIELD :
00568       if ( x == mDelimiter || x == '\n' ) {
00569         setText( row - mStartLine + 1, column, field );
00570         field = "";
00571         if ( x == '\n' ) {
00572           ++row;
00573           column = 1;
00574         } else {
00575           if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
00576             ++column;
00577           lastCharDelimiter = true;
00578         }
00579         state = S_START;
00580       } else {
00581         state = S_END_OF_QUOTED_FIELD;
00582       }
00583       break;
00584      case S_MAYBE_NORMAL_FIELD :
00585       if ( x == mTextQuote ) {
00586         field = "";
00587         state = S_QUOTED_FIELD;
00588         break;
00589       }
00590      case S_NORMAL_FIELD :
00591       if ( x == mDelimiter || x == '\n' ) {
00592         setText( row - mStartLine + 1, column, field );
00593         field = "";
00594         if ( x == '\n' ) {
00595           ++row;
00596           column = 1;
00597         } else {
00598           if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
00599             ++column;
00600           lastCharDelimiter = true;
00601         }
00602         state = S_START;
00603       } else {
00604         field += x;
00605       }
00606     }
00607     if ( x != mDelimiter )
00608       lastCharDelimiter = false;
00609 
00610     if ( column > maxColumn )
00611       maxColumn = column;
00612   }
00613 
00614   // file with only one line without '\n'
00615   if ( field.length() > 0 ) {
00616     setText( row - mStartLine + 1, column, field );
00617     ++row;
00618     field = "";
00619   }
00620 
00621   adjustRows( row - mStartLine );
00622   mTable->setNumCols( maxColumn );
00623 
00624   for ( column = 0; column < mTable->numCols(); ++column ) {
00625     QComboTableItem *item = new QComboTableItem( mTable, mTypeMap.keys() );
00626     mTable->setItem( 0, column, item );
00627     if ( column < (int)mTypeStore.count() )
00628       item->setCurrentItem( mTypeStore[ column ] );
00629     else
00630       item->setCurrentItem( typeToPos( Undefined ) );
00631     mTable->adjustColumn( column );
00632   }
00633 }
00634 
00635 void CSVImportDialog::clearTable()
00636 {
00637   for ( int row = 0; row < mTable->numRows(); ++row )
00638     for ( int column = 0; column < mTable->numCols(); ++column )
00639       mTable->clearCell( row, column );
00640 }
00641 
00642 void CSVImportDialog::fillComboBox()
00643 {
00644   mComboLine->clear();
00645   for ( int row = 1; row < mTable->numRows() + 1; ++row )
00646     mComboLine->insertItem( QString::number( row ), row - 1 );
00647 }
00648 
00649 void CSVImportDialog::reloadCodecs()
00650 {
00651   mCodecCombo->clear();
00652 
00653   mCodecs.clear();
00654 
00655   QTextCodec *codec;
00656   for ( int i = 0; ( codec = QTextCodec::codecForIndex( i ) ); i++ )
00657     mCodecs.append( codec );
00658 
00659   mCodecCombo->insertItem( i18n( "Local (%1)" ).arg( QTextCodec::codecForLocale()->name() ), Local );
00660   mCodecCombo->insertItem( i18n( "[guess]" ), Guess );
00661   mCodecCombo->insertItem( i18n( "Latin1" ), Latin1 );
00662   mCodecCombo->insertItem( i18n( "Unicode" ), Uni );
00663   mCodecCombo->insertItem( i18n( "Microsoft Unicode" ), MSBug );
00664 
00665     for ( uint i = 0; i < mCodecs.count(); i++ )
00666     mCodecCombo->insertItem( mCodecs.at( i )->name(), Codec + i );
00667 }
00668 
00669 void CSVImportDialog::setText( int row, int col, const QString& text )
00670 {
00671   if ( row < 1 ) // skipped by the user
00672     return;
00673 
00674   if ( mTable->numRows() < row ) {
00675     mTable->setNumRows( row + 5000 ); // We add 5000 at a time to limit recalculations
00676     mAdjustRows = true;
00677   }
00678 
00679   if ( mTable->numCols() < col )
00680     mTable->setNumCols( col + 50 ); // We add 50 at a time to limit recalculation
00681 
00682   mTable->setText( row - 1, col - 1, text );
00683 }
00684 
00685 /*
00686  * Called after the first fillTable() when number of rows are unknown.
00687  */
00688 void CSVImportDialog::adjustRows( int rows )
00689 {
00690   if ( mAdjustRows ) {
00691     mTable->setNumRows( rows );
00692     mAdjustRows = false;
00693   }
00694 }
00695 
00696 void CSVImportDialog::returnPressed()
00697 {
00698   if ( mDelimiterBox->id( mDelimiterBox->selected() ) != 4 )
00699     return;
00700 
00701   mDelimiter = mDelimiterEdit->text();
00702   fillTable();
00703 }
00704 
00705 void CSVImportDialog::textChanged ( const QString& )
00706 {
00707   mRadioOther->setChecked ( true );
00708   delimiterClicked( 4 ); // other
00709 }
00710 
00711 void CSVImportDialog::delimiterClicked( int id )
00712 {
00713   switch ( id ) {
00714    case 0: // comma
00715     mDelimiter = ",";
00716     break;
00717    case 4: // other
00718     mDelimiter = mDelimiterEdit->text();
00719     break;
00720    case 2: // tab
00721     mDelimiter = "\t";
00722     break;
00723    case 3: // space
00724     mDelimiter = " ";
00725     break;
00726    case 1: // semicolon
00727     mDelimiter = ";";
00728     break;
00729   }
00730 
00731   fillTable();
00732 }
00733 
00734 void CSVImportDialog::textquoteSelected( const QString& mark )
00735 {
00736   if ( mComboQuote->currentItem() == 2 )
00737     mTextQuote = 0;
00738   else
00739     mTextQuote = mark[ 0 ];
00740 
00741   fillTable();
00742 }
00743 
00744 void CSVImportDialog::lineSelected( const QString& line )
00745 {
00746   mStartLine = line.toInt() - 1;
00747   fillTable();
00748 }
00749 
00750 void CSVImportDialog::slotOk()
00751 {
00752   bool assigned = false;
00753 
00754   for ( int column = 0; column < mTable->numCols(); ++column ) {
00755     QComboTableItem *item = static_cast<QComboTableItem*>( mTable->item( 0,
00756                                                            column ) );
00757     if ( item && posToType( item->currentItem() ) != Undefined )
00758       assigned = true;
00759   }
00760 
00761   if ( assigned )
00762     KDialogBase::slotOk();
00763   else
00764     KMessageBox::sorry( this, i18n( "You have to assign at least one column." ) );
00765 }
00766 
00767 void CSVImportDialog::applyTemplate()
00768 {
00769   QMap<uint,int> columnMap;
00770   QMap<QString, QString> fileMap;
00771   QStringList templates;
00772 
00773   // load all template files
00774   QStringList list = KGlobal::dirs()->findAllResources( "data" , QString( kapp->name() ) +
00775       "/csv-templates/*.desktop", true, true );
00776 
00777   for ( QStringList::iterator it = list.begin(); it != list.end(); ++it )
00778   {
00779     KSimpleConfig config( *it, true );
00780 
00781     if ( !config.hasGroup( "csv column map" ) )
00782         continue;
00783 
00784     config.setGroup( "Misc" );
00785     templates.append( config.readEntry( "Name" ) );
00786     fileMap.insert( config.readEntry( "Name" ), *it );
00787   }
00788 
00789   // let the user chose, what to take
00790   bool ok = false;
00791   QString tmp;
00792   tmp = KInputDialog::getItem( i18n( "Template Selection" ),
00793                   i18n( "Please select a template, that matches the CSV file:" ),
00794                   templates, 0, false, &ok, this );
00795 
00796   if ( !ok )
00797     return;
00798 
00799   KSimpleConfig config( fileMap[ tmp ], true );
00800   config.setGroup( "General" );
00801   mDatePatternEdit->setText( config.readEntry( "DatePattern", "Y-M-D" ) );
00802   uint numColumns = config.readUnsignedNumEntry( "Columns" );
00803   mDelimiterEdit->setText( config.readEntry( "DelimiterOther" ) );
00804   mDelimiterBox->setButton( config.readNumEntry( "DelimiterType" ) );
00805   delimiterClicked( config.readNumEntry( "DelimiterType" ) );
00806   int quoteType = config.readNumEntry( "QuoteType" );
00807   mComboQuote->setCurrentItem( quoteType );
00808   textquoteSelected( mComboQuote->currentText() );
00809 
00810   // create the column map
00811   config.setGroup( "csv column map" );
00812   for ( uint i = 0; i < numColumns; ++i ) {
00813     int col = config.readNumEntry( QString::number( i ) );
00814     columnMap.insert( i, col );
00815   }
00816 
00817   // apply the column map
00818   for ( uint column = 0; column < columnMap.count(); ++column ) {
00819     int type = columnMap[ column ];
00820     QComboTableItem *item = static_cast<QComboTableItem*>( mTable->item( 0,
00821                                                            column ) );
00822     if ( item )
00823       item->setCurrentItem( typeToPos( type ) );
00824   }
00825 }
00826 
00827 void CSVImportDialog::saveTemplate()
00828 {
00829   QString fileName = KFileDialog::getSaveFileName(
00830                      locateLocal( "data", QString( kapp->name() ) + "/csv-templates/" ),
00831                      "*.desktop", this );
00832 
00833   if ( fileName.isEmpty() )
00834     return;
00835 
00836   if ( !fileName.contains( ".desktop" ) )
00837     fileName += ".desktop";
00838 
00839   QString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) );
00840 
00841   if ( name.isEmpty() )
00842     return;
00843 
00844   KConfig config( fileName );
00845   config.setGroup( "General" );
00846   config.writeEntry( "DatePattern", mDatePatternEdit->text() );
00847   config.writeEntry( "Columns", mTable->numCols() );
00848   config.writeEntry( "DelimiterType", mDelimiterBox->id( mDelimiterBox->selected() ) );
00849   config.writeEntry( "DelimiterOther", mDelimiterEdit->text() );
00850   config.writeEntry( "QuoteType", mComboQuote->currentItem() );
00851 
00852   config.setGroup( "Misc" );
00853   config.writeEntry( "Name", name );
00854 
00855   config.setGroup( "csv column map" );
00856 
00857   for ( int column = 0; column < mTable->numCols(); ++column ) {
00858     QComboTableItem *item = static_cast<QComboTableItem*>( mTable->item( 0,
00859                                                            column ) );
00860     if ( item )
00861       config.writeEntry( QString::number( column ), posToType(
00862                          item->currentItem() ) );
00863     else
00864       config.writeEntry( QString::number( column ), 0 );
00865   }
00866 
00867   config.sync();
00868 }
00869 
00870 QString CSVImportDialog::getText( int row, int col )
00871 {
00872   return mTable->text( row, col );
00873 }
00874 
00875 uint CSVImportDialog::posToType( int pos ) const
00876 {
00877   uint counter = 0;
00878   QMap<QString, uint>::ConstIterator it;
00879   for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
00880     if ( counter == (uint)pos )
00881       return it.data();
00882 
00883   return 0;
00884 }
00885 
00886 int CSVImportDialog::typeToPos( uint type ) const
00887 {
00888   uint counter = 0;
00889   QMap<QString, uint>::ConstIterator it;
00890   for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
00891     if ( it.data() == type )
00892       return counter;
00893 
00894   return -1;
00895 }
00896 
00897 void CSVImportDialog::ignoreDuplicatesChanged( int )
00898 {
00899   fillTable();
00900 }
00901 
00902 void CSVImportDialog::setFile( const QString &fileName )
00903 {
00904   if ( fileName.isEmpty() )
00905     return;
00906 
00907   QFile file( fileName );
00908   if ( !file.open( IO_ReadOnly ) ) {
00909     KMessageBox::sorry( this, i18n( "Cannot open input file." ) );
00910     file.close();
00911     return;
00912   }
00913 
00914   mFileArray = file.readAll();
00915   file.close();
00916 
00917   mClearTypeStore = true;
00918   clearTable();
00919   mTable->setNumCols( 0 );
00920   mTable->setNumRows( 0 );
00921   fillTable();
00922   mClearTypeStore = false;
00923 
00924   fillComboBox();
00925 }
00926 
00927 void CSVImportDialog::urlChanged( const QString &file )
00928 {
00929   bool state = !file.isEmpty();
00930 
00931   enableButtonOK( state );
00932   actionButton( User1 )->setEnabled( state );
00933   actionButton( User2 )->setEnabled( state );
00934 }
00935 
00936 void CSVImportDialog::codecChanged()
00937 {
00938   fillTable();
00939 }
00940 
00941 #include <csvimportdialog.moc>
KDE Logo
This file is part of the documentation for kaddressbook Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 4 14:41:39 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003