diff --git a/src/core/KexiFileFilters.h b/src/core/KexiFileFilters.h index 5da7bda9d..dbbb1b897 100644 --- a/src/core/KexiFileFilters.h +++ b/src/core/KexiFileFilters.h @@ -1,115 +1,117 @@ /* This file is part of the KDE project Copyright (C) 2003-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIFILEFILTERS_H #define KEXIFILEFILTERS_H #include "kexicore_export.h" class QMimeType; class QString; class QStringList; //! A tool for handling file filters for Kexi class KEXICORE_EXPORT KexiFileFilters { public: //! Filter mode enum Mode { Opening, //!< Opening opens existing database (or shortcut) CustomOpening, //!< Used for opening other files, like CSV SavingFileBasedDB, //!< Saving file-based database file CustomSavingFileBasedDB, //!< Used for saving other files, like CSV SavingServerBasedDB //!< Saving server-based (shortcut) file }; KexiFileFilters(); ~KexiFileFilters(); + //! @return mode for the filer Mode mode() const; + //! Sets mode for the filter void setMode(Mode mode); /*! Sets a default-filter, that is used when an empty filter is set. * By default, this is set to i18n("*|All Files") * @see defaultFilter */ void setDefaultFilter(const QString &filter); /** * @return the default filter, used when an empty filter is set. * @see setDefaultFilter */ QString defaultFilter() const; //! @return additional mime types QStringList additionalMimeTypes() const; //! Sets additional mime types, e.g. "text/x-csv" void setAdditionalMimeTypes(const QStringList &mimeTypes); //! @return excluded mime types QStringList excludedMimeTypes() const; //! Set excluded mime types void setExcludedMimeTypes(const QStringList &mimeTypes); //! @return glob patterns for all mime types for given mode //! Includes additional mime types and excludes miem types specified by excludedMimeTypes(). QStringList allGlobPatterns() const; //! @return @c true if existing file is required //! This is true for Opening and CustomOpening modes. bool isExistingFileRequired() const; enum Format { QtFormat, //!< QFileDialog-compatible format, e.g. "Image files (*.png *.xpm *.jpg)", ";;" separators KDEFormat, //!< KDE-compatible format, e.g. "*.png *.xpm *.jpg|Image files (*.png *.xpm *.jpg)", "\\n" separators KUrlRequesterFormat //!< KUrlRequester-compatible format, e.g. "*.png *.xpm *.jpg|Image files", "\\n" separators }; static QString separator(KexiFileFilters::Format format); //! @return filters based on supplied parameters in given format QString toString(Format format) const; //! @return list of filters based on supplied parameters in given format QStringList toList(Format format) const; //! @return filter string in given format static QString toString(const QMimeType &mime, Format format); //! @overload QString toString(const QMimeType &mime, Format format); static QString toString(const QString& mimeName, Format format); //! @overload QString toString(const QMimeType &mime, Format format); static QString toString(const QStringList &patterns, const QString &comment, Format format); //! Static version of QString KexiFileFilters::toString(Format format) const static QString toString(const QStringList& mimeNames, Format format); //! Static version of QStringList toList(Format format) const static QStringList toList(const QStringList& mimeNames, Format format); private: class Private; Private * const d; }; #endif diff --git a/src/main/startup/KexiOpenProjectAssistant.cpp b/src/main/startup/KexiOpenProjectAssistant.cpp index d6b8c376e..fd7fc0ba8 100644 --- a/src/main/startup/KexiOpenProjectAssistant.cpp +++ b/src/main/startup/KexiOpenProjectAssistant.cpp @@ -1,318 +1,317 @@ /* This file is part of the KDE project Copyright (C) 2011-2013 Jarosław Staniek Copyright (C) 2012 Dimitrios T. Tanis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiOpenProjectAssistant.h" #include "KexiStartup.h" #include "KexiPasswordPage.h" #include "ui_KexiProjectStorageTypeSelectionPage.h" #include #include #include #include #include #include #include #include #include #include #include #include KexiMainOpenProjectPage::KexiMainOpenProjectPage(QWidget* parent) : KexiAssistantPage(xi18nc("@title:window", "Open Project"), xi18nc("@info", "Select project to open. " "You can choose project stored in file or on database server."), parent) , connSelector(0) , m_errorMessagePopup(0) { setNextButtonVisible(true); tabWidget = new QTabWidget; tabWidget->setElideMode(Qt::ElideNone); //! @todo KEXI3 tabWidget->setAutomaticResizeTabs(true); tabWidget->setDocumentMode(true); m_fileSelectorWidget = new QWidget; tabWidget->addTab(m_fileSelectorWidget, Kexi::defaultFileBasedDriverIcon(), xi18nc("@title:tab", "Projects Stored in File")); fileSelector = new KexiConnectionSelectorWidget( &Kexi::connset(), QUrl("kfiledialog:///OpenExistingOrCreateNewProject"), KexiConnectionSelectorWidget::Opening); fileSelector->hide(); // delayed opening fileSelector->showSimpleConnection(); fileSelector->hideHelpers(); fileSelector->hideDescription(); - //connect(fileSelector->fileWidget, SIGNAL(accepted()), this, SLOT(accept())); - connect(fileSelector, SIGNAL(fileSelectionChanged()), this, SLOT(next())); + fileSelector->setFileWidgetFrameVisible(false); + connect(fileSelector, &KexiConnectionSelectorWidget::fileSelected, this, [=]() { next(); }); m_connSelectorWidget = new QWidget; tabWidget->addTab(m_connSelectorWidget, Kexi::serverIcon(), xi18nc("@title:tab", "Projects Stored on Database Server")); setFocusWidget(tabWidget); setContents(tabWidget); connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); // delayed opening: QTimer::singleShot(500, this, SLOT(init())); } void KexiMainOpenProjectPage::init() { // file-based: QVBoxLayout* fileSelectorLayout = new QVBoxLayout(m_fileSelectorWidget); fileSelectorLayout->setContentsMargins(0, KexiUtils::marginHint() * 2, 0, 0); fileSelectorLayout->addWidget(fileSelector); fileSelector->show(); } void KexiMainOpenProjectPage::tabChanged(int index) { QVBoxLayout* connSelectorLayout; if (!m_connSelectorWidget->layout()) { connSelectorLayout = new QVBoxLayout(m_connSelectorWidget); } else { connSelectorLayout = dynamic_cast(m_connSelectorWidget->layout()); } if (index == 1) { if (KDbDriverManager().hasDatabaseServerDrivers()) { if (!connSelector) { // server-based: connSelectorLayout->setContentsMargins(0, KexiUtils::marginHint() * 2, 0, 0); QLabel* connSelectorLabel = new QLabel( xi18nc("@info", "Select database server's connection with project you wish to open." "Here you may also add, edit or remove connections from the list.")); connSelectorLayout->addWidget(connSelectorLabel); connSelectorLayout->addSpacing(KexiUtils::marginHint()); connSelector = new KexiConnectionSelectorWidget( &Kexi::connset(), QUrl("kfiledialog:///OpenExistingOrCreateNewProject"), KexiConnectionSelectorWidget::Opening); connSelectorLayout->addWidget(connSelector); connSelector->showAdvancedConnection(); connSelector->layout()->setContentsMargins(0, 0, 0, 0); connSelector->hideHelpers(); connSelector->hideDescription(); connect(connSelector, SIGNAL(connectionItemExecuted(ConnectionDataLVItem*)), this, SLOT(next())); } } else { if (!m_errorMessagePopup) { setNextButtonVisible(false); setDescription(QString()); m_errorMessagePopup = new KexiServerDriverNotFoundMessage(m_connSelectorWidget); connSelectorLayout->addSpacing(KexiUtils::marginHint()); connSelectorLayout->addWidget(m_errorMessagePopup); connSelectorLayout->setAlignment(m_errorMessagePopup, Qt::AlignTop); m_errorMessagePopup->setAutoDelete(false); m_errorMessagePopup->animatedShow(); } } } } KexiMainOpenProjectPage::~KexiMainOpenProjectPage() { } // ---- KexiProjectDatabaseSelectionPage::KexiProjectDatabaseSelectionPage( KexiOpenProjectAssistant* parent) : KexiAssistantPage(xi18nc("@title:window", "Open Project on Database Server"), QString(), parent) , conndataToShow(0) , m_assistant(parent) { setBackButtonVisible(true); setNextButtonVisible(true); nextButton()->setLinkText(xi18n("Open")); projectSelector = new KexiProjectSelectorWidget( this, 0, true, // showProjectNameColumn false // showConnectionColumns ); projectSelector->label()->hide(); connect(projectSelector, SIGNAL(projectExecuted(KexiProjectData*)), m_assistant, SLOT(slotOpenProject(KexiProjectData*))); setFocusWidget(projectSelector); setContents(projectSelector); } KexiProjectDatabaseSelectionPage::~KexiProjectDatabaseSelectionPage() { } bool KexiProjectDatabaseSelectionPage::setConnection(KDbConnectionData* data) { projectSelector->setProjectSet(0); conndataToShow = 0; if (data) { KexiProjectSet *projectSetToShow = new KexiProjectSet(m_assistant); if (!projectSetToShow->setConnectionData(data)) { //! @todo show error delete projectSetToShow; return false; } conndataToShow = data; //-refresh projects list projectSelector->setProjectSet(projectSetToShow); } if (conndataToShow) { setDescription( xi18nc("@info", "Select project on database server %1 (%2) to open.", conndataToShow->caption(), conndataToShow->toUserVisibleString())); } return true; } // ---- class Q_DECL_HIDDEN KexiOpenProjectAssistant::Private { public: explicit Private(KexiOpenProjectAssistant *qq) : q(qq) { } ~Private() { } KexiMainOpenProjectPage* projectOpenPage() { return page(&m_projectOpenPage); } KexiProjectDatabaseSelectionPage* projectDatabaseSelectionPage() { return page(&m_projectDatabaseSelectionPage, q); } KexiPasswordPage* passwordPage() { return page(&m_passwordPage, q); } template C* page(QPointer* p, KexiOpenProjectAssistant *parent = 0) { if (p->isNull()) { *p = new C(parent); q->addPage(*p); } return *p; } QPointer m_projectOpenPage; QPointer m_projectDatabaseSelectionPage; QPointer m_passwordPage; KexiOpenProjectAssistant *q; }; // ---- KexiOpenProjectAssistant::KexiOpenProjectAssistant(QWidget* parent) : KexiAssistantWidget(parent) , d(new Private(this)) { setCurrentPage(d->projectOpenPage()); setFocusProxy(d->projectOpenPage()); } KexiOpenProjectAssistant::~KexiOpenProjectAssistant() { delete d; } void KexiOpenProjectAssistant::nextPageRequested(KexiAssistantPage* page) { if (page == d->m_projectOpenPage) { if (d->m_projectOpenPage->tabWidget->currentIndex() == 0) { // file-based if (!d->m_projectOpenPage->fileSelector->checkSelectedFile()) return; - emit openProject( - d->m_projectOpenPage->fileSelector->highlightedFile()); + emit openProject(d->m_projectOpenPage->fileSelector->selectedFile()); } else { // server-based KDbConnectionData *cdata = d->m_projectOpenPage->connSelector->selectedConnectionData(); if (cdata) { if (cdata->isPasswordNeeded()) { d->passwordPage()->setConnectionData(*cdata); setCurrentPage(d->passwordPage()); return; } if (d->projectDatabaseSelectionPage()->setConnection(cdata)) { setCurrentPage(d->projectDatabaseSelectionPage()); } } } } else if (page == d->m_passwordPage) { KDbConnectionData *cdata = d->projectOpenPage()->connSelector->selectedConnectionData(); d->passwordPage()->updateConnectionData(cdata); if (cdata && d->projectDatabaseSelectionPage()->setConnection(cdata)) { setCurrentPage(d->projectDatabaseSelectionPage()); } } else if (page == d->m_projectDatabaseSelectionPage) { slotOpenProject( d->m_projectDatabaseSelectionPage->projectSelector->selectedProjectData()); } } void KexiOpenProjectAssistant::cancelRequested(KexiAssistantPage* page) { Q_UNUSED(page); //! @todo } void KexiOpenProjectAssistant::slotOpenProject(KexiProjectData* data) { if (data) emit openProject(*data); } void KexiOpenProjectAssistant::tryAgainActionTriggered() { messageWidget()->animatedHide(); currentPage()->next(); } void KexiOpenProjectAssistant::cancelActionTriggered() { if (currentPage() == d->m_passwordPage) { d->passwordPage()->focusWidget()->setFocus(); } } const QWidget* KexiOpenProjectAssistant::calloutWidget() const { return currentPage()->nextButton(); } diff --git a/src/migration/importtablewizard.cpp b/src/migration/importtablewizard.cpp index da5fbeadb..2a0d6ceef 100644 --- a/src/migration/importtablewizard.cpp +++ b/src/migration/importtablewizard.cpp @@ -1,784 +1,785 @@ /* This file is part of the KDE project Copyright (C) 2009 Adam Pigg Copyright (C) 2014-2016 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "importtablewizard.h" #include "importoptionsdlg.h" #include "migratemanager.h" #include "keximigrate.h" #include "keximigratedata.h" #include "AlterSchemaWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; #define RECORDS_FOR_PREVIEW 3 ImportTableWizard::ImportTableWizard ( KDbConnection* curDB, QWidget* parent, QMap* args, Qt::WindowFlags flags) : KAssistantDialog ( parent, flags ), m_args(args) { m_connection = curDB; m_migrateDriver = 0; m_prjSet = 0; m_importComplete = false; m_importWasCanceled = false; m_sourceDbEncoding = QString::fromLatin1(KexiUtils::encoding()); //default KexiMainWindowIface::global()->setReasonableDialogSize(this); setupIntroPage(); setupSrcConn(); setupSrcDB(); setupTableSelectPage(); setupAlterTablePage(); setupImportingPage(); setupProgressPage(); setupFinishPage(); setValid(m_srcConnPageItem, false); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slot_currentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); //! @todo Change this to message prompt when we move to non-dialog wizard. connect(m_srcConnSel, SIGNAL(connectionSelected(bool)), this, SLOT(slotConnPageItemSelected(bool))); } ImportTableWizard::~ImportTableWizard() { delete m_prjSet; delete m_srcConnSel; } void ImportTableWizard::back() { KAssistantDialog::back(); } void ImportTableWizard::next() { if (currentPage() == m_srcConnPageItem) { if (fileBasedSrcSelected()) { setAppropriate(m_srcDBPageItem, false); } else { setAppropriate(m_srcDBPageItem, true); } } else if (currentPage() == m_alterTablePageItem) { if (m_alterSchemaWidget->nameExists(m_alterSchemaWidget->nameWidget()->nameText())) { KMessageBox::information(this, xi18nc("@info", "%1 name is already used by an existing table. " "Enter different table name to continue.", m_alterSchemaWidget->nameWidget()->nameText()), xi18n("Name Already Used")); return; } } KAssistantDialog::next(); } void ImportTableWizard::accept() { if (m_args) { if (m_finishCheckBox->isChecked()) { m_args->insert("destinationTableName",m_alterSchemaWidget->nameWidget()->nameText()); } else { m_args->remove("destinationTableName"); } } QDialog::accept(); } void ImportTableWizard::reject() { QDialog::reject(); } //=========================================================== // void ImportTableWizard::setupIntroPage() { m_introPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); m_introPageWidget->setLayout(vbox); KexiUtils::setStandardMarginsAndSpacing(vbox); QLabel *lblIntro = new QLabel(m_introPageWidget); lblIntro->setAlignment(Qt::AlignTop | Qt::AlignLeft); lblIntro->setWordWrap(true); lblIntro->setText( xi18nc("@info", "Table Importing Assistant allows you to import a table from an existing " "database into the current Kexi project." "Click Next button to continue or " "Cancel button to exit this assistant.")); vbox->addWidget(lblIntro); m_introPageItem = new KPageWidgetItem(m_introPageWidget, xi18n("Welcome to the Table Importing Assistant")); addPage(m_introPageItem); } void ImportTableWizard::setupSrcConn() { m_srcConnPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(m_srcConnPageWidget); m_srcConnSel = new KexiConnectionSelectorWidget(&Kexi::connset(), QUrl("kfiledialog:///ProjectMigrationSourceDir"), KexiConnectionSelectorWidget::Opening, m_srcConnPageWidget); m_srcConnSel->hideConnectonIcon(); m_srcConnSel->showSimpleConnection(); //! @todo remove when support for kexi files as source prj is added in migration const QStringList excludedMimeTypes({ KDb::defaultFileBasedDriverMimeType(), "application/x-kexiproject-shortcut", "application/x-kexi-connectiondata"}); m_srcConnSel->setExcludedMimeTypes(excludedMimeTypes); vbox->addWidget(m_srcConnSel); m_srcConnPageItem = new KPageWidgetItem(m_srcConnPageWidget, xi18n("Select Location for Source Database")); addPage(m_srcConnPageItem); } void ImportTableWizard::setupSrcDB() { // arrivesrcdbPage creates widgets on that page m_srcDBPageWidget = new QWidget(this); m_srcDBName = NULL; m_srcDBPageItem = new KPageWidgetItem(m_srcDBPageWidget, xi18n("Select Source Database")); addPage(m_srcDBPageItem); } void ImportTableWizard::setupTableSelectPage() { m_tablesPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(m_tablesPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_tableListWidget = new QListWidget(this); m_tableListWidget->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_tableListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(slotTableListWidgetSelectionChanged())); vbox->addWidget(m_tableListWidget); m_tablesPageItem = new KPageWidgetItem(m_tablesPageWidget, xi18n("Select the Table to Import")); addPage(m_tablesPageItem); } //=========================================================== // void ImportTableWizard::setupImportingPage() { m_importingPageWidget = new QWidget(this); m_importingPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_importingPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_lblImportingTxt = new QLabel(m_importingPageWidget); m_lblImportingTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_lblImportingTxt->setWordWrap(true); m_lblImportingErrTxt = new QLabel(m_importingPageWidget); m_lblImportingErrTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_lblImportingErrTxt->setWordWrap(true); vbox->addWidget(m_lblImportingTxt); vbox->addWidget(m_lblImportingErrTxt); vbox->addStretch(1); QWidget *options_widget = new QWidget(m_importingPageWidget); vbox->addWidget(options_widget); QVBoxLayout *options_vbox = new QVBoxLayout(options_widget); options_vbox->setSpacing(KexiUtils::spacingHint()); m_importOptionsButton = new QPushButton(koIcon("configure"), xi18n("Advanced Options"), options_widget); connect(m_importOptionsButton, SIGNAL(clicked()),this, SLOT(slotOptionsButtonClicked())); options_vbox->addWidget(m_importOptionsButton); options_vbox->addStretch(1); m_importingPageWidget->show(); m_importingPageItem = new KPageWidgetItem(m_importingPageWidget, xi18n("Importing")); addPage(m_importingPageItem); } void ImportTableWizard::setupAlterTablePage() { m_alterTablePageWidget = new QWidget(this); m_alterTablePageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_alterTablePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_alterSchemaWidget = new KexiMigration::AlterSchemaWidget(this); vbox->addWidget(m_alterSchemaWidget); m_alterTablePageWidget->show(); m_alterTablePageItem = new KPageWidgetItem(m_alterTablePageWidget, xi18n("Alter the Detected Table Design")); addPage(m_alterTablePageItem); } void ImportTableWizard::setupProgressPage() { m_progressPageWidget = new QWidget(this); m_progressPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_progressPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_progressPageWidget->setLayout(vbox); m_progressLbl = new QLabel(m_progressPageWidget); m_progressLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_progressLbl->setWordWrap(true); m_rowsImportedLbl = new QLabel(m_progressPageWidget); m_importingProgressBar = new QProgressBar(m_progressPageWidget); m_importingProgressBar->setMinimum(0); m_importingProgressBar->setMaximum(0); m_importingProgressBar->setValue(0); vbox->addWidget(m_progressLbl); vbox->addWidget(m_rowsImportedLbl); vbox->addWidget(m_importingProgressBar); vbox->addStretch(1); m_progressPageItem = new KPageWidgetItem(m_progressPageWidget, xi18n("Processing Import")); addPage(m_progressPageItem); } void ImportTableWizard::setupFinishPage() { m_finishPageWidget = new QWidget(this); m_finishPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(m_finishPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_finishLbl = new QLabel(m_finishPageWidget); m_finishLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_finishLbl->setWordWrap(true); vbox->addWidget(m_finishLbl); m_finishCheckBox = new QCheckBox(xi18n("Open imported table"), m_finishPageWidget); m_finishCheckBox->setChecked(true); vbox->addSpacing(KexiUtils::spacingHint()); vbox->addWidget(m_finishCheckBox); vbox->addStretch(1); m_finishPageItem = new KPageWidgetItem(m_finishPageWidget, xi18n("Success")); addPage(m_finishPageItem); } void ImportTableWizard::slot_currentPageChanged(KPageWidgetItem* curPage,KPageWidgetItem* prevPage) { Q_UNUSED(prevPage); if (curPage == m_introPageItem) { } else if (curPage == m_srcConnPageItem) { arriveSrcConnPage(); } else if (curPage == m_srcDBPageItem) { arriveSrcDBPage(); } else if (curPage == m_tablesPageItem) { arriveTableSelectPage(prevPage); } else if (curPage == m_alterTablePageItem) { if (prevPage == m_tablesPageItem) { arriveAlterTablePage(); } } else if (curPage == m_importingPageItem) { arriveImportingPage(); } else if (curPage == m_progressPageItem) { arriveProgressPage(); } else if (curPage == m_finishPageItem) { arriveFinishPage(); } } void ImportTableWizard::arriveSrcConnPage() { } void ImportTableWizard::arriveSrcDBPage() { if (fileBasedSrcSelected()) { //! @todo Back button doesn't work after selecting a file to import } else if (!m_srcDBName) { m_srcDBPageWidget->hide(); qDebug() << "Looks like we need a project selector widget!"; KDbConnectionData* conndata = m_srcConnSel->selectedConnectionData(); if (conndata) { KexiGUIMessageHandler handler; m_prjSet = new KexiProjectSet(&handler); if (!m_prjSet->setConnectionData(conndata)) { handler.showErrorMessage(m_prjSet->result()); delete m_prjSet; m_prjSet = 0; return; } QVBoxLayout *vbox = new QVBoxLayout(m_srcDBPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); m_srcDBName = new KexiProjectSelectorWidget(m_srcDBPageWidget, m_prjSet); vbox->addWidget(m_srcDBName); m_srcDBName->label()->setText(xi18n("Select source database you wish to import:")); } m_srcDBPageWidget->show(); } } void ImportTableWizard::arriveTableSelectPage(KPageWidgetItem *prevPage) { if (prevPage == m_alterTablePageItem) { if (m_tableListWidget->count() == 1) { //we was skiping it before back(); } } else { Kexi::ObjectStatus result; KexiUtils::WaitCursor wait; m_tableListWidget->clear(); m_migrateDriver = prepareImport(&result); bool ok = m_migrateDriver; if (ok) { if (!m_sourceDbEncoding.isEmpty()) { m_migrateDriver->setPropertyValue( "source_database_nonunicode_encoding", QVariant(m_sourceDbEncoding.toUpper().remove(' ')) // "CP1250", not "cp 1250" ); } ok = m_migrateDriver->connectSource(&result); } if (ok) { QStringList tableNames; if (m_migrateDriver->tableNames(&tableNames)) { m_tableListWidget->addItems(tableNames); } if (m_tableListWidget->item(0)) { m_tableListWidget->item(0)->setSelected(true); if (m_tableListWidget->count() == 1) { KexiUtils::removeWaitCursor(); next(); } } } KexiUtils::removeWaitCursor(); if (!ok) { QString errMessage =result.message.isEmpty() ? xi18n("Unknown error") : result.message; QString errDescription = result.description.isEmpty() ? errMessage : result.description; KMessageBox::error(this, errMessage, errDescription); setValid(m_tablesPageItem, false); } } } void ImportTableWizard::arriveAlterTablePage() { //! @todo handle errors if (m_tableListWidget->selectedItems().isEmpty()) return; //! @todo (js) support multiple tables? #if 0 foreach(QListWidgetItem *table, m_tableListWidget->selectedItems()) { m_importTableName = table->text(); } #else m_importTableName = m_tableListWidget->selectedItems().first()->text(); #endif QScopedPointer ts(new KDbTableSchema); if (!m_migrateDriver->readTableSchema(m_importTableName, ts.data())) { return; } setValid(m_alterTablePageItem, ts->fieldCount() > 0); if (isValid(m_alterTablePageItem)) { connect(m_alterSchemaWidget->nameWidget(), SIGNAL(textChanged()), this, SLOT(slotNameChanged()), Qt::UniqueConnection); } m_alterSchemaWidget->setTableSchema(ts.take()); if (!readFromTable()) { m_alterSchemaWidget->setTableSchema(nullptr); back(); KMessageBox::information(this, xi18nc("@info", "Could not import table %1. " "Select different table or cancel importing.", m_importTableName)); } } bool ImportTableWizard::readFromTable() { QSharedPointer tableResult = m_migrateDriver->readFromTable(m_importTableName); KDbTableSchema *newSchema = m_alterSchemaWidget->newSchema(); if (!tableResult || tableResult->lastResult().isError() || tableResult->fieldsCount() != newSchema->fieldCount()) { back(); KMessageBox::information(this, xi18nc("@info", "Could not import table %1. " "Select different table or cancel importing.", m_importTableName)); return false; } QScopedPointer> data(new QList); for (int i = 0; i < RECORDS_FOR_PREVIEW; ++i) { QSharedPointer record(tableResult->fetchRecordData()); if (!record) { if (tableResult->lastResult().isError()) { return false; } break; } data->append(record.data()); } if (data->isEmpty()) { back(); KMessageBox::information(this, xi18nc("@info", "No data has been found in table %1. " "Select different table or cancel importing.", m_importTableName)); return false; } m_alterSchemaWidget->model()->setRowCount(data->count()); m_alterSchemaWidget->setData(data.take()); return true; } void ImportTableWizard::arriveImportingPage() { m_importingPageWidget->hide(); #if 0 if (checkUserInput()) { //setNextEnabled(m_importingPageWidget, true); user2Button->setEnabled(true); } else { //setNextEnabled(m_importingPageWidget, false); user2Button->setEnabled(false); } #endif QString txt; txt = xi18nc("@info Table import wizard, final message", "All required information has now been gathered. " "Click Next button to start importing table %1." "Depending on size of the table this may take some time.", m_alterSchemaWidget->nameWidget()->nameText()); m_lblImportingTxt->setText(txt); //temp. hack for MS Access driver only //! @todo for other databases we will need KexiMigration::Connection //! and KexiMigration::Driver classes bool showOptions = false; if (fileBasedSrcSelected()) { Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(&result); if (sourceDriver) { showOptions = !result.error() && sourceDriver->propertyValue("source_database_has_nonunicode_encoding").toBool(); sourceDriver->setData(nullptr); } } if (showOptions) m_importOptionsButton->show(); else m_importOptionsButton->hide(); m_importingPageWidget->show(); setAppropriate(m_progressPageItem, true); } void ImportTableWizard::arriveProgressPage() { m_progressLbl->setText(xi18nc("@info", "Please wait while the table is imported.")); backButton()->setEnabled(false); nextButton()->setEnabled(false); connect(button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &ImportTableWizard::slotCancelClicked); QApplication::setOverrideCursor(Qt::BusyCursor); m_importComplete = doImport(); QApplication::restoreOverrideCursor(); disconnect(button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &ImportTableWizard::slotCancelClicked); next(); } void ImportTableWizard::arriveFinishPage() { if (m_importComplete) { m_finishPageItem->setHeader(xi18n("Success")); m_finishLbl->setText(xi18nc("@info", "Table %1 has been imported.", m_alterSchemaWidget->nameWidget()->nameText())); } else { m_finishPageItem->setHeader(xi18n("Failure")); m_finishLbl->setText(xi18n("An error occurred.")); } m_migrateDriver->disconnectSource(); button(QDialogButtonBox::Cancel)->setEnabled(!m_importComplete); m_finishCheckBox->setVisible(m_importComplete); finishButton()->setEnabled(m_importComplete); nextButton()->setEnabled(m_importComplete); setAppropriate(m_progressPageItem, false); } bool ImportTableWizard::fileBasedSrcSelected() const { return m_srcConnSel->selectedConnectionType() == KexiConnectionSelectorWidget::FileBased; } KexiMigrate* ImportTableWizard::prepareImport(Kexi::ObjectStatus *result) { Q_ASSERT(result); // Find a source (migration) driver name QString sourceDriverId = driverIdForSelectedSource(); if (sourceDriverId.isEmpty()) { result->setStatus(xi18n("No appropriate migration driver found."), m_migrateManager.possibleProblemsMessage()); } // Get a source (migration) driver KexiMigrate* sourceDriver = 0; if (!result->error()) { sourceDriver = m_migrateManager.driver(sourceDriverId); if (!sourceDriver || m_migrateManager.result().isError()) { qDebug() << "Import migrate driver error..."; result->setStatus(m_migrateManager.resultable()); } } // Set up source (migration) data required for connection if (sourceDriver && !result->error()) { #if 0 // Setup progress feedback for the GUI if (sourceDriver->progressSupported()) { m_progressBar->updateGeometry(); disconnect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); connect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); progressUpdated(0); } #endif bool keepData = true; #if 0 if (m_importTypeStructureAndDataCheckBox->isChecked()) { qDebug() << "Structure and data selected"; keepData = true; } else if (m_importTypeStructureOnlyCheckBox->isChecked()) { qDebug() << "structure only selected"; keepData = false; } else { qDebug() << "Neither radio button is selected (not possible?) presume keep data"; keepData = true; } #endif KexiMigration::Data* md = new KexiMigration::Data(); if (fileBasedSrcSelected()) { KDbConnectionData* conn_data = new KDbConnectionData(); - conn_data->setDatabaseName(m_srcConnSel->selectedFileName()); + conn_data->setDatabaseName(m_srcConnSel->selectedFile()); md->source = conn_data; md->sourceName.clear(); } else { md->source = m_srcConnSel->selectedConnectionData(); md->sourceName = m_srcDBName->selectedProjectData()->databaseName(); } md->setShouldCopyData(keepData); sourceDriver->setData(md); return sourceDriver; } return 0; } //=========================================================== // QString ImportTableWizard::driverIdForSelectedSource() { if (fileBasedSrcSelected()) { QMimeDatabase db; - QMimeType mime = db.mimeTypeForFile(m_srcConnSel->selectedFileName()); + QMimeType mime = db.mimeTypeForFile(m_srcConnSel->selectedFile()); if (!mime.isValid() || mime.name() == "application/octet-stream" - || mime.name() == "text/plain") + || mime.name() == "text/plain" + || mime.name() == "application/zip") { //try by URL: - mime = db.mimeTypeForFile(m_srcConnSel->selectedFileName()); + mime = db.mimeTypeForFile(m_srcConnSel->selectedFile()); } if (!mime.isValid()) { return QString(); } const QStringList ids(m_migrateManager.driverIdsForMimeType(mime.name())); //! @todo do we want to return first migrate driver for the mime type or allow to select it? return ids.isEmpty() ? QString() : ids.first(); } return m_srcConnSel->selectedConnectionData() ? m_srcConnSel->selectedConnectionData()->databaseName() : QString(); } bool ImportTableWizard::doImport() { KexiGUIMessageHandler msg; KexiProject* project = KexiMainWindowIface::global()->project(); if (!project) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No project available.")); return false; } KexiPart::Part *part = Kexi::partManager().partForPluginId("org.kexi-project.table"); if (!part) { msg.showErrorMessage(Kexi::partManager().result()); return false; } KDbTableSchema* newSchema = m_alterSchemaWidget->newSchema(); if (!newSchema) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No table was selected to import.")); return false; } newSchema->setName(m_alterSchemaWidget->nameWidget()->nameText()); newSchema->setCaption(m_alterSchemaWidget->nameWidget()->captionText()); KexiPart::Item* partItemForSavedTable = project->createPartItem(part->info(), newSchema->name()); if (!partItemForSavedTable) { msg.showErrorMessage(project->result()); return false; } //Create the table if (!m_connection->createTable(newSchema, KDbConnection::CreateTableOption::Default | KDbConnection::CreateTableOption::DropDestination)) { msg.showErrorMessage(KDbMessageHandler::Error, xi18nc("@info", "Unable to create table %1.", newSchema->name())); return false; } m_alterSchemaWidget->takeTableSchema(); //m_connection takes ownership of the KDbTableSchema object //Import the data QApplication::setOverrideCursor(Qt::BusyCursor); QList row; unsigned int fieldCount = newSchema->fieldCount(); m_migrateDriver->moveFirst(); KDbTransactionGuard tg(m_connection); if (m_connection->result().isError()) { QApplication::restoreOverrideCursor(); return false; } do { for (unsigned int i = 0; i < fieldCount; ++i) { if (m_importWasCanceled) { return false; } if (i % 100 == 0) { QApplication::processEvents(); } row.append(m_migrateDriver->value(i)); } m_connection->insertRecord(newSchema, row); row.clear(); } while (m_migrateDriver->moveNext()); if (!tg.commit()) { QApplication::restoreOverrideCursor(); return false; } QApplication::restoreOverrideCursor(); //Done so save part and update gui partItemForSavedTable->setIdentifier(newSchema->id()); project->addStoredItem(part->info(), partItemForSavedTable); return true; } void ImportTableWizard::slotConnPageItemSelected(bool isSelected) { setValid(m_srcConnPageItem, isSelected); if (isSelected && fileBasedSrcSelected()) { next(); } } void ImportTableWizard::slotTableListWidgetSelectionChanged() { setValid(m_tablesPageItem, !m_tableListWidget->selectedItems().isEmpty()); } void ImportTableWizard::slotNameChanged() { setValid(m_alterTablePageItem, !m_alterSchemaWidget->nameWidget()->captionText().isEmpty()); } void ImportTableWizard::slotCancelClicked() { m_importWasCanceled = true; } void ImportTableWizard::slotOptionsButtonClicked() { OptionsDialog dlg(m_srcConnSel->selectedFile(), m_sourceDbEncoding, this); if (QDialog::Accepted == dlg.exec()) { m_sourceDbEncoding = dlg.encodingComboBox()->selectedEncoding(); } } diff --git a/src/migration/importwizard.cpp b/src/migration/importwizard.cpp index 8772b92b5..8932334de 100644 --- a/src/migration/importwizard.cpp +++ b/src/migration/importwizard.cpp @@ -1,1134 +1,1134 @@ /* This file is part of the KDE project Copyright (C) 2004-2009 Adam Pigg Copyright (C) 2004-2016 Jarosław Staniek Copyright (C) 2005 Martin Ellis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "importwizard.h" #include "keximigrate.h" #include "importoptionsdlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KexiMigration; class Q_DECL_HIDDEN ImportWizard::Private { public: Private(QMap* args_) : srcProjectSelector(0) , fileBasedDstWasPresented(false) , setupFileBasedSrcNeeded(true) , importExecuted(false) , prjSet(0) , args(args_) { } ~Private() { delete prjSet; } QWidget *introPageWidget, *srcConnPageWidget, *srcDBPageWidget, *dstTypePageWidget, *dstPageWidget, *importTypePageWidget, *importingPageWidget, *finishPageWidget; KPageWidgetItem *introPageItem, *srcConnPageItem, *srcDBPageItem, *dstTypePageItem, *dstPageItem, *importTypePageItem, *importingPageItem, *finishPageItem; QGroupBox *importTypeGroupBox; QRadioButton *importTypeStructureAndDataCheckBox; QRadioButton *importTypeStructureOnlyCheckBox; KexiDBTitlePage* dstTitlePageWidget; KPageWidgetItem *dstTitlePageItem; KexiPrjTypeSelector *dstPrjTypeSelector; KexiConnectionSelectorWidget *srcConn, *dstConn; QString driverIdForSelectedSource; QLineEdit *dstNewDBTitleLineEdit; QLabel *dstNewDBNameLabel; QLineEdit *dstNewDBNameLineEdit; QLabel *dstNewDBNameUrlLabel; KUrlRequester *dstNewDBNameUrl; KexiStartupFileHandler *dstNewDBFileHandler; KexiProjectSelectorWidget *srcProjectSelector; QLabel *lblImportingTxt, *lblImportingErrTxt, *finishLbl; QCheckBox *openImportedProjectCheckBox; bool fileBasedDstWasPresented; bool setupFileBasedSrcNeeded; bool importExecuted; //!< used in import() KexiProjectSet* prjSet; QProgressBar *progressBar; QPushButton* importOptionsButton; QMap *args; QString predefinedDatabaseName, predefinedMimeType; KDbConnectionData *predefinedConnectionData; MigrateManager migrateManager; //!< object lives here, so status messages can be globally preserved //! Encoding for source db. Currently only used for MDB driver. //! @todo Hardcoded. Move to KexiMigrate driver's impl. QString sourceDBEncoding; }; //=========================================================== // ImportWizard::ImportWizard(QWidget *parent, QMap* args) : KAssistantDialog(parent) , d(new Private(args)) { setModal(true); setWindowTitle(xi18nc("@title:window", "Import Database")); setWindowIcon(KexiIcon("database-import")); KexiMainWindowIface::global()->setReasonableDialogSize(this); parseArguments(); setupIntro(); setupSrcConn(); setupSrcDB(); setupDstType(); setupDstTitle(); setupDst(); setupImportType(); setupImporting(); setupFinish(); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slot_currentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &ImportWizard::helpClicked); if (d->predefinedConnectionData) { // setup wizard for predefined server source d->srcConn->showAdvancedConnection(); setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); } else if (!d->predefinedDatabaseName.isEmpty()) { // setup wizard for predefined source // (used when external project type was opened in Kexi, e.g. mdb file) setAppropriate(d->srcConnPageItem, false); setAppropriate(d->srcDBPageItem, false); d->srcConn->showSimpleConnection(); - d->srcConn->setSelectedFileName(d->predefinedDatabaseName); + d->srcConn->setSelectedFile(d->predefinedDatabaseName); #if 0 //disable all prev pages except "welcome" page for (int i = 0; i < indexOf(d->dstTypePage); i++) { if (page(i) != d->introPage) setAppropriate(page(i), false); } #endif } d->sourceDBEncoding = QString::fromLatin1(KexiUtils::encoding()); //default } //=========================================================== // ImportWizard::~ImportWizard() { delete d; } //=========================================================== // void ImportWizard::parseArguments() { d->predefinedConnectionData = 0; if (!d->args) return; if (!(*d->args)["databaseName"].isEmpty() && !(*d->args)["mimeType"].isEmpty()) { d->predefinedDatabaseName = (*d->args)["databaseName"]; d->predefinedMimeType = (*d->args)["mimeType"]; if (d->args->contains("connectionData")) { bool ok; d->predefinedConnectionData = new KDbConnectionData( KDbUtils::deserializeMap((*d->args)["connectionData"]), &ok); if (!ok) { delete d->predefinedConnectionData; d->predefinedConnectionData = 0; } } } d->args->clear(); } QString ImportWizard::selectedSourceFileName() const { if (d->predefinedDatabaseName.isEmpty()) - return d->srcConn->selectedFileName(); + return d->srcConn->selectedFile(); return d->predefinedDatabaseName; } //=========================================================== // void ImportWizard::setupIntro() { d->introPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(); d->introPageWidget->setLayout(vbox); KexiUtils::setStandardMarginsAndSpacing(vbox); QLabel *lblIntro = new QLabel(d->introPageWidget); lblIntro->setAlignment(Qt::AlignTop | Qt::AlignLeft); lblIntro->setWordWrap(true); lblIntro->setTextFormat(Qt::RichText); QString msg; if (d->predefinedConnectionData) { //predefined import: server source msg = xi18nc("@info", "Database Importing Assistant is about to import %1 database " "(connection %2) into a Kexi project.", d->predefinedDatabaseName, d->predefinedConnectionData->toUserVisibleString()); } else if (!d->predefinedDatabaseName.isEmpty()) { //predefined import: file source //! @todo this message is currently ok for files only QMimeDatabase db; QMimeType mime = db.mimeTypeForName(d->predefinedMimeType); if (!mime.isValid()) { qWarning() << QString("'%1' mimetype not installed!").arg(d->predefinedMimeType); } d->driverIdForSelectedSource = driverIdForMimeType(mime); msg = xi18nc("@info", "Database Importing Assistant is about to import %1 file " "of type %2 into a Kexi project.", QDir::toNativeSeparators(d->predefinedDatabaseName), mime.isValid() ? mime.comment() : "???"); } else { msg = xi18nc("@info", "Database Importing Assistant allows you to import an existing database " "into a Kexi project."); } // note: we're using .arg() here because the msg argument is already in rich-text format QString finalMessage = xi18nc("@info", "%1" "Click Next button to continue or " "Cancel button to exit this assistant.").arg(msg); lblIntro->setText(finalMessage); vbox->addWidget(lblIntro); d->introPageItem = new KPageWidgetItem(d->introPageWidget, xi18n("Welcome to the Database Importing Assistant")); addPage(d->introPageItem); } //=========================================================== // void ImportWizard::setupSrcConn() { d->srcConnPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->srcConnPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcConn = new KexiConnectionSelectorWidget(&Kexi::connset(), QUrl("kfiledialog:///ProjectMigrationSourceDir"), KexiConnectionSelectorWidget::Opening, d->srcConnPageWidget); d->srcConn->hideConnectonIcon(); d->srcConn->showSimpleConnection(); connect(d->srcConn, &KexiConnectionSelectorWidget::connectionSelected, this, &ImportWizard::sourceConnectionSelected); const QStringList excludedMimeTypes({ //! @todo remove when support for kexi files as source prj is added in migration KDb::defaultFileBasedDriverMimeType(), "application/x-kexiproject-shortcut", "application/x-kexi-connectiondata"}); d->srcConn->setExcludedMimeTypes(excludedMimeTypes); vbox->addWidget(d->srcConn); d->srcConnPageItem = new KPageWidgetItem(d->srcConnPageWidget, xi18n("Select Location for Source Database")); addPage(d->srcConnPageItem); } //=========================================================== // void ImportWizard::setupSrcDB() { // arrivesrcdbPage creates widgets on that page d->srcDBPageWidget = new QWidget(this); d->srcDBPageItem = new KPageWidgetItem(d->srcDBPageWidget, xi18n("Select Source Database")); addPage(d->srcDBPageItem); } //=========================================================== // void ImportWizard::setupDstType() { d->dstTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); QHBoxLayout *hbox = new QHBoxLayout; vbox->addLayout(hbox); KexiUtils::setStandardMarginsAndSpacing(hbox); QLabel *lbl = new QLabel(xi18n("Destination database type:") /*+ ' '*/, d->dstTypePageWidget); lbl->setAlignment(Qt::AlignLeft | Qt::AlignTop); lbl->setTextFormat(Qt::RichText); hbox->addWidget(lbl); d->dstPrjTypeSelector = new KexiPrjTypeSelector(d->dstTypePageWidget); hbox->addWidget(d->dstPrjTypeSelector); d->dstPrjTypeSelector->option_file->setText(xi18n("Database project stored in a file")); d->dstPrjTypeSelector->option_server->setText(xi18n("Database project stored on a server")); hbox->addStretch(1); vbox->addStretch(1); d->dstTypePageItem = new KPageWidgetItem(d->dstTypePageWidget, xi18n("Select Destination Database Type")); addPage(d->dstTypePageItem); } //=========================================================== // void ImportWizard::setupDstTitle() { d->dstTitlePageWidget = new KexiDBTitlePage(xi18n("Destination project's caption:"), this); d->dstTitlePageWidget->layout()->setMargin(KexiUtils::marginHint()); d->dstTitlePageWidget->updateGeometry(); d->dstNewDBTitleLineEdit = d->dstTitlePageWidget->le_title; connect(d->dstNewDBTitleLineEdit, SIGNAL(textChanged(QString)), this, SLOT(destinationTitleTextChanged(QString))); d->dstNewDBNameUrlLabel = d->dstTitlePageWidget->label_requester; d->dstNewDBNameUrl = d->dstTitlePageWidget->file_requester; d->dstNewDBFileHandler = new KexiStartupFileHandler( QUrl("kfiledialog:///ProjectMigrationDestinationDir"), KexiFileFilters::SavingFileBasedDB, d->dstTitlePageWidget->file_requester); d->dstNewDBNameLabel = new QLabel(xi18n("Destination project's name:"), d->dstTitlePageWidget); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::LabelRole, d->dstNewDBNameLabel); d->dstNewDBNameLineEdit = new QLineEdit(d->dstTitlePageWidget); d->dstNewDBNameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); KDbIdentifierValidator *idValidator = new KDbIdentifierValidator(this); idValidator->setLowerCaseForced(true); d->dstNewDBNameLineEdit->setValidator(idValidator); d->dstTitlePageWidget->formLayout->setWidget(2, QFormLayout::FieldRole, d->dstNewDBNameLineEdit); d->dstTitlePageItem = new KPageWidgetItem(d->dstTitlePageWidget, xi18n("Enter Destination Database Project's Caption")); addPage(d->dstTitlePageItem); } void ImportWizard::destinationTitleTextChanged(const QString & text) { Q_UNUSED(text); updateDestinationDBFileName(); } void ImportWizard::updateDestinationDBFileName() { d->dstNewDBFileHandler->updateUrl(d->dstNewDBTitleLineEdit->text()); d->dstNewDBNameLineEdit->setText(d->dstNewDBTitleLineEdit->text()); } //=========================================================== // void ImportWizard::setupDst() { d->dstPageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->dstPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->dstConn = new KexiConnectionSelectorWidget(&Kexi::connset(), QUrl("kfiledialog:///ProjectMigrationDestinationDir"), KexiConnectionSelectorWidget::Saving, d->dstPageWidget); d->dstConn->hideHelpers(); vbox->addWidget(d->dstConn); connect(d->dstConn, SIGNAL(connectionItemExecuted(ConnectionDataLVItem*)), this, SLOT(next())); d->dstConn->showSimpleConnection(); //anyway, db files will be _saved_ d->dstConn->setFileMode(KexiFileFilters::SavingFileBasedDB); d->dstPageItem = new KPageWidgetItem(d->dstPageWidget, xi18n("Select Location for Destination Database Project")); addPage(d->dstPageItem); } //=========================================================== // void ImportWizard::setupImportType() { d->importTypePageWidget = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(d->importTypePageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->importTypeGroupBox = new QGroupBox(d->importTypePageWidget); vbox->addWidget(d->importTypeGroupBox); QVBoxLayout *importTypeGroupBoxLyr = new QVBoxLayout; importTypeGroupBoxLyr->addWidget( d->importTypeStructureAndDataCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure and data"), d->importTypeGroupBox)); d->importTypeStructureAndDataCheckBox->setChecked(true); importTypeGroupBoxLyr->addWidget( d->importTypeStructureOnlyCheckBox = new QRadioButton( xi18nc("Scope of import", "Structure only"), d->importTypeGroupBox)); importTypeGroupBoxLyr->addStretch(1); d->importTypeGroupBox->setLayout(importTypeGroupBoxLyr); d->importTypePageItem = new KPageWidgetItem(d->importTypePageWidget, xi18n("Select Scope of Import")); addPage(d->importTypePageItem); } //=========================================================== // void ImportWizard::setupImporting() { d->importingPageWidget = new QWidget(this); d->importingPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->importingPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->lblImportingTxt = new QLabel(d->importingPageWidget); d->lblImportingTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingTxt->setWordWrap(true); d->lblImportingTxt->setTextFormat(Qt::RichText); d->lblImportingErrTxt = new QLabel(d->importingPageWidget); d->lblImportingErrTxt->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->lblImportingErrTxt->setWordWrap(true); d->lblImportingErrTxt->setTextFormat(Qt::RichText); d->progressBar = new QProgressBar(d->importingPageWidget); d->progressBar->setRange(0, 100); d->progressBar->hide(); vbox->addWidget(d->lblImportingTxt); vbox->addWidget(d->lblImportingErrTxt); vbox->addStretch(1); QWidget *options_widget = new QWidget(d->importingPageWidget); vbox->addWidget(options_widget); QVBoxLayout *options_vbox = new QVBoxLayout(options_widget); options_vbox->setSpacing(KexiUtils::spacingHint()); QHBoxLayout *importOptionsButtonLyr = new QHBoxLayout; options_vbox->addLayout(importOptionsButtonLyr); d->importOptionsButton = new QPushButton(koIcon("configure"), xi18n("Advanced Options"), options_widget); connect(d->importOptionsButton, SIGNAL(clicked()), this, SLOT(slotOptionsButtonClicked())); importOptionsButtonLyr->addStretch(1); importOptionsButtonLyr->addWidget(d->importOptionsButton); importOptionsButtonLyr->addStretch(1); options_vbox->addStretch(1); vbox->addWidget(d->progressBar); vbox->addStretch(2); d->importingPageWidget->show(); d->importingPageItem = new KPageWidgetItem(d->importingPageWidget, xi18n("Importing")); addPage(d->importingPageItem); } //=========================================================== // void ImportWizard::setupFinish() { d->finishPageWidget = new QWidget(this); d->finishPageWidget->hide(); QVBoxLayout *vbox = new QVBoxLayout(d->finishPageWidget); KexiUtils::setStandardMarginsAndSpacing(vbox); d->finishLbl = new QLabel(d->finishPageWidget); d->finishLbl->setAlignment(Qt::AlignTop | Qt::AlignLeft); d->finishLbl->setWordWrap(true); d->finishLbl->setTextFormat(Qt::RichText); vbox->addWidget(d->finishLbl); d->openImportedProjectCheckBox = new QCheckBox(xi18n("Open imported project"), d->finishPageWidget); d->openImportedProjectCheckBox->setChecked(true); vbox->addSpacing(KexiUtils::spacingHint()); vbox->addWidget(d->openImportedProjectCheckBox); vbox->addStretch(1); d->finishPageItem = new KPageWidgetItem(d->finishPageWidget, xi18n("Success")); addPage(d->finishPageItem); } //=========================================================== // bool ImportWizard::checkUserInput() { QString issues; if (d->dstNewDBTitleLineEdit->text().isEmpty()) { issues = xi18nc("@info", "No new database name was entered."); } Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver && sourceDriver->isSourceAndDestinationDataSourceTheSame()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format issues = xi18nc("@info", "%1Source database is the same as destination.") .arg(issues); } if (!issues.isEmpty()) { // note: we're using .arg() here because the 'issues' argument is already in rich-text format d->lblImportingErrTxt->setText( xi18nc("@info", "Following issues were found with the data you entered:" "%1" "Please click Back button and correct these issues.") .arg(issues)); return false; } return true; } void ImportWizard::arriveSrcConnPage() { d->srcConnPageWidget->hide(); /*! @todo KexiFileWidget needs "open file" and "open server" modes in addition to just "open" */ if (d->setupFileBasedSrcNeeded) { d->setupFileBasedSrcNeeded = false; d->srcConn->setFileMode(KexiFileFilters::Opening); d->srcConn->setAdditionalMimeTypes(QStringList()); } /*! @todo Support different file extensions based on MigrationDriver */ d->srcConnPageWidget->show(); } void ImportWizard::arriveSrcDBPage() { if (fileBasedSrcSelected()) { //! @todo Back button doesn't work after selecting a file to import } else { if (!d->srcProjectSelector) { QVBoxLayout *vbox = new QVBoxLayout(d->srcDBPageWidget); d->srcProjectSelector = new KexiProjectSelectorWidget(d->srcDBPageWidget); vbox->addWidget(d->srcProjectSelector); KexiUtils::setStandardMarginsAndSpacing(vbox); d->srcProjectSelector->label()->setText(xi18n("Select source database you wish to import:")); } d->srcDBPageWidget->hide(); KDbConnectionData* condata = d->srcConn->selectedConnectionData(); Q_ASSERT(condata); Q_ASSERT(d->prjSet); d->srcProjectSelector->setProjectSet(d->prjSet); d->srcDBPageWidget->show(); } } void ImportWizard::arriveDstTitlePage() { d->dstNewDBNameUrlLabel->setVisible(fileBasedDstSelected()); d->dstNewDBNameUrl->setVisible(fileBasedDstSelected()); d->dstNewDBNameLabel->setVisible(!fileBasedDstSelected()); d->dstNewDBNameLineEdit->setVisible(!fileBasedDstSelected()); if (fileBasedSrcSelected()) { const QString fname(selectedSourceFileName()); QString suggestedDBName(QFileInfo(fname).fileName()); const QFileInfo fi(suggestedDBName); suggestedDBName = suggestedDBName.left(suggestedDBName.length() - (fi.completeSuffix().isEmpty() ? 0 : (fi.completeSuffix().length() + 1))); d->dstNewDBTitleLineEdit->setText(suggestedDBName); } else { if (d->predefinedConnectionData) { // server source db is predefined d->dstNewDBTitleLineEdit->setText(d->predefinedDatabaseName); } else { if (!d->srcProjectSelector || !d->srcProjectSelector->selectedProjectData()) { back(); //!< @todo return; } d->dstNewDBTitleLineEdit->setText(d->srcProjectSelector->selectedProjectData()->databaseName()); } } d->dstNewDBTitleLineEdit->selectAll(); d->dstNewDBTitleLineEdit->setFocus(); updateDestinationDBFileName(); } void ImportWizard::arriveDstPage() { if (fileBasedDstSelected()) { d->dstPageWidget->hide(); KAssistantDialog::next(); return; } else { d->dstConn->showAdvancedConnection(); } d->dstPageWidget->show(); } void ImportWizard::arriveImportingPage() { d->importingPageWidget->hide(); nextButton()->setEnabled(checkUserInput()); d->lblImportingTxt->setText(xi18nc("@info", "All required information has now " "been gathered. Click Next button to start importing." "Depending on size of the database this may take some time." /*"Note: You may be asked for extra " "information such as field types if " "the wizard could not automatically " "determine this for you."*/)); //temp. hack for MS Access driver only //! @todo for other databases we will need KexiMigration::Connection //! and KexiMigration::Driver classes bool showOptions = false; if (fileBasedSrcSelected()) { Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); if (sourceDriver) { showOptions = !result.error() && sourceDriver->propertyValue("source_database_has_nonunicode_encoding").toBool(); sourceDriver->setData(nullptr); } } if (showOptions) d->importOptionsButton->show(); else d->importOptionsButton->hide(); d->importingPageWidget->show(); } void ImportWizard::arriveFinishPage() { } bool ImportWizard::fileBasedSrcSelected() const { if (d->predefinedConnectionData) return false; // qDebug() << (d->srcConn->selectedConnectionType()==KexiConnectionSelectorWidget::FileBased); return d->srcConn->selectedConnectionType() == KexiConnectionSelectorWidget::FileBased; } bool ImportWizard::fileBasedDstSelected() const { return d->dstPrjTypeSelector->option_file->isChecked(); } void ImportWizard::progressUpdated(int percent) { d->progressBar->setValue(percent); qApp->processEvents(); } QString ImportWizard::driverIdForMimeType(const QMimeType &mime) const { if (!mime.isValid()) { return QString(); } const QStringList ids(d->migrateManager.driverIdsForMimeType(mime.name())); //! @todo do we want to return first migrate driver for the mime type or allow to select it? return ids.isEmpty() ? QString() : ids.first(); } QString ImportWizard::findDriverIdForSelectedSource() { if (fileBasedSrcSelected()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(selectedSourceFileName()); if (!mime.isValid() || mime.name() == "application/octet-stream" || mime.name() == "text/plain" || mime.name() == "application/zip") { //try by URL: mime = db.mimeTypeForFile(selectedSourceFileName()); } return driverIdForMimeType(mime); } //server-based QString sourceDriverId; if (d->predefinedConnectionData) { sourceDriverId = d->predefinedConnectionData->driverId(); } else if (d->srcConn->selectedConnectionData()) { sourceDriverId = d->srcConn->selectedConnectionData()->driverId(); } const QStringList migrationDriverIds(d->migrateManager.driverIdsForSourceDriver(sourceDriverId)); //! @todo First found driver ID is picked. It's OK as long as there is one migration //! driver per source database type. How about allowing users to pick migration driver? return migrationDriverIds.isEmpty() ? QString() : migrationDriverIds.first(); } //=========================================================== // void ImportWizard::accept() { if (d->args) { if ((!fileBasedDstSelected() && !d->args->contains("destinationConnectionShortcut")) || !d->openImportedProjectCheckBox->isChecked()) { //do not open dest db if used didn't want it //for server connections, destinationConnectionShortcut must be defined d->args->remove("destinationDatabaseName"); } } KAssistantDialog::accept(); } KexiMigrate* ImportWizard::prepareImport(Kexi::ObjectStatus& result) { KexiUtils::WaitCursor wait; // Start with a driver manager KDbDriverManager manager; //qDebug() << "Creating destination driver..."; // Get a driver to the destination database KDbDriver *destDriver = manager.driver( d->dstConn->selectedConnectionData() ? d->dstConn->selectedConnectionData()->driverId() : KDb::defaultFileBasedDriverId()); if (!destDriver || manager.result().isError()) { result.setStatus(manager.resultable()); qWarning() << "Manager error:" << manager.result(); } // Set up destination connection data KDbConnectionData *cdata = 0; QScopedPointer cdataDeleter; QString dbname; if (!result.error()) { if (d->dstConn->selectedConnectionData()) { //server-based project qDebug() << "Server destination..."; cdata = d->dstConn->selectedConnectionData(); dbname = d->dstNewDBNameLineEdit->text(); } else { //file-based project qDebug() << "File Destination..."; cdata = new KDbConnectionData(); cdataDeleter.reset(cdata); // ownership won't be transferred cdata->setCaption(d->dstNewDBTitleLineEdit->text()); cdata->setDriverId(KDb::defaultFileBasedDriverId()); dbname = d->dstTitlePageWidget->file_requester->url().toLocalFile(); cdata->setDatabaseName(dbname); qDebug() << "Current file name:" << dbname; } } // Find a source (migration) driver name if (!result.error()) { if (d->driverIdForSelectedSource.isEmpty()) result.setStatus(xi18n("No appropriate migration driver found."), d->migrateManager.possibleProblemsMessage()); } // Get a source (migration) driver KexiMigrate* sourceDriver = 0; if (!result.error()) { sourceDriver = d->migrateManager.driver(d->driverIdForSelectedSource); if (!sourceDriver || d->migrateManager.result().isError()) { qDebug() << "Import migrate driver error..."; result.setStatus(d->migrateManager.resultable()); } } KexiUtils::removeWaitCursor(); // Set up source (migration) data required for connection if (sourceDriver && !result.error() && cdata) { // Setup progress feedback for the GUI if (sourceDriver->progressSupported()) { d->progressBar->updateGeometry(); disconnect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); connect(sourceDriver, SIGNAL(progressPercent(int)), this, SLOT(progressUpdated(int))); progressUpdated(0); } bool keepData; if (d->importTypeStructureAndDataCheckBox->isChecked()) { qDebug() << "Structure and data selected"; keepData = true; } else if (d->importTypeStructureOnlyCheckBox->isChecked()) { qDebug() << "structure only selected"; keepData = false; } else { qDebug() << "Neither radio button is selected (not possible?) presume keep data"; keepData = true; } KexiMigration::Data* md = new KexiMigration::Data(); md->setDestinationProjectData(new KexiProjectData(*cdata, dbname)); if (fileBasedSrcSelected()) { KDbConnectionData* conn_data = new KDbConnectionData(); conn_data->setDatabaseName(selectedSourceFileName()); md->source = conn_data; md->sourceName.clear(); } else { if (d->predefinedConnectionData) md->source = d->predefinedConnectionData; else md->source = d->srcConn->selectedConnectionData(); if (!d->predefinedDatabaseName.isEmpty()) md->sourceName = d->predefinedDatabaseName; else md->sourceName = d->srcProjectSelector->selectedProjectData()->databaseName(); //! @todo Aah, this is so C-like. Move to performImport(). } md->setShouldCopyData(keepData); sourceDriver->setData(md); return sourceDriver; } return 0; } tristate ImportWizard::import() { d->importExecuted = true; Kexi::ObjectStatus result; KexiMigrate* sourceDriver = prepareImport(result); bool acceptingNeeded = false; // Perform import if (sourceDriver && !result.error()) { if (!d->sourceDBEncoding.isEmpty()) { sourceDriver->setPropertyValue("source_database_nonunicode_encoding", QVariant(d->sourceDBEncoding.toUpper().remove(' ')) // "CP1250", not "cp 1250" ); } if (!sourceDriver->checkIfDestinationDatabaseOverwritingNeedsAccepting(&result, &acceptingNeeded)) { qDebug() << "Abort import cause checkIfDestinationDatabaseOverwritingNeedsAccepting " "returned false."; return false; } qDebug() << sourceDriver->data()->destinationProjectData()->databaseName(); qDebug() << "Performing import..."; } if (sourceDriver && !result.error() && acceptingNeeded) { // ok, the destination-db already exists... if (KMessageBox::Yes != KMessageBox::warningYesNo(this, xi18nc("@info (don't add tags around %1, it's done already)", "Database %1 already exists." "Do you want to replace it with a new one?", KexiUtils::localizedStringToHtmlSubstring( sourceDriver->data()->destinationProjectData()->infoString())), 0, KGuiItem(xi18nc("@action:button Replace Database", "&Replace")), KStandardGuiItem::no())) { return cancelled; } } if (sourceDriver && !result.error() && sourceDriver->progressSupported()) { d->progressBar->show(); } if (sourceDriver && !result.error() && sourceDriver->performImport(&result)) { if (d->args) { d->args->insert("destinationDatabaseName", fileBasedDstSelected() ? sourceDriver->data()->destinationProjectData()->connectionData()->databaseName() : sourceDriver->data()->destinationProjectData()->databaseName()); QString destinationConnectionShortcut; if (d->dstConn->selectedConnectionData()) { destinationConnectionShortcut = Kexi::connset().fileNameForConnectionData(*d->dstConn->selectedConnectionData()); } if (!destinationConnectionShortcut.isEmpty()) { d->args->insert("destinationConnectionShortcut", destinationConnectionShortcut); } } d->finishPageItem->setHeader(xi18n("Success")); return true; } if (!sourceDriver || result.error()) { d->progressBar->setValue(0); d->progressBar->hide(); QString msg, details; KexiTextMessageHandler handler(&msg, &details); handler.showErrorMessage(&result); qDebug() << msg << "\n" << details; d->finishPageItem->setHeader(xi18n("Failure")); // note: we're using .arg() here because the msg and details arguments are already in rich-text format d->finishLbl->setText( xi18nc("@info", "Import failed." "%1" "%2" "You can click Back button and try again.") .arg(msg).arg(details)); return false; } return true; } void ImportWizard::reject() { KAssistantDialog::reject(); } //=========================================================== // void ImportWizard::next() { if (currentPage() == d->srcConnPageItem) { if (fileBasedSrcSelected() && /*! @todo use QUrl? */!QFileInfo(selectedSourceFileName()).isFile()) { KMessageBox::sorry(this, xi18n("Select source database filename.")); return; } KDbConnectionData* conndata = d->srcConn->selectedConnectionData(); if (!fileBasedSrcSelected() && !conndata) { KMessageBox::sorry(this, xi18n("Select source database.")); return; } d->driverIdForSelectedSource = findDriverIdForSelectedSource(); // cache KexiMigrate* import = d->migrateManager.driver(d->driverIdForSelectedSource); if (!import || d->migrateManager.result().isError()) { QString dbname; if (fileBasedSrcSelected()) dbname = QDir::toNativeSeparators(selectedSourceFileName()); else dbname = conndata ? conndata->toUserVisibleString() : QString(); KMessageBox::error(this, dbname.isEmpty() ? xi18n("Could not import database. This type is not supported.") : xi18nc("@info", "Could not import database %1. " "This type is not supported.", dbname)); return; } if (!fileBasedSrcSelected()) { // make sure we have password if needed tristate passwordNeeded = false; if (conndata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(conndata, this); } bool ok = passwordNeeded != cancelled; if (ok) { KexiGUIMessageHandler handler; d->prjSet = new KexiProjectSet(&handler); if (!d->prjSet->setConnectionData(conndata)) { handler.showErrorMessage(d->prjSet->result()); ok = false; } } if (!ok) { if (passwordNeeded == true) { conndata->setPassword(QString::null); // not clear(), we have to remove password } delete d->prjSet; d->prjSet = 0; return; } } } else if (currentPage() == d->dstTitlePageItem) { if (fileBasedDstSelected()) { if (QFileInfo::exists(d->dstNewDBNameUrl->url().toLocalFile())) { if (!KexiUtils::askForFileOverwriting(d->dstNewDBNameUrl->url().toLocalFile(), this)) { return; } } } } else if (currentPage() == d->importTypePageItem) { if (!fileBasedDstSelected()) { // make sure we have password if needed tristate passwordNeeded = false; KDbConnectionData* condata = d->dstConn->selectedConnectionData(); if (condata->password().isEmpty()) { passwordNeeded = KexiDBPasswordDialog::getPasswordIfNeeded(condata, this); } bool ok = passwordNeeded != cancelled; if (!ok) { if (passwordNeeded == true) { condata->setPassword(QString::null); // not clear(), we have to remove password } return; } } } else if (currentPage() == d->importingPageItem) { if (!d->importExecuted) { d->importOptionsButton->hide(); backButton()->setEnabled(false); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->lblImportingTxt->setText(xi18n("Importing in progress...")); tristate res = import(); if (true == res) { d->finishLbl->setText( xi18nc("@info", "Database has been imported into Kexi project %1.", d->dstNewDBNameLineEdit->text())); button(QDialogButtonBox::Cancel)->setEnabled(false); backButton()->setEnabled(false); nextButton()->setEnabled(true); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->show(); next(); return; } d->progressBar->hide(); button(QDialogButtonBox::Cancel)->setEnabled(true); backButton()->setEnabled(true); nextButton()->setEnabled(false); finishButton()->setEnabled(false); d->openImportedProjectCheckBox->hide(); if (!res) next(); else if (~res) { arriveImportingPage(); } d->importExecuted = false; return; } } setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); setAppropriate(d->dstPageItem, !fileBasedDstSelected()); KAssistantDialog::next(); } void ImportWizard::back() { setAppropriate(d->srcDBPageItem, !fileBasedSrcSelected() && !d->predefinedConnectionData); KAssistantDialog::back(); } void ImportWizard::slot_currentPageChanged(KPageWidgetItem* curPage,KPageWidgetItem* prevPage) { Q_UNUSED(prevPage); if (curPage == d->introPageItem) { } else if (curPage == d->srcConnPageItem) { arriveSrcConnPage(); } else if (curPage == d->srcDBPageItem) { arriveSrcDBPage(); } else if (curPage == d->dstTypePageItem) { } else if (curPage == d->dstTitlePageItem) { arriveDstTitlePage(); } else if (curPage == d->dstPageItem) { if (fileBasedDstSelected()) { if (prevPage == d->importTypePageItem) { KAssistantDialog::back(); } else { KAssistantDialog::next(); } } else { arriveDstPage(); } } else if (curPage == d->importingPageItem) { arriveImportingPage(); } else if (curPage == d->finishPageItem) { arriveFinishPage(); } } void ImportWizard::helpClicked() { if (currentPage() == d->introPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } else if (currentPage() == d->srcConnPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to import data from."), xi18n("Help")); } else if (currentPage() == d->srcDBPageItem) { KMessageBox::information(this, xi18n("Here you can choose the actual database to import data from."), xi18n("Help")); } else if (currentPage() == d->dstTypePageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data."), xi18n("Help")); } else if (currentPage() == d->dstPageItem) { KMessageBox::information(this, xi18n("Here you can choose the location to save the data in and the new database name."), xi18n("Help")); } else if (currentPage() == d->finishPageItem || currentPage() == d->importingPageItem) { KMessageBox::information(this, xi18n("No help is available for this page."), xi18n("Help")); } } void ImportWizard::slotOptionsButtonClicked() { OptionsDialog dlg(selectedSourceFileName(), d->sourceDBEncoding, this); if (QDialog::Accepted == dlg.exec()) { d->sourceDBEncoding = dlg.encodingComboBox()->selectedEncoding(); } } void ImportWizard::sourceConnectionSelected(bool selected) { if (selected && fileBasedSrcSelected()) { next(); } } diff --git a/src/plugins/importexport/csv/kexicsvexportwizard.cpp b/src/plugins/importexport/csv/kexicsvexportwizard.cpp index a7f262a07..f2008c3f3 100644 --- a/src/plugins/importexport/csv/kexicsvexportwizard.cpp +++ b/src/plugins/importexport/csv/kexicsvexportwizard.cpp @@ -1,459 +1,430 @@ /* This file is part of the KDE project Copyright (C) 2012 Oleg Kukharchuk - Copyright (C) 2005-2016 Jarosław Staniek + Copyright (C) 2005-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexicsvexportwizard.h" #include "kexicsvwidgets.h" #include #include #include #include #include #include #include +#include #include -#ifdef KEXI_USE_KFILEWIDGET -#include -#else -#include -#endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KexiCSVExportWizard::KexiCSVExportWizard(const KexiCSVExport::Options& options, QWidget * parent) : KAssistantDialog(parent) , m_options(options) , m_importExportGroup(KSharedConfig::openConfig()->group("ImportExport")) { KexiMainWindowIface::global()->setReasonableDialogSize(this); buttonBox()->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); if (m_options.mode == KexiCSVExport::Clipboard) { //! @todo KEXI3 ? button(QDialogButtonBox::Ok)->setText(xi18n("Copy")); } else { button(QDialogButtonBox::Ok)->setText(xi18n("Export")); } QString infoLblFromText; QString captionOrName; KexiGUIMessageHandler msgh(this); if (m_options.useTempQuery) { m_tableOrQuery = new KDbTableOrQuerySchema(KexiMainWindowIface::global()->unsavedQuery(options.itemId)); captionOrName = KexiMainWindowIface::global()->project()->dbConnection()->querySchema(m_options.itemId)->captionOrName(); } else { m_tableOrQuery = new KDbTableOrQuerySchema( KexiMainWindowIface::global()->project()->dbConnection(), m_options.itemId); captionOrName = m_tableOrQuery->captionOrName(); } if (m_tableOrQuery->table()) { if (m_options.mode == KexiCSVExport::Clipboard) { setWindowTitle(xi18nc("@title:window", "Copy Data From Table to Clipboard")); infoLblFromText = xi18n("Copying data from table:"); } else { setWindowTitle(xi18nc("@title:window", "Export Data From Table to CSV File")); infoLblFromText = xi18n("Exporting data from table:"); } } else if (m_tableOrQuery->query()) { if (m_options.mode == KexiCSVExport::Clipboard) { setWindowTitle(xi18nc("@title:window", "Copy Data From Query to Clipboard")); infoLblFromText = xi18n("Copying data from table:"); } else { setWindowTitle(xi18nc("@title:window", "Export Data From Query to CSV File")); infoLblFromText = xi18n("Exporting data from query:"); } } else { msgh.showErrorMessage(KexiMainWindowIface::global()->project()->dbConnection()->result(), KDbMessageHandler::Error, xi18n("Could not open data for exporting.")); m_canceled = true; return; } QString text = "\n" + captionOrName; int m_recordCount = KDb::recordCount(m_tableOrQuery); int columns = KDb::fieldCount(m_tableOrQuery); text += "\n"; if (m_recordCount > 0) text += xi18n("(rows: %1, columns: %2)", m_recordCount, columns); else text += xi18n("(columns: %1)", columns); infoLblFromText.append(text); // OK, source data found. // Setup pages // 1. File Save Page if (m_options.mode == KexiCSVExport::File) { - const QUrl url("kfiledialog:///CSVImportExport"); //startDir -#ifdef KEXI_USE_KFILEWIDGET - m_fileSaveWidget = new KexiFileWidget( - url, - KexiFileFilters::CustomSavingFileBasedDB, - this); - m_fileSaveWidget->setAdditionalMimeTypes(csvMimeTypes()); - m_fileSaveWidget->setDefaultExtension("csv"); - m_fileSaveWidget->setLocationText( - KDbUtils::stringToFileName(captionOrName)); -#else - m_fileSaveWidget = new QWidget(this); - QVBoxLayout *lyr = new QVBoxLayout(m_fileSaveWidget); - m_fileSaveRequester = new KexiFileRequester(url, m_fileSaveWidget); - lyr->addWidget(m_fileSaveRequester); - m_fileSaveRequester->setFileMode(KexiFileFilters::CustomSavingFileBasedDB); - m_fileSaveRequester->setAdditionalMimeTypes(csvMimeTypes()); - //TODO m_fileSaveRequester->setDefaultExtension("csv"); - lyr->addStretch(1); -#endif - m_fileSavePage = new KPageWidgetItem(m_fileSaveWidget, xi18n("Enter Name of File You Want to Save Data To")); + const QUrl url("kfiledialog:///CSVImportExport"); // startDir + m_fileIface = KexiFileWidgetInterface::createWidget( + url, KexiFileFilters::CustomSavingFileBasedDB, this); + m_fileIface->setAdditionalMimeTypes(csvMimeTypes()); + m_fileIface->setDefaultExtension("csv"); + //TODO m_fileSaveWidget->setLocationText( + // KDbUtils::stringToFileName(captionOrName)); + m_fileSavePage = new KPageWidgetItem(m_fileIface->widget(), + xi18n("Enter Name of File You Want to Save Data To")); addPage(m_fileSavePage); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); } /* 2. Export options m_exportOptionsPage exportOptionsLyr m_infoLblFrom m_infoLblTo m_showOptionsButton m_exportOptionsSection exportOptionsSectionLyr */ m_exportOptionsWidget = new QWidget(this); m_exportOptionsWidget->setObjectName("m_exportOptionsPage"); QGridLayout *exportOptionsLyr = new QGridLayout(m_exportOptionsWidget); exportOptionsLyr->setObjectName("exportOptionsLyr"); m_infoLblFrom = new KexiCSVInfoLabel(infoLblFromText, m_exportOptionsWidget, true/*showFnameLine*/); KexiPart::Info *partInfo = Kexi::partManager().infoForPluginId( QString("org.kexi-project.%1").arg(m_tableOrQuery->table() ? "table" : "query")); if (partInfo) { m_infoLblFrom->setIcon(partInfo->iconName()); } m_infoLblFrom->separator()->hide(); m_infoLblFrom->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); exportOptionsLyr->addWidget(m_infoLblFrom, 0, 0, 1, 2); m_infoLblTo = new KexiCSVInfoLabel( (m_options.mode == KexiCSVExport::File) ? xi18n("To CSV file:") : xi18n("To clipboard."), m_exportOptionsWidget, true/*showFnameLine*/); if (m_options.mode == KexiCSVExport::Clipboard) m_infoLblTo->setIcon(koIconName("edit-paste")); m_infoLblTo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); exportOptionsLyr->addWidget(m_infoLblTo, 1, 0, 1, 2); exportOptionsLyr->setRowStretch(2, 1); m_showOptionsButton = new QPushButton(xi18n("Show Options >>")); m_showOptionsButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(m_showOptionsButton, SIGNAL(clicked()), this, SLOT(slotShowOptionsButtonClicked())); exportOptionsLyr->addWidget(m_showOptionsButton, 3, 1, Qt::AlignRight); // - m_exportOptionsSection = new QGroupBox(""/*xi18n("Options")*/); m_exportOptionsSection->setObjectName("m_exportOptionsSection"); m_exportOptionsSection->setAlignment(Qt::Vertical); m_exportOptionsSection->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); exportOptionsLyr->addWidget(m_exportOptionsSection, 4, 0, 1, 2); QGridLayout *exportOptionsSectionLyr = new QGridLayout; exportOptionsLyr->setObjectName("exportOptionsLyr"); m_exportOptionsSection->setLayout(exportOptionsSectionLyr); // -delimiter QLabel *delimiterLabel = new QLabel(xi18n("Delimiter:")); exportOptionsSectionLyr->addWidget(delimiterLabel, 0, 0); m_delimiterWidget = new KexiCSVDelimiterWidget(false /* !lineEditOnBottom*/); m_delimiterWidget->setDelimiter(defaultDelimiter()); delimiterLabel->setBuddy(m_delimiterWidget); exportOptionsSectionLyr->addWidget(m_delimiterWidget, 0, 1); // -text quote QLabel *textQuoteLabel = new QLabel(xi18n("Text quote:")); exportOptionsSectionLyr->addWidget(textQuoteLabel, 1, 0); QWidget *textQuoteWidget = new QWidget; QHBoxLayout *textQuoteLyr = new QHBoxLayout(textQuoteWidget); m_textQuote = new KexiCSVTextQuoteComboBox(textQuoteWidget); m_textQuote->setTextQuote(defaultTextQuote()); textQuoteLabel->setBuddy(m_textQuote); textQuoteLyr->addWidget(m_textQuote); textQuoteLyr->addStretch(0); exportOptionsSectionLyr->addWidget(textQuoteWidget, 1, 1); // - character encoding QLabel *characterEncodingLabel = new QLabel(xi18n("Text encoding:")); exportOptionsSectionLyr->addWidget(characterEncodingLabel, 2, 0); m_characterEncodingCombo = new KexiCharacterEncodingComboBox(); m_characterEncodingCombo->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); characterEncodingLabel->setBuddy(m_characterEncodingCombo); exportOptionsSectionLyr->addWidget(m_characterEncodingCombo, 2, 1); // - checkboxes m_addColumnNamesCheckBox = new QCheckBox(xi18n("Add column names as the first row")); m_addColumnNamesCheckBox->setChecked(true); exportOptionsSectionLyr->addWidget(m_addColumnNamesCheckBox, 3, 1); m_defaultsBtn = new QPushButton(xi18n("Defaults"), this); connect(m_defaultsBtn, SIGNAL(clicked()), this, SLOT(slotDefaultsButtonClicked())); exportOptionsLyr->addWidget(m_defaultsBtn, 5, 0); exportOptionsLyr->setColumnStretch(1, 1); m_alwaysUseCheckBox = new QCheckBox( m_options.mode == KexiCSVExport::Clipboard ? xi18n("Always use above options for copying") : xi18n("Always use above options for exporting")); exportOptionsLyr->addWidget(m_alwaysUseCheckBox, 5, 1, Qt::AlignRight); m_exportOptionsSection->hide(); m_defaultsBtn->hide(); m_alwaysUseCheckBox->hide(); // - m_exportOptionsPage = new KPageWidgetItem(m_exportOptionsWidget, m_options.mode == KexiCSVExport::Clipboard ? xi18n("Copying") : xi18n("Exporting")); addPage(m_exportOptionsPage); // load settings if (m_options.mode != KexiCSVExport::Clipboard && readBoolEntry("ShowOptionsInCSVExportDialog", false)) { show(); slotShowOptionsButtonClicked(); } if (readBoolEntry("StoreOptionsForCSVExportDialog", false)) { // load defaults: m_alwaysUseCheckBox->setChecked(true); QString s = readEntry("DefaultDelimiterForExportingCSVFiles", defaultDelimiter()); if (!s.isEmpty()) m_delimiterWidget->setDelimiter(s); s = readEntry("DefaultTextQuoteForExportingCSVFiles", defaultTextQuote()); m_textQuote->setTextQuote(s); //will be invaliudated here, so not a problem s = readEntry("DefaultEncodingForExportingCSVFiles"); if (!s.isEmpty()) m_characterEncodingCombo->setSelectedEncoding(s); m_addColumnNamesCheckBox->setChecked( readBoolEntry("AddColumnNamesForExportingCSVFiles", true)); } // -keep widths equal on page #2: int width = qMax(m_infoLblFrom->leftLabel()->sizeHint().width(), m_infoLblTo->leftLabel()->sizeHint().width()); m_infoLblFrom->leftLabel()->setFixedWidth(width); m_infoLblTo->leftLabel()->setFixedWidth(width); updateGeometry(); } KexiCSVExportWizard::~KexiCSVExportWizard() { delete m_tableOrQuery; } bool KexiCSVExportWizard::canceled() const { return m_canceled; } void KexiCSVExportWizard::slotCurrentPageChanged(KPageWidgetItem *page, KPageWidgetItem *prev) { Q_UNUSED(prev) if (page == m_fileSavePage) { - m_fileSaveWidget->setFocus(); + m_fileIface->widget()->setFocus(); } else if (page == m_exportOptionsPage) { if (m_options.mode == KexiCSVExport::File) - m_infoLblTo->setFileName(selectedFile().toLocalFile()); + m_infoLblTo->setFileName(selectedFile()); } } -QUrl KexiCSVExportWizard::selectedFile() const +QString KexiCSVExportWizard::selectedFile() const { -#ifdef KEXI_USE_KFILEWIDGET - return QUrl::fromLocalFile(m_fileSaveWidget->highlightedFile()); -#else - return QUrl::fromLocalFile(m_fileSaveRequester->selectedFileName()); -#endif + return m_fileIface->selectedFile(); } void KexiCSVExportWizard::next() { if (currentPage() == m_fileSavePage) { -#ifdef KEXI_USE_KFILEWIDGET - if (!m_fileSaveWidget->checkSelectedFile()) { + if (!m_fileIface->checkSelectedFile()) { return; } -#else - if (m_fileSaveRequester->selectedFileName().isEmpty()) { - return; - } -#endif - //qDebug() << "selectedFile:" << selectedFile(); - //qDebug() << "selectedUrl:" << m_fileSaveWidget->selectedUrl(); - //qDebug() << "highlightedFile:" << m_fileSaveWidget->highlightedFile(); KAssistantDialog::next(); return; } KAssistantDialog::next(); } void KexiCSVExportWizard::done(int result) { if (QDialog::Accepted == result) { if (m_fileSavePage) { //qDebug() << selectedFile(); - m_options.fileName = selectedFile().toLocalFile(); + m_options.fileName = selectedFile(); } m_options.delimiter = m_delimiterWidget->delimiter(); m_options.textQuote = m_textQuote->textQuote(); m_options.addColumnNames = m_addColumnNamesCheckBox->isChecked(); if (!KexiCSVExport::exportData(m_tableOrQuery, m_options)) return; //store options if (m_options.mode != KexiCSVExport::Clipboard) writeEntry("ShowOptionsInCSVExportDialog", m_exportOptionsSection->isVisible()); const bool store = m_alwaysUseCheckBox->isChecked(); writeEntry("StoreOptionsForCSVExportDialog", store); // only save if an option differs from default if (store && m_delimiterWidget->delimiter() != defaultDelimiter()) writeEntry("DefaultDelimiterForExportingCSVFiles", m_delimiterWidget->delimiter()); else deleteEntry("DefaultDelimiterForExportingCSVFiles"); if (store && m_textQuote->textQuote() != defaultTextQuote()) writeEntry("DefaultTextQuoteForExportingCSVFiles", m_textQuote->textQuote()); else deleteEntry("DefaultTextQuoteForExportingCSVFiles"); if (store && !m_characterEncodingCombo->defaultEncodingSelected()) writeEntry( "DefaultEncodingForExportingCSVFiles", m_characterEncodingCombo->selectedEncoding()); else deleteEntry("DefaultEncodingForExportingCSVFiles"); if (store && !m_addColumnNamesCheckBox->isChecked()) writeEntry( "AddColumnNamesForExportingCSVFiles", m_addColumnNamesCheckBox->isChecked()); else deleteEntry("AddColumnNamesForExportingCSVFiles"); } else if (QDialog::Rejected == result) { //nothing to do } KAssistantDialog::done(result); } void KexiCSVExportWizard::slotShowOptionsButtonClicked() { if (m_exportOptionsSection->isVisible()) { m_showOptionsButton->setText(xi18n("Show Options >>")); m_exportOptionsSection->hide(); m_alwaysUseCheckBox->hide(); m_defaultsBtn->hide(); } else { m_showOptionsButton->setText(xi18n("Hide Options <<")); m_exportOptionsSection->show(); m_alwaysUseCheckBox->show(); m_defaultsBtn->show(); } } void KexiCSVExportWizard::slotDefaultsButtonClicked() { m_delimiterWidget->setDelimiter(defaultDelimiter()); m_textQuote->setTextQuote(defaultTextQuote()); m_addColumnNamesCheckBox->setChecked(true); m_characterEncodingCombo->selectDefaultEncoding(); } static QString convertKey(const char *key, KexiCSVExport::Mode mode) { QString _key(QString::fromLatin1(key)); if (mode == KexiCSVExport::Clipboard) { _key.replace("Exporting", "Copying"); _key.replace("Export", "Copy"); _key.replace("CSVFiles", "CSVToClipboard"); } return _key; } bool KexiCSVExportWizard::readBoolEntry(const char *key, bool defaultValue) { return m_importExportGroup.readEntry(convertKey(key, m_options.mode), defaultValue); } QString KexiCSVExportWizard::readEntry(const char *key, const QString& defaultValue) { return m_importExportGroup.readEntry(convertKey(key, m_options.mode), defaultValue); } void KexiCSVExportWizard::writeEntry(const char *key, const QString& value) { m_importExportGroup.writeEntry(convertKey(key, m_options.mode), value); } void KexiCSVExportWizard::writeEntry(const char *key, bool value) { m_importExportGroup.writeEntry(convertKey(key, m_options.mode), value); } void KexiCSVExportWizard::deleteEntry(const char *key) { m_importExportGroup.deleteEntry(convertKey(key, m_options.mode)); } QString KexiCSVExportWizard::defaultDelimiter() const { if (m_options.mode == KexiCSVExport::Clipboard) { if (!m_options.forceDelimiter.isEmpty()) return m_options.forceDelimiter; else return KEXICSV_DEFAULT_CLIPBOARD_DELIMITER; } return KEXICSV_DEFAULT_FILE_DELIMITER; } QString KexiCSVExportWizard::defaultTextQuote() const { if (m_options.mode == KexiCSVExport::Clipboard) return KEXICSV_DEFAULT_CLIPBOARD_TEXT_QUOTE; return KEXICSV_DEFAULT_FILE_TEXT_QUOTE; } diff --git a/src/plugins/importexport/csv/kexicsvexportwizard.h b/src/plugins/importexport/csv/kexicsvexportwizard.h index 4684752b0..a1f278c76 100644 --- a/src/plugins/importexport/csv/kexicsvexportwizard.h +++ b/src/plugins/importexport/csv/kexicsvexportwizard.h @@ -1,124 +1,113 @@ /* This file is part of the KDE project Copyright (C) 2012 Oleg Kukharchuk - Copyright (C) 2005-2016 Jarosław Staniek + Copyright (C) 2005-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_CSVEXPORTWIZARD_H #define KEXI_CSVEXPORTWIZARD_H #include #include #include -#include #include "kexicsvexport.h" class QCheckBox; class QGroupBox; class QPushButton; class KexiCSVDelimiterWidget; class KexiCSVTextQuoteComboBox; class KexiCSVInfoLabel; class KexiCharacterEncodingComboBox; +class KexiFileWidgetInterface; class KPageWidgetItem; class KDbTableOrQuerySchema; -#ifdef KEXI_USE_KFILEWIDGET -class KexiFileWidget; -#else -class KexiFileRequester; -#endif - /*! @short Kexi CSV export wizard Supports exporting to a file and to a clipboard. */ class KexiCSVExportWizard : public KAssistantDialog { Q_OBJECT public: explicit KexiCSVExportWizard(const KexiCSVExport::Options& options, QWidget * parent = 0); virtual ~KexiCSVExportWizard(); bool canceled() const; protected Q_SLOTS: virtual void next(); virtual void done(int result); void slotShowOptionsButtonClicked(); void slotDefaultsButtonClicked(); void slotCurrentPageChanged(KPageWidgetItem*, KPageWidgetItem*); protected: //! \return default delimiter depending on mode. QString defaultDelimiter() const; //! \return default text quote depending on mode. QString defaultTextQuote() const; //! Helper, works like KSharedConfig::openConfig()->readBoolEntry(const char*, bool) but if mode is Clipboard, //! "Exporting" is replaced with "Copying" and "Export" is replaced with "Copy" //! and "CSVFiles" is replaced with "CSVToClipboard" //! in \a key, to keep the setting separate. bool readBoolEntry(const char *key, bool defaultValue); //! Helper like \ref readBoolEntry(const char *, bool), but for QString values. QString readEntry(const char *key, const QString& defaultValue = QString()); //! Helper, works like KSharedConfig::openConfig()->writeEntry(const char*,bool) but if mode is Clipboard, //! "Exporting" is replaced with "Copying" and "Export" is replaced with "Copy" //! and "CSVFiles" is replaced with "CSVToClipboard" //! in \a key, to keep the setting separate. void writeEntry(const char *key, bool value); //! Helper like \ref writeEntry(const char *, bool), but for QString values. void writeEntry(const char *key, const QString& value); //! Helper like \ref writeEntry(const char *, bool), but for deleting config entry. void deleteEntry(const char *key); - QUrl selectedFile() const; + QString selectedFile() const; KexiCSVExport::Options m_options; QWidget* m_exportOptionsWidget = nullptr; KPageWidgetItem *m_fileSavePage = nullptr; KPageWidgetItem *m_exportOptionsPage = nullptr; QPushButton *m_showOptionsButton = nullptr; QPushButton *m_defaultsBtn = nullptr; QGroupBox* m_exportOptionsSection = nullptr; KexiCSVInfoLabel *m_infoLblFrom = nullptr; KexiCSVInfoLabel *m_infoLblTo = nullptr; KexiCSVDelimiterWidget* m_delimiterWidget = nullptr; KexiCSVTextQuoteComboBox* m_textQuote = nullptr; KexiCharacterEncodingComboBox *m_characterEncodingCombo = nullptr; QCheckBox *m_addColumnNamesCheckBox = nullptr; QCheckBox *m_alwaysUseCheckBox = nullptr; -#ifdef KEXI_USE_KFILEWIDGET - KexiFileWidget* m_fileSaveWidget = nullptr; -#else - QWidget* m_fileSaveWidget = nullptr; - KexiFileRequester *m_fileSaveRequester; -#endif + KexiFileWidgetInterface *m_fileIface = nullptr; KDbTableOrQuerySchema* m_tableOrQuery; KConfigGroup m_importExportGroup; bool m_canceled = false; }; #endif diff --git a/src/plugins/importexport/csv/kexicsvimportdialog.cpp b/src/plugins/importexport/csv/kexicsvimportdialog.cpp index dbecfae45..958c65ee2 100644 --- a/src/plugins/importexport/csv/kexicsvimportdialog.cpp +++ b/src/plugins/importexport/csv/kexicsvimportdialog.cpp @@ -1,2208 +1,2181 @@ /* This file is part of the KDE project - Copyright (C) 2005-2016 Jarosław Staniek + Copyright (C) 2005-2017 Jarosław Staniek Copyright (C) 2012 Oleg Kukharchuk This work is based on kspread/dialogs/kspread_dlg_csv.cc. Copyright (C) 2002-2003 Norbert Andres Copyright (C) 2002-2003 Ariya Hidayat Copyright (C) 2002 Laurent Montel Copyright (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexicsvimportdialog.h" #include "KexiCSVImportDialogModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "kexicsvwidgets.h" #include -#ifdef KEXI_USE_KFILEWIDGET -#include -#endif - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _IMPORT_ICON koIconNeededWithSubs("change to file_import or so", "file_import","table") //! @internal An item delegate for KexiCSVImportDialog's table view class KexiCSVImportDialogItemDelegate : public QStyledItemDelegate { Q_OBJECT public: KexiCSVImportDialogItemDelegate(QObject *parent = 0); virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; KexiCSVImportDialogItemDelegate::KexiCSVImportDialogItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QWidget* KexiCSVImportDialogItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem newOption(option); QWidget *editor = QStyledItemDelegate::createEditor(parent, newOption, index); if (editor && index.row() == 0) { QFont f(editor->font()); f.setBold(true); editor->setFont(f); } return editor; } // -- //! @internal class KexiCSVImportStatic { public: KexiCSVImportStatic() : types(QVector() << KDbField::Text << KDbField::Integer << KDbField::Double << KDbField::Boolean << KDbField::Date << KDbField::Time << KDbField::DateTime) { typeNames.insert(KDbField::Text, KDbField::typeGroupName(KDbField::TextGroup)); typeNames.insert(KDbField::Integer, KDbField::typeGroupName(KDbField::IntegerGroup)); typeNames.insert(KDbField::Double, KDbField::typeGroupName(KDbField::FloatGroup)); typeNames.insert(KDbField::Boolean, KDbField::typeName(KDbField::Boolean)); typeNames.insert(KDbField::Date, KDbField::typeName(KDbField::Date)); typeNames.insert(KDbField::Time, KDbField::typeName(KDbField::Time)); typeNames.insert(KDbField::DateTime, KDbField::typeName(KDbField::DateTime)); for (int i = 0; i < types.size(); ++i) { indicesForTypes.insert(types[i], i); } } const QVector types; QHash typeNames; QHash indicesForTypes; }; Q_GLOBAL_STATIC(KexiCSVImportStatic, kexiCSVImportStatic) #define MAX_ROWS_TO_PREVIEW 100 //max 100 rows is reasonable #define MAX_BYTES_TO_PREVIEW 10240 //max 10KB is reasonable #define MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER 4096 #define MINIMUM_YEAR_FOR_100_YEAR_SLIDING_WINDOW 1930 #define PROGRESS_STEP_MS (1000/5) // 5 updates per second static bool shouldSaveRow(int row, bool firstRowForFieldNames) { return row > (firstRowForFieldNames ? 1 : 0); } // -- class Q_DECL_HIDDEN KexiCSVImportDialog::Private { public: Private() : imported(false) { } ~Private() { qDeleteAll(m_uniquenessTest); } void clearDetectedTypes() { m_detectedTypes.clear(); } void clearUniquenessTests() { qDeleteAll(m_uniquenessTest); m_uniquenessTest.clear(); } KDbField::Type detectedType(int col) const { return m_detectedTypes.value(col, KDbField::InvalidType); } void setDetectedType(int col, KDbField::Type type) { if (m_detectedTypes.count() <= col) { for (int i = m_detectedTypes.count(); i < col; ++i) { // append missing bits m_detectedTypes.append(KDbField::InvalidType); } m_detectedTypes.append(type); } else { m_detectedTypes[col] = type; } } QList* uniquenessTest(int col) const { return m_uniquenessTest.value(col); } void setUniquenessTest(int col, QList* test) { if (m_uniquenessTest.count() <= col) { for (int i = m_uniquenessTest.count(); i < col; ++i) { // append missing bits m_uniquenessTest.append(0); } m_uniquenessTest.append(test); } else { m_uniquenessTest[col] = test; } } bool imported; private: //! vector of detected types //! @todo more types QList m_detectedTypes; //! m_detectedUniqueColumns[i]==true means that i-th column has unique values //! (only for numeric type) QList< QList* > m_uniquenessTest; }; // -- KexiCSVImportDialog::KexiCSVImportDialog(Mode mode, QWidget * parent) : KAssistantDialog(parent), m_parseComments(false), m_canceled(false), m_adjustRows(true), m_startline(0), m_textquote(QString(KEXICSV_DEFAULT_FILE_TEXT_QUOTE)[0]), m_commentSymbol(QString(KEXICSV_DEFAULT_COMMENT_START)[0]), m_mode(mode), m_columnsAdjusted(false), m_firstFillTableCall(true), m_blockUserEvents(false), m_primaryKeyColumn(-1), m_dialogCanceled(false), m_conn(0), m_fieldsListModel(0), m_destinationTableSchema(0), m_implicitPrimaryKeyAdded(false), m_allRowsLoadedInPreview(false), m_stoppedAt_MAX_BYTES_TO_PREVIEW(false), m_stringNo("no"), m_stringI18nNo(xi18n("no")), m_stringFalse("false"), m_stringI18nFalse(xi18n("false")), m_partItemForSavedTable(0), m_importInProgress(false), m_importCanceled(false), d(new Private) { setWindowTitle( mode == File ? xi18nc("@title:window", "Import CSV Data From File") : xi18nc("@title:window", "Paste CSV Data From Clipboard") ); setWindowIcon(_IMPORT_ICON); //! @todo use "Paste CSV Data From Clipboard" caption for mode==Clipboard setObjectName("KexiCSVImportDialog"); setSizeGripEnabled(true); KexiMainWindowIface::global()->setReasonableDialogSize(this); KGuiItem::assign(configureButton(), KStandardGuiItem::configure()); finishButton()->setEnabled(false); backButton()->setEnabled(false); KConfigGroup importExportGroup(KSharedConfig::openConfig()->group("ImportExport")); m_maximumRowsForPreview = importExportGroup.readEntry( "MaximumRowsForPreviewInImportDialog", MAX_ROWS_TO_PREVIEW); m_maximumBytesForPreview = importExportGroup.readEntry( "MaximumBytesForPreviewInImportDialog", MAX_BYTES_TO_PREVIEW); m_minimumYearFor100YearSlidingWindow = importExportGroup.readEntry( "MinimumYearFor100YearSlidingWindow", MINIMUM_YEAR_FOR_100_YEAR_SLIDING_WINDOW); m_pkIcon = KexiSmallIcon("database-key"); if (m_mode == File) { createFileOpenPage(); } else if (m_mode == Clipboard) { QString subtype("plain"); m_clipboardData = QApplication::clipboard()->text(subtype, QClipboard::Clipboard); /* debug for (int i=0;QApplication::clipboard()->data(QClipboard::Clipboard)->format(i);i++) qDebug() << i << ": " << QApplication::clipboard()->data(QClipboard::Clipboard)->format(i); */ } else { return; } m_file = 0; m_inputStream = 0; createOptionsPage(); createImportMethodPage(); createTableNamePage(); createImportPage(); /** @todo reuse Clipboard too! */ /*if ( m_mode == Clipboard ) { setWindowTitle( xi18n( "Inserting From Clipboard" ) ); QMimeSource * mime = QApplication::clipboard()->data(); if ( !mime ) { KMessageBox::information( this, xi18n("There is no data in the clipboard.") ); m_canceled = true; return; } if ( !mime->provides( "text/plain" ) ) { KMessageBox::information( this, xi18n("There is no usable data in the clipboard.") ); m_canceled = true; return; } m_fileArray = QByteArray(mime->encodedData( "text/plain" ) ); } else if ( mode == File ) {*/ m_dateRegExp = QRegularExpression("^(\\d{1,4})([/\\-\\.])(\\d{1,2})([/\\-\\.])(\\d{1,4})$"); m_timeRegExp1 = QRegularExpression("^(\\d{1,2}):(\\d{1,2}):(\\d{1,2})$"); m_timeRegExp2 = QRegularExpression("^(\\d{1,2}):(\\d{1,2})$"); m_fpNumberRegExp1 = QRegularExpression("^[\\-]{0,1}\\d*[,\\.]\\d+$"); // E notation, e.g. 0.1e2, 0.1e+2, 0.1e-2, 0.1E2, 0.1E+2, 0.1E-2 m_fpNumberRegExp2 = QRegularExpression("^[\\-]{0,1}\\d*[,\\.]\\d+[Ee][+-]{0,1}\\d+$"); m_loadingProgressDlg = 0; if (m_mode == Clipboard) { m_infoLbl->setIcon(koIconName("edit-paste")); } m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); connect(m_formatCombo, SIGNAL(activated(int)), this, SLOT(formatChanged(int))); connect(m_delimiterWidget, SIGNAL(delimiterChanged(QString)), this, SLOT(delimiterChanged(QString))); connect(m_commentWidget, SIGNAL(commentSymbolChanged(QString)), this, SLOT(commentSymbolChanged(QString))); connect(m_startAtLineSpinBox, SIGNAL(valueChanged(int)), this, SLOT(startlineSelected(int))); connect(m_comboQuote, SIGNAL(activated(int)), this, SLOT(textquoteSelected(int))); connect(m_tableView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(currentCellChanged(QModelIndex,QModelIndex))); connect(m_ignoreDuplicates, SIGNAL(stateChanged(int)), this, SLOT(ignoreDuplicatesChanged(int))); connect(m_1stRowForFieldNames, SIGNAL(stateChanged(int)), this, SLOT(slot1stRowForFieldNamesChanged(int))); connect(configureButton(), &QPushButton::clicked, this, &KexiCSVImportDialog::optionsButtonClicked); connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*))); KexiUtils::installRecursiveEventFilter(this, this); if ( m_mode == Clipboard ) initLater(); } KexiCSVImportDialog::~KexiCSVImportDialog() { delete m_file; delete m_inputStream; delete d; } void KexiCSVImportDialog::next() { KPageWidgetItem *curPage = currentPage(); if (curPage == m_openFilePage) { -#ifdef KEXI_USE_KFILEWIDGET - if (m_openFileWidget->checkSelectedFile()) { - m_fname = m_openFileWidget->highlightedFile(); -#else - if (!m_openFileRequester->selectedFileName().isEmpty()) { - m_fname = m_openFileRequester->selectedFileName(); -#endif + if (m_fileIface->checkSelectedFile()) { + m_fname = m_fileIface->selectedFile(); } else { return; } - if (m_fname.isEmpty()) { - KMessageBox::sorry(this, xi18nc("@info", "Select source filename.")); - return; - } if (!openData()) { return; } } else if (curPage == m_optionsPage) { const int numRows(m_table->rowCount()); if (numRows == 0) return; //impossible if (numRows == 1) { if (KMessageBox::No == KMessageBox::questionYesNo(this, xi18n("Data set contains no rows. Do you want to import empty table?"))) return; } } else if (curPage == m_saveMethodPage) { if (m_newTableOption->isChecked()) { m_tableNameWidget->setCurrentIndex(0); m_newTableWidget->setFocus(); } else { m_tableNameWidget->setCurrentIndex(1); m_tablesList->setFocus(); } } else if (curPage == m_tableNamePage) { KexiGUIMessageHandler msg; KexiProject *project = KexiMainWindowIface::global()->project(); if (!project) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No project available.")); return; } m_conn = project->dbConnection(); if (!m_conn) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No database connection available.")); return; } if (m_newTableOption->isChecked()) { m_partItemForSavedTable->setCaption(m_newTableWidget->captionText()); m_partItemForSavedTable->setName(m_newTableWidget->nameText()); KexiPart::Part *part = Kexi::partManager().partForPluginId("org.kexi-project.table"); KDbObject tmp; tristate res = (part && part->info()) ? m_conn->loadObjectData( project->typeIdForPluginId(part->info()->pluginId()), m_newTableWidget->nameText(), &tmp) : false; if (res == true) { KMessageBox::information(this, "

" + part->i18nMessage("Object %1 already exists.", 0) .subs(m_newTableWidget->nameText()).toString() + "

" + xi18n("Please choose other name.") + "

" ); return; } else if (res == false) { qFatal("Plugin org.kexi-project.table not found"); return; } } else { m_partItemForSavedTable = m_tablesList->selectedPartItem(); } } KAssistantDialog::next(); } void KexiCSVImportDialog::slotShowSchema(KexiPart::Item *item) { if (!item) { return; } nextButton()->setEnabled(true); KDbTableOrQuerySchema *tableOrQuery = new KDbTableOrQuerySchema( KexiMainWindowIface::global()->project()->dbConnection(), item->identifier() ); m_tableCaptionLabel->setText(tableOrQuery->captionOrName()); m_tableNameLabel->setText(tableOrQuery->name()); m_recordCountLabel->setText(QString::number(KDb::recordCount(tableOrQuery))); m_colCountLabel->setText(QString::number(tableOrQuery->fieldCount())); delete m_fieldsListModel; m_fieldsListModel = new KexiFieldListModel(m_fieldsListView, ShowDataTypes); m_fieldsListModel->setSchema(tableOrQuery); m_fieldsListView->setModel(m_fieldsListModel); m_fieldsListView->header()->resizeSections(QHeaderView::ResizeToContents); } void KexiCSVImportDialog::slotCurrentPageChanged(KPageWidgetItem *page, KPageWidgetItem *prev) { nextButton()->setEnabled(page == m_saveMethodPage ? false : true); finishButton()->setEnabled(page == m_importPage ? true : false); if (page == m_importPage) { KGuiItem::assign(finishButton(), KGuiItem(xi18nc("@action:button Import CSV", "&Import..."), _IMPORT_ICON)); } configureButton()->setEnabled(page == m_optionsPage); nextButton()->setEnabled(page == m_importPage ? false : true); backButton()->setEnabled(page == m_openFilePage ? false : true); if (page == m_saveMethodPage && prev == m_tableNamePage && m_partItemForSavedTable) { if (m_newTableOption->isChecked()) { KexiMainWindowIface::global()->project()->deleteUnstoredItem(m_partItemForSavedTable); } m_partItemForSavedTable = 0; } if(page == m_optionsPage){ if (m_mode == File) { m_loadingProgressDlg = new QProgressDialog(this); m_loadingProgressDlg->setObjectName("m_loadingProgressDlg"); m_loadingProgressDlg->setLabelText( xi18nc("@info", "Loading CSV Data from %1...", QDir::toNativeSeparators(m_fname))); m_loadingProgressDlg->setWindowTitle(xi18nc("@title:window", "Loading CSV Data")); m_loadingProgressDlg->setModal(true); m_loadingProgressDlg->setMaximum(m_maximumRowsForPreview); m_loadingProgressDlg->show(); } // delimiterChanged(detectedDelimiter); // this will cause fillTable() m_detectDelimiter = true; m_columnsAdjusted = false; fillTable(); delete m_loadingProgressDlg; m_loadingProgressDlg = 0; if (m_dialogCanceled) { // m_loadingProgressDlg->hide(); // m_loadingProgressDlg->close(); QTimer::singleShot(0, this, SLOT(reject())); return; } currentCellChanged(m_table->index(0,0), QModelIndex()); if (m_loadingProgressDlg) m_loadingProgressDlg->hide(); m_tableView->setFocus(); } else if (page == m_saveMethodPage) { m_newTableOption->setFocus(); } else if (page == m_tableNamePage) { if (m_newTableOption->isChecked() && !m_partItemForSavedTable) { KexiGUIMessageHandler msg; KexiProject *project = KexiMainWindowIface::global()->project(); //get suggested name based on the file name QString suggestedName; if (m_mode == File) { suggestedName = QUrl(m_fname).fileName(); //remove extension if (!suggestedName.isEmpty()) { const int idx = suggestedName.lastIndexOf('.'); if (idx != -1) { suggestedName = suggestedName.mid(0, idx).simplified(); } } } KexiPart::Part *part = Kexi::partManager().partForPluginId("org.kexi-project.table"); if (!part) { msg.showErrorMessage(Kexi::partManager().result()); return; } //-new part item m_partItemForSavedTable = project->createPartItem(part->info(), suggestedName); if (!m_partItemForSavedTable) { msg.showErrorMessage(project->result()); return; } m_newTableWidget->setCaptionText(m_partItemForSavedTable->caption()); m_newTableWidget->setNameText(m_partItemForSavedTable->name()); m_newTableWidget->captionLineEdit()->setFocus(); m_newTableWidget->captionLineEdit()->selectAll(); } else if (!m_newTableOption->isChecked()) { KexiPart::Item *i = m_tablesList->selectedPartItem(); if (!i) { nextButton()->setEnabled(false); } slotShowSchema(i); } } else if (page == m_importPage) { m_fromLabel->setFileName(m_fname); m_toLabel->setFileNameText(m_partItemForSavedTable->name()); m_importingProgressBar->hide(); m_importProgressLabel->hide(); } } void KexiCSVImportDialog::createFileOpenPage() { -#ifdef KEXI_USE_KFILEWIDGET - m_openFileWidget = new KexiFileWidget( - QUrl("kfiledialog:///CSVImportExport"), //startDir - KexiFileFilters::CustomOpening, - this); - m_openFileWidget->setAdditionalMimeTypes(csvMimeTypes()); - m_openFileWidget->setDefaultExtension("csv"); - connect(m_openFileWidget, SIGNAL(fileSelected(QUrl)), this, SLOT(next())); -#else - m_openFileWidget = new QWidget(this); - QVBoxLayout *lyr = new QVBoxLayout(m_openFileWidget); - m_openFileRequester = new KexiFileRequester( - QUrl("kfiledialog:///CSVImportExport"), //startDir - m_openFileWidget); - m_openFileRequester->setAdditionalMimeTypes(csvMimeTypes()); - //TODO m_openFileRequester->setDefaultExtension("csv"); - connect(m_openFileRequester, &KexiFileRequester::fileSelected, this, &KexiCSVImportDialog::next); - lyr->addWidget(m_openFileRequester, 1); -#endif - m_openFilePage = new KPageWidgetItem(m_openFileWidget, xi18n("Select Import Filename")); + m_fileIface = KexiFileWidgetInterface::createWidget(QUrl("kfiledialog:///CSVImportExport"), + KexiFileFilters::CustomOpening, this); + m_fileIface->setAdditionalMimeTypes(csvMimeTypes()); + m_fileIface->setDefaultExtension("csv"); + m_fileIface->connectFileSelectedSignal(this, SLOT(next())); + m_openFilePage = new KPageWidgetItem(m_fileIface->widget(), xi18n("Select Import Filename")); addPage(m_openFilePage); } void KexiCSVImportDialog::createOptionsPage() { QWidget *m_optionsWidget = new QWidget(this); QVBoxLayout *lyr = new QVBoxLayout(m_optionsWidget); m_infoLbl = new KexiCSVInfoLabel( m_mode == File ? xi18n("Preview of data from file:") : xi18n("Preview of data from clipboard"), m_optionsWidget, m_mode == File /*showFnameLine*/ ); lyr->addWidget(m_infoLbl); QWidget* page = new QFrame(m_optionsWidget); QGridLayout *glyr = new QGridLayout(page); lyr->addWidget(page); // Delimiter: comma, semicolon, tab, space, other m_delimiterWidget = new KexiCSVDelimiterWidget(true /*lineEditOnBottom*/, page); glyr->addWidget(m_delimiterWidget, 1, 0, 1, 1); QLabel *delimiterLabel = new QLabel(xi18n("Delimiter:"), page); delimiterLabel->setBuddy(m_delimiterWidget); delimiterLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); glyr->addWidget(delimiterLabel, 0, 0, 1, 1); m_commentWidget = new KexiCSVCommentWidget(true, page); glyr->addWidget(m_commentWidget, 1, 4); QLabel *commentLabel = new QLabel(xi18n("Comment symbol:"), page); commentLabel->setBuddy(m_commentWidget); commentLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); glyr->addWidget(commentLabel, 0, 4); // Format: number, text... //! @todo Object and Currency types m_formatCombo = new KComboBox(page); m_formatCombo->setObjectName("m_formatCombo"); for (int i = 0; i < kexiCSVImportStatic->types.size(); ++i) { m_formatCombo->addItem(kexiCSVImportStatic->typeNames.value(kexiCSVImportStatic->types[i])); } glyr->addWidget(m_formatCombo, 1, 1, 1, 1); m_formatLabel = new QLabel(page); m_formatLabel->setBuddy(m_formatCombo); m_formatLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); glyr->addWidget(m_formatLabel, 0, 1); m_primaryKeyField = new QCheckBox(xi18n("Primary key"), page); m_primaryKeyField->setObjectName("m_primaryKeyField"); glyr->addWidget(m_primaryKeyField, 2, 1); connect(m_primaryKeyField, SIGNAL(toggled(bool)), this, SLOT(slotPrimaryKeyFieldToggled(bool))); m_comboQuote = new KexiCSVTextQuoteComboBox(page); glyr->addWidget(m_comboQuote, 1, 2); TextLabel2 = new QLabel(xi18n("Text quote:"), page); TextLabel2->setBuddy(m_comboQuote); TextLabel2->setObjectName("TextLabel2"); TextLabel2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); TextLabel2->setAlignment(Qt::AlignLeft | Qt::AlignBottom); glyr->addWidget(TextLabel2, 0, 2); m_startAtLineSpinBox = new QSpinBox(page); m_startAtLineSpinBox->setObjectName("m_startAtLineSpinBox"); m_startAtLineSpinBox->setMinimum(1); m_startAtLineSpinBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_startAtLineSpinBox->setMinimumWidth( QFontMetrics(m_startAtLineSpinBox->font()).width("8888888")); glyr->addWidget(m_startAtLineSpinBox, 1, 3); m_startAtLineLabel = new QLabel(page); m_startAtLineLabel->setBuddy(m_startAtLineSpinBox); m_startAtLineLabel->setObjectName("m_startAtLineLabel"); m_startAtLineLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); m_startAtLineLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); glyr->addWidget(m_startAtLineLabel, 0, 3); m_ignoreDuplicates = new QCheckBox(page); m_ignoreDuplicates->setObjectName("m_ignoreDuplicates"); m_ignoreDuplicates->setText(xi18n("Ignore duplicated delimiters")); glyr->addWidget(m_ignoreDuplicates, 2, 2, 1, 2); m_1stRowForFieldNames = new QCheckBox(page); m_1stRowForFieldNames->setObjectName("m_1stRowForFieldNames"); m_1stRowForFieldNames->setText(xi18n("First row contains column names")); glyr->addWidget(m_1stRowForFieldNames, 3, 2, 1, 2); QSpacerItem* spacer_2 = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred); glyr->addItem(spacer_2, 0, 5, 4, 1); glyr->setColumnStretch(5, 2); m_tableView = new QTableView(m_optionsWidget); m_table = new KexiCSVImportDialogModel(m_tableView); m_table->setObjectName("m_table"); m_tableView->setModel(m_table); m_tableItemDelegate = new KexiCSVImportDialogItemDelegate(m_tableView); m_tableView->setItemDelegate(m_tableItemDelegate); lyr->addWidget(m_tableView); QSizePolicy spolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); spolicy.setHorizontalStretch(1); spolicy.setVerticalStretch(1); m_tableView->setSizePolicy(spolicy); m_optionsPage = new KPageWidgetItem(m_optionsWidget, xi18n("Import Options")); addPage(m_optionsPage); } void KexiCSVImportDialog::createImportMethodPage() { m_saveMethodWidget = new QWidget(this); QGridLayout *l = new QGridLayout(m_saveMethodWidget); m_newTableOption = new QRadioButton( xi18nc("@option:check CSV import: data will be appended to a new table", "&New table")); m_newTableOption->setChecked(true); m_existingTableOption = new QRadioButton( xi18nc("@option:check CSV import: data will be appended to existing table", "&Existing table")); l->addWidget(m_newTableOption, 0, 0, 1, 1); l->addWidget(m_existingTableOption, 1, 0, 1, 1); QSpacerItem *hSpacer = new QSpacerItem(200, 20, QSizePolicy::Preferred, QSizePolicy::Minimum); QSpacerItem *vSpacer = new QSpacerItem(20, 200, QSizePolicy::Minimum, QSizePolicy::Expanding); l->addItem(hSpacer, 1, 1, 1, 1); l->addItem(vSpacer, 2, 0, 1, 1); m_saveMethodPage = new KPageWidgetItem(m_saveMethodWidget, xi18n("Choose Destination for Imported Data")); addPage(m_saveMethodPage); } void KexiCSVImportDialog::createTableNamePage() { m_tableNameWidget = new QStackedWidget(this); m_tableNameWidget->setObjectName("m_tableNameWidget"); QWidget *page1=new QWidget(m_tableNameWidget); m_newTableWidget = new KexiNameWidget(QString(), page1); m_newTableWidget->addNameSubvalidator(new KDbObjectNameValidator( KexiMainWindowIface::global()->project()->dbConnection()->driver())); QVBoxLayout *l=new QVBoxLayout(page1); l->addWidget(m_newTableWidget); l->addStretch(1); m_tableNameWidget->addWidget(page1); QSplitter *splitter = new QSplitter(m_tableNameWidget); QWidget *tablesListParentWidget = new QWidget; QVBoxLayout *tablesListParentWidgetLayout = new QVBoxLayout(tablesListParentWidget); tablesListParentWidgetLayout->setMargin(0); QLabel *tablesListLabel = new QLabel(xi18nc("@label", "Select existing table:")); tablesListParentWidgetLayout->addWidget(tablesListLabel); KexiProjectNavigator::Features tablesListFeatures = KexiProjectNavigator::DefaultFeatures; tablesListFeatures &= (~KexiProjectNavigator::AllowSingleClickForOpeningItems); tablesListFeatures &= (~KexiProjectNavigator::ClearSelectionAfterAction); tablesListFeatures |= KexiProjectNavigator::Borders; m_tablesList = new KexiProjectNavigator(tablesListParentWidget, tablesListFeatures); tablesListParentWidgetLayout->addWidget(m_tablesList, 1); tablesListLabel->setBuddy(m_tablesList); QString errorString; m_tablesList->setProject(KexiMainWindowIface::global()->project(), "org.kexi-project.table", &errorString, false); connect (m_tablesList, SIGNAL(openOrActivateItem(KexiPart::Item*,Kexi::ViewMode)), this, SLOT(next())); connect (m_tablesList, SIGNAL(selectionChanged(KexiPart::Item*)), this, SLOT(slotShowSchema(KexiPart::Item*))); splitter->addWidget(tablesListParentWidget); QWidget *tableDetailsWidget = new QWidget; QFormLayout *formLayout = new QFormLayout(tableDetailsWidget); formLayout->setContentsMargins(KexiUtils::marginHint(), 0, 0, 0); formLayout->addRow(new QLabel(xi18nc("@label Preview of selected table", "Table preview:"))); formLayout->addRow(xi18nc("@label", "Name:"), m_tableNameLabel = new QLabel(tableDetailsWidget)); formLayout->addRow(xi18nc("@label", "Caption:"), m_tableCaptionLabel = new QLabel(tableDetailsWidget)); formLayout->addRow(xi18nc("@label", "Row count:"), m_recordCountLabel = new QLabel(tableDetailsWidget)); formLayout->addRow(xi18nc("@label", "Column count:"), m_colCountLabel = new QLabel(tableDetailsWidget)); formLayout->addItem(new QSpacerItem(1, KexiUtils::spacingHint())); m_fieldsListView = new QTreeView(tableDetailsWidget); m_fieldsListView->setItemsExpandable(false); m_fieldsListView->setRootIsDecorated(false); QSizePolicy fieldsListViewPolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); fieldsListViewPolicy.setVerticalStretch(1); m_fieldsListView->setSizePolicy(fieldsListViewPolicy); formLayout->addRow(new QLabel(xi18nc("@label", "Fields:"))); formLayout->addRow(m_fieldsListView); splitter->addWidget(tableDetailsWidget); splitter->setStretchFactor(splitter->indexOf(tableDetailsWidget), 1); m_tableNameWidget->addWidget(splitter); m_tableNamePage = new KPageWidgetItem(m_tableNameWidget, xi18nc("@label", "Choose Name of Destination Table")); addPage(m_tableNamePage); } void KexiCSVImportDialog::createImportPage() { m_importWidget = new QWidget(this); m_fromLabel = new KexiCSVInfoLabel(m_mode == File ? xi18n("From CSV file:") : xi18n("From Clipboard"), m_importWidget, m_mode == File); m_fromLabel->separator()->hide(); if (m_mode != File) { m_fromLabel->setIcon(koIconName("edit-paste")); } m_toLabel = new KexiCSVInfoLabel(xi18nc("@label Importing CSV data to table:", "To table:"), m_importWidget, true); KexiPart::Info *partInfo = Kexi::partManager().infoForPluginId("org.kexi-project.table"); m_toLabel->setIcon(partInfo->iconName()); m_importProgressLabel = new QLabel(m_importWidget); m_importingProgressBar = new QProgressBar(m_importWidget); QVBoxLayout *l = new QVBoxLayout(m_importWidget); l->addWidget(m_fromLabel); l->addWidget(m_toLabel); l->addSpacing(m_importProgressLabel->fontMetrics().height()); l->addWidget(m_importProgressLabel); l->addWidget(m_importingProgressBar); l->addStretch(1); m_importingProgressBar->hide(); m_importProgressLabel->hide(); m_importPage = new KPageWidgetItem(m_importWidget, xi18n("Ready to Import")); addPage(m_importPage); } void KexiCSVImportDialog::initLater() { if (!openData()) return; m_columnsAdjusted = false; fillTable(); delete m_loadingProgressDlg; m_loadingProgressDlg = 0; if (m_dialogCanceled) { QTimer::singleShot(0, this, SLOT(reject())); return; } currentCellChanged(m_table->index(0,0), QModelIndex()); if (m_loadingProgressDlg) m_loadingProgressDlg->hide(); show(); m_tableView->setFocus(); } bool KexiCSVImportDialog::openData() { if (m_mode != File) //data already loaded, no encoding stuff needed return true; delete m_inputStream; m_inputStream = 0; if (m_file) { m_file->close(); delete m_file; } m_file = new QFile(m_fname); if (!m_file->open(QIODevice::ReadOnly)) { m_file->close(); delete m_file; m_file = 0; KMessageBox::sorry(this, xi18n("Cannot open input file %1.", QDir::toNativeSeparators(m_fname))); nextButton()->setEnabled(false); m_canceled = true; if (parentWidget()) parentWidget()->raise(); return false; } return true; } bool KexiCSVImportDialog::canceled() const { return m_canceled; } void KexiCSVImportDialog::fillTable() { KexiUtils::WaitCursor wc(true); repaint(); m_blockUserEvents = true; button(QDialogButtonBox::Cancel)->setEnabled(true); KexiUtils::WaitCursor wait; if (m_table->rowCount() > 0) //to accept editor m_tableView->setCurrentIndex(QModelIndex()); int row, column, maxColumn; QString field; m_table->clear(); d->clearDetectedTypes(); d->clearUniquenessTests(); m_primaryKeyColumn = -1; if (true != loadRows(field, row, column, maxColumn, true)) return; // file with only one line without EOL if (field.length() > 0) { setText(row - m_startline, column, field, true); ++row; field.clear(); } adjustRows(row - m_startline - (m_1stRowForFieldNames->isChecked() ? 1 : 0)); maxColumn = qMax(maxColumn, column); m_table->setColumnCount(maxColumn); for (column = 0; column < m_table->columnCount(); ++column) { updateColumn(column); if (!m_columnsAdjusted) m_tableView->resizeColumnToContents(column); } m_columnsAdjusted = true; if (m_primaryKeyColumn >= 0 && m_primaryKeyColumn < m_table->columnCount()) { if (KDbField::Integer != d->detectedType(m_primaryKeyColumn)) { setPrimaryKeyIcon(m_primaryKeyColumn, false); m_primaryKeyColumn = -1; } } m_tableView->setCurrentIndex(m_table->index(0, 0)); currentCellChanged(m_table->index(0, 0), QModelIndex()); setPrimaryKeyIcon(m_primaryKeyColumn, true); const int count = qMax(0, m_table->rowCount() - 1 + m_startline); m_allRowsLoadedInPreview = count < m_maximumRowsForPreview && !m_stoppedAt_MAX_BYTES_TO_PREVIEW; if (count > 1) { if (m_allRowsLoadedInPreview) { m_startAtLineSpinBox->setMaximum(count); m_startAtLineSpinBox->setValue(m_startline + 1); } m_startAtLineSpinBox->setEnabled(true); m_startAtLineLabel->setText( m_allRowsLoadedInPreview ? xi18n("Start at line (1-%1):", count) : xi18n("Start at line:") //we do not know what's real count ); m_startAtLineLabel->setEnabled(true); } else { // no data m_startAtLineSpinBox->setMaximum(1); m_startAtLineSpinBox->setValue(1); m_startAtLineSpinBox->setEnabled(false); m_startAtLineLabel->setText(xi18n("Start at line:")); m_startAtLineLabel->setEnabled(false); } updateRowCountInfo(); m_blockUserEvents = false; repaint(); } QString KexiCSVImportDialog::detectDelimiterByLookingAtFirstBytesOfFile(QTextStream *inputStream) { // try to detect delimiter // \t has priority, then ; then , const qint64 origOffset = inputStream->pos(); QChar c, prevChar = 0; int detectedDelimiter = 0; bool insideQuote = false; //characters by priority const int CH_TAB_AFTER_QUOTE = 500; const int CH_SEMICOLON_AFTER_QUOTE = 499; const int CH_COMMA_AFTER_QUOTE = 498; const int CH_TAB = 200; // \t const int CH_SEMICOLON = 199; // ; const int CH_COMMA = 198; // , QList tabsPerLine, semicolonsPerLine, commasPerLine; int tabs = 0, semicolons = 0, commas = 0; int line = 0; bool wasChar13 = false; // true if previous x was '\r' for (int i = 0; !inputStream->atEnd() && i < MAX_CHARS_TO_SCAN_WHILE_DETECTING_DELIMITER; i++) { (*m_inputStream) >> c; // read one char if (prevChar == '"') { if (c != '"') //real quote (not double "") insideQuote = !insideQuote; } if (insideQuote) { prevChar = c; continue; } if (c == ' ') continue; if (wasChar13 && c == '\n') { wasChar13 = false; continue; // previous x was '\r', eat '\n' } wasChar13 = c == '\r'; if (c == '\n' || c == '\r') {//end of line //remember # of tabs/semicolons/commas in this line tabsPerLine += tabs; tabs = 0; semicolonsPerLine += semicolons; semicolons = 0; commasPerLine += commas; commas = 0; line++; } else if (c == '\t') { tabs++; detectedDelimiter = qMax(prevChar == '"' ? CH_TAB_AFTER_QUOTE : CH_TAB, detectedDelimiter); } else if (c == ';') { semicolons++; detectedDelimiter = qMax(prevChar == '"' ? CH_SEMICOLON_AFTER_QUOTE : CH_SEMICOLON, detectedDelimiter); } else if (c == ',') { commas++; detectedDelimiter = qMax(prevChar == '"' ? CH_COMMA_AFTER_QUOTE : CH_COMMA, detectedDelimiter); } prevChar = c; } inputStream->seek(origOffset); //restore orig. offset //now, try to find a delimiter character that exists the same number of times in all the checked lines //this detection method has priority over others QList::ConstIterator it; if (tabsPerLine.count() > 1) { tabs = tabsPerLine.isEmpty() ? 0 : tabsPerLine.first(); for (it = tabsPerLine.constBegin(); it != tabsPerLine.constEnd(); ++it) { if (tabs != *it) break; } if (tabs > 0 && it == tabsPerLine.constEnd()) return "\t"; } if (semicolonsPerLine.count() > 1) { semicolons = semicolonsPerLine.isEmpty() ? 0 : semicolonsPerLine.first(); for (it = semicolonsPerLine.constBegin(); it != semicolonsPerLine.constEnd(); ++it) { if (semicolons != *it) break; } if (semicolons > 0 && it == semicolonsPerLine.constEnd()) return ";"; } if (commasPerLine.count() > 1) { commas = commasPerLine.first(); for (it = commasPerLine.constBegin(); it != commasPerLine.constEnd(); ++it) { if (commas != *it) break; } if (commas > 0 && it == commasPerLine.constEnd()) return ","; } //now return the winning character by looking at CH_* symbol if (detectedDelimiter == CH_TAB_AFTER_QUOTE || detectedDelimiter == CH_TAB) return "\t"; if (detectedDelimiter == CH_SEMICOLON_AFTER_QUOTE || detectedDelimiter == CH_SEMICOLON) return ";"; if (detectedDelimiter == CH_COMMA_AFTER_QUOTE || detectedDelimiter == CH_COMMA) return ","; return KEXICSV_DEFAULT_FILE_DELIMITER; //<-- default } tristate KexiCSVImportDialog::loadRows(QString &field, int &row, int &column, int &maxColumn, bool inGUI) { enum { S_START, S_QUOTED_FIELD, S_MAYBE_END_OF_QUOTED_FIELD, S_END_OF_QUOTED_FIELD, S_MAYBE_NORMAL_FIELD, S_NORMAL_FIELD, S_COMMENT } state = S_START; field.clear(); const bool ignoreDups = m_ignoreDuplicates->isChecked(); bool lastCharDelimiter = false; bool nextRow = false; row = column = 1; m_prevColumnForSetText = 0; maxColumn = 0; QChar x; const bool hadInputStream = m_inputStream != 0; delete m_inputStream; if (m_mode == Clipboard) { m_inputStream = new QTextStream(&m_clipboardData, QIODevice::ReadOnly); if (!hadInputStream) m_delimiterWidget->setDelimiter(KEXICSV_DEFAULT_CLIPBOARD_DELIMITER); } else { m_file->seek(0); //always seek at 0 because loadRows() is called many times m_inputStream = new QTextStream(m_file); QTextCodec *codec = KCharsets::charsets()->codecForName(m_options.encoding); if (codec) { m_inputStream->setCodec(codec); //QTextCodec::codecForName("CP1250")); } if (m_detectDelimiter) { const QString delimiter(detectDelimiterByLookingAtFirstBytesOfFile(m_inputStream)); if (m_delimiterWidget->delimiter() != delimiter) m_delimiterWidget->setDelimiter(delimiter); } } const QChar delimiter(m_delimiterWidget->delimiter()[0]); const QChar commentSymbol(m_commentWidget->commentSymbol()[0]); m_stoppedAt_MAX_BYTES_TO_PREVIEW = false; if (m_importingProgressBar) { m_elapsedTimer.start(); m_elapsedMs = m_elapsedTimer.elapsed(); } int offset = 0; bool wasChar13 = false; // true if previous x was '\r' for (;; ++offset) { if (m_importingProgressBar && (offset % 0x100) == 0 && (m_elapsedMs + PROGRESS_STEP_MS) < m_elapsedTimer.elapsed()) { //update progr. bar dlg on final exporting m_elapsedMs = m_elapsedTimer.elapsed(); m_importingProgressBar->setValue(offset); qApp->processEvents(); if (m_importCanceled) { return ::cancelled; } } if (m_inputStream->atEnd()) { if (x != '\n' && x != '\r') { x = '\n'; // simulate missing \n at end wasChar13 = false; } else { break; // finish! } } else { (*m_inputStream) >> x; // read one char } if (wasChar13 && x == '\n') { wasChar13 = false; continue; // previous x was '\r', eat '\n' } wasChar13 = x == '\r'; if (offset == 0 && x.unicode() == 0xfeff) { // Ignore BOM, the "Byte Order Mark" // (http://en.wikipedia.org/wiki/Byte_Order_Mark, // http://www.unicode.org/charts/PDF/UFFF0.pdf) // Probably fixed in Qt4. continue; } switch (state) { case S_START : if (x == m_textquote) { state = S_QUOTED_FIELD; } else if (x == delimiter) { field.clear(); if ((ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } else if (x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { if (!inGUI) { //fill remaining empty fields (database wants them explicitly) for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) { setText(row - m_startline, additionalColumn, QString(), inGUI); } } nextRow = true; if (ignoreDups && lastCharDelimiter) { // we're ignoring repeated delimiters so remove any extra trailing delimiters --column; } maxColumn = qMax(maxColumn, column); column = 1; m_prevColumnForSetText = 0; if (x == commentSymbol && m_parseComments) { state = S_COMMENT; maxColumn -= 1; break; } } else { field += x; state = S_MAYBE_NORMAL_FIELD; } break; case S_QUOTED_FIELD : if (x == m_textquote) { state = S_MAYBE_END_OF_QUOTED_FIELD; } /*allow \n inside quoted fields else if (x == '\n') { setText(row - m_startline, column, field, inGUI); field = ""; if (x == '\n') { nextRow = true; maxColumn = qMax( maxColumn, column ); column = 1; } else { if ((ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; }*/ else { field += x; } break; case S_MAYBE_END_OF_QUOTED_FIELD : if (x == m_textquote) { field += x; //no, this was just escaped quote character state = S_QUOTED_FIELD; } else if (x == delimiter || x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { setText(row - m_startline, column, field, inGUI); field.clear(); if (x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { nextRow = true; maxColumn = qMax(maxColumn, column); column = 1; m_prevColumnForSetText = 0; if (x == commentSymbol && m_parseComments) { state = S_COMMENT; break; } } else { if ((ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } else { state = S_END_OF_QUOTED_FIELD; } break; case S_END_OF_QUOTED_FIELD : if (x == delimiter || x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { setText(row - m_startline, column, field, inGUI); field.clear(); if (x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { nextRow = true; maxColumn = qMax(maxColumn, column); column = 1; m_prevColumnForSetText = 0; if (x == commentSymbol && m_parseComments) { state = S_COMMENT; break; } } else { if ((ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } else { state = S_END_OF_QUOTED_FIELD; } break; case S_COMMENT : if (x == '\n' || x == '\r') { state = S_START; } if (lastCharDelimiter) { lastCharDelimiter = false; } break; case S_MAYBE_NORMAL_FIELD : if (x == m_textquote) { field.clear(); state = S_QUOTED_FIELD; } break; case S_NORMAL_FIELD : if (x == delimiter || x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { setText(row - m_startline, column, field, inGUI); field.clear(); if (x == '\n' || x == '\r' || (x == commentSymbol && m_parseComments)) { nextRow = true; maxColumn = qMax(maxColumn, column); column = 1; m_prevColumnForSetText = 0; if (x == commentSymbol && m_parseComments) { state = S_COMMENT; break; } } else { if ((ignoreDups == false) || (lastCharDelimiter == false)) ++column; lastCharDelimiter = true; } state = S_START; } else { field += x; } } // switch if (x != delimiter) lastCharDelimiter = false; if (nextRow) { if (!inGUI && !shouldSaveRow(row - m_startline, m_1stRowForFieldNames->isChecked())) { // do not save to the database 1st row if it contains column names m_valuesToInsert.clear(); } else if (!saveRow(inGUI)) return false; ++row; } if (m_firstFillTableCall && row == 2 && !m_1stRowForFieldNames->isChecked() && m_table->firstRowForFieldNames()) { m_table->clear(); m_firstFillTableCall = false; //this trick is allowed only once, on startup m_1stRowForFieldNames->setChecked(true); //this will reload table m_blockUserEvents = false; repaint(); return false; } if (!m_importingProgressBar && row % 20 == 0) { qApp->processEvents(); //only for GUI mode: if (!m_firstFillTableCall && m_loadingProgressDlg && m_loadingProgressDlg->wasCanceled()) { delete m_loadingProgressDlg; m_loadingProgressDlg = 0; m_dialogCanceled = true; reject(); return false; } } if (!m_firstFillTableCall && m_loadingProgressDlg) { m_loadingProgressDlg->setValue(qMin(m_maximumRowsForPreview, row)); } if (inGUI && row > (m_maximumRowsForPreview + (m_table->firstRowForFieldNames() ? 1 : 0))) { qDebug() << "loading stopped at row #" << m_maximumRowsForPreview; break; } if (nextRow) { nextRow = false; //additional speedup: stop processing now if too many bytes were loaded for preview qDebug() << offset; if (inGUI && offset >= m_maximumBytesForPreview && row >= 2) { m_stoppedAt_MAX_BYTES_TO_PREVIEW = true; return true; } } } return true; } void KexiCSVImportDialog::updateColumn(int col) { KDbField::Type detectedType = d->detectedType(col); if (detectedType == KDbField::InvalidType) { d->setDetectedType(col, KDbField::Text); //entirely empty column detectedType = KDbField::Text; } m_table->setHeaderData(col, Qt::Horizontal, QString(xi18n("Column %1", col + 1) + " \n(" + kexiCSVImportStatic->typeNames[detectedType].toLower() + ") ")); m_tableView->horizontalHeader()->adjustSize(); if (m_primaryKeyColumn == -1 && isPrimaryKeyAllowed(col)) { m_primaryKeyColumn = col; } } bool KexiCSVImportDialog::isPrimaryKeyAllowed(int col) { QList *list = d->uniquenessTest(col); if (m_primaryKeyColumn != -1 || !list || list->isEmpty()) { return false; } bool result = false; int expectedRowCount = m_table->rowCount(); if (m_table->firstRowForFieldNames()) { expectedRowCount--; } if (list->count() == expectedRowCount) { qSort(*list); QList::ConstIterator it = list->constBegin(); int prevValue = *it; ++it; for (; it != list->constEnd() && prevValue != (*it); ++it) { prevValue = (*it); } result = it == list->constEnd(); // no duplicates } list->clear(); // not needed now: conserve memory return result; } void KexiCSVImportDialog::detectTypeAndUniqueness(int row, int col, const QString& text) { int intValue; KDbField::Type type = d->detectedType(col); if (row == 1 || type != KDbField::Text) { bool found = false; if (text.isEmpty() && type == KDbField::InvalidType) found = true; //real type should be found later //detect type because it's 1st row or all prev. rows were not text //-FP number? (trying before "number" type is a must) if (!found && (row == 1 || type == KDbField::Integer || type == KDbField::Double || type == KDbField::InvalidType)) { bool ok = text.isEmpty() || m_fpNumberRegExp1.match(text).hasMatch() || m_fpNumberRegExp2.match(text).hasMatch(); if (ok && (row == 1 || type == KDbField::InvalidType)) { d->setDetectedType(col, KDbField::Double); found = true; //yes } } //-number? if (!found && (row == 1 || type == KDbField::Integer || type == KDbField::InvalidType)) { bool ok = text.isEmpty();//empty values allowed if (!ok) intValue = text.toInt(&ok); if (ok && (row == 1 || type == KDbField::InvalidType)) { d->setDetectedType(col, KDbField::Integer); found = true; //yes } } //-date? if (!found && (row == 1 || type == KDbField::Date || type == KDbField::InvalidType)) { if ((row == 1 || type == KDbField::InvalidType) && (text.isEmpty() || m_dateRegExp.match(text).hasMatch())) { d->setDetectedType(col, KDbField::Date); found = true; //yes } } //-time? if (!found && (row == 1 || type == KDbField::Time || type == KDbField::InvalidType)) { if ((row == 1 || type == KDbField::InvalidType) && (text.isEmpty() || m_timeRegExp1.match(text).hasMatch() || m_timeRegExp2.match(text).hasMatch())) { d->setDetectedType(col, KDbField::Time); found = true; //yes } } //-date/time? if (!found && (row == 1 || type == KDbField::Time || type == KDbField::InvalidType)) { if (row == 1 || type == KDbField::InvalidType) { bool detected = text.isEmpty(); if (!detected) { const QStringList dateTimeList(text.split(' ')); bool ok = dateTimeList.count() >= 2; //! @todo also support ISODateTime's "T" separator? //! @todo also support timezones? if (ok) { //try all combinations QString datePart(dateTimeList[0].trimmed()); QString timePart(dateTimeList[1].trimmed()); ok = m_dateRegExp.match(datePart).hasMatch() && (m_timeRegExp1.match(timePart).hasMatch() || m_timeRegExp2.match(timePart).hasMatch()); } detected = ok; } if (detected) { d->setDetectedType(col, KDbField::DateTime); found = true; //yes } } } if (!found && type == KDbField::InvalidType && !text.isEmpty()) { //eventually, a non-emptytext after a while d->setDetectedType(col, KDbField::Text); found = true; //yes } //default: text type (already set) } type = d->detectedType(col); //qDebug() << type; if (type == KDbField::Integer) { // check uniqueness for this value QList *list = d->uniquenessTest(col); if (text.isEmpty()) { if (list) { list->clear(); // empty value cannot be in PK } } else { if (!list) { list = new QList(); d->setUniquenessTest(col, list); } list->append(intValue); } } } QDate KexiCSVImportDialog::buildDate(int y, int m, int d) const { if (y < 100) { if ((1900 + y) >= m_minimumYearFor100YearSlidingWindow) return QDate(1900 + y, m, d); else return QDate(2000 + y, m, d); } return QDate(y, m, d); } bool KexiCSVImportDialog::parseDate(const QString& text, QDate& date) { QRegularExpressionMatch match = m_dateRegExp.match(text); if (!match.hasMatch()) return false; //dddd - dd - dddd //1 2 3 4 5 <- pos const int d1 = match.captured(1).toInt(), d3 = match.captured(3).toInt(), d5 = match.captured(5).toInt(); switch (m_options.dateFormat) { case KexiCSVImportOptions::DMY: date = buildDate(d5, d3, d1); break; case KexiCSVImportOptions::YMD: date = buildDate(d1, d3, d5); break; case KexiCSVImportOptions::MDY: date = buildDate(d5, d1, d3); break; case KexiCSVImportOptions::AutoDateFormat: if (match.captured(2) == "/") { //probably separator for american format mm/dd/yyyy date = buildDate(d5, d1, d3); } else { if (d5 > 31) //d5 == year date = buildDate(d5, d3, d1); else //d1 == year date = buildDate(d1, d3, d5); } break; default:; } return date.isValid(); } bool KexiCSVImportDialog::parseTime(const QString& text, QTime& time) { time = QTime::fromString(text, Qt::ISODate); //same as m_timeRegExp1 if (time.isValid()) return true; QRegularExpressionMatch match = m_timeRegExp2.match(text); if (match.hasMatch()) { //hh:mm:ss time = QTime(match.captured(1).toInt(), match.captured(3).toInt(), match.captured(5).toInt()); return true; } return false; } void KexiCSVImportDialog::setText(int row, int col, const QString& text, bool inGUI) { if (!inGUI) { if (!shouldSaveRow(row, m_1stRowForFieldNames->isChecked())) return; // do not care about this value if it contains column names (these were already used) //save text directly to database buffer if (m_prevColumnForSetText == 0) { //1st call m_valuesToInsert.clear(); if (m_implicitPrimaryKeyAdded) { m_valuesToInsert << QVariant(); //id will be autogenerated here } } if ((m_prevColumnForSetText + 1) < col) { //skipped one or more columns //before this: save NULLs first for (int i = m_prevColumnForSetText + 1; i < col; i++) { if (m_options.nullsImportedAsEmptyTextChecked && KDbField::isTextType(d->detectedType(i-1))) { m_valuesToInsert << QString(""); } else { m_valuesToInsert << QVariant(); } } } m_prevColumnForSetText = col; const KDbField::Type detectedType = d->detectedType(col-1); if (detectedType == KDbField::Integer) { m_valuesToInsert << (text.isEmpty() ? QVariant() : text.toInt()); //! @todo what about time and float/double types and different integer subtypes? } else if (detectedType == KDbField::Double) { //replace ',' with '.' QByteArray t(text.toLatin1()); const int textLen = t.length(); for (int i = 0; i < textLen; i++) { if (t[i] == ',') { t[i] = '.'; break; } } m_valuesToInsert << (t.isEmpty() ? QVariant() : t.toDouble()); } else if (detectedType == KDbField::Boolean) { const QString t(text.trimmed().toLower()); if (t.isEmpty()) m_valuesToInsert << QVariant(); else if (t == "0" || t == m_stringNo || t == m_stringI18nNo || t == m_stringFalse || t == m_stringI18nFalse) m_valuesToInsert << QVariant(false); else m_valuesToInsert << QVariant(true); //anything nonempty } else if (detectedType == KDbField::Date) { QDate date; if (parseDate(text, date)) m_valuesToInsert << date; else m_valuesToInsert << QVariant(); } else if (detectedType == KDbField::Time) { QTime time; if (parseTime(text, time)) m_valuesToInsert << time; else m_valuesToInsert << QVariant(); } else if (detectedType == KDbField::DateTime) { QStringList dateTimeList(text.split(' ')); if (dateTimeList.count() < 2) dateTimeList = text.split('T'); //also support ISODateTime's "T" separator //! @todo also support timezones? if (dateTimeList.count() >= 2) { //try all combinations QString datePart(dateTimeList[0].trimmed()); QDate date; if (parseDate(datePart, date)) { QString timePart(dateTimeList[1].trimmed()); QTime time; if (parseTime(timePart, time)) m_valuesToInsert << QDateTime(date, time); else m_valuesToInsert << QVariant(); } else m_valuesToInsert << QVariant(); } else m_valuesToInsert << QVariant(); } else { // Text type and the rest if (m_options.nullsImportedAsEmptyTextChecked && text.isNull()) { //default value is empty string not null - otherwise querying data without knowing SQL is very confusing m_valuesToInsert << QString(""); } else { m_valuesToInsert <columnCount() < col) { m_table->setColumnCount(col); } if (!m_1stRowForFieldNames->isChecked()) { if ((row + m_startline) == 1) {//this row is for column name if (m_table->firstRowForFieldNames() && !m_1stRowForFieldNames->isChecked()) { QString f(text.simplified()); if (f.isEmpty() || !f[0].isLetter()) { m_table->setFirstRowForFieldNames(false); } } } row++; //1st row was for column names } else { if ((row + m_startline) == 1) {//this is for column name m_table->setRowCount(1); QString colName(text.simplified()); if (!colName.isEmpty()) { if (colName.at(0) >= QLatin1Char('0') && colName.at(0) <= QLatin1Char('9')) { colName.prepend(xi18n("Column") + " "); } m_table->setData(m_table->index(0, col - 1), colName); } return; } } if (row < 2) // skipped by the user return; if (m_table->rowCount() < row) { m_table->setRowCount(row + 100); /* We add more rows at a time to limit recalculations */ m_adjustRows = true; } m_table->setData(m_table->index(row-1 ,col-1),m_options.trimmedInTextValuesChecked ? text.trimmed() : text); detectTypeAndUniqueness(row - 1, col - 1, text); } bool KexiCSVImportDialog::saveRow(bool inGUI) { if (inGUI) { //nothing to do return true; } bool res = m_importingStatement.execute(m_valuesToInsert); //! @todo move if (!res) { const QStringList msgList = KexiUtils::convertTypesUsingMethod(m_valuesToInsert); const KMessageBox::ButtonCode msgRes = KMessageBox::warningContinueCancelList(this, xi18nc("@info", "An error occurred during insert record."), QStringList(msgList.join(";")), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "SkipImportErrors" ); res = msgRes == KMessageBox::Continue; } m_valuesToInsert.clear(); return res; } void KexiCSVImportDialog::adjustRows(int iRows) { if (m_adjustRows) { m_table->setRowCount(iRows); m_adjustRows = false; for (int i = 0; i < iRows; i++) m_tableView->resizeRowToContents(i); } } void KexiCSVImportDialog::formatChanged(int index) { if (index < 0 || index >= kexiCSVImportStatic->types.size()) return; KDbField::Type type = kexiCSVImportStatic->types[index]; d->setDetectedType(m_tableView->currentIndex().column(), type); m_primaryKeyField->setEnabled(KDbField::Integer == type); m_primaryKeyField->setChecked(m_primaryKeyColumn == m_tableView->currentIndex().column() && m_primaryKeyField->isEnabled()); updateColumn(m_tableView->currentIndex().column()); } void KexiCSVImportDialog::delimiterChanged(const QString& delimiter) { Q_UNUSED(delimiter); m_columnsAdjusted = false; m_detectDelimiter = false; //selected by hand: do not detect in the future //delayed, otherwise combobox won't be repainted fillTableLater(); } void KexiCSVImportDialog::commentSymbolChanged(const QString& commentSymbol) { QString noneString = QString(xi18n("None")); if (commentSymbol.compare(noneString) == 0) { m_parseComments = false; } else { m_parseComments = true; } m_columnsAdjusted = false; m_detectDelimiter = false; //selected by hand: do not detect in the future //delayed, otherwise combobox won't be repainted fillTableLater(); } void KexiCSVImportDialog::textquoteSelected(int) { const QString tq(m_comboQuote->textQuote()); if (tq.isEmpty()) m_textquote = 0; else m_textquote = tq[0]; qDebug() << m_textquote; //delayed, otherwise combobox won't be repainted fillTableLater(); } void KexiCSVImportDialog::fillTableLater() { m_table->setColumnCount(0); QTimer::singleShot(10, this, SLOT(fillTable())); } void KexiCSVImportDialog::startlineSelected(int startline) { if (m_startline == (startline - 1)) return; m_startline = startline - 1; m_adjustRows = true; m_columnsAdjusted = false; fillTable(); m_tableView->setFocus(); } void KexiCSVImportDialog::currentCellChanged(const QModelIndex &cur, const QModelIndex &prev) { if (prev.column() == cur.column() || !cur.isValid()) return; const KDbField::Type type = d->detectedType(cur.column()); m_formatCombo->setCurrentIndex(kexiCSVImportStatic->indicesForTypes.value(type, -1)); m_formatLabel->setText(xi18n("Format for column %1:", cur.column() + 1)); m_primaryKeyField->setEnabled(KDbField::Integer == type); m_primaryKeyField->blockSignals(true); //block to disable executing slotPrimaryKeyFieldToggled() m_primaryKeyField->setChecked(m_primaryKeyColumn == cur.column()); m_primaryKeyField->blockSignals(false); } //! Used in emergency by accept() void KexiCSVImportDialog::dropDestinationTable(KexiProject* project, KexiPart::Item* &partItemForSavedTable) { m_importingProgressBar->hide(); project->deleteUnstoredItem(partItemForSavedTable); partItemForSavedTable = 0; m_conn->dropTable(m_destinationTableSchema); /*alsoRemoveSchema*/ m_destinationTableSchema = 0; m_conn = 0; } //! Used in emergency by accept() void KexiCSVImportDialog::raiseErrorInAccept(KexiProject* project, KexiPart::Item* &partItemForSavedTable) { finishButton()->setEnabled(true); KGuiItem::assign(finishButton(), KGuiItem(xi18nc("@action:button Import CSV", "&Import..."), _IMPORT_ICON)); project->deleteUnstoredItem(partItemForSavedTable); partItemForSavedTable = 0; delete m_destinationTableSchema; m_destinationTableSchema = 0; m_conn = 0; backButton()->setEnabled(true); m_importInProgress = false; m_importingProgressBar->hide(); } void KexiCSVImportDialog::accept() { if (d->imported) { parentWidget()->raise(); bool openingCanceled; KexiWindow *win = KexiMainWindowIface::global()->openedWindowFor(m_partItemForSavedTable); if (win) { KexiMainWindowIface::global()->closeObject(m_partItemForSavedTable); } KexiMainWindowIface::global()->openObject(m_partItemForSavedTable, Kexi::DataViewMode, &openingCanceled); KAssistantDialog::accept(); } else { import(); } } void KexiCSVImportDialog::import() { //! @todo MOVE MOST OF THIS TO CORE/ (KexiProject?) after KexiWindow code is moved to non-gui place KMessageBox::enableMessage("SkipImportErrors"); KexiGUIMessageHandler msg; //! @todo make it better integrated with main window KexiProject *project = KexiMainWindowIface::global()->project(); if (!project) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No project available.")); return; } m_conn = project->dbConnection(); if (!m_conn) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("No database connection available.")); return; } if (m_newTableOption->isChecked()) { m_destinationTableSchema = new KDbTableSchema(m_partItemForSavedTable->name()); m_destinationTableSchema->setCaption(m_partItemForSavedTable->caption()); m_destinationTableSchema->setDescription(m_partItemForSavedTable->description()); const int numCols(m_table->columnCount()); m_implicitPrimaryKeyAdded = false; //add PK if user wanted it int msgboxResult; if ( m_primaryKeyColumn == -1 && KMessageBox::No != (msgboxResult = KMessageBox::questionYesNoCancel(this, xi18nc("@info", "No primary key (autonumber) has been defined." "Should it be automatically defined on import (recommended)?" "An imported table without a primary key may not be " "editable (depending on database type)."), QString(), KGuiItem(xi18nc("@action:button Add Database Primary Key to a Table", "&Add Primary Key"), KexiIconName("database-key")), KGuiItem(xi18nc("@action:button Do Not Add Database Primary Key to a Table", "Do &Not Add"), KStandardGuiItem::no().icon())))) { if (msgboxResult == KMessageBox::Cancel) { raiseErrorInAccept(project, m_partItemForSavedTable); return; //cancel accepting } //add implicit PK field //! @todo make this field hidden (what about e.g. pgsql?) m_implicitPrimaryKeyAdded = true; QString fieldName("id"); QString fieldCaption("Id"); QSet colnames; for (int col = 0; col < numCols; col++) colnames.insert(m_table->data(m_table->index(0, col)).toString().toLower().simplified()); if (colnames.contains(fieldName)) { int num = 1; while (colnames.contains(fieldName + QString::number(num))) num++; fieldName += QString::number(num); fieldCaption += QString::number(num); } KDbField *field = new KDbField( fieldName, KDbField::Integer, KDbField::NoConstraints, KDbField::NoOptions, 0, 0, //int length=0, int precision=0, QVariant(), //QVariant defaultValue=QVariant(), fieldCaption ); //no description and width for now field->setPrimaryKey(true); field->setAutoIncrement(true); if (!m_destinationTableSchema->addField(field)) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("Cannot add column.")); delete field; delete m_destinationTableSchema; m_destinationTableSchema = 0; return; } } for (int col = 0; col < numCols; col++) { QString fieldCaption(m_table->data(m_table->index(0, col)).toString().simplified()); QString fieldName; if (fieldCaption.isEmpty()) { int i = 0; do { fieldCaption = xi18nc("@title:column Column 1, Column 2, etc.", "Column %1", i + 1); fieldName = KDb::stringToIdentifier(fieldCaption); if (!m_destinationTableSchema->field(fieldName)) { break; } i++; } while (true); } else { fieldName = KDb::stringToIdentifier(fieldCaption); if (m_destinationTableSchema->field(fieldName)) { QString fixedFieldName; int i = 2; //"apple 2, apple 3, etc. if there're many "apple" names do { fixedFieldName = fieldName + "_" + QString::number(i); if (!m_destinationTableSchema->field(fixedFieldName)) break; i++; } while (true); fieldName = fixedFieldName; fieldCaption += (" " + QString::number(i)); } } KDbField::Type detectedType = d->detectedType(col); //! @todo what about time and float/double types and different integer subtypes? //! @todo what about long text? if (detectedType == KDbField::InvalidType) { detectedType = KDbField::Text; } KDbField *field = new KDbField( fieldName, detectedType, KDbField::NoConstraints, KDbField::NoOptions, 0, 0, //int length=0, int precision=0, QVariant(), //QVariant defaultValue=QVariant(), fieldCaption ); //no description and width for now if ((int)col == m_primaryKeyColumn) { field->setPrimaryKey(true); field->setAutoIncrement(true); } if (!m_destinationTableSchema->addField(field)) { msg.showErrorMessage(KDbMessageHandler::Error, xi18n("Cannot add column.")); delete field; delete m_destinationTableSchema; m_destinationTableSchema = 0; return; } } } else { m_implicitPrimaryKeyAdded = false; m_destinationTableSchema = m_conn->tableSchema(m_partItemForSavedTable->name()); int firstColumn = 0; if (m_destinationTableSchema->field(0)->isPrimaryKey() && m_primaryKeyColumn == -1) { m_implicitPrimaryKeyAdded = true; firstColumn = 1; } if (m_destinationTableSchema->fields()->size() - firstColumn < m_table->columnCount()) { KMessageBox::error(this, xi18n("Field count does not match." "Please choose another table.")); return; } } m_importInProgress = true; backButton()->setEnabled(false); finishButton()->setEnabled(false); KexiPart::Part *part = Kexi::partManager().partForPluginId("org.kexi-project.table"); if (!part) { msg.showErrorMessage(Kexi::partManager().result()); return; } KDbTransaction transaction = m_conn->beginTransaction(); if (transaction.isNull()) { msg.showErrorMessage(m_conn->result()); raiseErrorInAccept(project, m_partItemForSavedTable); return; } KDbTransactionGuard tg(transaction); //-create physical table if (m_newTableOption->isChecked() && !m_conn->createTable(m_destinationTableSchema, KDbConnection::CreateTableOptions(KDbConnection::CreateTableOption::Default) & ~KDbConnection::CreateTableOptions(KDbConnection::CreateTableOption::DropDestination))) { msg.showErrorMessage(m_conn->result()); raiseErrorInAccept(project, m_partItemForSavedTable); return; } m_importingStatement = m_conn->prepareStatement( KDbPreparedStatement::InsertStatement, m_destinationTableSchema); if (!m_importingStatement.isValid()) { msg.showErrorMessage(m_conn->result()); raiseErrorInAccept(project, m_partItemForSavedTable); return; } if (m_file) { m_importProgressLabel->setText(xi18n("Importing data...")); m_importingProgressBar->setMaximum(QFileInfo(*m_file).size() - 1); m_importingProgressBar->show(); m_importProgressLabel->show(); } int row, column, maxColumn; QString field; // main job tristate res = loadRows(field, row, column, maxColumn, false /*!gui*/); if (true != res) { //importing canceled or failed if (!res) { //do not display err msg when res == cancelled m_importProgressLabel->setText(xi18n("Import has been canceled.")); } else if (~res) { m_importProgressLabel->setText(xi18n("Error occurred during import.")); } raiseErrorInAccept(project, m_partItemForSavedTable); return; } // file with only one line without '\n' if (field.length() > 0) { setText(row - m_startline, column, field, false /*!gui*/); //fill remaining empty fields (database wants them explicitly) for (int additionalColumn = column; additionalColumn <= maxColumn; additionalColumn++) { setText(row - m_startline, additionalColumn, QString(), false /*!gui*/); } if (!saveRow(false /*!gui*/)) { msg.showErrorMessage(m_conn->result()); raiseErrorInAccept(project, m_partItemForSavedTable); return; } ++row; field.clear(); } if (!tg.commit()) { msg.showErrorMessage(m_conn->result()); raiseErrorInAccept(project, m_partItemForSavedTable); return; } //-now we can store the item if (m_newTableOption->isChecked()) { m_partItemForSavedTable->setIdentifier(m_destinationTableSchema->id()); project->addStoredItem(part->info(), m_partItemForSavedTable); } m_importingProgressBar->hide(); m_importProgressLabel->setText(xi18nc("@info", "Data has been successfully imported to table %1.", m_destinationTableSchema->name())); m_importInProgress = false; //qDebug()<<"IMPORT DONE"; KGuiItem::assign(finishButton(), KStandardGuiItem::open()); finishButton()->setEnabled(true); KGuiItem::assign(button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); nextButton()->setEnabled(false); backButton()->setEnabled(false); m_conn = 0; d->imported = true; } void KexiCSVImportDialog::reject() { //qDebug()<<"IMP_P"<horizontalHeaderItem(col)->text(); if (header == xi18nc("Text type for column", "Text")) return TEXT; else if (header == xi18nc("Numeric type for column", "Number")) return NUMBER; else if (header == xi18nc("Currency type for column", "Currency")) return CURRENCY; else return DATE; } QString KexiCSVImportDialog::getText(int row, int col) { return m_table->item(row, col)->text(); } void KexiCSVImportDialog::ignoreDuplicatesChanged(int) { fillTable(); } void KexiCSVImportDialog::slot1stRowForFieldNamesChanged(int state) { m_adjustRows = true; if (m_1stRowForFieldNames->isChecked() && m_startline > 0 && m_startline >= (m_startAtLineSpinBox->maximum() - 1)) { m_startline--; } m_columnsAdjusted = false; fillTable(); m_table->setFirstRowForFieldNames(state); } void KexiCSVImportDialog::optionsButtonClicked() { KexiCSVImportOptionsDialog dlg(m_options, this); if (QDialog::Accepted != dlg.exec()) return; KexiCSVImportOptions newOptions(dlg.options()); if (m_options != newOptions) { m_options = newOptions; if (!openData()) return; fillTable(); } } bool KexiCSVImportDialog::eventFilter(QObject * watched, QEvent * e) { QEvent::Type t = e->type(); // temporary disable keyboard and mouse events for time-consuming tasks if (m_blockUserEvents && (t == QEvent::KeyPress || t == QEvent::KeyRelease || t == QEvent::MouseButtonPress || t == QEvent::MouseButtonDblClick || t == QEvent::Paint)) return true; if (watched == m_startAtLineSpinBox && t == QEvent::KeyPress) { QKeyEvent *ke = static_cast(e); if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) { m_tableView->setFocus(); return true; } } return QDialog::eventFilter(watched, e); } void KexiCSVImportDialog::slotPrimaryKeyFieldToggled(bool on) { setPrimaryKeyIcon(m_primaryKeyColumn, false); m_primaryKeyColumn = on ? m_tableView->currentIndex().column() : -1; setPrimaryKeyIcon(m_primaryKeyColumn, true); } void KexiCSVImportDialog::setPrimaryKeyIcon(int column, bool set) { if (column >= 0 && column < m_table->columnCount()) { m_table->setData(m_table->index(0, column), set ? m_pkIcon : QPixmap(), Qt::DecorationRole); } } void KexiCSVImportDialog::updateRowCountInfo() { m_infoLbl->setFileName(m_fname); if (m_allRowsLoadedInPreview) { m_infoLbl->setCommentText( xi18nc("row count", "(rows: %1)", m_table->rowCount() - 1 + m_startline)); m_infoLbl->commentLabel()->setToolTip(QString()); } else { m_infoLbl->setCommentText( xi18nc("row count", "(rows: more than %1)", m_table->rowCount() - 1 + m_startline)); m_infoLbl->commentLabel()->setToolTip(xi18n("Not all rows are visible on this preview")); } } QPushButton* KexiCSVImportDialog::configureButton() const { // Help button is used as Configure return button(QDialogButtonBox::Help); } #include "kexicsvimportdialog.moc" diff --git a/src/plugins/importexport/csv/kexicsvimportdialog.h b/src/plugins/importexport/csv/kexicsvimportdialog.h index 4ea00084e..b24be0b79 100644 --- a/src/plugins/importexport/csv/kexicsvimportdialog.h +++ b/src/plugins/importexport/csv/kexicsvimportdialog.h @@ -1,325 +1,315 @@ /* This file is part of the KDE project - Copyright (C) 2005-2016 Jarosław Staniek + Copyright (C) 2005-2017 Jarosław Staniek Copyright (C) 2012 Oleg Kukharchuk This work is based on kspread/dialogs/kspread_dlg_csv.cc. Copyright (C) 2002-2003 Norbert Andres Copyright (C) 2002-2003 Ariya Hidayat Copyright (C) 2002 Laurent Montel Copyright (C) 1999 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_CSVIMPORTDIALOG_H #define KEXI_CSVIMPORTDIALOG_H #include #include #include #include #include #include #include #include #include #include -#include #include "kexicsvimportoptionsdlg.h" class QHBoxLayout; class QGridLayout; class QCheckBox; class QLabel; class QTableView; class QTreeView; class QFile; class QStackedWidget; class QProgressDialog; class QProgressBar; class QRadioButton; class QSpinBox; class KComboBox; class KPageWidgetItem; class KDbConnection; class KDbTableSchema; class KexiCSVCommentWidget; class KexiCSVDelimiterWidget; class KexiCSVTextQuoteComboBox; class KexiCSVInfoLabel; class KexiProject; class KexiCSVImportDialogModel; class KexiCSVImportDialogItemDelegate; -#ifdef KEXI_USE_KFILEWIDGET -class KexiFileWidget; -#else -class KexiFileRequester; -#endif +class KexiFileWidgetInterface; class KexiNameWidget; class KexiProjectNavigator; class KexiFieldListModel; namespace KexiPart { class Item; } /** * @short Kexi CSV import dialog * * This is temporary solution for Kexi CSV import, * based on kspread/dialogs/kspread_dlg_csv.h, cc. * * Provides dialog for managing CSV (comma separated value) data. * * Currently KexiCSVImportDialog is used for converting text into columns, * inserting text file and pasting text from clipboard, where conversion * from CSV (comma separated value) data is is all required. * The different purposed mentioned above is determined * using mode, which can be Column, File, or Clipboard respectively. */ class KexiCSVImportDialog : public KAssistantDialog { Q_OBJECT public: enum Mode { Clipboard, File /*, Column*/ }; enum Header { TEXT, NUMBER, DATE, CURRENCY }; //! @todo what about making it kexidb-independent? explicit KexiCSVImportDialog(Mode mode, QWidget *parent = 0); virtual ~KexiCSVImportDialog(); bool canceled() const; protected: virtual bool eventFilter(QObject *watched, QEvent *e); bool openData(); virtual void accept(); virtual void reject(); private: //! Used in emergency by accept() //! @note @a partItemForSavedTable is IN-OUT void dropDestinationTable(KexiProject* project, KexiPart::Item* &partItemForSavedTable); //! Used in emergency by accept() //! @note @a partItemForSavedTable is IN-OUT void raiseErrorInAccept(KexiProject* project, KexiPart::Item* &partItemForSavedTable); QGridLayout* MyDialogLayout; QHBoxLayout* Layout1; KexiCSVImportDialogModel *m_table; KexiCSVImportDialogItemDelegate *m_tableItemDelegate; QTableView *m_tableView; KexiCSVDelimiterWidget* m_delimiterWidget; KexiCSVCommentWidget* m_commentWidget; bool m_detectDelimiter; //!< true if delimiter should be detected //!< (true by default, set to false if user sets delimiter) QLabel* m_formatLabel; KComboBox* m_formatCombo; QSpinBox *m_startAtLineSpinBox; KexiCSVTextQuoteComboBox* m_comboQuote; QLabel* m_startAtLineLabel; QLabel* TextLabel2; QCheckBox* m_ignoreDuplicates; QCheckBox* m_1stRowForFieldNames; QCheckBox* m_primaryKeyField; -#ifdef KEXI_USE_KFILEWIDGET - KexiFileWidget *m_openFileWidget; -#else - QWidget *m_openFileWidget; - KexiFileRequester *m_openFileRequester; -#endif + KexiFileWidgetInterface *m_fileIface; QWidget *m_optionsWidget; QWidget *m_saveMethodWidget; KPageWidgetItem *m_openFilePage; KPageWidgetItem *m_optionsPage; KPageWidgetItem *m_saveMethodPage; KPageWidgetItem *m_chooseTablePage; QRadioButton *m_newTableOption; QRadioButton *m_existingTableOption; QStackedWidget *m_tableNameWidget; KPageWidgetItem *m_tableNamePage; KexiNameWidget *m_newTableWidget; KexiProjectNavigator *m_tablesList; QTreeView *m_fieldsListView; QLabel *m_tableCaptionLabel; QLabel *m_tableNameLabel; QLabel *m_recordCountLabel; QLabel *m_colCountLabel; QWidget *m_importWidget; KPageWidgetItem *m_importPage; KexiCSVInfoLabel *m_fromLabel; KexiCSVInfoLabel *m_toLabel; QLabel *m_importProgressLabel; void detectTypeAndUniqueness(int row, int col, const QString& text); void setText(int row, int col, const QString& text, bool inGUI); /*! Parses date from \a text and stores into \a date. m_dateRegExp is used for clever detection; if '/' separated is found, it's assumed the format is american mm/dd/yyyy. This function supports omitted zeros, so 1/2/2006 is parsed properly too. \return true on success. */ bool parseDate(const QString& text, QDate& date); /*! Parses time from \a text and stores into \a date. m_timeRegExp1 and m_timeRegExp2 are used for clever detection; both hh:mm:ss and hh:mm are supported. This function supports omitted zeros, so 1:2:3 is parsed properly too. \return true on success. */ bool parseTime(const QString& text, QTime& time); /*! Called after the first fillTable() when number of rows is unknown. */ void adjustRows(int iRows); int getHeader(int col); QString getText(int row, int col); void updateColumn(int col); bool isPrimaryKeyAllowed(int col); void setPrimaryKeyIcon(int column, bool set); void updateRowCountInfo(); tristate loadRows(QString &field, int &row, int &columnm, int &maxColumn, bool inGUI); /*! Detects delimiter by looking at first 4K bytes of the data. Used by loadRows(). The used algorithm: 1. Look byte by byte and locate special characters that can be delimiters. Special fact is taken into account: if there are '"' quotes used for text values, delimiters that follow directly the closing quote has higher priority than the one that follows other character. We do not assume that every text value is quoted. Summing up, there is following hierarchy (from highest to lowest): quote+tab, quote+semicolon, quote+comma, tab, semicolon, comma. Space characters are skipped. Text inside quotes is skipped, as well as double (escaped) quotes. 2. While scanning the data, for every row following number of tabs, semicolons and commas (only these outside of the quotes) are computed. On every line the values are appended to a separate list (QList). 3. After scanning, all the values are checked on the QList of tabs. If the list has more one element (so there was more than one row) and all the values (numbers of tabs) are equal, it's very probable the tab is a delimiter. So, this character is returned as a delimiter. 3a. The same algorithm as in 3. is performed for semicolon character. 3b. The same algorithm as in 3. is performed for comma character. 4. If the step 3. did not return a delimiter, a character found in step 1. with the highest priority is retured as delimiter. */ QString detectDelimiterByLookingAtFirstBytesOfFile(QTextStream *inputStream); /*! Callback, called whenever row is loaded in loadRows(). When inGUI is true, nothing is performed, else database buffer is written back to the database. */ bool saveRow(bool inGUI); //! @return date built out of @a y, @a m, @a d parts, //! taking m_minimumYearFor100YearSlidingWindow into account QDate buildDate(int y, int m, int d) const; //! Updates size of m_columnNames and m_changedColumnNames if needed void updateColumnVectorSize(); QPushButton* configureButton() const; bool m_parseComments; bool m_canceled; bool m_adjustRows; int m_startline; QChar m_textquote; QChar m_commentSymbol; QString m_clipboardData; QByteArray m_fileArray; Mode m_mode; QRegularExpression m_dateRegExp, m_timeRegExp1, m_timeRegExp2, m_fpNumberRegExp1, m_fpNumberRegExp2; bool m_columnsAdjusted; //!< to call adjustColumn() only once bool m_1stRowForFieldNamesDetected; //!< used to force rerun fillTable() after 1st row bool m_firstFillTableCall; //!< used to know whether it's 1st fillTable() call bool m_blockUserEvents; int m_primaryKeyColumn; //!< index of column with PK assigned (-1 if none) int m_maximumRowsForPreview; int m_maximumBytesForPreview; /*! The minimum year for the "100 year sliding date window": range of years that defines where any year expressed as two digits falls. Example: for date window from 1930 to 2029, two-digit years between 0 and 29 fall in the 2000s, and two-digit years between 30 and 99 fall in the 1900s. The default is 1930. */ int m_minimumYearFor100YearSlidingWindow; QPixmap m_pkIcon; QString m_fname; QFile* m_file; QTextStream *m_inputStream; //!< used in loadData() KexiCSVImportOptions m_options; QProgressDialog *m_loadingProgressDlg; QProgressBar *m_importingProgressBar; bool m_dialogCanceled; KexiCSVInfoLabel *m_infoLbl; KDbConnection *m_conn; //!< (temp) database connection used for importing KexiFieldListModel *m_fieldsListModel; KDbTableSchema *m_destinationTableSchema; //!< (temp) dest. table schema used for importing KDbPreparedStatement m_importingStatement; QList m_dbRowBuffer; //!< (temp) used for importing bool m_implicitPrimaryKeyAdded; //!< (temp) used for importing bool m_allRowsLoadedInPreview; //!< we need to know whether all rows were loaded or it's just a partial data preview bool m_stoppedAt_MAX_BYTES_TO_PREVIEW; //!< used to compute m_allRowsLoadedInPreview const QString m_stringNo, m_stringI18nNo, m_stringFalse, m_stringI18nFalse; //!< used for importing boolean values int m_prevColumnForSetText; //!< used for non-gui tracking of skipped clolumns, //!< so can be saved to the database, //!< e.g. first three columns are saved for ,,,"abc" line in the CSV data QElapsedTimer m_elapsedTimer; //!< Used to update progress qint64 m_elapsedMs; void createImportMethodPage(); void createOptionsPage(); void createFileOpenPage(); void createTableNamePage(); void createImportPage(); KDbPreparedStatementParameters m_valuesToInsert; KexiPart::Item* m_partItemForSavedTable; bool m_importInProgress; bool m_importCanceled; class Private; Private * const d; public Q_SLOTS: virtual void next(); private Q_SLOTS: void fillTable(); void fillTableLater(); void initLater(); void formatChanged(int id); void delimiterChanged(const QString& delimiter); void commentSymbolChanged(const QString& commentSymbol); void startlineSelected(int line); void textquoteSelected(int); void currentCellChanged(const QModelIndex &cur, const QModelIndex &prev); void ignoreDuplicatesChanged(int); void slot1stRowForFieldNamesChanged(int state); void optionsButtonClicked(); void slotPrimaryKeyFieldToggled(bool on); void slotCurrentPageChanged(KPageWidgetItem *page, KPageWidgetItem *prev); void slotShowSchema(KexiPart::Item *item); void import(); }; #endif diff --git a/src/widget/CMakeLists.txt b/src/widget/CMakeLists.txt index 878a2382c..b2ba0f5ab 100644 --- a/src/widget/CMakeLists.txt +++ b/src/widget/CMakeLists.txt @@ -1,108 +1,109 @@ add_subdirectory( dataviewcommon ) add_subdirectory( relations ) add_subdirectory( undo ) add_subdirectory( utils ) if(SHOULD_BUILD_KEXI_DESKTOP_APP) add_subdirectory( tableview ) endif() add_definitions(-DKDE_DEFAULT_DEBUG_AREA=44023) include_directories(${CMAKE_SOURCE_DIR}/src/widget/tableview ${CMAKE_SOURCE_DIR}/src/core) ########### next target ############### set(kexiextendedwidgets_LIB_SRCS fields/KexiFieldComboBox.cpp fields/KexiFieldListModel.cpp fields/KexiFieldListModelItem.cpp fields/KexiFieldListView.cpp navigator/KexiProjectModel.cpp navigator/KexiProjectModelItem.cpp navigator/KexiProjectItemDelegate.cpp navigator/KexiProjectNavigator.cpp navigator/KexiProjectTreeView.cpp properties/KexiCustomPropertyFactory.cpp properties/KexiCustomPropertyFactory_p.cpp properties/KexiPropertyEditorView.cpp properties/KexiPropertyPaneViewBase.cpp kexiquerydesignersqleditor.cpp kexiqueryparameters.cpp kexisectionheader.cpp kexidbdrivercombobox.cpp kexieditor.cpp KexiDataSourceComboBox.cpp KexiObjectInfoLabel.cpp kexicharencodingcombobox.cpp KexiDBTitlePage.cpp KexiProjectSelectorWidget.cpp kexislider.cpp KexiServerDriverNotFoundMessage.cpp KexiNameWidget.cpp KexiNameDialog.cpp + KexiFileWidgetInterface.cpp KexiFileRequester.cpp KexiStartupFileHandler.cpp KexiListView.cpp ) if(SHOULD_BUILD_KEXI_DESKTOP_APP) list(APPEND kexiextendedwidgets_LIB_SRCS #navigator/KexiProjectListView.cpp #navigator/KexiProjectListViewItem.cpp kexidbconnectionwidget.cpp # TODO replace use of KexiProjectListView and KexiProjectListViewList (with KexiProjectNavigator) # in kexiactionselectiondialog and remove them kexiprjtypeselector.cpp KexiConnectionSelectorWidget.cpp KexiFileDialog.cpp KexiPasswordWidget.cpp KexiDBPasswordDialog.cpp ) if(KEXI_USE_KFILEWIDGET) list(APPEND kexiextendedwidgets_LIB_SRCS KexiFileWidget.cpp) endif() ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiConnectionSelector.ui kexidbconnectionwidget.ui kexidbconnectionwidgetdetails.ui kexiprjtypeselector.ui KexiPasswordWidget.ui ) endif () ki18n_wrap_ui(kexiextendedwidgets_LIB_SRCS KexiDBTitlePage.ui KexiProjectSelector.ui ) kexi_add_library(kexiextendedwidgets SHARED ${kexiextendedwidgets_LIB_SRCS}) generate_export_header(kexiextendedwidgets BASE_NAME kexiextwidgets) target_link_libraries(kexiextendedwidgets PRIVATE kexidataviewcommon kexiguiutils KF5::TextWidgets # KTextEdit KF5::Codecs # KCharsets KF5::KIOFileWidgets # KFileWidget::getStartUrl() PUBLIC kexicore KPropertyWidgets KF5::TextEditor ) if(KEXI_USE_KFILEWIDGET) target_link_libraries(kexiextendedwidgets PUBLIC KF5::KIOFileWidgets) # KFileWidget endif() install(TARGETS kexiextendedwidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/widget/KexiConnectionSelectorWidget.cpp b/src/widget/KexiConnectionSelectorWidget.cpp index 6370fa168..234975aa4 100644 --- a/src/widget/KexiConnectionSelectorWidget.cpp +++ b/src/widget/KexiConnectionSelectorWidget.cpp @@ -1,649 +1,552 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek Copyright (C) 2012 Dimitrios T. Tanis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiConnectionSelectorWidget.h" -#include - -#include "KexiFileWidget.h" -#ifdef KEXI_USE_KFILEWIDGET -#else -# ifdef Q_OS_WIN -# include -# undef KIOWIDGETS_DEPRECATED -# define KIOWIDGETS_DEPRECATED -# endif -#include "KexiFileRequester.h" -#include -#include -#endif - #include "ui_KexiConnectionSelector.h" #include "kexiprjtypeselector.h" #include "kexidbconnectionwidget.h" +#include "KexiFileWidgetInterface.h" #include #include #include +#include + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KexiConnectionSelector : public QWidget, public Ui_KexiConnectionSelector { Q_OBJECT public: explicit KexiConnectionSelector(QWidget *parent) : QWidget(parent) { setupUi(this); setObjectName("conn_sel"); lblIcon->setPixmap(koDesktopIconCStr(Kexi::serverIconName())); lblIcon->setFixedSize(lblIcon->pixmap()->size()); btn_add->setToolTip(xi18n("Add a new database connection")); btn_edit->setToolTip(xi18n("Edit selected database connection")); btn_remove->setToolTip(xi18n("Remove selected database connections")); } ~KexiConnectionSelector() { } }; /*================================================================*/ ConnectionDataLVItem::ConnectionDataLVItem(KDbConnectionData *data, const KDbDriverMetaData &driverMetaData, QTreeWidget* list) : QTreeWidgetItem(list) , m_data(data) { update(driverMetaData); } ConnectionDataLVItem::~ConnectionDataLVItem() { } void ConnectionDataLVItem::update(const KDbDriverMetaData &driverMetaData) { setText(0, m_data->caption() + " "); const QString sfile = xi18n("File"); QString driverName = driverMetaData.name(); QString column1; if (driverMetaData.isFileBased()) { column1 = xi18nc("file (driver name)", "%1 (%2)", sfile, driverName); } else { column1 = driverName; } setText(1, column1 + " "); setText(2, (driverMetaData.isFileBased() ? QString("<%1>").arg(sfile.toLower()) : m_data->toUserVisibleString()) + " "); } /*================================================================*/ //! @internal class Q_DECL_HIDDEN KexiConnectionSelectorWidget::Private { public: Private() : conn_sel_shown(false) , confirmOverwrites(true) { } void updateRemoteListColumns() { remote->list->resizeColumnToContents(0); // name remote->list->resizeColumnToContents(1); // type } -#ifdef KEXI_USE_KFILEWIDGET - KexiFileWidget *fileWidget = nullptr; -#else - QWidget *fileWidget = nullptr; - KexiFileRequester *fileRequester = nullptr; -#endif // !KEXI_USE_KFILEWIDGET + QWidget *fileWidget() + { + return fileIface ? fileIface->widget() : nullptr; + } + + KexiFileWidgetInterface *fileIface = nullptr; KexiConnectionSelector *remote; QWidget* openExistingWidget; KexiPrjTypeSelector* prjTypeSelector; QUrl startDirOrVariable; KexiConnectionSelectorWidget::OperationMode operationMode; QStackedWidget *stack; QPointer conn_set; KDbDriverManager manager; bool conn_sel_shown; //!< helper bool confirmOverwrites; KexiUtils::PaintBlocker* descGroupBoxPaintBlocker; bool isConnectionSelected; + bool fileWidgetFrameVisible = true; + QPointer errorMessagePopup; }; /*================================================================*/ KexiConnectionSelectorWidget::KexiConnectionSelectorWidget( KexiDBConnectionSet *conn_set, const QUrl& startDirOrVariable, OperationMode mode, QWidget* parent) : QWidget(parent) , d(new Private()) { Q_ASSERT(conn_set); d->conn_set = conn_set; d->startDirOrVariable = startDirOrVariable; d->operationMode = mode; - m_errorMessagePopup = 0; setWindowIcon(Kexi::defaultFileBasedDriverIcon()); QBoxLayout* globalLyr = new QVBoxLayout(this); + globalLyr->setContentsMargins(QMargins()); //create header with radio buttons d->openExistingWidget = new QWidget(this); d->openExistingWidget->setObjectName("openExistingWidget"); QVBoxLayout* openExistingWidgetLyr = new QVBoxLayout(d->openExistingWidget); openExistingWidgetLyr->setContentsMargins(0, 0, 0, 0); d->prjTypeSelector = new KexiPrjTypeSelector(d->openExistingWidget); connect(d->prjTypeSelector->buttonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(slotPrjTypeSelected(QAbstractButton*))); openExistingWidgetLyr->addWidget(d->prjTypeSelector); d->prjTypeSelector->setContentsMargins(0, 0, 0, KexiUtils::spacingHint()); //openExistingWidgetLyr->addSpacing(KexiUtils::spacingHint()); QFrame* line = new QFrame(d->openExistingWidget); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); openExistingWidgetLyr->addWidget(line); globalLyr->addWidget(d->openExistingWidget); d->stack = new QStackedWidget(this); d->stack->setObjectName("stack"); globalLyr->addWidget(d->stack, 1); d->remote = new KexiConnectionSelector(d->stack); connect(d->remote->btn_add, SIGNAL(clicked()), this, SLOT(slotRemoteAddBtnClicked())); connect(d->remote->btn_edit, SIGNAL(clicked()), this, SLOT(slotRemoteEditBtnClicked())); connect(d->remote->btn_remove, SIGNAL(clicked()), this, SLOT(slotRemoteRemoveBtnClicked())); d->stack->addWidget(d->remote); if (d->remote->layout()) d->remote->layout()->setMargin(0); connect(d->remote->list, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotConnectionItemExecuted(QTreeWidgetItem*))); connect(d->remote->list, SIGNAL(itemSelectionChanged()), this, SLOT(slotConnectionSelectionChanged())); d->remote->list->installEventFilter(this); d->descGroupBoxPaintBlocker = new KexiUtils::PaintBlocker(d->remote->descGroupBox); d->descGroupBoxPaintBlocker->setEnabled(false); d->isConnectionSelected = false; } KexiConnectionSelectorWidget::~KexiConnectionSelectorWidget() { delete d; } void KexiConnectionSelectorWidget::showAdvancedConnection() { d->prjTypeSelector->option_server->setChecked(true); slotPrjTypeSelected(d->prjTypeSelector->option_server); } void KexiConnectionSelectorWidget::slotPrjTypeSelected(QAbstractButton *btn) { if (btn == d->prjTypeSelector->option_file) { //file-based prj type showSimpleConnection(); } else if (btn == d->prjTypeSelector->option_server) { //server-based prj type if (KDbDriverManager().hasDatabaseServerDrivers()) { if (!d->conn_sel_shown) { d->conn_sel_shown = true; //show connections (on demand): foreach(KDbConnectionData* connData, d->conn_set->list()) { addConnectionData(connData); // else { //this error should be more verbose: // qWarning() << "no driver found for '" << it.current()->driverName << "'!"; // } } if (d->remote->list->topLevelItemCount() > 0) { d->updateRemoteListColumns(); d->remote->list->sortByColumn(0, Qt::AscendingOrder); d->remote->list->topLevelItem(0)->setSelected(true); } d->remote->descGroupBox->layout()->setMargin(2); d->remote->list->setFocus(); slotConnectionSelectionChanged(); } d->stack->setCurrentWidget(d->remote); } else { - if (!m_errorMessagePopup) { + if (!d->errorMessagePopup) { QWidget *errorMessagePopupParent = new QWidget(this); QVBoxLayout *vbox = new QVBoxLayout(errorMessagePopupParent); - m_errorMessagePopup = new KexiServerDriverNotFoundMessage(errorMessagePopupParent); - vbox->addWidget(m_errorMessagePopup); + d->errorMessagePopup = new KexiServerDriverNotFoundMessage(errorMessagePopupParent); + vbox->addWidget(d->errorMessagePopup); vbox->addStretch(0); d->stack->addWidget(errorMessagePopupParent); - m_errorMessagePopup->setAutoDelete(false); - d->stack->setCurrentWidget(m_errorMessagePopup->parentWidget()); - m_errorMessagePopup->animatedShow(); + d->errorMessagePopup->setAutoDelete(false); + d->stack->setCurrentWidget(d->errorMessagePopup->parentWidget()); + d->errorMessagePopup->animatedShow(); } else { - d->stack->setCurrentWidget(m_errorMessagePopup->parentWidget()); + d->stack->setCurrentWidget(d->errorMessagePopup->parentWidget()); } } } } ConnectionDataLVItem* KexiConnectionSelectorWidget::addConnectionData(KDbConnectionData* data) { const KDbDriverMetaData* driverMetaData = d->manager.driverMetaData(data->driverId()); return driverMetaData ? new ConnectionDataLVItem(data, *driverMetaData, d->remote->list) : 0; } void KexiConnectionSelectorWidget::showSimpleConnection() { d->prjTypeSelector->option_file->setChecked(true); -#ifdef KEXI_USE_KFILEWIDGET - if (!d->fileWidget) { - d->fileWidget = new KexiFileWidget( - d->startDirOrVariable, - d->operationMode == Opening - ? KexiFileFilters::Opening : KexiFileFilters::SavingFileBasedDB, + if (!d->fileIface) { + d->fileIface = KexiFileWidgetInterface::createWidget( + d->startDirOrVariable, d->operationMode == Opening ? KexiFileFilters::Opening + : KexiFileFilters::SavingFileBasedDB, d->stack); - d->fileWidget->setOperationMode(static_cast(d->operationMode)); - d->fileWidget->setConfirmOverwrites(d->confirmOverwrites); - d->stack->addWidget(d->fileWidget); - - for (QWidget *w = parentWidget(); w; w = w->parentWidget()) { - if (w->windowType() == Qt::Dialog) { -//! @todo KEXI3 KFileWidget connect(m_fileDlg, SIGNAL(rejected()), qobject_cast(w), SLOT(reject())); - break; - } - } - connect(d->fileWidget, &KexiFileWidget::fileHighlighted, - this, &KexiConnectionSelectorWidget::slotConnectionSelected); - connect(d->fileWidget, &KexiFileWidget::selectionChanged, - this, &KexiConnectionSelectorWidget::fileSelectionChanged); + d->fileIface->setWidgetFrame(d->fileWidgetFrameVisible); + d->fileIface->setConfirmOverwrites(d->confirmOverwrites); + d->stack->addWidget(d->fileIface->widget()); + d->fileIface->connectFileSelectedSignal(this, SLOT(slotFileConnectionSelected(QString))); } -#else - if (!d->fileWidget) { - d->fileWidget = new QWidget(d->stack); - QVBoxLayout *lyr = new QVBoxLayout(d->fileWidget); - d->fileRequester = new KexiFileRequester(d->startDirOrVariable); - d->fileRequester->setFileMode(d->operationMode == Opening ? - KexiFileFilters::Opening : KexiFileFilters::SavingFileBasedDB); - d->fileRequester->setFrame(false); - d->fileRequester->setContentsMargins(QMargins()); - //d->fileRequester->setUrl(QUrl()); - // TODO setConfirmOverwrites - // TODO connect - lyr->addWidget(d->fileRequester, 1); - d->stack->addWidget(d->fileWidget); - connect(d->fileRequester, &KexiFileRequester::fileSelected, - this, &KexiConnectionSelectorWidget::slotConnectionSelected); - connect(d->fileRequester, &KexiFileRequester::fileSelected, - this, &KexiConnectionSelectorWidget::fileSelectionChanged); + d->stack->setCurrentWidget(d->fileIface->widget()); +} + +void KexiConnectionSelectorWidget::setFileWidgetFrameVisible(bool set) +{ + d->fileWidgetFrameVisible = set; + if (d->fileIface) { + d->fileIface->setWidgetFrame(d->fileWidgetFrameVisible); } -#endif - d->stack->setCurrentWidget(d->fileWidget); } KexiConnectionSelectorWidget::ConnectionType KexiConnectionSelectorWidget::selectedConnectionType() const { - return (d->stack->currentWidget() == d->fileWidget) ? FileBased : ServerBased; + return (d->stack->currentWidget() == d->fileWidget()) ? FileBased : ServerBased; } KDbConnectionData* KexiConnectionSelectorWidget::selectedConnectionData() const { QList items = d->remote->list->selectedItems(); if (items.isEmpty()) return 0; ConnectionDataLVItem *item = static_cast(items.first()); if (!item) return 0; return item->data(); } -QString KexiConnectionSelectorWidget::selectedFileName() +QString KexiConnectionSelectorWidget::selectedFile() const { if (selectedConnectionType() != KexiConnectionSelectorWidget::FileBased) { return QString(); } -#ifdef KEXI_USE_KFILEWIDGET - if (d->fileWidget->selectedFile().isEmpty()) { - QUrl path = d->fileWidget->baseUrl(); - const QString firstUrl(d->fileWidget->locationEdit()->lineEdit()->text()); - if (QDir::isAbsolutePath(firstUrl)) - path = QUrl::fromLocalFile(firstUrl); - else { - path = path.adjusted(QUrl::StripTrailingSlash); - path.setPath(path.path() + '/' + (firstUrl)); - } - return path.toLocalFile(); - } - - return d->fileWidget->highlightedFile(); //ok? fileWidget->selectedFile(); -#else - return d->fileRequester->selectedFileName(); -#endif + return d->fileIface->selectedFile(); } -void KexiConnectionSelectorWidget::setSelectedFileName(const QString& fileName) +void KexiConnectionSelectorWidget::setSelectedFile(const QString& name) { if (selectedConnectionType() != KexiConnectionSelectorWidget::FileBased) { return; } -#ifdef KEXI_USE_KFILEWIDGET - return d->fileWidget->setSelection(fileName); -#else - d->fileRequester->setSelectedFileName(fileName); -#endif + return d->fileIface->setSelectedFile(name); } -/* -void KexiConnectionSelectorWidget::slotUrlSelected(const QUrl& url) -{ - if (selectedConnectionType() != KexiConnectionSelectorWidget::FileBased) { - return; - } -#ifndef KEXI_USE_KFILEWIDGET - d->updateFileRequesterUrl(url); -#endif -}*/ - void KexiConnectionSelectorWidget::slotConnectionItemExecuted(QTreeWidgetItem* item) { emit connectionItemExecuted(static_cast(item)); slotConnectionSelected(); } void KexiConnectionSelectorWidget::slotConnectionItemExecuted() { QList items = d->remote->list->selectedItems(); if (items.isEmpty()) return; slotConnectionItemExecuted(items.first()); slotConnectionSelected(); } void KexiConnectionSelectorWidget::slotConnectionSelectionChanged() { QList items = d->remote->list->selectedItems(); if (items.isEmpty()) return; ConnectionDataLVItem* item = static_cast(items.first()); d->remote->btn_edit->setEnabled(item); d->remote->btn_remove->setEnabled(item); QString desc; if (item) { desc = item->data()->description(); } d->descGroupBoxPaintBlocker->setEnabled(desc.isEmpty()); d->remote->descriptionLabel->setText(desc); slotConnectionSelected(); emit connectionItemHighlighted(item); } QTreeWidget* KexiConnectionSelectorWidget::connectionsList() const { return d->remote->list; } void KexiConnectionSelectorWidget::setFocus() { QWidget::setFocus(); - if (d->stack->currentWidget() == d->fileWidget) { - d->fileWidget->setFocus(); + if (d->stack->currentWidget() == d->fileWidget()) { + d->fileWidget()->setFocus(); } else { d->remote->list->setFocus(); } } void KexiConnectionSelectorWidget::hideHelpers() { d->openExistingWidget->hide(); } void KexiConnectionSelectorWidget::setConfirmOverwrites(bool set) { d->confirmOverwrites = set; - if (d->fileWidget) { -#ifdef KEXI_USE_KFILEWIDGET - d->fileWidget->setConfirmOverwrites(d->confirmOverwrites); -#else - // TODO? -#endif + if (d->fileIface) { + d->fileIface->setConfirmOverwrites(d->confirmOverwrites); } } bool KexiConnectionSelectorWidget::confirmOverwrites() const { return d->confirmOverwrites; } void KexiConnectionSelectorWidget::slotRemoteAddBtnClicked() { KDbConnectionData data; KexiDBConnectionDialog dlg(this, data, QString(), KGuiItem(xi18nc("@action:button Add Database Connection", "&Add"), koIconName("dialog-ok"), xi18n("Add database connection"))); dlg.setWindowTitle(xi18nc("@title:window", "Add a New Database Connection")); if (QDialog::Accepted != dlg.exec()) return; //store this conn. data KDbConnectionData *newData = new KDbConnectionData(*dlg.currentProjectData().connectionData()); KDbMessageGuard mg(d->conn_set); if (!d->conn_set->addConnectionData(newData)) { delete newData; return; } ConnectionDataLVItem* item = addConnectionData(newData); if (item) { d->remote->list->clearSelection(); d->updateRemoteListColumns(); item->setSelected(true); slotConnectionSelectionChanged(); } } void KexiConnectionSelectorWidget::slotRemoteEditBtnClicked() { QList items = d->remote->list->selectedItems(); if (items.isEmpty()) return; ConnectionDataLVItem* item = static_cast(items.first()); if (!item) return; KexiDBConnectionDialog dlg(this, *item->data(), QString(), KGuiItem(xi18nc("@action:button Save Database Connection", "&Save"), koIconName("document-save"), xi18n("Save changes made to this database connection"))); dlg.setWindowTitle(xi18nc("@title:window", "Edit Database Connection")); if (QDialog::Accepted != dlg.exec()) return; KDbMessageGuard mg(d->conn_set); if (!d->conn_set->saveConnectionData(item->data(), *dlg.currentProjectData().connectionData())) { return; } const KDbDriverMetaData *driverMetaData = d->manager.driverMetaData(item->data()->driverId()); if (driverMetaData) { item->update(*driverMetaData); d->updateRemoteListColumns(); slotConnectionSelectionChanged(); //to update descr. edit } } void KexiConnectionSelectorWidget::slotRemoteRemoveBtnClicked() { QList items = d->remote->list->selectedItems(); if (items.isEmpty()) return; ConnectionDataLVItem* item = static_cast(items.first()); if (!item) return; if (KMessageBox::Yes != KMessageBox::questionYesNo(this, xi18nc("@info", "Do you want to remove database connection %1 from " "the list of available connections?", item->data()->toUserVisibleString()), QString(), //caption KStandardGuiItem::remove(), KStandardGuiItem::cancel(), QString(), //dont'ask name KMessageBox::Notify | KMessageBox::Dangerous)) { return; } QTreeWidgetItem* nextItem = d->remote->list->itemBelow(item); if (!nextItem) nextItem = d->remote->list->itemAbove(item); KDbMessageGuard mg(d->conn_set); if (!d->conn_set->removeConnectionData(item->data())) return; delete item->data(); delete item; if (nextItem) nextItem->setSelected(true); d->updateRemoteListColumns(); } void KexiConnectionSelectorWidget::hideConnectonIcon() { d->remote->lblIcon->setFixedWidth(0); d->remote->lblIcon->setPixmap(QPixmap()); } void KexiConnectionSelectorWidget::hideDescription() { d->remote->lblIcon->hide(); d->remote->label->hide(); } void KexiConnectionSelectorWidget::setExcludedMimeTypes(const QStringList &mimeTypes) { -#ifdef KEXI_USE_KFILEWIDGET - d->fileWidget->setExcludedMimeTypes(mimeTypes); -#else - // TODO - d->fileRequester->setExcludedMimeTypes(mimeTypes); -#endif + d->fileIface->setExcludedMimeTypes(mimeTypes); } bool KexiConnectionSelectorWidget::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if ((ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) && ke->modifiers() == Qt::NoModifier) { slotConnectionItemExecuted(); return true; } } return QWidget::eventFilter(watched, event); } +void KexiConnectionSelectorWidget::slotFileConnectionSelected(const QString &name) +{ + Q_UNUSED(name) + d->isConnectionSelected = !d->fileIface->selectedFile().isEmpty(); + emit connectionSelected(d->isConnectionSelected); + emit fileSelected(name); +} + void KexiConnectionSelectorWidget::slotConnectionSelected() { - switch (selectedConnectionType()) { - case KexiConnectionSelectorWidget::FileBased: { -#ifdef KEXI_USE_KFILEWIDGET - QLineEdit *lineEdit = d->fileWidget->locationEdit()->lineEdit(); - d->isConnectionSelected = !lineEdit->text().isEmpty(); -#else - d->isConnectionSelected = !d->fileRequester->selectedFileName().isEmpty(); -#endif - break; - } - case KexiConnectionSelectorWidget::ServerBased: - d->isConnectionSelected = !d->remote->list->selectedItems().isEmpty(); - break; - default:; - } - emit connectionSelected (d->isConnectionSelected); + d->isConnectionSelected = !d->remote->list->selectedItems().isEmpty(); + emit connectionSelected(d->isConnectionSelected); } bool KexiConnectionSelectorWidget::hasSelectedConnection() const { return d->isConnectionSelected; } void KexiConnectionSelectorWidget::setFileMode(KexiFileFilters::Mode mode) { - if (d->fileWidget) { -#ifdef KEXI_USE_KFILEWIDGET - d->fileWidget->setMode(mode); -#else - d->fileRequester->setFileMode(mode); -#endif + if (d->fileIface) { + d->fileIface->setMode(mode); } } void KexiConnectionSelectorWidget::setAdditionalMimeTypes(const QStringList &mimeTypes) { - if (d->fileWidget) { -#ifdef KEXI_USE_KFILEWIDGET - d->fileWidget->setAdditionalMimeTypes(mimeTypes); -#else - d->fileRequester->setAdditionalMimeTypes(mimeTypes); -#endif + if (d->fileIface) { + d->fileIface->setAdditionalMimeTypes(mimeTypes); } } bool KexiConnectionSelectorWidget::checkSelectedFile() { - if (d->fileWidget) { -#ifdef KEXI_USE_KFILEWIDGET - return d->fileWidget->checkSelectedFile(); -#else - // TODO - return !d->fileRequester->selectedFileName().isEmpty(); -#endif + if (d->fileIface) { + return d->fileIface->checkSelectedFile(); } return false; } QString KexiConnectionSelectorWidget::highlightedFile() const { - if (d->fileWidget) { -#ifdef KEXI_USE_KFILEWIDGET - return d->fileWidget->highlightedFile(); -#else - return d->fileRequester->selectedFileName(); -#endif + if (d->fileIface) { + return d->fileIface->highlightedFile(); } return QString(); } #include "KexiConnectionSelectorWidget.moc" diff --git a/src/widget/KexiConnectionSelectorWidget.h b/src/widget/KexiConnectionSelectorWidget.h index 5f348f536..0647894e8 100644 --- a/src/widget/KexiConnectionSelectorWidget.h +++ b/src/widget/KexiConnectionSelectorWidget.h @@ -1,177 +1,176 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek Copyright (C) 2012 Dimitrios T. Tanis This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXICONNECTIONSELECTORWIDGET_H #define KEXICONNECTIONSELECTORWIDGET_H #include #include -#include -#include +#include "kexiextwidgets_export.h" -#include #include class QAbstractButton; class KDbDriverMetaData; //! An item for a single database connection class KEXIEXTWIDGETS_EXPORT ConnectionDataLVItem : public QTreeWidgetItem { public: ConnectionDataLVItem(KDbConnectionData *data, const KDbDriverMetaData &driverMetaData, QTreeWidget* list); ~ConnectionDataLVItem(); void update(const KDbDriverMetaData& driverMetaData); using QTreeWidgetItem::data; KDbConnectionData *data() const { return m_data; } protected: KDbConnectionData *m_data; }; //! @short Widget that allows to select a database connection (file- or server-based) /*! The widget allows to select database connection without choosing database itself. */ class KEXIEXTWIDGETS_EXPORT KexiConnectionSelectorWidget : public QWidget { Q_OBJECT public: //! Defines connection type enum ConnectionType { FileBased = 1, //!< the widget displays file-based connection ServerBased = 2 //!< the widget displays server-based connection }; //! Defines operation mode enum OperationMode { Opening = 1, Saving = 2 }; /*! Constructs a new widget which contains \a conn_set as connection set. \a conn_set can be altered, because Add/Edit/Remove buttons are available to users. \a startDirOrVariable can be provided to specify a start dir for file browser (it can also contain a configuration variable name with "kfiledialog:///" prefix as described in KRecentDirs documentation). */ //! @todo KEXI3 add equivalent of kfiledialog:/// for startDirOrVariable KexiConnectionSelectorWidget(KexiDBConnectionSet *conn_set, const QUrl& startDirOrVariable, OperationMode mode, QWidget* parent = 0); virtual ~KexiConnectionSelectorWidget(); /*! After accepting this dialog this method returns wherher user selected file- or server-based connection. */ ConnectionType selectedConnectionType() const; /*! \return data of selected connection, if server-based connection was selected. Returns NULL if no selection has been made or file-based connection has been selected. @see selectedConnectionType() */ KDbConnectionData* selectedConnectionData() const; /*! \return the name of database file, if file-based connection was selected. Returns empty string if no selection has been made or server-based connection has been selected. + //! @note Call checkSelectedFile() first @see selectedConnectionType() */ - QString selectedFileName(); + QString selectedFile() const; QTreeWidget* connectionsList() const; bool confirmOverwrites() const; bool hasSelectedConnection() const; - /*! @return true if the current file URL meets requies constraints - (i.e. exists or doesn't exist); - shows appropriate message box if needed. */ + /*! @return true if the current file URL meets requied constraints (i.e. the file exists) + Shows appropriate message box if needed. */ bool checkSelectedFile(); - //! @return selected file. - //! @note Call checkSelectedFile() first + //! @return highlighted file QString highlightedFile() const; Q_SIGNALS: void connectionItemExecuted(ConnectionDataLVItem *item); void connectionItemHighlighted(ConnectionDataLVItem *item); void connectionSelected(bool hasSelected); - void fileSelectionChanged(); + void fileSelected(const QString &name); public Q_SLOTS: void showSimpleConnection(); void showAdvancedConnection(); virtual void setFocus(); /*! Hides helpers on the server based connection page (sometimes it's convenient not to have these): - "Select existing database server's connection..." (label at the top) - "Click "Back" button" (label at the bottom) - "Back" button itself */ void hideHelpers(); void hideConnectonIcon(); void hideDescription(); - /*! Sets selected filename to \a fileName. + /*! Sets selected filename to @a name. Only works when selectedConnectionType()==FileBased. */ - void setSelectedFileName(const QString& fileName); + void setSelectedFile(const QString &name); /*! If true, user will be asked to accept overwriting existing project. This is true by default. */ void setConfirmOverwrites(bool set); void setFileMode(KexiFileFilters::Mode mode); void setAdditionalMimeTypes(const QStringList &mimeTypes); //! Sets excluded mime types void setExcludedMimeTypes(const QStringList& mimeTypes); + void setFileWidgetFrameVisible(bool set); + protected Q_SLOTS: void slotConnectionItemExecuted(QTreeWidgetItem *item); void slotConnectionItemExecuted(); void slotRemoteAddBtnClicked(); void slotRemoteEditBtnClicked(); void slotRemoteRemoveBtnClicked(); void slotConnectionSelectionChanged(); void slotPrjTypeSelected(QAbstractButton *btn); + void slotFileConnectionSelected(const QString &name); void slotConnectionSelected(); protected: virtual bool eventFilter(QObject* watched, QEvent* event); private: ConnectionDataLVItem* addConnectionData(KDbConnectionData* data); ConnectionDataLVItem* selectedConnectionDataItem() const; - QPointer m_errorMessagePopup; class Private; Private * const d; }; #endif // KEXICONNECTIONSELECTORWIDGET_H diff --git a/src/widget/KexiFileRequester.cpp b/src/widget/KexiFileRequester.cpp index 6420905ea..ad6aced9e 100644 --- a/src/widget/KexiFileRequester.cpp +++ b/src/widget/KexiFileRequester.cpp @@ -1,369 +1,346 @@ /* This file is part of the KDE project Copyright (C) 2016-2017 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiFileRequester.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum KexiFileSystemModelColumnIds { NameColumnId, LastModifiedColumnId }; } //! A model for KexiFileRequester class KexiFileSystemModel : public QFileSystemModel { Q_OBJECT public: explicit KexiFileSystemModel(QWidget *parent = nullptr) : QFileSystemModel(parent) { } int columnCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent) return 2; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { const int col = index.column(); if (col == NameColumnId) { switch (role) { case Qt::DecorationRole: { if (isDir(index)) { return koIcon("folder"); } else { QMimeDatabase db; return QIcon::fromTheme(db.mimeTypeForFile(filePath(index)).iconName()); } } default: break; } return QFileSystemModel::data(index, role); } else if (col == LastModifiedColumnId) { const QWidget *parentWidget = qobject_cast(QObject::parent()); switch (role) { case Qt::DisplayRole: return parentWidget->locale().toString(QFileSystemModel::lastModified(index), QLocale::ShortFormat); default: break; } } return QVariant(); } Qt::ItemFlags flags(const QModelIndex& index) const override { Q_UNUSED(index) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } }; //! @internal class Q_DECL_HIDDEN KexiFileRequester::Private : public QObject { Q_OBJECT public: Private(KexiFileRequester *r) : q(r) { } void updateFilter() { model->setNameFilters(filters.allGlobPatterns()); } static QString urlToPath(const QUrl &url) { QString filePath = QDir::toNativeSeparators(url.path(QUrl::RemoveScheme | QUrl::PreferLocalFile | QUrl::StripTrailingSlash)); #ifdef Q_OS_WIN if (filePath.startsWith('\\')) { filePath = filePath.mid(1); } else if (filePath.startsWith("file:\\")) { filePath = filePath.mid(6); } #endif return filePath; } public Q_SLOTS: void updateUrl(const QUrl &url) { updateFileName(urlToPath(url)); } void updateFileName(const QString &filePath) { const QFileInfo fileInfo(filePath); QString dirPath; if (fileInfo.isDir()) { dirPath = fileInfo.absoluteFilePath(); } else { dirPath = fileInfo.absolutePath(); } dirPath = QDir::toNativeSeparators(dirPath); if (filePath.isEmpty()) { // display Windows Explorer's "Computer" folder name for the top level #ifdef Q_OS_WIN QString computerNameString = QSettings( "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\" "CLSID\\{20D04FE0-3AEA-1069-A2D8-08002B30309D}", QSettings::NativeFormat).value("Default").toString(); if (computerNameString.isEmpty()) { computerNameString = xi18n("Computer"); } urlLabel->setText(computerNameString); folderIcon->setPixmap(koSmallIcon("computer")); upButton->setEnabled(false); #else urlLabel->setText("/"); folderIcon->setPixmap(koSmallIcon("folder")); upButton->setEnabled(false); #endif } else { urlLabel->setText(dirPath); folderIcon->setPixmap(koSmallIcon("folder")); upButton->setEnabled(filePath != "/"); } model->setRootPath(dirPath); list->setRootIndex(model->index(filePath)); list->resizeColumnToContents(LastModifiedColumnId); } void itemActivated(const QModelIndex & index) { const QString filePath(model->filePath(index)); if (model->isDir(index)) { updateFileName(filePath); } else { emit q->fileSelected(filePath); } } void upButtonClicked() { QString dirPath(urlLabel->text()); QDir dir(dirPath); if (dirPath.isEmpty() || !dir.cdUp()) { updateFileName(QString()); } else { updateFileName(dir.absolutePath()); } //! @todo update button enable flag } void selectUrlButtonClicked() { QUrl dirUrl; #ifdef Q_OS_WIN if (!upButton->isEnabled()) { // Computer folder, see http://doc.qt.io/qt-5/qfiledialog.html#setDirectoryUrl dirUrl = QUrl("clsid:0AC0837C-BBF8-452A-850D-79D08E667CA7"); } #else if (false) { } #endif else { dirUrl = QUrl::fromLocalFile(urlLabel->text()); } QUrl selectedUrl = QFileDialog::getExistingDirectoryUrl(q, QString(), dirUrl); if (selectedUrl.isLocalFile()) { updateFileName(selectedUrl.toLocalFile()); } } public: KexiFileRequester* const q; - QString recentDirClass; QPushButton *upButton; QLabel *folderIcon; QLabel *urlLabel; QPushButton *selectUrlButton; KexiFileSystemModel *model; QTreeView *list; KexiFileFilters filters; }; -KexiFileRequester::KexiFileRequester(const QUrl &fileOrVariable, QWidget *parent) - : QWidget(parent), d(new Private(this)) +KexiFileRequester::KexiFileRequester(const QUrl &fileOrVariable, KexiFileFilters::Mode mode, + QWidget *parent) + : QWidget(parent), KexiFileWidgetInterface(fileOrVariable), d(new Private(this)) { init(); - QUrl url; - if (fileOrVariable.scheme() == "kfiledialog") { - url = KFileWidget::getStartUrl(fileOrVariable, d->recentDirClass); - } else { - url = fileOrVariable; - } - const QString fileName = Private::urlToPath(url); + const QString fileName = Private::urlToPath(startUrl()); d->updateFileName(fileName); + setMode(mode); } -KexiFileRequester::KexiFileRequester(const QString &selectedFileName, QWidget *parent) - : QWidget(parent), d(new Private(this)) +KexiFileRequester::KexiFileRequester(const QString &selectedFileName, KexiFileFilters::Mode mode, + QWidget *parent) + : QWidget(parent), KexiFileWidgetInterface(QUrl(selectedFileName)), d(new Private(this)) { init(); d->updateFileName(selectedFileName); + setMode(mode); } KexiFileRequester::~KexiFileRequester() { const QString startDir(d->urlLabel->text()); - if (QDir(startDir).exists()) { - KRecentDirs::add(d->recentDirClass, startDir); - } + addRecentDir(startDir); delete d; } void KexiFileRequester::init() { // [^] [Dir ][..] // [ files ] QVBoxLayout *lyr = new QVBoxLayout(this); - setContentsMargins(lyr->contentsMargins()); + setContentsMargins(QMargins()); lyr->setContentsMargins(QMargins()); QHBoxLayout *urlLyr = new QHBoxLayout; urlLyr->setContentsMargins(QMargins()); lyr->addLayout(urlLyr); d->upButton = new QPushButton; d->upButton->setFocusPolicy(Qt::NoFocus); d->upButton->setIcon(koIcon("go-up")); d->upButton->setToolTip(xi18n("Go to parent directory")); d->upButton->setFlat(true); connect(d->upButton, &QPushButton::clicked, d, &KexiFileRequester::Private::upButtonClicked); urlLyr->addWidget(d->upButton); d->folderIcon = new QLabel; urlLyr->addWidget(d->folderIcon); d->urlLabel = new QLabel; d->urlLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); d->urlLabel->setWordWrap(true); d->urlLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); urlLyr->addWidget(d->urlLabel, 1); d->selectUrlButton = new QPushButton; d->selectUrlButton->setFocusPolicy(Qt::NoFocus); d->selectUrlButton->setIcon(koIcon("folder")); d->selectUrlButton->setToolTip(xi18n("Select directory")); d->selectUrlButton->setFlat(true); connect(d->selectUrlButton, &QPushButton::clicked, d, &KexiFileRequester::Private::selectUrlButtonClicked); urlLyr->addWidget(d->selectUrlButton); d->list = new QTreeView; connect(d->list, KexiUtils::activateItemsOnSingleClick(d->list) ? &QTreeView::clicked : &QTreeView::activated, d, &KexiFileRequester::Private::itemActivated); d->list->setRootIsDecorated(false); d->list->setItemsExpandable(false); d->list->header()->hide(); lyr->addWidget(d->list); d->model = new KexiFileSystemModel(d->list); d->model->setNameFilterDisables(false); d->list->setModel(d->model); d->list->header()->setStretchLastSection(false); d->list->header()->setSectionResizeMode(NameColumnId, QHeaderView::Stretch); d->list->header()->setSectionResizeMode(LastModifiedColumnId, QHeaderView::ResizeToContents); } -QString KexiFileRequester::selectedFileName() const +QString KexiFileRequester::selectedFile() const { const QModelIndexList list(d->list->selectionModel()->selectedIndexes()); if (list.isEmpty()) { return QString(); } const QModelIndex index(list.first()); if (d->model->isDir(index)) { return QString(); } return d->model->filePath(index); } -void KexiFileRequester::setSelectedFileName(const QString &selectedFileName) +QString KexiFileRequester::highlightedFile() const { - d->updateFileName(selectedFileName); + return selectedFile(); } -void KexiFileRequester::setFileMode(KexiFileFilters::Mode mode) +QString KexiFileRequester::currentDir() const { - KFile::Modes fileModes = KFile::File | KFile::LocalOnly; - if (mode == KexiFileFilters::Opening || mode == KexiFileFilters::CustomOpening) { - fileModes |= KFile::ExistingOnly; - } - d->filters.setMode(mode); - d->updateFilter(); + return d->model->rootPath(); } -QStringList KexiFileRequester::additionalMimeTypes() const +void KexiFileRequester::setSelectedFile(const QString &name) { - return d->filters.additionalMimeTypes(); + d->updateFileName(name); } -QStringList KexiFileRequester::excludedMimeTypes() const +void KexiFileRequester::updateFilters() { - return d->filters.excludedMimeTypes(); -} - -QString KexiFileRequester::defaultFilter() const -{ - return d->filters.defaultFilter(); -} - -void KexiFileRequester::setExcludedMimeTypes(const QStringList &mimeTypes) -{ - d->filters.setExcludedMimeTypes(mimeTypes); d->updateFilter(); } -void KexiFileRequester::setAdditionalMimeTypes(const QStringList &mimeTypes) +void KexiFileRequester::setWidgetFrame(bool set) { - d->filters.setAdditionalMimeTypes(mimeTypes); - d->updateFilter(); + d->list->setFrameShape(set ? QFrame::StyledPanel : QFrame::NoFrame); + d->list->setLineWidth(set ? 1 : 0); } -void KexiFileRequester::setDefaultFilter(const QString &filter) +void KexiFileRequester::applyEnteredFileName() { - d->filters.setDefaultFilter(filter); - d->updateFilter(); } -void KexiFileRequester::setFrame(bool frame) +QStringList KexiFileRequester::currentFilters() const { - d->list->setFrameShape(frame ? QFrame::StyledPanel : QFrame::NoFrame); + return QStringList(); } #include "KexiFileRequester.moc" diff --git a/src/widget/KexiFileRequester.h b/src/widget/KexiFileRequester.h index 22c62d51c..5ace71c2c 100644 --- a/src/widget/KexiFileRequester.h +++ b/src/widget/KexiFileRequester.h @@ -1,80 +1,99 @@ /* This file is part of the KDE project Copyright (C) 2016-2017 Jarosław Staniek This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIFILEREQUESTER_H #define KEXIFILEREQUESTER_H -#include "kexiextwidgets_export.h" -#include +#include "KexiFileWidgetInterface.h" #include //! @brief A widget showing a line edit and a button, which invokes a file dialog -class KEXIEXTWIDGETS_EXPORT KexiFileRequester : public QWidget +class KEXIEXTWIDGETS_EXPORT KexiFileRequester : public QWidget, public KexiFileWidgetInterface { Q_OBJECT public: - explicit KexiFileRequester(const QUrl &fileOrVariable, QWidget *parent = nullptr); + explicit KexiFileRequester(const QUrl &fileOrVariable, KexiFileFilters::Mode mode, + QWidget *parent = nullptr); - explicit KexiFileRequester(const QString &selectedFile, QWidget *parent = nullptr); + explicit KexiFileRequester(const QString &selectedFile, KexiFileFilters::Mode mode, + QWidget *parent = nullptr); - ~KexiFileRequester(); + ~KexiFileRequester() override; - //! @return absolute path of the selected file - QString selectedFileName() const; + /** + * Returns the full path of the selected file in the local filesystem. + * (Local files only) + */ + QString selectedFile() const override; + + /** + * Returns the full path of the highlighted file in the local filesystem. + * (Local files only) + */ + QString highlightedFile() const override; + + /** + * @return the currently shown directory. + */ + QString currentDir() const override; //! Sets file mode - void setFileMode(KexiFileFilters::Mode mode); + //void setFileMode(KexiFileFilters::Mode mode); //! @return additional mime types - QStringList additionalMimeTypes() const; + //QStringList additionalMimeTypes() const; //! @return excluded mime types - QStringList excludedMimeTypes() const; + //QStringList excludedMimeTypes() const; //! @return the default filter, used when an empty filter is set - QString defaultFilter() const; + //QString defaultFilter() const; Q_SIGNALS: - void fileSelected(const QString &filePath); + void fileHighlighted(const QString &name); + void fileSelected(const QString &name); public Q_SLOTS: //! Sets the url - void setSelectedFileName(const QString &fileName); + void setSelectedFile(const QString &name) override; - //! Set excluded mime types - void setExcludedMimeTypes(const QStringList &mimeTypes); + /** + * Sets whether the line edit draws itself with a frame. + */ + void setWidgetFrame(bool set) override; - //! Sets additional mime types, e.g. "text/x-csv" - void setAdditionalMimeTypes(const QStringList &mimeTypes); +protected: + /** + * Updates filters in the widget based on current filter selection. + */ + void updateFilters() override; - //! Sets a default-filter, that is used when an empty filter is set - void setDefaultFilter(const QString &filter); + void applyEnteredFileName() override; - //! @see QFrame::setFrame(bool) - void setFrame(bool frame); + QStringList currentFilters() const override; private: void init(); Q_DISABLE_COPY(KexiFileRequester) class Private; Private * const d; }; #endif // KEXIFILEREQUESTER_H diff --git a/src/widget/KexiFileWidget.cpp b/src/widget/KexiFileWidget.cpp index d92772fd9..aa15c7ca2 100644 --- a/src/widget/KexiFileWidget.cpp +++ b/src/widget/KexiFileWidget.cpp @@ -1,395 +1,320 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiFileWidget.h" #include #include #include #include #include -//! @todo KEXI3 #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include //! @internal class Q_DECL_HIDDEN KexiFileWidget::Private { public: Private() { } - - KexiFileFilters filters; - QString defaultExtension; - bool confirmOverwrites = true; - bool filtersUpdated = false; QUrl highlightedUrl; - QString recentDirClass; }; //------------------ -KexiFileWidget::KexiFileWidget( - const QUrl &startDirOrVariable, KexiFileFilters::Mode mode, QWidget *parent) - : KFileWidget(startDirOrVariable, parent) - , d(new Private) +KexiFileWidget::KexiFileWidget(const QUrl &startDirOrVariable, KexiFileFilters::Mode mode, + QWidget *parent) + : KFileWidget(startDirOrVariable, parent) + , KexiFileWidgetInterface(startDirOrVariable) + , d(new Private) { - qDebug() << startDirOrVariable.scheme(); - if (startDirOrVariable.scheme() == "kfiledialog") { -//! @todo KEXI3 test it - KFileWidget::getStartUrl(startDirOrVariable, d->recentDirClass); - } setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - setMode(mode); QAction *previewAction = actionCollection()->action("preview"); - if (previewAction) + if (previewAction) { previewAction->setChecked(false); + } setFocusProxy(locationEdit()); - connect(this, SIGNAL(fileHighlighted(QUrl)), - this, SLOT(slotExistingFileHighlighted(QUrl))); + connect(this, &KFileWidget::fileHighlighted, this, &KexiFileWidget::slotFileHighlighted); + connect(this, &KFileWidget::fileSelected, this, &KexiFileWidget::slotFileSelected); + setMode(mode); } KexiFileWidget::~KexiFileWidget() { + done(); +#if 0 qDebug() << d->recentDirClass; if (!d->recentDirClass.isEmpty()) { QString hf = highlightedFile(); QUrl dir; if (hf.isEmpty()) { dir = baseUrl(); } else { QFileInfo fi(hf); QString dirStr = fi.isDir() ? fi.absoluteFilePath() : fi.dir().absolutePath(); dir = QUrl::fromLocalFile(dirStr); } qDebug() << dir; qDebug() << highlightedFile(); if (!dir.isEmpty()) KRecentDirs::add(d->recentDirClass, dir.url()); } +#endif delete d; } -void KexiFileWidget::slotExistingFileHighlighted(const QUrl& url) +void KexiFileWidget::slotFileHighlighted(const QUrl& url) { qDebug() << url; d->highlightedUrl = url; - emit fileHighlighted(); -} - -QString KexiFileWidget::highlightedFile() const -{ - return d->highlightedUrl.toLocalFile(); + emit fileHighlighted(highlightedFile()); } -KexiFileFilters::Mode KexiFileWidget::mode() const +void KexiFileWidget::slotFileSelected(const QUrl& url) { - return d->filters.mode(); + qDebug() << url; + emit fileSelected(selectedFile()); } void KexiFileWidget::setMode(KexiFileFilters::Mode mode) { - //delayed - d->filters.setMode(mode); - d->filtersUpdated = false; - updateFilters(); -} - -QStringList KexiFileWidget::additionalMimeTypes() const -{ - return d->filters.additionalMimeTypes(); -} - -void KexiFileWidget::setAdditionalMimeTypes(const QStringList &mimeTypes) -{ - d->filters.setAdditionalMimeTypes(mimeTypes); - d->filtersUpdated = false; -} - -QStringList KexiFileWidget::excludedMimeTypes() const -{ - return d->filters.excludedMimeTypes(); -} - -void KexiFileWidget::setExcludedMimeTypes(const QStringList &mimeTypes) -{ - d->filters.setExcludedMimeTypes(mimeTypes); - d->filtersUpdated = false; + KexiFileWidgetInterface::setMode(mode); } void KexiFileWidget::updateFilters() { - if (d->filtersUpdated) + if (filtersUpdated()) return; - d->filtersUpdated = true; + setFiltersUpdated(true); clearFilter(); - d->filters.setDefaultFilter(filterWidget()->defaultFilter()); - setFilter(d->filters.toString(KexiFileFilters::KDEFormat)); + filters()->setDefaultFilter(filterWidget()->defaultFilter()); + setFilter(filters()->toString(KexiFileFilters::KDEFormat)); - if (d->filters.mode() == KexiFileFilters::Opening || d->filters.mode() == KexiFileFilters::CustomOpening) { + if (filters()->mode() == KexiFileFilters::Opening || filters()->mode() == KexiFileFilters::CustomOpening) { KFileWidget::setMode(KFile::ExistingOnly | KFile::LocalOnly | KFile::File); setOperationMode(KFileWidget::Opening); } else { KFileWidget::setMode(KFile::LocalOnly | KFile::File); setOperationMode(KFileWidget::Saving); } } void KexiFileWidget::showEvent(QShowEvent * event) { - d->filtersUpdated = false; + setFiltersUpdated(false); updateFilters(); KFileWidget::showEvent(event); } /*TODO QString KexiFileWidget::selectedFile() const { #ifdef Q_OS_WIN // QString path = selectedFile(); //js @todo // qDebug() << "selectedFile() == " << path << " '" << url().fileName() << "' " << m_lineEdit->text(); QString path = dir()->absolutePath(); if (!path.endsWith('/') && !path.endsWith("\\")) path.append("/"); path += m_lineEdit->text(); // QString path = QFileInfo(selectedFile()).dirPath(true) + "/" + m_lineEdit->text(); #else // QString path = locationEdit->currentText().trimmed(); //url.path().trimmed(); that does not work, if the full path is not in the location edit !!!!! QString path( KFileWidget::selectedFile() ); qDebug() << "prev selectedFile() == " << path; qDebug() << "locationEdit == " << locationEdit()->currentText().trimmed(); //make sure user-entered path is acceped: //! @todo KEXI3 setSelection( locationEdit()->currentText().trimmed() ); // path = KFileWidget::selectedFile(); path = locationEdit()->currentText().trimmed(); qDebug() << "selectedFile() == " << path; #endif if (!currentFilter().isEmpty()) { if (d->mode & SavingFileBasedDB) { const QStringList filters( currentFilter().split(' ') ); qDebug()<< " filter == " << filters; QString ext( QFileInfo(path).suffix() ); bool hasExtension = false; foreach (const QString& filter, filters) { const QString f( filter.trimmed() ); hasExtension = !f.mid(2).isEmpty() && ext==f.mid(2); if (hasExtension) break; } if (!hasExtension) { //no extension: add one QString defaultExtension( d->defaultExtension ); if (defaultExtension.isEmpty()) defaultExtension = filters.first().trimmed().mid(2); //first one path += (QString(".")+defaultExtension); qDebug() << "KexiFileWidget::checkURL(): append extension, " << path; } } } qDebug() << "KexiFileWidget::currentFileName() == " << path; return path; } */ -bool KexiFileWidget::checkSelectedFile() +QString KexiFileWidget::selectedFile() const { - qDebug() << "d->highlightedUrl: " << d->highlightedUrl; - - if (!locationEdit()->lineEdit()->text().isEmpty()) { - qDebug() << locationEdit()->lineEdit()->text(); - qDebug() << locationEdit()->urls(); - qDebug() << baseUrl(); - - d->highlightedUrl = baseUrl(); - const QString firstUrl(locationEdit()->lineEdit()->text()); // FIXME: find first... - if (QDir::isAbsolutePath(firstUrl)) { - d->highlightedUrl = QUrl::fromLocalFile(firstUrl); - } else { - d->highlightedUrl = d->highlightedUrl.adjusted(QUrl::StripTrailingSlash); - d->highlightedUrl.setPath(d->highlightedUrl.path() + '/' + firstUrl); - } - } + return KFileWidget::selectedFile(); +} - qDebug() << "d->highlightedUrl: " << d->highlightedUrl; - if (d->highlightedUrl.isEmpty()) { - KMessageBox::error(this, xi18n("Enter a filename.")); - return false; - } +QString KexiFileWidget::highlightedFile() const +{ + return d->highlightedUrl.toLocalFile(); +} - if (!currentFilter().isEmpty()) { - if (d->filters.mode() == KexiFileFilters::SavingFileBasedDB || d->filters.mode() == KexiFileFilters::CustomSavingFileBasedDB) { - const QStringList filters( currentFilter().split(' ') ); - QString path = highlightedFile(); - qDebug()<< "filter:" << filters << "path:" << path; - QString ext( QFileInfo(path).suffix() ); - bool hasExtension = false; - foreach (const QString& filter, filters) { - const QString f( filter.trimmed() ); - hasExtension = !f.midRef(2).isEmpty() && ext==f.midRef(2); - if (hasExtension) { - break; - } - } - if (!hasExtension) { - //no extension: add one - QString defaultExtension( d->defaultExtension ); - if (defaultExtension.isEmpty()) { - defaultExtension = filters.first().trimmed().mid(2); //first one - } - path += (QLatin1String(".")+defaultExtension); - qDebug() << "appended extension" << path; - setSelection( path ); - d->highlightedUrl = QUrl(path); - } - } - } +void KexiFileWidget::setSelectedFile(const QString &name) +{ + KFileWidget::setSelection(name); +} - qDebug() << "KexiFileWidget::checkURL() path: " << d->highlightedUrl; -// qDebug() << "KexiFileWidget::checkURL() fname: " << url.fileName(); -//! @todo if ( url.isLocalFile() ) { - QFileInfo fi(d->highlightedUrl.toLocalFile()); - if (KFileWidget::mode() & KFile::ExistingOnly) { - if (!fi.exists()) { - KMessageBox::error(this, - xi18nc("@info", "The file %1 does not exist.", - QDir::toNativeSeparators(d->highlightedUrl.toLocalFile()))); - return false; - } else if (KFileWidget::mode() & KFile::File) { - if (!fi.isFile()) { - KMessageBox::error(this, xi18nc("@info", "Enter a filename.")); - return false; - } else if (!fi.isReadable()) { - KMessageBox::error(this, - xi18nc("@info", "The file %1 is not readable.", - QDir::toNativeSeparators(d->highlightedUrl.path()))); - return false; - } - } - } else if (d->confirmOverwrites && !KexiUtils::askForFileOverwriting(d->highlightedUrl.path(), this)) { - return false; +QString KexiFileWidget::currentDir() const +{ + return KFileWidget::baseUrl().toLocalFile(); +} + +void KexiFileWidget::applyEnteredFileName() +{ + const QString enteredFileName(locationEdit()->lineEdit()->text()); + if (enteredFileName.isEmpty()) { + return; + } + qDebug() << enteredFileName; + qDebug() << locationEdit()->urls(); + qDebug() << currentDir(); + + setSelectedFile(currentDir()); + // FIXME: find first... + if (QDir::isAbsolutePath(enteredFileName)) { + setSelectedFile(enteredFileName); + } else { + setSelectedFile(currentDir() + '/' + enteredFileName); } - return true; +} + +QStringList KexiFileWidget::currentFilters() const +{ + return currentFilter().split(QLatin1Char(' ')); } void KexiFileWidget::accept() { - qDebug() << "KexiFileWidget::accept()..."; + qDebug(); KFileWidget::accept(); // qDebug() << selectedFile(); // locationEdit->setFocus(); // QKeyEvent ev(QEvent::KeyPress, Qt::Key_Enter, '\n', 0); // QApplication::sendEvent(locationEdit, &ev); // QApplication::postEvent(locationEdit, &ev); // qDebug() << "KexiFileWidget::accept() m_lastUrl == " << m_lastUrl.path(); // if (m_lastUrl.path()==currentURL().path()) {//(js) to prevent more multiple kjob signals (I do not know why this is) /* if (d->lastFileName==selectedFile()) {//(js) to prevent more multiple kjob signals (I do not know why this is) // m_lastUrl=QUrl(); d->lastFileName.clear(); qDebug() << "d->lastFileName==selectedFile()"; #ifdef Q_OS_WIN return; #endif } qDebug() << "KexiFileWidget::accept(): path = " << selectedFile(); if ( checkSelectedFile() ) { emit accepted(); } d->lastFileName = selectedFile(); #ifdef Q_OS_WIN saveLastVisitedPath(d->lastFileName); #endif*/ } void KexiFileWidget::reject() { - qDebug() << "KexiFileWidget: reject!"; + qDebug(); emit rejected(); } -void KexiFileWidget::setLocationText(const QString& fn) +#if 0 // TODO? +void KexiFileWidget::setLocationText(const QString& text) { - locationEdit()->setUrl(QUrl(fn)); + locationEdit()->setUrl(QUrl(text)); /* #ifdef Q_OS_WIN //js @todo setSelection(fn); #else setSelection(fn); // locationEdit->setCurrentText(fn); // locationEdit->lineEdit()->setEdited( true ); // setSelection(fn); #endif*/ } +#endif -void KexiFileWidget::focusInEvent(QFocusEvent *) +void KexiFileWidget::focusInEvent(QFocusEvent *event) { + Q_UNUSED(event) locationEdit()->setFocus(); } /*bool KexiFileWidget::eventFilter ( QObject * watched, QEvent * e ) { //filter-out ESC key if (e->type()==QEvent::KeyPress && static_cast(e)->key()==Qt::Key_Escape && static_cast(e)->state()==Qt::NoButton) { static_cast(e)->accept(); emit rejected(); return true; } return KexiFileWidgetBase::eventFilter(watched,e); } */ -void KexiFileWidget::setDefaultExtension(const QString& ext) -{ - d->defaultExtension = ext; -} - -void KexiFileWidget::setConfirmOverwrites(bool set) +void KexiFileWidget::setWidgetFrame(bool set) { - d->confirmOverwrites = set; + Q_UNUSED(set) } diff --git a/src/widget/KexiFileWidget.h b/src/widget/KexiFileWidget.h index d1ee8187f..3459ec37e 100644 --- a/src/widget/KexiFileWidget.h +++ b/src/widget/KexiFileWidget.h @@ -1,109 +1,114 @@ /* This file is part of the KDE project - Copyright (C) 2003-2016 Jarosław Staniek + Copyright (C) 2003-2017 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXIFILEWIDGET_H #define KEXIFILEWIDGET_H #include -#include "KexiStartupFileHandler.h" #ifdef KEXI_USE_KFILEWIDGET +#include "KexiFileWidgetInterface.h" #include -#include //! @short Widget for opening/saving files supported by Kexi /*! For simplicity, initially the widget has hidden the preview pane. */ -class KEXIEXTWIDGETS_EXPORT KexiFileWidget : public KFileWidget +class KEXIEXTWIDGETS_EXPORT KexiFileWidget : public KFileWidget, public KexiFileWidgetInterface { Q_OBJECT public: //! @todo KEXI3 add equivalent of kfiledialog:/// for startDirOrVariable - KexiFileWidget( - const QUrl &startDirOrVariable, KexiFileFilters::Mode mode, QWidget *parent); + KexiFileWidget(const QUrl &startDirOrVariable, KexiFileFilters::Mode mode, + QWidget *parent = nullptr); - virtual ~KexiFileWidget(); + ~KexiFileWidget() override; using KFileWidget::setMode; - KexiFileFilters::Mode mode() const; + /** + * Returns the full path of the selected file in the local filesystem. + * (Local files only) + */ + QString selectedFile() const override; - void setMode(KexiFileFilters::Mode mode); - - //! @return additional mime types - QStringList additionalMimeTypes() const; - - //! Sets additional mime types, e.g. "text/x-csv" - void setAdditionalMimeTypes(const QStringList &mimeTypes); - - //! @return excluded mime types - QStringList excludedMimeTypes() const; - - //! Set excluded mime types - void setExcludedMimeTypes(const QStringList &mimeTypes); + /** + * Returns the full path of the highlighted file in the local filesystem. + * (Local files only) + */ + QString highlightedFile() const override; - //! @return selected file. - //! @note Call checkSelectedFile() first - virtual QString highlightedFile() const; + /** + * @return the currently shown directory. + */ + QString currentDir() const override; - //! just sets locationWidget()->setCurrentText(fn) - //! (and something similar on win32) - void setLocationText(const QString& fn); - - //! Sets default extension which will be added after accepting - //! if user didn't provided one. This method is usable when there is - //! more than one filter so there is no rule what extension should be selected - //! (by default first one is selected). - void setDefaultExtension(const QString& ext); +public Q_SLOTS: + void setMode(KexiFileFilters::Mode mode); - /*! \return true if the current URL meets requies constraints - (i.e. exists or doesn't exist); - shows appropriate message box if needed. */ - bool checkSelectedFile(); + //! Just sets locationWidget()->setCurrentText(text) + //void setLocationText(const QString& text) override; - /*! If true, user will be asked to accept overwriting existing file. - This is true by default. */ - void setConfirmOverwrites(bool set); - -public Q_SLOTS: - virtual void showEvent(QShowEvent * event); - virtual void focusInEvent(QFocusEvent *); + /** + * Sets the file name to preselect to @p name + * + * This takes absolute URLs and relative file names. + */ + void setSelectedFile(const QString &name) override; //! Typing a file that doesn't exist closes the file dialog, we have to //! handle this case better here. virtual void accept(); + /** + * Sets whether the line edit draws itself with a frame. + */ + void setWidgetFrame(bool set) override; + Q_SIGNALS: - void fileHighlighted(); + void fileHighlighted(const QString &name); + void fileSelected(const QString &name); void rejected(); protected Q_SLOTS: virtual void reject(); - void slotExistingFileHighlighted(const QUrl& url); + void slotFileHighlighted(const QUrl& url); + void slotFileSelected(const QUrl& url); -private: - void updateFilters(); +protected: + virtual void showEvent(QShowEvent *event); + + virtual void focusInEvent(QFocusEvent *event); + /** + * Updates filters in the widget based on current filter selection. + */ + void updateFilters() override; + + void applyEnteredFileName() override; + + QStringList currentFilters() const override; + +private: class Private; Private * const d; }; #endif // KEXI_USE_KFILEWIDGET #endif // KEXIFILEWIDGET_H diff --git a/src/widget/KexiFileWidgetInterface.cpp b/src/widget/KexiFileWidgetInterface.cpp new file mode 100644 index 000000000..cd40b3bff --- /dev/null +++ b/src/widget/KexiFileWidgetInterface.cpp @@ -0,0 +1,281 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2017 Jarosław Staniek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "KexiFileWidgetInterface.h" +#include +#include +#include +#include +#include +#include "KexiFileWidget.h" +#include "KexiFileRequester.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//! @internal +class Q_DECL_HIDDEN KexiFileWidgetInterface::Private +{ +public: + Private() + { + } + + QUrl startUrl; + KexiFileFilters filters; + QString defaultExtension; + bool confirmOverwrites = true; + bool filtersUpdated = false; + QString highlightedName; + QString recentDirClass; +}; + +//------------------ + +KexiFileWidgetInterface::KexiFileWidgetInterface(const QUrl &startDirOrVariable) + : d(new Private) +{ + if (startDirOrVariable.scheme() == "kfiledialog") { +//! @todo Make it independent of KIOFileWidgets + d->startUrl = KFileWidget::getStartUrl(startDirOrVariable, d->recentDirClass); + } else { + d->startUrl = startDirOrVariable; + } +} + +KexiFileWidgetInterface::~KexiFileWidgetInterface() +{ + delete d; +} + +QUrl KexiFileWidgetInterface::startUrl() const +{ + return d->startUrl; +} + +void KexiFileWidgetInterface::addRecentDir(const QString &name) +{ + if (!d->recentDirClass.isEmpty() && QDir(name).exists()) { + //! @todo Make it independent of KIOFileWidgets + KRecentDirs::add(d->recentDirClass, name); + } +} + +void KexiFileWidgetInterface::done() +{ + qDebug() << d->recentDirClass; + if (!d->recentDirClass.isEmpty()) { + QString hf = highlightedFile(); + QString dir; + if (hf.isEmpty()) { + dir = currentDir(); + } + else { + QFileInfo fi(hf); + QString dirStr = fi.isDir() ? fi.absoluteFilePath() : fi.dir().absolutePath(); + dir = dirStr; + } + qDebug() << dir; + qDebug() << highlightedFile(); + addRecentDir(dir); + } +} + +// static +KexiFileWidgetInterface *KexiFileWidgetInterface::createWidget(const QUrl &startDirOrVariable, + KexiFileFilters::Mode mode, + QWidget *parent) +{ +#ifdef KEXI_USE_KFILEWIDGET + //! @todo allow to set option to use KexiFileWidget outside of the KDE session + if (KexiUtils::isKDEDesktopSession()) { + return new KexiFileWidget(startDirOrVariable, mode, parent); + } +#endif + return new KexiFileRequester(startDirOrVariable, mode, parent); +} + +KexiFileFilters::Mode KexiFileWidgetInterface::mode() const +{ + return d->filters.mode(); +} + +void KexiFileWidgetInterface::setMode(KexiFileFilters::Mode mode) +{ + //delayed + d->filters.setMode(mode); + d->filtersUpdated = false; + updateFilters(); +} + +QStringList KexiFileWidgetInterface::additionalMimeTypes() const +{ + return d->filters.additionalMimeTypes(); +} + +void KexiFileWidgetInterface::setAdditionalMimeTypes(const QStringList &mimeTypes) +{ + d->filters.setAdditionalMimeTypes(mimeTypes); + d->filtersUpdated = false; +} + +QStringList KexiFileWidgetInterface::excludedMimeTypes() const +{ + return d->filters.excludedMimeTypes(); +} + +void KexiFileWidgetInterface::setExcludedMimeTypes(const QStringList &mimeTypes) +{ + d->filters.setExcludedMimeTypes(mimeTypes); + d->filtersUpdated = false; +} + +QString KexiFileWidgetInterface::defaultExtension() const +{ + return d->defaultExtension; +} + +void KexiFileWidgetInterface::setDefaultExtension(const QString& ext) +{ + d->defaultExtension = ext; +} + +bool KexiFileWidgetInterface::confirmOverwrites() const +{ + return d->confirmOverwrites; +} + +void KexiFileWidgetInterface::setConfirmOverwrites(bool set) +{ + d->confirmOverwrites = set; +} + +QString KexiFileWidgetInterface::currentDir() const +{ + qFatal("Implement it"); + return QString(); +} + +void KexiFileWidgetInterface::setFiltersUpdated(bool set) +{ + d->filtersUpdated = set; +} + +bool KexiFileWidgetInterface::filtersUpdated() const +{ + return d->filtersUpdated; +} + +KexiFileFilters* KexiFileWidgetInterface::filters() +{ + return &d->filters; +} + +void KexiFileWidgetInterface::connectFileHighlightedSignal(QObject *receiver, const char *slot) +{ + QObject::connect(widget(), SIGNAL(fileHighlighted(QString)), receiver, slot); +} + +void KexiFileWidgetInterface::connectFileSelectedSignal(QObject *receiver, const char *slot) +{ + QObject::connect(widget(), SIGNAL(fileSelected(QString)), receiver, slot); +} + +bool KexiFileWidgetInterface::checkSelectedFile() +{ + qDebug() << "selectedFile:" << selectedFile(); + + applyEnteredFileName(); + + qDebug() << "selectedFile after applyEnteredFileName():" << selectedFile(); + + if (selectedFile().isEmpty()) { + KMessageBox::error(widget(), xi18n("Enter a filename.")); + return false; + } + + if (filters()->mode() == KexiFileFilters::SavingFileBasedDB || filters()->mode() == KexiFileFilters::CustomSavingFileBasedDB) { + const QStringList currentFilters(this->currentFilters()); + if (!currentFilters.isEmpty()) { + QString path = selectedFile(); + qDebug()<< "filter:" << currentFilters << "path:" << path; + QString ext(QFileInfo(path).suffix()); + bool hasExtension = false; + for (const QString &filter : currentFilters) { + const QString f(filter.trimmed()); + hasExtension = !f.midRef(2).isEmpty() && ext == f.midRef(2); + if (hasExtension) { + break; + } + } + if (!hasExtension) { + //no extension: add one + QString ext(defaultExtension()); + if (ext.isEmpty()) { + ext = currentFilters.first().trimmed().mid(2); //first one + } + path += (QLatin1String(".") + ext); + qDebug() << "appended extension:" << path; + setSelectedFile(path); + } + qDebug() << "selectedFile after applying extension:" << selectedFile(); + } + } + + if (filters()->isExistingFileRequired()) { + QFileInfo fi(selectedFile()); + if (!fi.exists()) { + KMessageBox::error(widget(), + xi18nc("@info", "The file %1 does not exist.", + QDir::toNativeSeparators(fi.absoluteFilePath()))); + return false; + } + if (!fi.isFile()) { + KMessageBox::error(widget(), xi18nc("@info", "Enter a filename.")); + return false; + } + if (!fi.isReadable()) { + KMessageBox::error(widget(), + xi18nc("@info", "The file %1 is not readable.", + QDir::toNativeSeparators(fi.absoluteFilePath()))); + return false; + } + } else if (confirmOverwrites() && !KexiUtils::askForFileOverwriting(selectedFile(), widget())) { + return false; + } + return true; +} diff --git a/src/widget/KexiFileWidgetInterface.h b/src/widget/KexiFileWidgetInterface.h new file mode 100644 index 000000000..34377e45b --- /dev/null +++ b/src/widget/KexiFileWidgetInterface.h @@ -0,0 +1,176 @@ +/* This file is part of the KDE project + Copyright (C) 2003-2017 Jarosław Staniek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef KEXIFILEWIDGETINTERFACE_H +#define KEXIFILEWIDGETINTERFACE_H + +#include "kexiextwidgets_export.h" +#include +#include + +//! @brief An interface for file widget supporting opening/saving files known by Kexi +class KEXIEXTWIDGETS_EXPORT KexiFileWidgetInterface +{ +public: + virtual ~KexiFileWidgetInterface(); + + /** + * @brief Creates a file widget + * @param startDirOrVariable A URL specifying the initial directory and/or filename, + * or using the @c kfiledialog:/// syntax to specify a last used location. + * Refer to the KFileWidget::KFileWidget() documentation + * for the @c kfiledialog:/// URL syntax. + * @param mode File widget's mode + * @param parent File widget's parent widget + * + * Depending on settings one of two file widget implementations is used: + * - if KEXI_USE_KFILEWIDGET build option is on and KDE Plasma desktop is detected as the current + * desktop, KF5's KFileWidget-based widget is created + * - if KEXI_USE_KFILEWIDGET build option is off or if non-KDE Plasma desktop is detected + * as the current desktop, a simple KexiFileRequester widget is used. + * @return the new file widget. + */ + static KexiFileWidgetInterface *createWidget(const QUrl &startDirOrVariable, + KexiFileFilters::Mode mode, + QWidget *parent = nullptr) Q_REQUIRED_RESULT; + + QWidget *widget() { return dynamic_cast(this); } + + //! @return mode for filters used in this widget + KexiFileFilters::Mode mode() const; + + //! Sets mode for filters to be used in this widget + void setMode(KexiFileFilters::Mode mode); + + //! @return additional mime types + QStringList additionalMimeTypes() const; + + //! Sets additional mime types, e.g. "text/x-csv" + void setAdditionalMimeTypes(const QStringList &mimeTypes); + + //! @return excluded mime types + QStringList excludedMimeTypes() const; + + //! Set excluded mime types + void setExcludedMimeTypes(const QStringList &mimeTypes); + + /** + * Returns the full path of the selected file in the local filesystem. + * (Local files only) + */ + virtual QString selectedFile() const = 0; + + /** + * @brief Sets the file name to preselect to @p name + * + * This takes absolute URLs and relative file names. + */ + virtual void setSelectedFile(const QString &name) = 0; + + /** + * @brief Returns @c true if the current URL meets requied constraints, e.g. exists + * + * Shows appropriate message box if needed. + */ + bool checkSelectedFile(); + + /** + * @brief Returns the full path of the highlighted file in the local filesystem + * + * (Local files only) + */ + virtual QString highlightedFile() const = 0; + + //! Sets location text + //! @todo + //virtual void setLocationText(const QString& text) = 0; + + //! @return default extension + QString defaultExtension() const; + + /** + * @brief Sets default extension which will be added after accepting if user didn't provided one + * This method is usable when there is more than one filter so there is no rule what extension + * should be selected. By default first one is selected. + */ + void setDefaultExtension(const QString& ext); + + bool confirmOverwrites() const; + + /*! If true, user will be asked to accept overwriting existing file. + This is true by default. */ + void setConfirmOverwrites(bool set); + + /** + * Sets whether the line edit draws itself with a frame. + */ + virtual void setWidgetFrame(bool set) = 0; + + /** + * @returns the currently shown directory. + * Reimplement it. + */ + virtual QString currentDir() const; + + /** + * @brief Connects "file hightlighted" signal to specific receiver + * + * Connects "fileHighlighted(QString)" signal of widget's returned by widget() to + * @a receiver and @a slot. + */ + void connectFileHighlightedSignal(QObject *receiver, const char *slot); + + /** + * @brief Connects "file selected" signal to specific receiver + * + * Connects "fileSelected(QString)" signal of widget's returned by widget() to + * @a receiver and @a slot. + */ + void connectFileSelectedSignal(QObject *receiver, const char *slot); + +protected: + KexiFileWidgetInterface(const QUrl &startDirOrVariable); + + /** + * Updates filters in the widget based on current filter selection. + */ + virtual void updateFilters() = 0; + + virtual void applyEnteredFileName() = 0; + + virtual QStringList currentFilters() const = 0; + + QUrl startUrl() const; + + void addRecentDir(const QString &name); + + KexiFileFilters* filters(); + + void setFiltersUpdated(bool set); + + bool filtersUpdated() const; + + void done(); + +private: + class Private; + Private * const d; +}; + +#endif // KEXIFILEWIDGETINTERFACE_H