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