00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <qbuttongroup.h>
00028 #include <qfile.h>
00029 #include <qlabel.h>
00030 #include <qlayout.h>
00031 #include <qlineedit.h>
00032 #include <qlistview.h>
00033 #include <qradiobutton.h>
00034 #include <qregexp.h>
00035 #include <qtable.h>
00036 #include <qtextstream.h>
00037 #include <qvbox.h>
00038
00039 #include <kapplication.h>
00040 #include <kdebug.h>
00041 #include <kcombobox.h>
00042 #include <kinputdialog.h>
00043 #include <klineedit.h>
00044 #include <klocale.h>
00045 #include <kprogress.h>
00046 #include <ksimpleconfig.h>
00047 #include <kstandarddirs.h>
00048 #include <kurlrequester.h>
00049 #include <kfiledialog.h>
00050
00051 #include "kimportdialog.h"
00052 #include "kimportdialog.moc"
00053
00054 KImportColumn::KImportColumn(KImportDialog *dlg,const QString &header, int count)
00055 : m_maxCount(count),
00056 m_refCount(0),
00057 m_header(header),
00058 mDialog(dlg)
00059 {
00060 mFormats.append(FormatPlain);
00061 mFormats.append(FormatUnquoted);
00062
00063
00064 mDefaultFormat = FormatUnquoted;
00065
00066 mDialog->addColumn(this);
00067 }
00068
00069 QValueList<int> KImportColumn::formats()
00070 {
00071 return mFormats;
00072 }
00073
00074 QString KImportColumn::formatName(int format)
00075 {
00076 switch (format) {
00077 case FormatPlain:
00078 return i18n("Plain");
00079 case FormatUnquoted:
00080 return i18n("Unquoted");
00081 case FormatBracketed:
00082 return i18n("Bracketed");
00083 default:
00084 return i18n("Undefined");
00085 }
00086 }
00087
00088 int KImportColumn::defaultFormat()
00089 {
00090 return mDefaultFormat;
00091 }
00092
00093 QString KImportColumn::preview(const QString &value, int format)
00094 {
00095 if (format == FormatBracketed) {
00096 return "(" + value + ")";
00097 } else if (format == FormatUnquoted) {
00098 if (value.left(1) == "\"" && value.right(1) == "\"") {
00099 return value.mid(1,value.length()-2);
00100 } else {
00101 return value;
00102 }
00103 } else {
00104 return value;
00105 }
00106 }
00107
00108 void KImportColumn::addColId(int id)
00109 {
00110 mColIds.append(id);
00111 }
00112
00113 void KImportColumn::removeColId(int id)
00114 {
00115 mColIds.remove(id);
00116 }
00117
00118 QValueList<int> KImportColumn::colIdList()
00119 {
00120 return mColIds;
00121 }
00122
00123 QString KImportColumn::convert()
00124 {
00125 QValueList<int>::ConstIterator it = mColIds.begin();
00126 if (it == mColIds.end()) return "";
00127 else return mDialog->cell(*it);
00128 }
00129
00130
00131 class ColumnItem : public QListViewItem {
00132 public:
00133 ColumnItem(KImportColumn *col,QListView *parent) : QListViewItem(parent), mColumn(col)
00134 {
00135 setText(0,mColumn->header());
00136 }
00137
00138 KImportColumn *column() { return mColumn; }
00139
00140 private:
00141 KImportColumn *mColumn;
00142 };
00143
00151 KImportDialog::KImportDialog(QWidget* parent)
00152 : KDialogBase(parent,"importdialog",true,i18n("Import Text File"),Ok|Cancel),
00153 mSeparator(","),
00154 mCurrentRow(0)
00155 {
00156 mData.setAutoDelete( true );
00157
00158 QVBox *topBox = new QVBox(this);
00159 setMainWidget(topBox);
00160 topBox->setSpacing(spacingHint());
00161
00162 QHBox *fileBox = new QHBox(topBox);
00163 fileBox->setSpacing(spacingHint());
00164 new QLabel(i18n("File to import:"),fileBox);
00165 KURLRequester *urlRequester = new KURLRequester(fileBox);
00166 urlRequester->setFilter( "*.csv" );
00167 connect(urlRequester,SIGNAL(returnPressed(const QString &)),
00168 SLOT(setFile(const QString &)));
00169 connect(urlRequester,SIGNAL(urlSelected(const QString &)),
00170 SLOT(setFile(const QString &)));
00171 connect(urlRequester->lineEdit(),SIGNAL(textChanged ( const QString & )),
00172 SLOT(slotUrlChanged(const QString & )));
00173 mTable = new QTable(5,5,topBox);
00174 mTable->setMinimumHeight( 150 );
00175 connect(mTable,SIGNAL(selectionChanged()),SLOT(tableSelected()));
00176
00177 QHBox *separatorBox = new QHBox( topBox );
00178 separatorBox->setSpacing( spacingHint() );
00179
00180 new QLabel( i18n( "Separator:" ), separatorBox );
00181
00182 mSeparatorCombo = new KComboBox( separatorBox );
00183 mSeparatorCombo->insertItem( "," );
00184 mSeparatorCombo->insertItem( i18n( "Tab" ) );
00185 mSeparatorCombo->insertItem( i18n( "Space" ) );
00186 mSeparatorCombo->insertItem( "=" );
00187 mSeparatorCombo->insertItem( ";" );
00188 connect(mSeparatorCombo, SIGNAL( activated(int) ),
00189 this, SLOT( separatorClicked(int) ) );
00190 mSeparatorCombo->setCurrentItem( 0 );
00191
00192 QHBox *rowsBox = new QHBox( topBox );
00193 rowsBox->setSpacing( spacingHint() );
00194
00195 new QLabel( i18n( "Import starts at row:" ), rowsBox );
00196 mStartRow = new QSpinBox( rowsBox );
00197 mStartRow->setMinValue( 1 );
00198
00199
00200
00201
00202
00203 QVBox *assignBox = new QVBox(topBox);
00204 assignBox->setSpacing(spacingHint());
00205
00206 QHBox *listsBox = new QHBox(assignBox);
00207 listsBox->setSpacing(spacingHint());
00208
00209 mHeaderList = new QListView(listsBox);
00210 mHeaderList->addColumn(i18n("Header"));
00211 connect(mHeaderList, SIGNAL(selectionChanged(QListViewItem*)),
00212 this, SLOT(headerSelected(QListViewItem*)));
00213 connect(mHeaderList,SIGNAL(doubleClicked(QListViewItem*)),
00214 SLOT(assignColumn(QListViewItem *)));
00215
00216 mFormatCombo = new KComboBox( listsBox );
00217 mFormatCombo->setDuplicatesEnabled( false );
00218
00219 QPushButton *assignButton = new QPushButton(i18n("Assign to Selected Column"),
00220 assignBox);
00221 connect(assignButton,SIGNAL(clicked()),SLOT(assignColumn()));
00222
00223 QPushButton *removeButton = new QPushButton(i18n("Remove Assignment From Selected Column"),
00224 assignBox);
00225 connect(removeButton,SIGNAL(clicked()),SLOT(removeColumn()));
00226
00227 QPushButton *assignTemplateButton = new QPushButton(i18n("Assign with Template..."),
00228 assignBox);
00229 connect(assignTemplateButton,SIGNAL(clicked()),SLOT(assignTemplate()));
00230
00231 QPushButton *saveTemplateButton = new QPushButton(i18n("Save Current Template"),
00232 assignBox);
00233 connect(saveTemplateButton,SIGNAL(clicked()),SLOT(saveTemplate()));
00234
00235 resize(500,300);
00236
00237 connect(this,SIGNAL(okClicked()),SLOT(applyConverter()));
00238 connect(this,SIGNAL(applyClicked()),SLOT(applyConverter()));
00239 enableButtonOK(!urlRequester->lineEdit()->text().isEmpty());
00240 }
00241
00242 void KImportDialog::slotUrlChanged(const QString & text)
00243 {
00244 enableButtonOK(!text.isEmpty());
00245 }
00246
00247 bool KImportDialog::setFile(const QString& file)
00248 {
00249 enableButtonOK(!file.isEmpty());
00250 kdDebug(5300) << "KImportDialog::setFile(): " << file << endl;
00251
00252 QFile f(file);
00253
00254 if (f.open(IO_ReadOnly)) {
00255 mFile = "";
00256 QTextStream t(&f);
00257 mFile = t.read();
00258
00259 f.close();
00260
00261 readFile();
00262
00263
00264
00265 return true;
00266 } else {
00267 kdDebug(5300) << " Open failed" << endl;
00268 return false;
00269 }
00270 }
00271
00272 void KImportDialog::registerColumns()
00273 {
00274 QPtrListIterator<KImportColumn> colIt(mColumns);
00275 for (; colIt.current(); ++colIt) {
00276 new ColumnItem(*colIt,mHeaderList);
00277 }
00278 mHeaderList->setSelected(mHeaderList->firstChild(),true);
00279 }
00280
00281 void KImportDialog::fillTable()
00282 {
00283
00284
00285 int row, column;
00286
00287 for (row = 0; row < mTable->numRows(); ++row)
00288 for (column = 0; column < mTable->numCols(); ++column)
00289 mTable->clearCell(row, column);
00290
00291 for ( row = 0; row < int(mData.count()); ++row ) {
00292 QValueVector<QString> *rowVector = mData[ row ];
00293 for( column = 0; column < int(rowVector->size()); ++column ) {
00294 setCellText( row, column, rowVector->at( column ) );
00295 }
00296 }
00297 }
00298
00299 void KImportDialog::readFile( int rows )
00300 {
00301 kdDebug(5300) << "KImportDialog::readFile(): " << rows << endl;
00302
00303 mData.clear();
00304
00305 int row, column;
00306 enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
00307 S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
00308
00309 QChar m_textquote = '"';
00310 int m_startline = 0;
00311
00312 QChar x;
00313 QString field = "";
00314
00315 row = column = 0;
00316 QTextStream inputStream(mFile, IO_ReadOnly);
00317 inputStream.setEncoding(QTextStream::Locale);
00318
00319 KProgressDialog pDialog(this, 0, i18n("Loading Progress"),
00320 i18n("Please wait while the file is loaded."), true);
00321 pDialog.setAllowCancel(true);
00322 pDialog.showCancelButton(true);
00323 pDialog.setAutoClose(true);
00324
00325 KProgress *progress = pDialog.progressBar();
00326 progress->setTotalSteps( mFile.contains(mSeparator, false) );
00327 progress->setValue(0);
00328 int progressValue = 0;
00329
00330 if (progress->totalSteps() > 0)
00331 pDialog.show();
00332
00333 while (!inputStream.atEnd() && !pDialog.wasCancelled()) {
00334 inputStream >> x;
00335
00336
00337 if (x == mSeparator)
00338 {
00339 progress->setValue(progressValue++);
00340 if (progressValue % 15 == 0)
00341 kapp->processEvents();
00342 }
00343
00344 if (x == '\r') inputStream >> x;
00345
00346 switch (state) {
00347 case S_START :
00348 if (x == m_textquote) {
00349 field += x;
00350 state = S_QUOTED_FIELD;
00351 } else if (x == mSeparator) {
00352 ++column;
00353 } else if (x == '\n') {
00354 ++row;
00355 column = 0;
00356 } else {
00357 field += x;
00358 state = S_MAYBE_NORMAL_FIELD;
00359 }
00360 break;
00361 case S_QUOTED_FIELD :
00362 if (x == m_textquote) {
00363 field += x;
00364 state = S_MAYBE_END_OF_QUOTED_FIELD;
00365 } else if (x == '\n') {
00366 setData(row - m_startline, column, field);
00367 field = "";
00368 if (x == '\n') {
00369 ++row;
00370 column = 0;
00371 } else {
00372 ++column;
00373 }
00374 state = S_START;
00375 } else {
00376 field += x;
00377 }
00378 break;
00379 case S_MAYBE_END_OF_QUOTED_FIELD :
00380 if (x == m_textquote) {
00381 field += x;
00382 state = S_QUOTED_FIELD;
00383 } else if (x == mSeparator || x == '\n') {
00384 setData(row - m_startline, column, field);
00385 field = "";
00386 if (x == '\n') {
00387 ++row;
00388 column = 0;
00389 } else {
00390 ++column;
00391 }
00392 state = S_START;
00393 } else {
00394 state = S_END_OF_QUOTED_FIELD;
00395 }
00396 break;
00397 case S_END_OF_QUOTED_FIELD :
00398 if (x == mSeparator || x == '\n') {
00399 setData(row - m_startline, column, field);
00400 field = "";
00401 if (x == '\n') {
00402 ++row;
00403 column = 0;
00404 } else {
00405 ++column;
00406 }
00407 state = S_START;
00408 } else {
00409 state = S_END_OF_QUOTED_FIELD;
00410 }
00411 break;
00412 case S_MAYBE_NORMAL_FIELD :
00413 if (x == m_textquote) {
00414 field = "";
00415 state = S_QUOTED_FIELD;
00416 }
00417 case S_NORMAL_FIELD :
00418 if (x == mSeparator || x == '\n') {
00419 setData(row - m_startline, column, field);
00420 field = "";
00421 if (x == '\n') {
00422 ++row;
00423 column = 0;
00424 } else {
00425 ++column;
00426 }
00427 state = S_START;
00428 } else {
00429 field += x;
00430 }
00431 }
00432
00433 if ( rows > 0 && row > rows ) break;
00434 }
00435
00436 fillTable();
00437 }
00438
00439 void KImportDialog::setCellText(int row, int col, const QString& text)
00440 {
00441 if (row < 0) return;
00442
00443 if ((mTable->numRows() - 1) < row) mTable->setNumRows(row + 1);
00444 if ((mTable->numCols() - 1) < col) mTable->setNumCols(col + 1);
00445
00446 KImportColumn *c = mColumnDict.find(col);
00447 QString formattedText;
00448 if (c) formattedText = c->preview(text,findFormat(col));
00449 else formattedText = text;
00450 mTable->setText(row, col, formattedText);
00451 }
00452
00453 void KImportDialog::formatSelected(QListViewItem*)
00454 {
00455
00456 }
00457
00458 void KImportDialog::headerSelected(QListViewItem* item)
00459 {
00460 KImportColumn *col = ((ColumnItem *)item)->column();
00461
00462 if (!col) return;
00463
00464 mFormatCombo->clear();
00465
00466 QValueList<int> formats = col->formats();
00467
00468 QValueList<int>::ConstIterator it = formats.begin();
00469 QValueList<int>::ConstIterator end = formats.end();
00470 while(it != end) {
00471 mFormatCombo->insertItem( col->formatName(*it), *it - 1 );
00472 ++it;
00473 }
00474
00475 QTableSelection selection = mTable->selection(mTable->currentSelection());
00476
00477 updateFormatSelection(selection.leftCol());
00478 }
00479
00480 void KImportDialog::updateFormatSelection(int column)
00481 {
00482 int format = findFormat(column);
00483
00484 if ( format == KImportColumn::FormatUndefined )
00485 mFormatCombo->setCurrentItem( 0 );
00486 else
00487 mFormatCombo->setCurrentItem( format - 1 );
00488 }
00489
00490 void KImportDialog::tableSelected()
00491 {
00492 QTableSelection selection = mTable->selection(mTable->currentSelection());
00493
00494 QListViewItem *item = mHeaderList->firstChild();
00495 KImportColumn *col = mColumnDict.find(selection.leftCol());
00496 if (col) {
00497 while(item) {
00498 if (item->text(0) == col->header()) {
00499 break;
00500 }
00501 item = item->nextSibling();
00502 }
00503 }
00504 if (item) {
00505 mHeaderList->setSelected(item,true);
00506 }
00507
00508 updateFormatSelection(selection.leftCol());
00509 }
00510
00511 void KImportDialog::separatorClicked(int id)
00512 {
00513 switch(id) {
00514 case 0:
00515 mSeparator = ',';
00516 break;
00517 case 1:
00518 mSeparator = '\t';
00519 break;
00520 case 2:
00521 mSeparator = ' ';
00522 break;
00523 case 3:
00524 mSeparator = '=';
00525 break;
00526 case 4:
00527 mSeparator = ';';
00528 break;
00529 default:
00530 mSeparator = ',';
00531 break;
00532 }
00533
00534 readFile();
00535 }
00536
00537 void KImportDialog::assignColumn(QListViewItem *item)
00538 {
00539 if (!item) return;
00540
00541
00542
00543
00544 ColumnItem *colItem = (ColumnItem *)item;
00545
00546 QTableSelection selection = mTable->selection(mTable->currentSelection());
00547
00548
00549
00550 for(int i=selection.leftCol();i<=selection.rightCol();++i) {
00551 if (i >= 0) {
00552 mTable->horizontalHeader()->setLabel(i,colItem->text(0));
00553 mColumnDict.replace(i,colItem->column());
00554 int format = mFormatCombo->currentItem() + 1;
00555 mFormats.replace(i,format);
00556 colItem->column()->addColId(i);
00557 }
00558 }
00559
00560 readFile();
00561 }
00562
00563 void KImportDialog::assignColumn()
00564 {
00565 assignColumn(mHeaderList->currentItem());
00566 }
00567
00568 void KImportDialog::assignTemplate()
00569 {
00570 QMap<uint,int> columnMap;
00571 QMap<QString, QString> fileMap;
00572 QStringList templates;
00573
00574
00575 QStringList list = KGlobal::dirs()->findAllResources( "data" , QString( kapp->name() ) +
00576 "/csv-templates/*.desktop", true, true );
00577
00578 for ( QStringList::iterator it = list.begin(); it != list.end(); ++it )
00579 {
00580 KSimpleConfig config( *it, true );
00581
00582 if ( !config.hasGroup( "csv column map" ) )
00583 continue;
00584
00585 config.setGroup( "Misc" );
00586 templates.append( config.readEntry( "Name" ) );
00587 fileMap.insert( config.readEntry( "Name" ), *it );
00588 }
00589
00590
00591 bool ok = false;
00592 QString tmp;
00593 tmp = KInputDialog::getItem( i18n( "Template Selection" ),
00594 i18n( "Please select a template, that matches the CSV file:" ),
00595 templates, 0, false, &ok, this );
00596
00597 if ( !ok )
00598 return;
00599
00600 KSimpleConfig config( fileMap[ tmp ], true );
00601 config.setGroup( "General" );
00602 uint numColumns = config.readUnsignedNumEntry( "Columns" );
00603 int format = config.readNumEntry( "Format" );
00604
00605
00606 config.setGroup( "csv column map" );
00607 for ( uint i = 0; i < numColumns; ++i ) {
00608 int col = config.readNumEntry( QString::number( i ) );
00609 columnMap.insert( i, col );
00610 }
00611
00612
00613 for ( uint i = 0; i < columnMap.count(); ++i ) {
00614 int tableColumn = columnMap[i];
00615 if ( tableColumn == -1 )
00616 continue;
00617 KImportColumn *col = mColumns.at(i);
00618 mTable->horizontalHeader()->setLabel( tableColumn, col->header() );
00619 mColumnDict.replace( tableColumn, col );
00620 mFormats.replace( tableColumn, format );
00621 col->addColId( tableColumn );
00622 }
00623
00624 readFile();
00625 }
00626
00627 void KImportDialog::removeColumn()
00628 {
00629 QTableSelection selection = mTable->selection(mTable->currentSelection());
00630
00631
00632
00633 for(int i=selection.leftCol();i<=selection.rightCol();++i) {
00634 if (i >= 0) {
00635 mTable->horizontalHeader()->setLabel(i,QString::number(i+1));
00636 KImportColumn *col = mColumnDict.find(i);
00637 if (col) {
00638 mColumnDict.remove(i);
00639 mFormats.remove(i);
00640 col->removeColId(i);
00641 }
00642 }
00643 }
00644
00645 readFile();
00646 }
00647
00648 void KImportDialog::applyConverter()
00649 {
00650 kdDebug(5300) << "KImportDialog::applyConverter" << endl;
00651
00652 KProgressDialog pDialog(this, 0, i18n("Importing Progress"),
00653 i18n("Please wait while the data is imported."), true);
00654 pDialog.setAllowCancel(true);
00655 pDialog.showCancelButton(true);
00656 pDialog.setAutoClose(true);
00657
00658 KProgress *progress = pDialog.progressBar();
00659 progress->setTotalSteps( mTable->numRows()-1 );
00660 progress->setValue(0);
00661
00662 readFile( 0 );
00663
00664 pDialog.show();
00665 for( uint i = mStartRow->value() - 1; i < mData.count() && !pDialog.wasCancelled(); ++i ) {
00666 mCurrentRow = i;
00667 progress->setValue(i);
00668 if (i % 5 == 0)
00669 kapp->processEvents();
00670
00671 convertRow();
00672 }
00673 }
00674
00675 int KImportDialog::findFormat(int column)
00676 {
00677 QMap<int,int>::ConstIterator formatIt = mFormats.find(column);
00678 int format;
00679 if (formatIt == mFormats.end()) format = KImportColumn::FormatUndefined;
00680 else format = *formatIt;
00681
00682
00683
00684 return format;
00685 }
00686
00687 QString KImportDialog::cell(uint col)
00688 {
00689 if ( col >= mData[ mCurrentRow ]->size() ) return "";
00690 else return data( mCurrentRow, col );
00691 }
00692
00693 void KImportDialog::addColumn(KImportColumn *col)
00694 {
00695 mColumns.append(col);
00696 }
00697
00698 void KImportDialog::setData( uint row, uint col, const QString &value )
00699 {
00700 QString val = value;
00701 val.replace( "\\n", "\n" );
00702
00703 if ( row >= mData.count() ) {
00704 mData.resize( row + 1 );
00705 }
00706
00707 QValueVector<QString> *rowVector = mData[ row ];
00708 if ( !rowVector ) {
00709 rowVector = new QValueVector<QString>;
00710 mData.insert( row, rowVector );
00711 }
00712 if ( col >= rowVector->size() ) {
00713 rowVector->resize( col + 1 );
00714 }
00715
00716 KImportColumn *c = mColumnDict.find( col );
00717 if ( c )
00718 rowVector->at( col ) = c->preview( val, findFormat(col) );
00719 else
00720 rowVector->at( col ) = val;
00721 }
00722
00723 QString KImportDialog::data( uint row, uint col )
00724 {
00725 return mData[ row ]->at( col );
00726 }
00727
00728 void KImportDialog::saveTemplate()
00729 {
00730 QString fileName = KFileDialog::getSaveFileName(
00731 locateLocal( "data", QString( kapp->name() ) + "/csv-templates/" ),
00732 "*.desktop", this );
00733
00734 if ( fileName.isEmpty() )
00735 return;
00736
00737 if ( !fileName.contains( ".desktop" ) )
00738 fileName += ".desktop";
00739
00740 QString name = KInputDialog::getText( i18n( "Template Name" ), i18n( "Please enter a name for the template:" ) );
00741
00742 if ( name.isEmpty() )
00743 return;
00744
00745 KConfig config( fileName );
00746 config.setGroup( "General" );
00747 config.writeEntry( "Columns", mColumns.count() );
00748 config.writeEntry( "Format", mFormatCombo->currentItem() + 1 );
00749
00750 config.setGroup( "Misc" );
00751 config.writeEntry( "Name", name );
00752
00753 config.setGroup( "csv column map" );
00754
00755 KImportColumn *column;
00756 uint counter = 0;
00757 for ( column = mColumns.first(); column; column = mColumns.next() ) {
00758 QValueList<int> list = column->colIdList();
00759 if ( list.count() > 0 )
00760 config.writeEntry( QString::number( counter ), list[ 0 ] );
00761 else
00762 config.writeEntry( QString::number( counter ), -1 );
00763 counter++;
00764 }
00765
00766 config.sync();
00767 }