KOffice – TDE office suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

kexicsvimportdialog.cpp 49KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664
  1. /* This file is part of the KDE project
  2. Copyright (C) 2005-2006 Jaroslaw Staniek <js@iidea.pl>
  3. This work is based on kspread/dialogs/kspread_dlg_csv.cc
  4. and will be merged back with KOffice libraries.
  5. Copyright (C) 2002-2003 Norbert Andres <nandres@web.de>
  6. Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org>
  7. Copyright (C) 2002 Laurent Montel <montel@kde.org>
  8. Copyright (C) 1999 David Faure <faure@kde.org>
  9. This library is free software; you can redistribute it and/or
  10. modify it under the terms of the GNU Library General Public
  11. License as published by the Free Software Foundation; either
  12. version 2 of the License, or (at your option) any later version.
  13. This library is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. Library General Public License for more details.
  17. You should have received a copy of the GNU Library General Public License
  18. along with this library; see the file COPYING.LIB. If not, write to
  19. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  20. * Boston, MA 02110-1301, USA.
  21. */
  22. #include <tqbuttongroup.h>
  23. #include <tqcheckbox.h>
  24. #include <tqclipboard.h>
  25. #include <tqlabel.h>
  26. #include <tqlineedit.h>
  27. #include <tqmime.h>
  28. #include <tqpushbutton.h>
  29. #include <tqradiobutton.h>
  30. #include <tqtable.h>
  31. #include <tqlayout.h>
  32. #include <tqfiledialog.h>
  33. #include <tqpainter.h>
  34. #include <tqtextcodec.h>
  35. #include <tqtimer.h>
  36. #include <tqfontmetrics.h>
  37. #include <tqtooltip.h>
  38. #include <kapplication.h>
  39. #include <kdebug.h>
  40. #include <kdialogbase.h>
  41. #include <tdefiledialog.h>
  42. #include <klocale.h>
  43. #include <kmessagebox.h>
  44. #include <kglobalsettings.h>
  45. #include <kiconloader.h>
  46. #include <kcharsets.h>
  47. #include <knuminput.h>
  48. #include <kprogress.h>
  49. #include <kactivelabel.h>
  50. #include <kexiutils/identifier.h>
  51. #include <kexiutils/utils.h>
  52. #include <core/kexi.h>
  53. #include <core/kexiproject.h>
  54. #include <core/kexipart.h>
  55. #include <core/kexipartinfo.h>
  56. #include <core/keximainwindow.h>
  57. #include <core/kexiguimsghandler.h>
  58. #include <kexidb/connection.h>
  59. #include <kexidb/tableschema.h>
  60. #include <kexidb/transaction.h>
  61. #include <widget/kexicharencodingcombobox.h>
  62. #include "kexicsvimportdialog.h"
  63. #include "kexicsvwidgets.h"
  64. #ifdef TQ_WS_WIN
  65. #include <krecentdirs.h>
  66. #include <windows.h>
  67. #endif
  68. #if 0
  69. #include <kspread_cell.h>
  70. #include <kspread_doc.h>
  71. #include <kspread_sheet.h>
  72. #include <kspread_undo.h>
  73. #include <kspread_view.h>
  74. #endif
  75. #define _IMPORT_ICON "table" /*todo: change to "file_import" or so*/
  76. #define _TEXT_TYPE 0
  77. #define _NUMBER_TYPE 1
  78. #define _DATE_TYPE 2
  79. #define _TIME_TYPE 3
  80. #define _DATETIME_TYPE 4
  81. #define _PK_FLAG 5
  82. //extra:
  83. #define _NO_TYPE_YET -1 //allows to accept a number of empty cells, before something non-empty
  84. #define _FP_NUMBER_TYPE 255 //_NUMBER_TYPE variant
  85. #define MAX_ROWS_TO_PREVIEW 100 //max 100 rows is reasonable
  86. #define MAX_BYTES_TO_PREVIEW 10240 //max 10KB is reasonable
  87. #define MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER 4096
  88. class KexiCSVImportDialogTable : public TQTable
  89. {
  90. public:
  91. KexiCSVImportDialogTable( TQWidget * parent = 0, const char * name = 0 )
  92. : TQTable(parent, name) {
  93. f = font();
  94. f.setBold(true);
  95. }
  96. virtual void paintCell( TQPainter * p, int row, int col, const TQRect & cr, bool selected, const TQColorGroup & cg ) {
  97. if (row==0)
  98. p->setFont(f);
  99. else
  100. p->setFont(font());
  101. TQTable::paintCell(p, row, col, cr, selected, cg);
  102. }
  103. virtual void setColumnWidth( int col, int w ) {
  104. //make columns a bit wider
  105. TQTable::setColumnWidth( col, w + 16 );
  106. }
  107. TQFont f;
  108. };
  109. //! Helper used to temporary disable keyboard and mouse events
  110. void installRecursiveEventFilter(TQObject *filter, TQObject *object)
  111. {
  112. object->installEventFilter(filter);
  113. TQObjectList clo = object->childrenListObject();
  114. if (clo.isEmpty())
  115. return;
  116. TQObjectList list = clo;
  117. for(TQObject *obj = list.first(); obj; obj = list.next())
  118. installRecursiveEventFilter(filter, obj);
  119. }
  120. KexiCSVImportDialog::KexiCSVImportDialog( Mode mode, KexiMainWindow* mainWin,
  121. TQWidget * parent, const char * name
  122. )
  123. : KDialogBase(
  124. KDialogBase::Plain,
  125. i18n( "Import CSV Data File" )
  126. //! @todo use "Paste CSV Data From Clipboard" caption for mode==Clipboard
  127. ,
  128. (mode==File ? User1 : (ButtonCode)0) |Ok|Cancel,
  129. Ok,
  130. parent,
  131. name ? name : "KexiCSVImportDialog",
  132. true,
  133. false,
  134. KGuiItem( i18n("&Options"))
  135. ),
  136. m_mainWin(mainWin),
  137. m_cancelled( false ),
  138. m_adjustRows( true ),
  139. m_startline( 0 ),
  140. m_textquote( TQString(KEXICSV_DEFAULT_FILE_TEXT_QUOTE)[0] ),
  141. m_mode(mode),
  142. m_prevSelectedCol(-1),
  143. m_columnsAdjusted(false),
  144. m_1stRowForFieldNamesDetected(false),
  145. m_firstFillTableCall(true),
  146. m_blockUserEvents(false),
  147. m_primaryKeyColumn(-1),
  148. m_dialogCancelled(false),
  149. m_conn(0),
  150. m_destinationTableSchema(0),
  151. m_allRowsLoadedInPreview(false),
  152. m_stoppedAt_MAX_BYTES_TO_PREVIEW(false)
  153. {
  154. setWFlags(getWFlags() | TQt::WStyle_Maximize | TQt::WStyle_SysMenu);
  155. hide();
  156. setButtonOK(KGuiItem( i18n("&Import..."), _IMPORT_ICON));
  157. m_typeNames.resize(5);
  158. m_typeNames[0] = i18n("text");
  159. m_typeNames[1] = i18n("number");
  160. m_typeNames[2] = i18n("date");
  161. m_typeNames[3] = i18n("time");
  162. m_typeNames[4] = i18n("date/time");
  163. kapp->config()->setGroup("ImportExport");
  164. m_maximumRowsForPreview = kapp->config()->readNumEntry("MaximumRowsForPreviewInImportDialog", MAX_ROWS_TO_PREVIEW);
  165. m_maximumBytesForPreview = kapp->config()->readNumEntry("MaximumBytesForPreviewInImportDialog", MAX_BYTES_TO_PREVIEW);
  166. m_pkIcon = SmallIcon("key");
  167. m_uniquenessTest.setAutoDelete(true);
  168. setIcon(DesktopIcon(_IMPORT_ICON));
  169. setSizeGripEnabled( TRUE );
  170. // m_encoding = TQString::fromLatin1(TDEGlobal::locale()->encoding());
  171. // m_stripWhiteSpaceInTextValuesChecked = true;
  172. m_file = 0;
  173. m_inputStream = 0;
  174. TQVBoxLayout *lyr = new TQVBoxLayout(plainPage(), 0, KDialogBase::spacingHint(), "lyr");
  175. m_infoLbl = new KexiCSVInfoLabel(
  176. m_mode==File ? i18n("Preview of data from file:")
  177. : i18n("Preview of data from clipboard:"),
  178. plainPage()
  179. );
  180. lyr->addWidget( m_infoLbl );
  181. TQWidget* page = new TQFrame( plainPage(), "page" );
  182. TQGridLayout *glyr= new TQGridLayout( page, 4, 5, 0, KDialogBase::spacingHint(), "glyr");
  183. lyr->addWidget( page );
  184. // Delimiter: comma, semicolon, tab, space, other
  185. m_delimiterWidget = new KexiCSVDelimiterWidget(true /*lineEditOnBottom*/, page);
  186. m_detectDelimiter = true;
  187. glyr->addMultiCellWidget( m_delimiterWidget, 1, 2, 0, 0 );
  188. TQLabel *delimiterLabel = new TQLabel(m_delimiterWidget, i18n("Delimiter:"), page);
  189. delimiterLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
  190. glyr->addMultiCellWidget( delimiterLabel, 0, 0, 0, 0 );
  191. // Format: number, text, currency,
  192. m_formatComboText = i18n( "Format for column %1:" );
  193. m_formatCombo = new KComboBox(page, "m_formatCombo");
  194. m_formatCombo->insertItem(i18n("Text"));
  195. m_formatCombo->insertItem(i18n("Number"));
  196. m_formatCombo->insertItem(i18n("Date"));
  197. m_formatCombo->insertItem(i18n("Time"));
  198. m_formatCombo->insertItem(i18n("Date/Time"));
  199. glyr->addMultiCellWidget( m_formatCombo, 1, 1, 1, 1 );
  200. m_formatLabel = new TQLabel(m_formatCombo, "", page);
  201. m_formatLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
  202. glyr->addWidget( m_formatLabel, 0, 1 );
  203. m_primaryKeyField = new TQCheckBox( i18n( "Primary key" ), page, "m_primaryKeyField" );
  204. glyr->addWidget( m_primaryKeyField, 2, 1 );
  205. connect(m_primaryKeyField, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPrimaryKeyFieldToggled(bool)));
  206. m_comboQuote = new KexiCSVTextQuoteComboBox( page );
  207. glyr->addWidget( m_comboQuote, 1, 2 );
  208. TextLabel2 = new TQLabel( m_comboQuote, i18n( "Text quote:" ), page, "TextLabel2" );
  209. TextLabel2->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
  210. TextLabel2->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
  211. glyr->addWidget( TextLabel2, 0, 2 );
  212. m_startAtLineSpinBox = new KIntSpinBox( page, "m_startAtLineSpinBox" );
  213. m_startAtLineSpinBox->setMinValue(1);
  214. m_startAtLineSpinBox->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed );
  215. m_startAtLineSpinBox->setMinimumWidth(TQFontMetrics(m_startAtLineSpinBox->font()).width("8888888"));
  216. glyr->addWidget( m_startAtLineSpinBox, 1, 3 );
  217. m_startAtLineLabel = new TQLabel( m_startAtLineSpinBox, "",
  218. page, "TextLabel3" );
  219. m_startAtLineLabel->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
  220. m_startAtLineLabel->setAlignment(TQt::AlignAuto | TQt::AlignBottom);
  221. glyr->addWidget( m_startAtLineLabel, 0, 3 );
  222. TQSpacerItem* spacer_2 = new TQSpacerItem( 0, 0, TQSizePolicy::Minimum, TQSizePolicy::Preferred );
  223. glyr->addItem( spacer_2, 0, 4 );
  224. m_ignoreDuplicates = new TQCheckBox( page, "m_ignoreDuplicates" );
  225. m_ignoreDuplicates->setText( i18n( "Ignore duplicated delimiters" ) );
  226. glyr->addMultiCellWidget( m_ignoreDuplicates, 2, 2, 2, 4 );
  227. m_1stRowForFieldNames = new TQCheckBox( page, "m_1stRowForFieldNames" );
  228. m_1stRowForFieldNames->setText( i18n( "First row contains column names" ) );
  229. glyr->addMultiCellWidget( m_1stRowForFieldNames, 3, 3, 2, 4 );
  230. m_table = new KexiCSVImportDialogTable( plainPage(), "m_table" );
  231. lyr->addWidget( m_table );
  232. m_table->setSizePolicy( TQSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding, 1, 1) );
  233. m_table->setNumRows( 0 );
  234. m_table->setNumCols( 0 );
  235. /** @todo reuse Clipboard too! */
  236. /*
  237. if ( m_mode == Clipboard )
  238. {
  239. setCaption( i18n( "Inserting From Clipboard" ) );
  240. TQMimeSource * mime = TQApplication::clipboard()->data();
  241. if ( !mime )
  242. {
  243. KMessageBox::information( this, i18n("There is no data in the clipboard.") );
  244. m_cancelled = true;
  245. return;
  246. }
  247. if ( !mime->provides( "text/plain" ) )
  248. {
  249. KMessageBox::information( this, i18n("There is no usable data in the clipboard.") );
  250. m_cancelled = true;
  251. return;
  252. }
  253. m_fileArray = TQByteArray(mime->encodedData( "text/plain" ) );
  254. }
  255. else if ( mode == File )
  256. {*/
  257. m_dateRegExp = TQRegExp("(\\d{1,4})([/\\-\\.])(\\d{1,2})([/\\-\\.])(\\d{1,4})");
  258. m_timeRegExp1 = TQRegExp("(\\d{1,2}):(\\d{1,2}):(\\d{1,2})");
  259. m_timeRegExp2 = TQRegExp("(\\d{1,2}):(\\d{1,2})");
  260. m_fpNumberRegExp = TQRegExp("[\\-]{0,1}\\d*[,\\.]\\d+");
  261. TQString caption( i18n("Open CSV Data File") );
  262. if (m_mode == File) {
  263. TQStringList mimetypes( csvMimeTypes() );
  264. #ifdef TQ_WS_WIN
  265. //! @todo remove
  266. TQString recentDir = TDEGlobalSettings::documentPath();
  267. m_fname = TQFileDialog::getOpenFileName(
  268. KFileDialog::getStartURL(":CSVImportExport", recentDir).path(),
  269. KexiUtils::fileDialogFilterStrings(mimetypes, false),
  270. page, "KexiCSVImportDialog", caption);
  271. if ( !m_fname.isEmpty() ) {
  272. //save last visited path
  273. KURL url;
  274. url.setPath( m_fname );
  275. if (url.isLocalFile())
  276. KRecentDirs::add(":CSVImportExport", url.directory());
  277. }
  278. #else
  279. m_fname = KFileDialog::getOpenFileName(":CSVImportExport", mimetypes.join(" "),
  280. this, caption);
  281. #endif
  282. //cancel action !
  283. if ( m_fname.isEmpty() )
  284. {
  285. actionButton( Ok )->setEnabled( false );
  286. m_cancelled = true;
  287. if (parentWidget())
  288. parentWidget()->raise();
  289. return;
  290. }
  291. }
  292. else if (m_mode == Clipboard) {
  293. TQCString subtype("plain");
  294. m_clipboardData = TQApplication::clipboard()->text(subtype, TQClipboard::Clipboard);
  295. /* debug
  296. for (int i=0;TQApplication::clipboard()->data(TQClipboard::Clipboard)->format(i);i++)
  297. kdDebug() << i << ": "
  298. << TQApplication::clipboard()->data(TQClipboard::Clipboard)->format(i) << endl;
  299. */
  300. }
  301. else {
  302. return;
  303. }
  304. m_loadingProgressDlg = 0;
  305. m_importingProgressDlg = 0;
  306. if (m_mode == File) {
  307. m_loadingProgressDlg = new KProgressDialog(
  308. this, "m_loadingProgressDlg", i18n("Loading CSV Data"), i18n("Loading CSV Data from \"%1\"...")
  309. .arg(TQDir::convertSeparators(m_fname)), true);
  310. m_loadingProgressDlg->progressBar()->setTotalSteps( m_maximumRowsForPreview+1 );
  311. m_loadingProgressDlg->show();
  312. }
  313. if (m_mode==Clipboard) {
  314. m_infoLbl->setIcon("editpaste");
  315. }
  316. //updateRowCountInfo();
  317. m_table->setSelectionMode(TQTable::NoSelection);
  318. connect(m_formatCombo, TQT_SIGNAL(activated(int)),
  319. this, TQT_SLOT(formatChanged(int)));
  320. connect(m_delimiterWidget, TQT_SIGNAL(delimiterChanged(const TQString&)),
  321. this, TQT_SLOT(delimiterChanged(const TQString&)));
  322. connect(m_startAtLineSpinBox, TQT_SIGNAL(valueChanged ( int )),
  323. this, TQT_SLOT(startlineSelected(int)));
  324. connect(m_comboQuote, TQT_SIGNAL(activated(int)),
  325. this, TQT_SLOT(textquoteSelected(int)));
  326. connect(m_table, TQT_SIGNAL(currentChanged(int, int)),
  327. this, TQT_SLOT(currentCellChanged(int, int)));
  328. connect(m_table, TQT_SIGNAL(valueChanged(int,int)),
  329. this, TQT_SLOT(cellValueChanged(int,int)));
  330. connect(m_ignoreDuplicates, TQT_SIGNAL(stateChanged(int)),
  331. this, TQT_SLOT(ignoreDuplicatesChanged(int)));
  332. connect(m_1stRowForFieldNames, TQT_SIGNAL(stateChanged(int)),
  333. this, TQT_SLOT(slot1stRowForFieldNamesChanged(int)));
  334. connect(this, TQT_SIGNAL(user1Clicked()), this, TQT_SLOT(optionsButtonClicked()));
  335. installRecursiveEventFilter(TQT_TQOBJECT(this), TQT_TQOBJECT(this));
  336. initLater();
  337. }
  338. KexiCSVImportDialog::~KexiCSVImportDialog()
  339. {
  340. delete m_file;
  341. }
  342. void KexiCSVImportDialog::initLater()
  343. {
  344. if (!openData())
  345. return;
  346. // delimiterChanged(detectedDelimiter); // this will cause fillTable()
  347. m_columnsAdjusted = false;
  348. fillTable();
  349. delete m_loadingProgressDlg;
  350. m_loadingProgressDlg = 0;
  351. if (m_dialogCancelled) {
  352. // m_loadingProgressDlg->hide();
  353. // m_loadingProgressDlg->close();
  354. TQTimer::singleShot(0, this, TQT_SLOT(reject()));
  355. return;
  356. }
  357. currentCellChanged(0, 0);
  358. // updateGeometry();
  359. adjustSize();
  360. KDialog::centerOnScreen( this );
  361. if (m_loadingProgressDlg)
  362. m_loadingProgressDlg->hide();
  363. show();
  364. m_table->setFocus();
  365. }
  366. bool KexiCSVImportDialog::openData()
  367. {
  368. if (m_mode!=File) //data already loaded, no encoding stuff needed
  369. return true;
  370. delete m_inputStream;
  371. m_inputStream = 0;
  372. if (m_file) {
  373. m_file->close();
  374. delete m_file;
  375. }
  376. m_file = new TQFile(m_fname);
  377. if (!m_file->open(IO_ReadOnly))
  378. {
  379. m_file->close();
  380. delete m_file;
  381. m_file = 0;
  382. KMessageBox::sorry( this, i18n("Cannot open input file <nobr>\"%1\"</nobr>.")
  383. .arg(TQDir::convertSeparators(m_fname)) );
  384. actionButton( Ok )->setEnabled( false );
  385. m_cancelled = true;
  386. if (parentWidget())
  387. parentWidget()->raise();
  388. return false;
  389. }
  390. return true;
  391. }
  392. bool KexiCSVImportDialog::cancelled() const
  393. {
  394. return m_cancelled;
  395. }
  396. void KexiCSVImportDialog::fillTable()
  397. {
  398. KexiUtils::WaitCursor wc(true);
  399. repaint();
  400. m_blockUserEvents = true;
  401. TQPushButton *pb = actionButton(KDialogBase::Cancel);
  402. if (pb)
  403. pb->setEnabled(true); //allow to cancel
  404. KexiUtils::WaitCursor wait;
  405. if (m_table->numRows()>0) //to accept editor
  406. m_table->setCurrentCell(0,0);
  407. int row, column, maxColumn;
  408. TQString field = TQString();
  409. for (row = 0; row < m_table->numRows(); ++row)
  410. for (column = 0; column < m_table->numCols(); ++column)
  411. m_table->clearCell(row, column);
  412. m_detectedTypes.clear();
  413. m_detectedTypes.resize(1024, _NO_TYPE_YET);//_TEXT_TYPE);
  414. m_uniquenessTest.clear();
  415. m_uniquenessTest.resize(1024);
  416. m_1stRowForFieldNamesDetected = true;
  417. if (true != loadRows(field, row, column, maxColumn, true))
  418. return;
  419. m_1stRowForFieldNamesDetected = false;
  420. // file with only one line without '\n'
  421. if (field.length() > 0)
  422. {
  423. setText(row - m_startline, column, field, true);
  424. ++row;
  425. field = TQString();
  426. }
  427. adjustRows( row - m_startline - (m_1stRowForFieldNames->isChecked()?1:0) );
  428. maxColumn = TQMAX( maxColumn, column );
  429. m_table->setNumCols(maxColumn);
  430. for (column = 0; column < m_table->numCols(); ++column)
  431. {
  432. // TQString header = m_table->horizontalHeader()->label(column);
  433. // if (header != i18n("Text") && header != i18n("Number") &&
  434. // header != i18n("Date") && header != i18n("Currency"))
  435. // const int detectedType = m_detectedTypes[column+1];
  436. // m_table->horizontalHeader()->setLabel(column, m_typeNames[ detectedType ]); //i18n("Text"));
  437. updateColumnText(column);
  438. if (!m_columnsAdjusted)
  439. m_table->adjustColumn(column);
  440. }
  441. m_columnsAdjusted = true;
  442. if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
  443. if (_NUMBER_TYPE != m_detectedTypes[ m_primaryKeyColumn ]) {
  444. m_primaryKeyColumn = -1;
  445. }
  446. }
  447. m_prevSelectedCol = -1;
  448. m_table->setCurrentCell(0,0);
  449. currentCellChanged(0, 0);
  450. if (m_primaryKeyColumn != -1)
  451. m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
  452. const int count = TQMAX(0, m_table->numRows()-1+m_startline);
  453. m_allRowsLoadedInPreview = count < m_maximumRowsForPreview && !m_stoppedAt_MAX_BYTES_TO_PREVIEW;
  454. if (m_allRowsLoadedInPreview) {
  455. m_startAtLineSpinBox->setMaxValue(count);
  456. m_startAtLineSpinBox->setValue(m_startline+1);
  457. }
  458. m_startAtLineLabel->setText(i18n( "Start at line%1:").arg(
  459. m_allRowsLoadedInPreview ? TQString(" (1-%1)").arg(count)
  460. : TQString() //we do not know what's real count
  461. ));
  462. updateRowCountInfo();
  463. m_blockUserEvents = false;
  464. repaint();
  465. m_table->verticalScrollBar()->repaint();//avoid missing repaint
  466. m_table->horizontalScrollBar()->repaint();//avoid missing repaint
  467. }
  468. TQString KexiCSVImportDialog::detectDelimiterByLookingAtFirstBytesOfFile(TQTextStream& inputStream)
  469. {
  470. m_file->at(0);
  471. // try to detect delimiter
  472. // \t has priority, then ; then ,
  473. const TQIODevice::Offset origOffset = inputStream.device()->at();
  474. TQChar c, prevChar=0;
  475. int detectedDelimiter = 0;
  476. bool insideQuote = false;
  477. //characters by priority
  478. const int CH_TAB_AFTER_QUOTE = 500;
  479. const int CH_SEMICOLON_AFTER_QUOTE = 499;
  480. const int CH_COMMA_AFTER_QUOTE = 498;
  481. const int CH_TAB = 200; // \t
  482. const int CH_SEMICOLON = 199; // ;
  483. const int CH_COMMA = 198; // ,
  484. TQValueList<int> tabsPerLine, semicolonsPerLine, commasPerLine;
  485. int tabs = 0, semicolons = 0, commas = 0;
  486. int line = 0;
  487. for (uint i=0; !inputStream.atEnd() && i < MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER; i++) {
  488. (*m_inputStream) >> c; // read one char
  489. if (prevChar=='"') {
  490. if (c!='"') //real quote (not double "")
  491. insideQuote = !insideQuote;
  492. }
  493. if (insideQuote) {
  494. prevChar = c;
  495. continue;
  496. }
  497. if (c==' ')
  498. continue;
  499. if (c=='\n') {//end of line
  500. //remember # of tabs/semicolons/commas in this line
  501. tabsPerLine += tabs;
  502. tabs = 0;
  503. semicolonsPerLine += semicolons;
  504. semicolons = 0;
  505. commasPerLine += commas;
  506. commas = 0;
  507. line++;
  508. }
  509. else if (c=='\t') {
  510. tabs++;
  511. detectedDelimiter = TQMAX( prevChar=='"' ? CH_TAB_AFTER_QUOTE : CH_TAB, detectedDelimiter );
  512. }
  513. else if (c==';') {
  514. semicolons++;
  515. detectedDelimiter = TQMAX( prevChar=='"' ? CH_SEMICOLON_AFTER_QUOTE : CH_SEMICOLON, detectedDelimiter );
  516. }
  517. else if (c==',') {
  518. commas++;
  519. detectedDelimiter = TQMAX( prevChar=='"' ? CH_COMMA_AFTER_QUOTE : CH_COMMA, detectedDelimiter );
  520. }
  521. prevChar = c;
  522. }
  523. inputStream.device()->at(origOffset); //restore orig. offset
  524. //now, try to find a delimiter character that exists the same number of times in all the checked lines
  525. //this detection method has priority over others
  526. TQValueList<int>::ConstIterator it;
  527. if (tabsPerLine.count()>1) {
  528. tabs = tabsPerLine.isEmpty() ? 0 : tabsPerLine.first();
  529. for (it=tabsPerLine.constBegin(); it!=tabsPerLine.constEnd(); ++it) {
  530. if (tabs != *it)
  531. break;
  532. }
  533. if (tabs>0 && it==tabsPerLine.constEnd())
  534. return "\t";
  535. }
  536. if (semicolonsPerLine.count()>1) {
  537. semicolons = semicolonsPerLine.isEmpty() ? 0 : semicolonsPerLine.first();
  538. for (it=semicolonsPerLine.constBegin(); it!=semicolonsPerLine.constEnd(); ++it) {
  539. if (semicolons != *it)
  540. break;
  541. }
  542. if (semicolons > 0 && it==semicolonsPerLine.constEnd())
  543. return ";";
  544. }
  545. if (commasPerLine.count()>1) {
  546. commas = commasPerLine.first();
  547. for (it=commasPerLine.constBegin(); it!=commasPerLine.constEnd(); ++it) {
  548. if (commas != *it)
  549. break;
  550. }
  551. if (commas > 0 && it==commasPerLine.constEnd())
  552. return ",";
  553. }
  554. //now return the winning character by looking at CH_* symbol
  555. if (detectedDelimiter == CH_TAB_AFTER_QUOTE || detectedDelimiter == CH_TAB)
  556. return "\t";
  557. if (detectedDelimiter == CH_SEMICOLON_AFTER_QUOTE || detectedDelimiter == CH_SEMICOLON)
  558. return ";";
  559. if (detectedDelimiter == CH_COMMA_AFTER_QUOTE || detectedDelimiter == CH_COMMA)
  560. return ",";
  561. return KEXICSV_DEFAULT_FILE_DELIMITER; //<-- default
  562. }
  563. tristate KexiCSVImportDialog::loadRows(TQString &field, int &row, int &column, int &maxColumn,
  564. bool inGUI)
  565. {
  566. enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD,
  567. S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD } state = S_START;
  568. field = TQString();
  569. const bool ignoreDups = m_ignoreDuplicates->isChecked();
  570. bool lastCharDelimiter = false;
  571. bool nextRow = false;
  572. row = column = 1;
  573. maxColumn = 0;
  574. TQChar x;
  575. const bool hadInputStream = m_inputStream!=0;
  576. delete m_inputStream;
  577. if ( m_mode == Clipboard ) {
  578. m_inputStream = new TQTextStream(m_clipboardData, IO_ReadOnly);
  579. if (!hadInputStream)
  580. m_delimiterWidget->setDelimiter(KEXICSV_DEFAULT_CLIPBOARD_DELIMITER);
  581. }
  582. else {
  583. m_file->at(0); //always seek at 0 because loadRows() is called many times
  584. m_inputStream = new TQTextStream(m_file);
  585. if (m_options.defaultEncodingExplicitySet) {
  586. TQTextCodec *codec = TDEGlobal::charsets()->codecForName(m_options.encoding);
  587. if (codec)
  588. m_inputStream->setCodec(codec); //TQTextCodec::codecForName("CP1250"));
  589. }
  590. if (m_detectDelimiter) {
  591. const TQString delimiter( detectDelimiterByLookingAtFirstBytesOfFile(*m_inputStream) );
  592. if (m_delimiterWidget->delimiter() != delimiter)
  593. m_delimiterWidget->setDelimiter( delimiter );
  594. }
  595. }
  596. const TQChar delimiter(m_delimiterWidget->delimiter()[0]);
  597. m_stoppedAt_MAX_BYTES_TO_PREVIEW = false;
  598. int progressStep = 0;
  599. if (m_importingProgressDlg)
  600. progressStep = TQMAX( 1, m_importingProgressDlg->progressBar()->totalSteps()/200 );
  601. int offset = 0;
  602. for (;!m_inputStream->atEnd(); offset++)
  603. {
  604. //disabled: this breaks wide spreadsheets
  605. // if (column >= m_maximumRowsForPreview)
  606. // return true;
  607. if (m_importingProgressDlg && ((offset % progressStep) < 5)) {
  608. //update progr. bar dlg on final exporting
  609. m_importingProgressDlg->progressBar()->setValue(offset);
  610. tqApp->processEvents();
  611. if (m_importingProgressDlg->wasCancelled()) {
  612. delete m_importingProgressDlg;
  613. m_importingProgressDlg = 0;
  614. return ::cancelled;
  615. }
  616. }
  617. (*m_inputStream) >> x; // read one char
  618. if (x == '\r') {
  619. continue; // eat '\r', to handle RFC-compliant files
  620. }
  621. if (offset==0 && x.unicode()==0xfeff) {
  622. // Ignore BOM, the "Byte Order Mark"
  623. // (http://en.wikipedia.org/wiki/Byte_Order_Mark, // http://www.unicode.org/charts/PDF/UFFF0.pdf)
  624. // Probably fixed in TQt4.
  625. continue;
  626. }
  627. switch (state)
  628. {
  629. case S_START :
  630. if (x == m_textquote)
  631. {
  632. state = S_QUOTED_FIELD;
  633. }
  634. else if (x == delimiter)
  635. {
  636. setText(row - m_startline, column, field, inGUI);
  637. field = TQString();
  638. if ((ignoreDups == false) || (lastCharDelimiter == false))
  639. ++column;
  640. lastCharDelimiter = true;
  641. }
  642. else if (x == '\n')
  643. {
  644. if (!inGUI) {
  645. //fill remaining empty fields (database wants them explicitly)
  646. for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
  647. setText(row - m_startline, additionalColumn, TQString(), inGUI);
  648. }
  649. }
  650. nextRow = true;
  651. maxColumn = TQMAX( maxColumn, column );
  652. column = 1;
  653. }
  654. else
  655. {
  656. field += x;
  657. state = S_MAYBE_NORMAL_FIELD;
  658. }
  659. break;
  660. case S_QUOTED_FIELD :
  661. if (x == m_textquote)
  662. {
  663. state = S_MAYBE_END_OF_QUOTED_FIELD;
  664. }
  665. /*allow \n inside quoted fields
  666. else if (x == '\n')
  667. {
  668. setText(row - m_startline, column, field, inGUI);
  669. field = "";
  670. if (x == '\n')
  671. {
  672. nextRow = true;
  673. maxColumn = TQMAX( maxColumn, column );
  674. column = 1;
  675. }
  676. else
  677. {
  678. if ((ignoreDups == false) || (lastCharDelimiter == false))
  679. ++column;
  680. lastCharDelimiter = true;
  681. }
  682. state = S_START;
  683. }*/
  684. else
  685. {
  686. field += x;
  687. }
  688. break;
  689. case S_MAYBE_END_OF_QUOTED_FIELD :
  690. if (x == m_textquote)
  691. {
  692. field += x; //no, this was just escaped quote character
  693. state = S_QUOTED_FIELD;
  694. }
  695. else if (x == delimiter || x == '\n')
  696. {
  697. setText(row - m_startline, column, field, inGUI);
  698. field = TQString();
  699. if (x == '\n')
  700. {
  701. nextRow = true;
  702. maxColumn = TQMAX( maxColumn, column );
  703. column = 1;
  704. }
  705. else
  706. {
  707. if ((ignoreDups == false) || (lastCharDelimiter == false))
  708. ++column;
  709. lastCharDelimiter = true;
  710. }
  711. state = S_START;
  712. }
  713. else
  714. {
  715. state = S_END_OF_QUOTED_FIELD;
  716. }
  717. break;
  718. case S_END_OF_QUOTED_FIELD :
  719. if (x == delimiter || x == '\n')
  720. {
  721. setText(row - m_startline, column, field, inGUI);
  722. field = TQString();
  723. if (x == '\n')
  724. {
  725. nextRow = true;
  726. maxColumn = TQMAX( maxColumn, column );
  727. column = 1;
  728. }
  729. else
  730. {
  731. if ((ignoreDups == false) || (lastCharDelimiter == false))
  732. ++column;
  733. lastCharDelimiter = true;
  734. }
  735. state = S_START;
  736. }
  737. else
  738. {
  739. state = S_END_OF_QUOTED_FIELD;
  740. }
  741. break;
  742. case S_MAYBE_NORMAL_FIELD :
  743. if (x == m_textquote)
  744. {
  745. field = TQString();
  746. state = S_QUOTED_FIELD;
  747. break;
  748. }
  749. case S_NORMAL_FIELD :
  750. if (x == delimiter || x == '\n')
  751. {
  752. setText(row - m_startline, column, field, inGUI);
  753. field = TQString();
  754. if (x == '\n')
  755. {
  756. nextRow = true;
  757. maxColumn = TQMAX( maxColumn, column );
  758. column = 1;
  759. }
  760. else
  761. {
  762. if ((ignoreDups == false) || (lastCharDelimiter == false))
  763. ++column;
  764. lastCharDelimiter = true;
  765. }
  766. state = S_START;
  767. }
  768. else
  769. {
  770. field += x;
  771. }
  772. }
  773. if (x != delimiter)
  774. lastCharDelimiter = false;
  775. if (nextRow) {
  776. if (!inGUI && row==1 && m_1stRowForFieldNames->isChecked()) {
  777. // do not save to the database 1st row if it contains column names
  778. m_importingStatement->clearArguments();
  779. }
  780. else if (!saveRow(inGUI))
  781. return false;
  782. ++row;
  783. }
  784. if (m_firstFillTableCall && row==2
  785. && !m_1stRowForFieldNames->isChecked() && m_1stRowForFieldNamesDetected)
  786. {
  787. //'1st row for field name' flag detected: reload table
  788. m_1stRowForFieldNamesDetected = false;
  789. m_table->setNumRows( 0 );
  790. m_firstFillTableCall = false; //this trick is allowed only once, on startup
  791. m_1stRowForFieldNames->setChecked(true); //this will reload table
  792. //slot1stRowForFieldNamesChanged(1);
  793. m_blockUserEvents = false;
  794. repaint();
  795. return false;
  796. }
  797. if (!m_importingProgressDlg && row % 20 == 0) {
  798. tqApp->processEvents();
  799. //only for GUI mode:
  800. if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCancelled()) {
  801. delete m_loadingProgressDlg;
  802. m_loadingProgressDlg = 0;
  803. m_dialogCancelled = true;
  804. reject();
  805. return false;
  806. }
  807. }
  808. if (!m_firstFillTableCall && m_loadingProgressDlg) {
  809. m_loadingProgressDlg->progressBar()->setValue(TQMIN(m_maximumRowsForPreview, row));
  810. }
  811. if ( inGUI && row > (m_maximumRowsForPreview + (m_1stRowForFieldNamesDetected?1:0)) ) {
  812. kexipluginsdbg << "KexiCSVImportDialog::fillTable() loading stopped at row #"
  813. << m_maximumRowsForPreview << endl;
  814. break;
  815. }
  816. if (nextRow) {
  817. nextRow = false;
  818. //additional speedup: stop processing now if too many bytes were loaded for preview
  819. kexipluginsdbg << offset << endl;
  820. if (inGUI && offset >= m_maximumBytesForPreview && row >= 2) {
  821. m_stoppedAt_MAX_BYTES_TO_PREVIEW = true;
  822. return true;
  823. }
  824. }
  825. }
  826. return true;
  827. }
  828. void KexiCSVImportDialog::updateColumnText(int col)
  829. {
  830. TQString colName;
  831. if (col<(int)m_columnNames.count() && (m_1stRowForFieldNames->isChecked() || m_changedColumnNames[col]))
  832. colName = m_columnNames[ col ];
  833. if (colName.isEmpty()) {
  834. colName = i18n("Column %1").arg(col+1); //will be changed to a valid identifier on import
  835. m_changedColumnNames[ col ] = false;
  836. }
  837. int detectedType = m_detectedTypes[col];
  838. if (detectedType==_FP_NUMBER_TYPE)
  839. detectedType=_NUMBER_TYPE; //we're simplifying that for now
  840. else if (detectedType==_NO_TYPE_YET) {
  841. m_detectedTypes[col]=_TEXT_TYPE; //entirely empty column
  842. detectedType=_TEXT_TYPE;
  843. }
  844. m_table->horizontalHeader()->setLabel(col,
  845. i18n("Column %1").arg(col+1) + " \n(" + m_typeNames[ detectedType ] + ") ");
  846. m_table->setText(0, col, colName);
  847. m_table->horizontalHeader()->adjustHeaderSize();
  848. //check uniqueness
  849. TQValueList<int> *list = m_uniquenessTest[col];
  850. if (m_primaryKeyColumn==-1 && list && !list->isEmpty()) {
  851. qHeapSort(*list);
  852. TQValueList<int>::ConstIterator it=list->constBegin();
  853. int prevValue = *it;
  854. ++it;
  855. for(; it!=list->constEnd() && prevValue!=(*it); ++it)
  856. prevValue=(*it);
  857. if (it!=list->constEnd()) {
  858. //duplicates:
  859. list->clear();
  860. }
  861. else {
  862. //a candidate for PK (autodetected)!
  863. if (-1==m_primaryKeyColumn) {
  864. m_primaryKeyColumn=col;
  865. }
  866. }
  867. }
  868. if (list) //not needed now: conserve memory
  869. list->clear();
  870. }
  871. void KexiCSVImportDialog::detectTypeAndUniqueness(int row, int col, const TQString& text)
  872. {
  873. int intValue;
  874. const int type = m_detectedTypes[col];
  875. if (row==1 || type!=_TEXT_TYPE) {
  876. bool found = false;
  877. if (text.isEmpty() && type==_NO_TYPE_YET)
  878. found = true; //real type should be found later
  879. //detect type because it's 1st row or all prev. rows were not text
  880. //-FP number? (trying before "number" type is a must)
  881. if (!found && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
  882. bool ok = text.isEmpty() || m_fpNumberRegExp.exactMatch(text);
  883. //if (!ok)
  884. // text.toDouble(&ok);
  885. if (ok && (row==1 || type==_NUMBER_TYPE || type==_FP_NUMBER_TYPE || type==_NO_TYPE_YET)) {
  886. m_detectedTypes[col]=_FP_NUMBER_TYPE;
  887. found = true; //yes
  888. }
  889. }
  890. //-number?
  891. if (!found && (row==1 || type==_NUMBER_TYPE || type==_NO_TYPE_YET)) {
  892. bool ok = text.isEmpty();//empty values allowed
  893. if (!ok)
  894. intValue = text.toInt(&ok);
  895. if (ok && (row==1 || type==_NO_TYPE_YET)) {
  896. m_detectedTypes[col]=_NUMBER_TYPE;
  897. found = true; //yes
  898. }
  899. }
  900. //-date?
  901. if (!found && (row==1 || type==_DATE_TYPE || type==_NO_TYPE_YET)) {
  902. if ((row==1 || type==_NO_TYPE_YET)
  903. && (text.isEmpty() || m_dateRegExp.exactMatch(text)))
  904. {
  905. m_detectedTypes[col]=_DATE_TYPE;
  906. found = true; //yes
  907. }
  908. }
  909. //-time?
  910. if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
  911. if ((row==1 || type==_NO_TYPE_YET)
  912. && (text.isEmpty() || m_timeRegExp1.exactMatch(text) || m_timeRegExp2.exactMatch(text)))
  913. {
  914. m_detectedTypes[col]=_TIME_TYPE;
  915. found = true; //yes
  916. }
  917. }
  918. //-date/time?
  919. if (!found && (row==1 || type==_TIME_TYPE || type==_NO_TYPE_YET)) {
  920. if (row==1 || type==_NO_TYPE_YET) {
  921. bool detected = text.isEmpty();
  922. if (!detected) {
  923. const TQStringList dateTimeList( TQStringList::split(" ", text) );
  924. bool ok = dateTimeList.count()>=2;
  925. //! @todo also support ISODateTime's "T" separator?
  926. //! @todo also support timezones?
  927. if (ok) {
  928. //try all combinations
  929. TQString datePart( dateTimeList[0].stripWhiteSpace() );
  930. TQString timePart( dateTimeList[1].stripWhiteSpace() );
  931. ok = m_dateRegExp.exactMatch(datePart)
  932. && (m_timeRegExp1.exactMatch(timePart) || m_timeRegExp2.exactMatch(timePart));
  933. }
  934. detected = ok;
  935. }
  936. if (detected) {
  937. m_detectedTypes[col]=_DATETIME_TYPE;
  938. found = true; //yes
  939. }
  940. }
  941. }
  942. if (!found && type==_NO_TYPE_YET && !text.isEmpty()) {
  943. //eventually, a non-emptytext after a while
  944. m_detectedTypes[col]=_TEXT_TYPE;
  945. found = true; //yes
  946. }
  947. //default: text type (already set)
  948. }
  949. //check uniqueness for this value
  950. TQValueList<int> *list = m_uniquenessTest[col];
  951. if (row==1 && (!list || !list->isEmpty()) && !text.isEmpty() && _NUMBER_TYPE == m_detectedTypes[col]) {
  952. if (!list) {
  953. list = new TQValueList<int>();
  954. m_uniquenessTest.insert(col, list);
  955. }
  956. list->append( intValue );
  957. }
  958. else {
  959. //the value is empty or uniqueness test failed in the past
  960. if (list && !list->isEmpty())
  961. list->clear(); //indicate that uniqueness test failed
  962. }
  963. }
  964. bool KexiCSVImportDialog::parseDate(const TQString& text, TQDate& date)
  965. {
  966. if (!m_dateRegExp.exactMatch(text))
  967. return false;
  968. //dddd - dd - dddd
  969. //1 2 3 4 5 <- pos
  970. const int d1 = m_dateRegExp.cap(1).toInt(), d3 = m_dateRegExp.cap(3).toInt(), d5 = m_dateRegExp.cap(5).toInt();
  971. if (m_dateRegExp.cap(2)=="/") //probably separator for american format mm/dd/yyyy
  972. date = TQDate(d5, d1, d3);
  973. else {
  974. if (d5 > 31) //d5 == year
  975. date = TQDate(d5, d3, d1);
  976. else //d1 == year
  977. date = TQDate(d1, d3, d5);
  978. }
  979. return date.isValid();
  980. }
  981. bool KexiCSVImportDialog::parseTime(const TQString& text, TQTime& time)
  982. {
  983. time = TQTime::fromString(text, Qt::ISODate); //same as m_timeRegExp1
  984. if (time.isValid())
  985. return true;
  986. if (m_timeRegExp2.exactMatch(text)) { //hh:mm:ss
  987. time = TQTime(m_timeRegExp2.cap(1).toInt(), m_timeRegExp2.cap(3).toInt(), m_timeRegExp2.cap(5).toInt());
  988. return true;
  989. }
  990. return false;
  991. }
  992. void KexiCSVImportDialog::setText(int row, int col, const TQString& text, bool inGUI)
  993. {
  994. if (!inGUI) {
  995. //save text directly to database buffer
  996. if (col==1) { //1st col
  997. m_importingStatement->clearArguments();
  998. if (m_implicitPrimaryKeyAdded)
  999. *m_importingStatement << TQVariant(); //id will be autogenerated here
  1000. }
  1001. const int detectedType = m_detectedTypes[col-1];
  1002. if (detectedType==_NUMBER_TYPE) {
  1003. *m_importingStatement << ( text.isEmpty() ? TQVariant() : text.toInt() );
  1004. //! @todo what about time and float/double types and different integer subtypes?
  1005. }
  1006. else if (detectedType==_FP_NUMBER_TYPE) {
  1007. //replace ',' with '.'
  1008. TQCString t(text.latin1());
  1009. const int textLen = t.length();
  1010. for (int i=0; i<textLen; i++) {
  1011. if (t.at(i)==',') {
  1012. t.at(i) = '.';
  1013. break;
  1014. }
  1015. }
  1016. *m_importingStatement << ( t.isEmpty() ? TQVariant() : t.toDouble() );
  1017. }
  1018. else if (detectedType==_DATE_TYPE) {
  1019. TQDate date;
  1020. if (parseDate(text, date))
  1021. *m_importingStatement << date;
  1022. }
  1023. else if (detectedType==_TIME_TYPE) {
  1024. TQTime time;
  1025. if (parseTime(text, time))
  1026. *m_importingStatement << time;
  1027. }
  1028. else if (detectedType==_DATETIME_TYPE) {
  1029. TQStringList dateTimeList( TQStringList::split(" ", text) );
  1030. if (dateTimeList.count()<2)
  1031. dateTimeList = TQStringList::split("T", text); //also support ISODateTime's "T" separator
  1032. //! @todo also support timezones?
  1033. if (dateTimeList.count()>=2) {
  1034. TQString datePart( dateTimeList[0].stripWhiteSpace() );
  1035. TQDate date;
  1036. if (parseDate(datePart, date)) {
  1037. TQString timePart( dateTimeList[1].stripWhiteSpace() );
  1038. TQTime time;
  1039. if (parseTime(timePart, time))
  1040. *m_importingStatement << TQDateTime(date, time);
  1041. }
  1042. }
  1043. }
  1044. else //_TEXT_TYPE and the rest
  1045. *m_importingStatement << (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text);
  1046. return;
  1047. }
  1048. //save text to GUI (table view)
  1049. if (m_table->numCols() < col) {
  1050. m_table->setNumCols(col);
  1051. if ((int)m_columnNames.size() < m_table->numCols()) {
  1052. m_columnNames.resize(m_table->numCols()+10);
  1053. m_changedColumnNames.resize(m_table->numCols()+10);
  1054. }
  1055. }
  1056. if (m_1stRowForFieldNames->isChecked()) {
  1057. if ((row+m_startline)==1) {//this is for column name
  1058. if ((col-1) < (int)m_changedColumnNames.size() && (int)false==(int)m_changedColumnNames[col-1]) {
  1059. //this column has no custom name entered by a user
  1060. //-get the name from the data cell
  1061. TQString colName(text.simplifyWhiteSpace());
  1062. if (!colName.isEmpty()) {
  1063. if (colName.left(1)>="0" && colName.left(1)<="9")
  1064. colName.prepend(i18n("Column")+" ");
  1065. m_columnNames[ col-1 ] = colName;
  1066. }
  1067. }
  1068. return;
  1069. }
  1070. }
  1071. else {
  1072. if ((row+m_startline)==1) {//this row is for column name
  1073. if (m_1stRowForFieldNamesDetected && !m_1stRowForFieldNames->isChecked()) {
  1074. TQString f( text.simplifyWhiteSpace() );
  1075. if (f.isEmpty() || !f[0].isLetter())
  1076. m_1stRowForFieldNamesDetected = false; //this couldn't be a column name
  1077. }
  1078. }
  1079. row++; //1st row was for column names
  1080. }
  1081. if (row < 2) // skipped by the user
  1082. return;
  1083. if (m_table->numRows() < row) {
  1084. // if (m_maximumRowsForPreview >= row+100)
  1085. m_table->setNumRows(row+100); /* We add more rows at a time to limit recalculations */
  1086. //else
  1087. // m_table->setNumRows(m_maximumRowsForPreview);
  1088. m_table->verticalHeader()->setLabel(0, i18n("Column name")+" ");
  1089. m_adjustRows=true;
  1090. }
  1091. m_table->setText(row - 1, col - 1, (m_options.stripWhiteSpaceInTextValuesChecked ? text.stripWhiteSpace() : text));
  1092. m_table->verticalHeader()->setLabel(row-1, TQString::number(row-1));
  1093. detectTypeAndUniqueness(row-1, col-1, text);
  1094. }
  1095. bool KexiCSVImportDialog::saveRow(bool inGUI)
  1096. {
  1097. if (inGUI) {
  1098. //nothing to do
  1099. return true;
  1100. }
  1101. //save db buffer
  1102. bool res = m_importingStatement->execute();
  1103. //todo: move
  1104. m_importingStatement->clearArguments();
  1105. return res;
  1106. // return m_conn->insertRecord(*m_destinationTableSchema, m_dbRowBuffer);
  1107. }
  1108. void KexiCSVImportDialog::adjustRows(int iRows)
  1109. {
  1110. if (m_adjustRows)
  1111. {
  1112. m_table->setNumRows( iRows );
  1113. m_adjustRows=false;
  1114. for (int i = 0; i<iRows; i++)
  1115. m_table->adjustRow(i);
  1116. }
  1117. }
  1118. void KexiCSVImportDialog::formatChanged(int id)
  1119. {
  1120. if (id==_PK_FLAG) {
  1121. if (m_primaryKeyColumn>=0 && m_primaryKeyColumn<m_table->numCols()) {
  1122. m_table->setPixmap(0, m_primaryKeyColumn, TQPixmap());
  1123. }
  1124. if (m_primaryKeyField->isChecked()) {
  1125. m_primaryKeyColumn = m_table->currentColumn();
  1126. m_table->setPixmap(0, m_primaryKeyColumn, m_pkIcon);
  1127. }
  1128. else
  1129. m_primaryKeyColumn = -1;
  1130. return;
  1131. }
  1132. else {
  1133. m_detectedTypes[m_table->currentColumn()]=id;
  1134. m_primaryKeyField->setEnabled( _NUMBER_TYPE == id );
  1135. m_primaryKeyField->setChecked( m_primaryKeyColumn == m_table->currentColumn() && m_primaryKeyField->isEnabled() );
  1136. }
  1137. updateColumnText(m_table->currentColumn());
  1138. }
  1139. void KexiCSVImportDialog::delimiterChanged(const TQString& delimiter)
  1140. {
  1141. Q_UNUSED(delimiter);
  1142. m_columnsAdjusted = false;
  1143. m_detectDelimiter = false; //selected by hand: do not detect in the future
  1144. //delayed, otherwise combobox won't be repainted
  1145. fillTableLater();
  1146. }
  1147. void KexiCSVImportDialog::textquoteSelected(int)
  1148. {
  1149. const TQString tq(m_comboQuote->textQuote());
  1150. if (tq.isEmpty())
  1151. m_textquote = 0;
  1152. else
  1153. m_textquote = tq[0];
  1154. kexipluginsdbg << "KexiCSVImportDialog::textquoteSelected(): " << m_textquote << endl;
  1155. //delayed, otherwise combobox won't be repainted
  1156. fillTableLater();
  1157. }
  1158. void KexiCSVImportDialog::fillTableLater()
  1159. {
  1160. m_table->setNumRows( 0 );
  1161. TQTimer::singleShot(10, this, TQT_SLOT(fillTable()));
  1162. }
  1163. void KexiCSVImportDialog::startlineSelected(int startline)
  1164. {
  1165. // const int startline = line.toInt() - 1;
  1166. if (m_startline == (startline-1))
  1167. return;
  1168. m_startline = startline-1;
  1169. m_adjustRows=true;
  1170. fillTable();
  1171. m_table->setFocus();
  1172. }
  1173. void KexiCSVImportDialog::currentCellChanged(int, int col)
  1174. {
  1175. if (m_prevSelectedCol==col)
  1176. return;
  1177. m_prevSelectedCol = col;
  1178. int type = m_detectedTypes[col];
  1179. if (type==_FP_NUMBER_TYPE)
  1180. type=_NUMBER_TYPE; //we're simplifying that for now
  1181. m_formatCombo->setCurrentItem( type );
  1182. m_formatLabel->setText( m_formatComboText.arg(col+1) );
  1183. m_primaryKeyField->setEnabled( _NUMBER_TYPE == m_detectedTypes[col]);
  1184. m_primaryKeyField->blockSignals(true); //block to disable executing slotPrimaryKeyFieldToggled()
  1185. m_primaryKeyField->setChecked( m_primaryKeyColumn == col );
  1186. m_primaryKeyField->blockSignals(false);
  1187. }
  1188. void KexiCSVImportDialog::cellValueChanged(int row,int col)
  1189. {
  1190. if (row==0) {//column name has changed
  1191. m_columnNames[ col ] = m_table->text(row, col);
  1192. m_changedColumnNames.setBit( col );
  1193. }
  1194. }
  1195. void KexiCSVImportDialog::accept()
  1196. {
  1197. //! @todo MOVE MOST OF THIS TO CORE/ (KexiProject?) after KexiDialogBase code is moved to non-gui place
  1198. KexiGUIMessageHandler msg; //! @todo make it better integrated with main window
  1199. const uint numRows( m_table->numRows() );
  1200. if (numRows == 0)
  1201. return; //impossible
  1202. if (numRows == 1) {
  1203. if (KMessageBox::No == KMessageBox::questionYesNo(this,
  1204. i18n("Data set contains no rows. Do you want to import empty table?")))
  1205. return;
  1206. }
  1207. KexiProject* project = m_mainWin->project();
  1208. if (!project) {
  1209. msg.showErrorMessage(i18n("No project available."));
  1210. return;
  1211. }
  1212. m_conn = project->dbConnection(); //cache this pointer
  1213. if (!m_conn) {
  1214. msg.showErrorMessage(i18n("No database connection available."));
  1215. return;
  1216. }
  1217. KexiPart::Part *part = Kexi::partManager().partForMimeType("kexi/table");
  1218. if (!part) {
  1219. msg.showErrorMessage(&Kexi::partManager());
  1220. return;
  1221. }
  1222. //get suggested name based on the file name
  1223. TQString suggestedName;
  1224. if (m_mode==File) {
  1225. suggestedName = KURL::fromPathOrURL(m_fname).fileName();
  1226. //remove extension
  1227. if (!suggestedName.isEmpty()) {
  1228. const int idx = suggestedName.findRev(".");
  1229. if (idx!=-1)
  1230. suggestedName = suggestedName.mid(0, idx ).simplifyWhiteSpace();
  1231. }
  1232. }
  1233. //-new part item
  1234. KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), suggestedName);
  1235. if (!partItemForSavedTable) {
  1236. // msg.showErrorMessage(project);
  1237. return;
  1238. }
  1239. #define _ERR \
  1240. { project->deleteUnstoredItem(partItemForSavedTable); \
  1241. m_conn = 0; \
  1242. delete m_destinationTableSchema; \
  1243. m_destinationTableSchema = 0; \
  1244. return; }
  1245. //-ask for table name/title
  1246. // (THIS IS FROM KexiMainWindowImpl::saveObject())
  1247. bool allowOverwriting = true;
  1248. tristate res = m_mainWin->getNewObjectInfo( partItemForSavedTable, part, allowOverwriting );
  1249. if (~res || !res) {
  1250. //! @todo: err
  1251. _ERR;
  1252. }
  1253. //(allowOverwriting is now set to true, if user accepts overwriting,
  1254. // and overwriting will be needed)
  1255. // KexiDB::SchemaData sdata(part->info()->projectPartID());
  1256. // sdata.setName( partItem->name() );
  1257. //-create table schema (and thus schema object)
  1258. //-assign information (THIS IS FROM KexiDialogBase::storeNewData())
  1259. m_destinationTableSchema = new KexiDB::TableSchema(partItemForSavedTable->name());
  1260. m_destinationTableSchema->setCaption( partItemForSavedTable->caption() );
  1261. m_destinationTableSchema->setDescription( partItemForSavedTable->description() );
  1262. const uint numCols( m_table->numCols() );
  1263. m_implicitPrimaryKeyAdded = false;
  1264. //add PK if user wanted it
  1265. int msgboxResult;
  1266. if (m_primaryKeyColumn==-1
  1267. && KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(this,
  1268. i18n("No Primary Key (autonumber) has been defined.\n"
  1269. "Should it be automatically defined on import (recommended)?\n\n"
  1270. "Note: An imported table without a Primary Key may not be editable (depending on database type)."),
  1271. TQString(), KGuiItem(i18n("Add Database Primary Key to a Table", "Add Primary Key"), "key"),
  1272. KGuiItem(i18n("Do Not Add Database Primary Key to a Table", "Do Not Add")))))
  1273. {
  1274. if (msgboxResult == KMessageBox::Cancel)
  1275. _ERR; //cancel accepting
  1276. //add implicit PK field
  1277. //! @todo make this field hidden (what about e.g. pgsql?)
  1278. m_implicitPrimaryKeyAdded = true;
  1279. TQString fieldName("id");
  1280. TQString fieldCaption("Id");
  1281. TQStringList colnames;
  1282. for (uint col = 0; col < numCols; col++)
  1283. colnames.append( m_table->text(0, col).lower().simplifyWhiteSpace() );
  1284. if (colnames.find(fieldName)!=colnames.end()) {
  1285. int num = 1;
  1286. while (colnames.find(fieldName+TQString::number(num))!=colnames.end())
  1287. num++;
  1288. fieldName += TQString::number(num);
  1289. fieldCaption += TQString::number(num);
  1290. }
  1291. KexiDB::Field *field = new KexiDB::Field(
  1292. fieldName,
  1293. KexiDB::Field::Integer,
  1294. KexiDB::Field::NoConstraints,
  1295. KexiDB::Field::NoOptions,
  1296. 0,0, //uint length=0, uint precision=0,
  1297. TQVariant(), //TQVariant defaultValue=TQVariant(),
  1298. fieldCaption
  1299. ); //no description and width for now
  1300. field->setPrimaryKey(true);
  1301. field->setAutoIncrement(true);
  1302. m_destinationTableSchema->addField( field );
  1303. }
  1304. for (uint col = 0; col < numCols; col++) {
  1305. TQString fieldCaption( m_table->text(0, col).simplifyWhiteSpace() );
  1306. TQString fieldName( KexiUtils::string2Identifier( fieldCaption ) );
  1307. if (m_destinationTableSchema->field(fieldName)) {
  1308. TQString fixedFieldName;
  1309. uint i = 2; //"apple 2, apple 3, etc. if there're many "apple" names
  1310. do {
  1311. fixedFieldName = fieldName + "_" + TQString::number(i);
  1312. if (!m_destinationTableSchema->field(fixedFieldName))
  1313. break;
  1314. i++;
  1315. } while (true);
  1316. fieldName = fixedFieldName;
  1317. fieldCaption += (" " + TQString::number(i));
  1318. }
  1319. const int detectedType = m_detectedTypes[col];
  1320. KexiDB::Field::Type fieldType;
  1321. if (detectedType==_DATE_TYPE)
  1322. fieldType = KexiDB::Field::Date;
  1323. if (detectedType==_TIME_TYPE)
  1324. fieldType = KexiDB::Field::Time;
  1325. if (detectedType==_DATETIME_TYPE)
  1326. fieldType = KexiDB::Field::DateTime;
  1327. else if (detectedType==_NUMBER_TYPE)
  1328. fieldType = KexiDB::Field::Integer;
  1329. else if (detectedType==_FP_NUMBER_TYPE)
  1330. fieldType = KexiDB::Field::Double;
  1331. //! @todo what about time and float/double types and different integer subtypes?
  1332. else //_TEXT_TYPE and the rest
  1333. fieldType = KexiDB::Field::Text;
  1334. //! @todo what about long text?
  1335. KexiDB::Field *field = new KexiDB::Field(
  1336. fieldName,
  1337. fieldType,
  1338. KexiDB::Field::NoConstraints,
  1339. KexiDB::Field::NoOptions,
  1340. 0,0, //uint length=0, uint precision=0,
  1341. TQVariant(), //TQVariant defaultValue=TQVariant(),
  1342. fieldCaption
  1343. ); //no description and width for now
  1344. if ((int)col == m_primaryKeyColumn) {
  1345. field->setPrimaryKey(true);
  1346. field->setAutoIncrement(true);
  1347. }
  1348. m_destinationTableSchema->addField( field );
  1349. }
  1350. KexiDB::Transaction transaction = m_conn->beginTransaction();
  1351. if (transaction.isNull()) {
  1352. msg.showErrorMessage(m_conn);
  1353. _ERR;
  1354. }
  1355. KexiDB::TransactionGuard tg(transaction);
  1356. //-create physical table
  1357. if (!m_conn->createTable(m_destinationTableSchema, allowOverwriting)) {
  1358. msg.showErrorMessage(m_conn);
  1359. _ERR;
  1360. }
  1361. #define _DROP_DEST_TABLE_AND_RETURN \
  1362. { \
  1363. if (m_importingProgressDlg) \
  1364. m_importingProgressDlg->hide(); \
  1365. project->deleteUnstoredItem(partItemForSavedTable); \
  1366. m_conn->dropTable(m_destinationTableSchema); /*alsoRemoveSchema*/ \
  1367. m_destinationTableSchema = 0; \
  1368. m_conn = 0; \
  1369. return; \
  1370. }
  1371. m_importingStatement = m_conn->prepareStatement(
  1372. KexiDB::PreparedStatement::InsertStatement, *m_destinationTableSchema);
  1373. if (!m_importingStatement) {
  1374. msg.showErrorMessage(m_conn);
  1375. _DROP_DEST_TABLE_AND_RETURN;
  1376. }
  1377. if (m_file) {
  1378. if (!m_importingProgressDlg) {
  1379. m_importingProgressDlg = new KProgressDialog( this, "m_importingProgressDlg",
  1380. i18n("Importing CSV Data"), TQString(), true );
  1381. }
  1382. m_importingProgressDlg->setLabel(
  1383. i18n("Importing CSV Data from <nobr>\"%1\"</nobr> into \"%2\" table...")
  1384. .arg(TQDir::convertSeparators(m_fname)).arg(m_destinationTableSchema->name()) );
  1385. m_importingProgressDlg->progressBar()->setTotalSteps( TQFileInfo(*m_file).size() );
  1386. m_importingProgressDlg->show();
  1387. }
  1388. int row, column, maxColumn;
  1389. TQString field = TQString();
  1390. // main job
  1391. res = loadRows(field, row, column, maxColumn, false /*!gui*/ );
  1392. delete m_importingProgressDlg;
  1393. m_importingProgressDlg = 0;
  1394. if (true != res) {
  1395. //importing cancelled or failed
  1396. if (!res) //do not display err msg when res == cancelled
  1397. msg.showErrorMessage(m_conn);
  1398. _DROP_DEST_TABLE_AND_RETURN;
  1399. }
  1400. // file with only one line without '\n'
  1401. if (field.length() > 0)
  1402. {
  1403. setText(row - m_startline, column, field, false /*!gui*/);
  1404. //fill remaining empty fields (database wants them explicitly)
  1405. for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) {
  1406. setText(row - m_startline, additionalColumn, TQString(), false /*!gui*/);
  1407. }
  1408. if (!saveRow(false /*!gui*/)) {
  1409. msg.showErrorMessage(m_conn);
  1410. _DROP_DEST_TABLE_AND_RETURN;
  1411. }
  1412. ++row;
  1413. field = TQString();
  1414. }
  1415. if (!tg.commit()) {
  1416. msg.showErrorMessage(m_conn);
  1417. _DROP_DEST_TABLE_AND_RETURN;
  1418. }
  1419. //-now we can store the item
  1420. partItemForSavedTable->setIdentifier( m_destinationTableSchema->id() );
  1421. project->addStoredItem( part->info(), partItemForSavedTable );
  1422. TQDialog::accept();
  1423. KMessageBox::information(this, i18n("Data has been successfully imported to table \"%1\".")
  1424. .arg(m_destinationTableSchema->name()));
  1425. parentWidget()->raise();
  1426. m_conn = 0;
  1427. }
  1428. int KexiCSVImportDialog::getHeader(int col)
  1429. {
  1430. TQString header = m_table->horizontalHeader()->label(col);
  1431. if (header == i18n("Text type for column", "Text"))
  1432. return TEXT;
  1433. else if (header == i18n("Numeric type for column", "Number"))
  1434. return NUMBER;
  1435. else if (header == i18n("Currency type for column", "Currency"))
  1436. return CURRENCY;
  1437. else
  1438. return DATE;
  1439. }
  1440. TQString KexiCSVImportDialog::getText(int row, int col)
  1441. {
  1442. return m_table->text(row, col);
  1443. }
  1444. void KexiCSVImportDialog::ignoreDuplicatesChanged(int)
  1445. {
  1446. fillTable();
  1447. }
  1448. void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(int)
  1449. {
  1450. m_adjustRows=true;
  1451. if (m_1stRowForFieldNames->isChecked() && m_startline>0 && m_startline>=(m_startAtLineSpinBox->maxValue()-1))
  1452. m_startline--;
  1453. fillTable();
  1454. }
  1455. void KexiCSVImportDialog::optionsButtonClicked()
  1456. {
  1457. KexiCSVImportOptionsDialog dlg(m_options, this);
  1458. if (TQDialog::Accepted != dlg.exec())
  1459. return;
  1460. KexiCSVImportOptions newOptions( dlg.options() );
  1461. if (m_options != newOptions) {
  1462. m_options = newOptions;
  1463. if (!openData())
  1464. return;
  1465. fillTable();
  1466. }
  1467. }
  1468. bool KexiCSVImportDialog::eventFilter ( TQObject * watched, TQEvent * e )
  1469. {
  1470. TQEvent::Type t = e->type();
  1471. // temporary disable keyboard and mouse events for time-consuming tasks
  1472. if (m_blockUserEvents && (t==TQEvent::KeyPress || t==TQEvent::KeyRelease
  1473. || t==TQEvent::MouseButtonPress || t==TQEvent::MouseButtonDblClick
  1474. || t==TQEvent::Paint ))
  1475. return true;
  1476. if (TQT_BASE_OBJECT(watched) == TQT_BASE_OBJECT(m_startAtLineSpinBox) && t==TQEvent::KeyPress) {
  1477. TQKeyEvent *ke = TQT_TQKEYEVENT(e);
  1478. if (ke->key()==TQt::Key_Enter || ke->key()==TQt::Key_Return) {
  1479. m_table->setFocus();
  1480. return true;
  1481. }
  1482. }
  1483. return TQDialog::eventFilter( watched, e );
  1484. }
  1485. void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(bool on)
  1486. {
  1487. Q_UNUSED(on);
  1488. formatChanged(_PK_FLAG);
  1489. }
  1490. void KexiCSVImportDialog::updateRowCountInfo()
  1491. {
  1492. m_infoLbl->setFileName( m_fname );
  1493. if (m_allRowsLoadedInPreview) {
  1494. m_infoLbl->setCommentText(
  1495. i18n("row count", "(rows: %1)").arg( m_table->numRows()-1+m_startline ) );
  1496. TQToolTip::remove( m_infoLbl );
  1497. }
  1498. else {
  1499. m_infoLbl->setCommentText(
  1500. i18n("row count", "(rows: more than %1)").arg( m_table->numRows()-1+m_startline ) );
  1501. TQToolTip::add( m_infoLbl->commentLabel(), i18n("Not all rows are visible on this preview") );
  1502. }
  1503. }
  1504. #include "kexicsvimportdialog.moc"