28#include <tqbuttongroup.h>
29#include <tqcheckbox.h>
30#include <tqclipboard.h>
32#include <tqlineedit.h>
34#include <tqpushbutton.h>
35#include <tqradiobutton.h>
38#include <tqfiledialog.h>
40#include <tqtextcodec.h>
42#include <tqfontmetrics.h>
45#include <tdeapplication.h>
47#include <kdialogbase.h>
48#include <tdefiledialog.h>
50#include <tdemessagebox.h>
51#include <tdeglobalsettings.h>
52#include <kiconloader.h>
56#include <kactivelabel.h>
58#include <kexiutils/identifier.h>
59#include <kexiutils/utils.h>
61#include <core/kexiproject.h>
62#include <core/kexipart.h>
63#include <core/kexipartinfo.h>
64#include <core/keximainwindow.h>
65#include <core/kexiguimsghandler.h>
66#include <kexidb/connection.h>
67#include <kexidb/tableschema.h>
68#include <kexidb/transaction.h>
69#include <widget/kexicharencodingcombobox.h>
71#include "kexicsvimportdialog.h"
72#include "kexicsvwidgets.h"
75#include <tderecentdirs.h>
80#include <kspread_cell.h>
81#include <kspread_doc.h>
82#include <kspread_sheet.h>
83#include <kspread_undo.h>
84#include <kspread_view.h>
87#define _IMPORT_ICON "table"
92#define _DATETIME_TYPE 4
96#define _NO_TYPE_YET -1
97#define _FP_NUMBER_TYPE 255
98#define MAX_ROWS_TO_PREVIEW 100
99#define MAX_BYTES_TO_PREVIEW 10240
100#define MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER 4096
102class KexiCSVImportDialogTable :
public TQTable
105 KexiCSVImportDialogTable( TQWidget * parent = 0,
const char * name = 0 )
106 : TQTable(parent, name) {
110 virtual void paintCell( TQPainter * p,
int row,
int col,
const TQRect & cr,
bool selected,
const TQColorGroup & cg ) {
115 TQTable::paintCell(p, row, col, cr, selected, cg);
117 virtual void setColumnWidth(
int col,
int w ) {
119 TQTable::setColumnWidth( col, w + 16 );
127 object->installEventFilter(filter);
129 TQObjectList clo =
object->childrenListObject();
134 TQObjectList list = clo;
135 for(TQObject *obj = list.first(); obj; obj = list.next())
140 TQWidget * parent,
const char * name
144 i18n(
"Import CSV Data File" )
147 (mode==File ? User1 : (ButtonCode)0) |Ok|Cancel,
150 name ? name :
"KexiCSVImportDialog",
153 KGuiItem( i18n(
"&Options"))
156 m_cancelled( false ),
157 m_adjustRows( true ),
159 m_textquote( TQString(KEXICSV_DEFAULT_FILE_TEXT_QUOTE)[0] ),
161 m_prevSelectedCol(-1),
162 m_columnsAdjusted(false),
163 m_1stRowForFieldNamesDetected(false),
164 m_firstFillTableCall(true),
165 m_blockUserEvents(false),
166 m_primaryKeyColumn(-1),
167 m_dialogCancelled(false),
169 m_destinationTableSchema(0),
170 m_allRowsLoadedInPreview(false),
171 m_stoppedAt_MAX_BYTES_TO_PREVIEW(false)
173 setWFlags(getWFlags() | TQt::WStyle_Maximize | TQt::WStyle_SysMenu);
175 setButtonOK(KGuiItem( i18n(
"&Import..."), _IMPORT_ICON));
177 m_typeNames.resize(5);
178 m_typeNames[0] = i18n(
"text");
179 m_typeNames[1] = i18n(
"number");
180 m_typeNames[2] = i18n(
"date");
181 m_typeNames[3] = i18n(
"time");
182 m_typeNames[4] = i18n(
"date/time");
184 kapp->config()->setGroup(
"ImportExport");
185 m_maximumRowsForPreview = kapp->config()->readNumEntry(
"MaximumRowsForPreviewInImportDialog", MAX_ROWS_TO_PREVIEW);
186 m_maximumBytesForPreview = kapp->config()->readNumEntry(
"MaximumBytesForPreviewInImportDialog", MAX_BYTES_TO_PREVIEW);
188 m_pkIcon = SmallIcon(
"key");
190 m_uniquenessTest.setAutoDelete(
true);
192 setIcon(DesktopIcon(_IMPORT_ICON));
193 setSizeGripEnabled(
true );
200 TQVBoxLayout *lyr =
new TQVBoxLayout(plainPage(), 0, KDialogBase::spacingHint(),
"lyr");
203 m_mode==File ? i18n(
"Preview of data from file:")
204 : i18n(
"Preview of data from clipboard:"),
207 lyr->addWidget( m_infoLbl );
209 TQWidget* page =
new TQFrame( plainPage(),
"page" );
210 TQGridLayout *glyr=
new TQGridLayout( page, 4, 5, 0, KDialogBase::spacingHint(),
"glyr");
211 lyr->addWidget( page );
215 m_detectDelimiter =
true;
216 glyr->addMultiCellWidget( m_delimiterWidget, 1, 2, 0, 0 );
218 TQLabel *delimiterLabel =
new TQLabel(m_delimiterWidget, i18n(
"Delimiter:"), page);
219 delimiterLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
220 glyr->addMultiCellWidget( delimiterLabel, 0, 0, 0, 0 );
223 m_formatComboText = i18n(
"Format for column %1:" );
224 m_formatCombo =
new KComboBox(page,
"m_formatCombo");
225 m_formatCombo->insertItem(i18n(
"Text"));
226 m_formatCombo->insertItem(i18n(
"Number"));
227 m_formatCombo->insertItem(i18n(
"Date"));
228 m_formatCombo->insertItem(i18n(
"Time"));
229 m_formatCombo->insertItem(i18n(
"Date/Time"));
230 glyr->addMultiCellWidget( m_formatCombo, 1, 1, 1, 1 );
232 m_formatLabel =
new TQLabel(m_formatCombo,
"", page);
233 m_formatLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
234 glyr->addWidget( m_formatLabel, 0, 1 );
236 m_primaryKeyField =
new TQCheckBox( i18n(
"Primary key" ), page,
"m_primaryKeyField" );
237 glyr->addWidget( m_primaryKeyField, 2, 1 );
238 connect(m_primaryKeyField, TQ_SIGNAL(toggled(
bool)),
this, TQ_SLOT(slotPrimaryKeyFieldToggled(
bool)));
241 glyr->addWidget( m_comboQuote, 1, 2 );
243 TextLabel2 =
new TQLabel( m_comboQuote, i18n(
"Text quote:" ), page,
"TextLabel2" );
244 TextLabel2->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
245 TextLabel2->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
246 glyr->addWidget( TextLabel2, 0, 2 );
248 m_startAtLineSpinBox =
new KIntSpinBox( page,
"m_startAtLineSpinBox" );
249 m_startAtLineSpinBox->setMinValue(1);
250 m_startAtLineSpinBox->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed );
251 m_startAtLineSpinBox->setMinimumWidth(TQFontMetrics(m_startAtLineSpinBox->font()).width(
"8888888"));
252 glyr->addWidget( m_startAtLineSpinBox, 1, 3 );
254 m_startAtLineLabel =
new TQLabel( m_startAtLineSpinBox,
"",
255 page,
"TextLabel3" );
256 m_startAtLineLabel->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
257 m_startAtLineLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
258 glyr->addWidget( m_startAtLineLabel, 0, 3 );
260 TQSpacerItem* spacer_2 =
new TQSpacerItem( 0, 0, TQSizePolicy::Minimum, TQSizePolicy::Preferred );
261 glyr->addItem( spacer_2, 0, 4 );
263 m_ignoreDuplicates =
new TQCheckBox( page,
"m_ignoreDuplicates" );
264 m_ignoreDuplicates->setText( i18n(
"Ignore duplicated delimiters" ) );
265 glyr->addMultiCellWidget( m_ignoreDuplicates, 2, 2, 2, 4 );
267 m_1stRowForFieldNames =
new TQCheckBox( page,
"m_1stRowForFieldNames" );
268 m_1stRowForFieldNames->setText( i18n(
"First row contains column names" ) );
269 glyr->addMultiCellWidget( m_1stRowForFieldNames, 3, 3, 2, 4 );
271 m_table =
new KexiCSVImportDialogTable( plainPage(),
"m_table" );
272 lyr->addWidget( m_table );
274 m_table->setSizePolicy( TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding, 1, 1) );
275 m_table->setNumRows( 0 );
276 m_table->setNumCols( 0 );
301 m_dateRegExp = TQRegExp(
"(\\d{1,4})([/\\-\\.])(\\d{1,2})([/\\-\\.])(\\d{1,4})");
302 m_timeRegExp1 = TQRegExp(
"(\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
303 m_timeRegExp2 = TQRegExp(
"(\\d{1,2}):(\\d{1,2})");
304 m_fpNumberRegExp = TQRegExp(
"[\\-]{0,1}\\d*[,\\.]\\d+");
305 TQString caption( i18n(
"Open CSV Data File") );
307 if (m_mode == File) {
308 TQStringList mimetypes( csvMimeTypes() );
311 TQString recentDir = TDEGlobalSettings::documentPath();
312 m_fname = TQFileDialog::getOpenFileName(
313 KFileDialog::getStartURL(
":CSVImportExport", recentDir).path(),
315 page,
"KexiCSVImportDialog", caption);
316 if ( !m_fname.isEmpty() ) {
319 url.setPath( m_fname );
320 if (url.isLocalFile())
321 TDERecentDirs::add(
":CSVImportExport", url.directory());
324 m_fname = KFileDialog::getOpenFileName(
":CSVImportExport", mimetypes.join(
" "),
328 if ( m_fname.isEmpty() )
330 actionButton( Ok )->setEnabled(
false );
333 parentWidget()->raise();
337 else if (m_mode == Clipboard) {
338 TQCString subtype(
"plain");
339 m_clipboardData = TQApplication::clipboard()->text(subtype, TQClipboard::Clipboard);
350 m_loadingProgressDlg = 0;
351 m_importingProgressDlg = 0;
352 if (m_mode == File) {
353 m_loadingProgressDlg =
new KProgressDialog(
354 this,
"m_loadingProgressDlg", i18n(
"Loading CSV Data"), i18n(
"Loading CSV Data from \"%1\"...")
355 .arg(TQDir::convertSeparators(m_fname)),
true);
356 m_loadingProgressDlg->progressBar()->setTotalSteps( m_maximumRowsForPreview+1 );
357 m_loadingProgressDlg->show();
360 if (m_mode==Clipboard) {
361 m_infoLbl->
setIcon(
"edit-paste");
365 m_table->setSelectionMode(TQTable::NoSelection);
367 connect(m_formatCombo, TQ_SIGNAL(activated(
int)),
368 this, TQ_SLOT(formatChanged(
int)));
369 connect(m_delimiterWidget, TQ_SIGNAL(delimiterChanged(
const TQString&)),
370 this, TQ_SLOT(delimiterChanged(
const TQString&)));
371 connect(m_startAtLineSpinBox, TQ_SIGNAL(valueChanged (
int )),
372 this, TQ_SLOT(startlineSelected(
int)));
373 connect(m_comboQuote, TQ_SIGNAL(activated(
int)),
374 this, TQ_SLOT(textquoteSelected(
int)));
375 connect(m_table, TQ_SIGNAL(currentChanged(
int,
int)),
376 this, TQ_SLOT(currentCellChanged(
int,
int)));
377 connect(m_table, TQ_SIGNAL(valueChanged(
int,
int)),
378 this, TQ_SLOT(cellValueChanged(
int,
int)));
379 connect(m_ignoreDuplicates, TQ_SIGNAL(stateChanged(
int)),
380 this, TQ_SLOT(ignoreDuplicatesChanged(
int)));
381 connect(m_1stRowForFieldNames, TQ_SIGNAL(stateChanged(
int)),
382 this, TQ_SLOT(slot1stRowForFieldNamesChanged(
int)));
384 connect(
this, TQ_SIGNAL(user1Clicked()),
this, TQ_SLOT(optionsButtonClicked()));
386 installRecursiveEventFilter(
this,
this);
391KexiCSVImportDialog::~KexiCSVImportDialog()
396void KexiCSVImportDialog::initLater()
402 m_columnsAdjusted =
false;
404 delete m_loadingProgressDlg;
405 m_loadingProgressDlg = 0;
406 if (m_dialogCancelled) {
409 TQTimer::singleShot(0,
this, TQ_SLOT(reject()));
413 currentCellChanged(0, 0);
417 KDialog::centerOnScreen(
this );
419 if (m_loadingProgressDlg)
420 m_loadingProgressDlg->hide();
425bool KexiCSVImportDialog::openData()
430 delete m_inputStream;
436 m_file =
new TQFile(m_fname);
437 if (!m_file->open(IO_ReadOnly))
442 KMessageBox::sorry(
this, i18n(
"Cannot open input file <nobr>\"%1\"</nobr>.")
443 .arg(TQDir::convertSeparators(m_fname)) );
444 actionButton( Ok )->setEnabled(
false );
447 parentWidget()->raise();
453bool KexiCSVImportDialog::cancelled()
const
458void KexiCSVImportDialog::fillTable()
462 m_blockUserEvents =
true;
463 TQPushButton *pb = actionButton(KDialogBase::Cancel);
465 pb->setEnabled(
true);
468 if (m_table->numRows()>0)
469 m_table->setCurrentCell(0,0);
471 int row, column, maxColumn;
472 TQString field = TQString();
474 for (row = 0; row < m_table->numRows(); ++row)
475 for (column = 0; column < m_table->numCols(); ++column)
476 m_table->clearCell(row, column);
478 m_detectedTypes.clear();
479 m_detectedTypes.resize(1024, _NO_TYPE_YET);
480 m_uniquenessTest.clear();
481 m_uniquenessTest.resize(1024);
482 m_1stRowForFieldNamesDetected =
true;
484 if (
true != loadRows(field, row, column, maxColumn,
true))
487 m_1stRowForFieldNamesDetected =
false;
490 if (field.length() > 0)
492 setText(row - m_startline, column, field,
true);
497 adjustRows( row - m_startline - (m_1stRowForFieldNames->isChecked()?1:0) );
499 maxColumn = TQMAX( maxColumn, column );
500 m_table->setNumCols(maxColumn);
502 for (column = 0; column < m_table->numCols(); ++column)
509 updateColumnText(column);
510 if (!m_columnsAdjusted)
511 m_table->adjustColumn(column);
513 m_columnsAdjusted =
true;
515 if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
516 if (_NUMBER_TYPE != m_detectedTypes[ m_primaryKeyColumn ]) {
517 m_primaryKeyColumn = -1;
521 m_prevSelectedCol = -1;
522 m_table->setCurrentCell(0,0);
523 currentCellChanged(0, 0);
524 if (m_primaryKeyColumn != -1)
525 m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
527 const int count = TQMAX(0, m_table->numRows()-1+m_startline);
528 m_allRowsLoadedInPreview = count < m_maximumRowsForPreview && !m_stoppedAt_MAX_BYTES_TO_PREVIEW;
529 if (m_allRowsLoadedInPreview) {
530 m_startAtLineSpinBox->setMaxValue(count);
531 m_startAtLineSpinBox->setValue(m_startline+1);
533 m_startAtLineLabel->setText(i18n(
"Start at line%1:").arg(
534 m_allRowsLoadedInPreview ? TQString(
" (1-%1)").arg(count)
537 updateRowCountInfo();
539 m_blockUserEvents =
false;
541 m_table->verticalScrollBar()->repaint();
542 m_table->horizontalScrollBar()->repaint();
545TQString KexiCSVImportDialog::detectDelimiterByLookingAtFirstBytesOfFile(TQTextStream& inputStream)
551 const TQIODevice::Offset origOffset = inputStream.device()->at();
552 TQChar c, prevChar=0;
553 int detectedDelimiter = 0;
554 bool insideQuote =
false;
557 const int CH_TAB_AFTER_QUOTE = 500;
558 const int CH_SEMICOLON_AFTER_QUOTE = 499;
559 const int CH_COMMA_AFTER_QUOTE = 498;
560 const int CH_TAB = 200;
561 const int CH_SEMICOLON = 199;
562 const int CH_COMMA = 198;
564 TQValueList<int> tabsPerLine, semicolonsPerLine, commasPerLine;
565 int tabs = 0, semicolons = 0, commas = 0;
567 for (uint i=0; !inputStream.atEnd() && i < MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER; i++) {
568 (*m_inputStream) >> c;
571 insideQuote = !insideQuote;
583 semicolonsPerLine += semicolons;
585 commasPerLine += commas;
591 detectedDelimiter = TQMAX( prevChar==
'"' ? CH_TAB_AFTER_QUOTE : CH_TAB, detectedDelimiter );
595 detectedDelimiter = TQMAX( prevChar==
'"' ? CH_SEMICOLON_AFTER_QUOTE : CH_SEMICOLON, detectedDelimiter );
599 detectedDelimiter = TQMAX( prevChar==
'"' ? CH_COMMA_AFTER_QUOTE : CH_COMMA, detectedDelimiter );
604 inputStream.device()->at(origOffset);
608 TQValueList<int>::ConstIterator it;
609 if (tabsPerLine.count()>1) {
610 tabs = tabsPerLine.isEmpty() ? 0 : tabsPerLine.first();
611 for (it=tabsPerLine.constBegin(); it!=tabsPerLine.constEnd(); ++it) {
615 if (tabs>0 && it==tabsPerLine.constEnd())
618 if (semicolonsPerLine.count()>1) {
619 semicolons = semicolonsPerLine.isEmpty() ? 0 : semicolonsPerLine.first();
620 for (it=semicolonsPerLine.constBegin(); it!=semicolonsPerLine.constEnd(); ++it) {
621 if (semicolons != *it)
624 if (semicolons > 0 && it==semicolonsPerLine.constEnd())
627 if (commasPerLine.count()>1) {
628 commas = commasPerLine.first();
629 for (it=commasPerLine.constBegin(); it!=commasPerLine.constEnd(); ++it) {
633 if (commas > 0 && it==commasPerLine.constEnd())
637 if (detectedDelimiter == CH_TAB_AFTER_QUOTE || detectedDelimiter == CH_TAB)
639 if (detectedDelimiter == CH_SEMICOLON_AFTER_QUOTE || detectedDelimiter == CH_SEMICOLON)
641 if (detectedDelimiter == CH_COMMA_AFTER_QUOTE || detectedDelimiter == CH_COMMA)
644 return KEXICSV_DEFAULT_FILE_DELIMITER;
647tristate KexiCSVImportDialog::loadRows(TQString &field,
int &row,
int &column,
int &maxColumn,
650 enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
651 S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
653 const bool ignoreDups = m_ignoreDuplicates->isChecked();
654 bool lastCharDelimiter =
false;
655 bool nextRow =
false;
659 const bool hadInputStream = m_inputStream!=0;
660 delete m_inputStream;
661 if ( m_mode == Clipboard ) {
662 m_inputStream =
new TQTextStream(m_clipboardData, IO_ReadOnly);
664 m_delimiterWidget->setDelimiter(KEXICSV_DEFAULT_CLIPBOARD_DELIMITER);
668 m_inputStream =
new TQTextStream(m_file);
669 if (m_options.defaultEncodingExplicitySet) {
670 TQTextCodec *codec = TDEGlobal::charsets()->codecForName(m_options.encoding);
672 m_inputStream->setCodec(codec);
674 if (m_detectDelimiter) {
675 const TQString delimiter( detectDelimiterByLookingAtFirstBytesOfFile(*m_inputStream) );
676 if (m_delimiterWidget->delimiter() != delimiter)
677 m_delimiterWidget->setDelimiter( delimiter );
680 const TQChar delimiter(m_delimiterWidget->delimiter()[0]);
681 m_stoppedAt_MAX_BYTES_TO_PREVIEW =
false;
682 int progressStep = 0;
683 if (m_importingProgressDlg)
684 progressStep = TQMAX( 1, m_importingProgressDlg->progressBar()->totalSteps()/200 );
686 for (;!m_inputStream->atEnd(); offset++)
692 if (m_importingProgressDlg && ((offset % progressStep) < 5)) {
694 m_importingProgressDlg->progressBar()->setValue(offset);
695 tqApp->processEvents();
696 if (m_importingProgressDlg->wasCancelled()) {
697 delete m_importingProgressDlg;
698 m_importingProgressDlg = 0;
703 (*m_inputStream) >> x;
708 if (offset==0 && x.unicode()==0xfeff) {
717 if (x == m_textquote)
719 state = S_QUOTED_FIELD;
721 else if (x == delimiter)
723 setText(row - m_startline, column, field, inGUI);
725 if ((ignoreDups ==
false) || (lastCharDelimiter ==
false))
727 lastCharDelimiter =
true;
733 for (
int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
734 setText(row - m_startline, additionalColumn, TQString(), inGUI);
738 maxColumn = TQMAX( maxColumn, column );
744 state = S_MAYBE_NORMAL_FIELD;
747 case S_QUOTED_FIELD :
748 if (x == m_textquote)
750 state = S_MAYBE_END_OF_QUOTED_FIELD;
776 case S_MAYBE_END_OF_QUOTED_FIELD :
777 if (x == m_textquote)
780 state = S_QUOTED_FIELD;
782 else if (x == delimiter || x ==
'\n')
784 setText(row - m_startline, column, field, inGUI);
789 maxColumn = TQMAX( maxColumn, column );
794 if ((ignoreDups ==
false) || (lastCharDelimiter ==
false))
796 lastCharDelimiter =
true;
802 state = S_END_OF_QUOTED_FIELD;
805 case S_END_OF_QUOTED_FIELD :
806 if (x == delimiter || x ==
'\n')
808 setText(row - m_startline, column, field, inGUI);
813 maxColumn = TQMAX( maxColumn, column );
818 if ((ignoreDups ==
false) || (lastCharDelimiter ==
false))
820 lastCharDelimiter =
true;
826 state = S_END_OF_QUOTED_FIELD;
829 case S_MAYBE_NORMAL_FIELD :
830 if (x == m_textquote)
833 state = S_QUOTED_FIELD;
836 case S_NORMAL_FIELD :
837 if (x == delimiter || x ==
'\n')
839 setText(row - m_startline, column, field, inGUI);
844 maxColumn = TQMAX( maxColumn, column );
849 if ((ignoreDups ==
false) || (lastCharDelimiter ==
false))
851 lastCharDelimiter =
true;
861 lastCharDelimiter =
false;
864 if (!inGUI && row==1 && m_1stRowForFieldNames->isChecked()) {
866 m_importingStatement->clearArguments();
868 else if (!saveRow(inGUI))
874 if (m_firstFillTableCall && row==2
875 && !m_1stRowForFieldNames->isChecked() && m_1stRowForFieldNamesDetected)
878 m_1stRowForFieldNamesDetected =
false;
879 m_table->setNumRows( 0 );
880 m_firstFillTableCall =
false;
881 m_1stRowForFieldNames->setChecked(
true);
883 m_blockUserEvents =
false;
888 if (!m_importingProgressDlg && row % 20 == 0) {
889 tqApp->processEvents();
891 if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCancelled()) {
892 delete m_loadingProgressDlg;
893 m_loadingProgressDlg = 0;
894 m_dialogCancelled =
true;
900 if (!m_firstFillTableCall && m_loadingProgressDlg) {
901 m_loadingProgressDlg->progressBar()->setValue(TQMIN(m_maximumRowsForPreview, row));
904 if ( inGUI && row > (m_maximumRowsForPreview + (m_1stRowForFieldNamesDetected?1:0)) ) {
905 kexipluginsdbg <<
"KexiCSVImportDialog::fillTable() loading stopped at row #"
906 << m_maximumRowsForPreview << endl;
912 kexipluginsdbg << offset << endl;
913 if (inGUI && offset >= m_maximumBytesForPreview && row >= 2) {
914 m_stoppedAt_MAX_BYTES_TO_PREVIEW =
true;
922void KexiCSVImportDialog::updateColumnText(
int col)
925 if (col<(
int)m_columnNames.count() && (m_1stRowForFieldNames->isChecked() || m_changedColumnNames[col]))
926 colName = m_columnNames[ col ];
927 if (colName.isEmpty()) {
928 colName = i18n(
"Column %1").arg(col+1);
929 m_changedColumnNames[ col ] =
false;
931 int detectedType = m_detectedTypes[col];
932 if (detectedType==_FP_NUMBER_TYPE)
933 detectedType=_NUMBER_TYPE;
934 else if (detectedType==_NO_TYPE_YET) {
935 m_detectedTypes[col]=_TEXT_TYPE;
936 detectedType=_TEXT_TYPE;
938 m_table->horizontalHeader()->setLabel(col,
939 i18n(
"Column %1").arg(col+1) +
" \n(" + m_typeNames[ detectedType ] +
") ");
940 m_table->setText(0, col, colName);
941 m_table->horizontalHeader()->adjustHeaderSize();
944 TQValueList<int> *list = m_uniquenessTest[col];
945 if (m_primaryKeyColumn==-1 && list && !list->isEmpty()) {
947 TQValueList<int>::ConstIterator it=list->constBegin();
950 for(; it!=list->constEnd() && prevValue!=(*it); ++it)
952 if (it!=list->constEnd()) {
958 if (-1==m_primaryKeyColumn) {
959 m_primaryKeyColumn=col;
967void KexiCSVImportDialog::detectTypeAndUniqueness(
int row,
int col,
const TQString& text)
970 const int type = m_detectedTypes[col];
971 if (row==1 || type!=_TEXT_TYPE) {
973 if (text.isEmpty() && type==_NO_TYPE_YET)
977 if (!found && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
978 bool ok = text.isEmpty() || m_fpNumberRegExp.exactMatch(text);
981 if (ok && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
982 m_detectedTypes[col]=_FP_NUMBER_TYPE;
987 if (!found && (row==1 || type==_NUMBER_TYPE || type==_NO_TYPE_YET)) {
988 bool ok = text.isEmpty();
990 intValue = text.toInt(&ok);
991 if (ok && (row==1 || type==_NO_TYPE_YET)) {
992 m_detectedTypes[col]=_NUMBER_TYPE;
997 if (!found && (row==1 || type==_DATE_TYPE || type==_NO_TYPE_YET)) {
998 if ((row==1 || type==_NO_TYPE_YET)
999 && (text.isEmpty() || m_dateRegExp.exactMatch(text)))
1001 m_detectedTypes[col]=_DATE_TYPE;
1006 if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
1007 if ((row==1 || type==_NO_TYPE_YET)
1008 && (text.isEmpty() || m_timeRegExp1.exactMatch(text) || m_timeRegExp2.exactMatch(text)))
1010 m_detectedTypes[col]=_TIME_TYPE;
1015 if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
1016 if (row==1 || type==_NO_TYPE_YET) {
1017 bool detected = text.isEmpty();
1019 const TQStringList dateTimeList( TQStringList::split(
" ", text) );
1020 bool ok = dateTimeList.count()>=2;
1025 TQString datePart( dateTimeList[0].stripWhiteSpace() );
1026 TQString timePart( dateTimeList[1].stripWhiteSpace() );
1027 ok = m_dateRegExp.exactMatch(datePart)
1028 && (m_timeRegExp1.exactMatch(timePart) || m_timeRegExp2.exactMatch(timePart));
1033 m_detectedTypes[col]=_DATETIME_TYPE;
1038 if (!found && type==_NO_TYPE_YET && !text.isEmpty()) {
1040 m_detectedTypes[col]=_TEXT_TYPE;
1046 TQValueList<int> *list = m_uniquenessTest[col];
1047 if (row==1 && (!list || !list->isEmpty()) && !text.isEmpty() && _NUMBER_TYPE == m_detectedTypes[col]) {
1049 list =
new TQValueList<int>();
1050 m_uniquenessTest.insert(col, list);
1052 list->append( intValue );
1056 if (list && !list->isEmpty())
1061bool KexiCSVImportDialog::parseDate(
const TQString& text, TQDate& date)
1063 if (!m_dateRegExp.exactMatch(text))
1067 const int d1 = m_dateRegExp.cap(1).toInt(), d3 = m_dateRegExp.cap(3).toInt(), d5 = m_dateRegExp.cap(5).toInt();
1068 if (m_dateRegExp.cap(2)==
"/")
1069 date = TQDate(d5, d1, d3);
1072 date = TQDate(d5, d3, d1);
1074 date = TQDate(d1, d3, d5);
1076 return date.isValid();
1079bool KexiCSVImportDialog::parseTime(
const TQString& text, TQTime& time)
1081 time = TQTime::fromString(text, TQt::ISODate);
1084 if (m_timeRegExp2.exactMatch(text)) {
1085 time = TQTime(m_timeRegExp2.cap(1).toInt(), m_timeRegExp2.cap(3).toInt(), m_timeRegExp2.cap(5).toInt());
1091void KexiCSVImportDialog::setText(
int row,
int col,
const TQString& text,
bool inGUI)
1096 m_importingStatement->clearArguments();
1097 if (m_implicitPrimaryKeyAdded)
1098 *m_importingStatement << TQVariant();
1100 const int detectedType = m_detectedTypes[col-1];
1101 if (detectedType==_NUMBER_TYPE) {
1102 *m_importingStatement << ( text.isEmpty() ? TQVariant() : text.toInt() );
1105 else if (detectedType==_FP_NUMBER_TYPE) {
1107 TQCString t(text.latin1());
1108 const int textLen = t.length();
1109 for (
int i=0; i<textLen; i++) {
1115 *m_importingStatement << ( t.isEmpty() ? TQVariant() : t.toDouble() );
1117 else if (detectedType==_DATE_TYPE) {
1119 if (parseDate(text, date))
1120 *m_importingStatement << date;
1122 else if (detectedType==_TIME_TYPE) {
1124 if (parseTime(text, time))
1125 *m_importingStatement << time;
1127 else if (detectedType==_DATETIME_TYPE) {
1128 TQStringList dateTimeList( TQStringList::split(
" ", text) );
1129 if (dateTimeList.count()<2)
1130 dateTimeList = TQStringList::split(
"T", text);
1132 if (dateTimeList.count()>=2) {
1133 TQString datePart( dateTimeList[0].stripWhiteSpace() );
1135 if (parseDate(datePart, date)) {
1136 TQString timePart( dateTimeList[1].stripWhiteSpace() );
1138 if (parseTime(timePart, time))
1139 *m_importingStatement << TQDateTime(date, time);
1144 *m_importingStatement << (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text);
1148 if (m_table->numCols() < col) {
1149 m_table->setNumCols(col);
1150 if ((
int)m_columnNames.size() < m_table->numCols()) {
1151 m_columnNames.resize(m_table->numCols()+10);
1152 m_changedColumnNames.resize(m_table->numCols()+10);
1156 if (m_1stRowForFieldNames->isChecked()) {
1157 if ((row+m_startline)==1) {
1158 if ((col-1) < (int)m_changedColumnNames.size() && (int)
false==(
int)m_changedColumnNames[col-1]) {
1161 TQString colName(text.simplifyWhiteSpace());
1162 if (!colName.isEmpty()) {
1163 if (colName.left(1)>=
"0" && colName.left(1)<=
"9")
1164 colName.prepend(i18n(
"Column")+
" ");
1165 m_columnNames[ col-1 ] = colName;
1172 if ((row+m_startline)==1) {
1173 if (m_1stRowForFieldNamesDetected && !m_1stRowForFieldNames->isChecked()) {
1174 TQString f( text.simplifyWhiteSpace() );
1175 if (f.isEmpty() || !f[0].isLetter())
1176 m_1stRowForFieldNamesDetected =
false;
1185 if (m_table->numRows() < row) {
1187 m_table->setNumRows(row+100);
1190 m_table->verticalHeader()->setLabel(0, i18n(
"Column name")+
" ");
1194 m_table->setText(row - 1, col - 1, (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text));
1195 m_table->verticalHeader()->setLabel(row-1, TQString::number(row-1));
1197 detectTypeAndUniqueness(row-1, col-1, text);
1200bool KexiCSVImportDialog::saveRow(
bool inGUI)
1207 bool res = m_importingStatement->execute();
1209 m_importingStatement->clearArguments();
1214void KexiCSVImportDialog::adjustRows(
int iRows)
1218 m_table->setNumRows( iRows );
1220 for (
int i = 0; i<iRows; i++)
1221 m_table->adjustRow(i);
1225void KexiCSVImportDialog::formatChanged(
int id)
1228 if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
1229 m_table->setPixmap(0, m_primaryKeyColumn, TQPixmap());
1231 if (m_primaryKeyField->isChecked()) {
1232 m_primaryKeyColumn = m_table->currentColumn();
1233 m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
1236 m_primaryKeyColumn = -1;
1240 m_detectedTypes[m_table->currentColumn()]=id;
1241 m_primaryKeyField->setEnabled( _NUMBER_TYPE ==
id );
1242 m_primaryKeyField->setChecked( m_primaryKeyColumn == m_table->currentColumn() && m_primaryKeyField->isEnabled() );
1244 updateColumnText(m_table->currentColumn());
1247void KexiCSVImportDialog::delimiterChanged(
const TQString& delimiter)
1249 Q_UNUSED(delimiter);
1250 m_columnsAdjusted =
false;
1251 m_detectDelimiter =
false;
1256void KexiCSVImportDialog::textquoteSelected(
int)
1258 const TQString tq(m_comboQuote->textQuote());
1262 m_textquote = tq[0];
1264 kexipluginsdbg <<
"KexiCSVImportDialog::textquoteSelected(): " << m_textquote << endl;
1270void KexiCSVImportDialog::fillTableLater()
1272 m_table->setNumRows( 0 );
1273 TQTimer::singleShot(10,
this, TQ_SLOT(fillTable()));
1276void KexiCSVImportDialog::startlineSelected(
int startline)
1279 if (m_startline == (startline-1))
1281 m_startline = startline-1;
1284 m_table->setFocus();
1287void KexiCSVImportDialog::currentCellChanged(
int,
int col)
1289 if (m_prevSelectedCol==col)
1291 m_prevSelectedCol = col;
1292 int type = m_detectedTypes[col];
1293 if (type==_FP_NUMBER_TYPE)
1296 m_formatCombo->setCurrentItem( type );
1297 m_formatLabel->setText( m_formatComboText.arg(col+1) );
1298 m_primaryKeyField->setEnabled( _NUMBER_TYPE == m_detectedTypes[col]);
1299 m_primaryKeyField->blockSignals(
true);
1300 m_primaryKeyField->setChecked( m_primaryKeyColumn == col );
1301 m_primaryKeyField->blockSignals(
false);
1304void KexiCSVImportDialog::cellValueChanged(
int row,
int col)
1307 m_columnNames[ col ] = m_table->text(row, col);
1308 m_changedColumnNames.setBit( col );
1316 KexiGUIMessageHandler msg;
1318 const uint numRows( m_table->numRows() );
1323 if (KMessageBox::No == KMessageBox::questionYesNo(
this,
1324 i18n(
"Data set contains no rows. Do you want to import empty table?")))
1330 msg.showErrorMessage(i18n(
"No project available."));
1335 msg.showErrorMessage(i18n(
"No database connection available."));
1345 TQString suggestedName;
1347 suggestedName = KURL::fromPathOrURL(m_fname).fileName();
1349 if (!suggestedName.isEmpty()) {
1350 const int idx = suggestedName.findRev(
".");
1352 suggestedName = suggestedName.mid(0, idx ).simplifyWhiteSpace();
1358 if (!partItemForSavedTable) {
1364 { project->deleteUnstoredItem(partItemForSavedTable); \
1366 delete m_destinationTableSchema; \
1367 m_destinationTableSchema = 0; \
1372 bool allowOverwriting =
true;
1387 m_destinationTableSchema->setCaption( partItemForSavedTable->caption() );
1388 m_destinationTableSchema->setDescription( partItemForSavedTable->description() );
1389 const uint numCols( m_table->numCols() );
1391 m_implicitPrimaryKeyAdded =
false;
1394 if (m_primaryKeyColumn==-1
1395 && KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(
this,
1396 i18n(
"No Primary Key (autonumber) has been defined.\n"
1397 "Should it be automatically defined on import (recommended)?\n\n"
1398 "Note: An imported table without a Primary Key may not be editable (depending on database type)."),
1399 TQString(), KGuiItem(i18n(
"Add Database Primary Key to a Table",
"Add Primary Key"),
"key"),
1400 KGuiItem(i18n(
"Do Not Add Database Primary Key to a Table",
"Do Not Add")))))
1402 if (msgboxResult == KMessageBox::Cancel)
1407 m_implicitPrimaryKeyAdded =
true;
1409 TQString fieldName(
"id");
1410 TQString fieldCaption(
"Id");
1412 TQStringList colnames;
1413 for (uint col = 0; col < numCols; col++)
1414 colnames.append( m_table->text(0, col).lower().simplifyWhiteSpace() );
1416 if (colnames.find(fieldName)!=colnames.end()) {
1418 while (colnames.find(fieldName+TQString::number(num))!=colnames.end())
1420 fieldName += TQString::number(num);
1421 fieldCaption += TQString::number(num);
1426 KexiDB::Field::NoConstraints,
1427 KexiDB::Field::NoOptions,
1432 field->setPrimaryKey(
true);
1433 field->setAutoIncrement(
true);
1434 m_destinationTableSchema->
addField( field );
1437 for (uint col = 0; col < numCols; col++) {
1438 TQString fieldCaption( m_table->text(0, col).simplifyWhiteSpace() );
1440 if (m_destinationTableSchema->
field(fieldName)) {
1441 TQString fixedFieldName;
1444 fixedFieldName = fieldName +
"_" + TQString::number(i);
1445 if (!m_destinationTableSchema->
field(fixedFieldName))
1449 fieldName = fixedFieldName;
1450 fieldCaption += (
" " + TQString::number(i));
1452 const int detectedType = m_detectedTypes[col];
1454 if (detectedType==_DATE_TYPE)
1455 fieldType = KexiDB::Field::Date;
1456 if (detectedType==_TIME_TYPE)
1457 fieldType = KexiDB::Field::Time;
1458 if (detectedType==_DATETIME_TYPE)
1459 fieldType = KexiDB::Field::DateTime;
1460 else if (detectedType==_NUMBER_TYPE)
1462 else if (detectedType==_FP_NUMBER_TYPE)
1472 KexiDB::Field::NoConstraints,
1473 KexiDB::Field::NoOptions,
1479 if ((
int)col == m_primaryKeyColumn) {
1480 field->setPrimaryKey(
true);
1481 field->setAutoIncrement(
true);
1483 m_destinationTableSchema->
addField( field );
1487 if (transaction.
isNull()) {
1488 msg.showErrorMessage(m_conn);
1494 if (!m_conn->
createTable(m_destinationTableSchema, allowOverwriting)) {
1495 msg.showErrorMessage(m_conn);
1499#define _DROP_DEST_TABLE_AND_RETURN \
1501 if (m_importingProgressDlg) \
1502 m_importingProgressDlg->hide(); \
1503 project->deleteUnstoredItem(partItemForSavedTable); \
1504 m_conn->dropTable(m_destinationTableSchema); \
1505 m_destinationTableSchema = 0; \
1512 if (!m_importingStatement) {
1513 msg.showErrorMessage(m_conn);
1514 _DROP_DEST_TABLE_AND_RETURN;
1518 if (!m_importingProgressDlg) {
1519 m_importingProgressDlg =
new KProgressDialog(
this,
"m_importingProgressDlg",
1520 i18n(
"Importing CSV Data"), TQString(),
true );
1522 m_importingProgressDlg->setLabel(
1523 i18n(
"Importing CSV Data from <nobr>\"%1\"</nobr> into \"%2\" table...")
1524 .arg(TQDir::convertSeparators(m_fname)).arg(m_destinationTableSchema->name()) );
1525 m_importingProgressDlg->progressBar()->setTotalSteps( TQFileInfo(*m_file).size() );
1526 m_importingProgressDlg->show();
1529 int row, column, maxColumn;
1530 TQString field = TQString();
1533 res = loadRows(field, row, column, maxColumn,
false );
1535 delete m_importingProgressDlg;
1536 m_importingProgressDlg = 0;
1540 msg.showErrorMessage(m_conn);
1541 _DROP_DEST_TABLE_AND_RETURN;
1545 if (field.length() > 0)
1547 setText(row - m_startline, column, field,
false );
1549 for (
int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
1550 setText(row - m_startline, additionalColumn, TQString(),
false );
1552 if (!saveRow(
false )) {
1553 msg.showErrorMessage(m_conn);
1554 _DROP_DEST_TABLE_AND_RETURN;
1561 msg.showErrorMessage(m_conn);
1562 _DROP_DEST_TABLE_AND_RETURN;
1566 partItemForSavedTable->setIdentifier( m_destinationTableSchema->id() );
1567 project->
addStoredItem( part->info(), partItemForSavedTable );
1570 KMessageBox::information(
this, i18n(
"Data has been successfully imported to table \"%1\".")
1571 .arg(m_destinationTableSchema->name()));
1572 parentWidget()->raise();
1576int KexiCSVImportDialog::getHeader(
int col)
1578 TQString header = m_table->horizontalHeader()->label(col);
1580 if (header == i18n(
"Text type for column",
"Text"))
1582 else if (header == i18n(
"Numeric type for column",
"Number"))
1584 else if (header == i18n(
"Currency type for column",
"Currency"))
1590TQString KexiCSVImportDialog::getText(
int row,
int col)
1592 return m_table->text(row, col);
1595void KexiCSVImportDialog::ignoreDuplicatesChanged(
int)
1600void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(
int)
1603 if (m_1stRowForFieldNames->isChecked() && m_startline>0 && m_startline>=(m_startAtLineSpinBox->maxValue()-1))
1608void KexiCSVImportDialog::optionsButtonClicked()
1611 if (TQDialog::Accepted != dlg.exec())
1615 if (m_options != newOptions) {
1616 m_options = newOptions;
1623bool KexiCSVImportDialog::eventFilter ( TQObject * watched, TQEvent * e )
1625 TQEvent::Type t = e->type();
1627 if (m_blockUserEvents && (t==TQEvent::KeyPress || t==TQEvent::KeyRelease
1628 || t==TQEvent::MouseButtonPress || t==TQEvent::MouseButtonDblClick
1629 || t==TQEvent::Paint ))
1632 if (watched == m_startAtLineSpinBox && t==TQEvent::KeyPress) {
1633 TQKeyEvent *ke =
static_cast<TQKeyEvent*
>(e);
1634 if (ke->key()==TQt::Key_Enter || ke->key()==TQt::Key_Return) {
1635 m_table->setFocus();
1639 return TQDialog::eventFilter( watched, e );
1642void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(
bool on)
1645 formatChanged(_PK_FLAG);
1648void KexiCSVImportDialog::updateRowCountInfo()
1650 m_infoLbl->setFileName( m_fname );
1651 if (m_allRowsLoadedInPreview) {
1652 m_infoLbl->setCommentText(
1653 i18n(
"row count",
"(rows: %1)").arg( m_table->numRows()-1+m_startline ) );
1654 TQToolTip::remove( m_infoLbl );
1657 m_infoLbl->setCommentText(
1658 i18n(
"row count",
"(rows: more than %1)").arg( m_table->numRows()-1+m_startline ) );
1659 TQToolTip::add( m_infoLbl->commentLabel(), i18n(
"Not all rows are visible on this preview") );
1663#include "kexicsvimportdialog.moc"
KexiCSVImportDialog(Mode mode, KexiMainWindow *mainWin, TQWidget *parent, const char *name=0)
A helper widget showing a short text information with an icon. See ctor for details....
void setIcon(const TQString &iconName)
sets icon pixmap to iconName. Used wher setIconForFilename was false in ctor.
A combo box widget providing a list of possible quote characters Used by CSV import and export dialog...
bool createTable(TableSchema *tableSchema, bool replaceExisting=false)
Creates a table according to the given schema.
virtual PreparedStatement::Ptr prepareStatement(PreparedStatement::StatementType type, FieldList &fields)=0
Transaction beginTransaction()
Creates new transaction handle and starts a new transaction.
FieldList & addField(Field *field)
@ InsertStatement
INSERT statement will be prepared end executed.
Helper class for using inside methods for given connection.
This class encapsulates transaction handle.
Kexi's main window interface This interface is implemented by KexiMainWindowImpl class.
virtual KexiProject * project()=0
Project data of currently opened project or NULL if no project here yet.
virtual tristate getNewObjectInfo(KexiPart::Item *partItem, KexiPart::Part *part, bool &allowOverwriting, const TQString &messageWhenAskingForName=TQString())=0
Information about a single object that can be instantiated using Kexi Part.
Part * partForMimeType(const TQString &mimeTypt)
The main class for kexi frontend parts like tables, queries, forms and reports.
A project's main controller.
void addStoredItem(KexiPart::Info *info, KexiPart::Item *item)
KexiDB::Connection * dbConnection() const
KexiPart::Item * createPartItem(KexiPart::Info *info, const TQString &suggestedCaption=TQString())
3-state logical type with three values: true, false and cancelled and convenient operators.
KEXIUTILS_EXPORT TQString string2Identifier(const TQString &s)
KEXIUTILS_EXPORT TQString fileDialogFilterStrings(const TQStringList &mimeStrings, bool kdeFormat)
KEXICORE_EXPORT KexiPart::Manager & partManager()
shared part manager