1
/* This file is part of the KDE project
2
Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
4
This work is based on kspread/dialogs/kspread_dlg_csv.cc
5
and will be merged back with KOffice libraries.
7
Copyright (C) 2002-2003 Norbert Andres <nandres@web.de>
8
(C) 2002-2003 Ariya Hidayat <ariya@kde.org>
9
(C) 2002 Laurent Montel <montel@kde.org>
10
(C) 1999 David Faure <faure@kde.org>
12
This library is free software; you can redistribute it and/or
13
modify it under the terms of the GNU Library General Public
14
License as published by the Free Software Foundation; either
15
version 2 of the License, or (at your option) any later version.
17
This library is distributed in the hope that it will be useful,
18
but WITHOUT ANY WARRANTY; without even the implied warranty of
19
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
Library General Public License for more details.
22
You should have received a copy of the GNU Library General Public License
23
along with this library; see the file COPYING.LIB. If not, write to
24
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25
* Boston, MA 02110-1301, USA.
28
#include <qbuttongroup.h>
29
#include <qcheckbox.h>
30
#include <qclipboard.h>
32
#include <qlineedit.h>
34
#include <qpushbutton.h>
35
#include <qradiobutton.h>
38
#include <qfiledialog.h>
40
#include <qtextcodec.h>
42
#include <qfontmetrics.h>
44
#include <kapplication.h>
46
#include <kdialogbase.h>
47
#include <kfiledialog.h>
49
#include <kmessagebox.h>
50
#include <kglobalsettings.h>
51
#include <kiconloader.h>
52
#include <kcharsets.h>
53
#include <knuminput.h>
54
#include <kprogress.h>
56
#include <kexiutils/identifier.h>
57
#include <kexiutils/utils.h>
58
#include <core/kexi.h>
59
#include <core/kexiproject.h>
60
#include <core/kexipart.h>
61
#include <core/kexipartinfo.h>
62
#include <core/keximainwindow.h>
63
#include <core/kexiguimsghandler.h>
64
#include <kexidb/connection.h>
65
#include <kexidb/tableschema.h>
66
#include <kexidb/transaction.h>
67
#include <widget/kexicharencodingcombobox.h>
69
#include "kexicsvimportdialog.h"
70
#include "kexicsvimportoptionsdlg.h"
71
#include "kexicsvwidgets.h"
74
#include <krecentdirs.h>
79
#include <kspread_cell.h>
80
#include <kspread_doc.h>
81
#include <kspread_sheet.h>
82
#include <kspread_undo.h>
83
#include <kspread_view.h>
86
#define _IMPORT_ICON "table" /*todo: change to "file_import" or so*/
88
#define _NUMBER_TYPE 1
91
#define _DATETIME_TYPE 4
95
#define _NO_TYPE_YET -1 //allows to accept a number of empty cells, before something non-empty
96
#define _FP_NUMBER_TYPE 255 //_NUMBER_TYPE variant
97
#define MAX_COLUMNS 100 //max 100 columns is reasonable
99
class KexiCSVImportDialogTable : public QTable
102
KexiCSVImportDialogTable( QWidget * parent = 0, const char * name = 0 )
103
: QTable(parent, name) {
107
virtual void paintCell( QPainter * p, int row, int col, const QRect & cr, bool selected, const QColorGroup & cg ) {
112
QTable::paintCell(p, row, col, cr, selected, cg);
114
virtual void setColumnWidth( int col, int w ) {
115
//make columns a bit wider
116
QTable::setColumnWidth( col, w + 16 );
121
//! Helper used to temporary disable keyboard and mouse events
122
void installRecursiveEventFilter(QObject *filter, QObject *object)
124
object->installEventFilter(filter);
126
if (!object->children())
129
QObjectList list = *object->children();
130
for(QObject *obj = list.first(); obj; obj = list.next())
131
installRecursiveEventFilter(filter, obj);
134
KexiCSVImportDialog::KexiCSVImportDialog( Mode mode, KexiMainWindow* mainWin,
135
QWidget * parent, const char * name
139
i18n( "Import CSV Data File" )
140
//! @todo use "Paste CSV Data From Clipboard" caption for mode==Clipboard
142
(mode==File ? User1 : (ButtonCode)0) |Ok|Cancel,
145
name ? name : "KexiCSVImportDialog",
148
KGuiItem( i18n("&Options"))
151
m_cancelled( false ),
154
m_textquote( QString(KEXICSV_DEFAULT_TEXT_QUOTE)[0] ),
156
m_prevSelectedCol(-1),
157
m_columnsAdjusted(false),
158
m_1stRowForFieldNamesDetected(false),
159
m_firstFillTableCall(true),
160
m_blockUserEvents(false),
161
m_primaryKeyColumn(-1),
162
m_dialogCancelled(false),
164
m_destinationTableSchema(0)
166
setWFlags(getWFlags() | Qt::WStyle_Maximize | Qt::WStyle_SysMenu);
168
setButtonOK(KGuiItem( i18n("&Import..."), _IMPORT_ICON));
170
m_typeNames.resize(5);
171
m_typeNames[0] = i18n("text");
172
m_typeNames[1] = i18n("number");
173
m_typeNames[2] = i18n("date");
174
m_typeNames[3] = i18n("time");
175
m_typeNames[4] = i18n("date/time");
177
kapp->config()->setGroup("ImportExport");
178
m_maximumRowsForPreview = kapp->config()->readNumEntry("MaximumRowsForPreviewInImportDialog", MAX_COLUMNS);
180
m_pkIcon = SmallIcon("key");
182
m_uniquenessTest.setAutoDelete(true);
184
setIcon(DesktopIcon(_IMPORT_ICON));
185
setSizeGripEnabled( TRUE );
187
m_encoding = QString::fromLatin1(KGlobal::locale()->encoding());
191
QVBoxLayout *lyr = new QVBoxLayout(plainPage(), 0, KDialogBase::spacingHint(), "lyr");
193
m_infoLbl = new KexiCSVInfoLabel(
194
m_mode==File ? i18n("Preview of data from file:")
195
: i18n("Preview of data from clipboard:"),
198
lyr->addWidget( m_infoLbl );
200
QWidget* page = new QFrame( plainPage(), "page" );
201
QGridLayout *glyr= new QGridLayout( page, 4, 5, 0, KDialogBase::spacingHint(), "glyr");
202
lyr->addWidget( page );
204
// Delimiter: comma, semicolon, tab, space, other
205
m_delimiterWidget = new KexiCSVDelimiterWidget(true /*lineEditOnBottom*/, page);
206
glyr->addMultiCellWidget( m_delimiterWidget, 1, 2, 0, 0 );
208
QLabel *delimiterLabel = new QLabel(m_delimiterWidget, i18n("Delimiter:"), page);
209
delimiterLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
210
glyr->addMultiCellWidget( delimiterLabel, 0, 0, 0, 0 );
212
// Format: number, text, currency,
213
m_formatComboText = i18n( "Format for column %1:" );
214
m_formatCombo = new KComboBox(page, "m_formatCombo");
215
m_formatCombo->insertItem(i18n("Text"));
216
m_formatCombo->insertItem(i18n("Number"));
217
m_formatCombo->insertItem(i18n("Date"));
218
m_formatCombo->insertItem(i18n("Time"));
219
m_formatCombo->insertItem(i18n("Date/Time"));
220
glyr->addMultiCellWidget( m_formatCombo, 1, 1, 1, 1 );
222
m_formatLabel = new QLabel(m_formatCombo, "", page);
223
m_formatLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
224
glyr->addWidget( m_formatLabel, 0, 1 );
226
m_primaryKeyField = new QCheckBox( i18n( "Primary key" ), page, "m_primaryKeyField" );
227
glyr->addWidget( m_primaryKeyField, 2, 1 );
228
connect(m_primaryKeyField, SIGNAL(toggled(bool)), this, SLOT(slotPrimaryKeyFieldToggled(bool)));
230
m_comboQuote = new KexiCSVTextQuoteComboBox( page );
231
glyr->addWidget( m_comboQuote, 1, 2 );
233
TextLabel2 = new QLabel( m_comboQuote, i18n( "Text quote:" ), page, "TextLabel2" );
234
TextLabel2->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
235
TextLabel2->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
236
glyr->addWidget( TextLabel2, 0, 2 );
238
m_startAtLineSpinBox = new KIntSpinBox( page, "m_startAtLineSpinBox" );
239
m_startAtLineSpinBox->setMinValue(1);
240
m_startAtLineSpinBox->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
241
m_startAtLineSpinBox->setMinimumWidth(QFontMetrics(m_startAtLineSpinBox->font()).width("8888888"));
242
glyr->addWidget( m_startAtLineSpinBox, 1, 3 );
244
m_startAtLineLabel = new QLabel( m_startAtLineSpinBox, "",
245
page, "TextLabel3" );
246
m_startAtLineLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred );
247
m_startAtLineLabel->setAlignment(Qt::AlignAuto | Qt::AlignBottom);
248
glyr->addWidget( m_startAtLineLabel, 0, 3 );
250
QSpacerItem* spacer_2 = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred );
251
glyr->addItem( spacer_2, 0, 4 );
253
m_ignoreDuplicates = new QCheckBox( page, "m_ignoreDuplicates" );
254
m_ignoreDuplicates->setText( i18n( "Ignore duplicated delimiters" ) );
255
glyr->addMultiCellWidget( m_ignoreDuplicates, 2, 2, 2, 4 );
257
m_1stRowForFieldNames = new QCheckBox( page, "m_1stRowForFieldNames" );
258
m_1stRowForFieldNames->setText( i18n( "First row contains column names" ) );
259
glyr->addMultiCellWidget( m_1stRowForFieldNames, 3, 3, 2, 4 );
261
m_table = new KexiCSVImportDialogTable( plainPage(), "m_table" );
262
lyr->addWidget( m_table );
264
m_table->setSizePolicy( QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1) );
265
m_table->setNumRows( 0 );
266
m_table->setNumCols( 0 );
268
/** @todo reuse Clipboard too! */
270
if ( m_mode == Clipboard )
272
setCaption( i18n( "Inserting From Clipboard" ) );
273
QMimeSource * mime = QApplication::clipboard()->data();
276
KMessageBox::information( this, i18n("There is no data in the clipboard.") );
281
if ( !mime->provides( "text/plain" ) )
283
KMessageBox::information( this, i18n("There is no usable data in the clipboard.") );
287
m_fileArray = QByteArray(mime->encodedData( "text/plain" ) );
289
else if ( mode == File )
291
m_dateRegExp1 = QRegExp("\\d{1,4}[/\\-\\.]\\d{1,2}[/\\-\\.]\\d{1,2}");
292
m_dateRegExp2 = QRegExp("\\d{1,2}[/\\-\\.]\\d{1,2}[/\\-\\.]\\d{1,4}");
293
m_timeRegExp1 = QRegExp("\\d{1,2}:\\d{1,2}:\\d{1,2}");
294
m_timeRegExp2 = QRegExp("\\d{1,2}:\\d{1,2}");
295
m_fpNumberRegExp = QRegExp("\\d*[,\\.]\\d+");
297
if (m_mode == File) {
298
QStringList mimetypes( csvMimeTypes() );
301
QString recentDir = KGlobalSettings::documentPath();
302
m_fname = QFileDialog::getOpenFileName(
303
KFileDialog::getStartURL(":CSVImportExport", recentDir).path(),
304
KexiUtils::fileDialogFilterStrings(mimetypes, false),
305
page, "KexiCSVImportDialog", i18n("Open CSV Data File"));
306
if ( !m_fname.isEmpty() ) {
307
//save last visited path
309
url.setPath( m_fname );
310
if (url.isLocalFile())
311
KRecentDirs::add(":CSVImportExport", url.directory());
314
m_fname = KFileDialog::getOpenFileName(":CSVImportExport", mimetypes.join(" "), this);
317
if ( m_fname.isEmpty() )
319
actionButton( Ok )->setEnabled( false );
322
parentWidget()->raise();
326
else if (m_mode == Clipboard) {
327
QCString subtype("plain");
328
m_data = QApplication::clipboard()->text(subtype, QClipboard::Clipboard);
330
for (int i=0;QApplication::clipboard()->data(QClipboard::Clipboard)->format(i);i++)
331
kdDebug() << i << ": "
332
<< QApplication::clipboard()->data(QClipboard::Clipboard)->format(i) << endl;
339
m_loadingProgressDlg = 0;
340
m_importingProgressDlg = 0;
341
if (m_mode == File) {
342
m_loadingProgressDlg = new KProgressDialog(
343
this, "m_loadingProgressDlg", i18n("Loading CSV Data"), i18n("Loading CSV Data from \"%1\"...")
344
.arg(QDir::convertSeparators(m_fname)), true);
345
m_loadingProgressDlg->progressBar()->setTotalSteps( m_maximumRowsForPreview+1 );
346
m_loadingProgressDlg->show();
349
if (m_mode==Clipboard) {
350
m_infoLbl->setIcon("editpaste");
352
updateRowCountInfo();
354
m_table->setSelectionMode(QTable::NoSelection);
356
connect(m_formatCombo, SIGNAL(activated(int)),
357
this, SLOT(formatChanged(int)));
358
connect(m_delimiterWidget, SIGNAL(delimiterChanged(const QString&)),
359
this, SLOT(delimiterChanged(const QString&)));
360
connect(m_startAtLineSpinBox, SIGNAL(valueChanged ( int )),
361
this, SLOT(startlineSelected(int)));
362
connect(m_comboQuote, SIGNAL(activated(int)),
363
this, SLOT(textquoteSelected(int)));
364
connect(m_table, SIGNAL(currentChanged(int, int)),
365
this, SLOT(currentCellChanged(int, int)));
366
connect(m_table, SIGNAL(valueChanged(int,int)),
367
this, SLOT(cellValueChanged(int,int)));
368
connect(m_ignoreDuplicates, SIGNAL(stateChanged(int)),
369
this, SLOT(ignoreDuplicatesChanged(int)));
370
connect(m_1stRowForFieldNames, SIGNAL(stateChanged(int)),
371
this, SLOT(slot1stRowForFieldNamesChanged(int)));
373
connect(this, SIGNAL(user1Clicked()), this, SLOT(optionsButtonClicked()));
375
installRecursiveEventFilter(this, this);
380
KexiCSVImportDialog::~KexiCSVImportDialog()
385
void KexiCSVImportDialog::initLater()
390
QChar detectedDelimiter;
391
if (m_mode==File) { //only detect for File mode
392
// try to detect delimiter
393
// \t has priority, then , then ;
394
for (uint i=0; i < QMIN(4096, m_data.length()); i++) {
395
const QChar c(m_data[i]);
397
detectedDelimiter = c;
400
else if (c==',' && detectedDelimiter!='\t') {
401
detectedDelimiter = c;
403
else if (c==';' && detectedDelimiter!='\t' && detectedDelimiter!=',') {
404
detectedDelimiter = c;
408
if (detectedDelimiter.isNull())
409
detectedDelimiter = m_mode==File
410
? KEXICSV_DEFAULT_FILE_DELIMITER[0] : KEXICSV_DEFAULT_CLIPBOARD_DELIMITER[0]; //<-- defaults
412
m_delimiterWidget->setDelimiter(QString(detectedDelimiter));
413
// delimiterChanged(detectedDelimiter); // this will cause fillTable()
414
m_columnsAdjusted = false;
416
delete m_loadingProgressDlg;
417
m_loadingProgressDlg = 0;
418
if (m_dialogCancelled) {
419
// m_loadingProgressDlg->hide();
420
// m_loadingProgressDlg->close();
421
QTimer::singleShot(0, this, SLOT(reject()));
425
currentCellChanged(0, 0);
429
KDialog::centerOnScreen( this );
431
if (m_loadingProgressDlg)
432
m_loadingProgressDlg->hide();
437
bool KexiCSVImportDialog::openData()
439
if (m_mode!=File) //data already loaded, no encoding stuff needed
442
delete m_inputStream;
448
m_file = new QFile(m_fname);
449
if (!m_file->open(IO_ReadOnly))
454
KMessageBox::sorry( this, i18n("Cannot open input file <nobr>\"%1\"</nobr>.")
455
.arg(QDir::convertSeparators(m_fname)) );
456
actionButton( Ok )->setEnabled( false );
459
parentWidget()->raise();
465
bool KexiCSVImportDialog::cancelled() const
470
void KexiCSVImportDialog::fillTable()
472
KexiUtils::WaitCursor wc(true);
474
m_blockUserEvents = true;
475
QPushButton *pb = actionButton(KDialogBase::Cancel);
477
pb->setEnabled(true); //allow to cancel
478
KexiUtils::WaitCursor wait;
480
if (m_table->numRows()>0) //to accept editor
481
m_table->setCurrentCell(0,0);
483
int row, column, maxColumn;
484
QString field = QString::null;
486
for (row = 0; row < m_table->numRows(); ++row)
487
for (column = 0; column < m_table->numCols(); ++column)
488
m_table->clearCell(row, column);
490
m_detectedTypes.clear();
491
m_detectedTypes.resize(1024, _NO_TYPE_YET);//_TEXT_TYPE);
492
m_uniquenessTest.clear();
493
m_uniquenessTest.resize(1024);
494
m_1stRowForFieldNamesDetected = true;
496
if (true != loadRows(field, row, column, maxColumn, true))
499
m_1stRowForFieldNamesDetected = false;
501
// file with only one line without '\n'
502
if (field.length() > 0)
504
setText(row - m_startline, column, field, true);
506
field = QString::null;
509
adjustRows( row - m_startline - (m_1stRowForFieldNames->isChecked()?1:0) );
511
maxColumn = QMAX( maxColumn, column );
512
m_table->setNumCols(maxColumn);
514
for (column = 0; column < m_table->numCols(); ++column)
516
// QString header = m_table->horizontalHeader()->label(column);
517
// if (header != i18n("Text") && header != i18n("Number") &&
518
// header != i18n("Date") && header != i18n("Currency"))
519
// const int detectedType = m_detectedTypes[column+1];
520
// m_table->horizontalHeader()->setLabel(column, m_typeNames[ detectedType ]); //i18n("Text"));
521
updateColumnText(column);
522
if (!m_columnsAdjusted)
523
m_table->adjustColumn(column);
525
m_columnsAdjusted = true;
527
if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
528
if (_NUMBER_TYPE != m_detectedTypes[ m_primaryKeyColumn ]) {
529
m_primaryKeyColumn = -1;
533
m_prevSelectedCol = -1;
534
m_table->setCurrentCell(0,0);
535
currentCellChanged(0, 0);
536
if (m_primaryKeyColumn != -1)
537
m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
539
const int count = QMAX(0, m_table->numRows()-1+m_startline);
540
const bool allRowsLoaded = count < m_maximumRowsForPreview;
542
m_startAtLineSpinBox->setMaxValue(count);
543
m_startAtLineSpinBox->setValue(m_startline+1);
545
m_startAtLineLabel->setText(i18n( "Start at line%1:").arg(
546
allRowsLoaded ? QString(" (1-%1)").arg(count)
547
: QString::null //we do not know what's real count
550
m_blockUserEvents = false;
552
m_table->verticalScrollBar()->repaint();//avoid missing repaint
553
m_table->horizontalScrollBar()->repaint();//avoid missing repaint
556
tristate KexiCSVImportDialog::loadRows(QString &field, int &row, int &column, int &maxColumn,
559
enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
560
S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
561
const QChar delimiter(m_delimiterWidget->delimiter()[0]);
562
field = QString::null;
563
const bool ignoreDups = m_ignoreDuplicates->isChecked();
564
bool lastCharDelimiter = false;
565
bool nextRow = false;
569
delete m_inputStream;
570
if ( m_mode == Clipboard ) {
571
m_inputStream = new QTextStream(m_data, IO_ReadOnly);
574
m_file->at(0); //always seek at 0 because loadRows() is called many times
575
m_inputStream = new QTextStream(m_file);
576
if (m_encoding != QString::fromLatin1(KGlobal::locale()->encoding())) {
577
QTextCodec *codec = KGlobal::charsets()->codecForName(m_encoding);
579
m_inputStream->setCodec(codec); //QTextCodec::codecForName("CP1250"));
582
int progressStep = 0;
583
if (m_importingProgressDlg)
584
progressStep = QMAX( 1, m_importingProgressDlg->progressBar()->totalSteps()/200 );
586
for (;!m_inputStream->atEnd(); offset++)
588
if (column >= m_maximumRowsForPreview)
591
if (m_importingProgressDlg && ((offset % progressStep) < 5)) {
592
//update progr. bar dlg on final exporting
593
m_importingProgressDlg->progressBar()->setValue(offset);
594
qApp->processEvents();
595
if (m_importingProgressDlg->wasCancelled()) {
596
delete m_importingProgressDlg;
597
m_importingProgressDlg = 0;
602
(*m_inputStream) >> x; // read one char
605
continue; // eat '\r', to handle RFC-compliant files
611
if (x == m_textquote)
613
state = S_QUOTED_FIELD;
615
else if (x == delimiter)
617
setText(row - m_startline, column, field, inGUI);
618
field = QString::null;
619
if ((ignoreDups == false) || (lastCharDelimiter == false))
621
lastCharDelimiter = true;
626
//fill remaining empty fields (database wants them explicity)
627
for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
628
setText(row - m_startline, additionalColumn, QString::null, inGUI);
632
maxColumn = QMAX( maxColumn, column );
638
state = S_MAYBE_NORMAL_FIELD;
641
case S_QUOTED_FIELD :
642
if (x == m_textquote)
644
state = S_MAYBE_END_OF_QUOTED_FIELD;
646
/*allow \n inside quoted fields
649
setText(row - m_startline, column, field, inGUI);
654
maxColumn = QMAX( maxColumn, column );
659
if ((ignoreDups == false) || (lastCharDelimiter == false))
661
lastCharDelimiter = true;
670
case S_MAYBE_END_OF_QUOTED_FIELD :
671
if (x == m_textquote)
673
field += x; //no, this was just escaped quote character
674
state = S_QUOTED_FIELD;
676
else if (x == delimiter || x == '\n')
678
setText(row - m_startline, column, field, inGUI);
679
field = QString::null;
683
maxColumn = QMAX( maxColumn, column );
688
if ((ignoreDups == false) || (lastCharDelimiter == false))
690
lastCharDelimiter = true;
696
state = S_END_OF_QUOTED_FIELD;
699
case S_END_OF_QUOTED_FIELD :
700
if (x == delimiter || x == '\n')
702
setText(row - m_startline, column, field, inGUI);
703
field = QString::null;
707
maxColumn = QMAX( maxColumn, column );
712
if ((ignoreDups == false) || (lastCharDelimiter == false))
714
lastCharDelimiter = true;
720
state = S_END_OF_QUOTED_FIELD;
723
case S_MAYBE_NORMAL_FIELD :
724
if (x == m_textquote)
726
field = QString::null;
727
state = S_QUOTED_FIELD;
730
case S_NORMAL_FIELD :
731
if (x == delimiter || x == '\n')
733
setText(row - m_startline, column, field, inGUI);
734
field = QString::null;
738
maxColumn = QMAX( maxColumn, column );
743
if ((ignoreDups == false) || (lastCharDelimiter == false))
745
lastCharDelimiter = true;
755
lastCharDelimiter = false;
759
if (!inGUI && row==1 && m_1stRowForFieldNames->isChecked()) {
760
// do not save to the database 1st row if it contains column names
761
m_importingStatement->clearArguments();
763
else if (!saveRow(inGUI))
768
if (m_firstFillTableCall && row==2
769
&& !m_1stRowForFieldNames->isChecked() && m_1stRowForFieldNamesDetected)
771
//'1st row for field name' flag detected: reload table
772
m_1stRowForFieldNamesDetected = false;
773
m_table->setNumRows( 0 );
774
m_firstFillTableCall = false; //this trick is allowed only once, on startup
775
m_1stRowForFieldNames->setChecked(true); //this will reload table
776
//slot1stRowForFieldNamesChanged(1);
777
m_blockUserEvents = false;
782
if (!m_importingProgressDlg && row % 20 == 0) {
783
qApp->processEvents();
785
if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCancelled()) {
786
delete m_loadingProgressDlg;
787
m_loadingProgressDlg = 0;
788
m_dialogCancelled = true;
794
if (!m_firstFillTableCall && m_loadingProgressDlg) {
795
m_loadingProgressDlg->progressBar()->setValue(QMIN(m_maximumRowsForPreview, row));
798
if ( inGUI && row > (m_maximumRowsForPreview + (m_1stRowForFieldNamesDetected?1:0)) ) {
799
kexipluginsdbg << "KexiCSVImportDialog::fillTable() loading stopped at row #"
800
<< m_maximumRowsForPreview << endl;
807
void KexiCSVImportDialog::updateColumnText(int col)
810
if (col<(int)m_columnNames.count() && (m_1stRowForFieldNames->isChecked() || m_changedColumnNames[col]))
811
colName = m_columnNames[ col ];
812
if (colName.isEmpty()) {
813
colName = i18n("Column %1").arg(col+1); //will be changed to a valid identifier on import
814
m_changedColumnNames[ col ] = false;
816
int detectedType = m_detectedTypes[col];
817
if (detectedType==_FP_NUMBER_TYPE)
818
detectedType=_NUMBER_TYPE; //we're simplifying that for now
819
else if (detectedType==_NO_TYPE_YET) {
820
m_detectedTypes[col]=_TEXT_TYPE; //entirely empty column
821
detectedType=_TEXT_TYPE;
823
m_table->horizontalHeader()->setLabel(col,
824
i18n("Column %1").arg(col+1) + " \n(" + m_typeNames[ detectedType ] + ") ");
825
m_table->setText(0, col, colName);
826
m_table->horizontalHeader()->adjustHeaderSize();
829
QValueList<int> *list = m_uniquenessTest[col];
830
if (m_primaryKeyColumn==-1 && list && !list->isEmpty()) {
832
QValueList<int>::ConstIterator it=list->constBegin();
835
for(; it!=list->constEnd() && prevValue!=(*it); ++it)
837
if (it!=list->constEnd()) {
842
//a candidate for PK (autodetected)!
843
if (-1==m_primaryKeyColumn) {
844
m_primaryKeyColumn=col;
848
if (list) //not needed now: conserve memory
852
void KexiCSVImportDialog::detectTypeAndUniqueness(int row, int col, const QString& text)
855
const int type = m_detectedTypes[col];
856
if (row==1 || type!=_TEXT_TYPE) {
858
if (text.isEmpty() && type==_NO_TYPE_YET)
859
found = true; //real type should be found later
860
//detect type because it's 1st row or all prev. rows were not text
862
if (!found && (row==1 || type==_NUMBER_TYPE || type==_NO_TYPE_YET)) {
863
bool ok = text.isEmpty();//empty values allowed
865
intValue = text.toInt(&ok);
866
if (ok && (row==1 || type==_NO_TYPE_YET)) {
867
m_detectedTypes[col]=_NUMBER_TYPE;
872
if (!found && (row==1 || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
873
bool ok = text.isEmpty() || m_fpNumberRegExp.exactMatch(text);
876
if (ok && (row==1 || type==_NO_TYPE_YET)) {
877
m_detectedTypes[col]=_FP_NUMBER_TYPE;
882
if (!found && (row==1 || type==_DATE_TYPE || type==_NO_TYPE_YET)) {
883
if ((row==1 || type==_NO_TYPE_YET)
884
&& (text.isEmpty() || m_dateRegExp1.exactMatch(text) || m_dateRegExp2.exactMatch(text)))
886
m_detectedTypes[col]=_DATE_TYPE;
891
if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
892
if ((row==1 || type==_NO_TYPE_YET)
893
&& (text.isEmpty() || m_timeRegExp1.exactMatch(text) || m_timeRegExp2.exactMatch(text)))
895
m_detectedTypes[col]=_TIME_TYPE;
900
if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
901
if (row==1 || type==_NO_TYPE_YET) {
902
bool detected = text.isEmpty();
904
const QStringList dateTimeList( QStringList::split(" ", text) );
905
bool ok = dateTimeList.count()>=2;
906
//! @todo also support ISODateTime's "T" separator?
907
//! @todo also support timezones?
909
//try all combinations
910
QString datePart( dateTimeList[0].stripWhiteSpace() );
911
QString timePart( dateTimeList[1].stripWhiteSpace() );
912
ok = (m_dateRegExp1.exactMatch(datePart) || m_dateRegExp2.exactMatch(datePart))
913
&& (m_timeRegExp1.exactMatch(timePart) || m_timeRegExp2.exactMatch(timePart));
918
m_detectedTypes[col]=_DATETIME_TYPE;
923
if (!found && type==_NO_TYPE_YET && !text.isEmpty()) {
924
//eventually, a non-emptytext after a while
925
m_detectedTypes[col]=_TEXT_TYPE;
928
//default: text type (already set)
930
//check uniqueness for this value
931
QValueList<int> *list = m_uniquenessTest[col];
932
if (row==1 && (!list || !list->isEmpty()) && !text.isEmpty() && _NUMBER_TYPE == m_detectedTypes[col]) {
934
list = new QValueList<int>();
935
m_uniquenessTest.insert(col, list);
937
list->append( intValue );
940
//the value is empty or uniqueness test failed in the past
941
if (list && !list->isEmpty())
942
list->clear(); //indicate that uniqueness test failed
946
void KexiCSVImportDialog::setText(int row, int col, const QString& text, bool inGUI)
949
//save text directly to database buffer
950
if (col==1) { //1st col
951
m_importingStatement->clearArguments();
952
if (m_implicitPrimaryKeyAdded)
953
*m_importingStatement << QVariant(); //id will be autogenerated here
955
const int detectedType = m_detectedTypes[col-1];
956
if (detectedType==_NUMBER_TYPE) {
957
*m_importingStatement << ( text.isEmpty() ? QVariant() : text.toInt() );
958
//! @todo what about time and float/double types and different integer subtypes?
960
else if (detectedType==_FP_NUMBER_TYPE) {
961
*m_importingStatement << ( text.isEmpty() ? QVariant() : text.toDouble() );
963
else if (detectedType==_DATE_TYPE) {
964
QDate date( QDate::fromString(text, Qt::ISODate) ); //same as m_dateRegExp1
965
if (!date.isValid() && m_dateRegExp2.exactMatch(text)) //dd-mm-yyyy
966
date = QDate(text.mid(6,4).toInt(), text.mid(3,2).toInt(), text.left(2).toInt());
967
*m_importingStatement << date;
969
else if (detectedType==_TIME_TYPE) {
970
QTime time( QTime::fromString(text, Qt::ISODate) ); //same as m_timeRegExp1
971
if (!time.isValid() && m_timeRegExp2.exactMatch(text)) //hh:mm:ss
972
time = QTime(text.left(2).toInt(), text.mid(3,2).toInt(), text.mid(6,2).toInt());
973
*m_importingStatement << time;
975
else if (detectedType==_DATETIME_TYPE) {
976
const QStringList dateTimeList( QStringList::split(" ", text) );
977
//! @todo also support ISODateTime's "T" separator?
978
//! @todo also support timezones?
979
if (dateTimeList.count()>=2) {
980
//try all combinations
981
QString datePart( dateTimeList[0].stripWhiteSpace() );
982
QString timePart( dateTimeList[1].stripWhiteSpace() );
984
dateTime.setDate( QDate::fromString(datePart, Qt::ISODate) ); //same as m_dateRegExp1
985
if (!dateTime.date().isValid() && m_dateRegExp2.exactMatch(datePart)) //dd-mm-yyyy
986
dateTime.setDate( QDate(datePart.mid(6,4).toInt(),
987
datePart.mid(3,2).toInt(), datePart.left(2).toInt()) );
988
dateTime.setTime( QTime::fromString(timePart, Qt::ISODate) ); //same as m_timeRegExp1
989
if (!dateTime.time().isValid() && m_timeRegExp2.exactMatch(timePart)) //hh:mm:ss
990
dateTime.setTime( QTime(timePart.left(2).toInt(),
991
timePart.mid(3,2).toInt(), timePart.mid(6,2).toInt()) );
992
*m_importingStatement << dateTime;
995
else //_TEXT_TYPE and the rest
996
*m_importingStatement << text;
999
//save text to GUI (table view)
1000
if (m_table->numCols() < col) {
1001
m_table->setNumCols(col);
1002
if ((int)m_columnNames.size() < m_table->numCols()) {
1003
m_columnNames.resize(m_table->numCols()+10);
1004
m_changedColumnNames.resize(m_table->numCols()+10);
1008
if (m_1stRowForFieldNames->isChecked()) {
1009
if ((row+m_startline)==1) {//this is for column name
1010
if ((col-1) < (int)m_changedColumnNames.size() && false==m_changedColumnNames[col-1]) {
1011
//this column has no custom name entered by a user
1012
//-get the name from the data cell
1013
QString colName(text.simplifyWhiteSpace());
1014
if (!colName.isEmpty()) {
1015
if (colName.left(1)>="0" && colName.left(1)<="9")
1016
colName.prepend(i18n("Column")+" ");
1017
m_columnNames[ col-1 ] = colName;
1024
if ((row+m_startline)==1) {//this row is for column name
1025
if (m_1stRowForFieldNamesDetected && !m_1stRowForFieldNames->isChecked()) {
1026
QString f( text.simplifyWhiteSpace() );
1027
if (f.isEmpty() || !f[0].isLetter())
1028
m_1stRowForFieldNamesDetected = false; //this couldn't be a column name
1031
row++; //1st row was for column names
1034
if (row < 2) // skipped by the user
1037
if (m_table->numRows() < row) {
1038
// if (m_maximumRowsForPreview >= row+100)
1039
m_table->setNumRows(row+100); /* We add more rows at a time to limit recalculations */
1041
// m_table->setNumRows(m_maximumRowsForPreview);
1042
m_table->verticalHeader()->setLabel(0, i18n("Column name")+" ");
1046
m_table->setText(row - 1, col - 1, text);
1047
m_table->verticalHeader()->setLabel(row-1, QString::number(row-1));
1049
detectTypeAndUniqueness(row-1, col-1, text);
1052
bool KexiCSVImportDialog::saveRow(bool inGUI)
1059
bool res = m_importingStatement->execute();
1061
m_importingStatement->clearArguments();
1063
// return m_conn->insertRecord(*m_destinationTableSchema, m_dbRowBuffer);
1066
void KexiCSVImportDialog::adjustRows(int iRows)
1070
m_table->setNumRows( iRows );
1072
for (int i = 0; i<iRows; i++)
1073
m_table->adjustRow(i);
1077
void KexiCSVImportDialog::formatChanged(int id)
1080
if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
1081
m_table->setPixmap(0, m_primaryKeyColumn, QPixmap());
1083
if (m_primaryKeyField->isChecked()) {
1084
m_primaryKeyColumn = m_table->currentColumn();
1085
m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
1088
m_primaryKeyColumn = -1;
1092
m_detectedTypes[m_table->currentColumn()]=id;
1093
m_primaryKeyField->setEnabled( _NUMBER_TYPE == id );
1094
m_primaryKeyField->setChecked( m_primaryKeyColumn == m_table->currentColumn() && m_primaryKeyField->isEnabled() );
1096
updateColumnText(m_table->currentColumn());
1099
void KexiCSVImportDialog::delimiterChanged(const QString& delimiter)
1101
Q_UNUSED(delimiter);
1102
m_columnsAdjusted = false;
1103
//delayed, otherwise combobox won't be repainted
1104
QTimer::singleShot(10, this, SLOT(fillTable()));
1107
void KexiCSVImportDialog::textquoteSelected(int)
1109
const QString tq(m_comboQuote->textQuote());
1113
m_textquote = tq[0];
1115
//delayed, otherwise combobox won't be repainted
1116
QTimer::singleShot(10, this, SLOT(fillTable()));
1119
void KexiCSVImportDialog::startlineSelected(int startline)
1121
// const int startline = line.toInt() - 1;
1122
if (m_startline == (startline-1))
1124
m_startline = startline-1;
1127
m_table->setFocus();
1130
void KexiCSVImportDialog::currentCellChanged(int, int col)
1132
if (m_prevSelectedCol==col)
1134
m_prevSelectedCol = col;
1135
int type = m_detectedTypes[col];
1136
if (type==_FP_NUMBER_TYPE)
1137
type=_NUMBER_TYPE; //we're simplifying that for now
1139
m_formatCombo->setCurrentItem( type );
1140
m_formatLabel->setText( m_formatComboText.arg(col+1) );
1141
m_primaryKeyField->setEnabled( _NUMBER_TYPE == m_detectedTypes[col]);
1142
m_primaryKeyField->blockSignals(true); //block to disable executing slotPrimaryKeyFieldToggled()
1143
m_primaryKeyField->setChecked( m_primaryKeyColumn == col );
1144
m_primaryKeyField->blockSignals(false);
1147
void KexiCSVImportDialog::cellValueChanged(int row,int col)
1149
if (row==0) {//column name has changed
1150
m_columnNames[ col ] = m_table->text(row, col);
1151
m_changedColumnNames.setBit( col );
1155
void KexiCSVImportDialog::accept()
1157
//! @todo MOVE MOST OF THIS TO CORE/ (KexiProject?) after KexiDialogBase code is moved to non-gui place
1159
KexiGUIMessageHandler msg; //! @todo make it better integrated with main window
1161
const uint numRows( m_table->numRows() );
1163
return; //impossible
1166
if (KMessageBox::No == KMessageBox::questionYesNo(this,
1167
i18n("Data set contains no rows. Do you want to import empty table?")))
1171
KexiProject* project = m_mainWin->project();
1173
msg.showErrorMessage(i18n("No project available."));
1176
m_conn = project->dbConnection(); //cache this pointer
1178
msg.showErrorMessage(i18n("No database connection available."));
1181
KexiPart::Part *part = Kexi::partManager().partForMimeType("kexi/table");
1183
msg.showErrorMessage(&Kexi::partManager());
1187
//get suggested name based on the file name
1188
QString suggestedName;
1190
suggestedName = KURL::fromPathOrURL(m_fname).fileName();
1192
if (!suggestedName.isEmpty()) {
1193
const int idx = suggestedName.findRev(".");
1195
suggestedName = suggestedName.mid(0, idx ).simplifyWhiteSpace();
1200
KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), suggestedName);
1201
if (!partItemForSavedTable) {
1202
// msg.showErrorMessage(project);
1207
{ project->deleteUnstoredItem(partItemForSavedTable); \
1209
delete m_destinationTableSchema; \
1210
m_destinationTableSchema = 0; \
1213
//-ask for table name/title
1214
// (THIS IS FROM KexiMainWindowImpl::saveObject())
1215
bool allowOverwriting = true;
1216
tristate res = m_mainWin->getNewObjectInfo( partItemForSavedTable, part, allowOverwriting );
1221
//(allowOverwriting is now set to true, if user accepts overwriting,
1222
// and overwriting will be needed)
1224
// KexiDB::SchemaData sdata(part->info()->projectPartID());
1225
// sdata.setName( partItem->name() );
1227
//-create table schema (and thus schema object)
1228
//-assign information (THIS IS FROM KexiDialogBase::storeNewData())
1229
m_destinationTableSchema = new KexiDB::TableSchema(partItemForSavedTable->name());
1230
m_destinationTableSchema->setCaption( partItemForSavedTable->caption() );
1231
m_destinationTableSchema->setDescription( partItemForSavedTable->description() );
1232
const uint numCols( m_table->numCols() );
1234
m_implicitPrimaryKeyAdded = false;
1235
//add PK if user wanted it
1237
if (m_primaryKeyColumn==-1
1238
&& KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(this,
1239
i18n("No Primary Key (autonumber) has been defined.\n"
1240
"Should it be automatically defined on import (recommended)?\n\n"
1241
"Note: An imported table without a Primary Key may not be editable (depending on database type)."),
1242
QString::null, KGuiItem(i18n("Add Database Primary Key to a Table", "Add Primary Key"), "key"),
1243
KGuiItem(i18n("Do Not Add Database Primary Key to a Table", "Do Not Add")))))
1245
if (msgboxResult == KMessageBox::Cancel)
1246
_ERR; //cancel accepting
1248
//add implicit PK field
1249
//! @todo make this field hidden (what about e.g. pgsql?)
1250
m_implicitPrimaryKeyAdded = true;
1252
QString fieldName("id");
1253
QString fieldCaption("Id");
1255
QStringList colnames;
1256
for (uint col = 0; col < numCols; col++)
1257
colnames.append( m_table->text(0, col).lower().simplifyWhiteSpace() );
1259
if (colnames.find(fieldName)!=colnames.end()) {
1261
while (colnames.find(fieldName+QString::number(num))!=colnames.end())
1263
fieldName += QString::number(num);
1264
fieldCaption += QString::number(num);
1266
KexiDB::Field *field = new KexiDB::Field(
1268
KexiDB::Field::Integer,
1269
KexiDB::Field::NoConstraints,
1270
KexiDB::Field::NoOptions,
1271
0,0, //uint length=0, uint precision=0,
1272
QVariant(), //QVariant defaultValue=QVariant(),
1274
); //no description and width for now
1275
field->setPrimaryKey(true);
1276
field->setAutoIncrement(true);
1277
m_destinationTableSchema->addField( field );
1280
for (uint col = 0; col < numCols; col++) {
1281
QString fieldCaption( m_table->text(0, col).simplifyWhiteSpace() );
1282
QString fieldName( KexiUtils::string2Identifier( fieldCaption ) );
1283
if (m_destinationTableSchema->field(fieldName)) {
1284
QString fixedFieldName;
1285
uint i = 2; //"apple 2, apple 3, etc. if there're many "apple" names
1287
fixedFieldName = fieldName + "_" + QString::number(i);
1288
if (!m_destinationTableSchema->field(fixedFieldName))
1292
fieldName = fixedFieldName;
1293
fieldCaption += (" " + QString::number(i));
1295
const int detectedType = m_detectedTypes[col];
1296
KexiDB::Field::Type fieldType;
1297
if (detectedType==_DATE_TYPE)
1298
fieldType = KexiDB::Field::Date;
1299
if (detectedType==_TIME_TYPE)
1300
fieldType = KexiDB::Field::Time;
1301
if (detectedType==_DATETIME_TYPE)
1302
fieldType = KexiDB::Field::DateTime;
1303
else if (detectedType==_NUMBER_TYPE)
1304
fieldType = KexiDB::Field::Integer;
1305
else if (detectedType==_FP_NUMBER_TYPE)
1306
fieldType = KexiDB::Field::Double;
1307
//! @todo what about time and float/double types and different integer subtypes?
1308
else //_TEXT_TYPE and the rest
1309
fieldType = KexiDB::Field::Text;
1310
//! @todo what about long text?
1312
KexiDB::Field *field = new KexiDB::Field(
1315
KexiDB::Field::NoConstraints,
1316
KexiDB::Field::NoOptions,
1317
0,0, //uint length=0, uint precision=0,
1318
QVariant(), //QVariant defaultValue=QVariant(),
1320
); //no description and width for now
1322
if ((int)col == m_primaryKeyColumn) {
1323
field->setPrimaryKey(true);
1324
field->setAutoIncrement(true);
1326
m_destinationTableSchema->addField( field );
1329
KexiDB::Transaction transaction = m_conn->beginTransaction();
1330
if (transaction.isNull()) {
1331
msg.showErrorMessage(m_conn);
1334
KexiDB::TransactionGuard tg(transaction);
1336
//-create physical table
1337
if (!m_conn->createTable(m_destinationTableSchema, allowOverwriting)) {
1338
msg.showErrorMessage(m_conn);
1342
#define _DROP_DEST_TABLE_AND_RETURN \
1344
if (m_importingProgressDlg) \
1345
m_importingProgressDlg->hide(); \
1346
project->deleteUnstoredItem(partItemForSavedTable); \
1347
m_conn->dropTable(m_destinationTableSchema); /*alsoRemoveSchema*/ \
1348
m_destinationTableSchema = 0; \
1353
m_importingStatement = m_conn->prepareStatement(
1354
KexiDB::PreparedStatement::InsertStatement, *m_destinationTableSchema);
1355
if (!m_importingStatement) {
1356
msg.showErrorMessage(m_conn);
1357
_DROP_DEST_TABLE_AND_RETURN;
1361
if (!m_importingProgressDlg) {
1362
m_importingProgressDlg = new KProgressDialog( this, "m_importingProgressDlg",
1363
i18n("Importing CSV Data"), QString::null, true );
1365
m_importingProgressDlg->setLabel(
1366
i18n("Importing CSV Data from <nobr>\"%1\"</nobr> into \"%2\" table...")
1367
.arg(QDir::convertSeparators(m_fname)).arg(m_destinationTableSchema->name()) );
1368
m_importingProgressDlg->progressBar()->setTotalSteps( QFileInfo(*m_file).size() );
1369
m_importingProgressDlg->show();
1372
int row, column, maxColumn;
1373
QString field = QString::null;
1376
res = loadRows(field, row, column, maxColumn, false /*!gui*/ );
1378
delete m_importingProgressDlg;
1379
m_importingProgressDlg = 0;
1381
//importing cancelled or failed
1382
if (!res) //do not display err msg when res == cancelled
1383
msg.showErrorMessage(m_conn);
1384
_DROP_DEST_TABLE_AND_RETURN;
1387
// file with only one line without '\n'
1388
if (field.length() > 0)
1390
setText(row - m_startline, column, field, false /*!gui*/);
1391
//fill remaining empty fields (database wants them explicity)
1392
for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
1393
setText(row - m_startline, additionalColumn, QString::null, false /*!gui*/);
1395
if (!saveRow(false /*!gui*/)) {
1396
msg.showErrorMessage(m_conn);
1397
_DROP_DEST_TABLE_AND_RETURN;
1400
field = QString::null;
1404
msg.showErrorMessage(m_conn);
1405
_DROP_DEST_TABLE_AND_RETURN;
1408
//-now we can store the item
1409
partItemForSavedTable->setIdentifier( m_destinationTableSchema->id() );
1410
project->addStoredItem( part->info(), partItemForSavedTable );
1413
KMessageBox::information(this, i18n("Data has been successfully imported to table \"%1\".")
1414
.arg(m_destinationTableSchema->name()));
1415
parentWidget()->raise();
1419
int KexiCSVImportDialog::getHeader(int col)
1421
QString header = m_table->horizontalHeader()->label(col);
1423
if (header == i18n("Text type for column", "Text"))
1425
else if (header == i18n("Numeric type for column", "Number"))
1427
else if (header == i18n("Currency type for column", "Currency"))
1433
QString KexiCSVImportDialog::getText(int row, int col)
1435
return m_table->text(row, col);
1438
void KexiCSVImportDialog::ignoreDuplicatesChanged(int)
1443
void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(int)
1446
if (m_1stRowForFieldNames->isChecked() && m_startline>0 && m_startline>=(m_startAtLineSpinBox->maxValue()-1))
1451
void KexiCSVImportDialog::optionsButtonClicked()
1453
KexiCSVImportOptionsDialog dlg(m_encoding, this);
1454
if (QDialog::Accepted != dlg.exec())
1457
if (m_encoding != dlg.encodingComboBox()->selectedEncoding()) {
1458
m_encoding = dlg.encodingComboBox()->selectedEncoding();
1465
bool KexiCSVImportDialog::eventFilter ( QObject * watched, QEvent * e )
1467
QEvent::Type t = e->type();
1468
// temporary disable keyboard and mouse events for time-consuming tasks
1469
if (m_blockUserEvents && (t==QEvent::KeyPress || t==QEvent::KeyRelease
1470
|| t==QEvent::MouseButtonPress || t==QEvent::MouseButtonDblClick
1471
|| t==QEvent::Paint ))
1474
if (watched == m_startAtLineSpinBox && t==QEvent::KeyPress) {
1475
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
1476
if (ke->key()==Key_Enter || ke->key()==Key_Return) {
1477
m_table->setFocus();
1481
return QDialog::eventFilter( watched, e );
1484
void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(bool on)
1487
formatChanged(_PK_FLAG);
1490
void KexiCSVImportDialog::updateRowCountInfo()
1492
//! @todo infoLbl->setFileName( m_fname + " " + i18n("row count", "(rows: %1)").arg(10);
1493
m_infoLbl->setFileName( m_fname );
1496
#include "kexicsvimportdialog.moc"