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
00028 #include <qbuttongroup.h>
00029 #include <qcheckbox.h>
00030 #include <qclipboard.h>
00031 #include <qlabel.h>
00032 #include <qlineedit.h>
00033 #include <qmime.h>
00034 #include <qpushbutton.h>
00035 #include <qradiobutton.h>
00036 #include <qtable.h>
00037 #include <qlayout.h>
00038 #include <qfiledialog.h>
00039 #include <qpainter.h>
00040 #include <qtextcodec.h>
00041 #include <qtimer.h>
00042 #include <qfontmetrics.h>
00043 #include <qtooltip.h>
00044
00045 #include <kapplication.h>
00046 #include <kdebug.h>
00047 #include <kdialogbase.h>
00048 #include <kfiledialog.h>
00049 #include <klocale.h>
00050 #include <kmessagebox.h>
00051 #include <kglobalsettings.h>
00052 #include <kiconloader.h>
00053 #include <kcharsets.h>
00054 #include <knuminput.h>
00055 #include <kprogress.h>
00056 #include <kactivelabel.h>
00057
00058 #include <kexiutils/identifier.h>
00059 #include <kexiutils/utils.h>
00060 #include <core/kexi.h>
00061 #include <core/kexiproject.h>
00062 #include <core/kexipart.h>
00063 #include <core/kexipartinfo.h>
00064 #include <core/keximainwindow.h>
00065 #include <core/kexiguimsghandler.h>
00066 #include <kexidb/connection.h>
00067 #include <kexidb/tableschema.h>
00068 #include <kexidb/transaction.h>
00069 #include <widget/kexicharencodingcombobox.h>
00070
00071 #include "kexicsvimportdialog.h"
00072 #include "kexicsvimportoptionsdlg.h"
00073 #include "kexicsvwidgets.h"
00074
00075 #ifdef Q_WS_WIN
00076 #include <krecentdirs.h>
00077 #include <windows.h>
00078 #endif
00079
00080 #if 0
00081 #include <kspread_cell.h>
00082 #include <kspread_doc.h>
00083 #include <kspread_sheet.h>
00084 #include <kspread_undo.h>
00085 #include <kspread_view.h>
00086 #endif
00087
00088 #define _IMPORT_ICON "table"
00089 #define _TEXT_TYPE 0
00090 #define _NUMBER_TYPE 1
00091 #define _DATE_TYPE 2
00092 #define _TIME_TYPE 3
00093 #define _DATETIME_TYPE 4
00094 #define _PK_FLAG 5
00095
00096
00097 #define _NO_TYPE_YET -1 //allows to accept a number of empty cells, before something non-empty
00098 #define _FP_NUMBER_TYPE 255 //_NUMBER_TYPE variant
00099 #define MAX_ROWS_TO_PREVIEW 100 //max 100 rows is reasonable
00100 #define MAX_BYTES_TO_PREVIEW 10240 //max 10KB is reasonable
00101 #define MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER 4096
00102
00103 class KexiCSVImportDialogTable : public QTable
00104 {
00105 public:
00106 KexiCSVImportDialogTable( QWidget * parent = 0, const char * name = 0 )
00107 : QTable(parent, name) {
00108 f = font();
00109 f.setBold(true);
00110 }
00111 virtual void paintCell( QPainter * p, int row, int col, const QRect & cr, bool selected, const QColorGroup & cg ) {
00112 if (row==0)
00113 p->setFont(f);
00114 else
00115 p->setFont(font());
00116 QTable::paintCell(p, row, col, cr, selected, cg);
00117 }
00118 virtual void setColumnWidth( int col, int w ) {
00119
00120 QTable::setColumnWidth( col, w + 16 );
00121 }
00122 QFont f;
00123 };
00124
00126 void installRecursiveEventFilter(QObject *filter, QObject *object)
00127 {
00128 object->installEventFilter(filter);
00129
00130 if (!object->children())
00131 return;
00132
00133 QObjectList list = *object->children();
00134 for(QObject *obj = list.first(); obj; obj = list.next())
00135 installRecursiveEventFilter(filter, obj);
00136 }
00137
00138 KexiCSVImportDialog::KexiCSVImportDialog( Mode mode, KexiMainWindow* mainWin,
00139 QWidget * parent, const char * name
00140 )
00141 : KDialogBase(
00142 KDialogBase::Plain,
00143 i18n( "Import CSV Data File" )
00145 ,
00146 (mode==File ? User1 : (ButtonCode)0) |Ok|Cancel,
00147 Ok,
00148 parent,
00149 name ? name : "KexiCSVImportDialog",
00150 true,
00151 false,
00152 KGuiItem( i18n("&Options"))
00153 ),
00154 m_mainWin(mainWin),
00155 m_cancelled( false ),
00156 m_adjustRows( 0 ),
00157 m_startline( 0 ),
00158 m_textquote( QString(KEXICSV_DEFAULT_TEXT_QUOTE)[0] ),
00159 m_mode(mode),
00160 m_prevSelectedCol(-1),
00161 m_columnsAdjusted(false),
00162 m_1stRowForFieldNamesDetected(false),
00163 m_firstFillTableCall(true),
00164 m_blockUserEvents(false),
00165 m_primaryKeyColumn(-1),
00166 m_dialogCancelled(false),
00167 m_conn(0),
00168 m_destinationTableSchema(0),
00169 m_allRowsLoadedInPreview(false),
00170 m_stoppedAt_MAX_BYTES_TO_PREVIEW(false)
00171 {
00172 setWFlags(getWFlags() | Qt::WStyle_Maximize | Qt::WStyle_SysMenu);
00173 hide();
00174 setButtonOK(KGuiItem( i18n("&Import..."), _IMPORT_ICON));
00175
00176 m_typeNames.resize(5);
00177 m_typeNames[0] = i18n("text");
00178 m_typeNames[1] = i18n("number");
00179 m_typeNames[2] = i18n("date");
00180 m_typeNames[3] = i18n("time");
00181 m_typeNames[4] = i18n("date/time");
00182
00183 kapp->config()->setGroup("ImportExport");
00184 m_maximumRowsForPreview = kapp->config()->readNumEntry("MaximumRowsForPreviewInImportDialog", MAX_ROWS_TO_PREVIEW);
00185 m_maximumBytesForPreview = kapp->config()->readNumEntry("MaximumBytesForPreviewInImportDialog", MAX_BYTES_TO_PREVIEW);
00186
00187 m_pkIcon = SmallIcon("key");
00188
00189 m_uniquenessTest.setAutoDelete(true);
00190
00191 setIcon(DesktopIcon(_IMPORT_ICON));
00192 setSizeGripEnabled( TRUE );
00193
00194 m_encoding = QString::fromLatin1(KGlobal::locale()->encoding());
00195 m_file = 0;
00196 m_inputStream = 0;
00197
00198 QVBoxLayout *lyr = new QVBoxLayout(plainPage(), 0, KDialogBase::spacingHint(), "lyr");
00199
00200 m_infoLbl = new KexiCSVInfoLabel(
00201 m_mode==File ? i18n("Preview of data from file:")
00202 : i18n("Preview of data from clipboard:"),
00203 plainPage()
00204 );
00205 lyr->addWidget( m_infoLbl );
00206
00207 QWidget* page = new QFrame( plainPage(), "page" );
00208 QGridLayout *glyr= new QGridLayout( page, 4, 5, 0, KDialogBase::spacingHint(), "glyr");
00209 lyr->addWidget( page );
00210
00211
00212 m_delimiterWidget = new KexiCSVDelimiterWidget(true , page);
00213 glyr->addMultiCellWidget( m_delimiterWidget, 1, 2, 0, 0 );
00214
00215 QLabel *delimiterLabel = new QLabel(m_delimiterWidget, i18n("Delimiter:"), page);
00216 delimiterLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
00217 glyr->addMultiCellWidget( delimiterLabel, 0, 0, 0, 0 );
00218
00219
00220 m_formatComboText = i18n( "Format for column %1:" );
00221 m_formatCombo = new KComboBox(page, "m_formatCombo");
00222 m_formatCombo->insertItem(i18n("Text"));
00223 m_formatCombo->insertItem(i18n("Number"));
00224 m_formatCombo->insertItem(i18n("Date"));
00225 m_formatCombo->insertItem(i18n("Time"));
00226 m_formatCombo->insertItem(i18n("Date/Time"));
00227 glyr->addMultiCellWidget( m_formatCombo, 1, 1, 1, 1 );
00228
00229 m_formatLabel = new QLabel(m_formatCombo, "", page);
00230 m_formatLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
00231 glyr->addWidget( m_formatLabel, 0, 1 );
00232
00233 m_primaryKeyField = new QCheckBox( i18n( "Primary key" ), page, "m_primaryKeyField" );
00234 glyr->addWidget( m_primaryKeyField, 2, 1 );
00235 connect(m_primaryKeyField, SIGNAL(toggled(bool)), this, SLOT(slotPrimaryKeyFieldToggled(bool)));
00236
00237 m_comboQuote = new KexiCSVTextQuoteComboBox( page );
00238 glyr->addWidget( m_comboQuote, 1, 2 );
00239
00240 TextLabel2 = new QLabel( m_comboQuote, i18n( "Text quote:" ), page, "TextLabel2" );
00241 TextLabel2->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
00242 TextLabel2->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
00243 glyr->addWidget( TextLabel2, 0, 2 );
00244
00245 m_startAtLineSpinBox = new KIntSpinBox( page, "m_startAtLineSpinBox" );
00246 m_startAtLineSpinBox->setMinValue(1);
00247 m_startAtLineSpinBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
00248 m_startAtLineSpinBox->setMinimumWidth(QFontMetrics(m_startAtLineSpinBox->font()).width("8888888"));
00249 glyr->addWidget( m_startAtLineSpinBox, 1, 3 );
00250
00251 m_startAtLineLabel = new QLabel( m_startAtLineSpinBox, "",
00252 page, "TextLabel3" );
00253 m_startAtLineLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
00254 m_startAtLineLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
00255 glyr->addWidget( m_startAtLineLabel, 0, 3 );
00256
00257 QSpacerItem* spacer_2 = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred );
00258 glyr->addItem( spacer_2, 0, 4 );
00259
00260 m_ignoreDuplicates = new QCheckBox( page, "m_ignoreDuplicates" );
00261 m_ignoreDuplicates->setText( i18n( "Ignore duplicated delimiters" ) );
00262 glyr->addMultiCellWidget( m_ignoreDuplicates, 2, 2, 2, 4 );
00263
00264 m_1stRowForFieldNames = new QCheckBox( page, "m_1stRowForFieldNames" );
00265 m_1stRowForFieldNames->setText( i18n( "First row contains column names" ) );
00266 glyr->addMultiCellWidget( m_1stRowForFieldNames, 3, 3, 2, 4 );
00267
00268 m_table = new KexiCSVImportDialogTable( plainPage(), "m_table" );
00269 lyr->addWidget( m_table );
00270
00271 m_table->setSizePolicy( QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1) );
00272 m_table->setNumRows( 0 );
00273 m_table->setNumCols( 0 );
00274
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 m_dateRegExp = QRegExp("(\\d{1,4})([/\\-\\.])(\\d{1,2})([/\\-\\.])(\\d{1,4})");
00299 m_timeRegExp1 = QRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
00300 m_timeRegExp2 = QRegExp("(\\d{1,2}):(\\d{1,2})");
00301 m_fpNumberRegExp = QRegExp("[\\-]{0,1}\\d*[,\\.]\\d+");
00302
00303 if (m_mode == File) {
00304 QStringList mimetypes( csvMimeTypes() );
00305 #ifdef Q_WS_WIN
00307 QString recentDir = KGlobalSettings::documentPath();
00308 m_fname = QFileDialog::getOpenFileName(
00309 KFileDialog::getStartURL(":CSVImportExport", recentDir).path(),
00310 KexiUtils::fileDialogFilterStrings(mimetypes, false),
00311 page, "KexiCSVImportDialog", i18n("Open CSV Data File"));
00312 if ( !m_fname.isEmpty() ) {
00313
00314 KURL url;
00315 url.setPath( m_fname );
00316 if (url.isLocalFile())
00317 KRecentDirs::add(":CSVImportExport", url.directory());
00318 }
00319 #else
00320 m_fname = KFileDialog::getOpenFileName(":CSVImportExport", mimetypes.join(" "), this);
00321 #endif
00322
00323 if ( m_fname.isEmpty() )
00324 {
00325 actionButton( Ok )->setEnabled( false );
00326 m_cancelled = true;
00327 if (parentWidget())
00328 parentWidget()->raise();
00329 return;
00330 }
00331 }
00332 else if (m_mode == Clipboard) {
00333 QCString subtype("plain");
00334 m_clipboardData = QApplication::clipboard()->text(subtype, QClipboard::Clipboard);
00335
00336
00337
00338
00339
00340 }
00341 else {
00342 return;
00343 }
00344
00345 m_loadingProgressDlg = 0;
00346 m_importingProgressDlg = 0;
00347 if (m_mode == File) {
00348 m_loadingProgressDlg = new KProgressDialog(
00349 this, "m_loadingProgressDlg", i18n("Loading CSV Data"), i18n("Loading CSV Data from \"%1\"...")
00350 .arg(QDir::convertSeparators(m_fname)), true);
00351 m_loadingProgressDlg->progressBar()->setTotalSteps( m_maximumRowsForPreview+1 );
00352 m_loadingProgressDlg->show();
00353 }
00354
00355 if (m_mode==Clipboard) {
00356 m_infoLbl->setIcon("editpaste");
00357 }
00358
00359
00360 m_table->setSelectionMode(QTable::NoSelection);
00361
00362 connect(m_formatCombo, SIGNAL(activated(int)),
00363 this, SLOT(formatChanged(int)));
00364 connect(m_delimiterWidget, SIGNAL(delimiterChanged(const QString&)),
00365 this, SLOT(delimiterChanged(const QString&)));
00366 connect(m_startAtLineSpinBox, SIGNAL(valueChanged ( int )),
00367 this, SLOT(startlineSelected(int)));
00368 connect(m_comboQuote, SIGNAL(activated(int)),
00369 this, SLOT(textquoteSelected(int)));
00370 connect(m_table, SIGNAL(currentChanged(int, int)),
00371 this, SLOT(currentCellChanged(int, int)));
00372 connect(m_table, SIGNAL(valueChanged(int,int)),
00373 this, SLOT(cellValueChanged(int,int)));
00374 connect(m_ignoreDuplicates, SIGNAL(stateChanged(int)),
00375 this, SLOT(ignoreDuplicatesChanged(int)));
00376 connect(m_1stRowForFieldNames, SIGNAL(stateChanged(int)),
00377 this, SLOT(slot1stRowForFieldNamesChanged(int)));
00378
00379 connect(this, SIGNAL(user1Clicked()), this, SLOT(optionsButtonClicked()));
00380
00381 installRecursiveEventFilter(this, this);
00382
00383 initLater();
00384 }
00385
00386 KexiCSVImportDialog::~KexiCSVImportDialog()
00387 {
00388 delete m_file;
00389 }
00390
00391 void KexiCSVImportDialog::initLater()
00392 {
00393 if (!openData())
00394 return;
00395
00396
00397 m_columnsAdjusted = false;
00398 fillTable();
00399 delete m_loadingProgressDlg;
00400 m_loadingProgressDlg = 0;
00401 if (m_dialogCancelled) {
00402
00403
00404 QTimer::singleShot(0, this, SLOT(reject()));
00405 return;
00406 }
00407
00408 currentCellChanged(0, 0);
00409
00410
00411 adjustSize();
00412 KDialog::centerOnScreen( this );
00413
00414 if (m_loadingProgressDlg)
00415 m_loadingProgressDlg->hide();
00416 show();
00417 m_table->setFocus();
00418 }
00419
00420 bool KexiCSVImportDialog::openData()
00421 {
00422 if (m_mode!=File)
00423 return true;
00424
00425 delete m_inputStream;
00426 m_inputStream = 0;
00427 if (m_file) {
00428 m_file->close();
00429 delete m_file;
00430 }
00431 m_file = new QFile(m_fname);
00432 if (!m_file->open(IO_ReadOnly))
00433 {
00434 m_file->close();
00435 delete m_file;
00436 m_file = 0;
00437 KMessageBox::sorry( this, i18n("Cannot open input file <nobr>\"%1\"</nobr>.")
00438 .arg(QDir::convertSeparators(m_fname)) );
00439 actionButton( Ok )->setEnabled( false );
00440 m_cancelled = true;
00441 if (parentWidget())
00442 parentWidget()->raise();
00443 return false;
00444 }
00445 return true;
00446 }
00447
00448 bool KexiCSVImportDialog::cancelled() const
00449 {
00450 return m_cancelled;
00451 }
00452
00453 void KexiCSVImportDialog::fillTable()
00454 {
00455 KexiUtils::WaitCursor wc(true);
00456 repaint();
00457 m_blockUserEvents = true;
00458 QPushButton *pb = actionButton(KDialogBase::Cancel);
00459 if (pb)
00460 pb->setEnabled(true);
00461 KexiUtils::WaitCursor wait;
00462
00463 if (m_table->numRows()>0)
00464 m_table->setCurrentCell(0,0);
00465
00466 int row, column, maxColumn;
00467 QString field = QString::null;
00468
00469 for (row = 0; row < m_table->numRows(); ++row)
00470 for (column = 0; column < m_table->numCols(); ++column)
00471 m_table->clearCell(row, column);
00472
00473 m_detectedTypes.clear();
00474 m_detectedTypes.resize(1024, _NO_TYPE_YET);
00475 m_uniquenessTest.clear();
00476 m_uniquenessTest.resize(1024);
00477 m_1stRowForFieldNamesDetected = true;
00478
00479 if (true != loadRows(field, row, column, maxColumn, true))
00480 return;
00481
00482 m_1stRowForFieldNamesDetected = false;
00483
00484
00485 if (field.length() > 0)
00486 {
00487 setText(row - m_startline, column, field, true);
00488 ++row;
00489 field = QString::null;
00490 }
00491
00492 adjustRows( row - m_startline - (m_1stRowForFieldNames->isChecked()?1:0) );
00493
00494 maxColumn = QMAX( maxColumn, column );
00495 m_table->setNumCols(maxColumn);
00496
00497 for (column = 0; column < m_table->numCols(); ++column)
00498 {
00499
00500
00501
00502
00503
00504 updateColumnText(column);
00505 if (!m_columnsAdjusted)
00506 m_table->adjustColumn(column);
00507 }
00508 m_columnsAdjusted = true;
00509
00510 if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
00511 if (_NUMBER_TYPE != m_detectedTypes[ m_primaryKeyColumn ]) {
00512 m_primaryKeyColumn = -1;
00513 }
00514 }
00515
00516 m_prevSelectedCol = -1;
00517 m_table->setCurrentCell(0,0);
00518 currentCellChanged(0, 0);
00519 if (m_primaryKeyColumn != -1)
00520 m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
00521
00522 const int count = QMAX(0, m_table->numRows()-1+m_startline);
00523 m_allRowsLoadedInPreview = count < m_maximumRowsForPreview && !m_stoppedAt_MAX_BYTES_TO_PREVIEW;
00524 if (m_allRowsLoadedInPreview) {
00525 m_startAtLineSpinBox->setMaxValue(count);
00526 m_startAtLineSpinBox->setValue(m_startline+1);
00527 }
00528 m_startAtLineLabel->setText(i18n( "Start at line%1:").arg(
00529 m_allRowsLoadedInPreview ? QString(" (1-%1)").arg(count)
00530 : QString::null
00531 ));
00532 updateRowCountInfo();
00533
00534 m_blockUserEvents = false;
00535 repaint();
00536 m_table->verticalScrollBar()->repaint();
00537 m_table->horizontalScrollBar()->repaint();
00538 }
00539
00540 QString KexiCSVImportDialog::detectDelimiterByLookingAtFirstBytesOfFile(QTextStream& inputStream)
00541 {
00542 QChar detectedDelimiter;
00543
00544
00545 const QIODevice::Offset origOffset = inputStream.device()->at();
00546 QChar c;
00547 m_file->at(0);
00548 for (uint i=0; !inputStream.atEnd() && i < MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER; i++) {
00549 (*m_inputStream) >> c;
00550 if (c=='\t') {
00551 detectedDelimiter = c;
00552 break;
00553 }
00554 else if (c==';' && detectedDelimiter!='\t') {
00555 detectedDelimiter = c;
00556 break;
00557 }
00558 else if (c==',' && detectedDelimiter!='\t' && detectedDelimiter!=';') {
00559 detectedDelimiter = c;
00560 }
00561 }
00562 inputStream.device()->at(origOffset);
00563 if (detectedDelimiter.isNull())
00564 return KEXICSV_DEFAULT_FILE_DELIMITER;
00565 return QString( detectedDelimiter );
00566 }
00567
00568 tristate KexiCSVImportDialog::loadRows(QString &field, int &row, int &column, int &maxColumn,
00569 bool inGUI)
00570 {
00571 enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
00572 S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
00573 field = QString::null;
00574 const bool ignoreDups = m_ignoreDuplicates->isChecked();
00575 bool lastCharDelimiter = false;
00576 bool nextRow = false;
00577 row = column = 1;
00578 maxColumn = 0;
00579 QChar x;
00580 const bool hadInputStream = m_inputStream!=0;
00581 delete m_inputStream;
00582 if ( m_mode == Clipboard ) {
00583 m_inputStream = new QTextStream(m_clipboardData, IO_ReadOnly);
00584 if (!hadInputStream)
00585 m_delimiterWidget->setDelimiter(KEXICSV_DEFAULT_CLIPBOARD_DELIMITER);
00586 }
00587 else {
00588 m_file->at(0);
00589 m_inputStream = new QTextStream(m_file);
00590 if (m_encoding != QString::fromLatin1(KGlobal::locale()->encoding())) {
00591 QTextCodec *codec = KGlobal::charsets()->codecForName(m_encoding);
00592 if (codec)
00593 m_inputStream->setCodec(codec);
00594 }
00595 const QString delimiter( detectDelimiterByLookingAtFirstBytesOfFile(*m_inputStream) );
00596 if (m_delimiterWidget->delimiter() != delimiter)
00597 m_delimiterWidget->setDelimiter( delimiter );
00598 }
00599 const QChar delimiter(m_delimiterWidget->delimiter()[0]);
00600 m_stoppedAt_MAX_BYTES_TO_PREVIEW = false;
00601 int progressStep = 0;
00602 if (m_importingProgressDlg)
00603 progressStep = QMAX( 1, m_importingProgressDlg->progressBar()->totalSteps()/200 );
00604 int offset = 0;
00605 for (;!m_inputStream->atEnd(); offset++)
00606 {
00607
00608
00609
00610
00611 if (m_importingProgressDlg && ((offset % progressStep) < 5)) {
00612
00613 m_importingProgressDlg->progressBar()->setValue(offset);
00614 qApp->processEvents();
00615 if (m_importingProgressDlg->wasCancelled()) {
00616 delete m_importingProgressDlg;
00617 m_importingProgressDlg = 0;
00618 return ::cancelled;
00619 }
00620 }
00621
00622 (*m_inputStream) >> x;
00623
00624 if (x == '\r') {
00625 continue;
00626 }
00627
00628 switch (state)
00629 {
00630 case S_START :
00631 if (x == m_textquote)
00632 {
00633 state = S_QUOTED_FIELD;
00634 }
00635 else if (x == delimiter)
00636 {
00637 setText(row - m_startline, column, field, inGUI);
00638 field = QString::null;
00639 if ((ignoreDups == false) || (lastCharDelimiter == false))
00640 ++column;
00641 lastCharDelimiter = true;
00642 }
00643 else if (x == '\n')
00644 {
00645 if (!inGUI) {
00646
00647 for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
00648 setText(row - m_startline, additionalColumn, QString::null, inGUI);
00649 }
00650 }
00651 nextRow = true;
00652 maxColumn = QMAX( maxColumn, column );
00653 column = 1;
00654 }
00655 else
00656 {
00657 field += x;
00658 state = S_MAYBE_NORMAL_FIELD;
00659 }
00660 break;
00661 case S_QUOTED_FIELD :
00662 if (x == m_textquote)
00663 {
00664 state = S_MAYBE_END_OF_QUOTED_FIELD;
00665 }
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685 else
00686 {
00687 field += x;
00688 }
00689 break;
00690 case S_MAYBE_END_OF_QUOTED_FIELD :
00691 if (x == m_textquote)
00692 {
00693 field += x;
00694 state = S_QUOTED_FIELD;
00695 }
00696 else if (x == delimiter || x == '\n')
00697 {
00698 setText(row - m_startline, column, field, inGUI);
00699 field = QString::null;
00700 if (x == '\n')
00701 {
00702 nextRow = true;
00703 maxColumn = QMAX( maxColumn, column );
00704 column = 1;
00705 }
00706 else
00707 {
00708 if ((ignoreDups == false) || (lastCharDelimiter == false))
00709 ++column;
00710 lastCharDelimiter = true;
00711 }
00712 state = S_START;
00713 }
00714 else
00715 {
00716 state = S_END_OF_QUOTED_FIELD;
00717 }
00718 break;
00719 case S_END_OF_QUOTED_FIELD :
00720 if (x == delimiter || x == '\n')
00721 {
00722 setText(row - m_startline, column, field, inGUI);
00723 field = QString::null;
00724 if (x == '\n')
00725 {
00726 nextRow = true;
00727 maxColumn = QMAX( maxColumn, column );
00728 column = 1;
00729 }
00730 else
00731 {
00732 if ((ignoreDups == false) || (lastCharDelimiter == false))
00733 ++column;
00734 lastCharDelimiter = true;
00735 }
00736 state = S_START;
00737 }
00738 else
00739 {
00740 state = S_END_OF_QUOTED_FIELD;
00741 }
00742 break;
00743 case S_MAYBE_NORMAL_FIELD :
00744 if (x == m_textquote)
00745 {
00746 field = QString::null;
00747 state = S_QUOTED_FIELD;
00748 break;
00749 }
00750 case S_NORMAL_FIELD :
00751 if (x == delimiter || x == '\n')
00752 {
00753 setText(row - m_startline, column, field, inGUI);
00754 field = QString::null;
00755 if (x == '\n')
00756 {
00757 nextRow = true;
00758 maxColumn = QMAX( maxColumn, column );
00759 column = 1;
00760 }
00761 else
00762 {
00763 if ((ignoreDups == false) || (lastCharDelimiter == false))
00764 ++column;
00765 lastCharDelimiter = true;
00766 }
00767 state = S_START;
00768 }
00769 else
00770 {
00771 field += x;
00772 }
00773 }
00774 if (x != delimiter)
00775 lastCharDelimiter = false;
00776
00777 if (nextRow) {
00778 if (!inGUI && row==1 && m_1stRowForFieldNames->isChecked()) {
00779
00780 m_importingStatement->clearArguments();
00781 }
00782 else if (!saveRow(inGUI))
00783 return false;
00784
00785 ++row;
00786 }
00787
00788 if (m_firstFillTableCall && row==2
00789 && !m_1stRowForFieldNames->isChecked() && m_1stRowForFieldNamesDetected)
00790 {
00791
00792 m_1stRowForFieldNamesDetected = false;
00793 m_table->setNumRows( 0 );
00794 m_firstFillTableCall = false;
00795 m_1stRowForFieldNames->setChecked(true);
00796
00797 m_blockUserEvents = false;
00798 repaint();
00799 return false;
00800 }
00801
00802 if (!m_importingProgressDlg && row % 20 == 0) {
00803 qApp->processEvents();
00804
00805 if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCancelled()) {
00806 delete m_loadingProgressDlg;
00807 m_loadingProgressDlg = 0;
00808 m_dialogCancelled = true;
00809 reject();
00810 return false;
00811 }
00812 }
00813
00814 if (!m_firstFillTableCall && m_loadingProgressDlg) {
00815 m_loadingProgressDlg->progressBar()->setValue(QMIN(m_maximumRowsForPreview, row));
00816 }
00817
00818 if ( inGUI && row > (m_maximumRowsForPreview + (m_1stRowForFieldNamesDetected?1:0)) ) {
00819 kexipluginsdbg << "KexiCSVImportDialog::fillTable() loading stopped at row #"
00820 << m_maximumRowsForPreview << endl;
00821 break;
00822 }
00823 if (nextRow) {
00824 nextRow = false;
00825
00826 kexipluginsdbg << offset << endl;
00827 if (inGUI && offset >= m_maximumBytesForPreview && row >= 2) {
00828 m_stoppedAt_MAX_BYTES_TO_PREVIEW = true;
00829 return true;
00830 }
00831 }
00832 }
00833 return true;
00834 }
00835
00836 void KexiCSVImportDialog::updateColumnText(int col)
00837 {
00838 QString colName;
00839 if (col<(int)m_columnNames.count() && (m_1stRowForFieldNames->isChecked() || m_changedColumnNames[col]))
00840 colName = m_columnNames[ col ];
00841 if (colName.isEmpty()) {
00842 colName = i18n("Column %1").arg(col+1);
00843 m_changedColumnNames[ col ] = false;
00844 }
00845 int detectedType = m_detectedTypes[col];
00846 if (detectedType==_FP_NUMBER_TYPE)
00847 detectedType=_NUMBER_TYPE;
00848 else if (detectedType==_NO_TYPE_YET) {
00849 m_detectedTypes[col]=_TEXT_TYPE;
00850 detectedType=_TEXT_TYPE;
00851 }
00852 m_table->horizontalHeader()->setLabel(col,
00853 i18n("Column %1").arg(col+1) + " \n(" + m_typeNames[ detectedType ] + ") ");
00854 m_table->setText(0, col, colName);
00855 m_table->horizontalHeader()->adjustHeaderSize();
00856
00857
00858 QValueList<int> *list = m_uniquenessTest[col];
00859 if (m_primaryKeyColumn==-1 && list && !list->isEmpty()) {
00860 qHeapSort(*list);
00861 QValueList<int>::ConstIterator it=list->constBegin();
00862 int prevValue = *it;
00863 ++it;
00864 for(; it!=list->constEnd() && prevValue!=(*it); ++it)
00865 prevValue=(*it);
00866 if (it!=list->constEnd()) {
00867
00868 list->clear();
00869 }
00870 else {
00871
00872 if (-1==m_primaryKeyColumn) {
00873 m_primaryKeyColumn=col;
00874 }
00875 }
00876 }
00877 if (list)
00878 list->clear();
00879 }
00880
00881 void KexiCSVImportDialog::detectTypeAndUniqueness(int row, int col, const QString& text)
00882 {
00883 int intValue;
00884 const int type = m_detectedTypes[col];
00885 if (row==1 || type!=_TEXT_TYPE) {
00886 bool found = false;
00887 if (text.isEmpty() && type==_NO_TYPE_YET)
00888 found = true;
00889
00890
00891 if (!found && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
00892 bool ok = text.isEmpty() || m_fpNumberRegExp.exactMatch(text);
00893
00894
00895 if (ok && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
00896 m_detectedTypes[col]=_FP_NUMBER_TYPE;
00897 found = true;
00898 }
00899 }
00900
00901 if (!found && (row==1 || type==_NUMBER_TYPE || type==_NO_TYPE_YET)) {
00902 bool ok = text.isEmpty();
00903 if (!ok)
00904 intValue = text.toInt(&ok);
00905 if (ok && (row==1 || type==_NO_TYPE_YET)) {
00906 m_detectedTypes[col]=_NUMBER_TYPE;
00907 found = true;
00908 }
00909 }
00910
00911 if (!found && (row==1 || type==_DATE_TYPE || type==_NO_TYPE_YET)) {
00912 if ((row==1 || type==_NO_TYPE_YET)
00913 && (text.isEmpty() || m_dateRegExp.exactMatch(text)))
00914 {
00915 m_detectedTypes[col]=_DATE_TYPE;
00916 found = true;
00917 }
00918 }
00919
00920 if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
00921 if ((row==1 || type==_NO_TYPE_YET)
00922 && (text.isEmpty() || m_timeRegExp1.exactMatch(text) || m_timeRegExp2.exactMatch(text)))
00923 {
00924 m_detectedTypes[col]=_TIME_TYPE;
00925 found = true;
00926 }
00927 }
00928
00929 if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
00930 if (row==1 || type==_NO_TYPE_YET) {
00931 bool detected = text.isEmpty();
00932 if (!detected) {
00933 const QStringList dateTimeList( QStringList::split(" ", text) );
00934 bool ok = dateTimeList.count()>=2;
00937 if (ok) {
00938
00939 QString datePart( dateTimeList[0].stripWhiteSpace() );
00940 QString timePart( dateTimeList[1].stripWhiteSpace() );
00941 ok = m_dateRegExp.exactMatch(datePart)
00942 && (m_timeRegExp1.exactMatch(timePart) || m_timeRegExp2.exactMatch(timePart));
00943 }
00944 detected = ok;
00945 }
00946 if (detected) {
00947 m_detectedTypes[col]=_DATETIME_TYPE;
00948 found = true;
00949 }
00950 }
00951 }
00952 if (!found && type==_NO_TYPE_YET && !text.isEmpty()) {
00953
00954 m_detectedTypes[col]=_TEXT_TYPE;
00955 found = true;
00956 }
00957
00958 }
00959
00960 QValueList<int> *list = m_uniquenessTest[col];
00961 if (row==1 && (!list || !list->isEmpty()) && !text.isEmpty() && _NUMBER_TYPE == m_detectedTypes[col]) {
00962 if (!list) {
00963 list = new QValueList<int>();
00964 m_uniquenessTest.insert(col, list);
00965 }
00966 list->append( intValue );
00967 }
00968 else {
00969
00970 if (list && !list->isEmpty())
00971 list->clear();
00972 }
00973 }
00974
00975 bool KexiCSVImportDialog::parseDate(const QString& text, QDate& date)
00976 {
00977 if (!m_dateRegExp.exactMatch(text))
00978 return false;
00979
00980
00981 const int d1 = m_dateRegExp.cap(1).toInt(), d3 = m_dateRegExp.cap(3).toInt(), d5 = m_dateRegExp.cap(5).toInt();
00982 if (m_dateRegExp.cap(2)=="/")
00983 date = QDate(d5, d1, d3);
00984 else {
00985 if (d5 > 31)
00986 date = QDate(d5, d3, d1);
00987 else
00988 date = QDate(d1, d3, d5);
00989 }
00990 return date.isValid();
00991 }
00992
00993 bool KexiCSVImportDialog::parseTime(const QString& text, QTime& time)
00994 {
00995 time = QTime::fromString(text, Qt::ISODate);
00996 if (time.isValid())
00997 return true;
00998 if (m_timeRegExp2.exactMatch(text)) {
00999 time = QTime(m_timeRegExp2.cap(1).toInt(), m_timeRegExp2.cap(3).toInt(), m_timeRegExp2.cap(5).toInt());
01000 return true;
01001 }
01002 return false;
01003 }
01004
01005 void KexiCSVImportDialog::setText(int row, int col, const QString& text, bool inGUI)
01006 {
01007 if (!inGUI) {
01008
01009 if (col==1) {
01010 m_importingStatement->clearArguments();
01011 if (m_implicitPrimaryKeyAdded)
01012 *m_importingStatement << QVariant();
01013 }
01014 const int detectedType = m_detectedTypes[col-1];
01015 if (detectedType==_NUMBER_TYPE) {
01016 *m_importingStatement << ( text.isEmpty() ? QVariant() : text.toInt() );
01018 }
01019 else if (detectedType==_FP_NUMBER_TYPE) {
01020
01021 QCString t(text.latin1());
01022 const int textLen = t.length();
01023 for (int i=0; i<textLen; i++) {
01024 if (t.at(i)==',') {
01025 t.at(i) = '.';
01026 break;
01027 }
01028 }
01029 *m_importingStatement << ( t.isEmpty() ? QVariant() : t.toDouble() );
01030 }
01031 else if (detectedType==_DATE_TYPE) {
01032
01033 QDate date;
01034 if (parseDate(text, date))
01035 *m_importingStatement << date;
01036 }
01037 else if (detectedType==_TIME_TYPE) {
01038 QTime time;
01039 if (parseTime(text, time))
01040 *m_importingStatement << time;
01041 }
01042 else if (detectedType==_DATETIME_TYPE) {
01043 QStringList dateTimeList( QStringList::split(" ", text) );
01044 if (dateTimeList.count()<2)
01045 dateTimeList = QStringList::split("T", text);
01047 if (dateTimeList.count()>=2) {
01048 QString datePart( dateTimeList[0].stripWhiteSpace() );
01049 QDate date;
01050 if (parseDate(datePart, date)) {
01051 QString timePart( dateTimeList[1].stripWhiteSpace() );
01052 QTime time;
01053 if (parseTime(timePart, time))
01054 *m_importingStatement << QDateTime(date, time);
01055 }
01056 }
01057 }
01058 else
01059 *m_importingStatement << text;
01060 return;
01061 }
01062
01063 if (m_table->numCols() < col) {
01064 m_table->setNumCols(col);
01065 if ((int)m_columnNames.size() < m_table->numCols()) {
01066 m_columnNames.resize(m_table->numCols()+10);
01067 m_changedColumnNames.resize(m_table->numCols()+10);
01068 }
01069 }
01070
01071 if (m_1stRowForFieldNames->isChecked()) {
01072 if ((row+m_startline)==1) {
01073 if ((col-1) < (int)m_changedColumnNames.size() && false==m_changedColumnNames[col-1]) {
01074
01075
01076 QString colName(text.simplifyWhiteSpace());
01077 if (!colName.isEmpty()) {
01078 if (colName.left(1)>="0" && colName.left(1)<="9")
01079 colName.prepend(i18n("Column")+" ");
01080 m_columnNames[ col-1 ] = colName;
01081 }
01082 }
01083 return;
01084 }
01085 }
01086 else {
01087 if ((row+m_startline)==1) {
01088 if (m_1stRowForFieldNamesDetected && !m_1stRowForFieldNames->isChecked()) {
01089 QString f( text.simplifyWhiteSpace() );
01090 if (f.isEmpty() || !f[0].isLetter())
01091 m_1stRowForFieldNamesDetected = false;
01092 }
01093 }
01094 row++;
01095 }
01096
01097 if (row < 2)
01098 return;
01099
01100 if (m_table->numRows() < row) {
01101
01102 m_table->setNumRows(row+100);
01103
01104
01105 m_table->verticalHeader()->setLabel(0, i18n("Column name")+" ");
01106 m_adjustRows=1;
01107 }
01108
01109 m_table->setText(row - 1, col - 1, text.stripWhiteSpace());
01110 m_table->verticalHeader()->setLabel(row-1, QString::number(row-1));
01111
01112 detectTypeAndUniqueness(row-1, col-1, text);
01113 }
01114
01115 bool KexiCSVImportDialog::saveRow(bool inGUI)
01116 {
01117 if (inGUI) {
01118
01119 return true;
01120 }
01121
01122 bool res = m_importingStatement->execute();
01123
01124 m_importingStatement->clearArguments();
01125 return res;
01126
01127 }
01128
01129 void KexiCSVImportDialog::adjustRows(int iRows)
01130 {
01131 if (m_adjustRows)
01132 {
01133 m_table->setNumRows( iRows );
01134 m_adjustRows=0;
01135 for (int i = 0; i<iRows; i++)
01136 m_table->adjustRow(i);
01137 }
01138 }
01139
01140 void KexiCSVImportDialog::formatChanged(int id)
01141 {
01142 if (id==_PK_FLAG) {
01143 if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
01144 m_table->setPixmap(0, m_primaryKeyColumn, QPixmap());
01145 }
01146 if (m_primaryKeyField->isChecked()) {
01147 m_primaryKeyColumn = m_table->currentColumn();
01148 m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
01149 }
01150 else
01151 m_primaryKeyColumn = -1;
01152 return;
01153 }
01154 else {
01155 m_detectedTypes[m_table->currentColumn()]=id;
01156 m_primaryKeyField->setEnabled( _NUMBER_TYPE == id );
01157 m_primaryKeyField->setChecked( m_primaryKeyColumn == m_table->currentColumn() && m_primaryKeyField->isEnabled() );
01158 }
01159 updateColumnText(m_table->currentColumn());
01160 }
01161
01162 void KexiCSVImportDialog::delimiterChanged(const QString& delimiter)
01163 {
01164 Q_UNUSED(delimiter);
01165 m_columnsAdjusted = false;
01166
01167 QTimer::singleShot(10, this, SLOT(fillTable()));
01168 }
01169
01170 void KexiCSVImportDialog::textquoteSelected(int)
01171 {
01172 const QString tq(m_comboQuote->textQuote());
01173 if (tq.isEmpty())
01174 m_textquote = 0;
01175 else
01176 m_textquote = tq[0];
01177
01178
01179 QTimer::singleShot(10, this, SLOT(fillTable()));
01180 }
01181
01182 void KexiCSVImportDialog::startlineSelected(int startline)
01183 {
01184
01185 if (m_startline == (startline-1))
01186 return;
01187 m_startline = startline-1;
01188 m_adjustRows=1;
01189 fillTable();
01190 m_table->setFocus();
01191 }
01192
01193 void KexiCSVImportDialog::currentCellChanged(int, int col)
01194 {
01195 if (m_prevSelectedCol==col)
01196 return;
01197 m_prevSelectedCol = col;
01198 int type = m_detectedTypes[col];
01199 if (type==_FP_NUMBER_TYPE)
01200 type=_NUMBER_TYPE;
01201
01202 m_formatCombo->setCurrentItem( type );
01203 m_formatLabel->setText( m_formatComboText.arg(col+1) );
01204 m_primaryKeyField->setEnabled( _NUMBER_TYPE == m_detectedTypes[col]);
01205 m_primaryKeyField->blockSignals(true);
01206 m_primaryKeyField->setChecked( m_primaryKeyColumn == col );
01207 m_primaryKeyField->blockSignals(false);
01208 }
01209
01210 void KexiCSVImportDialog::cellValueChanged(int row,int col)
01211 {
01212 if (row==0) {
01213 m_columnNames[ col ] = m_table->text(row, col);
01214 m_changedColumnNames.setBit( col );
01215 }
01216 }
01217
01218 void KexiCSVImportDialog::accept()
01219 {
01221
01222 KexiGUIMessageHandler msg;
01223
01224 const uint numRows( m_table->numRows() );
01225 if (numRows == 0)
01226 return;
01227
01228 if (numRows == 1) {
01229 if (KMessageBox::No == KMessageBox::questionYesNo(this,
01230 i18n("Data set contains no rows. Do you want to import empty table?")))
01231 return;
01232 }
01233
01234 KexiProject* project = m_mainWin->project();
01235 if (!project) {
01236 msg.showErrorMessage(i18n("No project available."));
01237 return;
01238 }
01239 m_conn = project->dbConnection();
01240 if (!m_conn) {
01241 msg.showErrorMessage(i18n("No database connection available."));
01242 return;
01243 }
01244 KexiPart::Part *part = Kexi::partManager().partForMimeType("kexi/table");
01245 if (!part) {
01246 msg.showErrorMessage(&Kexi::partManager());
01247 return;
01248 }
01249
01250
01251 QString suggestedName;
01252 if (m_mode==File) {
01253 suggestedName = KURL::fromPathOrURL(m_fname).fileName();
01254
01255 if (!suggestedName.isEmpty()) {
01256 const int idx = suggestedName.findRev(".");
01257 if (idx!=-1)
01258 suggestedName = suggestedName.mid(0, idx ).simplifyWhiteSpace();
01259 }
01260 }
01261
01262
01263 KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), suggestedName);
01264 if (!partItemForSavedTable) {
01265
01266 return;
01267 }
01268
01269 #define _ERR \
01270 { project->deleteUnstoredItem(partItemForSavedTable); \
01271 m_conn = 0; \
01272 delete m_destinationTableSchema; \
01273 m_destinationTableSchema = 0; \
01274 return; }
01275
01276
01277
01278 bool allowOverwriting = true;
01279 tristate res = m_mainWin->getNewObjectInfo( partItemForSavedTable, part, allowOverwriting );
01280 if (~res || !res) {
01282 _ERR;
01283 }
01284
01285
01286
01287
01288
01289
01290
01291
01292 m_destinationTableSchema = new KexiDB::TableSchema(partItemForSavedTable->name());
01293 m_destinationTableSchema->setCaption( partItemForSavedTable->caption() );
01294 m_destinationTableSchema->setDescription( partItemForSavedTable->description() );
01295 const uint numCols( m_table->numCols() );
01296
01297 m_implicitPrimaryKeyAdded = false;
01298
01299 int msgboxResult;
01300 if (m_primaryKeyColumn==-1
01301 && KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(this,
01302 i18n("No Primary Key (autonumber) has been defined.\n"
01303 "Should it be automatically defined on import (recommended)?\n\n"
01304 "Note: An imported table without a Primary Key may not be editable (depending on database type)."),
01305 QString::null, KGuiItem(i18n("Add Database Primary Key to a Table", "Add Primary Key"), "key"),
01306 KGuiItem(i18n("Do Not Add Database Primary Key to a Table", "Do Not Add")))))
01307 {
01308 if (msgboxResult == KMessageBox::Cancel)
01309 _ERR;
01310
01311
01313 m_implicitPrimaryKeyAdded = true;
01314
01315 QString fieldName("id");
01316 QString fieldCaption("Id");
01317
01318 QStringList colnames;
01319 for (uint col = 0; col < numCols; col++)
01320 colnames.append( m_table->text(0, col).lower().simplifyWhiteSpace() );
01321
01322 if (colnames.find(fieldName)!=colnames.end()) {
01323 int num = 1;
01324 while (colnames.find(fieldName+QString::number(num))!=colnames.end())
01325 num++;
01326 fieldName += QString::number(num);
01327 fieldCaption += QString::number(num);
01328 }
01329 KexiDB::Field *field = new KexiDB::Field(
01330 fieldName,
01331 KexiDB::Field::Integer,
01332 KexiDB::Field::NoConstraints,
01333 KexiDB::Field::NoOptions,
01334 0,0,
01335 QVariant(),
01336 fieldCaption
01337 );
01338 field->setPrimaryKey(true);
01339 field->setAutoIncrement(true);
01340 m_destinationTableSchema->addField( field );
01341 }
01342
01343 for (uint col = 0; col < numCols; col++) {
01344 QString fieldCaption( m_table->text(0, col).simplifyWhiteSpace() );
01345 QString fieldName( KexiUtils::string2Identifier( fieldCaption ) );
01346 if (m_destinationTableSchema->field(fieldName)) {
01347 QString fixedFieldName;
01348 uint i = 2;
01349 do {
01350 fixedFieldName = fieldName + "_" + QString::number(i);
01351 if (!m_destinationTableSchema->field(fixedFieldName))
01352 break;
01353 i++;
01354 } while (true);
01355 fieldName = fixedFieldName;
01356 fieldCaption += (" " + QString::number(i));
01357 }
01358 const int detectedType = m_detectedTypes[col];
01359 KexiDB::Field::Type fieldType;
01360 if (detectedType==_DATE_TYPE)
01361 fieldType = KexiDB::Field::Date;
01362 if (detectedType==_TIME_TYPE)
01363 fieldType = KexiDB::Field::Time;
01364 if (detectedType==_DATETIME_TYPE)
01365 fieldType = KexiDB::Field::DateTime;
01366 else if (detectedType==_NUMBER_TYPE)
01367 fieldType = KexiDB::Field::Integer;
01368 else if (detectedType==_FP_NUMBER_TYPE)
01369 fieldType = KexiDB::Field::Double;
01371 else
01372 fieldType = KexiDB::Field::Text;
01374
01375 KexiDB::Field *field = new KexiDB::Field(
01376 fieldName,
01377 fieldType,
01378 KexiDB::Field::NoConstraints,
01379 KexiDB::Field::NoOptions,
01380 0,0,
01381 QVariant(),
01382 fieldCaption
01383 );
01384
01385 if ((int)col == m_primaryKeyColumn) {
01386 field->setPrimaryKey(true);
01387 field->setAutoIncrement(true);
01388 }
01389 m_destinationTableSchema->addField( field );
01390 }
01391
01392 KexiDB::Transaction transaction = m_conn->beginTransaction();
01393 if (transaction.isNull()) {
01394 msg.showErrorMessage(m_conn);
01395 _ERR;
01396 }
01397 KexiDB::TransactionGuard tg(transaction);
01398
01399
01400 if (!m_conn->createTable(m_destinationTableSchema, allowOverwriting)) {
01401 msg.showErrorMessage(m_conn);
01402 _ERR;
01403 }
01404
01405 #define _DROP_DEST_TABLE_AND_RETURN \
01406 { \
01407 if (m_importingProgressDlg) \
01408 m_importingProgressDlg->hide(); \
01409 project->deleteUnstoredItem(partItemForSavedTable); \
01410 m_conn->dropTable(m_destinationTableSchema); \
01411 m_destinationTableSchema = 0; \
01412 m_conn = 0; \
01413 return; \
01414 }
01415
01416 m_importingStatement = m_conn->prepareStatement(
01417 KexiDB::PreparedStatement::InsertStatement, *m_destinationTableSchema);
01418 if (!m_importingStatement) {
01419 msg.showErrorMessage(m_conn);
01420 _DROP_DEST_TABLE_AND_RETURN;
01421 }
01422
01423 if (m_file) {
01424 if (!m_importingProgressDlg) {
01425 m_importingProgressDlg = new KProgressDialog( this, "m_importingProgressDlg",
01426 i18n("Importing CSV Data"), QString::null, true );
01427 }
01428 m_importingProgressDlg->setLabel(
01429 i18n("Importing CSV Data from <nobr>\"%1\"</nobr> into \"%2\" table...")
01430 .arg(QDir::convertSeparators(m_fname)).arg(m_destinationTableSchema->name()) );
01431 m_importingProgressDlg->progressBar()->setTotalSteps( QFileInfo(*m_file).size() );
01432 m_importingProgressDlg->show();
01433 }
01434
01435 int row, column, maxColumn;
01436 QString field = QString::null;
01437
01438
01439 res = loadRows(field, row, column, maxColumn, false );
01440
01441 delete m_importingProgressDlg;
01442 m_importingProgressDlg = 0;
01443 if (true != res) {
01444
01445 if (!res)
01446 msg.showErrorMessage(m_conn);
01447 _DROP_DEST_TABLE_AND_RETURN;
01448 }
01449
01450
01451 if (field.length() > 0)
01452 {
01453 setText(row - m_startline, column, field, false );
01454
01455 for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
01456 setText(row - m_startline, additionalColumn, QString::null, false );
01457 }
01458 if (!saveRow(false )) {
01459 msg.showErrorMessage(m_conn);
01460 _DROP_DEST_TABLE_AND_RETURN;
01461 }
01462 ++row;
01463 field = QString::null;
01464 }
01465
01466 if (!tg.commit()) {
01467 msg.showErrorMessage(m_conn);
01468 _DROP_DEST_TABLE_AND_RETURN;
01469 }
01470
01471
01472 partItemForSavedTable->setIdentifier( m_destinationTableSchema->id() );
01473 project->addStoredItem( part->info(), partItemForSavedTable );
01474
01475 QDialog::accept();
01476 KMessageBox::information(this, i18n("Data has been successfully imported to table \"%1\".")
01477 .arg(m_destinationTableSchema->name()));
01478 parentWidget()->raise();
01479 m_conn = 0;
01480 }
01481
01482 int KexiCSVImportDialog::getHeader(int col)
01483 {
01484 QString header = m_table->horizontalHeader()->label(col);
01485
01486 if (header == i18n("Text type for column", "Text"))
01487 return TEXT;
01488 else if (header == i18n("Numeric type for column", "Number"))
01489 return NUMBER;
01490 else if (header == i18n("Currency type for column", "Currency"))
01491 return CURRENCY;
01492 else
01493 return DATE;
01494 }
01495
01496 QString KexiCSVImportDialog::getText(int row, int col)
01497 {
01498 return m_table->text(row, col);
01499 }
01500
01501 void KexiCSVImportDialog::ignoreDuplicatesChanged(int)
01502 {
01503 fillTable();
01504 }
01505
01506 void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(int)
01507 {
01508 m_adjustRows=1;
01509 if (m_1stRowForFieldNames->isChecked() && m_startline>0 && m_startline>=(m_startAtLineSpinBox->maxValue()-1))
01510 m_startline--;
01511 fillTable();
01512 }
01513
01514 void KexiCSVImportDialog::optionsButtonClicked()
01515 {
01516 KexiCSVImportOptionsDialog dlg(m_encoding, this);
01517 if (QDialog::Accepted != dlg.exec())
01518 return;
01519
01520 if (m_encoding != dlg.encodingComboBox()->selectedEncoding()) {
01521 m_encoding = dlg.encodingComboBox()->selectedEncoding();
01522 if (!openData())
01523 return;
01524 fillTable();
01525 }
01526 }
01527
01528 bool KexiCSVImportDialog::eventFilter ( QObject * watched, QEvent * e )
01529 {
01530 QEvent::Type t = e->type();
01531
01532 if (m_blockUserEvents && (t==QEvent::KeyPress || t==QEvent::KeyRelease
01533 || t==QEvent::MouseButtonPress || t==QEvent::MouseButtonDblClick
01534 || t==QEvent::Paint ))
01535 return true;
01536
01537 if (watched == m_startAtLineSpinBox && t==QEvent::KeyPress) {
01538 QKeyEvent *ke = static_cast<QKeyEvent*>(e);
01539 if (ke->key()==Key_Enter || ke->key()==Key_Return) {
01540 m_table->setFocus();
01541 return true;
01542 }
01543 }
01544 return QDialog::eventFilter( watched, e );
01545 }
01546
01547 void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(bool on)
01548 {
01549 Q_UNUSED(on);
01550 formatChanged(_PK_FLAG);
01551 }
01552
01553 void KexiCSVImportDialog::updateRowCountInfo()
01554 {
01556 m_infoLbl->setFileName( m_fname );
01557 }
01558
01559 #include "kexicsvimportdialog.moc"