You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
967 lines
30 KiB
967 lines
30 KiB
/*
|
|
This file is part of KAddressBook.
|
|
Copyright (C) 2003 Tobias Koenig <tokoe@kde.org>
|
|
based on the code of KSpread's CSV Import Dialog
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include <tqbuttongroup.h>
|
|
#include <tqcheckbox.h>
|
|
#include <tqcombobox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqlayout.h>
|
|
#include <tqlineedit.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqradiobutton.h>
|
|
#include <tqtable.h>
|
|
#include <tqtextcodec.h>
|
|
#include <tqtooltip.h>
|
|
|
|
#include <kapplication.h>
|
|
#include <kdebug.h>
|
|
#include <kdialogbase.h>
|
|
#include <kfiledialog.h>
|
|
#include <klineedit.h>
|
|
#include <klocale.h>
|
|
#include <kinputdialog.h>
|
|
#include <kmessagebox.h>
|
|
#include <kprogress.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kurlrequester.h>
|
|
|
|
#include "dateparser.h"
|
|
|
|
#include "csvimportdialog.h"
|
|
|
|
enum { Local = 0, Guess = 1, Latin1 = 2, Uni = 3, MSBug = 4, Codec = 5 };
|
|
|
|
CSVImportDialog::CSVImportDialog( KABC::AddressBook *ab, TQWidget *parent,
|
|
const char * name )
|
|
: KDialogBase( Plain, i18n ( "CSV Import Dialog" ), Ok | Cancel | User1 |
|
|
User2, Ok, parent, name, true, true ),
|
|
mAdjustRows( false ),
|
|
mStartLine( 0 ),
|
|
mTextQuote( '"' ),
|
|
mDelimiter( "," ),
|
|
mAddressBook( ab )
|
|
{
|
|
initGUI();
|
|
|
|
mTypeMap.insert( i18n( "Undefined" ), Undefined );
|
|
mTypeMap.insert( KABC::Addressee::formattedNameLabel(), FormattedName );
|
|
mTypeMap.insert( KABC::Addressee::familyNameLabel(), FamilyName );
|
|
mTypeMap.insert( KABC::Addressee::givenNameLabel(), GivenName );
|
|
mTypeMap.insert( KABC::Addressee::additionalNameLabel(), AdditionalName );
|
|
mTypeMap.insert( KABC::Addressee::prefixLabel(), Prefix );
|
|
mTypeMap.insert( KABC::Addressee::suffixLabel(), Suffix );
|
|
mTypeMap.insert( KABC::Addressee::nickNameLabel(), NickName );
|
|
mTypeMap.insert( KABC::Addressee::birthdayLabel(), Birthday );
|
|
|
|
mTypeMap.insert( KABC::Addressee::homeAddressStreetLabel(), HomeAddressStreet );
|
|
mTypeMap.insert( KABC::Addressee::homeAddressLocalityLabel(),
|
|
HomeAddressLocality );
|
|
mTypeMap.insert( KABC::Addressee::homeAddressRegionLabel(), HomeAddressRegion );
|
|
mTypeMap.insert( KABC::Addressee::homeAddressPostalCodeLabel(),
|
|
HomeAddressPostalCode );
|
|
mTypeMap.insert( KABC::Addressee::homeAddressCountryLabel(),
|
|
HomeAddressCountry );
|
|
mTypeMap.insert( KABC::Addressee::homeAddressLabelLabel(), HomeAddressLabel );
|
|
|
|
mTypeMap.insert( KABC::Addressee::businessAddressStreetLabel(),
|
|
BusinessAddressStreet );
|
|
mTypeMap.insert( KABC::Addressee::businessAddressLocalityLabel(),
|
|
BusinessAddressLocality );
|
|
mTypeMap.insert( KABC::Addressee::businessAddressRegionLabel(),
|
|
BusinessAddressRegion );
|
|
mTypeMap.insert( KABC::Addressee::businessAddressPostalCodeLabel(),
|
|
BusinessAddressPostalCode );
|
|
mTypeMap.insert( KABC::Addressee::businessAddressCountryLabel(),
|
|
BusinessAddressCountry );
|
|
mTypeMap.insert( KABC::Addressee::businessAddressLabelLabel(),
|
|
BusinessAddressLabel );
|
|
|
|
mTypeMap.insert( KABC::Addressee::homePhoneLabel(), HomePhone );
|
|
mTypeMap.insert( KABC::Addressee::businessPhoneLabel(), BusinessPhone );
|
|
mTypeMap.insert( KABC::Addressee::mobilePhoneLabel(), MobilePhone );
|
|
mTypeMap.insert( KABC::Addressee::homeFaxLabel(), HomeFax );
|
|
mTypeMap.insert( KABC::Addressee::businessFaxLabel(), BusinessFax );
|
|
mTypeMap.insert( KABC::Addressee::carPhoneLabel(), CarPhone );
|
|
mTypeMap.insert( KABC::Addressee::isdnLabel(), Isdn );
|
|
mTypeMap.insert( KABC::Addressee::pagerLabel(), Pager );
|
|
mTypeMap.insert( KABC::Addressee::emailLabel(), Email );
|
|
mTypeMap.insert( KABC::Addressee::mailerLabel(), Mailer );
|
|
mTypeMap.insert( KABC::Addressee::titleLabel(), Title );
|
|
mTypeMap.insert( KABC::Addressee::roleLabel(), Role );
|
|
mTypeMap.insert( KABC::Addressee::organizationLabel(), Organization );
|
|
mTypeMap.insert( KABC::Addressee::noteLabel(), Note );
|
|
mTypeMap.insert( KABC::Addressee::urlLabel(), URL );
|
|
|
|
mCustomCounter = mTypeMap.count();
|
|
int count = mCustomCounter;
|
|
|
|
KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory );
|
|
KABC::Field::List::Iterator it;
|
|
for ( it = fields.begin(); it != fields.end(); ++it, ++count )
|
|
mTypeMap.insert( (*it)->label(), count );
|
|
|
|
reloadCodecs();
|
|
|
|
connect( mDelimiterBox, TQT_SIGNAL( clicked( int ) ),
|
|
this, TQT_SLOT( delimiterClicked( int ) ) );
|
|
connect( mDelimiterEdit, TQT_SIGNAL( returnPressed() ),
|
|
this, TQT_SLOT( returnPressed() ) );
|
|
connect( mDelimiterEdit, TQT_SIGNAL( textChanged ( const TQString& ) ),
|
|
this, TQT_SLOT( textChanged ( const TQString& ) ) );
|
|
connect( mComboLine, TQT_SIGNAL( activated( const TQString& ) ),
|
|
this, TQT_SLOT( lineSelected( const TQString& ) ) );
|
|
connect( mComboQuote, TQT_SIGNAL( activated( const TQString& ) ),
|
|
this, TQT_SLOT( textquoteSelected( const TQString& ) ) );
|
|
connect( mIgnoreDuplicates, TQT_SIGNAL( stateChanged( int ) ),
|
|
this, TQT_SLOT( ignoreDuplicatesChanged( int ) ) );
|
|
connect( mCodecCombo, TQT_SIGNAL( activated( const TQString& ) ),
|
|
this, TQT_SLOT( codecChanged() ) );
|
|
|
|
connect( mUrlRequester, TQT_SIGNAL( returnPressed( const TQString& ) ),
|
|
this, TQT_SLOT( setFile( const TQString& ) ) );
|
|
connect( mUrlRequester, TQT_SIGNAL( urlSelected( const TQString& ) ),
|
|
this, TQT_SLOT( setFile( const TQString& ) ) );
|
|
connect( mUrlRequester->lineEdit(), TQT_SIGNAL( textChanged ( const TQString& ) ),
|
|
this, TQT_SLOT( urlChanged( const TQString& ) ) );
|
|
|
|
connect( this, TQT_SIGNAL( user1Clicked() ),
|
|
this, TQT_SLOT( applyTemplate() ) );
|
|
|
|
connect( this, TQT_SIGNAL( user2Clicked() ),
|
|
this, TQT_SLOT( saveTemplate() ) );
|
|
}
|
|
|
|
CSVImportDialog::~CSVImportDialog()
|
|
{
|
|
mCodecs.clear();
|
|
}
|
|
|
|
KABC::AddresseeList CSVImportDialog::contacts() const
|
|
{
|
|
DateParser dateParser( mDatePatternEdit->text() );
|
|
KABC::AddresseeList contacts;
|
|
|
|
KProgressDialog progressDialog( mPage );
|
|
progressDialog.setAutoClose( true );
|
|
progressDialog.progressBar()->setTotalSteps( mTable->numRows() );
|
|
progressDialog.setLabel( i18n( "Importing contacts" ) );
|
|
progressDialog.show();
|
|
|
|
kapp->processEvents();
|
|
|
|
for ( int row = 1; row < mTable->numRows(); ++row ) {
|
|
KABC::Addressee a;
|
|
bool emptyRow = true;
|
|
KABC::Address addrHome( KABC::Address::Home );
|
|
KABC::Address addrWork( KABC::Address::Work );
|
|
for ( int col = 0; col < mTable->numCols(); ++col ) {
|
|
TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
|
|
col ) );
|
|
if ( !item ) {
|
|
kdError() << "ERROR: item cast failed" << endl;
|
|
continue;
|
|
}
|
|
|
|
TQString value = mTable->text( row, col );
|
|
if ( 1 == row && static_cast<TQTableItem *>(item)->text() == value )
|
|
// we are looking at a header row, stop now
|
|
break;
|
|
|
|
if ( !value.isEmpty() )
|
|
emptyRow = false;
|
|
|
|
switch ( posToType( item->currentItem() ) ) {
|
|
case Undefined:
|
|
continue;
|
|
break;
|
|
case FormattedName:
|
|
a.setFormattedName( value );
|
|
break;
|
|
case GivenName:
|
|
a.setGivenName( value );
|
|
break;
|
|
case FamilyName:
|
|
a.setFamilyName( value );
|
|
break;
|
|
case AdditionalName:
|
|
a.setAdditionalName( value );
|
|
break;
|
|
case Prefix:
|
|
a.setPrefix( value );
|
|
break;
|
|
case Suffix:
|
|
a.setSuffix( value );
|
|
break;
|
|
case NickName:
|
|
a.setNickName( value );
|
|
break;
|
|
case Birthday:
|
|
a.setBirthday( dateParser.parse( value ) );
|
|
break;
|
|
case Email:
|
|
if ( !value.isEmpty() )
|
|
a.insertEmail( value, true );
|
|
break;
|
|
case Role:
|
|
a.setRole( value );
|
|
break;
|
|
case Title:
|
|
a.setTitle( value );
|
|
break;
|
|
case Mailer:
|
|
a.setMailer( value );
|
|
break;
|
|
case URL:
|
|
a.setUrl( KURL( value ) );
|
|
break;
|
|
case Organization:
|
|
a.setOrganization( value );
|
|
break;
|
|
case Note:
|
|
a.setNote( a.note() + value + "\n" );
|
|
break;
|
|
|
|
case HomePhone:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Home );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case BusinessPhone:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Work );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case MobilePhone:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Cell );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case HomeFax:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Home |
|
|
KABC::PhoneNumber::Fax );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case BusinessFax:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Work |
|
|
KABC::PhoneNumber::Fax );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case CarPhone:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Car );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case Isdn:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Isdn );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
case Pager:
|
|
if ( !value.isEmpty() ) {
|
|
KABC::PhoneNumber number( value, KABC::PhoneNumber::Pager );
|
|
a.insertPhoneNumber( number );
|
|
}
|
|
break;
|
|
|
|
case HomeAddressStreet:
|
|
addrHome.setStreet( value );
|
|
break;
|
|
case HomeAddressLocality:
|
|
addrHome.setLocality( value );
|
|
break;
|
|
case HomeAddressRegion:
|
|
addrHome.setRegion( value );
|
|
break;
|
|
case HomeAddressPostalCode:
|
|
addrHome.setPostalCode( value );
|
|
break;
|
|
case HomeAddressCountry:
|
|
addrHome.setCountry( value );
|
|
break;
|
|
case HomeAddressLabel:
|
|
addrHome.setLabel( value );
|
|
break;
|
|
|
|
case BusinessAddressStreet:
|
|
addrWork.setStreet( value );
|
|
break;
|
|
case BusinessAddressLocality:
|
|
addrWork.setLocality( value );
|
|
break;
|
|
case BusinessAddressRegion:
|
|
addrWork.setRegion( value );
|
|
break;
|
|
case BusinessAddressPostalCode:
|
|
addrWork.setPostalCode( value );
|
|
break;
|
|
case BusinessAddressCountry:
|
|
addrWork.setCountry( value );
|
|
break;
|
|
case BusinessAddressLabel:
|
|
addrWork.setLabel( value );
|
|
break;
|
|
default:
|
|
KABC::Field::List fields = mAddressBook->fields( KABC::Field::CustomCategory );
|
|
KABC::Field::List::Iterator it;
|
|
|
|
int counter = 0;
|
|
for ( it = fields.begin(); it != fields.end(); ++it ) {
|
|
if ( counter == (int)( posToType( item->currentItem() ) - mCustomCounter ) ) {
|
|
(*it)->setValue( a, value );
|
|
break;
|
|
}
|
|
++counter;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
kapp->processEvents();
|
|
|
|
if ( progressDialog.wasCancelled() )
|
|
return KABC::AddresseeList();
|
|
|
|
progressDialog.progressBar()->advance( 1 );
|
|
|
|
if ( !addrHome.isEmpty() )
|
|
a.insertAddress( addrHome );
|
|
if ( !addrWork.isEmpty() )
|
|
a.insertAddress( addrWork );
|
|
|
|
if ( !emptyRow && !a.isEmpty() )
|
|
contacts.append( a );
|
|
}
|
|
|
|
return contacts;
|
|
}
|
|
|
|
void CSVImportDialog::initGUI()
|
|
{
|
|
mPage = plainPage();
|
|
|
|
TQGridLayout *tqlayout = new TQGridLayout( mPage, 1, 1, marginHint(),
|
|
spacingHint() );
|
|
TQHBoxLayout *hbox = new TQHBoxLayout();
|
|
hbox->setSpacing( spacingHint() );
|
|
|
|
TQLabel *label = new TQLabel( i18n( "File to import:" ), mPage );
|
|
hbox->addWidget( label );
|
|
|
|
mUrlRequester = new KURLRequester( mPage );
|
|
mUrlRequester->setFilter( "*.csv" );
|
|
hbox->addWidget( mUrlRequester );
|
|
|
|
tqlayout->addMultiCellLayout( hbox, 0, 0, 0, 4 );
|
|
|
|
// Delimiter: comma, semicolon, tab, space, other
|
|
mDelimiterBox = new TQButtonGroup( i18n( "Delimiter" ), mPage );
|
|
mDelimiterBox->setColumnLayout( 0, Qt::Vertical );
|
|
mDelimiterBox->tqlayout()->setSpacing( spacingHint() );
|
|
mDelimiterBox->tqlayout()->setMargin( marginHint() );
|
|
TQGridLayout *delimiterLayout = new TQGridLayout( mDelimiterBox->tqlayout() );
|
|
delimiterLayout->tqsetAlignment( Qt::AlignTop );
|
|
tqlayout->addMultiCellWidget( mDelimiterBox, 1, 4, 0, 0 );
|
|
|
|
mRadioComma = new TQRadioButton( i18n( "Comma" ), mDelimiterBox );
|
|
mRadioComma->setChecked( true );
|
|
delimiterLayout->addWidget( mRadioComma, 0, 0 );
|
|
|
|
mRadioSemicolon = new TQRadioButton( i18n( "Semicolon" ), mDelimiterBox );
|
|
delimiterLayout->addWidget( mRadioSemicolon, 0, 1 );
|
|
|
|
mRadioTab = new TQRadioButton( i18n( "Tabulator" ), mDelimiterBox );
|
|
delimiterLayout->addWidget( mRadioTab, 1, 0 );
|
|
|
|
mRadioSpace = new TQRadioButton( i18n( "Space" ), mDelimiterBox );
|
|
delimiterLayout->addWidget( mRadioSpace, 1, 1 );
|
|
|
|
mRadioOther = new TQRadioButton( i18n( "Other" ), mDelimiterBox );
|
|
delimiterLayout->addWidget( mRadioOther, 0, 2 );
|
|
|
|
mDelimiterEdit = new TQLineEdit( mDelimiterBox );
|
|
delimiterLayout->addWidget( mDelimiterEdit, 1, 2 );
|
|
|
|
mComboLine = new TQComboBox( false, mPage );
|
|
mComboLine->insertItem( i18n( "1" ) );
|
|
tqlayout->addWidget( mComboLine, 2, 3 );
|
|
|
|
mComboQuote = new TQComboBox( false, mPage );
|
|
mComboQuote->insertItem( i18n( "\"" ), 0 );
|
|
mComboQuote->insertItem( i18n( "'" ), 1 );
|
|
mComboQuote->insertItem( i18n( "None" ), 2 );
|
|
tqlayout->addWidget( mComboQuote, 2, 2 );
|
|
|
|
mDatePatternEdit = new TQLineEdit( mPage );
|
|
mDatePatternEdit->setText( "Y-M-D" ); // ISO 8601 format as default
|
|
TQToolTip::add( mDatePatternEdit, i18n( "<ul><li>y: year with 2 digits</li>"
|
|
"<li>Y: year with 4 digits</li>"
|
|
"<li>m: month with 1 or 2 digits</li>"
|
|
"<li>M: month with 2 digits</li>"
|
|
"<li>d: day with 1 or 2 digits</li>"
|
|
"<li>D: day with 2 digits</li></ul>" ) );
|
|
tqlayout->addWidget( mDatePatternEdit, 2, 4 );
|
|
|
|
label = new TQLabel( i18n( "Start at line:" ), mPage );
|
|
tqlayout->addWidget( label, 1, 3 );
|
|
|
|
label = new TQLabel( i18n( "Textquote:" ), mPage );
|
|
tqlayout->addWidget( label, 1, 2 );
|
|
|
|
label = new TQLabel( i18n( "Date format:" ), mPage );
|
|
tqlayout->addWidget( label, 1, 4 );
|
|
|
|
mIgnoreDuplicates = new TQCheckBox( mPage );
|
|
mIgnoreDuplicates->setText( i18n( "Ignore duplicate delimiters" ) );
|
|
tqlayout->addMultiCellWidget( mIgnoreDuplicates, 3, 3, 2, 4 );
|
|
|
|
mCodecCombo = new TQComboBox( mPage );
|
|
tqlayout->addMultiCellWidget( mCodecCombo, 4, 4, 2, 4 );
|
|
|
|
mTable = new TQTable( 0, 0, mPage );
|
|
mTable->setSelectionMode( TQTable::NoSelection );
|
|
mTable->horizontalHeader()->hide();
|
|
tqlayout->addMultiCellWidget( mTable, 5, 5, 0, 4 );
|
|
|
|
setButtonText( User1, i18n( "Apply Template..." ) );
|
|
setButtonText( User2, i18n( "Save Template..." ) );
|
|
|
|
enableButtonOK( false );
|
|
actionButton( User1 )->setEnabled( false );
|
|
actionButton( User2 )->setEnabled( false );
|
|
|
|
resize( 400, 300 );
|
|
}
|
|
|
|
void CSVImportDialog::fillTable()
|
|
{
|
|
int row, column;
|
|
bool lastCharDelimiter = false;
|
|
bool ignoreDups = mIgnoreDuplicates->isChecked();
|
|
enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
|
|
S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
|
|
|
|
TQChar x;
|
|
TQString field;
|
|
|
|
// store previous assignment
|
|
mTypeStore.clear();
|
|
for ( column = 0; column < mTable->numCols(); ++column ) {
|
|
TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
|
|
column ) );
|
|
if ( !item || mClearTypeStore )
|
|
mTypeStore.append( typeToPos( Undefined ) );
|
|
else if ( item )
|
|
mTypeStore.append( item->currentItem() );
|
|
}
|
|
|
|
clearTable();
|
|
|
|
row = column = 1;
|
|
|
|
TQTextStream inputStream( mFileArray, IO_ReadOnly );
|
|
|
|
// find the current codec
|
|
int code = mCodecCombo->currentItem();
|
|
if ( code == Local )
|
|
inputStream.setEncoding( TQTextStream::Locale );
|
|
else if ( code >= Codec )
|
|
inputStream.setCodec( mCodecs.at( code - Codec ) );
|
|
else if ( code == Uni )
|
|
inputStream.setEncoding( TQTextStream::Unicode );
|
|
else if ( code == MSBug )
|
|
inputStream.setEncoding( TQTextStream::UnicodeReverse );
|
|
else if ( code == Latin1 )
|
|
inputStream.setEncoding( TQTextStream::Latin1 );
|
|
else if ( code == Guess ) {
|
|
TQTextCodec* codec = TQTextCodec::codecForContent( mFileArray.data(), mFileArray.size() );
|
|
if ( codec ) {
|
|
KMessageBox::information( this, i18n( "Using codec '%1'" ).arg( codec->name() ), i18n( "Encoding" ) );
|
|
inputStream.setCodec( codec );
|
|
}
|
|
}
|
|
|
|
int maxColumn = 0;
|
|
while ( !inputStream.atEnd() ) {
|
|
inputStream >> x; // read one char
|
|
|
|
if ( x == '\r' ) inputStream >> x; // eat '\r', to handle DOS/LOSEDOWS files correctly
|
|
|
|
switch ( state ) {
|
|
case S_START :
|
|
if ( x == mTextQuote ) {
|
|
state = S_QUOTED_FIELD;
|
|
} else if ( x == mDelimiter ) {
|
|
if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
|
|
++column;
|
|
lastCharDelimiter = true;
|
|
} else if ( x == '\n' ) {
|
|
++row;
|
|
column = 1;
|
|
} else {
|
|
field += x;
|
|
state = S_MAYBE_NORMAL_FIELD;
|
|
}
|
|
break;
|
|
case S_QUOTED_FIELD :
|
|
if ( x == mTextQuote ) {
|
|
state = S_MAYBE_END_OF_QUOTED_FIELD;
|
|
} else if ( x == '\n' && mTextQuote.isNull() ) {
|
|
setText( row - mStartLine + 1, column, field );
|
|
field = "";
|
|
if ( x == '\n' ) {
|
|
++row;
|
|
column = 1;
|
|
} else {
|
|
if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
|
|
++column;
|
|
lastCharDelimiter = true;
|
|
}
|
|
state = S_START;
|
|
} else {
|
|
field += x;
|
|
}
|
|
break;
|
|
case S_MAYBE_END_OF_QUOTED_FIELD :
|
|
if ( x == mTextQuote ) {
|
|
field += x;
|
|
state = S_QUOTED_FIELD;
|
|
} else if ( x == mDelimiter || x == '\n' ) {
|
|
setText( row - mStartLine + 1, column, field );
|
|
field = "";
|
|
if ( x == '\n' ) {
|
|
++row;
|
|
column = 1;
|
|
} else {
|
|
if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
|
|
++column;
|
|
lastCharDelimiter = true;
|
|
}
|
|
state = S_START;
|
|
} else {
|
|
state = S_END_OF_QUOTED_FIELD;
|
|
}
|
|
break;
|
|
case S_END_OF_QUOTED_FIELD :
|
|
if ( x == mDelimiter || x == '\n' ) {
|
|
setText( row - mStartLine + 1, column, field );
|
|
field = "";
|
|
if ( x == '\n' ) {
|
|
++row;
|
|
column = 1;
|
|
} else {
|
|
if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
|
|
++column;
|
|
lastCharDelimiter = true;
|
|
}
|
|
state = S_START;
|
|
} else {
|
|
state = S_END_OF_QUOTED_FIELD;
|
|
}
|
|
break;
|
|
case S_MAYBE_NORMAL_FIELD :
|
|
if ( x == mTextQuote ) {
|
|
field = "";
|
|
state = S_QUOTED_FIELD;
|
|
break;
|
|
}
|
|
case S_NORMAL_FIELD :
|
|
if ( x == mDelimiter || x == '\n' ) {
|
|
setText( row - mStartLine + 1, column, field );
|
|
field = "";
|
|
if ( x == '\n' ) {
|
|
++row;
|
|
column = 1;
|
|
} else {
|
|
if ( ( ignoreDups == false ) || ( lastCharDelimiter == false ) )
|
|
++column;
|
|
lastCharDelimiter = true;
|
|
}
|
|
state = S_START;
|
|
} else {
|
|
field += x;
|
|
}
|
|
}
|
|
if ( x != mDelimiter )
|
|
lastCharDelimiter = false;
|
|
|
|
if ( column > maxColumn )
|
|
maxColumn = column;
|
|
}
|
|
|
|
// file with only one line without '\n'
|
|
if ( field.length() > 0 ) {
|
|
setText( row - mStartLine + 1, column, field );
|
|
++row;
|
|
field = "";
|
|
}
|
|
|
|
adjustRows( row - mStartLine );
|
|
mTable->setNumCols( maxColumn );
|
|
|
|
for ( column = 0; column < mTable->numCols(); ++column ) {
|
|
TQComboTableItem *item = new TQComboTableItem( mTable, mTypeMap.keys() );
|
|
mTable->setItem( 0, column, item );
|
|
if ( column < (int)mTypeStore.count() )
|
|
item->setCurrentItem( mTypeStore[ column ] );
|
|
else
|
|
item->setCurrentItem( typeToPos( Undefined ) );
|
|
mTable->adjustColumn( column );
|
|
}
|
|
|
|
resizeColumns();
|
|
}
|
|
|
|
void CSVImportDialog::clearTable()
|
|
{
|
|
for ( int row = 0; row < mTable->numRows(); ++row )
|
|
for ( int column = 0; column < mTable->numCols(); ++column )
|
|
mTable->clearCell( row, column );
|
|
}
|
|
|
|
void CSVImportDialog::fillComboBox()
|
|
{
|
|
mComboLine->clear();
|
|
for ( int row = 1; row < mTable->numRows() + 1; ++row )
|
|
mComboLine->insertItem( TQString::number( row ), row - 1 );
|
|
}
|
|
|
|
void CSVImportDialog::reloadCodecs()
|
|
{
|
|
mCodecCombo->clear();
|
|
|
|
mCodecs.clear();
|
|
|
|
TQTextCodec *codec;
|
|
for ( int i = 0; ( codec = TQTextCodec::codecForIndex( i ) ); i++ )
|
|
mCodecs.append( codec );
|
|
|
|
mCodecCombo->insertItem( i18n( "Local (%1)" ).arg( TQTextCodec::codecForLocale()->name() ), Local );
|
|
mCodecCombo->insertItem( i18n( "[guess]" ), Guess );
|
|
mCodecCombo->insertItem( i18n( "Latin1" ), Latin1 );
|
|
mCodecCombo->insertItem( i18n( "Unicode" ), Uni );
|
|
mCodecCombo->insertItem( i18n( "Microsoft Unicode" ), MSBug );
|
|
|
|
for ( uint i = 0; i < mCodecs.count(); i++ )
|
|
mCodecCombo->insertItem( mCodecs.at( i )->name(), Codec + i );
|
|
}
|
|
|
|
void CSVImportDialog::setText( int row, int col, const TQString& text )
|
|
{
|
|
if ( row < 1 ) // skipped by the user
|
|
return;
|
|
|
|
if ( mTable->numRows() < row ) {
|
|
mTable->setNumRows( row + 5000 ); // We add 5000 at a time to limit recalculations
|
|
mAdjustRows = true;
|
|
}
|
|
|
|
if ( mTable->numCols() < col )
|
|
mTable->setNumCols( col + 50 ); // We add 50 at a time to limit recalculation
|
|
|
|
mTable->setText( row - 1, col - 1, text );
|
|
}
|
|
|
|
/*
|
|
* Called after the first fillTable() when number of rows are unknown.
|
|
*/
|
|
void CSVImportDialog::adjustRows( int rows )
|
|
{
|
|
if ( mAdjustRows ) {
|
|
mTable->setNumRows( rows );
|
|
mAdjustRows = false;
|
|
}
|
|
}
|
|
|
|
void CSVImportDialog::resizeColumns()
|
|
{
|
|
TQFontMetrics fm = fontMetrics();
|
|
int width = 0;
|
|
|
|
TQMap<TQString, uint>::ConstIterator it;
|
|
for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it ) {
|
|
width = QMAX( width, fm.width( it.key() ) );
|
|
}
|
|
|
|
for ( int i = 0; i < mTable->numCols(); ++i )
|
|
mTable->setColumnWidth( i, QMAX( width + 15, mTable->columnWidth( i ) ) );
|
|
}
|
|
|
|
void CSVImportDialog::returnPressed()
|
|
{
|
|
if ( mDelimiterBox->id( mDelimiterBox->selected() ) != 4 )
|
|
return;
|
|
|
|
mDelimiter = mDelimiterEdit->text();
|
|
fillTable();
|
|
}
|
|
|
|
void CSVImportDialog::textChanged ( const TQString& )
|
|
{
|
|
mRadioOther->setChecked ( true );
|
|
delimiterClicked( 4 ); // other
|
|
}
|
|
|
|
void CSVImportDialog::delimiterClicked( int id )
|
|
{
|
|
switch ( id ) {
|
|
case 0: // comma
|
|
mDelimiter = ",";
|
|
break;
|
|
case 4: // other
|
|
mDelimiter = mDelimiterEdit->text();
|
|
break;
|
|
case 2: // tab
|
|
mDelimiter = "\t";
|
|
break;
|
|
case 3: // space
|
|
mDelimiter = " ";
|
|
break;
|
|
case 1: // semicolon
|
|
mDelimiter = ";";
|
|
break;
|
|
}
|
|
|
|
fillTable();
|
|
}
|
|
|
|
void CSVImportDialog::textquoteSelected( const TQString& mark )
|
|
{
|
|
if ( mComboQuote->currentItem() == 2 )
|
|
mTextQuote = 0;
|
|
else
|
|
mTextQuote = mark[ 0 ];
|
|
|
|
fillTable();
|
|
}
|
|
|
|
void CSVImportDialog::lineSelected( const TQString& line )
|
|
{
|
|
mStartLine = line.toInt() - 1;
|
|
fillTable();
|
|
}
|
|
|
|
void CSVImportDialog::slotOk()
|
|
{
|
|
bool assigned = false;
|
|
|
|
for ( int column = 0; column < mTable->numCols(); ++column ) {
|
|
TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
|
|
column ) );
|
|
if ( item && posToType( item->currentItem() ) != Undefined )
|
|
assigned = true;
|
|
}
|
|
|
|
if ( assigned )
|
|
KDialogBase::slotOk();
|
|
else
|
|
KMessageBox::sorry( this, i18n( "You have to assign at least one column." ) );
|
|
}
|
|
|
|
void CSVImportDialog::applyTemplate()
|
|
{
|
|
TQMap<uint,int> columnMap;
|
|
TQMap<TQString, TQString> fileMap;
|
|
TQStringList templates;
|
|
|
|
// load all template files
|
|
TQStringList list = KGlobal::dirs()->findAllResources( "data" , TQString( kapp->name() ) +
|
|
"/csv-templates/*.desktop", true, true );
|
|
|
|
for ( TQStringList::iterator it = list.begin(); it != list.end(); ++it )
|
|
{
|
|
KSimpleConfig config( *it, true );
|
|
|
|
if ( !config.hasGroup( "csv column map" ) )
|
|
continue;
|
|
|
|
config.setGroup( "Misc" );
|
|
templates.append( config.readEntry( "Name" ) );
|
|
fileMap.insert( config.readEntry( "Name" ), *it );
|
|
}
|
|
|
|
// let the user chose, what to take
|
|
bool ok = false;
|
|
TQString tmp;
|
|
tmp = KInputDialog::getItem( i18n( "Template Selection" ),
|
|
i18n( "Please select a template, that matches the CSV file:" ),
|
|
templates, 0, false, &ok, this );
|
|
|
|
if ( !ok )
|
|
return;
|
|
|
|
KSimpleConfig config( fileMap[ tmp ], true );
|
|
config.setGroup( "General" );
|
|
mDatePatternEdit->setText( config.readEntry( "DatePattern", "Y-M-D" ) );
|
|
uint numColumns = config.readUnsignedNumEntry( "Columns" );
|
|
mDelimiterEdit->setText( config.readEntry( "DelimiterOther" ) );
|
|
mDelimiterBox->setButton( config.readNumEntry( "DelimiterType" ) );
|
|
delimiterClicked( config.readNumEntry( "DelimiterType" ) );
|
|
int quoteType = config.readNumEntry( "QuoteType" );
|
|
mComboQuote->setCurrentItem( quoteType );
|
|
textquoteSelected( mComboQuote->currentText() );
|
|
|
|
// create the column map
|
|
config.setGroup( "csv column map" );
|
|
for ( uint i = 0; i < numColumns; ++i ) {
|
|
int col = config.readNumEntry( TQString::number( i ) );
|
|
columnMap.insert( i, col );
|
|
}
|
|
|
|
// apply the column map
|
|
for ( uint column = 0; column < columnMap.count(); ++column ) {
|
|
int type = columnMap[ column ];
|
|
TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
|
|
column ) );
|
|
if ( item )
|
|
item->setCurrentItem( typeToPos( type ) );
|
|
}
|
|
}
|
|
|
|
void CSVImportDialog::saveTemplate()
|
|
{
|
|
TQString fileName = KFileDialog::getSaveFileName(
|
|
locateLocal( "data", TQString( kapp->name() ) + "/csv-templates/" ),
|
|
"*.desktop", this );
|
|
|
|
if ( fileName.isEmpty() )
|
|
return;
|
|
|
|
if ( !fileName.tqcontains( ".desktop" ) )
|
|
fileName += ".desktop";
|
|
|
|
if( TQFileInfo(fileName).exists() ) {
|
|
if(KMessageBox::questionYesNo( this, i18n("Do you want to overwrite file \"%1\"").arg(fileName) ) == KMessageBox::No)
|
|
return;
|
|
}
|
|
TQString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) );
|
|
|
|
if ( name.isEmpty() )
|
|
return;
|
|
|
|
KConfig config( fileName );
|
|
config.setGroup( "General" );
|
|
config.writeEntry( "DatePattern", mDatePatternEdit->text() );
|
|
config.writeEntry( "Columns", mTable->numCols() );
|
|
config.writeEntry( "DelimiterType", mDelimiterBox->id( mDelimiterBox->selected() ) );
|
|
config.writeEntry( "DelimiterOther", mDelimiterEdit->text() );
|
|
config.writeEntry( "QuoteType", mComboQuote->currentItem() );
|
|
|
|
config.setGroup( "Misc" );
|
|
config.writeEntry( "Name", name );
|
|
|
|
config.setGroup( "csv column map" );
|
|
|
|
for ( int column = 0; column < mTable->numCols(); ++column ) {
|
|
TQComboTableItem *item = static_cast<TQComboTableItem*>( mTable->item( 0,
|
|
column ) );
|
|
if ( item )
|
|
config.writeEntry( TQString::number( column ), posToType(
|
|
item->currentItem() ) );
|
|
else
|
|
config.writeEntry( TQString::number( column ), 0 );
|
|
}
|
|
|
|
config.sync();
|
|
}
|
|
|
|
TQString CSVImportDialog::getText( int row, int col )
|
|
{
|
|
return mTable->text( row, col );
|
|
}
|
|
|
|
uint CSVImportDialog::posToType( int pos ) const
|
|
{
|
|
uint counter = 0;
|
|
TQMap<TQString, uint>::ConstIterator it;
|
|
for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
|
|
if ( counter == (uint)pos )
|
|
return it.data();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CSVImportDialog::typeToPos( uint type ) const
|
|
{
|
|
uint counter = 0;
|
|
TQMap<TQString, uint>::ConstIterator it;
|
|
for ( it = mTypeMap.begin(); it != mTypeMap.end(); ++it, ++counter )
|
|
if ( it.data() == type )
|
|
return counter;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void CSVImportDialog::ignoreDuplicatesChanged( int )
|
|
{
|
|
fillTable();
|
|
}
|
|
|
|
void CSVImportDialog::setFile( const TQString &fileName )
|
|
{
|
|
if ( fileName.isEmpty() )
|
|
return;
|
|
|
|
TQFile file( fileName );
|
|
if ( !file.open( IO_ReadOnly ) ) {
|
|
KMessageBox::sorry( this, i18n( "Cannot open input file." ) );
|
|
file.close();
|
|
return;
|
|
}
|
|
|
|
mFileArray = file.readAll();
|
|
file.close();
|
|
|
|
mClearTypeStore = true;
|
|
clearTable();
|
|
mTable->setNumCols( 0 );
|
|
mTable->setNumRows( 0 );
|
|
fillTable();
|
|
mClearTypeStore = false;
|
|
|
|
fillComboBox();
|
|
}
|
|
|
|
void CSVImportDialog::urlChanged( const TQString &file )
|
|
{
|
|
bool state = !file.isEmpty();
|
|
|
|
enableButtonOK( state );
|
|
actionButton( User1 )->setEnabled( state );
|
|
actionButton( User2 )->setEnabled( state );
|
|
}
|
|
|
|
void CSVImportDialog::codecChanged()
|
|
{
|
|
fillTable();
|
|
}
|
|
|
|
#include <csvimportdialog.moc>
|