From 7167ce41b61d2ba2cdb526777a4233eb84a3b66a Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Sat, 6 Dec 2014 17:33:25 -0500 Subject: Imported Upstream version 2.99.6 --- .../guiSQLiteStudio/dialogs/aboutdialog.cpp | 94 + .../guiSQLiteStudio/dialogs/aboutdialog.h | 37 + .../guiSQLiteStudio/dialogs/aboutdialog.ui | 109 ++ .../guiSQLiteStudio/dialogs/bugdialog.cpp | 220 +++ SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.h | 42 + SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.ui | 208 +++ .../dialogs/bugreportlogindialog.cpp | 94 + .../guiSQLiteStudio/dialogs/bugreportlogindialog.h | 40 + .../dialogs/bugreportlogindialog.ui | 132 ++ .../guiSQLiteStudio/dialogs/columndialog.cpp | 616 +++++++ .../guiSQLiteStudio/dialogs/columndialog.h | 113 ++ .../guiSQLiteStudio/dialogs/columndialog.ui | 348 ++++ .../dialogs/columndialogconstraintsmodel.cpp | 335 ++++ .../dialogs/columndialogconstraintsmodel.h | 58 + .../guiSQLiteStudio/dialogs/configdialog.cpp | 1529 ++++++++++++++++ .../guiSQLiteStudio/dialogs/configdialog.h | 141 ++ .../guiSQLiteStudio/dialogs/configdialog.ui | 1923 ++++++++++++++++++++ .../guiSQLiteStudio/dialogs/constraintdialog.cpp | 213 +++ .../guiSQLiteStudio/dialogs/constraintdialog.h | 79 + .../guiSQLiteStudio/dialogs/constraintdialog.ui | 113 ++ .../guiSQLiteStudio/dialogs/dbconverterdialog.cpp | 219 +++ .../guiSQLiteStudio/dialogs/dbconverterdialog.h | 52 + .../guiSQLiteStudio/dialogs/dbconverterdialog.ui | 144 ++ SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp | 590 ++++++ SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h | 89 + SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui | 236 +++ .../guiSQLiteStudio/dialogs/ddlpreviewdialog.cpp | 58 + .../guiSQLiteStudio/dialogs/ddlpreviewdialog.h | 35 + .../guiSQLiteStudio/dialogs/ddlpreviewdialog.ui | 106 ++ .../dialogs/errorsconfirmdialog.cpp | 47 + .../guiSQLiteStudio/dialogs/errorsconfirmdialog.h | 28 + .../guiSQLiteStudio/dialogs/errorsconfirmdialog.ui | 85 + .../guiSQLiteStudio/dialogs/exportdialog.cpp | 737 ++++++++ .../guiSQLiteStudio/dialogs/exportdialog.h | 105 ++ .../guiSQLiteStudio/dialogs/exportdialog.ui | 438 +++++ .../guiSQLiteStudio/dialogs/importdialog.cpp | 376 ++++ .../guiSQLiteStudio/dialogs/importdialog.h | 69 + .../guiSQLiteStudio/dialogs/importdialog.ui | 230 +++ .../guiSQLiteStudio/dialogs/indexdialog.cpp | 468 +++++ .../guiSQLiteStudio/dialogs/indexdialog.h | 72 + .../guiSQLiteStudio/dialogs/indexdialog.ui | 195 ++ .../guiSQLiteStudio/dialogs/messagelistdialog.cpp | 97 + .../guiSQLiteStudio/dialogs/messagelistdialog.h | 37 + .../guiSQLiteStudio/dialogs/messagelistdialog.ui | 84 + .../dialogs/newconstraintdialog.cpp | 275 +++ .../guiSQLiteStudio/dialogs/newconstraintdialog.h | 65 + .../guiSQLiteStudio/dialogs/newconstraintdialog.ui | 75 + .../guiSQLiteStudio/dialogs/newversiondialog.cpp | 68 + .../guiSQLiteStudio/dialogs/newversiondialog.h | 34 + .../guiSQLiteStudio/dialogs/newversiondialog.ui | 144 ++ .../dialogs/populateconfigdialog.cpp | 119 ++ .../guiSQLiteStudio/dialogs/populateconfigdialog.h | 47 + .../dialogs/populateconfigdialog.ui | 99 + .../guiSQLiteStudio/dialogs/populatedialog.cpp | 332 ++++ .../guiSQLiteStudio/dialogs/populatedialog.h | 76 + .../guiSQLiteStudio/dialogs/populatedialog.ui | 158 ++ .../guiSQLiteStudio/dialogs/quitconfirmdialog.cpp | 42 + .../guiSQLiteStudio/dialogs/quitconfirmdialog.h | 28 + .../guiSQLiteStudio/dialogs/quitconfirmdialog.ui | 83 + .../guiSQLiteStudio/dialogs/searchtextdialog.cpp | 75 + .../guiSQLiteStudio/dialogs/searchtextdialog.h | 39 + .../guiSQLiteStudio/dialogs/searchtextdialog.ui | 153 ++ .../guiSQLiteStudio/dialogs/sortdialog.cpp | 248 +++ SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.h | 60 + .../guiSQLiteStudio/dialogs/sortdialog.ui | 112 ++ .../dialogs/triggercolumnsdialog.cpp | 52 + .../guiSQLiteStudio/dialogs/triggercolumnsdialog.h | 33 + .../dialogs/triggercolumnsdialog.ui | 117 ++ .../guiSQLiteStudio/dialogs/triggerdialog.cpp | 413 +++++ .../guiSQLiteStudio/dialogs/triggerdialog.h | 62 + .../guiSQLiteStudio/dialogs/triggerdialog.ui | 215 +++ .../dialogs/versionconvertsummarydialog.cpp | 31 + .../dialogs/versionconvertsummarydialog.h | 28 + .../dialogs/versionconvertsummarydialog.ui | 91 + 74 files changed, 14415 insertions(+) create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.ui (limited to 'SQLiteStudio3/guiSQLiteStudio/dialogs') diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp new file mode 100644 index 0000000..df790de --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp @@ -0,0 +1,94 @@ +#include "aboutdialog.h" +#include "ui_aboutdialog.h" +#include "common/utils.h" +#include "sqlitestudio.h" +#include "iconmanager.h" +#include "services/extralicensemanager.h" +#include +#include + +AboutDialog::AboutDialog(InitialMode initialMode, QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + init(initialMode); +} + +AboutDialog::~AboutDialog() +{ + delete ui; +} + +void AboutDialog::init(InitialMode initialMode) +{ + ui->setupUi(this); + ui->leftIcon->setPixmap(ICONS.SQLITESTUDIO_APP.toQIcon().pixmap(200, 200)); + + ui->tabWidget->setCurrentWidget(initialMode == ABOUT ? ui->about : ui->license); + + QString distName; + switch (getDistributionType()) + { + case DistributionType::PORTABLE: + distName = tr("Portable distribution."); + break; + case DistributionType::OSX_BOUNDLE: + distName = tr("MacOS X application boundle distribution."); + break; + case DistributionType::OS_MANAGED: + distName = tr("Operating system managed distribution."); + break; + } + + QString newLabelValue = ui->aboutLabel->text().arg(SQLITESTUDIO->getVersionString(), distName); + ui->aboutLabel->setText(newLabelValue); + + licenseContents = ""; + int row = 1; + + QHash licenses = SQLITESTUDIO->getExtraLicenseManager()->getLicenses(); + QHashIterator it(licenses); + while (it.hasNext()) + { + it.next(); + readLicense(row++, it.key(), it.value()); + } + + buildIndex(); + + ui->licenseEdit->setHtml(licenseContents); + indexContents.clear(); + licenseContents.clear(); +} + +void AboutDialog::buildIndex() +{ + static const QString entryTpl = QStringLiteral("
  • %1
  • "); + QStringList entries; + for (const QString& idx : indexContents) + entries += entryTpl.arg(idx); + + licenseContents.prepend("

    Table of contents:

      " + entries.join("") + "
    "); +} + +void AboutDialog::readLicense(int row, const QString& title, const QString& path) +{ + QString rowNum = QString::number(row); + QString contents = readFile(path); + licenseContents += "

    " + rowNum + ". " + title + "

    "; + licenseContents += "
    " + contents + "
    "; + indexContents += title; +} + +QString AboutDialog::readFile(const QString& path) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + { + qCritical() << "Error opening" << file.fileName(); + return QString::null; + } + QString contents = QString::fromLatin1(file.readAll()).toHtmlEscaped(); + file.close(); + return contents; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h new file mode 100644 index 0000000..3c828c0 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.h @@ -0,0 +1,37 @@ +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include +#include + +namespace Ui { + class AboutDialog; +} + +class GUI_API_EXPORT AboutDialog : public QDialog +{ + Q_OBJECT + + public: + enum InitialMode + { + ABOUT, + LICENSES + }; + + AboutDialog(InitialMode initialMode, QWidget *parent = 0); + ~AboutDialog(); + + private: + void init(InitialMode initialMode); + void buildIndex(); + void readLicense(int row, const QString& title, const QString& path); + QString readFile(const QString& path); + + Ui::AboutDialog *ui = nullptr; + QStringList indexContents; + QString licenseContents; +}; + +#endif // ABOUTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui new file mode 100644 index 0000000..67fa632 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.ui @@ -0,0 +1,109 @@ + + + AboutDialog + + + + 0 + 0 + 741 + 447 + + + + About SQLiteStudio and licenses + + + + + + 1 + + + + About + + + + + + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> + + + true + + + + + + + + Licenses + + + + + + true + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.cpp new file mode 100644 index 0000000..8f5d433 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.cpp @@ -0,0 +1,220 @@ +#include "bugdialog.h" +#include "ui_bugdialog.h" +#include "iconmanager.h" +#include "uiutils.h" +#include "common/utils.h" +#include "sqlitestudio.h" +#include "mainwindow.h" +#include "bugreportlogindialog.h" +#include "services/pluginmanager.h" +#include "services/bugreporter.h" +#include "services/notifymanager.h" +#include +#include +#include + +BugDialog::BugDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::BugDialog) +{ + init(); +} + +BugDialog::~BugDialog() +{ + delete ui; +} + +void BugDialog::setFeatureRequestMode(bool feature) +{ + bugMode = !feature; + updateState(); +} + +void BugDialog::init() +{ + ui->setupUi(this); + resize(width(), height() - 50); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Send")); + + connect(ui->moreDetailsGroup, SIGNAL(toggled(bool)), this, SLOT(updateState())); + connect(ui->shortDescriptionEdit, SIGNAL(textChanged(QString)), this, SLOT(validate())); + connect(ui->longDescriptionEdit, SIGNAL(textChanged()), this, SLOT(validate())); + connect(ui->emailEdit, SIGNAL(textChanged(QString)), this, SLOT(validate())); + connect(ui->helpButton, SIGNAL(clicked()), this, SLOT(help())); + connect(ui->loginButton, SIGNAL(clicked()), this, SLOT(logIn())); + + ui->versionEdit->setText(SQLITESTUDIO->getVersionString()); + ui->osEdit->setText(getOsString()); + ui->pluginsEdit->setText(PLUGINS->getLoadedPluginNames().join(", ")); + + user = CFG_CORE.Internal.BugReportUser.get(); + + if (CFG_CORE.Internal.BugReportRecentError.get()) + { + ui->shortDescriptionEdit->setText(CFG_CORE.Internal.BugReportRecentTitle.get()); + ui->longDescriptionEdit->setPlainText(CFG_CORE.Internal.BugReportRecentContents.get()); + } + + updateState(); + validate(); +} + +QString BugDialog::getMessageAboutReportHistory() +{ + return tr("You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'.").arg(MAINWINDOW->getSQLiteStudioMenu()->title()) + .arg(MAINWINDOW->getAction(MainWindow::BUG_REPORT_HISTORY)->text()); + return ""; +} + +void BugDialog::finishedBugReport(bool success, const QString& errorMsg) +{ + if (success) + { + notifyInfo(tr("A bug report sent successfully.") + " " + getMessageAboutReportHistory()); + } + else + { + CFG_CORE.Internal.BugReportRecentError.set(true); + notifyError(tr("An error occurred while sending a bug report: %1\n%2").arg(errorMsg, + tr("You can retry sending. The contents will be restored when you open a report dialog after an error like this."))); + } +} + +void BugDialog::finishedFeatureRequest(bool success, const QString& errorMsg) +{ + if (success) + { + notifyInfo(tr("An idea proposal sent successfully.") + " " + getMessageAboutReportHistory()); + } + else + { + CFG_CORE.Internal.BugReportRecentError.set(true); + notifyError(tr("An error occurred while sending an idea proposal: %1\n%2").arg(errorMsg, + tr("You can retry sending. The contents will be restored when you open a report dialog after an error like this."))); + } +} + +void BugDialog::updateState() +{ + ui->scrollArea->setVisible(ui->moreDetailsGroup->isChecked()); + + ui->moreDetailsGroup->setVisible(bugMode); + if (bugMode) + { + setWindowTitle(tr("A bug report")); + ui->shortDescriptionEdit->setPlaceholderText(tr("Describe problem in few words")); + ui->longDescriptionEdit->setPlaceholderText(tr("Describe problem and how to reproduce it")); + } + else + { + setWindowTitle(tr("A new feature idea")); + ui->shortDescriptionEdit->setPlaceholderText(tr("A title for your idea")); + ui->longDescriptionEdit->setPlaceholderText(tr("Describe your idea in more details")); + } + + if (user.isNull()) + { + ui->currentLoginLabel->setToolTip(tr("Reporting as an unregistered user, using e-mail address.")); + ui->currentLoginLabel->setPixmap(ICONS.USER_UNKNOWN); + ui->emailEdit->setEnabled(true); + ui->emailEdit->clear(); + ui->loginButton->setText(tr("Log in")); + ui->loginButton->setIcon(ICONS.USER); + } + else + { + ui->currentLoginLabel->setToolTip(tr("Reporting as a registered user.")); + ui->currentLoginLabel->setPixmap(ICONS.USER); + ui->emailEdit->setText(user); + ui->emailEdit->setEnabled(false); + ui->loginButton->setText(tr("Log out")); + ui->loginButton->setIcon(ICONS.USER_UNKNOWN); + } +} + +void BugDialog::validate() +{ + bool emailOk = !user.isNull() || validateEmail(ui->emailEdit->text()); + int shortSize = ui->shortDescriptionEdit->text().trimmed().size(); + int longSize = ui->longDescriptionEdit->toPlainText().trimmed().size(); + bool shortOk = shortSize >= 10 && shortSize <= 100; + bool longOk = longSize >= 30; + + setValidStateWihtTooltip(ui->emailEdit, tr("Providing true email address will make it possible to contact you regarding your report. " + "To learn more, press 'help' button on the right side."), + emailOk, tr("Enter vaild e-mail address, or log in.")); + + setValidState(ui->shortDescriptionEdit, shortOk, tr("Short description requires at least 10 characters, but not more than 100. " + "Longer description can be entered in the field below.")); + + setValidState(ui->longDescriptionEdit, longOk, tr("Long description requires at least 30 characters.")); + + bool valid = shortOk && longOk && emailOk; + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); +} + +void BugDialog::help() +{ + if (user.isNull()) + QDesktopServices::openUrl(QUrl(BUGS->getReporterEmailHelpUrl())); + else + QDesktopServices::openUrl(QUrl(BUGS->getReporterUserAndPasswordHelpUrl())); +} + +void BugDialog::logIn() +{ + if (!user.isNull()) + { + // Log out + user = QString(); + updateState(); + BUGS->clearBugReportCredentials(); + return; + } + + BugReportLoginDialog dialog(this); + if (dialog.exec() != QDialog::Accepted) + return; + + if (!dialog.isValid()) + return; + + BUGS->useBugReportCredentials(dialog.getLogin(), dialog.getPassword()); + user = dialog.getLogin(); + updateState(); +} + +void BugDialog::accept() +{ + CFG_CORE.Internal.BugReportRecentError.set(false); + CFG_CORE.Internal.BugReportRecentTitle.set(ui->shortDescriptionEdit->text()); + CFG_CORE.Internal.BugReportRecentContents.set(ui->longDescriptionEdit->toPlainText()); + + if (bugMode) + { + if (user.isNull()) + { + BUGS->reportBug(ui->emailEdit->text(), ui->shortDescriptionEdit->text(), ui->longDescriptionEdit->toPlainText(), ui->versionEdit->text(), + ui->osEdit->text(), ui->pluginsEdit->text(), BugDialog::finishedBugReport); + } + else + { + BUGS->reportBug(ui->shortDescriptionEdit->text(), ui->longDescriptionEdit->toPlainText(), ui->versionEdit->text(), ui->osEdit->text(), ui->pluginsEdit->text(), + BugDialog::finishedFeatureRequest); + } + } + else + { + if (user.isNull()) + { + BUGS->requestFeature(ui->emailEdit->text(), ui->shortDescriptionEdit->text(), ui->longDescriptionEdit->toPlainText(), BugDialog::finishedFeatureRequest); + } + else + { + BUGS->requestFeature(ui->shortDescriptionEdit->text(), ui->longDescriptionEdit->toPlainText(), BugDialog::finishedFeatureRequest); + } + } + QDialog::accept(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.h new file mode 100644 index 0000000..bf60104 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.h @@ -0,0 +1,42 @@ +#ifndef BUGDIALOG_H +#define BUGDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class BugDialog; +} + +class GUI_API_EXPORT BugDialog : public QDialog +{ + Q_OBJECT + + public: + explicit BugDialog(QWidget *parent = 0); + ~BugDialog(); + + void setFeatureRequestMode(bool feature); + + private: + void init(); + + static QString getMessageAboutReportHistory(); + static void finishedBugReport(bool success, const QString& errorMsg); + static void finishedFeatureRequest(bool success, const QString& errorMsg); + + Ui::BugDialog *ui = nullptr; + bool bugMode = true; + QString user; + + private slots: + void updateState(); + void validate(); + void help(); + void logIn(); + + public slots: + void accept(); +}; + +#endif // BUGDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.ui new file mode 100644 index 0000000..f2dbcf3 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugdialog.ui @@ -0,0 +1,208 @@ + + + BugDialog + + + + 0 + 0 + 516 + 421 + + + + Dialog + + + + + + Reporter + + + + + + + + + :/icons/img/user_unknown.png + + + + + + + E-mail address + + + + + + + Log in + + + + :/icons/img/user.png:/icons/img/user.png + + + + + + + ... + + + + :/icons/img/help.png:/icons/img/help.png + + + + + + + + + + Short description + + + + + + + + + + + + Detailed description + + + + + + + + + + + + Show more details + + + true + + + false + + + + + + true + + + + + 0 + 0 + 462 + 209 + + + + + + + SQLiteStudio version + + + + + + + + + + + + Operating system + + + + + + + + + + + + Loaded plugins + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + BugDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + BugDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.cpp new file mode 100644 index 0000000..19727fe --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.cpp @@ -0,0 +1,94 @@ +#include "bugreportlogindialog.h" +#include "ui_bugreportlogindialog.h" +#include "uiutils.h" +#include "services/bugreporter.h" +#include "iconmanager.h" +#include "common/widgetcover.h" +#include + +BugReportLoginDialog::BugReportLoginDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::BugReportLoginDialog) +{ + init(); +} + +BugReportLoginDialog::~BugReportLoginDialog() +{ + delete ui; +} + +bool BugReportLoginDialog::isValid() const +{ + return validCredentials; +} + +QString BugReportLoginDialog::getLogin() const +{ + return ui->loginEdit->text(); +} + +QString BugReportLoginDialog::getPassword() const +{ + return ui->passwordEdit->text(); +} + +void BugReportLoginDialog::init() +{ + ui->setupUi(this); + connect(ui->loginEdit, SIGNAL(textChanged(QString)), this, SLOT(credentialsChanged())); + connect(ui->passwordEdit, SIGNAL(textChanged(QString)), this, SLOT(credentialsChanged())); + connect(ui->validationButton, SIGNAL(clicked()), this, SLOT(remoteValidation())); + connect(BUGS, SIGNAL(credentialsValidationResult(bool,QString)), this, SLOT(remoteValidationResult(bool,QString))); + + widgetCover = new WidgetCover(this); + widgetCover->initWithInterruptContainer(tr("Abort")); + connect(widgetCover, SIGNAL(cancelClicked()), this, SLOT(abortRemoteValidation())); + + validate(); +} + +void BugReportLoginDialog::credentialsChanged() +{ + validCredentials = false; + validate(); +} + +void BugReportLoginDialog::validate() +{ + QString login = ui->loginEdit->text(); + QString pass = ui->passwordEdit->text(); + + bool loginOk = login.size() >= 2; + bool passOk = pass.size() >= 5; + + setValidState(ui->loginEdit, loginOk, tr("A login must be at least 2 characters long.")); + setValidState(ui->passwordEdit, passOk, tr("A password must be at least 5 characters long.")); + + bool credentialsOk = loginOk && passOk; + ui->validationButton->setEnabled(credentialsOk); + ui->validationLabel->setEnabled(credentialsOk); + + bool valid = credentialsOk && validCredentials; + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); +} + +void BugReportLoginDialog::abortRemoteValidation() +{ + BUGS->abortCredentialsValidation(); +} + +void BugReportLoginDialog::remoteValidation() +{ + widgetCover->show(); + BUGS->validateBugReportCredentials(ui->loginEdit->text(), ui->passwordEdit->text()); +} + +void BugReportLoginDialog::remoteValidationResult(bool success, const QString& errorMessage) +{ + validCredentials = success; + ui->validationButton->setIcon(success ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + ui->validationLabel->setText(success ? tr("Valid") : errorMessage); + validate(); + widgetCover->hide(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.h new file mode 100644 index 0000000..131ba3d --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.h @@ -0,0 +1,40 @@ +#ifndef BUGREPORTLOGINDIALOG_H +#define BUGREPORTLOGINDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class BugReportLoginDialog; +} + +class WidgetCover; + +class GUI_API_EXPORT BugReportLoginDialog : public QDialog +{ + Q_OBJECT + + public: + explicit BugReportLoginDialog(QWidget *parent = 0); + ~BugReportLoginDialog(); + + bool isValid() const; + QString getLogin() const; + QString getPassword() const; + + private: + void init(); + + Ui::BugReportLoginDialog *ui = nullptr; + bool validCredentials = false; + WidgetCover* widgetCover = nullptr; + + private slots: + void credentialsChanged(); + void validate(); + void abortRemoteValidation(); + void remoteValidation(); + void remoteValidationResult(bool success, const QString& errorMessage); +}; + +#endif // BUGREPORTLOGINDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.ui new file mode 100644 index 0000000..f6597bc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/bugreportlogindialog.ui @@ -0,0 +1,132 @@ + + + BugReportLoginDialog + + + + 0 + 0 + 343 + 197 + + + + Log in + + + + + + Credentials + + + + + + Login: + + + + + + + + + + Password + + + + + + + QLineEdit::Password + + + + + + + + + + Validation + + + + + + Validate + + + + :/icons/img/test_conn_error.png:/icons/img/test_conn_error.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Validation result message + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + BugReportLoginDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + BugReportLoginDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp new file mode 100644 index 0000000..f4e48fe --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp @@ -0,0 +1,616 @@ +#include "columndialog.h" +#include "common/unused.h" +#include "ui_columndialog.h" +#include "columndialogconstraintsmodel.h" +#include "iconmanager.h" +#include "newconstraintdialog.h" +#include "dialogs/constraintdialog.h" +#include "constraints/constraintpanel.h" +#include "datatype.h" +#include "uiutils.h" +#include +#include +#include +#include +#include + +ColumnDialog::ColumnDialog(Db* db, QWidget *parent) : + QDialog(parent), + ui(new Ui::ColumnDialog), + db(db) +{ + init(); +} + +ColumnDialog::~ColumnDialog() +{ + delete ui; +} + +void ColumnDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + setWindowIcon(ICONS.COLUMN); + + ui->scale->setStrict(true); + ui->precision->setStrict(true); + + ui->typeCombo->addItem(""); + foreach (DataType::Enum type, DataType::getAllTypes()) + ui->typeCombo->addItem(DataType::toString(type)); + + connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateDataType())); + + constraintsModel = new ColumnDialogConstraintsModel(); + ui->constraintsView->setModel(constraintsModel); + initActions(); + + setupConstraintCheckBoxes(); + + connect(ui->advancedCheck, SIGNAL(toggled(bool)), this, SLOT(switchMode(bool))); + + connect(ui->constraintsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateConstraintsToolbarState())); + connect(ui->constraintsView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editConstraint(QModelIndex))); + connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateConstraints())); + connect(constraintsModel, SIGNAL(constraintsChanged()), this, SLOT(updateState())); + + connect(ui->pkButton, SIGNAL(clicked()), this, SLOT(configurePk())); + connect(ui->fkButton, SIGNAL(clicked()), this, SLOT(configureFk())); + connect(ui->checkButton, SIGNAL(clicked()), this, SLOT(configureCheck())); + connect(ui->defaultButton, SIGNAL(clicked()), this, SLOT(configureDefault())); + connect(ui->notNullButton, SIGNAL(clicked()), this, SLOT(configureNotNull())); + connect(ui->collateButton, SIGNAL(clicked()), this, SLOT(configureCollate())); + connect(ui->uniqueButton, SIGNAL(clicked()), this, SLOT(configureUnique())); + + updateState(); +} + +void ColumnDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void ColumnDialog::createActions() +{ + createAction(ADD_CONSTRAINT, ICONS.COLUMN_CONSTRAINT_ADD, tr("Add constraint", "column dialog"), this, SLOT(addConstraint()), ui->constraintsToolbar); + createAction(EDIT_CONSTRAINT, ICONS.COLUMN_CONSTRAINT_EDIT, tr("Edit constraint", "column dialog"), this, SLOT(editConstraint()), ui->constraintsToolbar); + createAction(DEL_CONSTRAINT, ICONS.COLUMN_CONSTRAINT_DEL, tr("Delete constraint", "column dialog"), this, SLOT(delConstraint()), ui->constraintsToolbar); + createAction(MOVE_CONSTRAINT_UP, ICONS.MOVE_UP, tr("Move constraint up", "column dialog"), this, SLOT(moveConstraintUp()), ui->constraintsToolbar); + createAction(MOVE_CONSTRAINT_DOWN, ICONS.MOVE_DOWN, tr("Move constraint down", "column dialog"), this, SLOT(moveConstraintDown()), ui->constraintsToolbar); + ui->constraintsToolbar->addSeparator(); + createAction(ADD_PK, ICONS.CONSTRAINT_PRIMARY_KEY_ADD, tr("Add a primary key", "column dialog"), this, SLOT(addPk()), ui->constraintsToolbar); + createAction(ADD_FK, ICONS.CONSTRAINT_FOREIGN_KEY_ADD, tr("Add a foreign key", "column dialog"), this, SLOT(addFk()), ui->constraintsToolbar); + createAction(ADD_UNIQUE, ICONS.CONSTRAINT_UNIQUE_ADD, tr("Add an unique constraint", "column dialog"), this, SLOT(addUnique()), ui->constraintsToolbar); + createAction(ADD_CHECK, ICONS.CONSTRAINT_CHECK_ADD, tr("Add a check constraint", "column dialog"), this, SLOT(addCheck()), ui->constraintsToolbar); + createAction(ADD_NOT_NULL, ICONS.CONSTRAINT_NOT_NULL_ADD, tr("Add a not null constraint", "column dialog"), this, SLOT(addNotNull()), ui->constraintsToolbar); + createAction(ADD_COLLATE, ICONS.CONSTRAINT_COLLATION_ADD, tr("Add a collate constraint", "column dialog"), this, SLOT(addCollate()), ui->constraintsToolbar); + createAction(ADD_DEFAULT, ICONS.CONSTRAINT_DEFAULT_ADD, tr("Add a default constraint", "column dialog"), this, SLOT(addDefault()), ui->constraintsToolbar); +} + +void ColumnDialog::setupDefShortcuts() +{ +} + +void ColumnDialog::updateConstraintsToolbarState() +{ + QModelIndex idx = ui->constraintsView->selectionModel()->currentIndex(); + bool hasSelected = idx.isValid(); + bool isFirst = false; + bool isLast = false; + if (constraintsModel->rowCount() > 0) + { + isFirst = (idx.row() == 0); + isLast = (idx.row() == (constraintsModel->rowCount() - 1)); + } + + actionMap[EDIT_CONSTRAINT]->setEnabled(hasSelected); + actionMap[DEL_CONSTRAINT]->setEnabled(hasSelected); + actionMap[MOVE_CONSTRAINT_UP]->setEnabled(hasSelected && !isFirst); + actionMap[MOVE_CONSTRAINT_DOWN]->setEnabled(hasSelected && !isLast); +} + +void ColumnDialog::updateState() +{ + ui->pkButton->setEnabled(ui->pkCheck->isChecked()); + ui->fkButton->setEnabled(ui->fkCheck->isChecked()); + ui->uniqueButton->setEnabled(ui->uniqueCheck->isChecked()); + ui->notNullButton->setEnabled(ui->notNullCheck->isChecked()); + ui->checkButton->setEnabled(ui->checkCheck->isChecked()); + ui->collateButton->setEnabled(ui->collateCheck->isChecked()); + ui->defaultButton->setEnabled(ui->defaultCheck->isChecked()); + updateConstraintsToolbarState(); +} + +void ColumnDialog::addConstraint(ConstraintDialog::Constraint mode) +{ + NewConstraintDialog dialog(mode, column.data(), db, this); + if (dialog.exec() != QDialog::Accepted) + return; + + SqliteStatement* constrStmt = dialog.getConstraint(); + SqliteCreateTable::Column::Constraint* constr = dynamic_cast(constrStmt); + if (!constr) + { + qCritical() << "Constraint returned from ConstraintDialog was not of column type, while we're trying to add column constraint."; + return; + } + + constraintsModel->appendConstraint(constr); + ui->constraintsView->resizeColumnToContents(0); + ui->constraintsView->resizeColumnToContents(1); +} + +void ColumnDialog::setupConstraintCheckBoxes() +{ + ui->pkCheck->setIcon(ICONS.CONSTRAINT_PRIMARY_KEY); + ui->fkCheck->setIcon(ICONS.CONSTRAINT_FOREIGN_KEY); + ui->uniqueCheck->setIcon(ICONS.CONSTRAINT_UNIQUE); + ui->notNullCheck->setIcon(ICONS.CONSTRAINT_NOT_NULL); + ui->checkCheck->setIcon(ICONS.CONSTRAINT_CHECK); + ui->collateCheck->setIcon(ICONS.CONSTRAINT_COLLATION); + ui->defaultCheck->setIcon(ICONS.CONSTRAINT_DEFAULT); + + connect(ui->pkCheck, SIGNAL(clicked(bool)), this, SLOT(pkToggled(bool))); + connect(ui->fkCheck, SIGNAL(clicked(bool)), this, SLOT(fkToggled(bool))); + connect(ui->uniqueCheck, SIGNAL(clicked(bool)), this, SLOT(uniqueToggled(bool))); + connect(ui->notNullCheck, SIGNAL(clicked(bool)), this, SLOT(notNullToggled(bool))); + connect(ui->checkCheck, SIGNAL(clicked(bool)), this, SLOT(checkToggled(bool))); + connect(ui->collateCheck, SIGNAL(clicked(bool)), this, SLOT(collateToggled(bool))); + connect(ui->defaultCheck, SIGNAL(clicked(bool)), this, SLOT(defaultToggled(bool))); + + for (QCheckBox* cb : { + ui->pkCheck, + ui->fkCheck, + ui->uniqueCheck, + ui->notNullCheck, + ui->checkCheck, + ui->collateCheck, + ui->defaultCheck + }) + { + connect(cb, SIGNAL(toggled(bool)), this, SLOT(updateState())); + } +} + +void ColumnDialog::addConstraint() +{ + addConstraint(ConstraintDialog::UNKNOWN); +} + +void ColumnDialog::editConstraint() +{ + QModelIndex idx = ui->constraintsView->currentIndex(); + editConstraint(idx); +} + +void ColumnDialog::delConstraint() +{ + QModelIndex idx = ui->constraintsView->currentIndex(); + delConstraint(idx); +} + +void ColumnDialog::editConstraint(const QModelIndex& idx) +{ + if (!idx.isValid()) + return; + + SqliteCreateTable::Column::Constraint* constr = constraintsModel->getConstraint(idx.row()); + editConstraint(constr); +} + +void ColumnDialog::editConstraint(SqliteCreateTable::Column::Constraint* constraint) +{ + ConstraintDialog dialog(ConstraintDialog::EDIT, constraint, column.data(), db, this); + if (dialog.exec() != QDialog::Accepted) + return; + + ui->constraintsView->resizeColumnToContents(0); + ui->constraintsView->resizeColumnToContents(1); + updateConstraints(); +} + +void ColumnDialog::delConstraint(const QModelIndex& idx) +{ + if (!idx.isValid()) + return; + + SqliteCreateTable::Column::Constraint* constr = constraintsModel->getConstraint(idx.row()); + + QString arg = constr->name.isNull() ? constr->typeString() : constr->name; + QString msg = tr("Are you sure you want to delete constraint '%1'?", "column dialog").arg(arg); + int btn = QMessageBox::question(this, tr("Delete constraint", "column dialog"), msg); + if (btn != QMessageBox::Yes) + return; + + constraintsModel->delConstraint(idx.row()); +} + +void ColumnDialog::configureConstraint(SqliteCreateTable::Column::Constraint::Type type) +{ + SqliteCreateTable::Column::Constraint* constraint = column->getConstraint(type); + if (!constraint) + { + qCritical() << "Called ColumnDialog::configureConstraint(), but there's no specified type constraint in the column!"; + return; + } + editConstraint(constraint); +} + +void ColumnDialog::addEmptyConstraint(SqliteCreateTable::Column::Constraint::Type type) +{ + SqliteCreateTable::Column::Constraint* constr = new SqliteCreateTable::Column::Constraint(); + constr->type = type; + constraintsModel->appendConstraint(constr); + constr->rebuildTokens(); +} + +void ColumnDialog::delAllConstraint(SqliteCreateTable::Column::Constraint::Type type) +{ + SqliteCreateTable::Column::Constraint* constr = nullptr; + while ((constr = column->getConstraint(type)) != nullptr) + constraintsModel->delConstraint(constr); +} + +void ColumnDialog::constraintToggled(SqliteCreateTable::Column::Constraint::Type type, bool enabled) +{ + if (enabled) + addEmptyConstraint(type); + else + delAllConstraint(type); +} + +void ColumnDialog::updateConstraintState(SqliteCreateTable::Column::Constraint* constraint) +{ + QToolButton* toolButton = getToolButtonForConstraint(constraint); + if (!toolButton) + return; + + bool result = true; + ConstraintPanel* panel = ConstraintPanel::produce(constraint); + if (!panel) + { + qCritical() << "Could not produce ConstraintPanel for constraint validation in ColumnDialog::updateConstraintState()."; + } + else + { + panel->setDb(db); + panel->setConstraint(constraint); + result = panel->validateOnly(); + delete panel; + } + + QString errMsg = tr("Correct the constraint's configuration."); + if (db->getDialect() == Dialect::Sqlite2 && isUnofficialSqlite2Constraint(constraint)) + { + QString tooltip = tr("This constraint is not officially supported by SQLite 2,\nbut it's okay to use it."); + setValidStateWihtTooltip(toolButton, tooltip, result, errMsg); + } + else + { + setValidState(toolButton, result, errMsg); + } + + if (!result) + { + QPushButton* btn = ui->buttonBox->button(QDialogButtonBox::Ok); + btn->setEnabled(false); + } +} + +QCheckBox* ColumnDialog::getCheckBoxForConstraint(SqliteCreateTable::Column::Constraint* constraint) +{ + switch (constraint->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return ui->pkCheck; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return ui->notNullCheck; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return ui->uniqueCheck; + case SqliteCreateTable::Column::Constraint::CHECK: + return ui->checkCheck; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return ui->defaultCheck; + case SqliteCreateTable::Column::Constraint::COLLATE: + return ui->collateCheck; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return ui->fkCheck; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return nullptr; +} + +QToolButton* ColumnDialog::getToolButtonForConstraint(SqliteCreateTable::Column::Constraint* constraint) +{ + switch (constraint->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return ui->pkButton; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return ui->notNullButton; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return ui->uniqueButton; + case SqliteCreateTable::Column::Constraint::CHECK: + return ui->checkButton; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return ui->defaultButton; + case SqliteCreateTable::Column::Constraint::COLLATE: + return ui->collateButton; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return ui->fkButton; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return nullptr; +} + +bool ColumnDialog::isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Constraint* constraint) +{ + switch (constraint->type) + { + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + case SqliteCreateTable::Column::Constraint::COLLATE: + return true; + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + case SqliteCreateTable::Column::Constraint::NOT_NULL: + case SqliteCreateTable::Column::Constraint::UNIQUE: + case SqliteCreateTable::Column::Constraint::CHECK: + case SqliteCreateTable::Column::Constraint::DEFAULT: + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return false; +} + +void ColumnDialog::moveConstraintUp() +{ + QModelIndex idx = ui->constraintsView->currentIndex(); + if (!idx.isValid()) + return; + + constraintsModel->moveConstraintUp(idx.row()); +} + +void ColumnDialog::moveConstraintDown() +{ + QModelIndex idx = ui->constraintsView->currentIndex(); + if (!idx.isValid()) + return; + + constraintsModel->moveConstraintDown(idx.row()); +} + +void ColumnDialog::addPk() +{ + addConstraint(ConstraintDialog::PK); +} + +void ColumnDialog::addFk() +{ + addConstraint(ConstraintDialog::FK); +} + +void ColumnDialog::addUnique() +{ + addConstraint(ConstraintDialog::UNIQUE); +} + +void ColumnDialog::addCheck() +{ + addConstraint(ConstraintDialog::CHECK); +} + +void ColumnDialog::addCollate() +{ + addConstraint(ConstraintDialog::COLLATE); +} + +void ColumnDialog::addNotNull() +{ + addConstraint(ConstraintDialog::NOTNULL); +} + +void ColumnDialog::addDefault() +{ + addConstraint(ConstraintDialog::DEFAULT); +} + +void ColumnDialog::configurePk() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY); +} + +void ColumnDialog::configureFk() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::FOREIGN_KEY); +} + +void ColumnDialog::configureUnique() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::UNIQUE); +} + +void ColumnDialog::configureCheck() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::CHECK); +} + +void ColumnDialog::configureCollate() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::COLLATE); +} + +void ColumnDialog::configureNotNull() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::NOT_NULL); +} + +void ColumnDialog::configureDefault() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::DEFAULT); +} + +void ColumnDialog::pkToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::PRIMARY_KEY, enabled); +} + +void ColumnDialog::fkToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::FOREIGN_KEY, enabled); +} + +void ColumnDialog::uniqueToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::UNIQUE, enabled); +} + +void ColumnDialog::checkToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::CHECK, enabled); +} + +void ColumnDialog::collateToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::COLLATE, enabled); +} + +void ColumnDialog::notNullToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::NOT_NULL, enabled); +} + +void ColumnDialog::defaultToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::DEFAULT, enabled); +} + +void ColumnDialog::switchMode(bool advanced) +{ + ui->constraintModesWidget->setCurrentWidget(advanced ? ui->advancedPage : ui->simplePage); +} + +void ColumnDialog::updateConstraints() +{ + QPushButton* btn = ui->buttonBox->button(QDialogButtonBox::Ok); + btn->setEnabled(true); + + for (QCheckBox* cb : { + ui->pkCheck, + ui->fkCheck, + ui->uniqueCheck, + ui->notNullCheck, + ui->checkCheck, + ui->collateCheck, + ui->defaultCheck + }) + { + cb->setChecked(false); + } + + for (QToolButton* tb : { + ui->pkButton, + ui->fkButton, + ui->uniqueButton, + ui->notNullButton, + ui->checkButton, + ui->collateButton, + ui->defaultButton + }) + { + setValidState(tb, true); + } + + foreach (SqliteCreateTable::Column::Constraint* constr, column->constraints) + updateConstraint(constr); +} + +void ColumnDialog::updateConstraint(SqliteCreateTable::Column::Constraint* constraint) +{ + QCheckBox* checkBox = getCheckBoxForConstraint(constraint); + if (checkBox) + { + checkBox->setChecked(true); + updateConstraintState(constraint); + } +} + +void ColumnDialog::setColumn(SqliteCreateTable::Column* value) +{ + column = SqliteCreateTable::ColumnPtr::create(*value); + column->setParent(value->parent()); + constraintsModel->setColumn(column.data()); + + ui->name->setText(value->name); + if (value->type) + { + ui->typeCombo->setEditText(value->type->name); + ui->scale->setValue(value->type->scale, false); + ui->precision->setValue(value->type->precision, false); + } + + updateConstraints(); +} + +SqliteCreateTable::Column* ColumnDialog::getModifiedColumn() +{ + column->name = ui->name->text(); + updateDataType(); + column->rebuildTokens(); + + return new SqliteCreateTable::Column(*column); +} + +QToolBar* ColumnDialog::getToolBar(int toolbar) const +{ + UNUSED(toolbar); + return nullptr; +} + +void ColumnDialog::updateDataType() +{ + if (!column) + return; + + QString typeTxt = ui->typeCombo->currentText(); + QString scaleTxt = ui->scale->getValue().toString(); + QString precisionTxt = ui->precision->getValue().toString(); + if (!typeTxt.isEmpty()) + { + if (!column->type) + { + column->type = new SqliteColumnType(); + column->type->setParent(column.data()); + } + + column->type->name = typeTxt; + + if (!scaleTxt.isEmpty()) + column->type->scale = ui->scale->getValue(); + + if (!precisionTxt.isEmpty()) + column->type->precision = ui->precision->getValue(); + + column->type->rebuildTokens(); + } + else if (column->type) // there was a type, but there's not now + { + delete column->type; + column->type = nullptr; + } +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h new file mode 100644 index 0000000..c567e1a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h @@ -0,0 +1,113 @@ +#ifndef COLUMNDIALOG_H +#define COLUMNDIALOG_H + +#include "parser/ast/sqlitecreatetable.h" +#include "common/extactioncontainer.h" +#include "constraintdialog.h" +#include "guiSQLiteStudio_global.h" +#include +#include + +class ColumnDialogConstraintsModel; +class QCheckBox; +class QToolButton; + +namespace Ui { + class ColumnDialog; +} + +class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer +{ + Q_OBJECT + + public: + enum Action + { + ADD_CONSTRAINT, + EDIT_CONSTRAINT, + DEL_CONSTRAINT, + MOVE_CONSTRAINT_UP, + MOVE_CONSTRAINT_DOWN, + ADD_PK, + ADD_FK, + ADD_UNIQUE, + ADD_CHECK, + ADD_DEFAULT, + ADD_NOT_NULL, + ADD_COLLATE + }; + + enum ToolBar + { + }; + + explicit ColumnDialog(Db* db, QWidget *parent = 0); + ~ColumnDialog(); + + void init(); + void setColumn(SqliteCreateTable::Column* value); + SqliteCreateTable::Column* getModifiedColumn(); + QToolBar* getToolBar(int toolbar) const; + + protected: + void changeEvent(QEvent *e); + void createActions(); + void setupDefShortcuts(); + + private: + void addConstraint(ConstraintDialog::Constraint mode); + void setupConstraintCheckBoxes(); + void editConstraint(SqliteCreateTable::Column::Constraint* constraint); + void delConstraint(const QModelIndex& idx); + void configureConstraint(SqliteCreateTable::Column::Constraint::Type type); + void addEmptyConstraint(SqliteCreateTable::Column::Constraint::Type type); + void delAllConstraint(SqliteCreateTable::Column::Constraint::Type type); + void constraintToggled(SqliteCreateTable::Column::Constraint::Type type, bool enabled); + void updateConstraintState(SqliteCreateTable::Column::Constraint* constraint); + QCheckBox* getCheckBoxForConstraint(SqliteCreateTable::Column::Constraint* constraint); + QToolButton* getToolButtonForConstraint(SqliteCreateTable::Column::Constraint* constraint); + bool isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Constraint* constraint); + + Ui::ColumnDialog *ui = nullptr; + SqliteCreateTable::ColumnPtr column; + ColumnDialogConstraintsModel* constraintsModel = nullptr; + QCheckBox* modeCheckBox = nullptr; + Db* db = nullptr; + + private slots: + void updateConstraintsToolbarState(); + void updateState(); + void addConstraint(); + void editConstraint(); + void editConstraint(const QModelIndex& idx); + void delConstraint(); + void moveConstraintUp(); + void moveConstraintDown(); + void addPk(); + void addFk(); + void addUnique(); + void addCheck(); + void addCollate(); + void addNotNull(); + void addDefault(); + void configurePk(); + void configureFk(); + void configureUnique(); + void configureCheck(); + void configureCollate(); + void configureNotNull(); + void configureDefault(); + void pkToggled(bool enabled); + void fkToggled(bool enabled); + void uniqueToggled(bool enabled); + void checkToggled(bool enabled); + void collateToggled(bool enabled); + void notNullToggled(bool enabled); + void defaultToggled(bool enabled); + void switchMode(bool advanced); + void updateConstraints(); + void updateConstraint(SqliteCreateTable::Column::Constraint* constraint); + void updateDataType(); +}; + +#endif // COLUMNDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui new file mode 100644 index 0000000..ac7e5ae --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui @@ -0,0 +1,348 @@ + + + ColumnDialog + + + + 0 + 0 + 424 + 360 + + + + Column + + + true + + + + + + Name and type + + + + + + + 50 + 0 + + + + + + + + , + + + + + + + + 50 + 0 + + + + + + + + Data type: + + + + + + + Column name: + + + + + + + + + + Size: + + + + + + + + 120 + 0 + + + + true + + + + + + + + + + Constraints + + + + + + 0 + + + + + + + Unique + + + + + + + Configure + + + + + + + Foreign Key + + + + + + + Configure + + + + + + + Collate + + + + + + + Not NULL + + + + + + + Check condition + + + + + + + Primary Key + + + + + + + Default + + + + + + + Configure + + + + + + + Configure + + + + + + + Configure + + + + + + + Configure + + + + + + + Configure + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Advanced mode + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + NumericSpinBox + QSpinBox +
    common/numericspinbox.h
    +
    +
    + + name + typeCombo + scale + precision + pkCheck + pkButton + fkCheck + fkButton + uniqueCheck + uniqueButton + checkCheck + checkButton + notNullCheck + notNullButton + collateCheck + collateButton + defaultCheck + defaultButton + advancedCheck + buttonBox + constraintsView + + + + + buttonBox + accepted() + ColumnDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ColumnDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp new file mode 100644 index 0000000..853b680 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp @@ -0,0 +1,335 @@ +#include "columndialogconstraintsmodel.h" +#include "common/unused.h" +#include "iconmanager.h" + +ColumnDialogConstraintsModel::ColumnDialogConstraintsModel(QObject *parent) : + QAbstractTableModel(parent) +{ +} + +void ColumnDialogConstraintsModel::setColumn(SqliteCreateTable::Column* value) +{ + beginResetModel(); + column = value; + endResetModel(); +} + +SqliteCreateTable::Column::Constraint* ColumnDialogConstraintsModel::getConstraint(int constrIdx) const +{ + if (column.isNull()) + return nullptr; + + return column->constraints[constrIdx]; +} + +void ColumnDialogConstraintsModel::replaceConstraint(int constrIdx, SqliteCreateTable::Column::Constraint* constr) +{ + if (column.isNull()) + return; + + delete column->constraints[constrIdx]; + column->constraints[constrIdx] = constr; + constr->setParent(column); + + emit constraintsChanged(); +} + +void ColumnDialogConstraintsModel::insertConstraint(int constrIdx, SqliteCreateTable::Column::Constraint* constr) +{ + if (column.isNull()) + return; + + beginInsertRows(QModelIndex(), constrIdx, constrIdx); + column->constraints.insert(constrIdx, constr); + constr->setParent(column); + endInsertRows(); + + emit constraintsChanged(); +} + +void ColumnDialogConstraintsModel::appendConstraint(SqliteCreateTable::Column::Constraint* constr) +{ + if (column.isNull()) + return; + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + column->constraints.append(constr); + constr->setParent(column); + endInsertRows(); + + emit constraintsChanged(); +} + +void ColumnDialogConstraintsModel::delConstraint(int constrIdx) +{ + if (column.isNull()) + return; + + beginRemoveRows(QModelIndex(), constrIdx, constrIdx); + delete column->constraints[constrIdx]; + column->constraints.removeAt(constrIdx); + endRemoveRows(); + + emit constraintsChanged(); +} + +void ColumnDialogConstraintsModel::delConstraint(SqliteCreateTable::Column::Constraint* constr) +{ + if (column.isNull()) + return; + + int constrIdx = column->constraints.indexOf(constr); + if (constrIdx < -1) + return; + + delConstraint(constrIdx); +} + +void ColumnDialogConstraintsModel::moveConstraintUp(int constrIdx) +{ + moveConstraintColumnTo(constrIdx, constrIdx-1); +} + +void ColumnDialogConstraintsModel::moveConstraintDown(int constrIdx) +{ + moveConstraintColumnTo(constrIdx, constrIdx+1); +} + +void ColumnDialogConstraintsModel::moveConstraintColumnTo(int constrIdx, int newIdx) +{ + if (column.isNull()) + return; + + if (newIdx == constrIdx) + return; + + if (newIdx == constrIdx+1) + { + // See TableStructureModel::moveColumnTo() for details above code below. + int tmpIdx = newIdx; + newIdx = constrIdx; + constrIdx = tmpIdx; + } + + beginMoveRows(QModelIndex(), constrIdx, constrIdx, QModelIndex(), newIdx); + if (newIdx >= column->constraints.size()) + { + SqliteCreateTable::Column::Constraint* constr = column->constraints.takeAt(constrIdx); + column->constraints.append(constr); + } + else + column->constraints.move(constrIdx, newIdx); + + endMoveRows(); + + emit constraintsChanged(); +} + +ColumnDialogConstraintsModel::Column ColumnDialogConstraintsModel::getColumn(int colIdx) const +{ + return static_cast(colIdx); +} + +QIcon ColumnDialogConstraintsModel::getIcon(int rowIdx) const +{ + SqliteCreateTable::Column::Constraint* constr = column->constraints[rowIdx]; + switch (constr->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return ICONS.CONSTRAINT_PRIMARY_KEY; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return ICONS.CONSTRAINT_NOT_NULL; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return ICONS.CONSTRAINT_UNIQUE; + case SqliteCreateTable::Column::Constraint::CHECK: + return ICONS.CONSTRAINT_CHECK; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return ICONS.CONSTRAINT_DEFAULT; + case SqliteCreateTable::Column::Constraint::COLLATE: + return ICONS.CONSTRAINT_COLLATION; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return ICONS.CONSTRAINT_FOREIGN_KEY; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return QIcon(); +} + +QString ColumnDialogConstraintsModel::getName(int rowIdx) const +{ + SqliteCreateTable::Column::Constraint* constr = column->constraints[rowIdx]; + return constr->name; +} + +QString ColumnDialogConstraintsModel::getType(int rowIdx) const +{ + SqliteCreateTable::Column::Constraint* constr = column->constraints[rowIdx]; + switch (constr->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return "PRIMARY KEY"; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return "NOT NULL"; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return "UNIQUE"; + case SqliteCreateTable::Column::Constraint::CHECK: + return "CHECK"; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return "DEFAULT"; + case SqliteCreateTable::Column::Constraint::COLLATE: + return "COLLATE"; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return "FOREIGN KEY"; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return QString::null; +} + +QString ColumnDialogConstraintsModel::getDetails(int rowIdx) const +{ + SqliteCreateTable::Column::Constraint* constr = column->constraints[rowIdx]; + switch (constr->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return getPkDetails(constr); + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return getNotNullDetails(constr); + case SqliteCreateTable::Column::Constraint::UNIQUE: + return getUniqueDetails(constr); + case SqliteCreateTable::Column::Constraint::CHECK: + return getCheckDetails(constr); + case SqliteCreateTable::Column::Constraint::DEFAULT: + return getDefaultDetails(constr); + case SqliteCreateTable::Column::Constraint::COLLATE: + return getCollateDetails(constr); + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return getFkDetails(constr); + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return QString::null; +} + +QString ColumnDialogConstraintsModel::getPkDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "KEY", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getNotNullDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "NULL", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getUniqueDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "UNIQUE", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getCheckDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "CHECK", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "DEFAULT", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getCollateDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "COLLATE", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getFkDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "KEY", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + +QString ColumnDialogConstraintsModel::getConstrDetails(SqliteCreateTable::Column::Constraint* constr, int tokenOffset) const +{ + TokenList tokens = constr->tokens.mid(tokenOffset + 1); + tokens.trimLeft(); + return tokens.detokenize(); +} + +int ColumnDialogConstraintsModel::rowCount(const QModelIndex& parent) const +{ + UNUSED(parent); + if (column.isNull()) + return 0; + + return column->constraints.size(); +} + +int ColumnDialogConstraintsModel::columnCount(const QModelIndex& parent) const +{ + UNUSED(parent); + return 3; +} + +QVariant ColumnDialogConstraintsModel::data(const QModelIndex& index, int role) const +{ + if (column.isNull()) + return QVariant(); + + switch (getColumn(index.column())) + { + case Column::TYPE: + { + if (role == Qt::DecorationRole) + return getIcon(index.row()); + + if (role == Qt::DisplayRole) + return getType(index.row()); + + break; + } + case Column::NAME: + { + if (role == Qt::DisplayRole) + return getName(index.row()); + + break; + } + case Column::DETAILS: + if (role == Qt::DisplayRole) + return getDetails(index.row()); + + break; + } + return QVariant(); +} + +QVariant ColumnDialogConstraintsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QAbstractTableModel::headerData(section, orientation, role); + + if (orientation == Qt::Vertical) + return section + 1; + + switch (getColumn(section)) + { + case Column::TYPE: + return tr("Type", "column dialog constraints"); + case Column::NAME: + return tr("Name", "column dialog constraints"); + case Column::DETAILS: + return tr("Details", "column dialog constraints"); + } + return QVariant(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h new file mode 100644 index 0000000..f37933a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h @@ -0,0 +1,58 @@ +#ifndef COLUMNDIALOGCONSTRAINTSMODEL_H +#define COLUMNDIALOGCONSTRAINTSMODEL_H + +#include "parser/ast/sqlitecreatetable.h" +#include "guiSQLiteStudio_global.h" +#include +#include + +class GUI_API_EXPORT ColumnDialogConstraintsModel : public QAbstractTableModel +{ + Q_OBJECT + public: + explicit ColumnDialogConstraintsModel(QObject *parent = 0); + + int rowCount(const QModelIndex& parent = QModelIndex()) const; + int columnCount(const QModelIndex& parent) const; + QVariant data(const QModelIndex& index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + void setColumn(SqliteCreateTable::Column* value); + SqliteCreateTable::Column::Constraint* getConstraint(int constrIdx) const; + void replaceConstraint(int constrIdx, SqliteCreateTable::Column::Constraint* constr); + void insertConstraint(int constrIdx, SqliteCreateTable::Column::Constraint* constr); + void appendConstraint(SqliteCreateTable::Column::Constraint* constr); + void delConstraint(int constrIdx); + void delConstraint(SqliteCreateTable::Column::Constraint* constr); + void moveConstraintUp(int constrIdx); + void moveConstraintDown(int constrIdx); + void moveConstraintColumnTo(int constrIdx, int newIdx); + + private: + enum class Column + { + TYPE, + NAME, + DETAILS + }; + + Column getColumn(int colIdx) const; + QIcon getIcon(int rowIdx) const; + QString getName(int rowIdx) const; + QString getType(int rowIdx) const; + QString getDetails(int rowIdx) const; + QString getPkDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getNotNullDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getUniqueDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getCheckDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getCollateDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getFkDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getConstrDetails(SqliteCreateTable::Column::Constraint* constr, int tokenOffset) const; + + QPointer column; + + signals: + void constraintsChanged(); +}; + +#endif // COLUMNDIALOGCONSTRAINTSMODEL_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp new file mode 100644 index 0000000..932036e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -0,0 +1,1529 @@ +#include "configdialog.h" +#include "ui_configdialog.h" +#include "services/config.h" +#include "uiconfig.h" +#include "customconfigwidgetplugin.h" +#include "services/pluginmanager.h" +#include "formmanager.h" +#include "services/codeformatter.h" +#include "plugins/codeformatterplugin.h" +#include "configwidgets/styleconfigwidget.h" +#include "configwidgets/combodatawidget.h" +#include "configwidgets/listtostringlisthash.h" +#include "iconmanager.h" +#include "common/userinputfilter.h" +#include "multieditor/multieditorwidget.h" +#include "multieditor/multieditorwidgetplugin.h" +#include "plugins/confignotifiableplugin.h" +#include "mainwindow.h" +#include "common/unused.h" +#include "sqlitestudio.h" +#include "configmapper.h" +#include "datatype.h" +#include "uiutils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GET_FILTER_STRING(Widget, WidgetType, Method) \ + if (qobject_cast(Widget))\ + return qobject_cast(Widget)->Method() + " " + Widget->toolTip();\ + +#define GET_FILTER_STRING2(Widget, WidgetType) \ + WidgetType* w##WidgetType = qobject_cast(widget);\ + if (w##WidgetType)\ + return getFilterString(w##WidgetType) + " " + Widget->toolTip(); + +ConfigDialog::ConfigDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ConfigDialog) +{ + init(); +} + +ConfigDialog::~ConfigDialog() +{ + // Cancel transaction on CfgMain objects from plugins + rollbackPluginConfigs(); + + // Notify plugins about dialog being closed + UiConfiguredPlugin* cfgPlugin = nullptr; + foreach (Plugin* plugin, PLUGINS->getLoadedPlugins()) + { + cfgPlugin = dynamic_cast(plugin); + if (!cfgPlugin) + continue; + + cfgPlugin->configDialogClosed(); + } + + // Delete UI and other resources + delete ui; + safe_delete(configMapper); + + for (ConfigMapper* mapper : pluginConfigMappers.values()) + delete mapper; + + pluginConfigMappers.clear(); + +} + +void ConfigDialog::configureDataEditors(const QString& dataTypeString) +{ + ui->categoriesWidget->setVisible(false); + ui->stackedWidget->setCurrentWidget(ui->dataEditorsPage); + + for (int i = 0; i < ui->dataEditorsTypesList->count(); i++) + { + if (ui->dataEditorsTypesList->item(i)->text() == dataTypeString.toUpper()) + { + ui->dataEditorsTypesList->setCurrentRow(i); + return; + } + } + + addDataType(dataTypeString.toUpper()); +} + +QString ConfigDialog::getFilterString(QWidget *widget) +{ + // Common code for widgets with single method call + GET_FILTER_STRING(widget, QLabel, text); + GET_FILTER_STRING(widget, QAbstractButton, text); + GET_FILTER_STRING(widget, QLineEdit, text); + GET_FILTER_STRING(widget, QTextEdit, toPlainText); + GET_FILTER_STRING(widget, QPlainTextEdit, toPlainText); + GET_FILTER_STRING(widget, QGroupBox, title); + GET_FILTER_STRING(widget, QKeySequenceEdit, keySequence().toString); + + // Widgets needs a little more than single method call + GET_FILTER_STRING2(widget, QComboBox); + GET_FILTER_STRING2(widget, QTreeWidget); + GET_FILTER_STRING2(widget, QListWidget); + GET_FILTER_STRING2(widget, QTableWidget); + + return QString::null; +} + +QString ConfigDialog::getFilterString(QComboBox *widget) +{ + QStringList items; + for (int i = 0; i < widget->count(); i++) + items << widget->itemText(i); + + return items.join(" "); +} + +QString ConfigDialog::getFilterString(QTreeWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QTreeWidgetItem* item, items) + for (int i = 0; i < widget->columnCount(); i++) + strList << item->text(i) + " " + item->toolTip(0); + + return strList.join(" "); +} + +QString ConfigDialog::getFilterString(QListWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QListWidgetItem* item, items) + strList << item->text() + " " + item->toolTip(); + + return strList.join(" "); +} + +QString ConfigDialog::getFilterString(QTableWidget *widget) +{ + QList items = widget->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); + QStringList strList; + foreach (QTableWidgetItem* item, items) + strList << item->text() + " " + item->toolTip(); + + return strList.join(" "); +} + +void ConfigDialog::init() +{ + ui->setupUi(this); + setWindowIcon(ICONS.CONFIGURE); + + ui->categoriesTree->setCurrentItem(ui->categoriesTree->topLevelItem(0)); + + configMapper = new ConfigMapper(CfgMain::getPersistableInstances()); + connect(configMapper, SIGNAL(modified()), this, SLOT(markModified())); + connect(configMapper, &ConfigMapper::notifyEnabledWidgetModified, [=](QWidget* widget, CfgEntry* key, const QVariant& value) + { + UNUSED(widget); + for (ConfigNotifiablePlugin* plugin : notifiablePlugins) + plugin->configModified(key, value); + }); + + ui->categoriesFilter->setClearButtonEnabled(true); + UserInputFilter* filter = new UserInputFilter(ui->categoriesFilter, this, SLOT(applyFilter(QString))); + filter->setDelay(500); + + ui->stackedWidget->setCurrentWidget(ui->generalPage); + initPageMap(); + initInternalCustomConfigWidgets(); + initPlugins(); + initPluginsPage(); + initFormatterPlugins(); + initDataEditors(); + initShortcuts(); + + connect(ui->categoriesTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(switchPage(QTreeWidgetItem*))); + connect(ui->previewTabs, SIGNAL(currentChanged(int)), this, SLOT(updateStylePreview())); + connect(ui->activeStyleCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateStylePreview())); + connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); + connect(ui->hideBuiltInPluginsCheck, SIGNAL(toggled(bool)), this, SLOT(updateBuiltInPluginsVisibility())); + + ui->activeStyleCombo->addItems(QStyleFactory::keys()); + + connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(pageSwitched())); + + ui->hideBuiltInPluginsCheck->setChecked(true); + +#ifdef NO_AUTO_UPDATES + ui->updatesGroup->setVisible(false); +#endif + + load(); + updateStylePreview(); +} + +void ConfigDialog::load() +{ + updatingDataEditorItem = true; + configMapper->loadToWidget(ui->stackedWidget); + updatingDataEditorItem = false; + setModified(false); +} + +void ConfigDialog::save() +{ + MainWindow::getInstance()->setStyle(ui->activeStyleCombo->currentText()); + + QString loadedPlugins = collectLoadedPlugins(); + storeSelectedFormatters(); + CFG->beginMassSave(); + CFG_CORE.General.LoadedPlugins.set(loadedPlugins); + configMapper->saveFromWidget(ui->stackedWidget, true); + commitPluginConfigs(); + CFG->commitMassSave(); +} + +void ConfigDialog::storeSelectedFormatters() +{ + CodeFormatterPlugin* plugin = nullptr; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QString lang; + QString pluginName; + for (int i = 0, total = ui->formatterPluginsTree->topLevelItemCount(); i < total; ++i) + { + item = ui->formatterPluginsTree->topLevelItem(i); + lang = item->text(0); + + combo = formatterLangToPluginComboMap[lang]; + if (!combo) + { + qCritical() << "Could not find combo for lang " << lang << " in storeSelectedFormatters()"; + continue; + } + + pluginName = combo->currentData().toString(); + plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin for lang " << lang << " in storeSelectedFormatters()"; + continue; + } + + FORMATTER->setFormatter(lang, plugin); + } + + FORMATTER->storeCurrentSettings(); +} + +void ConfigDialog::markModified() +{ + setModified(true); +} + +void ConfigDialog::setModified(bool modified) +{ + modifiedFlag = modified; + updateModified(); +} + +void ConfigDialog::updateModified() +{ + ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(modifiedFlag); +} + +void ConfigDialog::applyFilter(const QString &filter) +{ + QColor normalColor = ui->categoriesTree->palette().color(QPalette::Active, QPalette::WindowText); + QColor disabledColor = ui->categoriesTree->palette().color(QPalette::Disabled, QPalette::WindowText); + if (filter.isEmpty()) + { + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + item->setForeground(0, normalColor); + + return; + } + + QList widgets = ui->stackedWidget->findChildren(); + QList matchedWidgets; + foreach (QWidget* widget, widgets) + { + if (getFilterString(widget).contains(filter, Qt::CaseInsensitive)) + matchedWidgets << widget; + } + + QHash pageToCategoryItem = buildPageToCategoryItemMap(); + QSet matchedCategories; + foreach (QWidget* page, pageToCategoryItem.keys()) + { + foreach (QWidget* matched, matchedWidgets) + { + if (page->isAncestorOf(matched)) + { + if (!pageToCategoryItem.contains(page)) + { + qCritical() << "Page" << page << "not on page-to-category-item mapping."; + continue; + } + + matchedCategories << pageToCategoryItem[page]; + break; + } + } + } + + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + item->setForeground(0, disabledColor); + + foreach (QTreeWidgetItem* item, matchedCategories) + { + item->setForeground(0, normalColor); + while ((item = item->parent()) != nullptr) + item->setForeground(0, normalColor); + } +} + +QHash ConfigDialog::buildPageToCategoryItemMap() const +{ + QHash pageNameToCategoryItem; + foreach (QTreeWidgetItem* item, getAllCategoryItems()) + pageNameToCategoryItem[item->statusTip(0)] = item; + + QWidget* page = nullptr; + QHash pageToCategoryItem; + for (int i = 0; i < ui->stackedWidget->count(); i++) + { + page = ui->stackedWidget->widget(i); + pageToCategoryItem[page] = pageNameToCategoryItem[page->objectName()]; + } + return pageToCategoryItem; +} + +QList ConfigDialog::getAllCategoryItems() const +{ + return ui->categoriesTree->findItems("*", Qt::MatchWildcard|Qt::MatchRecursive); +} + +QList ConfigDialog::getDefaultEditorsForType(DataType::Enum dataType) +{ + QList plugins = PLUGINS->getLoadedPlugins(); + DataType modelDataType; + modelDataType.setType(dataType); + + typedef QPair PluginWithPriority; + QList sortedPlugins; + PluginWithPriority editorWithPrio; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + if (!plugin->validFor(modelDataType)) + continue; + + editorWithPrio.first = plugin->getPriority(modelDataType); + editorWithPrio.second = plugin; + sortedPlugins << editorWithPrio; + } + + qSort(sortedPlugins.begin(), sortedPlugins.end(), [=](const PluginWithPriority& p1, const PluginWithPriority& p2) -> bool + { + return p1.first < p2.first; + }); + + QList results; + for (const PluginWithPriority& p: sortedPlugins) + results << p.second; + + return results; +} + +void ConfigDialog::pageSwitched() +{ + if (ui->stackedWidget->currentWidget() == ui->dataEditorsPage) + { + updateDataTypeEditors(); + return; + } +} + +void ConfigDialog::updateDataTypeEditors() +{ + QString typeName = ui->dataEditorsTypesList->currentItem()->text(); + DataType::Enum typeEnum = DataType::fromString(typeName); + bool usingCustomOrder = false; + QStringList editorsOrder = getPluginNamesFromDataTypeItem(ui->dataEditorsTypesList->currentItem(), &usingCustomOrder); + QList sortedPlugins; + + while (ui->dataEditorsSelectedTabs->count() > 0) + delete ui->dataEditorsSelectedTabs->widget(0); + + ui->dataEditorsAvailableList->clear(); + if (usingCustomOrder) + sortedPlugins = updateCustomDataTypeEditors(editorsOrder); + else + sortedPlugins = updateDefaultDataTypeEditors(typeEnum); + + ui->dataEditorsAvailableList->sortItems(); + + for (MultiEditorWidgetPlugin* plugin : sortedPlugins) + addDataTypeEditor(plugin); +} + +QList ConfigDialog::updateCustomDataTypeEditors(const QStringList& editorsOrder) +{ + // Building plugins list + QList plugins = PLUGINS->getLoadedPlugins(); + QList enabledPlugins; + QListWidgetItem* item = nullptr; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + item = new QListWidgetItem(plugin->getTitle()); + item->setFlags(item->flags()|Qt::ItemIsUserCheckable); + item->setCheckState(editorsOrder.contains(plugin->getName()) ? Qt::Checked : Qt::Unchecked); + item->setData(QListWidgetItem::UserType, plugin->getName()); + if (item->checkState() == Qt::Checked) + enabledPlugins << plugin; + + ui->dataEditorsAvailableList->addItem(item); + } + + qSort(enabledPlugins.begin(), enabledPlugins.end(), [=](MultiEditorWidgetPlugin* p1, MultiEditorWidgetPlugin* p2) -> bool + { + return editorsOrder.indexOf(p1->getName()) < editorsOrder.indexOf(p2->getName()); + }); + + return enabledPlugins; +} + +QList ConfigDialog::updateDefaultDataTypeEditors(DataType::Enum typeEnum) +{ + // Building plugins list + QList plugins = PLUGINS->getLoadedPlugins(); + QList enabledPlugins = getDefaultEditorsForType(typeEnum); + QListWidgetItem* item = nullptr; + for (MultiEditorWidgetPlugin* plugin : plugins) + { + item = new QListWidgetItem(plugin->getTitle()); + item->setFlags(item->flags()|Qt::ItemIsUserCheckable); + item->setCheckState(enabledPlugins.contains(plugin) ? Qt::Checked : Qt::Unchecked); + item->setData(QListWidgetItem::UserType, plugin->getName()); + ui->dataEditorsAvailableList->addItem(item); + } + return enabledPlugins; +} + +void ConfigDialog::addDataTypeEditor(const QString& pluginName) +{ + MultiEditorWidgetPlugin* plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin" << pluginName << " in ConfigDialog::addDataTypeEditor()"; + return; + } + + addDataTypeEditor(plugin); +} + +void ConfigDialog::addDataTypeEditor(MultiEditorWidgetPlugin* plugin) +{ + MultiEditorWidget* editor = plugin->getInstance(); + ui->dataEditorsSelectedTabs->addTab(editor, editor->getTabLabel().replace("&", "&&")); +} + +void ConfigDialog::removeDataTypeEditor(QListWidgetItem* item, const QString& pluginName) +{ + QStringList orderedPlugins = getPluginNamesFromDataTypeItem(item); + int idx = orderedPlugins.indexOf(pluginName); + removeDataTypeEditor(idx); +} + +void ConfigDialog::removeDataTypeEditor(int idx) +{ + if (idx < 0 || idx > (ui->dataEditorsSelectedTabs->count() - 1)) + { + qCritical() << "Index out of range in ConfigDialog::removeDataTypeEditor():" << idx << "(tabs:" << ui->dataEditorsSelectedTabs->count() << ")"; + return; + } + + delete ui->dataEditorsSelectedTabs->widget(idx); +} + +void ConfigDialog::transformDataTypeEditorsToCustomList(QListWidgetItem* typeItem) +{ + DataType::Enum dataType = DataType::fromString(typeItem->text()); + QList plugins = getDefaultEditorsForType(dataType); + + QStringList pluginNames; + for (MultiEditorWidgetPlugin* plugin : plugins) + pluginNames << plugin->getName(); + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +QStringList ConfigDialog::getPluginNamesFromDataTypeItem(QListWidgetItem* typeItem, bool* exists) +{ + QVariant data = typeItem->data(QListWidgetItem::UserType); + if (exists) + *exists = data.isValid(); + + return data.toStringList(); +} + +void ConfigDialog::setPluginNamesForDataTypeItem(QListWidgetItem* typeItem, const QStringList& pluginNames) +{ + updatingDataEditorItem = true; + typeItem->setData(QListWidgetItem::UserType, pluginNames); + updatingDataEditorItem = false; +} + +void ConfigDialog::addDataType(const QString& typeStr) +{ + QListWidgetItem* item = new QListWidgetItem(typeStr); + item->setFlags(item->flags()|Qt::ItemIsEditable); + ui->dataEditorsTypesList->addItem(item); + ui->dataEditorsTypesList->setCurrentRow(ui->dataEditorsTypesList->count() - 1, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + markModified(); +} + +void ConfigDialog::rollbackPluginConfigs() +{ + CfgMain* mainCfg = nullptr; + for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + { + mainCfg = plugin->getMainUiConfig(); + if (mainCfg) + mainCfg->rollback(); + } +} + +void ConfigDialog::commitPluginConfigs() +{ + CfgMain* mainCfg = nullptr; + for (UiConfiguredPlugin* plugin : pluginConfigMappers.keys()) + { + mainCfg = plugin->getMainUiConfig(); + if (mainCfg) + { + mainCfg->commit(); + mainCfg->begin(); // be prepared for further changes after "Apply" + } + } +} + +void ConfigDialog::updateDataTypeListState() +{ + bool listEditingEnabled = ui->dataEditorsTypesList->selectedItems().size() > 0 && ui->dataEditorsTypesList->currentItem()->flags().testFlag(Qt::ItemIsEditable); + dataEditRenameAction->setEnabled(listEditingEnabled); + dataEditDeleteAction->setEnabled(listEditingEnabled); + + bool orderEditingEnabled = ui->dataEditorsTypesList->selectedItems().size() > 0; + ui->dataEditorsAvailableList->setEnabled(orderEditingEnabled); + ui->dataEditorsSelectedTabs->setEnabled(orderEditingEnabled); +} + +void ConfigDialog::dataEditorItemEdited(QListWidgetItem* item) +{ + if (updatingDataEditorItem) + return; + + updatingDataEditorItem = true; + QString txt = item->text().toUpper(); + if (DataType::getAllNames().contains(txt)) + txt += "_"; + + while (ui->dataEditorsTypesList->findItems(txt, Qt::MatchExactly).size() > 1) + txt += "_"; + + item->setText(txt); + updatingDataEditorItem = false; +} + +void ConfigDialog::dataEditorAvailableChanged(QListWidgetItem* item) +{ + QListWidgetItem* typeItem = ui->dataEditorsTypesList->currentItem(); + if (!typeItem) + return; + + bool exists = false; + QStringList pluginNames = getPluginNamesFromDataTypeItem(typeItem, &exists); + if (!exists) + { + transformDataTypeEditorsToCustomList(typeItem); + pluginNames = getPluginNamesFromDataTypeItem(typeItem); + } + + QString pluginName = item->data(QListWidgetItem::UserType).toString(); + Qt::CheckState state = item->checkState(); + if (pluginNames.contains(pluginName) && state == Qt::Unchecked) + { + removeDataTypeEditor(typeItem, pluginName); + pluginNames.removeOne(pluginName); + + } + else if (!pluginNames.contains(pluginName) && state == Qt::Checked) + { + addDataTypeEditor(pluginName); + pluginNames << pluginName; + } + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +void ConfigDialog::dataEditorTabsOrderChanged(int from, int to) +{ + QListWidgetItem* typeItem = ui->dataEditorsTypesList->currentItem(); + if (!typeItem) + return; + + bool exists = false; + QStringList pluginNames = getPluginNamesFromDataTypeItem(typeItem, &exists); + if (!exists) + { + transformDataTypeEditorsToCustomList(typeItem); + pluginNames = getPluginNamesFromDataTypeItem(typeItem); + } + + int pluginSize = pluginNames.size(); + if (from >= pluginSize || to >= pluginSize) + { + qCritical() << "Tabse moved out of range. in ConfigDialog::dataEditorTabsOrderChanged(). Range was: " << pluginSize << "and indexes were:" << from << to; + return; + } + + QString pluginName = pluginNames[from]; + pluginNames.removeAt(from); + pluginNames.insert(to, pluginName); + + setPluginNamesForDataTypeItem(typeItem, pluginNames); +} + +void ConfigDialog::addDataType() +{ + addDataType(""); + renameDataType(); +} + +void ConfigDialog::renameDataType() +{ + QListWidgetItem* item = ui->dataEditorsTypesList->currentItem(); + if (!item) + return; + + ui->dataEditorsTypesList->editItem(item); +} + +void ConfigDialog::delDataType() +{ + QListWidgetItem* item = ui->dataEditorsTypesList->currentItem(); + if (!item) + return; + + int row = ui->dataEditorsTypesList->currentRow(); + delete ui->dataEditorsTypesList->takeItem(row); + + if (ui->dataEditorsTypesList->count() > 0) + { + if (ui->dataEditorsTypesList->count() <= row) + { + row--; + if (row < 0) + row = 0; + } + + ui->dataEditorsTypesList->setCurrentRow(row, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + } + + updateDataTypeListState(); + markModified(); +} + +void ConfigDialog::dataTypesHelp() +{ + static const QString url = QStringLiteral("http://wiki.sqlitestudio.pl/index.php/User_Manual#Customizing_data_type_editors"); + QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode)); +} + +void ConfigDialog::updateActiveFormatterState() +{ + CodeFormatterPlugin* plugin = nullptr; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QToolButton* button = nullptr; + QString lang; + QString pluginName; + for (int i = 0, total = ui->formatterPluginsTree->topLevelItemCount(); i < total; ++i) + { + item = ui->formatterPluginsTree->topLevelItem(i); + lang = item->text(0); + + combo = formatterLangToPluginComboMap[lang]; + button = formatterLangToConfigButtonMap[lang]; + if (!button) + { + qCritical() << "Could not find button for lang " << lang << " in updateActiveFormatterState()"; + continue; + } + + if (!combo) + { + qCritical() << "Could not find combo for lang " << lang << " in updateActiveFormatterState()"; + button->setEnabled(false); + continue; + } + + pluginName = combo->currentData().toString(); + plugin = dynamic_cast(PLUGINS->getLoadedPlugin(pluginName)); + if (!plugin) + { + qCritical() << "Could not find plugin for lang " << lang << " in updateActiveFormatterState()"; + button->setEnabled(false); + continue; + } + + button->setEnabled(dynamic_cast(plugin)); + } +} + +void ConfigDialog::configureFormatter(const QString& pluginTitle) +{ + QTreeWidgetItem* item = getItemByTitle(pluginTitle); + if (!item) + return; + + ui->categoriesTree->setCurrentItem(item); +} + +void ConfigDialog::activeFormatterChanged() +{ + markModified(); + updateActiveFormatterState(); +} + +void ConfigDialog::detailsClicked(const QString& pluginName) +{ + static const QString details = QStringLiteral( + "" + "" + "" + "" + "" + "%2" + "
    %1
    "); + static const QString row = QStringLiteral("%1%2"); + static const QString hline = QStringLiteral("
    "); + + PluginType* type = PLUGINS->getPluginType(pluginName); + Q_ASSERT(type != nullptr); + + // Rows + QStringList rows; + rows << row.arg(tr("Description:", "plugin details")).arg(PLUGINS->getDescription(pluginName)); + rows << row.arg(tr("Category:", "plugin details")).arg(type->getTitle()); + rows << row.arg(tr("Version:", "plugin details")).arg(PLUGINS->getPrintableVersion(pluginName)); + rows << row.arg(tr("Author:", "plugin details")).arg(PLUGINS->getAuthor(pluginName)); + rows << hline; + rows << row.arg(tr("Internal name:", "plugin details")).arg(pluginName); + rows << row.arg(tr("Dependencies:", "plugin details")).arg(PLUGINS->getDependencies(pluginName).join(", ")); + rows << row.arg(tr("Conflicts:", "plugin details")).arg(PLUGINS->getConflicts(pluginName).join(", ")); + + // Message + QString pluginDetails = details.arg(PLUGINS->getTitle(pluginName)).arg(rows.join("")); + QMessageBox::information(this, tr("Plugin details"), pluginDetails); +} + +void ConfigDialog::failedToLoadPlugin(const QString& pluginName) +{ + QTreeWidgetItem* theItem = itemToPluginNameMap.valueByRight(pluginName); + if (!theItem) + { + qWarning() << "Plugin" << pluginName << "failed to load, but it could not be found on the plugins list in ConfigDialog."; + return; + } + + theItem->setCheckState(0, Qt::Unchecked); +} + +void ConfigDialog::codeFormatterUnloaded() +{ + refreshFormattersPage(); +} + +void ConfigDialog::codeFormatterLoaded() +{ + refreshFormattersPage(); +} + +void ConfigDialog::loadUnloadPlugin(QTreeWidgetItem* item, int column) +{ + if (column != 0) + return; + + QString pluginName = itemToPluginNameMap.valueByLeft(item); + if (PLUGINS->isBuiltIn(pluginName)) + return; + + bool wasLoaded = PLUGINS->isLoaded(pluginName); + + if (wasLoaded == (item->checkState(0) == Qt::Checked)) + return; + + if (wasLoaded) + PLUGINS->unload(pluginName); + else + PLUGINS->load(pluginName); + + markModified(); +} + +void ConfigDialog::pluginAboutToUnload(Plugin* plugin, PluginType* type) +{ + // Deinit tree item + QTreeWidgetItem* typeItem = getPluginsCategoryItem(type); + QTreeWidgetItem* pluginItem = getPluginItem(plugin); + if (pluginItem) + { + typeItem->removeChild(pluginItem); + pluginToItemMap.remove(plugin); + } + + // Notifiable plugin + ConfigNotifiablePlugin* notifiablePlugin = dynamic_cast(plugin); + if (notifiablePlugin && notifiablePlugins.contains(notifiablePlugin)) + notifiablePlugins.removeOne(notifiablePlugin); + + // Deinit page + deinitPluginPage(plugin); + + // Update tree categories + updatePluginCategoriesVisibility(); +} + +void ConfigDialog::pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfigLoading) +{ + // Update formatters page + if (type->isForPluginType()) + codeFormatterLoaded(); + + // Init page + if (!initPluginPage(plugin, skipConfigLoading)) + return; + + // Init tree item + QTreeWidgetItem* typeItem = getPluginsCategoryItem(type); + QTreeWidgetItem* pluginItem = new QTreeWidgetItem({plugin->getTitle()}); + pluginItem->setStatusTip(0, plugin->getName()); + typeItem->addChild(pluginItem); + pluginToItemMap[plugin] = pluginItem; + + // Update tree categories + updatePluginCategoriesVisibility(); + + // Notifiable plugin + ConfigNotifiablePlugin* notifiablePlugin = dynamic_cast(plugin); + if (notifiablePlugin) + notifiablePlugins << notifiablePlugin; +} + +void ConfigDialog::pluginUnloaded(const QString& pluginName, PluginType* type) +{ + UNUSED(pluginName); + + // Update formatters page + if (type->isForPluginType()) + codeFormatterUnloaded(); +} + +void ConfigDialog::updatePluginCategoriesVisibility() +{ + QTreeWidgetItem* categories = getPluginsCategoryItem(); + for (int i = 0; i < categories->childCount(); i++) + updatePluginCategoriesVisibility(categories->child(i)); +} + +void ConfigDialog::updateBuiltInPluginsVisibility() +{ + bool hideBuiltIn = ui->hideBuiltInPluginsCheck->isChecked(); + QHashIterator it = itemToPluginNameMap.iterator(); + while (it.hasNext()) + { + it.next(); + if (PLUGINS->isBuiltIn(it.value())) + ui->pluginsList->setItemHidden(it.key(), hideBuiltIn); + else + ui->pluginsList->setItemHidden(it.key(), false); + } +} + +void ConfigDialog::applyShortcutsFilter(const QString &filter) +{ + QTreeWidgetItem* categoryItem = nullptr; + QTreeWidgetItem* item = nullptr; + QKeySequenceEdit* seqEdit = nullptr; + bool empty = filter.isEmpty(); + bool visible = true; + int foundInCategory = 0; + for (int i = 0, total_i = ui->shortcutsTable->topLevelItemCount(); i < total_i; ++i) + { + foundInCategory = 0; + categoryItem = ui->shortcutsTable->topLevelItem(i); + for (int j = 0 , total_j = categoryItem->childCount(); j < total_j; ++j) + { + item = categoryItem->child(j); + seqEdit = dynamic_cast(ui->shortcutsTable->itemWidget(item, 1)); + visible = empty || item->text(0).contains(filter, Qt::CaseInsensitive) || + seqEdit->keySequence().toString().contains(filter, Qt::CaseInsensitive); + + item->setHidden(!visible); + if (visible) + foundInCategory++; + } + + categoryItem->setHidden(foundInCategory == 0); + } +} + +void ConfigDialog::updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem) +{ + categoryItem->setHidden(categoryItem->childCount() == 0); +} + +QString ConfigDialog::collectLoadedPlugins() const +{ + QStringList loaded; + QHashIterator it = itemToPluginNameMap.iterator(); + while (it.hasNext()) + { + it.next(); + loaded << (it.value() + "=" + ((it.key()->checkState(0) == Qt::Checked) ? "1" : "0")); + } + + return loaded.join(","); +} + +void ConfigDialog::initPageMap() +{ + int pages = ui->stackedWidget->count(); + QWidget* widget = nullptr; + for (int i = 0; i < pages; i++) + { + widget = ui->stackedWidget->widget(i); + nameToPage[widget->objectName()] = widget; + } +} + +void ConfigDialog::initInternalCustomConfigWidgets() +{ + QList customWidgets; + customWidgets << new StyleConfigWidget(); + customWidgets << new ListToStringListHash(&CFG_UI.General.DataEditorsOrder); + configMapper->setInternalCustomConfigWidgets(customWidgets); +} + +void ConfigDialog::initFormatterPlugins() +{ + ui->formatterPluginsTree->header()->setSectionsMovable(false); + ui->formatterPluginsTree->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->formatterPluginsTree->resizeColumnToContents(1); + ui->formatterPluginsTree->resizeColumnToContents(2); + + refreshFormattersPage(); +} + +void ConfigDialog::refreshFormattersPage() +{ + ui->formatterPluginsTree->clear(); + + QHash activeFormatters = CFG_CORE.General.ActiveCodeFormatter.get(); + + QList plugins = PLUGINS->getLoadedPlugins(); + QHash> groupedPlugins; + for (CodeFormatterPlugin* plugin : plugins) + groupedPlugins[plugin->getLanguage()] << plugin; + + formatterLangToPluginComboMap.clear(); + formatterLangToConfigButtonMap.clear(); + int row = 0; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + QToolButton* configButton = nullptr; + QStringList pluginTitles; + QStringList pluginNames; + QStringList sortedPluginNames; + QString selectedPluginName; + QModelIndex index; + QString groupName; + QHashIterator> it(groupedPlugins); + while (it.hasNext()) + { + it.next(); + groupName = it.key(); + + item = new QTreeWidgetItem({groupName}); + ui->formatterPluginsTree->addTopLevelItem(item); + + pluginNames.clear(); + pluginTitles.clear(); + for (CodeFormatterPlugin* plugin : it.value()) + { + pluginNames << plugin->getName(); + pluginTitles << plugin->getTitle(); + } + sortedPluginNames = pluginNames; + qSort(sortedPluginNames); + + combo = new QComboBox(ui->formatterPluginsTree); + for (int i = 0, total = pluginNames.size(); i < total; ++i) + combo->addItem(pluginTitles[i], pluginNames[i]); + + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(activeFormatterChanged())); + index = ui->formatterPluginsTree->model()->index(row, 1); + ui->formatterPluginsTree->setIndexWidget(index, combo); + formatterLangToPluginComboMap[groupName] = combo; + + if (activeFormatters.contains(groupName) && pluginNames.contains(activeFormatters[groupName].toString())) + { + selectedPluginName = activeFormatters[groupName].toString(); + } + else + { + // Pick first from sorted list and put it to combobox + selectedPluginName = sortedPluginNames.first(); + } + + configButton = new QToolButton(ui->formatterPluginsTree); + configButton->setIcon(ICONS.CONFIGURE); + index = ui->formatterPluginsTree->model()->index(row, 2); + ui->formatterPluginsTree->setIndexWidget(index, configButton); + connect(configButton, &QToolButton::clicked, [this, combo]() {configureFormatter(combo->currentText());}); + formatterLangToConfigButtonMap[groupName] = configButton; + + combo->setCurrentIndex(pluginNames.indexOf(selectedPluginName)); + + row++; + } + + updateActiveFormatterState(); +} + +void ConfigDialog::applyStyle(QWidget *widget, QStyle *style) +{ + widget->setStyle(style); + foreach (QObject* child, widget->children()) + { + if (!qobject_cast(child)) + continue; + + applyStyle(qobject_cast(child), style); + } +} + +QTreeWidgetItem* ConfigDialog::getPluginsCategoryItem() const +{ + QTreeWidgetItem* item = nullptr; + for (int i = 0; i < ui->categoriesTree->topLevelItemCount(); i++) + { + item = ui->categoriesTree->topLevelItem(i); + if (item->statusTip(0) == ui->pluginsPage->objectName()) + return item; + } + Q_ASSERT_X(true, "ConfigDialog", "No Plugins toplevel item in config categories tree!"); + return nullptr; +} + +QTreeWidgetItem* ConfigDialog::getPluginsCategoryItem(PluginType* type) const +{ + if (!pluginTypeToItemMap.contains(type)) + return nullptr; + + return pluginTypeToItemMap[type]; +} + +QTreeWidgetItem* ConfigDialog::getPluginItem(Plugin* plugin) const +{ + if (!pluginToItemMap.contains(plugin)) + return nullptr; + + return pluginToItemMap[plugin]; +} + +QTreeWidgetItem* ConfigDialog::createPluginsTypeItem(const QString& widgetName, const QString& title) const +{ + if (FORMS->hasWidget(widgetName)) + return new QTreeWidgetItem({title}); + + QTreeWidgetItem* pluginsCategoryItem = getPluginsCategoryItem(); + QTreeWidgetItem* item = nullptr; + for (int i = 0; i < pluginsCategoryItem->childCount(); i++) + { + item = pluginsCategoryItem->child(i); + if (item->statusTip(0) == widgetName) + return item; + } + return nullptr; + +} + +QTreeWidgetItem* ConfigDialog::getItemByTitle(const QString& title) const +{ + QList items = ui->categoriesTree->findItems(title, Qt::MatchExactly|Qt::MatchRecursive); + if (items.size() == 0) + return nullptr; + + return items.first(); +} + +void ConfigDialog::switchPage(QTreeWidgetItem *item) +{ + if (isPluginCategoryItem((item))) + { + switchPageToPlugin(item); + return; + } + + QString name = item->statusTip(0); + if (!nameToPage.contains(name)) + { + qWarning() << "Switched page to item" << name << "but there's no such named page defined in ConfigDialog."; + return; + } + + ui->stackedWidget->setCurrentWidget(nameToPage[name]); +} + +void ConfigDialog::switchPageToPlugin(QTreeWidgetItem *item) +{ + QString pluginName = item->statusTip(0); + if (!nameToPage.contains(pluginName)) + { + qCritical() << "No plugin page available for plugin:" << pluginName; + return; + } + ui->stackedWidget->setCurrentWidget(nameToPage[pluginName]); +} + +void ConfigDialog::initPlugins() +{ + QTreeWidgetItem *item = getPluginsCategoryItem(); + + // Recreate + QTreeWidgetItem *typeItem = nullptr; + foreach (PluginType* pluginType, PLUGINS->getPluginTypes()) + { + typeItem = createPluginsTypeItem(pluginType->getConfigUiForm(), pluginType->getTitle()); + if (!typeItem) + continue; + + item->addChild(typeItem); + pluginTypeToItemMap[pluginType] = typeItem; + + foreach (Plugin* plugin, pluginType->getLoadedPlugins()) + pluginLoaded(plugin, pluginType, true); + } + + updatePluginCategoriesVisibility(); + + connect(PLUGINS, SIGNAL(loaded(Plugin*,PluginType*)), this, SLOT(pluginLoaded(Plugin*,PluginType*))); + connect(PLUGINS, SIGNAL(aboutToUnload(Plugin*,PluginType*)), this, SLOT(pluginAboutToUnload(Plugin*,PluginType*))); +} + +void ConfigDialog::initPluginsPage() +{ + setValidStateTooltip(ui->pluginsList, tr("Plugins are loaded/unloaded immediately when checked/unchecked, " + "but modified list of plugins to load at startup is not saved until " + "you commit the whole configuration dialog.")); + + QTreeWidgetItem* category = nullptr; + QTreeWidgetItem* item = nullptr; + QFont font; + QModelIndex categoryIndex; + QModelIndex itemIndex; + int itemRow; + int categoryRow; + bool builtIn; + QLabel* detailsLabel = nullptr; + QString title; + QSize itemSize; + QStringList pluginNames; + + // Font and metrics + item = new QTreeWidgetItem({""}); + font = item->font(0); + + QFontMetrics fm(font); + itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); + + delete item; + + // Creating... + ui->pluginsList->header()->setSectionsMovable(false); + ui->pluginsList->header()->setSectionResizeMode(0, QHeaderView::Stretch); + + QBrush categoryBg = ui->pluginsList->palette().button(); + QBrush categoryFg = ui->pluginsList->palette().buttonText(); + + connect(ui->pluginsList, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(loadUnloadPlugin(QTreeWidgetItem*,int))); + connect(PLUGINS, SIGNAL(failedToLoad(QString)), this, SLOT(failedToLoadPlugin(QString))); + + categoryRow = 0; + QList pluginTypes = PLUGINS->getPluginTypes(); + qSort(pluginTypes.begin(), pluginTypes.end(), PluginType::nameLessThan); + foreach (PluginType* pluginType, pluginTypes) + { + category = new QTreeWidgetItem({pluginType->getTitle()}); + font.setItalic(false); + font.setBold(true); + category->setFont(0, font); + for (int i = 0; i < 2; i++) + { + category->setBackground(i, categoryBg); + category->setForeground(i, categoryFg); + } + category->setSizeHint(0, itemSize); + ui->pluginsList->addTopLevelItem(category); + + categoryIndex = ui->pluginsList->model()->index(categoryRow, 0); + categoryRow++; + + itemRow = 0; + pluginNames = pluginType->getAllPluginNames(); + qSort(pluginNames); + foreach (const QString& pluginName, pluginNames) + { + builtIn = PLUGINS->isBuiltIn(pluginName); + title = PLUGINS->getTitle(pluginName); + if (builtIn) + title += tr(" (built-in)", "plugins manager in configuration dialog"); + + item = new QTreeWidgetItem({title}); + item->setCheckState(0, PLUGINS->isLoaded(pluginName) ? Qt::Checked : Qt::Unchecked); + item->setSizeHint(0, itemSize); + if (builtIn) + item->setDisabled(true); + + category->addChild(item); + + itemToPluginNameMap.insert(item, pluginName); + + // Details button + detailsLabel = new QLabel(QString("%2 ").arg(pluginName).arg(tr("Details")), ui->pluginsList); + detailsLabel->setAlignment(Qt::AlignRight); + itemIndex = ui->pluginsList->model()->index(itemRow, 1, categoryIndex); + ui->pluginsList->setIndexWidget(itemIndex, detailsLabel); + + connect(detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(detailsClicked(QString))); + + itemRow++; + } + + if (itemRow == 0) + { + item = new QTreeWidgetItem({tr("No plugins in this category.")}); + item->setDisabled(true); + item->setSizeHint(0, itemSize); + + font.setItalic(true); + font.setBold(false); + item->setFont(0, font); + + category->addChild(item); + } + + category->setExpanded(true); + } +} + +bool ConfigDialog::initPluginPage(Plugin* plugin, bool skipConfigLoading) +{ + if (!dynamic_cast(plugin)) + return false; + + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + QString pluginName = plugin->getName(); + QString formName = cfgPlugin->getConfigUiForm(); + QWidget* widget = FORMS->createWidget(formName); + if (!widget) + { + qWarning() << "Could not load plugin UI file" << formName << "for plugin:" << pluginName; + return false; + } + + nameToPage[pluginName] = widget; + ui->stackedWidget->addWidget(widget); + CfgMain* mainConfig = cfgPlugin->getMainUiConfig(); + if (mainConfig) + { + pluginConfigMappers[cfgPlugin] = new ConfigMapper(mainConfig); + pluginConfigMappers[cfgPlugin]->bindToConfig(widget); + mainConfig->begin(); + } + else if (!skipConfigLoading) + { + configMapper->loadToWidget(widget); + } + + cfgPlugin->configDialogOpen(); + return true; +} + +void ConfigDialog::deinitPluginPage(Plugin* plugin) +{ + QString pluginName = plugin->getName(); + if (!nameToPage.contains(pluginName)) + return; + + if (!dynamic_cast(plugin)) + { + UiConfiguredPlugin* cfgPlugin = dynamic_cast(plugin); + CfgMain* mainCfg = cfgPlugin->getMainUiConfig(); + if (mainCfg) + mainCfg->rollback(); + + cfgPlugin->configDialogClosed(); + + if (pluginConfigMappers.contains(cfgPlugin)) + { + delete pluginConfigMappers[cfgPlugin]; + pluginConfigMappers.remove(cfgPlugin); + } + } + + QWidget* widget = nameToPage[pluginName]; + nameToPage.remove(pluginName); + ui->stackedWidget->removeWidget(widget); + delete widget; +} + +void ConfigDialog::initDataEditors() +{ + ui->dataEditorsAvailableList->setSpacing(1); + + QHash editorsOrder = CFG_UI.General.DataEditorsOrder.get(); + QSet dataTypeSet = editorsOrder.keys().toSet(); + dataTypeSet += DataType::getAllNames().toSet(); + QStringList dataTypeList = dataTypeSet.toList(); + qSort(dataTypeList); + + QListWidgetItem* item = nullptr; + for (const QString& type : dataTypeList) + { + item = new QListWidgetItem(type); + if (!DataType::getAllNames().contains(type)) + item->setFlags(item->flags()|Qt::ItemIsEditable); + + ui->dataEditorsTypesList->addItem(item); + } + + QAction* act = new QAction(ICONS.INSERT_DATATYPE, tr("Add new data type"), ui->dataEditorsTypesToolbar); + connect(act, SIGNAL(triggered()), this, SLOT(addDataType())); + ui->dataEditorsTypesToolbar->addAction(act); + + dataEditRenameAction = new QAction(ICONS.RENAME_DATATYPE, tr("Rename selected data type"), ui->dataEditorsTypesToolbar); + connect(dataEditRenameAction, SIGNAL(triggered()), this, SLOT(renameDataType())); + ui->dataEditorsTypesToolbar->addAction(dataEditRenameAction); + + dataEditDeleteAction = new QAction(ICONS.DELETE_DATATYPE, tr("Delete selected data type"), ui->dataEditorsTypesToolbar); + connect(dataEditDeleteAction, SIGNAL(triggered()), this, SLOT(delDataType())); + ui->dataEditorsTypesToolbar->addAction(dataEditDeleteAction); + + act = new QAction(ICONS.HELP, tr("Help for configuring data type editors"), ui->dataEditorsTypesToolbar); + connect(act, SIGNAL(triggered()), this, SLOT(dataTypesHelp())); + ui->dataEditorsTypesToolbar->addAction(act); + + connect(ui->dataEditorsTypesList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateDataTypeEditors())); + connect(ui->dataEditorsTypesList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateDataTypeListState())); + connect(ui->dataEditorsTypesList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(dataEditorItemEdited(QListWidgetItem*))); + connect(ui->dataEditorsAvailableList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(dataEditorAvailableChanged(QListWidgetItem*))); + connect(ui->dataEditorsSelectedTabs->tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(dataEditorTabsOrderChanged(int,int))); + + ui->dataEditorsTypesList->setCurrentRow(0, QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent); + updateDataTypeListState(); +} + +void ConfigDialog::initShortcuts() +{ + ui->shortcutsTable->header()->setSectionsMovable(false); + ui->shortcutsTable->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->shortcutsTable->header()->setSectionResizeMode(1, QHeaderView::Fixed); + ui->shortcutsTable->header()->setSectionResizeMode(2, QHeaderView::Fixed); + ui->shortcutsTable->header()->resizeSection(1, 150); + ui->shortcutsTable->header()->resizeSection(2, 26); + + ui->shortcutsFilterEdit->setClearButtonEnabled(true); + new UserInputFilter(ui->shortcutsFilterEdit, this, SLOT(applyShortcutsFilter(QString))); + + static const QString metaName = CFG_SHORTCUTS_METANAME; + QList categories; + for (CfgMain* cfgMain : CfgMain::getInstances()) + { + if (cfgMain->getMetaName() != metaName) + continue; + + for (CfgCategory* cat : cfgMain->getCategories().values()) + categories << cat; + } + + qSort(categories.begin(), categories.end(), [](CfgCategory* cat1, CfgCategory* cat2) -> bool + { + return cat1->getTitle().compare(cat2->getTitle()) < 0; + }); + + for (CfgCategory* cat : categories) + initShortcuts(cat); +} + +void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) +{ + QTreeWidgetItem* item = nullptr; + QFont font; + QModelIndex categoryIndex; + QModelIndex itemIndex; + QKeySequenceEdit *sequenceEdit = nullptr; + QToolButton* clearButton = nullptr; + QString title; + QSize itemSize; + + // Font and metrics + item = new QTreeWidgetItem({""}); + font = item->font(0); + + QFontMetrics fm(font); + itemSize = QSize(-1, (fm.ascent() + fm.descent() + 4)); + + delete item; + + // Creating... + QBrush categoryBg = ui->shortcutsTable->palette().button(); + QBrush categoryFg = ui->shortcutsTable->palette().buttonText(); + + QTreeWidgetItem* category = new QTreeWidgetItem({cfgCategory->getTitle()}); + font.setItalic(false); + font.setBold(true); + category->setFont(0, font); + for (int i = 0; i < 3; i++) + { + category->setBackground(i, categoryBg); + category->setForeground(i, categoryFg); + } + category->setSizeHint(0, itemSize); + category->setFlags(category->flags() ^ Qt::ItemIsSelectable); + ui->shortcutsTable->addTopLevelItem(category); + + int categoryRow = ui->shortcutsTable->topLevelItemCount() - 1; + categoryIndex = ui->shortcutsTable->model()->index(categoryRow, 0); + + int itemRow = 0; + QStringList entryNames = cfgCategory->getEntries().keys(); + qSort(entryNames); + foreach (const QString& entryName, entryNames) + { + // Title + title = cfgCategory->getEntries()[entryName]->getTitle(); + item = new QTreeWidgetItem(category, {title}); + + // Key edit + sequenceEdit = new QKeySequenceEdit(ui->shortcutsTable); + sequenceEdit->setFixedWidth(150); + sequenceEdit->setProperty("cfg", cfgCategory->getEntries()[entryName]->getFullKey()); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 1, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, sequenceEdit); + configMapper->addExtraWidget(sequenceEdit); + + // Clear button + clearButton = new QToolButton(ui->shortcutsTable); + clearButton->setIcon(ICONS.CLEAR_LINEEDIT); + connect(clearButton, &QToolButton::clicked, [this, sequenceEdit]() + { + sequenceEdit->clear(); + this->markModified(); + + }); + itemIndex = ui->shortcutsTable->model()->index(itemRow, 2, categoryIndex); + ui->shortcutsTable->setIndexWidget(itemIndex, clearButton); + + itemRow++; + } + + category->setExpanded(true); +} + +bool ConfigDialog::isPluginCategoryItem(QTreeWidgetItem *item) const +{ + return item->parent() && item->parent()->parent() && item->parent()->parent() == getPluginsCategoryItem(); +} + +void ConfigDialog::updateStylePreview() +{ + ui->previewWidget->parentWidget()->layout()->removeWidget(ui->previewWidget); + ui->previewTabs->currentWidget()->layout()->addWidget(ui->previewWidget); + ui->previewWidget->setEnabled(ui->previewTabs->currentIndex() == 0); + + QStyle* previousStyle = previewStyle; + previewStyle = QStyleFactory::create(ui->activeStyleCombo->currentText()); + if (!previewStyle) + { + qWarning() << "Could not create style:" << ui->activeStyleCombo->currentText(); + return; + } + + applyStyle(ui->activeStylePreviewGroup, previewStyle); + + if (previousStyle) + delete previousStyle; +} + +void ConfigDialog::apply() +{ + if (modifiedFlag) + save(); + + setModified(false); +} + +void ConfigDialog::accept() +{ + apply(); + QDialog::accept(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h new file mode 100644 index 0000000..95e9f1a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.h @@ -0,0 +1,141 @@ +#ifndef CONFIGDIALOG_H +#define CONFIGDIALOG_H + +#include "config_builder.h" +#include "datatype.h" +#include "common/bihash.h" +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class ConfigDialog; +} + +class QListWidgetItem; +class QTreeWidgetItem; +class CustomConfigWidgetPlugin; +class QSignalMapper; +class Plugin; +class PluginType; +class QComboBox; +class QToolButton; +class QTreeWidget; +class QListWidget; +class QTableWidget; +class ConfigMapper; +class MultiEditorWidgetPlugin; +class ConfigNotifiablePlugin; +class UiConfiguredPlugin; + +class GUI_API_EXPORT ConfigDialog : public QDialog +{ + Q_OBJECT + + public: + explicit ConfigDialog(QWidget *parent = 0); + ~ConfigDialog(); + + void configureDataEditors(const QString& dataTypeString); + + static QString getFilterString(QWidget* widget); + static QString getFilterString(QComboBox* widget); + static QString getFilterString(QTreeWidget* widget); + static QString getFilterString(QListWidget* widget); + static QString getFilterString(QTableWidget* widget); + + private: + void init(); + void load(); + void initPageMap(); + void initInternalCustomConfigWidgets(); + void initFormatterPlugins(); + void initPlugins(); + void initPluginsPage(); + bool initPluginPage(Plugin* plugin, bool skipConfigLoading); + void deinitPluginPage(Plugin* pluginName); + void initDataEditors(); + void initShortcuts(); + void initShortcuts(CfgCategory* cfgCategory); + void applyStyle(QWidget* widget, QStyle* style); + QTreeWidgetItem* getPluginsCategoryItem() const; + QTreeWidgetItem* getPluginsCategoryItem(PluginType* type) const; + QTreeWidgetItem* getPluginItem(Plugin* plugin) const; + QTreeWidgetItem* createPluginsTypeItem(const QString& widgetName, const QString& title) const; + QTreeWidgetItem* getItemByTitle(const QString& title) const; + void switchPageToPlugin(QTreeWidgetItem* item); + bool isPluginCategoryItem(QTreeWidgetItem *item) const; + void codeFormatterUnloaded(); + void codeFormatterLoaded(); + void updatePluginCategoriesVisibility(QTreeWidgetItem* categoryItem); + QString collectLoadedPlugins() const; + QHash buildPageToCategoryItemMap() const; + QList getAllCategoryItems() const; + QList getDefaultEditorsForType(DataType::Enum dataType); + QList updateCustomDataTypeEditors(const QStringList& editorsOrder); + QList updateDefaultDataTypeEditors(DataType::Enum typeEnum); + void addDataTypeEditor(const QString& pluginName); + void addDataTypeEditor(MultiEditorWidgetPlugin* plugin); + void removeDataTypeEditor(QListWidgetItem* item, const QString& pluginName); + void removeDataTypeEditor(int idx); + void transformDataTypeEditorsToCustomList(QListWidgetItem* typeItem); + QStringList getPluginNamesFromDataTypeItem(QListWidgetItem* typeItem, bool* exists = nullptr); + void setPluginNamesForDataTypeItem(QListWidgetItem* typeItem, const QStringList& pluginNames); + void addDataType(const QString& typeStr); + void rollbackPluginConfigs(); + void commitPluginConfigs(); + + Ui::ConfigDialog *ui = nullptr; + QStyle* previewStyle = nullptr; + QHash nameToPage; + BiHash itemToPluginNameMap; + QHash pluginTypeToItemMap; + QHash pluginToItemMap; + QHash formatterLangToPluginComboMap; + QHash formatterLangToConfigButtonMap; + ConfigMapper* configMapper = nullptr; + QHash pluginConfigMappers; + QAction* dataEditRenameAction = nullptr; + QAction* dataEditDeleteAction = nullptr; + bool updatingDataEditorItem = false; + bool modifiedFlag = false; + QList notifiablePlugins; + + private slots: + void refreshFormattersPage(); + void pageSwitched(); + void updateDataTypeEditors(); + void updateDataTypeListState(); + void dataEditorItemEdited(QListWidgetItem* item); + void dataEditorAvailableChanged(QListWidgetItem* item); + void dataEditorTabsOrderChanged(int from, int to); + void addDataType(); + void renameDataType(); + void delDataType(); + void dataTypesHelp(); + void switchPage(QTreeWidgetItem* item); + void updateStylePreview(); + void apply(); + void save(); + void storeSelectedFormatters(); + void markModified(); + void setModified(bool modified); + void updateModified(); + void applyFilter(const QString& filter); + void updateActiveFormatterState(); + void configureFormatter(const QString& pluginTitle); + void activeFormatterChanged(); + void detailsClicked(const QString& pluginName); + void failedToLoadPlugin(const QString& pluginName); + void loadUnloadPlugin(QTreeWidgetItem* item, int column); + void pluginAboutToUnload(Plugin* plugin, PluginType* type); + void pluginLoaded(Plugin* plugin, PluginType* type, bool skipConfigLoading = false); + void pluginUnloaded(const QString& pluginName, PluginType* type); + void updatePluginCategoriesVisibility(); + void updateBuiltInPluginsVisibility(); + void applyShortcutsFilter(const QString& filter); + + public slots: + void accept(); +}; + +#endif // CONFIGDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui new file mode 100644 index 0000000..82cc286 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui @@ -0,0 +1,1923 @@ + + + ConfigDialog + + + + 0 + 0 + 770 + 539 + + + + Configuration + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::PreventContextMenu + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + QAbstractItemView::ScrollPerPixel + + + false + + + + 1 + + + + + General + + + generalPage + + + + :/icons/img/config_general.png:/icons/img/config_general.png + + + + + Keyboard shortcuts + + + shortcutsPage + + + + :/icons/img/keyboard.png:/icons/img/keyboard.png + + + + + Look & feel + + + lookAndFeelPage + + + + :/icons/img/config_look_and_feel.png:/icons/img/config_look_and_feel.png + + + + Style + + + stylePage + + + + :/icons/img/config_style.png:/icons/img/config_style.png + + + + + Fonts + + + fontsPage + + + + :/icons/img/config_font.png:/icons/img/config_font.png + + + + + Colors + + + colorsPage + + + + :/icons/img/config_colors.png:/icons/img/config_colors.png + + + + + + Plugins + + + pluginsPage + + + + :/icons/img/plugin.png:/icons/img/plugin.png + + + + Code formatters + + + formatterPluginsPage + + + + + + Data browsing + + + dataBrowsingPage + + + + :/icons/img/table.png:/icons/img/table.png + + + + Data editors + + + dataEditorsPage + + + + :/icons/img/config_data_editors.png:/icons/img/config_data_editors.png + + + + + + + + + + + 5 + 0 + + + + 2 + + + + + + + Data browsing and editing + + + + + + Number of data rows per page: + + + + + + + + 150 + 16777215 + + + + 1 + + + 99999 + + + General.NumberOfRowsPerPage + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + + + 1 + 0 + + + + Data types + + + + + + + + + General.DataEditorsOrder + + + + + + + + + 3 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Available editors: + + + + + + true + + + + + + + + + + Editors selected for this data type: + + + + + + true + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Schema editing + + + + + + + 150 + 16777215 + + + + Number of DDL changes kept in history. + + + 9999999 + + + General.DdlHistorySize + + + + + + + DDL history size: + + + + + + + Don't show DDL preview dialog when commiting schema changes + + + General.DontShowDdlPreview + + + + + + + + + + SQL queries + + + + + + + 150 + 16777215 + + + + Number of queries kept in the history. + + + 999999 + + + General.SqlHistorySize + + + + + + + Number of queries kept in the history. + + + History size: + + + + + + + <p>If there is more than one query in the SQL editor window, then (if this option is enabled) only a single query will be executed - the one under the keyboard insertion cursor. Otherwise all queries will be executed. You can always limit queries to be executed by selecting those queries before calling to execute.</p> + + + Execute only the query under the cursor + + + General.ExecuteCurrentQueryOnly + + + + + + + + + + Updates + + + + + + Automatically check for updates at startup + + + General.CheckUpdatesOnStartup + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Filter shortcuts by name or key combination + + + + + + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::SingleSelection + + + 0 + + + false + + + true + + + false + + + 150 + + + 16 + + + false + + + + Action + + + + + Key combination + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Database list + + + + + + If switched off, then columns will be sorted in the order they are typed in CREATE TABLE statement. + + + Sort table columns alphabetically + + + General.SortColumns + + + + + + + Expand tables node when connected to a database + + + General.ExpandTables + + + + + + + <p>Additional labels are those displayed next to the names on the databases list (they are blue, unless configured otherwise). Enabling this option will result in labels for databases, invalid databases and aggregated nodes (column group, index group, trigger group). For more labels see options below.<p> + + + Display additional labels on the list + + + true + + + false + + + General.ShowDbTreeLabels + + + + + + For regular tables labels will show number of columns, indexes and triggers for each of tables. + + + Display labels for regular tables + + + General.ShowRegularTableLabels + + + + + + + Virtual tables will be marked with a 'virtual' label. + + + Display labels for virtual tables + + + General.ShowVirtualTableLabels + + + + + + + + + + Expand views node when connected to a database + + + General.ExpandViews + + + + + + + If this option is switched off, then objects will be sorted in order they appear in sqlite_master table (which is in order they were created) + + + Sort objects (tables, indexes, triggers and views) alphabetically + + + General.SortObjects + + + + + + + Display system tables and indexes on the list + + + General.ShowSystemObjects + + + + + + + + + + Table windows + + + + + + When enabled, Table Windows will show up with the data tab, instead of the structure tab. + + + Open Table Windows with the data tab for start + + + General.OpenTablesOnData + + + + + + + + + + View windows + + + + + + When enabled, View Windows will show up with the data tab, instead of the structure tab. + + + Open View Windows with the data tab for start + + + General.OpenViewsOnData + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + 0 + + + false + + + false + + + 2 + + + false + + + false + + + + 1 + + + + + 2 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Hide built-in plugins + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Current style: + + + + + + + General.Style + + + + + + + + + + Preview + + + + + + 0 + + + + Enabled + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 24 + + + + + + + CheckBox + + + + + + + 50 + + + Qt::Vertical + + + + + + + Qt::Vertical + + + + + + + PushButton + + + + + + + + + + 0 + + + 0 + + + Qt::AlignCenter + + + + + + + + Column + + + + + 123 + + + + 11111 + + + + + 22222 + + + + + 33333 + + + + + + 456 + + + + 44444 + + + + + 55555 + + + + + 66666 + + + + + + + + + ... + + + + + + + + + + RadioButton + + + + + + + + ABC + + + + + XYZ + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Abcdefgh</span></p></body></html> + + + + + + + + + + + Disabled + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + false + + + + Language + + + + + Active formatter plugin + + + + + Configuration + + + + + + + + + + + + QFrame::NoFrame + + + 0 + + + true + + + + + 0 + 0 + 258 + 286 + + + + + + + SQL editor font + + + + + + Fonts.SqlEditor + + + + + + + + + + Database list font + + + + + + Fonts.DbTree + + + + + + + + + + Database list additional label font + + + + + + Fonts.DbTreeLabel + + + + + + + + + + Data view font + + + + + + Fonts.DataView + + + + + + + + + + Status field font + + + + + + Fonts.StatusField + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + QFrame::NoFrame + + + 0 + + + true + + + + + 0 + 0 + 307 + 666 + + + + + + + SQL editor colors + + + + + + Current line background + + + + + + + <p>SQL strings are enclosed with single quote characters.</p> + + + String foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorKeywordFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorStringFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorCommentFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorForeground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorCurrentLineBg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorLineNumAreaBg + + + + + + + <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> + + + Bind parameter foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorParenthesisBg + + + + + + + Highlighted parenthesis background + + + + + + + <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> + + + BLOB value foreground + + + + + + + Regular foreground + + + + + + + Line numbers area background + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorBlobFg + + + + + + + Keyword foreground + + + + + + + Number foreground + + + + + + + Comment foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorNumberFg + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorBindParamFg + + + + + + + <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> + + + Valid objects foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.SqlEditorValidObject + + + + + + + + + + Data view colors + + + + + + <p>Any data changes will be outlined with this color, until they're commited to the database.</p> + + + Uncommited data outline color + + + + + + + + 50 + 16777215 + + + + + + + Colors.DataUncommited + + + + + + + <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> + + + Commit error outline color + + + + + + + + 50 + 16777215 + + + + + + + Colors.DataUncommitedError + + + + + + + NULL value foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.DataNullFg + + + + + + + Deleted row background + + + + + + + + 50 + 16777215 + + + + + + + Colors.DataDeletedBg + + + + + + + + + + Database list colors + + + + + + <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> + + + Additional labels foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.DbTreeLabelsFg + + + + + + + + + + Status field colors + + + + + + Information message foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.StatusFieldInfoFg + + + + + + + Warning message foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.StatusFieldWarnFg + + + + + + + Error message foreground + + + + + + + + 50 + 16777215 + + + + + + + Colors.StatusFieldErrorFg + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 100 + 30 + + + + + + + + ColorButton + QPushButton +
    common/colorbutton.h
    +
    + + FontEdit + QWidget +
    common/fontedit.h
    + 1 +
    +
    + + categoriesFilter + categoriesTree + pluginsList + buttonBox + expandViewsCheck + sortObjects + sortColumns + ddlHistorySizeSpin + dontShowDdlPreview + queryHistorySizeSpin + checkBox + expandTablesCheck + activeStyleCombo + previewTabs + previewCheckBox + previewVerticalSlider + previewPushButton + previewSpinBox + previewTreeWidget + previewToolButton + previewLineEdit + previewRadioButton + previewComboBox + previewTextEdit + fontsScrollArea + scrollArea + sqlEditorKeywordFgButton + sqlEditorStringFgButton + sqlEditorCommentFgButton + sqlEditorRegularFgButton + sqlEditorCurrLineBgButton + sqlEditorLineNumAreaBgButton + sqlEditorParBgButton + sqlEditorBlobFgButton + sqlEditorNumberFgButton + sqlEditorBindParamFgButton + sqlEditorValidObjectsButton + dataViewUncommitedButton + dataViewErrorButton + dataViewNullFgButton + dataViewDeletedRowBgButton + dbTreeLabelsButton + statusFieldInfoButton + statusFieldWarnButton + statusFieldErrorButton + + + + + + + buttonBox + accepted() + ConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp new file mode 100644 index 0000000..0094ad0 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp @@ -0,0 +1,213 @@ +#include "constraintdialog.h" +#include "ui_constraintdialog.h" +#include "iconmanager.h" +#include "constraints/constraintpanel.h" +#include +#include + +ConstraintDialog::ConstraintDialog(Mode mode, SqliteCreateTable::Constraint* constraint, SqliteCreateTable* createTable, Db* db, QWidget* parent) : + QDialog(parent), + ui(new Ui::ConstraintDialog), + mode(mode), + db(db) +{ + ui->setupUi(this); + type = TABLE; + constrStatement = constraint; + this->createTable = createTable; + init(); +} + +ConstraintDialog::ConstraintDialog(Mode mode, SqliteCreateTable::Column::Constraint* constraint, SqliteCreateTable::Column* column, Db* db, QWidget* parent) : + QDialog(parent), + ui(new Ui::ConstraintDialog), + mode(mode), + db(db) +{ + ui->setupUi(this); + type = COLUMN; + constrStatement = constraint; + this->columnStmt = column; + createTable = dynamic_cast(column->parent()); + init(); +} + +ConstraintDialog::~ConstraintDialog() +{ + delete ui; +} + +SqliteStatement* ConstraintDialog::getConstraint() +{ + return constrStatement; +} + +void ConstraintDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void ConstraintDialog::init() +{ + switch (mode) + { + case ConstraintDialog::NEW: + setWindowTitle(tr("New constraint", "constraint dialog")); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Create", "constraint dialog")); + break; + case ConstraintDialog::EDIT: + setWindowTitle(tr("Edit constraint", "dialog window")); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Apply", "constraint dialog")); + break; + } + + connect(this, SIGNAL(accepted()), this, SLOT(storeConfiguration())); + + // Panel object + currentPanel = createConstraintPanel(); + if (!currentPanel) + { + qCritical() << "The constraint panel was not constructed. Probably the constraint type was invalid."; + return; + } + + currentPanel->setDb(db); + currentPanel->setConstraint(constrStatement); + + connect(currentPanel, SIGNAL(updateValidation()), this, SLOT(validate())); + validate(); + + // Put everything in place + updateDefinitionHeader(); + ui->definitionWidget->layout()->addWidget(currentPanel); + + adjustSize(); + currentPanel->setFocus(); +} + +ConstraintDialog::Constraint ConstraintDialog::getSelectedConstraint() +{ + switch (type) + { + case ConstraintDialog::TABLE: + return getSelectedConstraint(dynamic_cast(constrStatement)); + case ConstraintDialog::COLUMN: + return getSelectedConstraint(dynamic_cast(constrStatement)); + } + return UNKNOWN; +} + +ConstraintDialog::Constraint ConstraintDialog::getSelectedConstraint(SqliteCreateTable::Constraint* constraint) +{ + switch (constraint->type) + { + case SqliteCreateTable::Constraint::PRIMARY_KEY: + return PK; + case SqliteCreateTable::Constraint::UNIQUE: + return UNIQUE; + case SqliteCreateTable::Constraint::CHECK: + return CHECK; + case SqliteCreateTable::Constraint::FOREIGN_KEY: + return FK; + case SqliteCreateTable::Constraint::NAME_ONLY: + break; + } + return UNKNOWN; +} + +ConstraintDialog::Constraint ConstraintDialog::getSelectedConstraint(SqliteCreateTable::Column::Constraint* constraint) +{ + switch (constraint->type) + { + case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: + return PK; + case SqliteCreateTable::Column::Constraint::NOT_NULL: + return NOTNULL; + case SqliteCreateTable::Column::Constraint::UNIQUE: + return UNIQUE; + case SqliteCreateTable::Column::Constraint::CHECK: + return CHECK; + case SqliteCreateTable::Column::Constraint::DEFAULT: + return DEFAULT; + case SqliteCreateTable::Column::Constraint::COLLATE: + return COLLATE; + case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: + return FK; + case SqliteCreateTable::Column::Constraint::NULL_: + case SqliteCreateTable::Column::Constraint::NAME_ONLY: + case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: + break; + } + return UNKNOWN; +} + +ConstraintPanel* ConstraintDialog::createConstraintPanel() +{ + if (!constrStatement) + return nullptr; + + if (type == COLUMN) + return ConstraintPanel::produce(dynamic_cast(constrStatement)); + else + return ConstraintPanel::produce(dynamic_cast(constrStatement)); +} + +void ConstraintDialog::updateDefinitionHeader() +{ + switch (getSelectedConstraint()) + { + case ConstraintDialog::UNKNOWN: + return; + case ConstraintDialog::PK: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_PRIMARY_KEY); + ui->titleLabel->setText(tr("Primary key", "table constraints")); + break; + case ConstraintDialog::FK: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_FOREIGN_KEY); + ui->titleLabel->setText(tr("Foreign key", "table constraints")); + break; + case ConstraintDialog::UNIQUE: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_UNIQUE); + ui->titleLabel->setText(tr("Unique", "table constraints")); + break; + case ConstraintDialog::NOTNULL: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_NOT_NULL); + ui->titleLabel->setText(tr("Not NULL", "table constraints")); + break; + case ConstraintDialog::CHECK: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_CHECK); + ui->titleLabel->setText(tr("Check", "table constraints")); + break; + case ConstraintDialog::COLLATE: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_COLLATION); + ui->titleLabel->setText(tr("Collate", "table constraints")); + break; + case ConstraintDialog::DEFAULT: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_DEFAULT); + ui->titleLabel->setText(tr("Default", "table constraints")); + break; + } +} + +void ConstraintDialog::validate() +{ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(currentPanel->validate()); +} + +void ConstraintDialog::storeConfiguration() +{ + if (!currentPanel) + { + qWarning() << "Called to store constraint configuration, but there's no current panel."; + return; + } + + currentPanel->storeDefinition(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h new file mode 100644 index 0000000..fe24c0f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h @@ -0,0 +1,79 @@ +#ifndef CONSTRAINTDIALOG_H +#define CONSTRAINTDIALOG_H + +#include "parser/ast/sqlitecreatetable.h" +#include "db/db.h" +#include "guiSQLiteStudio_global.h" +#include +#include + +namespace Ui { + class ConstraintDialog; +} + +class ConstraintPanel; + +class GUI_API_EXPORT ConstraintDialog : public QDialog +{ + Q_OBJECT + + public: + enum Mode + { + NEW, + EDIT + }; + + enum Constraint + { + PK, + FK, + UNIQUE, + NOTNULL, + CHECK, + COLLATE, + DEFAULT, + UNKNOWN + }; + + enum Type + { + TABLE, + COLUMN + }; + + explicit ConstraintDialog(Mode mode, SqliteCreateTable::Constraint* constraint, SqliteCreateTable* createTable, Db* db, + QWidget *parent = 0); + explicit ConstraintDialog(Mode mode, SqliteCreateTable::Column::Constraint* constraint, SqliteCreateTable::Column* column, Db* db, + QWidget *parent = 0); + ~ConstraintDialog(); + + SqliteStatement* getConstraint(); + + protected: + void changeEvent(QEvent *e); + + private: + void init(); + Constraint getSelectedConstraint(); + Constraint getSelectedConstraint(SqliteCreateTable::Constraint* constraint); + Constraint getSelectedConstraint(SqliteCreateTable::Column::Constraint* constraint); + ConstraintPanel* createConstraintPanel(); + void updateDefinitionHeader(); + + Ui::ConstraintDialog *ui = nullptr; + Type type; + Mode mode; + Db* db = nullptr; + SqliteStatement* constrStatement = nullptr; + QPointer createTable; + QPointer columnStmt; + QHash panels; + ConstraintPanel* currentPanel = nullptr; + + private slots: + void validate(); + void storeConfiguration(); +}; + +#endif // CONSTRAINTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.ui new file mode 100644 index 0000000..7df34d8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.ui @@ -0,0 +1,113 @@ + + + ConstraintDialog + + + + 0 + 0 + 400 + 300 + + + + + 400 + 0 + + + + Dialog + + + + + + + + + + 18 + 16777215 + + + + + + + + + + + + 75 + true + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ConstraintDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConstraintDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp new file mode 100644 index 0000000..c94546e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp @@ -0,0 +1,219 @@ +#include "dbconverterdialog.h" +#include "ui_dbconverterdialog.h" +#include "common/global.h" +#include "dblistmodel.h" +#include "db/db.h" +#include "common/utils_sql.h" +#include "dbversionconverter.h" +#include "services/dbmanager.h" +#include "iconmanager.h" +#include "uiutils.h" +#include "versionconvertsummarydialog.h" +#include "mainwindow.h" +#include "errorsconfirmdialog.h" +#include "parser/ast/sqlitecreatetable.h" +#include "services/pluginmanager.h" +#include "plugins/dbplugin.h" +#include "db/sqlquery.h" +#include "services/notifymanager.h" +#include "common/widgetcover.h" +#include +#include +#include + +DbConverterDialog::DbConverterDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::DbConverterDialog) +{ + init(); +} + +DbConverterDialog::~DbConverterDialog() +{ + delete ui; + safe_delete(converter); +} + +void DbConverterDialog::setDb(Db* db) +{ + ui->srcDbCombo->setCurrentText(db->getName()); + srcDb = db; + srcDbChanged(); +} + +void DbConverterDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + setWindowTitle(tr("Convert database")); + + widgetCover = new WidgetCover(this); + widgetCover->setVisible(false); + widgetCover->initWithInterruptContainer(); + + ui->trgFileButton->setIcon(ICONS.OPEN_FILE); + + converter = new DbVersionConverter(); + + dbListModel = new DbListModel(this); + ui->srcDbCombo->setModel(dbListModel); + + connect(ui->srcDbCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(srcDbChanged(int))); + connect(ui->trgVersionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState())); + connect(ui->trgFileEdit, SIGNAL(textChanged(QString)), this, SLOT(updateState())); + connect(ui->trgNameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateState())); + connect(converter, SIGNAL(conversionFailed(QString)), this, SLOT(processingFailed(QString))); + connect(converter, SIGNAL(conversionSuccessful()), this, SLOT(processingSuccessful())); + connect(converter, SIGNAL(conversionAborted()), this, SLOT(processingAborted())); + connect(widgetCover, SIGNAL(cancelClicked()), converter, SLOT(interrupt())); +} + +void DbConverterDialog::srcDbChanged() +{ + dontUpdateState = true; + ui->srcDbVersionCombo->clear(); + ui->trgVersionCombo->clear(); + if (srcDb) + { + // Source version + QList dialects = converter->getSupportedVersions(); + QStringList versionNames = converter->getSupportedVersionNames(); + Dialect dialect = srcDb->getDialect(); + int idx = dialects.indexOf(dialect); + QString type = versionNames[idx]; + ui->srcDbVersionCombo->addItem(type); + ui->srcDbVersionCombo->setCurrentText(type); + + // Target version + QString oldTrgVersion = ui->trgVersionCombo->currentText(); + versionNames.removeAt(idx); + ui->trgVersionCombo->addItems(versionNames); + if (versionNames.contains(oldTrgVersion)) + ui->trgVersionCombo->setCurrentText(oldTrgVersion); + else if (versionNames.size() > 0) + ui->trgVersionCombo->setCurrentIndex(0); + + // File + QString trgFile = srcDb->getPath() + "_new"; + int i = 0; + while (QFileInfo(trgFile).exists()) + { + trgFile = srcDb->getPath() + "_new" + QString::number(i++); + } + + ui->trgFileEdit->setText(trgFile); + + // Name + QString generatedName = generateUniqueName(srcDb->getName() + "_new", DBLIST->getDbNames()); + ui->trgNameEdit->setText(generatedName); + } + else + { + ui->srcDbVersionCombo->setCurrentText(""); + ui->trgFileEdit->setText(""); + ui->trgVersionCombo->setCurrentText(""); + ui->trgNameEdit->setText(""); + } + dontUpdateState = false; + updateState(); +} + +bool DbConverterDialog::validate() +{ + bool srcDbOk = (srcDb != nullptr); + setValidState(ui->srcDbCombo, srcDbOk, tr("Select source database")); + + QString dstDbPath = ui->trgFileEdit->text(); + QFileInfo dstDbFi(dstDbPath); + bool dstDbOk = (!dstDbFi.exists() || dstDbFi.isWritable()) && dstDbFi != QFileInfo(srcDb->getPath()); + bool dstExists = dstDbFi.exists(); + setValidState(ui->trgFileEdit, dstDbOk, tr("Enter valid and writable file path.")); + if (dstExists && dstDbOk) + setValidStateInfo(ui->trgFileEdit, tr("Entered file exists and will be overwritten.")); + + QString name = ui->trgNameEdit->text(); + bool nameOk = !name.isEmpty() && !DBLIST->getDbNames().contains(name); + setValidState(ui->trgNameEdit, nameOk, tr("Enter a not empty, unique name (as in the list of databases on the left).")); + + bool dstDialectOk = ui->trgVersionCombo->currentIndex() > -1; + QString msg; + if (!dstDialectOk && ui->trgVersionCombo->count() == 0) + msg = tr("No valid target dialect available. Conversion not possible."); + else + msg = tr("Select valid target dialect."); + + setValidState(ui->trgVersionCombo, dstDialectOk, msg); + + return (srcDbOk && nameOk && dstDbOk && dstDialectOk); +} + +void DbConverterDialog::accept() +{ + if (!validate()) + return; + + QStringList versionNames = converter->getSupportedVersionNames(); + QList dialects = converter->getSupportedVersions(); + QString trgDialectName = ui->trgVersionCombo->currentText(); + int idx = versionNames.indexOf(trgDialectName); + if (idx == -1) + { + qCritical() << "Could not find target dialect on list of supported dialects in DbConverterDialog::accept()"; + return; + } + + Dialect srcDialect = srcDb->getDialect(); + Dialect trgDialect = dialects[idx]; + QString trgFile = ui->trgFileEdit->text(); + QString trgName = ui->trgNameEdit->text(); + widgetCover->show(); + converter->convert(srcDialect, trgDialect, srcDb, trgFile, trgName, &DbConverterDialog::confirmConversion, &DbConverterDialog::confirmConversionErrors); +} + +void DbConverterDialog::srcDbChanged(int index) +{ + srcDb = dbListModel->getDb(index); + srcDbChanged(); +} + +void DbConverterDialog::updateState() +{ + if (dontUpdateState) + return; + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(validate()); +} + +void DbConverterDialog::processingFailed(const QString& errorMessage) +{ + widgetCover->hide(); + notifyError(errorMessage); +} + +void DbConverterDialog::processingSuccessful() +{ + notifyInfo(tr("Database %1 has been successfully converted and now is available under new name: %2").arg(srcDb->getName(), ui->trgNameEdit->text())); + QDialog::accept(); +} + +void DbConverterDialog::processingAborted() +{ + widgetCover->hide(); +} + +bool DbConverterDialog::confirmConversion(const QList >& diffs) +{ + VersionConvertSummaryDialog dialog(MAINWINDOW); + dialog.setWindowTitle(tr("SQL statements conversion")); + dialog.setSides(diffs); + return dialog.exec() == QDialog::Accepted; +} + +bool DbConverterDialog::confirmConversionErrors(const QSet& errors) +{ + ErrorsConfirmDialog dialog(MAINWINDOW); + dialog.setTopLabel(tr("Following error occurred while converting SQL statements to the target SQLite version:")); + dialog.setBottomLabel(tr("Would you like to ignore those errors and proceed?")); + dialog.setErrors(errors); + return dialog.exec() == QDialog::Accepted; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h new file mode 100644 index 0000000..4267a1b --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h @@ -0,0 +1,52 @@ +#ifndef DBCONVERTERDIALOG_H +#define DBCONVERTERDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +class DbListModel; +class Db; +class DbVersionConverter; +class WidgetCover; + +namespace Ui { + class DbConverterDialog; +} + +class GUI_API_EXPORT DbConverterDialog : public QDialog +{ + Q_OBJECT + + public: + explicit DbConverterDialog(QWidget *parent = 0); + ~DbConverterDialog(); + + void setDb(Db* db); + + private: + void init(); + void srcDbChanged(); + bool validate(); + + static bool confirmConversion(const QList >& diffs); + static bool confirmConversionErrors(const QSet& errors); + + Ui::DbConverterDialog *ui = nullptr; + DbListModel* dbListModel = nullptr; + Db* srcDb = nullptr; + DbVersionConverter* converter = nullptr; + bool dontUpdateState = false; + WidgetCover* widgetCover = nullptr; + + public slots: + void accept(); + + private slots: + void srcDbChanged(int index); + void updateState(); + void processingFailed(const QString& errorMessage); + void processingSuccessful(); + void processingAborted(); +}; + +#endif // DBCONVERTERDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui new file mode 100644 index 0000000..d328e99 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui @@ -0,0 +1,144 @@ + + + DbConverterDialog + + + + 0 + 0 + 400 + 251 + + + + Dialog + + + + + + Source database + + + + + + + + + Source database version: + + + + + + + false + + + + + + + + + + Target database + + + + + + Target version: + + + + + + + This is the file that will be created as a result of the conversion. + + + + + + + Target file: + + + + + + + Name of the new database: + + + + + + + + + + + + + + + + + This is the name that the converted database will be added to SQLiteStudio with. + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DbConverterDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DbConverterDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp new file mode 100644 index 0000000..c9a7f28 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp @@ -0,0 +1,590 @@ +#include "dbdialog.h" +#include "ui_dbdialog.h" +#include "services/pluginmanager.h" +#include "plugins/dbplugin.h" +#include "uiutils.h" +#include "common/utils.h" +#include "uiconfig.h" +#include "services/dbmanager.h" +#include "common/global.h" +#include "iconmanager.h" +#include "common/unused.h" +#include +#include +#include +#include +#include +#include + +DbDialog::DbDialog(Mode mode, QWidget *parent) : + QDialog(parent), + ui(new Ui::DbDialog), + mode(mode) +{ + init(); +} + +DbDialog::~DbDialog() +{ + delete ui; +} + +void DbDialog::setDb(Db* db) +{ + this->db = db; +} + +void DbDialog::setPermanent(bool perm) +{ + ui->permamentCheckBox->setChecked(perm); +} + +QString DbDialog::getPath() +{ + return ui->fileEdit->text(); +} + +void DbDialog::setPath(const QString& path) +{ + ui->fileEdit->setText(path); +} + +QString DbDialog::getName() +{ + return ui->nameEdit->text(); +} + +Db* DbDialog::getDb() +{ + if (ui->typeCombo->currentIndex() < 0) + return nullptr; + + Db* testDb = nullptr; + QHash options = collectOptions(); + QString path = ui->fileEdit->text(); + foreach (DbPlugin* plugin, dbPlugins) + { + if (options.contains(DB_PLUGIN) && options[DB_PLUGIN].toString() != plugin->getName()) + continue; + + testDb = plugin->getInstance("", path, options); + if (testDb) + return testDb; + } + return testDb; +} + +bool DbDialog::isPermanent() +{ + return ui->permamentCheckBox->isChecked(); +} + +void DbDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void DbDialog::showEvent(QShowEvent *e) +{ + if (db) + { + int idx = ui->typeCombo->findText(db->getTypeLabel()); + ui->typeCombo->setCurrentIndex(idx); + ui->typeCombo->setEnabled(false); // converting to other type is in separate dialog, it's different feature + + ui->generateCheckBox->setChecked(false); + ui->fileEdit->setText(db->getPath()); + ui->nameEdit->setText(db->getName()); + } + else if (ui->typeCombo->count() > 0) + { + int idx = ui->typeCombo->findText("SQLite3"); // we should have SQLite3 plugin + if (idx > -1) + ui->typeCombo->setCurrentIndex(idx); + else + ui->typeCombo->setCurrentIndex(0); + } + + existingDatabaseNames = DBLIST->getDbNames(); + if (mode == EDIT) + existingDatabaseNames.removeOne(db->getName()); + + updateOptions(); + updateState(); + + QDialog::showEvent(e); +} + +void DbDialog::init() +{ + ui->setupUi(this); + + ui->browseButton->setIcon(ICONS.DATABASE_FILE); + dbPlugins = PLUGINS->getLoadedPlugins(); + foreach (DbPlugin* dbPlugin, dbPlugins) + { + ui->typeCombo->addItem(dbPlugin->getLabel()); + } + + ui->browseButton->setVisible(true); + ui->testConnIcon->setVisible(false); + + connect(ui->fileEdit, SIGNAL(textChanged(QString)), this, SLOT(fileChanged(QString))); + connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SLOT(nameModified(QString))); + connect(ui->generateCheckBox, SIGNAL(toggled(bool)), this, SLOT(generateNameSwitched(bool))); + connect(ui->browseButton, SIGNAL(clicked()), this, SLOT(browseClicked())); + connect(ui->testConnButton, SIGNAL(clicked()), this, SLOT(testConnectionClicked())); + connect(ui->typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(dbTypeChanged(int))); + + generateNameSwitched(true); +} + +void DbDialog::updateOptions() +{ + setUpdatesEnabled(false); + + // Remove olds + foreach (QWidget* w, optionWidgets) + { + ui->gridLayout->removeWidget(w); + delete w; + } + adjustSize(); + + optionWidgets.clear(); + optionKeyToWidget.clear(); + optionKeyToType.clear(); + helperToKey.clear(); + + lastWidgetInTabOrder = ui->permamentCheckBox; + + // Retrieve new list + DbPlugin* plugin = nullptr; + if (dbPlugins.count() > 0) + { + int idx = ui->typeCombo->currentIndex(); + if (idx > -1 ) + { + plugin = dbPlugins[idx]; + QList optList = plugin->getOptionsList(); + if (optList.size() > 0) + { + // Add new options + int row = ADDITIONAL_ROWS_BEGIN_INDEX; + foreach (DbPluginOption opt, optList) + addOption(opt, row++); + } + } + } + + adjustSize(); + setUpdatesEnabled(true); +} + +void DbDialog::addOption(const DbPluginOption& option, int row) +{ + QLabel* label = new QLabel(option.label, this); + QWidget* editor = nullptr; + QWidget* editorHelper = nullptr; // TODO, based on plugins for Url handlers + + editor = getEditor(option, editorHelper); + Q_ASSERT(editor != nullptr); + + if (!option.toolTip.isNull()) + editor->setToolTip(option.toolTip); + + optionWidgets << label << editor; + + optionKeyToWidget[option.key] = editor; + optionKeyToType[option.key] = option.type; + ui->gridLayout->addWidget(label, row, 0); + ui->gridLayout->addWidget(editor, row, 1); + + setTabOrder(lastWidgetInTabOrder, editor); + lastWidgetInTabOrder = editor; + + if (editorHelper) + { + ui->gridLayout->addWidget(editorHelper, row, 2); + optionWidgets << editorHelper; + helperToKey[editorHelper] = option.key; + + setTabOrder(lastWidgetInTabOrder, editorHelper); + lastWidgetInTabOrder = editorHelper; + } + + if (db && db->getConnectionOptions().contains(option.key)) + setValueFor(option.type, editor, db->getConnectionOptions()[option.key]); +} + +QWidget *DbDialog::getEditor(const DbPluginOption& opt, QWidget*& editorHelper) +{ + QWidget* editor = nullptr; + QLineEdit* le = nullptr; + editorHelper = nullptr; + switch (opt.type) + { + case DbPluginOption::STRING: + { + editor = new QLineEdit(this); + le = dynamic_cast(editor); + connect(le, SIGNAL(textChanged(QString)), this, SLOT(propertyChanged())); + break; + } + case DbPluginOption::PASSWORD: + { + editor = new QLineEdit(this); + le = dynamic_cast(editor); + le->setEchoMode(QLineEdit::Password); + connect(le, SIGNAL(textChanged(QString)), this, SLOT(propertyChanged())); + break; + } + case DbPluginOption::CHOICE: + { + QComboBox* cb = new QComboBox(this); + editor = cb; + cb->setEditable(!opt.choiceReadOnly); + cb->addItems(opt.choiceValues); + cb->setCurrentText(opt.defaultValue.toString()); + connect(cb, SIGNAL(currentIndexChanged(QString)), this, SLOT(propertyChanged())); + break; + } + case DbPluginOption::INT: + { + QSpinBox* sb = new QSpinBox(this); + editor = sb; + if (!opt.minValue.isNull()) + sb->setMinimum(opt.minValue.toInt()); + + if (!opt.maxValue.isNull()) + sb->setMaximum(opt.maxValue.toInt()); + + if (!opt.defaultValue.isNull()) + sb->setValue(opt.defaultValue.toInt()); + + connect(sb, SIGNAL(valueChanged(int)), this, SLOT(propertyChanged())); + break; + } + case DbPluginOption::FILE: + { + editor = new QLineEdit(this); + le = dynamic_cast(editor); + editorHelper = new QPushButton(tr("Browse"), this); + connect(le, SIGNAL(textChanged(QString)), this, SLOT(propertyChanged())); + connect(editorHelper, SIGNAL(pressed()), this, SLOT(browseForFile())); + break; + } + case DbPluginOption::BOOL: + { + QCheckBox* cb = new QCheckBox(this); + editor = cb; + if (!opt.defaultValue.isNull()) + cb->setChecked(opt.defaultValue.toBool()); + + connect(cb, SIGNAL(stateChanged(int)), this, SLOT(propertyChanged())); + break; + } + case DbPluginOption::DOUBLE: + { + QDoubleSpinBox* sb = new QDoubleSpinBox(this); + editor = sb; + if (!opt.minValue.isNull()) + sb->setMinimum(opt.minValue.toDouble()); + + if (!opt.maxValue.isNull()) + sb->setMaximum(opt.maxValue.toDouble()); + + if (!opt.defaultValue.isNull()) + sb->setValue(opt.defaultValue.toDouble()); + + connect(sb, SIGNAL(valueChanged(double)), this, SLOT(propertyChanged())); + break; + } + default: + // TODO plugin based handling of custom editors + qWarning() << "Unhandled DbDialog option for creating editor."; + break; + } + + if (le) + { + le->setPlaceholderText(opt.placeholderText); + le->setText(opt.defaultValue.toString()); + } + + return editor; +} + +QVariant DbDialog::getValueFrom(DbPluginOption::Type type, QWidget *editor) +{ + QVariant value; + switch (type) + { + case DbPluginOption::STRING: + case DbPluginOption::PASSWORD: + case DbPluginOption::FILE: + value = dynamic_cast(editor)->text(); + break; + case DbPluginOption::INT: + value = dynamic_cast(editor)->value(); + break; + case DbPluginOption::BOOL: + value = dynamic_cast(editor)->isChecked(); + break; + case DbPluginOption::DOUBLE: + value = dynamic_cast(editor)->value(); + break; + case DbPluginOption::CHOICE: + value = dynamic_cast(editor)->currentText(); + break; + default: + // TODO plugin based handling of custom editors + qWarning() << "Unhandled DbDialog option for value."; + break; + } + return value; +} + +void DbDialog::setValueFor(DbPluginOption::Type type, QWidget *editor, const QVariant &value) +{ + switch (type) + { + case DbPluginOption::STRING: + case DbPluginOption::FILE: + case DbPluginOption::PASSWORD: + dynamic_cast(editor)->setText(value.toString()); + break; + case DbPluginOption::INT: + dynamic_cast(editor)->setValue(value.toInt()); + break; + case DbPluginOption::BOOL: + dynamic_cast(editor)->setChecked(value.toBool()); + break; + case DbPluginOption::DOUBLE: + dynamic_cast(editor)->setValue(value.toDouble()); + break; + case DbPluginOption::CHOICE: + dynamic_cast(editor)->setCurrentText(value.toString()); + break; + default: + qWarning() << "Unhandled DbDialog option to set value."; + // TODO plugin based handling of custom editors + break; + } +} + +void DbDialog::updateType() +{ + QFileInfo file(ui->fileEdit->text()); + if (!file.exists() || file.isDir()) + { + ui->typeCombo->setEnabled(true); + return; + } + + DbPlugin* validPlugin = nullptr; + QHash options; + QString path = ui->fileEdit->text(); + Db* probeDb = nullptr; + foreach (DbPlugin* plugin, dbPlugins) + { + probeDb = plugin->getInstance("", path, options); + if (probeDb) + { + delete probeDb; + probeDb = nullptr; + + validPlugin = plugin; + break; + } + } + + if (validPlugin) + ui->typeCombo->setCurrentText(validPlugin->getLabel()); + + ui->typeCombo->setEnabled(!validPlugin); +} + +QHash DbDialog::collectOptions() +{ + QHash options; + if (ui->typeCombo->currentIndex() < 0) + return options; + + for (const QString key : optionKeyToWidget.keys()) + options[key] = getValueFrom(optionKeyToType[key], optionKeyToWidget[key]); + + DbPlugin* plugin = nullptr; + if (dbPlugins.count() > 0) + { + plugin = dbPlugins[ui->typeCombo->currentIndex()]; + options[DB_PLUGIN] = plugin->getName(); + } + + return options; +} + +bool DbDialog::testDatabase() +{ + QString path = ui->fileEdit->text(); + bool existed = QFile::exists(path); + bool res = getDb() != nullptr; + if (!existed) + { + QFile file(path); + file.remove(); + } + return res; +} + +bool DbDialog::validate() +{ + if (ui->fileEdit->text().isEmpty()) + return false; + + if (ui->nameEdit->text().isEmpty()) + return false; + + if (ui->typeCombo->count() == 0) + return false; + + if (ui->typeCombo->currentIndex() < 0) + return false; + + return true; +} + +void DbDialog::updateState() +{ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(validate()); +} + +void DbDialog::propertyChanged() +{ + ui->testConnIcon->setVisible(false); +} + +void DbDialog::typeChanged(int index) +{ + UNUSED(index); + updateOptions(); + updateState(); +} + +void DbDialog::valueForNameGenerationChanged() +{ + if (!ui->generateCheckBox->isChecked()) + { + updateState(); + return; + } + + DbPlugin* plugin = nullptr; + if (dbPlugins.count() > 0) + { + plugin = dbPlugins[ui->typeCombo->currentIndex()]; + QString generatedName = plugin->generateDbName(ui->fileEdit->text()); + generatedName = generateUniqueName(generatedName, existingDatabaseNames); + ui->nameEdit->setText(generatedName); + } +} + +void DbDialog::browseForFile() +{ + QString dir = getFileDialogInitPath(); + QString path = QFileDialog::getOpenFileName(0, QString(), dir); + if (path.isNull()) + return; + + QString key = helperToKey[dynamic_cast(sender())]; + setValueFor(optionKeyToType[key], optionKeyToWidget[key], path); + + setFileDialogInitPathByFile(path); +} + +void DbDialog::generateNameSwitched(bool checked) +{ + if (checked) + { + ui->nameEdit->setPlaceholderText(tr("The name will be auto-generated")); + valueForNameGenerationChanged(); + } + else + { + ui->nameEdit->setPlaceholderText(tr("Type the name")); + } + + ui->nameEdit->setReadOnly(checked); +} + +void DbDialog::fileChanged(const QString &arg1) +{ + UNUSED(arg1); + valueForNameGenerationChanged(); + updateType(); + propertyChanged(); +} + +void DbDialog::browseClicked() +{ + QFileInfo fileInfo(ui->fileEdit->text()); + QString dir; + if (ui->fileEdit->text().isEmpty()) + dir = getFileDialogInitPath(); + else if (fileInfo.exists() && fileInfo.isFile()) + dir = fileInfo.absolutePath(); + else if (fileInfo.dir().exists()) + dir = fileInfo.dir().absolutePath(); + else + dir = getFileDialogInitPath(); + + QString path = getDbPath(dir); + if (path.isNull()) + return; + + setFileDialogInitPathByFile(path); + + ui->fileEdit->setText(path); + updateState(); +} + +void DbDialog::testConnectionClicked() +{ + ui->testConnIcon->setPixmap(testDatabase() ? ICONS.TEST_CONN_OK : ICONS.TEST_CONN_ERROR); + ui->testConnIcon->setVisible(true); +} + +void DbDialog::dbTypeChanged(int index) +{ + typeChanged(index); + propertyChanged(); +} + +void DbDialog::nameModified(const QString &arg1) +{ + UNUSED(arg1); + updateState(); +} + +void DbDialog::accept() +{ + QString name = getName(); + QString path = getPath(); + QHash options = collectOptions(); + bool perm = isPermanent(); + bool result; + if (mode == ADD) + result = DBLIST->addDb(name, path, options, perm); + else + result = DBLIST->updateDb(db, name, path, options, perm); + + if (result) + QDialog::accept(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h new file mode 100644 index 0000000..b2c0d68 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.h @@ -0,0 +1,89 @@ +#ifndef DBDIALOG_H +#define DBDIALOG_H + +#include "db/db.h" +#include "db/dbpluginoption.h" +#include "guiSQLiteStudio_global.h" +#include +#include +#include +#include + +class DbPlugin; +class QGridLayout; +struct DbPluginOption; + +namespace Ui { + class DbDialog; +} + +class GUI_API_EXPORT DbDialog : public QDialog +{ + Q_OBJECT + + public: + enum Mode + { + ADD, + EDIT + }; + + DbDialog(Mode mode, QWidget *parent = 0); + ~DbDialog(); + + void setDb(Db* db); + void setPermanent(bool perm); + + QString getPath(); + void setPath(const QString& path); + QString getName(); + QHash collectOptions(); + bool isPermanent(); + + protected: + void changeEvent(QEvent *e); + void showEvent(QShowEvent* e); + + private: + void init(); + void updateOptions(); + void addOption(const DbPluginOption& option, int row); + QWidget* getEditor(const DbPluginOption& opt, QWidget *&editorHelper); + QVariant getValueFrom(DbPluginOption::Type type, QWidget* editor); + void setValueFor(DbPluginOption::Type type, QWidget* editor, const QVariant& value); + void updateType(); + Db* getDb(); + bool testDatabase(); + bool validate(); + void updateState(); + + Ui::DbDialog *ui = nullptr; + Mode mode; + QStringList existingDatabaseNames; + Db* db = nullptr; + QList dbPlugins; + QList optionWidgets; + QHash optionKeyToWidget; + QHash optionKeyToType; + QHash helperToKey; + QWidget* lastWidgetInTabOrder = nullptr; + + static const constexpr int ADDITIONAL_ROWS_BEGIN_INDEX = 4; + + private slots: + void typeChanged(int index); + void valueForNameGenerationChanged(); + void browseForFile(); + void generateNameSwitched(bool checked); + void fileChanged(const QString &arg1); + void browseClicked(); + void testConnectionClicked(); + void propertyChanged(); + void dbTypeChanged(int index); + void nameModified(const QString &arg1); + + public slots: + void accept(); +}; + +#endif // DBDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui new file mode 100644 index 0000000..fb53428 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.ui @@ -0,0 +1,236 @@ + + + DbDialog + + + + 0 + 0 + 455 + 200 + + + + + 450 + 0 + + + + Database + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Database driver + + + + + + + + + true + + + + + + + Name + + + + + + + Type + + + + + + + + + + + + Browse for database file on local computer + + + + + + + + + + + + File + + + + + + + Generate name basing on file path + + + + + + true + + + + + + + Permanent + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> + + + + + + true + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Test database connection + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + + fileEdit + browseButton + nameEdit + generateCheckBox + typeCombo + permamentCheckBox + + + + + buttonBox + accepted() + DbDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DbDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.cpp new file mode 100644 index 0000000..e86f9cd --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.cpp @@ -0,0 +1,58 @@ +#include "ddlpreviewdialog.h" +#include "ui_ddlpreviewdialog.h" +#include "services/codeformatter.h" +#include "uiconfig.h" +#include "sqlitestudio.h" +#include "db/db.h" + +DdlPreviewDialog::DdlPreviewDialog(Db* db, QWidget *parent) : + QDialog(parent), + ui(new Ui::DdlPreviewDialog), + db(db) +{ + ui->setupUi(this); +} + +DdlPreviewDialog::~DdlPreviewDialog() +{ + delete ui; +} + +void DdlPreviewDialog::setDdl(const QString& ddl) +{ + QString formatted = SQLITESTUDIO->getCodeFormatter()->format("sql", ddl, db); + ui->ddlEdit->setPlainText(formatted); +} + +void DdlPreviewDialog::setDdl(const QStringList& ddlList) +{ + QStringList fixedList; + QString newDdl; + foreach (const QString& ddl, ddlList) + { + newDdl = ddl.trimmed(); + if (!newDdl.endsWith(";")) + newDdl.append(";"); + + fixedList << SQLITESTUDIO->getCodeFormatter()->format("sql", newDdl, db); + } + setDdl(fixedList.join("\n")); +} + +void DdlPreviewDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void DdlPreviewDialog::accept() +{ + CFG_UI.General.DontShowDdlPreview.set(ui->dontShowAgainCheck->isChecked()); + QDialog::accept(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.h new file mode 100644 index 0000000..403405f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.h @@ -0,0 +1,35 @@ +#ifndef DDLPREVIEWDIALOG_H +#define DDLPREVIEWDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +class Db; + +namespace Ui { + class DdlPreviewDialog; +} + +class GUI_API_EXPORT DdlPreviewDialog : public QDialog +{ + Q_OBJECT + + public: + explicit DdlPreviewDialog(Db* db, QWidget *parent = 0); + ~DdlPreviewDialog(); + + void setDdl(const QString& ddl); + void setDdl(const QStringList& ddlList); + + protected: + void changeEvent(QEvent *e); + + private: + Ui::DdlPreviewDialog *ui = nullptr; + Db* db = nullptr; + + public slots: + void accept(); +}; + +#endif // DDLPREVIEWDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.ui new file mode 100644 index 0000000..8c3678a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/ddlpreviewdialog.ui @@ -0,0 +1,106 @@ + + + DdlPreviewDialog + + + + 0 + 0 + 527 + 351 + + + + Queries to be executed + + + + + + true + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Don't show again + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + + + SqlView + QPlainTextEdit +
    sqlview.h
    +
    +
    + + + + buttonBox + accepted() + DdlPreviewDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DdlPreviewDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp new file mode 100644 index 0000000..c0a73f3 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp @@ -0,0 +1,47 @@ +#include "errorsconfirmdialog.h" +#include "ui_errorsconfirmdialog.h" +#include "iconmanager.h" + +ErrorsConfirmDialog::ErrorsConfirmDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ErrorsConfirmDialog) +{ + ui->setupUi(this); +} + +ErrorsConfirmDialog::~ErrorsConfirmDialog() +{ + delete ui; +} + +void ErrorsConfirmDialog::setErrors(const QHash>& errors) +{ + ui->list->clear(); + + for (const QString& key : errors.keys()) + { + for (const QString& err : errors[key]) + ui->list->addItem(QString("[%1] %2").arg(key, err)); + } + + for (int i = 0, total = ui->list->count(); i < total; ++i) + ui->list->item(i)->setIcon(ICONS.STATUS_ERROR); +} + +void ErrorsConfirmDialog::setErrors(const QSet& errors) +{ + ui->list->clear(); + ui->list->addItems(errors.toList()); + for (int i = 0, total = ui->list->count(); i < total; ++i) + ui->list->item(i)->setIcon(ICONS.STATUS_ERROR); +} + +void ErrorsConfirmDialog::setTopLabel(const QString& text) +{ + ui->topLabel->setText(text); +} + +void ErrorsConfirmDialog::setBottomLabel(const QString& text) +{ + ui->bottomLabel->setText(text); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.h new file mode 100644 index 0000000..3e71d7b --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.h @@ -0,0 +1,28 @@ +#ifndef ERRORSCONFIRMDIALOG_H +#define ERRORSCONFIRMDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class ErrorsConfirmDialog; +} + +class GUI_API_EXPORT ErrorsConfirmDialog : public QDialog +{ + Q_OBJECT + + public: + explicit ErrorsConfirmDialog(QWidget *parent = 0); + ~ErrorsConfirmDialog(); + + void setErrors(const QHash >& errors); + void setErrors(const QSet& errors); + void setTopLabel(const QString& text); + void setBottomLabel(const QString& text); + + private: + Ui::ErrorsConfirmDialog *ui = nullptr; +}; + +#endif // ERRORSCONFIRMDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.ui new file mode 100644 index 0000000..81cdb17 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.ui @@ -0,0 +1,85 @@ + + + ErrorsConfirmDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Following errors occured: + + + + + + + true + + + + + + + Would you like to proceed? + + + + + + + Qt::Horizontal + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + + + + + + + buttonBox + accepted() + ErrorsConfirmDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ErrorsConfirmDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp new file mode 100644 index 0000000..e495dd9 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp @@ -0,0 +1,737 @@ +#include "exportdialog.h" +#include "ui_exportdialog.h" +#include "dblistmodel.h" +#include "dbobjlistmodel.h" +#include "services/dbmanager.h" +#include "uiutils.h" +#include "services/pluginmanager.h" +#include "formmanager.h" +#include "plugins/exportplugin.h" +#include "configmapper.h" +#include "selectabledbobjmodel.h" +#include "dbtree/dbtree.h" +#include "dbtree/dbtreemodel.h" +#include "schemaresolver.h" +#include "common/widgetcover.h" +#include "services/notifymanager.h" +#include "uiconfig.h" +#include +#include +#include +#include +#include +#include +#include + +ExportDialog::ExportDialog(QWidget *parent) : + QWizard(parent), + ui(new Ui::ExportDialog) +{ + init(); +} + +ExportDialog::~ExportDialog() +{ + EXPORT_MANAGER->interrupt(); + safe_delete(configMapper); + delete ui; +} + +void ExportDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + +#ifdef Q_OS_MACX + resize(width() + 150, height()); + setPixmap(QWizard::BackgroundPixmap, addOpacity(ICONS.DATABASE_EXPORT_WIZARD.toQIcon().pixmap(800, 800), 0.3)); +#endif + + widgetCover = new WidgetCover(this); + widgetCover->initWithInterruptContainer(tr("Cancel")); + connect(widgetCover, SIGNAL(cancelClicked()), EXPORT_MANAGER, SLOT(interrupt())); + widgetCover->setVisible(false); + + initPageOrder(); + + initModePage(); + initTablePage(); + initFormatPage(); + initQueryPage(); + initDbObjectsPage(); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged(int))); + connect(EXPORT_MANAGER, SIGNAL(exportSuccessful()), this, SLOT(success())); + connect(EXPORT_MANAGER, SIGNAL(exportFinished()), this, SLOT(hideCoverWidget())); + connect(EXPORT_MANAGER, SIGNAL(storeInClipboard(QByteArray, QString)), this, SLOT(storeInClipboard(QByteArray, QString))); + connect(EXPORT_MANAGER, SIGNAL(storeInClipboard(QString)), this, SLOT(storeInClipboard(QString))); + connect(EXPORT_MANAGER, SIGNAL(validationResultFromPlugin(bool,CfgEntry*,QString)), this, SLOT(handleValidationResultFromPlugin(bool,CfgEntry*,QString))); + connect(EXPORT_MANAGER, SIGNAL(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool)), this, SLOT(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool))); +} + +void ExportDialog::setTableMode(Db* db, const QString& table) +{ + if (!db->isOpen()) + { + qWarning() << "Cannot export from closed database."; + return; + } + + setStartId(pageId(ui->tablePage)); + exportMode = ExportManager::TABLE; + this->db = db; + this->table = table; + + ui->exportTableDbNameCombo->addItem(db->getName()); + ui->exportTableDbNameCombo->setCurrentText(db->getName()); + ui->exportTableDbNameCombo->setEnabled(false); + ui->exportTableNameCombo->addItem(table); + ui->exportTableNameCombo->setCurrentText(table); + ui->exportTableNameCombo->setEnabled(false); +} + +void ExportDialog::setQueryMode(Db* db, const QString& query) +{ + if (!db->isOpen()) + { + qWarning() << "Cannot export from closed database."; + return; + } + + setStartId(pageId(ui->queryPage)); + exportMode = ExportManager::QUERY_RESULTS; + this->db = db; + this->query = query; + + ui->queryDatabaseCombo->addItem(db->getName()); + ui->queryDatabaseCombo->setCurrentText(db->getName()); + ui->queryDatabaseCombo->setEnabled(false); + ui->queryEdit->setPlainText(query); + updateQueryEditDb(); + ui->queryEdit->checkSyntaxNow(); +} + +void ExportDialog::setDatabaseMode(Db* db) +{ + if (!db->isOpen()) + { + qWarning() << "Cannot export from closed database."; + return; + } + + setStartId(pageId(ui->databaseObjectsPage)); + exportMode = ExportManager::DATABASE; + this->db = db; +} + +void ExportDialog::initModePage() +{ + connect(ui->subjectDatabaseRadio, SIGNAL(clicked()), this, SLOT(updateExportMode())); + connect(ui->subjectTableRadio, SIGNAL(clicked()), this, SLOT(updateExportMode())); + connect(ui->subjectQueryRadio, SIGNAL(clicked()), this, SLOT(updateExportMode())); +} + +void ExportDialog::initTablePage() +{ + ui->tablePage->setValidator([=]() -> bool + { + bool dbOk = ui->exportTableDbNameCombo->currentIndex() > -1; + bool tableOk = ui->exportTableNameCombo->currentIndex() > -1; + + setValidState(ui->exportTableDbNameCombo, dbOk, tr("Select database to export.")); + setValidState(ui->exportTableNameCombo, tableOk, tr("Select table to export.")); + + return dbOk && tableOk; + }); + + dbListModel = new DbListModel(this); + dbListModel->setCombo(ui->exportTableDbNameCombo); + dbListModel->setSortMode(DbListModel::SortMode::Alphabetical); + + tablesModel = new DbObjListModel(this); + tablesModel->setType(DbObjListModel::ObjectType::TABLE); + + connect(this, SIGNAL(tablePageCompleteChanged()), ui->tablePage, SIGNAL(completeChanged())); +} + +void ExportDialog::initQueryPage() +{ + ui->queryPage->setValidator([=]() -> bool + { + bool queryOk = !ui->queryEdit->toPlainText().trimmed().isEmpty(); + queryOk &= ui->queryEdit->isSyntaxChecked() && !ui->queryEdit->haveErrors(); + bool dbOk = ui->queryDatabaseCombo->currentIndex() > -1; + + setValidState(ui->queryDatabaseCombo, dbOk, tr("Select database to export.")); + setValidState(ui->queryEdit, queryOk, tr("Enter valid query to export.")); + + return dbOk && queryOk; + }); + + connect(ui->queryEdit, SIGNAL(errorsChecked(bool)), ui->queryPage, SIGNAL(completeChanged())); + connect(ui->queryEdit, SIGNAL(textChanged()), ui->queryPage, SIGNAL(completeChanged())); + connect(ui->queryDatabaseCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateQueryEditDb())); + connect(this, SIGNAL(queryPageCompleteChanged()), ui->queryPage, SIGNAL(completeChanged())); +} + +void ExportDialog::initDbObjectsPage() +{ + selectableDbListModel = new SelectableDbObjModel(this); + selectableDbListModel->setSourceModel(DBTREE->getModel()); + ui->dbObjectsTree->setModel(selectableDbListModel); + + ui->databaseObjectsPage->setValidator([=]() -> bool + { + bool dbOk = ui->dbObjectsDatabaseCombo->currentIndex() > -1; + bool listOk = selectableDbListModel->getCheckedObjects().size() > 0; + + setValidState(ui->dbObjectsDatabaseCombo, dbOk, tr("Select database to export.")); + setValidState(ui->dbObjectsTree, listOk, tr("Select at least one object to export.")); + + return listOk; + }); + + connect(ui->dbObjectsDatabaseCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateDbObjTree())); + connect(ui->dbObjectsDatabaseCombo, SIGNAL(currentIndexChanged(QString)), ui->databaseObjectsPage, SIGNAL(completeChanged())); + connect(selectableDbListModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->databaseObjectsPage, SIGNAL(completeChanged())); + connect(ui->objectsSelectAllButton, SIGNAL(clicked()), this, SLOT(dbObjectsSelectAll())); + connect(ui->objectsDeselectAllButton, SIGNAL(clicked()), this, SLOT(dbObjectsDeselectAll())); +} + +void ExportDialog::initFormatPage() +{ + ui->formatAndOptionsPage->setValidator([=]() -> bool + { + setValidState(ui->exportFileEdit, true); + bool outputFileSupported = currentPlugin && currentPlugin->getSupportedModes().testFlag(ExportManager::FILE); + if (outputFileSupported && ui->exportFileRadio->isChecked()) + { + QString path = ui->exportFileEdit->text(); + if (path.trimmed().isEmpty()) + { + setValidState(ui->exportFileEdit, false, tr("You must provide a file name to export to.")); + return false; + } + + QDir dir(path); + if (dir.exists() && QFileInfo(path).isDir()) + { + setValidState(ui->exportFileEdit, false, tr("Path you provided is an existing directory. You cannot overwrite it.")); + return false; + } + + if (!dir.cdUp()) + { + setValidState(ui->exportFileEdit, false, tr("The directory '%1' does not exist.").arg(dir.dirName())); + return false; + } + + QFileInfo fi(path); + if (fi.exists()) + setValidStateInfo(ui->exportFileEdit, tr("The file '%1' exists and will be overwritten.").arg(fi.fileName())); + } + return ui->formatCombo->currentIndex() > -1 && ui->encodingCombo->currentIndex() > -1 && isPluginConfigValid(); + }); + + ui->exportFileButton->setIcon(ICONS.EXPORT_FILE_BROWSE); + connect(ui->exportFileButton, SIGNAL(clicked()), this, SLOT(browseForExportFile())); + + connect(ui->formatCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(pluginSelected())); + connect(ui->formatCombo, SIGNAL(currentTextChanged(QString)), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(ui->encodingCombo, SIGNAL(currentTextChanged(QString)), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(ui->exportFileEdit, SIGNAL(textChanged(QString)), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(ui->exportFileRadio, SIGNAL(clicked()), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(ui->exportClipboardRadio, SIGNAL(clicked()), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(this, SIGNAL(formatPageCompleteChanged()), ui->formatAndOptionsPage, SIGNAL(completeChanged())); + connect(ui->exportFileRadio, SIGNAL(clicked()), this, SLOT(updateOptions())); + connect(ui->exportClipboardRadio, SIGNAL(clicked()), this, SLOT(updateOptions())); + connect(ui->exportFileRadio, SIGNAL(clicked()), this, SLOT(updateExportOutputOptions())); + connect(ui->exportClipboardRadio, SIGNAL(clicked()), this, SLOT(updateExportOutputOptions())); +} + +int ExportDialog::nextId() const +{ + if (exportMode == ExportManager::UNDEFINED) + return pageId(ui->proxyPage); + + QList order = pageOrder[exportMode]; + + int idx = order.indexOf(currentPage()); + idx++; + if (idx < order.size()) + return pageId(order[idx]); + + return -1; +} + +bool ExportDialog::isPluginConfigValid() const +{ + return pluginConfigOk.size() == 0; +} + +void ExportDialog::initPageOrder() +{ + setStartId(pageId(ui->exportSubjectPage)); + pageOrder[ExportManager::DATABASE] = {ui->databaseObjectsPage, ui->formatAndOptionsPage}; + pageOrder[ExportManager::TABLE] = {ui->tablePage, ui->formatAndOptionsPage}; + pageOrder[ExportManager::QUERY_RESULTS] = {ui->queryPage, ui->formatAndOptionsPage}; + updateExportMode(); +} + +int ExportDialog::pageId(QWizardPage* wizardPage) const +{ + for (int id : pageIds()) + { + if (page(id) == wizardPage) + return id; + } + return -1; +} + +void ExportDialog::tablePageDisplayed() +{ + if (!tablePageVisited) + { + if (table.isNull()) // table mode selected by user, not forced by setTableMode(). + { + ui->exportTableDbNameCombo->setModel(dbListModel); + connect(ui->exportTableDbNameCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDbTables())); + + ui->exportTableNameCombo->setModel(tablesModel); + connect(ui->exportTableNameCombo, SIGNAL(currentTextChanged(QString)), ui->tablePage, SIGNAL(completeChanged())); + } + updateDbTables(); + emit tablePageCompleteChanged(); + tablePageVisited = true; + } +} + +void ExportDialog::queryPageDisplayed() +{ + if (!queryPageVisited) + { + if (query.isNull()) // query mode selected by user, not forced by setQueryMode(). + { + ui->queryDatabaseCombo->setModel(dbListModel); + connect(ui->queryDatabaseCombo, SIGNAL(currentIndexChanged(int)), ui->queryPage, SIGNAL(completeChanged())); + } + + updateQueryEditDb(); + emit queryPageCompleteChanged(); + queryPageVisited = true; + } +} + +void ExportDialog::dbObjectsPageDisplayed() +{ + if (!dbObjectsPageVisited) + { + ui->dbObjectsDatabaseCombo->setModel(dbListModel); + connect(ui->dbObjectsDatabaseCombo, SIGNAL(currentIndexChanged(int)), ui->queryPage, SIGNAL(completeChanged())); + + if (db) + ui->dbObjectsDatabaseCombo->setCurrentText(db->getName()); + + dbObjectsPageVisited = true; + } +} + +void ExportDialog::formatPageDisplayed() +{ + if (!formatPageVisited) + { + ui->formatCombo->addItems(EXPORT_MANAGER->getAvailableFormats(exportMode)); + + ui->encodingCombo->addItems(textCodecNames()); + ui->encodingCombo->setCurrentText(defaultCodecName()); + + formatPageVisited = true; + } + pluginSelected(); +} + +ExportPlugin* ExportDialog::getSelectedPlugin() const +{ + return EXPORT_MANAGER->getPluginForFormat(ui->formatCombo->currentText()); +} + +void ExportDialog::updateExportMode() +{ + if (ui->subjectDatabaseRadio->isChecked()) + exportMode = ExportManager::DATABASE; + else if (ui->subjectTableRadio->isChecked()) + exportMode = ExportManager::TABLE; + else if (ui->subjectQueryRadio->isChecked()) + exportMode = ExportManager::QUERY_RESULTS; + else + exportMode = ExportManager::UNDEFINED; +} + +void ExportDialog::pageChanged(int pageId) +{ + QWizardPage* wizardPage = page(pageId); + if (wizardPage == ui->tablePage) + tablePageDisplayed(); + else if (wizardPage == ui->queryPage) + queryPageDisplayed(); + else if (wizardPage == ui->databaseObjectsPage) + dbObjectsPageDisplayed(); + else if (wizardPage == ui->formatAndOptionsPage) + formatPageDisplayed(); + else if (wizardPage == ui->proxyPage) + next(); +} + +void ExportDialog::updateDbTables() +{ + if (!table.isNull()) + return; // we don't want tables to be automatically updated if this is strictly set table + + QString dbName = ui->exportTableDbNameCombo->currentText(); + db = DBLIST->getByName(dbName); + + tablesModel->setDb(db); +} + +void ExportDialog::browseForExportFile() +{ + QStringList filters; + if (currentPlugin) + filters << currentPlugin->getFormatName()+" (*." + currentPlugin->defaultFileExtension() + ")"; + + filters << tr("All files (*)"); + + QString dir = getFileDialogInitPath(); + QString fileName = QFileDialog::getSaveFileName(this, tr("Pick file to export to"), dir, filters.join(";;"), 0, QFileDialog::DontConfirmOverwrite); + if (fileName.isNull()) + return; + + if (currentPlugin && !fileName.endsWith("." + currentPlugin->defaultFileExtension())) + fileName += "." + currentPlugin->defaultFileExtension(); + + ui->exportFileEdit->setText(fileName); + setFileDialogInitPathByFile(fileName); +} + +void ExportDialog::pluginSelected() +{ + pluginConfigOk.clear(); + + currentPlugin = getSelectedPlugin(); + if (!currentPlugin) + { + qCritical() << "Could not find export plugin, while it was selected on ui:" << ui->formatCombo->currentText(); + return; + } + + currentPlugin->setExportMode(exportMode); + + updateExportOutputOptions(); + updateOptions(); + + if (currentPlugin->getConfig() && !currentPlugin->getConfig()->isPersistable()) + currentPlugin->getConfig()->reset(); +} + +void ExportDialog::updateExportOutputOptions() +{ + ExportManager::StandardConfigFlags options = currentPlugin->standardOptionsToEnable(); + bool displayCodec = options.testFlag(ExportManager::CODEC) && !ui->exportClipboardRadio->isChecked(); + bool clipboardSupported = currentPlugin->getSupportedModes().testFlag(ExportManager::CLIPBOARD); + bool outputFileSupported = currentPlugin->getSupportedModes().testFlag(ExportManager::FILE); + + bool enabled = outputFileSupported && ui->exportFileRadio->isChecked(); + ui->exportFileEdit->setEnabled(enabled); + ui->exportFileButton->setEnabled(enabled); + + ui->exportClipboardRadio->setVisible(clipboardSupported); + ui->exportFileRadio->setVisible(outputFileSupported); + ui->exportFileEdit->setVisible(outputFileSupported); + ui->exportFileButton->setVisible(outputFileSupported); + if (!clipboardSupported && outputFileSupported) + ui->exportFileRadio->setChecked(true); + + ui->encodingCombo->setVisible(displayCodec); + ui->encodingLabel->setVisible(displayCodec); + if (displayCodec) + { + QString codec = currentPlugin->getDefaultEncoding(); + int idx = ui->encodingCombo->findText(codec); + if (idx > -1) + ui->encodingCombo->setCurrentIndex(idx); + } + + ui->exportToGroup->setVisible(clipboardSupported || outputFileSupported || displayCodec); +} + +void ExportDialog::updateQueryEditDb() +{ + Db* db = getDbForExport(ui->queryDatabaseCombo->currentText()); + ui->queryEdit->setDb(db); +} + +void ExportDialog::updateOptions() +{ + ui->optionsGroup->setVisible(false); + + if (!currentPlugin) + { + qCritical() << "Could not find export plugin, while it was selected on ui:" << ui->formatCombo->currentText(); + return; + } + + int optionsRow = 0; + updatePluginOptions(currentPlugin, optionsRow); + ui->optionsGroup->setVisible(optionsRow > 0); +} + +void ExportDialog::updateDbObjTree() +{ + selectableDbListModel->setDbName(ui->dbObjectsDatabaseCombo->currentText()); + + QModelIndex root = selectableDbListModel->index(0, 0); + if (root.isValid()) + { + root = setupNewDbObjTreeRoot(root); + ui->dbObjectsTree->setRootIndex(root); + + ui->dbObjectsTree->expand(root); + QModelIndex child; + for (int i = 0; (child = root.child(i, 0)).isValid(); i++) + ui->dbObjectsTree->expand(child); + } + dbObjectsSelectAll(); +} + +void ExportDialog::dbObjectsSelectAll() +{ + selectableDbListModel->setRootChecked(true); +} + +void ExportDialog::dbObjectsDeselectAll() +{ + selectableDbListModel->setRootChecked(false); +} + +void ExportDialog::hideCoverWidget() +{ + widgetCover->hide(); +} + +void ExportDialog::storeInClipboard(const QByteArray& bytes, const QString& mimeType) +{ + QMimeData* mimeData = new QMimeData; + mimeData->setData(mimeType, bytes); + QApplication::clipboard()->setMimeData(mimeData); +} + +void ExportDialog::storeInClipboard(const QString& str) +{ + QApplication::clipboard()->setText(str); +} + +void ExportDialog::success() +{ + QWizard::accept(); +} + +void ExportDialog::accept() +{ + doExport(); +} + +void ExportDialog::updatePluginOptions(ExportPlugin* plugin, int& optionsRow) +{ + safe_delete(pluginOptionsWidget); + + QString formName = plugin->getExportConfigFormName(); + CfgMain* cfgMain = plugin->getConfig(); + if (formName.isNull() || !cfgMain) + { + if (!formName.isNull()) + { + qWarning() << "FormName is given, but cfgMain is null in ExportDialog::updatePluginOptions() for plugin:" << plugin->getName() + << ", formName:" << formName; + } + return; + } + + if (!FORMS->hasWidget(formName)) + { + qWarning() << "Export plugin" << plugin->getName() << "requested for form named" << formName << "but FormManager doesn't have it." + << "Available forms are:" << FORMS->getAvailableForms(); + return; + } + + safe_delete(configMapper); + + QGridLayout* grid = dynamic_cast(ui->optionsGroup->layout()); + + pluginOptionsWidget = FORMS->createWidget(formName); + + if (pluginOptionsWidget->layout()) + pluginOptionsWidget->layout()->setMargin(0); + + grid->addWidget(pluginOptionsWidget, 1, 0, 1, 2); + optionsRow++; + + configMapper = new ConfigMapper(cfgMain); + configMapper->bindToConfig(pluginOptionsWidget); + connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + plugin->validateOptions(); +} + +void ExportDialog::updateValidation() +{ + if (!currentPlugin) + return; + + currentPlugin->validateOptions(); + emit formatPageCompleteChanged(); +} + +void ExportDialog::doExport() +{ + widgetCover->show(); + + ExportManager::StandardExportConfig stdConfig = getExportConfig(); + QString format = ui->formatCombo->currentText(); + switch (exportMode) + { + case ExportManager::DATABASE: + exportDatabase(stdConfig, format); + break; + case ExportManager::TABLE: + exportTable(stdConfig, format); + break; + case ExportManager::QUERY_RESULTS: + exportQuery(stdConfig, format); + break; + case ExportManager::UNDEFINED: + qCritical() << "Finished export dialog with undefined mode."; + notifyInternalError(); + break; + case ExportManager::FILE: + case ExportManager::CLIPBOARD: + break; + } +} + +void ExportDialog::exportDatabase(const ExportManager::StandardExportConfig& stdConfig, const QString& format) +{ + Db* db = getDbForExport(ui->dbObjectsDatabaseCombo->currentText()); + if (!db || !db->isValid()) + return; + + EXPORT_MANAGER->configure(format, stdConfig); + EXPORT_MANAGER->exportDatabase(db, selectableDbListModel->getCheckedObjects()); +} + +void ExportDialog::exportTable(const ExportManager::StandardExportConfig& stdConfig, const QString& format) +{ + Db* db = getDbForExport(ui->exportTableDbNameCombo->currentText()); + if (!db || !db->isValid()) + return; + + EXPORT_MANAGER->configure(format, stdConfig); + // TODO when dbnames are fully supported, pass the dbname below + EXPORT_MANAGER->exportTable(db, QString::null, ui->exportTableNameCombo->currentText()); +} + +void ExportDialog::exportQuery(const ExportManager::StandardExportConfig& stdConfig, const QString& format) +{ + Db* db = getDbForExport(ui->queryDatabaseCombo->currentText()); + if (!db || !db->isValid()) + return; + + EXPORT_MANAGER->configure(format, stdConfig); + EXPORT_MANAGER->exportQueryResults(db, ui->queryEdit->toPlainText()); +} + +ExportManager::StandardExportConfig ExportDialog::getExportConfig() const +{ + bool clipboardSupported = currentPlugin->getSupportedModes().testFlag(ExportManager::CLIPBOARD); + bool outputFileSupported = currentPlugin->getSupportedModes().testFlag(ExportManager::FILE); + bool clipboard = clipboardSupported && ui->exportClipboardRadio->isChecked(); + + ExportManager::StandardExportConfig stdConfig; + stdConfig.intoClipboard = clipboard; + + if (clipboard) + stdConfig.outputFileName = QString::null; + else if (outputFileSupported) + stdConfig.outputFileName = ui->exportFileEdit->text(); + + if (exportMode == ExportManager::DATABASE) + stdConfig.exportData = ui->exportDbDataCheck->isChecked(); + else if (exportMode == ExportManager::TABLE) + stdConfig.exportData = ui->exportTableDataCheck->isChecked(); + else + stdConfig.exportData = false; + + if (ui->encodingCombo->isVisible() && ui->encodingCombo->currentIndex() > -1) + stdConfig.codec = ui->encodingCombo->currentText(); + else + stdConfig.codec = defaultCodecName(); + + return stdConfig; +} + +Db* ExportDialog::getDbForExport(const QString& name) +{ + Db* db = DBLIST->getByName(name); + if (!db) + { + qCritical() << "Could not find db selected in combo:" << name; + notifyInternalError(); + return nullptr; + } + return db; +} + +void ExportDialog::notifyInternalError() +{ + notifyError(tr("Internal error during export. This is a bug. Please report it.")); +} + +QModelIndex ExportDialog::setupNewDbObjTreeRoot(const QModelIndex& root) +{ + QModelIndex newRoot = root; + DbTreeItem* item = nullptr; + while (newRoot.isValid()) + { + item = selectableDbListModel->getItemForIndex(newRoot); + if (item->getType() == DbTreeItem::Type::DB) + return newRoot; + + newRoot = newRoot.child(0, 0); + } + return newRoot; +} + +void ExportDialog::handleValidationResultFromPlugin(bool valid, CfgEntry* key, const QString& errorMsg) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (w) + setValidState(w, valid, errorMsg); + + if (valid == pluginConfigOk.contains(key)) // if state changed + { + if (!valid) + pluginConfigOk[key] = false; + else + pluginConfigOk.remove(key); + + emit formatPageCompleteChanged(); + } +} + +void ExportDialog::stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (!w) + return; + + w->setVisible(visible); + w->setEnabled(enabled); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.h new file mode 100644 index 0000000..296aa4d --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.h @@ -0,0 +1,105 @@ +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include "services/exportmanager.h" +#include + +namespace Ui { + class ExportDialog; +} + +class DbListModel; +class DbObjListModel; +class SelectableDbObjModel; +class WidgetCover; +class ConfigMapper; + +class GUI_API_EXPORT ExportDialog : public QWizard +{ + Q_OBJECT + + public: + explicit ExportDialog(QWidget *parent = 0); + ~ExportDialog(); + + void setTableMode(Db* db, const QString& table); + void setQueryMode(Db* db, const QString& query); + void setDatabaseMode(Db* db); + int nextId() const; + bool isPluginConfigValid() const; + + private: + void init(); + void initModePage(); + void initTablePage(); + void initQueryPage(); + void initDbObjectsPage(); + void initFormatPage(); + void initPageOrder(); + int pageId(QWizardPage* wizardPage) const; + void tablePageDisplayed(); + void queryPageDisplayed(); + void dbObjectsPageDisplayed(); + void formatPageDisplayed(); + ExportPlugin* getSelectedPlugin() const; + void updatePluginOptions(ExportPlugin* plugin, int& optionsRow); + void doExport(); + void exportDatabase(const ExportManager::StandardExportConfig& stdConfig, const QString& format); + void exportTable(const ExportManager::StandardExportConfig& stdConfig, const QString& format); + void exportQuery(const ExportManager::StandardExportConfig& stdConfig, const QString& format); + ExportManager::StandardExportConfig getExportConfig() const; + Db* getDbForExport(const QString& name); + void notifyInternalError(); + QModelIndex setupNewDbObjTreeRoot(const QModelIndex& root); + + QHash> pageOrder; + + Ui::ExportDialog *ui = nullptr; + ExportManager::ExportMode exportMode = ExportManager::UNDEFINED; + Db* db = nullptr; + QString query; + QString table; + DbListModel* dbListModel = nullptr; + DbObjListModel* tablesModel = nullptr; + SelectableDbObjModel* selectableDbListModel = nullptr; + QWidget* pluginOptionsWidget = nullptr; + bool tablePageVisited = false; + bool queryPageVisited = false; + bool dbObjectsPageVisited = false; + bool formatPageVisited = false; + WidgetCover* widgetCover = nullptr; + ConfigMapper* configMapper = nullptr; + QHash pluginConfigOk; + ExportPlugin* currentPlugin = nullptr; + + private slots: + void handleValidationResultFromPlugin(bool valid, CfgEntry* key, const QString& errorMsg); + void stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled); + void updateExportMode(); + void pageChanged(int pageId); + void updateDbTables(); + void browseForExportFile(); + void pluginSelected(); + void updateExportOutputOptions(); + void updateQueryEditDb(); + void updateOptions(); + void updateDbObjTree(); + void dbObjectsSelectAll(); + void dbObjectsDeselectAll(); + void hideCoverWidget(); + void storeInClipboard(const QByteArray& bytes, const QString& mimeType); + void storeInClipboard(const QString& str); + void success(); + void updateValidation(); + + public slots: + void accept(); + + signals: + void formatPageCompleteChanged(); + void tablePageCompleteChanged(); + void queryPageCompleteChanged(); +}; + +#endif // EXPORTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui new file mode 100644 index 0000000..9f84232 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.ui @@ -0,0 +1,438 @@ + + + ExportDialog + + + + 0 + 0 + 515 + 414 + + + + Export dialog + + + QWizard::CancelButtonOnLeft|QWizard::NoDefaultButton + + + + What do you want to export? + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + A database + + + true + + + + + + + A single table + + + + + + + Query results + + + + + + + + + + + + Table to export + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Database + + + + + + + Table + + + + + + + . + + + + + + + + + + Options + + + + + + When this option is unchecked, then only table DDL (CREATE TABLE statement) is exported. + + + Export table data + + + true + + + + + + + Export table indexes + + + true + + + + + + + Export table triggers + + + true + + + + + + + Qt::Horizontal + + + + + + + + true + + + + Note, that exporting table indexes and triggers may be unsupported by some output formats. + + + true + + + + + + + + + + + Select database objects to export + + + + + + false + + + + + + + Export data from tables + + + true + + + + + + + Select all + + + + + + + Deselect all + + + + + + + + + + Database: + + + + + + + + Query to export results for + + + + + + + + + + + + Database: + + + + + + + Query to be executed for results: + + + + + + + + Export format and options + + + + + + #formatScrollArea { background: transparent; } + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 298 + 288 + + + + #formatScrollAreaContents { background: transparent; } + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Export format + + + + + + + + + + + + Output + + + + + + Exported file path + + + + + + + Clipboard + + + + + + + ... + + + + + + + File + + + true + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Exported text encoding: + + + + + + + + + + + + + + + + Export format options + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + VerifiableWizardPage + QWizardPage +
    common/verifiablewizardpage.h
    + 1 +
    + + SqlEditor + QPlainTextEdit +
    sqleditor.h
    +
    +
    + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp new file mode 100644 index 0000000..32ec30f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp @@ -0,0 +1,376 @@ +#include "importdialog.h" +#include "dblistmodel.h" +#include "dbobjlistmodel.h" +#include "common/widgetstateindicator.h" +#include "uiutils.h" +#include "common/widgetcover.h" +#include "services/dbmanager.h" +#include "services/pluginmanager.h" +#include "services/importmanager.h" +#include "sqlitestudio.h" +#include "plugins/importplugin.h" +#include "ui_importdialog.h" +#include "configmapper.h" +#include "formmanager.h" +#include "common/utils.h" +#include "uiconfig.h" +#include +#include +#include + +ImportDialog::ImportDialog(QWidget *parent) : + QWizard(parent), + ui(new Ui::ImportDialog) +{ + init(); +} + +ImportDialog::~ImportDialog() +{ + IMPORT_MANAGER->interrupt(); + safe_delete(configMapper); + delete ui; +} + +void ImportDialog::setDbAndTable(Db* db, const QString& table) +{ + if (!db) + return; + + ui->dbNameCombo->setCurrentText(db->getName()); + ui->tableNameCombo->setCurrentText(table); +} + +void ImportDialog::setDb(Db* db) +{ + if (!db) + return; + + ui->dbNameCombo->setCurrentText(db->getName()); +} + +bool ImportDialog::isPluginConfigValid() const +{ + return pluginConfigOk.size() == 0; +} + +void ImportDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + +#ifdef Q_OS_MACX + resize(width() + 150, height()); + setPixmap(QWizard::BackgroundPixmap, addOpacity(ICONS.DATABASE_IMPORT_WIZARD.toQIcon().pixmap(800, 800), 0.3)); +#endif + + initTablePage(); + initDataSourcePage(); + + widgetCover = new WidgetCover(this); + widgetCover->initWithInterruptContainer(tr("Cancel")); + connect(widgetCover, SIGNAL(cancelClicked()), IMPORT_MANAGER, SLOT(interrupt())); + widgetCover->setVisible(false); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged())); + connect(IMPORT_MANAGER, SIGNAL(validationResultFromPlugin(bool,CfgEntry*,QString)), this, SLOT(handleValidationResultFromPlugin(bool,CfgEntry*,QString))); + connect(IMPORT_MANAGER, SIGNAL(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool)), this, SLOT(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool))); + connect(IMPORT_MANAGER, SIGNAL(importSuccessful()), this, SLOT(success())); + connect(IMPORT_MANAGER, SIGNAL(importFinished()), this, SLOT(hideCoverWidget())); +} + +void ImportDialog::initTablePage() +{ + dbListModel = new DbListModel(this); + dbListModel->setCombo(ui->dbNameCombo); + dbListModel->setSortMode(DbListModel::SortMode::Alphabetical); + ui->dbNameCombo->setModel(dbListModel); + + tablesModel = new DbObjListModel(this); + tablesModel->setIncludeSystemObjects(false); + tablesModel->setType(DbObjListModel::ObjectType::TABLE); + ui->tableNameCombo->setModel(tablesModel); + refreshTables(); + + connect(ui->dbNameCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(refreshTables())); + connect(ui->tableNameCombo, SIGNAL(currentTextChanged(QString)), ui->tablePage, SIGNAL(completeChanged())); + + ui->tablePage->setValidator([=]() -> bool + { + bool valid = !ui->tableNameCombo->currentText().isEmpty(); + setValidStateWihtTooltip(ui->tableNameCombo, tr("If you type table name that doesn't exist, it will be created."), valid, tr("Enter the table name")); + return valid; + }); +} + +void ImportDialog::initDataSourcePage() +{ + ui->inputFileButton->setIcon(ICONS.OPEN_FILE); + connect(ui->inputFileButton, SIGNAL(clicked()), this, SLOT(browseForInputFile())); + + ui->codecCombo->addItems(textCodecNames()); + ui->codecCombo->setCurrentText(defaultCodecName()); + + ui->dsPage->setValidator([=]() -> bool + { + setValidState(ui->dsTypeCombo, true); + if (!currentPlugin) + { + setValidState(ui->dsTypeCombo, false, tr("Select import plugin.")); + return false; + } + + if (currentPlugin->standardOptionsToEnable().testFlag(ImportManager::FILE_NAME)) + { + QString path = ui->inputFileEdit->text(); + if (path.trimmed().isEmpty()) + { + setValidState(ui->inputFileEdit, false, tr("You must provide a file to import from.")); + return false; + } + + QFileInfo file(path); + if (!file.exists()) + { + setValidState(ui->inputFileEdit, false, tr("The file '%1' does not exist.").arg(path)); + return false; + } + + if (file.exists() && file.isDir()) + { + setValidState(ui->inputFileEdit, false, tr("Path you provided is a directory. A regular file is required.")); + return false; + } + setValidState(ui->inputFileEdit, true); + } + return ui->dsTypeCombo->currentIndex() > -1 && ui->codecCombo->currentIndex() > -1 && isPluginConfigValid(); + }); + + connect(this, SIGNAL(dsPageCompleteChanged()), ui->dsPage, SIGNAL(completeChanged())); + connect(ui->dsTypeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(pluginSelected())); + connect(ui->dsTypeCombo, SIGNAL(currentTextChanged(QString)), ui->dsPage, SIGNAL(completeChanged())); + connect(ui->codecCombo, SIGNAL(currentTextChanged(QString)), ui->dsPage, SIGNAL(completeChanged())); + connect(ui->inputFileEdit, SIGNAL(textChanged(QString)), ui->dsPage, SIGNAL(completeChanged())); + + ui->dsTypeCombo->addItems(IMPORT_MANAGER->getImportDataSourceTypes()); +} + +void ImportDialog::removeOldOptions() +{ + safe_delete(configMapper); + safe_delete(pluginOptionsWidget); +} + +void ImportDialog::updateStandardOptions() +{ + bool showFileName = currentPlugin->standardOptionsToEnable().testFlag(ImportManager::FILE_NAME); + bool showCodec = currentPlugin->standardOptionsToEnable().testFlag(ImportManager::CODEC); + + if (!showFileName && !showCodec) + { + ui->dsOptionsGroup->setVisible(false); + return; + } + + ui->dsOptionsGroup->setVisible(true); + + int row = 0; + QGridLayout* grid = dynamic_cast(ui->dsOptionsGroup->layout()); + if (showFileName) + { + grid->addWidget(ui->inputFileLabel, row, 0); + grid->addWidget(ui->inputFileWidget, row, 1); + row++; + } + else + { + grid->removeWidget(ui->inputFileLabel); + grid->removeWidget(ui->inputFileWidget); + } + + ui->inputFileLabel->setVisible(showFileName); + ui->inputFileWidget->setVisible(showFileName); + + if (showCodec) + { + grid->addWidget(ui->codecLabel, row, 0); + grid->addWidget(ui->codecCombo, row, 1); + row++; + } + else + { + grid->removeWidget(ui->codecLabel); + grid->removeWidget(ui->codecCombo); + } + + ui->codecLabel->setVisible(showCodec); + ui->codecCombo->setVisible(showCodec); +} + +void ImportDialog::updatePluginOptions(int& rows) +{ + QString formName = currentPlugin->getImportConfigFormName(); + CfgMain* cfgMain = currentPlugin->getConfig(); + ui->dsPluginOptionsGroup->setVisible(false); + if (formName.isNull() || !cfgMain) + { + if (!formName.isNull()) + { + qWarning() << "FormName is given, but cfgMain is null in ImportDialog::updatePluginOptions() for plugin:" << currentPlugin->getName() + << ", formName:" << formName; + } + return; + } + + if (!FORMS->hasWidget(formName)) + { + qWarning() << "Import plugin" << currentPlugin->getName() << "requested for form named" << formName << "but FormManager doesn't have it." + << "Available forms are:" << FORMS->getAvailableForms(); + return; + } + + pluginOptionsWidget = FORMS->createWidget(formName); + if (!pluginOptionsWidget) + { + qWarning() << "Import plugin" << currentPlugin->getName() << "requested for form named" << formName << "but FormManager returned null."; + return; + } + + ui->dsPluginOptionsGroup->setVisible(true); + + if (pluginOptionsWidget->layout()) + pluginOptionsWidget->layout()->setMargin(0); + + ui->dsPluginOptionsGroup->layout()->addWidget(pluginOptionsWidget); + rows++; + + configMapper = new ConfigMapper(cfgMain); + configMapper->bindToConfig(pluginOptionsWidget); + connect(configMapper, SIGNAL(modified()), this, SLOT(updateValidation())); + updateValidation(); +} + +void ImportDialog::handleValidationResultFromPlugin(bool valid, CfgEntry* key, const QString& errorMsg) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (w) + setValidState(w, valid, errorMsg); + + if (valid == pluginConfigOk.contains(key)) // if state changed + { + if (!valid) + pluginConfigOk[key] = false; + else + pluginConfigOk.remove(key); + } +} + +void ImportDialog::stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (!w) + return; + + w->setVisible(visible); + w->setEnabled(enabled); +} + +void ImportDialog::refreshTables() +{ + Db* db = DBLIST->getByName(ui->dbNameCombo->currentText()); + if (db) + tablesModel->setDb(db); +} + +void ImportDialog::pluginSelected() +{ + ui->dsPluginOptionsGroup->setVisible(false); + removeOldOptions(); + currentPlugin = IMPORT_MANAGER->getPluginForDataSourceType(ui->dsTypeCombo->currentText()); + if (!currentPlugin) + return; + + updateStandardOptions(); + + int rows = 0; + updatePluginOptions(rows); + ui->dsPluginOptionsGroup->setVisible(rows > 0); +} + +void ImportDialog::updateValidation() +{ + if (!currentPlugin) + return; + + currentPlugin->validateOptions(); + emit dsPageCompleteChanged(); +} + +void ImportDialog::pageChanged() +{ + if (currentPage() == ui->dsPage) + updateValidation(); +} + +void ImportDialog::browseForInputFile() +{ + if (!currentPlugin) + { + qCritical() << "Called ImportDialog::browseForInputFile(), but no ImportPlugin is selected."; + return; + } + + QString dir = getFileDialogInitPath(); + QString filter = currentPlugin->getFileFilter(); + QString fileName = QFileDialog::getOpenFileName(this, tr("Pick file to import from"), dir, filter); + if (fileName.isNull()) + return; + + ui->inputFileEdit->setText(fileName); + setFileDialogInitPathByFile(fileName); +} + +void ImportDialog::success() +{ + QWizard::accept(); +} + +void ImportDialog::hideCoverWidget() +{ + widgetCover->hide(); +} + +void ImportDialog::accept() +{ + if (!currentPlugin) + { + qCritical() << "Called ImportDialog::accept(), but no ImportPlugin is selected."; + return; + } + + ImportManager::StandardImportConfig stdConfig; + if (currentPlugin->standardOptionsToEnable().testFlag(ImportManager::FILE_NAME)) + stdConfig.inputFileName = ui->inputFileEdit->text(); + + if (currentPlugin->standardOptionsToEnable().testFlag(ImportManager::CODEC)) + stdConfig.codec = ui->codecCombo->currentText(); + + Db* db = DBLIST->getByName(ui->dbNameCombo->currentText());; + if (!db) + { + qCritical() << "Called ImportDialog::accept(), but no database is selected."; + return; + } + + QString table = ui->tableNameCombo->currentText(); + + widgetCover->show(); + IMPORT_MANAGER->configure(currentPlugin->getDataSourceTypeName(), stdConfig); + IMPORT_MANAGER->importToTable(db, table); +} + +void ImportDialog::showEvent(QShowEvent* e) +{ + QWizard::showEvent(e); + ui->tableNameCombo->setFocus(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.h new file mode 100644 index 0000000..c50703f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.h @@ -0,0 +1,69 @@ +#ifndef IMPORTDIALOG_H +#define IMPORTDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class ImportDialog; +} + +class DbListModel; +class DbObjListModel; +class ImportPlugin; +class ConfigMapper; +class CfgEntry; +class WidgetCover; +class Db; + +class GUI_API_EXPORT ImportDialog : public QWizard +{ + Q_OBJECT + + public: + explicit ImportDialog(QWidget *parent = 0); + ~ImportDialog(); + + void setDbAndTable(Db* db, const QString& table); + void setDb(Db* db); + + protected: + void showEvent(QShowEvent* e); + + private: + void init(); + void initTablePage(); + void initDataSourcePage(); + void removeOldOptions(); + void updateStandardOptions(); + void updatePluginOptions(int& rows); + bool isPluginConfigValid() const; + + Ui::ImportDialog *ui = nullptr; + DbListModel* dbListModel = nullptr; + DbObjListModel* tablesModel = nullptr; + ConfigMapper* configMapper = nullptr; + QWidget* pluginOptionsWidget = nullptr; + ImportPlugin* currentPlugin = nullptr; + QHash pluginConfigOk; + WidgetCover* widgetCover = nullptr; + + private slots: + void handleValidationResultFromPlugin(bool valid, CfgEntry* key, const QString& errorMsg); + void stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled); + void refreshTables(); + void pluginSelected(); + void updateValidation(); + void pageChanged(); + void browseForInputFile(); + void success(); + void hideCoverWidget(); + + public slots: + void accept(); + + signals: + void dsPageCompleteChanged(); +}; + +#endif // IMPORTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.ui new file mode 100644 index 0000000..b853ab8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.ui @@ -0,0 +1,230 @@ + + + ImportDialog + + + + 0 + 0 + 511 + 406 + + + + Import data + + + QWizard::CancelButtonOnLeft|QWizard::NoDefaultButton + + + + Table to import to + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Table + + + + + + + Database + + + + + + + . + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + true + + + + + + + + + + + Data source to import from + + + + + + #scrollArea { background: transparent; } + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 269 + 280 + + + + #scrollAreaWidgetContents { background: transparent; } + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Data source type + + + + + + + + + + + + Options + + + + + + Input file: + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + ... + + + + + + + + + + Text encoding: + + + + + + + + + + + + + Data source options + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + VerifiableWizardPage + QWizardPage +
    common/verifiablewizardpage.h
    + 1 +
    +
    + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp new file mode 100644 index 0000000..d835dd1 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -0,0 +1,468 @@ +#include "indexdialog.h" +#include "ui_indexdialog.h" +#include "schemaresolver.h" +#include "parser/ast/sqliteindexedcolumn.h" +#include "services/notifymanager.h" +#include "common/utils_sql.h" +#include "db/chainexecutor.h" +#include "dbtree/dbtree.h" +#include "ddlpreviewdialog.h" +#include "uiconfig.h" +#include "services/config.h" +#include "uiutils.h" +#include "sqlite3.h" +#include "windows/editorwindow.h" +#include "services/codeformatter.h" +#include +#include +#include +#include +#include +#include + +IndexDialog::IndexDialog(Db* db, QWidget *parent) : + QDialog(parent), + db(db), + ui(new Ui::IndexDialog) +{ + init(); +} + +IndexDialog::IndexDialog(Db* db, const QString& index, QWidget* parent) : + QDialog(parent), + db(db), + index(index), + ui(new Ui::IndexDialog) +{ + existingIndex = true; + init(); +} + +IndexDialog::~IndexDialog() +{ + delete ui; +} + +void IndexDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void IndexDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + if (!db || !db->isOpen()) + { + qCritical() << "Created IndexDialog for null or closed database."; + notifyError(tr("Tried to open index dialog for closed or inexisting database.")); + reject(); + return; + } + + ui->columnsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + ui->partialIndexEdit->setDb(db); + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + + columnStateSignalMapping = new QSignalMapper(this); + connect(columnStateSignalMapping, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int))); + + SchemaResolver resolver(db); + ui->tableCombo->addItem(QString::null); + ui->tableCombo->addItems(resolver.getTables()); + connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateTable(QString))); + connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidation())); + if (existingIndex) + ui->tableCombo->setEnabled(false); + + if (db->getDialect() == Dialect::Sqlite3) + { + connect(ui->partialIndexCheck, SIGNAL(toggled(bool)), this, SLOT(updatePartialConditionState())); + connect(ui->partialIndexEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->partialIndexEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->partialIndexEdit->setVirtualSqlExpression("SELECT %1"); + updatePartialConditionState(); + ui->columnsTable->setColumnHidden(1, false); + } + else + { + ui->partialIndexCheck->setVisible(false); + ui->partialIndexEdit->setVisible(false); + ui->columnsTable->setColumnHidden(1, true); + } + + readCollations(); + + ui->ddlEdit->setSqliteVersion(db->getVersion()); + + if (index.isNull()) + createIndex = SqliteCreateIndexPtr::create(); + else + readIndex(); + + originalCreateIndex = SqliteCreateIndexPtr::create(*createIndex); + + ui->nameEdit->setText(index); + setTable(createIndex->table); + + if (!index.isNull()) + applyIndex(); + + updateValidation(); + + ui->nameEdit->setFocus(); +} + +void IndexDialog::readIndex() +{ + SchemaResolver resolver(db); + SqliteQueryPtr parsedObject = resolver.getParsedObject(index, SchemaResolver::INDEX); + if (!parsedObject.dynamicCast()) + { + notifyError(tr("Could not process index %1 correctly. Unable to open an index dialog.").arg(index)); + reject(); + return; + } + + createIndex = parsedObject.dynamicCast(); +} + +void IndexDialog::buildColumns() +{ + // Clean up + ui->columnsTable->setRowCount(0); + columnCheckBoxes.clear(); + sortComboBoxes.clear(); + collateComboBoxes.clear(); + + totalColumns = tableColumns.size(); + ui->columnsTable->setRowCount(totalColumns); + + int row = 0; + foreach (const QString& column, tableColumns) + buildColumn(column, row++); +} + +void IndexDialog::updateTable(const QString& value) +{ + table = value; + + SchemaResolver resolver(db); + tableColumns = resolver.getTableColumns(table); + + buildColumns(); +} + +void IndexDialog::updateValidation() +{ + bool tableOk = ui->tableCombo->currentIndex() > 0; + bool colSelected = false; + + if (tableOk) + { + foreach (QCheckBox* cb, columnCheckBoxes) + { + if (cb->isChecked()) + { + colSelected = true; + break; + } + } + } + + bool partialConditionOk = (!ui->partialIndexCheck->isChecked() || + (ui->partialIndexEdit->isSyntaxChecked() && !ui->partialIndexEdit->haveErrors())); + + setValidState(ui->tableCombo, tableOk, tr("Pick the table for the index.")); + setValidState(ui->columnsTable, colSelected, tr("Select at least one column.")); + setValidState(ui->partialIndexCheck, partialConditionOk, tr("Enter a valid condition.")); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(colSelected && partialConditionOk); +} + +void IndexDialog::setTable(const QString& value) +{ + ui->tableCombo->setCurrentText(value); +} + +void IndexDialog::readCollations() +{ + SchemaResolver resolver(db); + QStringList collList = resolver.getCollations(); + + if (collList.size() > 0) + collList.prepend(""); + + collations.setStringList(collList); +} + +void IndexDialog::buildColumn(const QString& name, int row) +{ + int col = 0; + + QWidget* checkParent = new QWidget(); + QHBoxLayout* layout = new QHBoxLayout(); + QMargins margins = layout->contentsMargins(); + margins.setTop(0); + margins.setBottom(0); + margins.setLeft(4); + margins.setRight(4); + layout->setContentsMargins(margins); + checkParent->setLayout(layout); + + QCheckBox* check = new QCheckBox(name); + checkParent->layout()->addWidget(check); + + ui->columnsTable->setCellWidget(row, col++, checkParent); + columnStateSignalMapping->setMapping(check, row); + connect(check, SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); + connect(check, SIGNAL(toggled(bool)), this, SLOT(updateValidation())); + columnCheckBoxes << check; + + QComboBox* collation = nullptr; + if (db->getDialect() == Dialect::Sqlite3) + { + collation = new QComboBox(); + collation->setEditable(true); + collation->lineEdit()->setPlaceholderText(tr("default", "index dialog")); + collation->setModel(&collations); + ui->columnsTable->setCellWidget(row, col++, collation); + collateComboBoxes << collation; + } + else + { + col++; + } + + QComboBox* sortOrder = new QComboBox(); + sortOrder->setToolTip(tr("Sort order", "table constraints")); + ui->columnsTable->setCellWidget(row, col++, sortOrder); + sortComboBoxes << sortOrder; + + QStringList sortList = {"", sqliteSortOrder(SqliteSortOrder::ASC), sqliteSortOrder(SqliteSortOrder::DESC)}; + sortOrder->addItems(sortList); + + totalColumns++; + + updateColumnState(row); +} + +void IndexDialog::updateColumnState(int row) +{ + bool enabled = columnCheckBoxes[row]->isChecked(); + sortComboBoxes[row]->setEnabled(enabled); + if (db->getDialect() == Dialect::Sqlite3) + collateComboBoxes[row]->setEnabled(enabled); +} + +void IndexDialog::updatePartialConditionState() +{ + ui->partialIndexEdit->setEnabled(ui->partialIndexCheck->isChecked()); + updateValidation(); +} + +void IndexDialog::updateDdl() +{ + rebuildCreateIndex(); + QString formatted = FORMATTER->format("sql", createIndex->detokenize(), db); + ui->ddlEdit->setPlainText(formatted); +} + +void IndexDialog::tabChanged(int tab) +{ + if (tab == 1) + updateDdl(); +} + +void IndexDialog::applyColumnValues() +{ + Dialect dialect = db->getDialect(); + int row; + foreach (SqliteIndexedColumn* idxCol, createIndex->indexedColumns) + { + row = indexOf(tableColumns, idxCol->name, Qt::CaseInsensitive); + if (row == -1) + { + qCritical() << "Cannot find column from index in the table columns! Indexed column:" << idxCol->name + << ", table columns:" << tableColumns << ", index name:" << index << ", table name:" << table; + continue; + } + + columnCheckBoxes[row]->setChecked(true); + updateColumnState(row); + sortComboBoxes[row]->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); + if (dialect == Dialect::Sqlite3) + collateComboBoxes[row]->setCurrentText(idxCol->collate); + } +} + +void IndexDialog::applyIndex() +{ + applyColumnValues(); + + ui->partialIndexCheck->setChecked(createIndex->where != nullptr); + if (createIndex->where) + ui->partialIndexEdit->setPlainText(createIndex->where->detokenize()); +} + +SqliteIndexedColumn* IndexDialog::addIndexedColumn(const QString& name) +{ + SqliteIndexedColumn* idxCol = new SqliteIndexedColumn(); + idxCol->name = name; + idxCol->setParent(createIndex.data()); + createIndex->indexedColumns << idxCol; + return idxCol; +} + +void IndexDialog::rebuildCreateIndex() +{ + createIndex = SqliteCreateIndexPtr::create(); + createIndex->index = ui->nameEdit->text(); + if (ui->tableCombo->currentIndex() > -1) + createIndex->table = ui->tableCombo->currentText(); + + createIndex->uniqueKw = ui->uniqueCheck->isChecked(); + + SqliteIndexedColumn* idxCol = nullptr; + int i = -1; + for (const QString& column : tableColumns) + { + i++; + + if (!columnCheckBoxes[i]->isChecked()) + continue; + + idxCol = addIndexedColumn(column); + if (!collateComboBoxes[i]->currentText().isEmpty()) + idxCol->collate = collateComboBoxes[i]->currentText(); + + if (sortComboBoxes[i]->currentIndex() > 0) + idxCol->sortOrder = sqliteSortOrder(sortComboBoxes[i]->currentText()); + } + + if (ui->partialIndexCheck->isChecked()) + { + if (createIndex->where) + delete createIndex->where; + + Parser parser(db->getDialect()); + SqliteExpr* expr = parser.parseExpr(ui->partialIndexEdit->toPlainText()); + + if (expr) + { + expr->setParent(createIndex.data()); + createIndex->where = expr; + } + else + { + qCritical() << "Could not parse expression from partial index condition: " << ui->partialIndexEdit->toPlainText() + << ", the CREATE INDEX statement will be incomplete."; + } + } + + createIndex->rebuildTokens(); +} + +void IndexDialog::queryDuplicates() +{ + static QString queryTpl = QStringLiteral("SELECT %1 FROM %2 GROUP BY %3 HAVING %4;\n"); + static QString countTpl = QStringLiteral("count(%1) AS %2"); + static QString countColNameTpl = QStringLiteral("count(%1)"); + static QString countConditionTpl = QStringLiteral("count(%1) > 1"); + + Dialect dialect = db->getDialect(); + + QStringList cols; + QStringList grpCols; + QStringList countCols; + QString wrappedCol; + QString countColName; + int i = 0; + for (const QString& column : tableColumns) + { + if (!columnCheckBoxes[i++]->isChecked()) + continue; + + wrappedCol = wrapObjIfNeeded(column, dialect); + cols << wrappedCol; + grpCols << wrappedCol; + countColName = wrapObjIfNeeded(countColNameTpl.arg(column), dialect); + cols << countTpl.arg(wrappedCol, countColName); + countCols << countConditionTpl.arg(wrappedCol); + } + + EditorWindow* editor = MAINWINDOW->openSqlEditor(); + editor->setCurrentDb(db); + + QString sqlCols = cols.join(", "); + QString sqlGrpCols = grpCols.join(", "); + QString sqlCntCols = countCols.join(" AND "); + QString sqlTable = wrapObjIfNeeded(ui->tableCombo->currentText(), dialect); + editor->setContents(queryTpl.arg(sqlCols, sqlTable, sqlGrpCols, sqlCntCols)); + editor->execute(); +} + +void IndexDialog::accept() +{ + rebuildCreateIndex(); + + Dialect dialect = db->getDialect(); + + QStringList sqls; + if (existingIndex) + sqls << QString("DROP INDEX %1").arg(wrapObjIfNeeded(originalCreateIndex->index, dialect)); + + sqls << createIndex->detokenize(); + + if (!CFG_UI.General.DontShowDdlPreview.get()) + { + DdlPreviewDialog dialog(db, this); + dialog.setDdl(sqls); + if (dialog.exec() != QDialog::Accepted) + return; + } + + ChainExecutor executor; + executor.setDb(db); + executor.setAsync(false); + executor.setQueries(sqls); + executor.exec(); + + if (executor.getSuccessfulExecution()) + { + CFG->addDdlHistory(sqls.join("\n"), db->getName(), db->getPath()); + + QDialog::accept(); + DBTREE->refreshSchema(db); + return; + } + + if (executor.getErrors().size() == 1 && executor.getErrors().first().first == SQLITE_CONSTRAINT) + { + int res = QMessageBox::critical(this, + tr("Error", "index dialog"), + tr("Cannot create unique index, because values in selected columns are not unique. " + "Would you like to execute SELECT query to see problematic values?"), + QMessageBox::Yes, + QMessageBox::No); + if (res == QMessageBox::Yes) + { + QDialog::reject(); + queryDuplicates(); + } + } + else + { + QMessageBox::critical(this, tr("Error", "index dialog"), tr("An error occurred while executing SQL statements:\n%1") + .arg(executor.getErrorsMessages().join(",\n")), QMessageBox::Ok); + } +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h new file mode 100644 index 0000000..1f9d1f8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h @@ -0,0 +1,72 @@ +#ifndef INDEXDIALOG_H +#define INDEXDIALOG_H + +#include "db/db.h" +#include "guiSQLiteStudio_global.h" +#include "parser/ast/sqlitecreateindex.h" +#include +#include + +namespace Ui { + class IndexDialog; +} + +class QGridLayout; +class QSignalMapper; +class QCheckBox; +class QComboBox; + +class GUI_API_EXPORT IndexDialog : public QDialog +{ + Q_OBJECT + + public: + IndexDialog(Db* db, QWidget *parent = 0); + IndexDialog(Db* db, const QString& index, QWidget *parent = 0); + ~IndexDialog(); + + void setTable(const QString& value); + + protected: + void changeEvent(QEvent *e); + + private: + void init(); + void readIndex(); + void readCollations(); + void buildColumn(const QString& name, int row); + void applyColumnValues(); + void applyIndex(); + SqliteIndexedColumn* addIndexedColumn(const QString& name); + void rebuildCreateIndex(); + void queryDuplicates(); + + bool existingIndex = false; + Db* db = nullptr; + QString table; + QString index; + SqliteCreateIndexPtr createIndex; + SqliteCreateIndexPtr originalCreateIndex; + QStringList tableColumns; + QSignalMapper* columnStateSignalMapping = nullptr; + QStringListModel collations; + QList columnCheckBoxes; + QList sortComboBoxes; + QList collateComboBoxes; + int totalColumns = 0; + Ui::IndexDialog *ui = nullptr; + + private slots: + void updateValidation(); + void buildColumns(); + void updateTable(const QString& value); + void updateColumnState(int row); + void updatePartialConditionState(); + void updateDdl(); + void tabChanged(int tab); + + public slots: + void accept(); +}; + +#endif // INDEXDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui new file mode 100644 index 0000000..e231550 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.ui @@ -0,0 +1,195 @@ + + + IndexDialog + + + + 0 + 0 + 491 + 410 + + + + + 400 + 300 + + + + Index dialog + + + + + + 0 + + + + Index + + + + + + On table: + + + + + + + Index name: + + + + + + + Partial index condition + + + + + + + + 0 + 1 + + + + + + + + Unique index + + + + + + + + 0 + 2 + + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + Column + + + + + Collation + + + + + Sort + + + + + + + + + + + + + + + DDL + + + + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + SqlView + QPlainTextEdit +
    sqlview.h
    +
    + + SqlEditor + QPlainTextEdit +
    sqleditor.h
    +
    +
    + + tabWidget + partialIndexCheck + partialIndexEdit + buttonBox + ddlEdit + + + + + buttonBox + accepted() + IndexDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IndexDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.cpp new file mode 100644 index 0000000..fb78367 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.cpp @@ -0,0 +1,97 @@ +#include "messagelistdialog.h" +#include "iconmanager.h" +#include "ui_messagelistdialog.h" +#include +#include + +MessageListDialog::MessageListDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::MessageListDialog) +{ + ui->setupUi(this); + ui->message->setVisible(false); +} + +MessageListDialog::MessageListDialog(const QString& message, QWidget* parent) : + QDialog(parent), + ui(new Ui::MessageListDialog) +{ + ui->setupUi(this); + ui->buttonBox->clear(); + ui->buttonBox->addButton(QDialogButtonBox::Yes); + ui->buttonBox->addButton(QDialogButtonBox::No); + ui->message->setText(message); +} + +MessageListDialog::~MessageListDialog() +{ + delete ui; +} + +void MessageListDialog::addMessage(const QString& text, const QBrush& background) +{ + addMessage(QIcon(), text, background); +} + +void MessageListDialog::addMessage(const QIcon& icon, const QString& text, const QBrush& background) +{ + QListWidgetItem* item = new QListWidgetItem(); + item->setText(text); + item->setBackground(background); + item->setIcon(icon); + ui->listWidget->addItem(item); +} + +void MessageListDialog::addInfo(const QString& text) +{ + addMessage(ICONS.STATUS_INFO, text, getGradient(0, 0, 1, 0.2)); +} + +void MessageListDialog::addWarning(const QString& text) +{ + addMessage(ICONS.STATUS_WARNING, text, getGradient(0.8, 0.8, 0, 0.4)); +} + +void MessageListDialog::addError(const QString& text) +{ + addMessage(ICONS.STATUS_ERROR, text, getGradient(0.6, 0, 0, 0.6)); +} + +void MessageListDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +QBrush MessageListDialog::getGradient(qreal r, qreal g, qreal b, qreal a) const +{ + QLinearGradient gradient(0, 0, 20, 120); + gradient.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0)); + gradient.setColorAt(1, QColor::fromRgbF(r, g, b, a)); + + return QBrush(gradient); +} + +void MessageListDialog::showEvent(QShowEvent*) +{ + adjustSize(); +} + +void MessageListDialog::resizeEvent(QResizeEvent*) +{ + QFontMetrics metrics = ui->listWidget->fontMetrics(); + QRect rect = ui->listWidget->rect(); + int cnt = ui->listWidget->count(); + QListWidgetItem* item = nullptr; + for (int row = 0; row < cnt; row++) + { + item = ui->listWidget->item(row); + item->setSizeHint(metrics.boundingRect(rect, Qt::TextWordWrap|Qt::TextLongestVariant, item->text()).size() + QSize(0, 10)); + } +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.h new file mode 100644 index 0000000..e20d9f8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.h @@ -0,0 +1,37 @@ +#ifndef MESSAGELISTDIALOG_H +#define MESSAGELISTDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class MessageListDialog; +} + +class GUI_API_EXPORT MessageListDialog : public QDialog +{ + Q_OBJECT + + public: + explicit MessageListDialog(QWidget *parent = 0); + explicit MessageListDialog(const QString& message, QWidget *parent = 0); + ~MessageListDialog(); + + void addMessage(const QString& text, const QBrush& background = QBrush()); + void addMessage(const QIcon& icon, const QString& text, const QBrush& background = QBrush()); + void addInfo(const QString& text); + void addWarning(const QString& text); + void addError(const QString& text); + + protected: + void changeEvent(QEvent *e); + void showEvent(QShowEvent*); + void resizeEvent(QResizeEvent*); + + private: + QBrush getGradient(qreal r, qreal g, qreal b, qreal a) const; + + Ui::MessageListDialog *ui = nullptr; +}; + +#endif // MESSAGELISTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.ui new file mode 100644 index 0000000..10ee6b8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/messagelistdialog.ui @@ -0,0 +1,84 @@ + + + MessageListDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + + + + + + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + MessageListDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MessageListDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp new file mode 100644 index 0000000..36b400b --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp @@ -0,0 +1,275 @@ +#include "newconstraintdialog.h" +#include "ui_newconstraintdialog.h" +#include "iconmanager.h" +#include + +NewConstraintDialog::NewConstraintDialog(SqliteCreateTable* createTable, Db* db, QWidget *parent) : + QDialog(parent), + ui(new Ui::NewConstraintDialog), + type(ConstraintDialog::TABLE), + db(db), + createTable(createTable) +{ + ui->setupUi(this); + init(); +} + +NewConstraintDialog::NewConstraintDialog(SqliteCreateTable::Column* column, Db* db, QWidget* parent) : + QDialog(parent), + ui(new Ui::NewConstraintDialog), + type(ConstraintDialog::COLUMN), + db(db), + columnStmt(column) +{ + ui->setupUi(this); + createTable = dynamic_cast(column->parent()); + init(); +} + +NewConstraintDialog::NewConstraintDialog(ConstraintDialog::Constraint constraintType, SqliteCreateTable* createTable, Db* db, QWidget* parent) : + NewConstraintDialog(createTable, db, parent) +{ + predefinedConstraintType = constraintType; +} + +NewConstraintDialog::NewConstraintDialog(ConstraintDialog::Constraint constraintType, SqliteCreateTable::Column* column, Db* db, QWidget* parent) : + NewConstraintDialog(column, db, parent) +{ + predefinedConstraintType = constraintType; +} + +NewConstraintDialog::~NewConstraintDialog() +{ + delete ui; + if (constraintDialog) + delete constraintDialog; +} + +SqliteStatement* NewConstraintDialog::getConstraint() +{ + return constrStatement; +} + +void NewConstraintDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void NewConstraintDialog::init() +{ + switch (type) + { + case ConstraintDialog::TABLE: + { + initTable(); + break; + } + case ConstraintDialog::COLUMN: + { + initColumn(); + break; + } + } + adjustSize(); + setMaximumSize(size()); + setMinimumSize(size()); +} + +void NewConstraintDialog::initTable() +{ + addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createTablePk())); + if (createTable->dialect == Dialect::Sqlite3) + addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createTableFk())); + + addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createTableUnique())); + addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createTableCheck())); +} + +void NewConstraintDialog::initColumn() +{ + addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createColumnPk())); + if (createTable->dialect == Dialect::Sqlite3) + addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createColumnFk())); + + addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createColumnUnique())); + addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createColumnCheck())); + addButton(ICONS.CONSTRAINT_NOT_NULL, tr("Not NULL", "new constraint dialog"), SLOT(createColumnNotNull())); + if (createTable->dialect == Dialect::Sqlite3) + addButton(ICONS.CONSTRAINT_COLLATION, tr("Collate", "new constraint dialog"), SLOT(createColumnCollate())); + + addButton(ICONS.CONSTRAINT_DEFAULT, tr("Default", "new constraint dialog"), SLOT(createColumnDefault())); +} + +void NewConstraintDialog::addButton(const Icon& icon, const QString text, const char* slot) +{ + QCommandLinkButton* btn = new QCommandLinkButton(); + btn->setIcon(icon); + btn->setText(text); + connect(btn, SIGNAL(clicked()), this, slot); + ui->container->layout()->addWidget(btn); +} + +int NewConstraintDialog::createColumnConstraint(ConstraintDialog::Constraint constraintType) +{ + SqliteCreateTable::Column::Constraint* constraint = new SqliteCreateTable::Column::Constraint(); + switch (constraintType) + { + case ConstraintDialog::PK: + constraint->type = SqliteCreateTable::Column::Constraint::PRIMARY_KEY; + break; + case ConstraintDialog::FK: + constraint->type = SqliteCreateTable::Column::Constraint::FOREIGN_KEY; + break; + case ConstraintDialog::UNIQUE: + constraint->type = SqliteCreateTable::Column::Constraint::UNIQUE; + break; + case ConstraintDialog::NOTNULL: + constraint->type = SqliteCreateTable::Column::Constraint::NOT_NULL; + break; + case ConstraintDialog::CHECK: + constraint->type = SqliteCreateTable::Column::Constraint::CHECK; + break; + case ConstraintDialog::COLLATE: + constraint->type = SqliteCreateTable::Column::Constraint::COLLATE; + break; + case ConstraintDialog::DEFAULT: + constraint->type = SqliteCreateTable::Column::Constraint::DEFAULT; + break; + case ConstraintDialog::UNKNOWN: + break; + } + + constrStatement = constraint; + constrStatement->setParent(columnStmt); + + return editConstraint(); +} + +int NewConstraintDialog::createTableConstraint(ConstraintDialog::Constraint constraintType) +{ + SqliteCreateTable::Constraint* constraint = new SqliteCreateTable::Constraint(); + switch (constraintType) + { + case ConstraintDialog::PK: + constraint->type = SqliteCreateTable::Constraint::PRIMARY_KEY; + break; + case ConstraintDialog::FK: + constraint->type = SqliteCreateTable::Constraint::FOREIGN_KEY; + break; + case ConstraintDialog::UNIQUE: + constraint->type = SqliteCreateTable::Constraint::UNIQUE; + break; + case ConstraintDialog::CHECK: + constraint->type = SqliteCreateTable::Constraint::CHECK; + break; + case ConstraintDialog::NOTNULL: + case ConstraintDialog::COLLATE: + case ConstraintDialog::DEFAULT: + case ConstraintDialog::UNKNOWN: + break; + } + + constrStatement = constraint; + constrStatement->setParent(createTable); + + return editConstraint(); +} + +int NewConstraintDialog::editConstraint() +{ + switch (type) + { + case ConstraintDialog::TABLE: + constraintDialog = new ConstraintDialog(ConstraintDialog::NEW, dynamic_cast(constrStatement), + createTable.data(), db, parentWidget()); + break; + case ConstraintDialog::COLUMN: + constraintDialog = new ConstraintDialog(ConstraintDialog::NEW, dynamic_cast(constrStatement), + columnStmt.data(), db, parentWidget()); + break; + } + + connect(constraintDialog, SIGNAL(rejected()), this, SLOT(reject())); + connect(constraintDialog, SIGNAL(accepted()), this, SLOT(accept())); + + hide(); + return constraintDialog->exec(); +} + +void NewConstraintDialog::createTablePk() +{ + createTableConstraint(ConstraintDialog::PK); +} + +void NewConstraintDialog::createTableFk() +{ + createTableConstraint(ConstraintDialog::FK); +} + +void NewConstraintDialog::createTableUnique() +{ + createTableConstraint(ConstraintDialog::UNIQUE); +} + +void NewConstraintDialog::createTableCheck() +{ + createTableConstraint(ConstraintDialog::CHECK); +} + +void NewConstraintDialog::createColumnPk() +{ + createColumnConstraint(ConstraintDialog::PK); +} + +void NewConstraintDialog::createColumnFk() +{ + createColumnConstraint(ConstraintDialog::FK); +} + +void NewConstraintDialog::createColumnUnique() +{ + createColumnConstraint(ConstraintDialog::UNIQUE); +} + +void NewConstraintDialog::createColumnCheck() +{ + createColumnConstraint(ConstraintDialog::CHECK); +} + +void NewConstraintDialog::createColumnNotNull() +{ + createColumnConstraint(ConstraintDialog::NOTNULL); +} + +void NewConstraintDialog::createColumnDefault() +{ + createColumnConstraint(ConstraintDialog::DEFAULT); +} + +void NewConstraintDialog::createColumnCollate() +{ + createColumnConstraint(ConstraintDialog::COLLATE); +} + +int NewConstraintDialog::exec() +{ + if (predefinedConstraintType == ConstraintDialog::UNKNOWN) + return QDialog::exec(); + + switch (type) + { + case ConstraintDialog::TABLE: + return createTableConstraint(predefinedConstraintType); + case ConstraintDialog::COLUMN: + return createColumnConstraint(predefinedConstraintType); + } + + return QDialog::Rejected; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h new file mode 100644 index 0000000..e374312 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h @@ -0,0 +1,65 @@ +#ifndef NEWCONSTRAINTDIALOG_H +#define NEWCONSTRAINTDIALOG_H + +#include "parser/ast/sqlitecreatetable.h" +#include "db/db.h" +#include "dialogs/constraintdialog.h" +#include "iconmanager.h" +#include "guiSQLiteStudio_global.h" +#include +#include + +namespace Ui { + class NewConstraintDialog; +} + +class GUI_API_EXPORT NewConstraintDialog : public QDialog +{ + Q_OBJECT + + public: + explicit NewConstraintDialog(SqliteCreateTable* createTable, Db* db, QWidget *parent = 0); + explicit NewConstraintDialog(SqliteCreateTable::Column* column, Db* db, QWidget *parent = 0); + explicit NewConstraintDialog(ConstraintDialog::Constraint constraintType, SqliteCreateTable* createTable, Db* db, QWidget *parent = 0); + explicit NewConstraintDialog(ConstraintDialog::Constraint constraintType, SqliteCreateTable::Column* column, Db* db, QWidget *parent = 0); + ~NewConstraintDialog(); + + SqliteStatement* getConstraint(); + int exec(); + + protected: + void changeEvent(QEvent *e); + + private: + void init(); + void initTable(); + void initColumn(); + void addButton(const Icon& icon, const QString text, const char* slot); + int createColumnConstraint(ConstraintDialog::Constraint constraintType); + int createTableConstraint(ConstraintDialog::Constraint constraintType); + int editConstraint(); + + Ui::NewConstraintDialog *ui = nullptr; + ConstraintDialog::Type type; + Db* db = nullptr; + ConstraintDialog::Constraint predefinedConstraintType = ConstraintDialog::UNKNOWN; + SqliteStatement* constrStatement = nullptr; + QPointer createTable; + QPointer columnStmt; + ConstraintDialog* constraintDialog = nullptr; + + private slots: + void createTablePk(); + void createTableFk(); + void createTableUnique(); + void createTableCheck(); + void createColumnPk(); + void createColumnFk(); + void createColumnUnique(); + void createColumnCheck(); + void createColumnNotNull(); + void createColumnDefault(); + void createColumnCollate(); +}; + +#endif // NEWCONSTRAINTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.ui new file mode 100644 index 0000000..20fc5ce --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.ui @@ -0,0 +1,75 @@ + + + NewConstraintDialog + + + + 0 + 0 + 400 + 300 + + + + + 300 + 0 + + + + New constraint + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + buttonBox + accepted() + NewConstraintDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NewConstraintDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp new file mode 100644 index 0000000..1c73de0 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp @@ -0,0 +1,68 @@ +#include "newversiondialog.h" +#include "services/pluginmanager.h" +#include "sqlitestudio.h" +#include "ui_newversiondialog.h" +#include "services/config.h" +#include + +NewVersionDialog::NewVersionDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::NewVersionDialog) +{ + init(); +} + +NewVersionDialog::~NewVersionDialog() +{ + delete ui; +} + +void NewVersionDialog::setUpdates(const QList& updates) +{ + QTableWidgetItem* item = nullptr; + QString currVersion; + int row = 0; + ui->updateList->setRowCount(updates.size()); + for (const UpdateManager::UpdateEntry& entry : updates) + { + if (entry.compontent == "SQLiteStudio") + currVersion = SQLITESTUDIO->getVersionString(); + else + currVersion = PLUGINS->getPrintableVersion(entry.compontent); + + item = new QTableWidgetItem(entry.compontent); + ui->updateList->setItem(row, 0, item); + + item = new QTableWidgetItem(currVersion); + ui->updateList->setItem(row, 1, item); + + item = new QTableWidgetItem(entry.version); + ui->updateList->setItem(row, 2, item); + + row++; + } + ui->updateList->resizeColumnsToContents(); +} + +void NewVersionDialog::init() +{ + ui->setupUi(this); + + connect(ui->abortButton, SIGNAL(clicked()), this, SLOT(reject())); + connect(ui->updateButton, SIGNAL(clicked()), this, SLOT(installUpdates())); + connect(ui->checkOnStartupCheck, &QCheckBox::clicked, [=](bool checked) + { + CFG_CORE.General.CheckUpdatesOnStartup.set(checked); + }); +} + +void NewVersionDialog::installUpdates() +{ + UPDATES->update(); + close(); +} + +void NewVersionDialog::showEvent(QShowEvent*) +{ + ui->checkOnStartupCheck->setChecked(CFG_CORE.General.CheckUpdatesOnStartup.get()); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h new file mode 100644 index 0000000..784c6cf --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h @@ -0,0 +1,34 @@ +#ifndef NEWVERSIONDIALOG_H +#define NEWVERSIONDIALOG_H + +#include "services/updatemanager.h" +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class NewVersionDialog; +} + +class GUI_API_EXPORT NewVersionDialog : public QDialog +{ + Q_OBJECT + + public: + explicit NewVersionDialog(QWidget *parent = 0); + ~NewVersionDialog(); + + void setUpdates(const QList& updates); + + protected: + void showEvent(QShowEvent*); + + private: + void init(); + + Ui::NewVersionDialog *ui = nullptr; + + private slots: + void installUpdates(); +}; + +#endif // NEWVERSIONDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui new file mode 100644 index 0000000..6f50e7f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui @@ -0,0 +1,144 @@ + + + NewVersionDialog + + + + 0 + 0 + 400 + 307 + + + + SQLiteStudio updates + + + + + + + 75 + true + + + + New updates are available! + + + Qt::AlignCenter + + + + + + + Qt::NoFocus + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + false + + + true + + + false + + + 20 + + + 20 + + + + Component + + + + + Current version + + + + + Update version + + + + + + + + + + + Check for updates on startup + + + + + + + + + + Update to new version! + + + + :/icons/img/get_update.png:/icons/img/get_update.png + + + + 24 + 24 + + + + The update will be automatically downloaded and installed. This will also restart application at the end. + + + + + + + Not now. + + + + :/icons/img/abort24.png:/icons/img/abort24.png + + + + 24 + 24 + + + + Don't install the update and close this window. + + + + + + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp new file mode 100644 index 0000000..5dc506f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.cpp @@ -0,0 +1,119 @@ +#include "populateconfigdialog.h" +#include "ui_populateconfigdialog.h" +#include "plugins/populateplugin.h" +#include "services/populatemanager.h" +#include "sqlitestudio.h" +#include "formmanager.h" +#include "configmapper.h" +#include "uiutils.h" +#include +#include +#include + +PopulateConfigDialog::PopulateConfigDialog(PopulateEngine* engine, const QString& column, const QString& pluginName, QWidget *parent) : + QDialog(parent), + ui(new Ui::PopulateConfigDialog), + engine(engine), + column(column), + pluginName(pluginName) +{ + init(); +} + +PopulateConfigDialog::~PopulateConfigDialog() +{ + safe_delete(configMapper); + delete ui; +} + +int PopulateConfigDialog::exec() +{ + QString formName = engine->getPopulateConfigFormName(); + if (formName.isNull()) + { + qCritical() << "Null form name from populating engine."; + return QDialog::Rejected; + } + + innerWidget = FORMS->createWidget(formName); + if (!innerWidget) + return QDialog::Rejected; + + configMapper->bindToConfig(innerWidget); + ui->contents->layout()->addWidget(innerWidget); + adjustSize(); + validateEngine(); + return QDialog::exec(); +} + +void PopulateConfigDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + + QString headerString = tr("Configuring %1 for column %2").arg(pluginName, column); + ui->headerLabel->setText(headerString ); + + configMapper = new ConfigMapper(engine->getConfig()); + connect(configMapper, SIGNAL(modified()), this, SLOT(validateEngine())); + + connect(POPULATE_MANAGER, SIGNAL(validationResultFromPlugin(bool,CfgEntry*,QString)), this, SLOT(validationResultFromPlugin(bool,CfgEntry*,QString))); + connect(POPULATE_MANAGER, SIGNAL(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool)), this, SLOT(stateUpdateRequestFromPlugin(CfgEntry*,bool,bool))); + connect(POPULATE_MANAGER, SIGNAL(widgetPropertyFromPlugin(CfgEntry*,QString,QVariant)), this, SLOT(widgetPropertyFromPlugin(CfgEntry*,QString,QVariant))); +} + +void PopulateConfigDialog::validateEngine() +{ + engine->validateOptions(); +} + +void PopulateConfigDialog::validationResultFromPlugin(bool valid, CfgEntry* key, const QString& msg) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (w) + setValidState(w, valid, msg); + + if (valid == pluginConfigOk.contains(key)) // if state changed + { + if (!valid) + pluginConfigOk[key] = false; + else + pluginConfigOk.remove(key); + } + updateState(); +} + +void PopulateConfigDialog::stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (!w) + return; + + w->setVisible(visible); + w->setEnabled(enabled); +} + + +void PopulateConfigDialog::widgetPropertyFromPlugin(CfgEntry* key, const QString& propName, const QVariant& value) +{ + QWidget* w = configMapper->getBindWidgetForConfig(key); + if (!w) + return; + + w->setProperty(propName.toLatin1().constData(), value); +} + +void PopulateConfigDialog::updateState() +{ + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(pluginConfigOk.size() == 0); +} + + +void PopulateConfigDialog::showEvent(QShowEvent* e) +{ + QVariant prop = innerWidget->property("initialSize"); + if (prop.isValid()) + resize(prop.toSize() + QSize(0, ui->headerLabel->height() + ui->line->height())); + + QDialog::showEvent(e); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.h new file mode 100644 index 0000000..45bc333 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.h @@ -0,0 +1,47 @@ +#ifndef POPULATECONFIGDIALOG_H +#define POPULATECONFIGDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +class PopulateEngine; +class ConfigMapper; +class CfgEntry; + +namespace Ui { + class PopulateConfigDialog; +} + +class GUI_API_EXPORT PopulateConfigDialog : public QDialog +{ + Q_OBJECT + + public: + explicit PopulateConfigDialog(PopulateEngine* engine, const QString& column, const QString& pluginName, QWidget *parent = 0); + ~PopulateConfigDialog(); + + int exec(); + + protected: + void showEvent(QShowEvent* e); + + private: + void init(); + + Ui::PopulateConfigDialog *ui = nullptr; + PopulateEngine* engine = nullptr; + ConfigMapper* configMapper = nullptr; + QHash pluginConfigOk; + QString column; + QString pluginName; + QWidget* innerWidget = nullptr; + + private slots: + void validateEngine(); + void validationResultFromPlugin(bool valid, CfgEntry* key, const QString& msg); + void stateUpdateRequestFromPlugin(CfgEntry* key, bool visible, bool enabled); + void widgetPropertyFromPlugin(CfgEntry* key, const QString& propName, const QVariant& value); + void updateState(); +}; + +#endif // POPULATECONFIGDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.ui new file mode 100644 index 0000000..ac90f02 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populateconfigdialog.ui @@ -0,0 +1,99 @@ + + + PopulateConfigDialog + + + + 0 + 0 + 400 + 300 + + + + Populating configuration + + + + + + + + + Qt::RichText + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PopulateConfigDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PopulateConfigDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp new file mode 100644 index 0000000..ca3fd31 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp @@ -0,0 +1,332 @@ +#include "populatedialog.h" +#include "ui_populatedialog.h" +#include "dblistmodel.h" +#include "dbobjlistmodel.h" +#include "services/dbmanager.h" +#include "schemaresolver.h" +#include "services/pluginmanager.h" +#include "plugins/populateplugin.h" +#include "populateconfigdialog.h" +#include "uiutils.h" +#include "services/populatemanager.h" +#include "common/widgetcover.h" +#include +#include +#include +#include +#include +#include + +PopulateDialog::PopulateDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::PopulateDialog) +{ + init(); +} + +PopulateDialog::~PopulateDialog() +{ + delete ui; +} + +void PopulateDialog::setDbAndTable(Db* db, const QString& table) +{ + ui->databaseCombo->setCurrentText(db->getName()); + ui->tableCombo->setCurrentText(table); +} + +void PopulateDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Populate", "populate dialog button")); + + plugins = PLUGINS->getLoadedPlugins(); + qSort(plugins.begin(), plugins.end(), [](PopulatePlugin* p1, PopulatePlugin* p2) -> bool + { + return p1->getTitle().compare(p2->getTitle()) < 0; + }); + + for (PopulatePlugin* plugin : plugins) + pluginTitles << plugin->getTitle(); + + widgetCover = new WidgetCover(this); + widgetCover->setVisible(false); + + ui->scrollArea->setAutoFillBackground(false); + ui->scrollArea->viewport()->setAutoFillBackground(false); + ui->columnsWidget->setAutoFillBackground(false); + + dbListModel = new DbListModel(this); + dbListModel->setCombo(ui->databaseCombo); + dbListModel->setSortMode(DbListModel::SortMode::Alphabetical); + ui->databaseCombo->setModel(dbListModel); + + tablesModel = new DbObjListModel(this); + tablesModel->setIncludeSystemObjects(false); + tablesModel->setType(DbObjListModel::ObjectType::TABLE); + ui->tableCombo->setModel(tablesModel); + refreshTables(); + + connect(ui->databaseCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(refreshTables())); + connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(refreshColumns())); + connect(POPULATE_MANAGER, SIGNAL(populatingFinished()), widgetCover, SLOT(hide())); + connect(POPULATE_MANAGER, SIGNAL(populatingSuccessful()), this, SLOT(finished())); +} + +PopulateEngine* PopulateDialog::getEngine(int selectedPluginIndex) +{ + if (selectedPluginIndex < 0 || selectedPluginIndex >= plugins.size()) + { + qCritical() << "Selected populate plugin out of range!"; + return nullptr; + } + + return plugins[selectedPluginIndex]->createEngine(); +} + +void PopulateDialog::deleteEngines(const QList& engines) +{ + for (PopulateEngine* engine : engines) + delete engine; +} + +void PopulateDialog::rebuildEngines() +{ + int row = 0; + for (const ColumnEntry& entry : columnEntries) + { + pluginSelected(entry.combo, entry.combo->currentIndex()); + updateColumnState(row++, false); + } +} + +void PopulateDialog::refreshTables() +{ + db = DBLIST->getByName(ui->databaseCombo->currentText()); + if (db) + tablesModel->setDb(db); + + updateState(); +} + +void PopulateDialog::refreshColumns() +{ + for (const ColumnEntry& entry : columnEntries) + { + delete entry.check; + delete entry.combo; + delete entry.button; + } + columnEntries.clear(); + safe_delete(buttonMapper); + safe_delete(checkMapper); + + delete ui->columnsLayout; + ui->columnsLayout = new QGridLayout(); + ui->columnsWidget->setLayout(ui->columnsLayout); + + if (!db) + { + qCritical() << "No Db while refreshing columns in PopulateDialog!"; + return; + } + + buttonMapper = new QSignalMapper(this); + connect(buttonMapper, SIGNAL(mapped(int)), this, SLOT(configurePlugin(int))); + + checkMapper = new QSignalMapper(this); + connect(checkMapper, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int))); + + SchemaResolver resolver(db); + QStringList columns = resolver.getTableColumns(ui->tableCombo->currentText()); + QCheckBox* check = nullptr; + QComboBox* combo = nullptr; + QToolButton* btn = nullptr; + int row = 0; + for (const QString& column : columns) + { + check = new QCheckBox(column); + connect(check, SIGNAL(toggled(bool)), checkMapper, SLOT(map())); + checkMapper->setMapping(check, row); + + combo = new QComboBox(); + combo->addItems(pluginTitles); + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(pluginSelected(int))); + + btn = new QToolButton(); + btn->setText(tr("Configure")); + connect(btn, SIGNAL(clicked()), buttonMapper, SLOT(map())); + buttonMapper->setMapping(btn, row); + + ui->columnsLayout->addWidget(check, row, 0); + ui->columnsLayout->addWidget(combo, row, 1); + ui->columnsLayout->addWidget(btn, row, 2); + columnEntries << ColumnEntry(check, combo, btn); + row++; + } + + rebuildEngines(); + + QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + ui->columnsLayout->addItem(spacer, row, 0, 1, 3); + + updateState(); +} + +void PopulateDialog::pluginSelected(int index) +{ + QComboBox* cb = dynamic_cast(sender()); + pluginSelected(cb, index); +} + +void PopulateDialog::pluginSelected(QComboBox* combo, int index) +{ + if (!combo) + return; + + ColumnEntry* entry = nullptr; + + int columnIndex = 0; + for (ColumnEntry& e : columnEntries) + { + if (e.combo == combo) + { + entry = &e; + break; + } + columnIndex++; + } + + if (!entry) + return; + + safe_delete(entry->engine); + + if (index < 0 || index >= plugins.size()) + return; + + entry->engine = plugins[index]->createEngine(); + updateColumnState(columnIndex); +} + +void PopulateDialog::configurePlugin(int index) +{ + if (index < 0 || index >= columnEntries.size()) + { + qCritical() << "Plugin configure index out of range:" << index << "," << columnEntries.size(); + return; + } + + PopulateEngine* engine = columnEntries[index].engine; + if (!engine->getConfig()) + { + qWarning() << "Called config on populate plugin, but it has no CfgMain."; + return; + } + + engine->getConfig()->savepoint(); + + PopulateConfigDialog dialog(engine, columnEntries[index].check->text(), columnEntries[index].combo->currentText(), this); + if (dialog.exec() != QDialog::Accepted) + engine->getConfig()->restore(); + + engine->getConfig()->release(); + + updateColumnState(index); +} + +void PopulateDialog::updateColumnState(int index, bool updateGlobalState) +{ + if (index < 0 || index >= columnEntries.size()) + { + qCritical() << "Column update called but index out of range:" << index << "," << columnEntries.size(); + return; + } + + bool checked = columnEntries[index].check->isChecked(); + bool hasConfig = columnEntries[index].engine->getConfig() != nullptr; + columnEntries[index].combo->setEnabled(checked); + columnEntries[index].button->setEnabled(checked && hasConfig); + + bool valid = true; + if (checked && hasConfig) + { + valid = columnEntries[index].engine->validateOptions(); + setValidState(columnEntries[index].button, valid, tr("Populating configuration for this column is invalid or incomplete.")); + } + + if (valid == columnsValid.contains(index)) // if state changed + { + if (!valid) + columnsValid[index] = false; + else + columnsValid.remove(index); + } + + if (updateGlobalState) + updateState(); +} + +void PopulateDialog::updateState() +{ + bool columnsOk = columnsValid.size() == 0; + bool dbOk = !ui->databaseCombo->currentText().isNull(); + bool tableOk = !ui->tableCombo->currentText().isNull(); + + bool colCountOk = false; + for (const ColumnEntry& entry : columnEntries) + { + if (entry.check->isChecked()) + { + colCountOk = true; + break; + } + } + + setValidState(ui->databaseCombo, dbOk, tr("Select database with table to populate")); + setValidState(ui->tableCombo, tableOk, tr("Select table to populate")); + setValidState(ui->columnsGroup, (!tableOk || colCountOk), tr("You have to select at least one column.")); + + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(columnsOk && tableOk && colCountOk); +} + +void PopulateDialog::finished() +{ + QDialog::accept(); +} + +void PopulateDialog::accept() +{ + if (!db) + return; + + QHash engines; + for (ColumnEntry& entry : columnEntries) + { + if (!entry.check->isChecked()) + continue; + + if (!entry.engine) + return; + + engines[entry.check->text()] = entry.engine; +// entry.engine = nullptr; // to avoid deleting it in the entry's destructor - worker will delete it after it's done + } + + QString table = ui->tableCombo->currentText(); + qint64 rows = ui->rowsSpin->value(); + + widgetCover->show(); + POPULATE_MANAGER->populate(db, table, engines, rows); +} + +PopulateDialog::ColumnEntry::ColumnEntry(QCheckBox* check, QComboBox* combo, QToolButton* button) : + check(check), combo(combo), button(button) +{ +} + +PopulateDialog::ColumnEntry::~ColumnEntry() +{ + safe_delete(engine); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h new file mode 100644 index 0000000..0ecc318 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.h @@ -0,0 +1,76 @@ +#ifndef POPULATEDIALOG_H +#define POPULATEDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class PopulateDialog; +} + +class PopulatePlugin; +class PopulateEngine; +class QGridLayout; +class DbListModel; +class DbObjListModel; +class Db; +class QComboBox; +class QCheckBox; +class QToolButton; +class QSignalMapper; +class WidgetCover; + +class GUI_API_EXPORT PopulateDialog : public QDialog +{ + Q_OBJECT + + public: + explicit PopulateDialog(QWidget *parent = 0); + ~PopulateDialog(); + void setDbAndTable(Db* db, const QString& table); + + private: + struct GUI_API_EXPORT ColumnEntry + { + ColumnEntry(QCheckBox* check, QComboBox* combo, QToolButton* button); + ~ColumnEntry(); + + QCheckBox* check = nullptr; + QComboBox* combo = nullptr; + QToolButton* button = nullptr; + PopulateEngine* engine = nullptr; + }; + + void init(); + PopulateEngine* getEngine(int selectedPluginIndex); + void deleteEngines(const QList& engines); + void rebuildEngines(); + + Ui::PopulateDialog *ui = nullptr; + QGridLayout* columnsGrid = nullptr; + DbListModel* dbListModel = nullptr; + DbObjListModel* tablesModel = nullptr; + Db* db = nullptr; + QStringList pluginTitles; + QList plugins; + QList columnEntries; + QSignalMapper* checkMapper = nullptr; + QSignalMapper* buttonMapper = nullptr; + QHash columnsValid; + WidgetCover* widgetCover = nullptr; + + private slots: + void refreshTables(); + void refreshColumns(); + void pluginSelected(int index); + void pluginSelected(QComboBox* combo, int index); + void configurePlugin(int index); + void updateColumnState(int index, bool updateGlobalState = true); + void updateState(); + void finished(); + + public: + void accept(); +}; + +#endif // POPULATEDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.ui new file mode 100644 index 0000000..811b185 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.ui @@ -0,0 +1,158 @@ + + + PopulateDialog + + + + 0 + 0 + 447 + 358 + + + + Populate table + + + + + + + 0 + 0 + + + + Database + + + + + + + + + + + + + 0 + 0 + + + + Table + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 0 + 0 + + + + Columns + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 409 + 144 + + + + + + + + + + + + + Number of rows to populate: + + + + + + 1 + + + 999999999 + + + 100 + + + + + + + + + + + + buttonBox + accepted() + PopulateDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PopulateDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.cpp new file mode 100644 index 0000000..a55464d --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.cpp @@ -0,0 +1,42 @@ +#include "quitconfirmdialog.h" +#include "ui_quitconfirmdialog.h" + +QuitConfirmDialog::QuitConfirmDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QuitConfirmDialog) +{ + init(); +} + +QuitConfirmDialog::~QuitConfirmDialog() +{ + delete ui; +} + +void QuitConfirmDialog::addMessage(const QString& msg) +{ + ui->itemList->addItem(msg); +} + +void QuitConfirmDialog::setMessages(const QStringList& messages) +{ + for (const QString& msg : messages) + addMessage(msg); +} + +int QuitConfirmDialog::getMessageCount() const +{ + return ui->itemList->count(); +} + +void QuitConfirmDialog::init() +{ + ui->setupUi(this); + + QStyle* style = QApplication::style(); + int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize); + QIcon icon = style->standardIcon(QStyle::SP_MessageBoxQuestion); + + if (!icon.isNull()) + ui->iconLabel->setPixmap(icon.pixmap(iconSize, iconSize)); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.h new file mode 100644 index 0000000..0907c9b --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.h @@ -0,0 +1,28 @@ +#ifndef QUITCONFIRMDIALOG_H +#define QUITCONFIRMDIALOG_H + +#include + +namespace Ui { + class QuitConfirmDialog; +} + +class QuitConfirmDialog : public QDialog +{ + Q_OBJECT + + public: + explicit QuitConfirmDialog(QWidget *parent = 0); + ~QuitConfirmDialog(); + + void addMessage(const QString& msg); + void setMessages(const QStringList& messages); + int getMessageCount() const; + + private: + void init(); + + Ui::QuitConfirmDialog *ui = nullptr; +}; + +#endif // QUITCONFIRMDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.ui new file mode 100644 index 0000000..6f97934 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/quitconfirmdialog.ui @@ -0,0 +1,83 @@ + + + QuitConfirmDialog + + + + 0 + 0 + 489 + 186 + + + + Uncommited changes + + + + + + Are you sure you want to quit the application? + +Following items are pending: + + + + + + + Qt::Horizontal + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + + + + + + + + + + + + + + + + + buttonBox + accepted() + QuitConfirmDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QuitConfirmDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.cpp new file mode 100644 index 0000000..87a6d88 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.cpp @@ -0,0 +1,75 @@ +#include "searchtextdialog.h" +#include "ui_searchtextdialog.h" +#include "searchtextlocator.h" +#include "common/unused.h" + +SearchTextDialog::SearchTextDialog(SearchTextLocator* textLocator, QWidget *parent) : + QDialog(parent), + ui(new Ui::SearchTextDialog), textLocator(textLocator) +{ + ui->setupUi(this); + connect(textLocator, SIGNAL(replaceAvailable(bool)), this, SLOT(setReplaceAvailable(bool))); +} + +SearchTextDialog::~SearchTextDialog() +{ + delete ui; +} + +void SearchTextDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void SearchTextDialog::showEvent(QShowEvent* e) +{ + UNUSED(e); + ui->findEdit->setFocus(); + ui->findEdit->selectAll(); + configModifiedState = true; + setReplaceAvailable(false); +} + +void SearchTextDialog::applyConfigToLocator() +{ + if (!configModifiedState) + return; + + textLocator->setCaseSensitive(ui->caseSensitiveCheck->isChecked()); + textLocator->setSearchBackwards(ui->backwardsCheck->isChecked()); + textLocator->setRegularExpression(ui->regExpCheck->isChecked()); + textLocator->setLookupString(ui->findEdit->text()); + configModifiedState = false; +} + +void SearchTextDialog::setReplaceAvailable(bool available) +{ + ui->replaceButton->setEnabled(available); +} + +void SearchTextDialog::on_findButton_clicked() +{ + applyConfigToLocator(); + textLocator->find(); +} + +void SearchTextDialog::on_replaceButton_clicked() +{ + applyConfigToLocator(); + textLocator->setReplaceString(ui->replaceEdit->text()); + textLocator->replaceAndFind(); +} + +void SearchTextDialog::on_replaceAllButton_clicked() +{ + applyConfigToLocator(); + textLocator->setReplaceString(ui->replaceEdit->text()); + textLocator->replaceAll(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.h new file mode 100644 index 0000000..54f6f72 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.h @@ -0,0 +1,39 @@ +#ifndef SEARCHTEXTDIALOG_H +#define SEARCHTEXTDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class SearchTextDialog; +} + +class SearchTextLocator; + +class GUI_API_EXPORT SearchTextDialog : public QDialog +{ + Q_OBJECT + + public: + explicit SearchTextDialog(SearchTextLocator* textLocator, QWidget *parent = 0); + ~SearchTextDialog(); + + protected: + void changeEvent(QEvent *e); + void showEvent(QShowEvent* e); + + private: + void applyConfigToLocator(); + + Ui::SearchTextDialog *ui = nullptr; + SearchTextLocator* textLocator = nullptr; + bool configModifiedState = false; + + private slots: + void setReplaceAvailable(bool available); + void on_findButton_clicked(); + void on_replaceButton_clicked(); + void on_replaceAllButton_clicked(); +}; + +#endif // SEARCHTEXTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.ui new file mode 100644 index 0000000..ce9e12e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/searchtextdialog.ui @@ -0,0 +1,153 @@ + + + SearchTextDialog + + + + 0 + 0 + 403 + 184 + + + + Dialog + + + + + + + + + Find: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Case sensitive + + + + + + + Search backwards + + + + + + + Regular expression matching + + + + + + + + + + Replace && +find next + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + Replace with: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Replace all + + + + + + + Find + + + true + + + + + + + findEdit + replaceEdit + caseSensitiveCheck + regExpCheck + backwardsCheck + findButton + replaceButton + replaceAllButton + buttonBox + + + + + buttonBox + accepted() + SearchTextDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SearchTextDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.cpp new file mode 100644 index 0000000..b1451eb --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.cpp @@ -0,0 +1,248 @@ +#include "sortdialog.h" +#include "ui_sortdialog.h" +#include "iconmanager.h" +#include "common/unused.h" +#include +#include +#include + +SortDialog::SortDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::SortDialog) +{ + ui->setupUi(this); + + initActions(); + ui->list->header()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(ui->list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateButtons())); + connect(ui->list, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(itemChanged(QTreeWidgetItem*,int))); + connect(ui->buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(reset())); + connect(ui->list->model(), &QAbstractItemModel::rowsInserted, [=](const QModelIndex & parent, int start, int end) + { + UNUSED(parent); + UNUSED(end); + rebuildComboForItem(ui->list->topLevelItem(start)); + }); +} + +SortDialog::~SortDialog() +{ + delete ui; +} + +void SortDialog::setColumns(const QStringList& columns) +{ + originalColumns = columns; + ui->list->clear(); + + QTreeWidgetItem* item = nullptr; + for (int row = 0, total = columns.size(); row < total; ++row) + { + item = new QTreeWidgetItem({columns[row], "ASC"}); + item->setData(2, Qt::UserRole, row); + fixItemFlags(item); + ui->list->insertTopLevelItem(row, item); + item->setCheckState(0, Qt::Unchecked); + } + ui->list->setHeaderLabels({tr("Column"), tr("Order")}); + updateButtons(); +} + +QueryExecutor::SortList SortDialog::getSortOrder() const +{ + QueryExecutor::SortList sortOrder; + + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + for (int row = 0, total = ui->list->topLevelItemCount(); row < total; ++row) + { + item = ui->list->topLevelItem(row); + if (item->checkState(0) != Qt::Checked) + continue; + + combo = dynamic_cast(ui->list->itemWidget(item, 1)); + sortOrder << QueryExecutor::Sort((combo->currentText() == "ASC" ? Qt::AscendingOrder : Qt::DescendingOrder), item->data(2, Qt::UserRole).toInt()); + } + return sortOrder; +} + +void SortDialog::setSortOrder(const QueryExecutor::SortList& sortOrder) +{ + // Translate sort order into more usable (in here) form + QHash checkedColumns; + QList checkedColumnsOrder; + for (const QueryExecutor::Sort& sort : sortOrder) + { + checkedColumns[sort.column] = sort.order; + checkedColumnsOrder << sort.column; + } + + // Select proper columns and set order + bool checked; + QTreeWidgetItem* item = nullptr; + QComboBox* combo = nullptr; + for (int row = 0, total = ui->list->topLevelItemCount(); row < total; ++row) + { + item = ui->list->topLevelItem(row); + checked = checkedColumns.contains(item->data(2, Qt::UserRole).toInt()); + item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); + + combo = dynamic_cast(ui->list->itemWidget(item, 1)); + combo->setCurrentText(checkedColumns[row] == QueryExecutor::Sort::DESC ? "DESC" : "ASC"); + } + + // Get selected items as an ordered list of items (in order as defined in the sort order), so we can easly relocate them + QList orderedItems; + for (int row : checkedColumnsOrder) + orderedItems << ui->list->topLevelItem(row); + + // Move selected items in front, in the same order as they were mentioned in the sort order + int newRow = 0; + for (QTreeWidgetItem* itemToMove : orderedItems) + { + ui->list->takeTopLevelItem(ui->list->indexOfTopLevelItem(itemToMove)); + ui->list->insertTopLevelItem(newRow++, itemToMove); + } + + updateState(); +} + +QToolBar* SortDialog::getToolBar(int toolbar) const +{ + UNUSED(toolbar); + return nullptr; +} + +void SortDialog::updateState(QTreeWidgetItem* item) +{ + QComboBox* combo = dynamic_cast(ui->list->itemWidget(item, 1)); + if (!combo) + return; + + combo->setEnabled(item->checkState(0) == Qt::Checked); +} + +void SortDialog::updateState() +{ + for (int row = 0, total = ui->list->topLevelItemCount(); row < total; ++row) + updateState(ui->list->topLevelItem(row)); +} + +void SortDialog::fixItemFlags(QTreeWidgetItem* item) +{ + Qt::ItemFlags flags = item->flags(); + flags |= Qt::ItemNeverHasChildren; + flags |= Qt::ItemIsUserCheckable; + flags ^= Qt::ItemIsDropEnabled; + flags ^= Qt::ItemIsEditable; + item->setFlags(flags); +} + +void SortDialog::rebuildComboForItem(QTreeWidgetItem* item) +{ + QComboBox* combo = new QComboBox(); + combo->addItems({"ASC", "DESC"}); + combo->setCurrentText(item->text(1)); + combo->setEnabled(item->checkState(0) == Qt::Checked); + ui->list->setItemWidget(item, 1, combo); + item->setSizeHint(1, combo->sizeHint()); // bug in Qt? without this comboboxes were misaligned vertically + + connect(combo, &QComboBox::currentTextChanged, [item](const QString& newText) + { + item->setText(1, newText); + }); + + updateSortLabel(); +} + +void SortDialog::updateSortLabel() +{ + QStringList entries; + QTreeWidgetItem* item = nullptr; + for (int row = 0, total = ui->list->topLevelItemCount(); row < total; ++row) + { + item = ui->list->topLevelItem(row); + if (item->checkState(0) != Qt::Checked) + continue; + + entries << item->text(0) + " " + item->text(1); + } + + if (entries.size() == 0) + { + ui->sortByLabel->setVisible(false); + } + else + { + static QString label = tr("Sort by: %1"); + ui->sortByLabel->setText(label.arg(entries.join(", "))); + ui->sortByLabel->setVisible(true); + } +} + +void SortDialog::itemChanged(QTreeWidgetItem* item, int column) +{ + if (column == 0) + updateState(item); + + updateSortLabel(); +} + +void SortDialog::reset() +{ + setColumns(originalColumns); +} + +void SortDialog::updateButtons() +{ + QTreeWidgetItem* item = ui->list->currentItem(); + actionMap[MOVE_UP]->setEnabled(item && ui->list->itemAbove(item) != nullptr); + actionMap[MOVE_DOWN]->setEnabled(item && ui->list->itemBelow(item) != nullptr); +} + +void SortDialog::moveCurrentUp() +{ + QTreeWidgetItem* item = ui->list->currentItem(); + if (!item) + return; + + int row = ui->list->indexOfTopLevelItem(item); + if (row < 1) + return; + + ui->list->takeTopLevelItem(row); + ui->list->insertTopLevelItem(row - 1, item); + + QModelIndex idx = ui->list->model()->index(row - 1, 0); + ui->list->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Rows|QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current); + updateButtons(); +} + +void SortDialog::moveCurrentDown() +{ + QTreeWidgetItem* item = ui->list->currentItem(); + if (!item) + return; + + int row = ui->list->indexOfTopLevelItem(item); + if (row + 1 >= ui->list->topLevelItemCount()) + return; + + ui->list->takeTopLevelItem(row); + ui->list->insertTopLevelItem(row + 1, item); + + QModelIndex idx = ui->list->model()->index(row + 1, 0); + ui->list->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::Rows|QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current); + updateButtons(); +} + +void SortDialog::createActions() +{ + createAction(MOVE_UP, ICONS.MOVE_UP, tr("Move column up"), this, SLOT(moveCurrentUp()), ui->toolbar, this); + createAction(MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move column down"), this, SLOT(moveCurrentDown()), ui->toolbar, this); +} + +void SortDialog::setupDefShortcuts() +{ +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.h new file mode 100644 index 0000000..a103d90 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.h @@ -0,0 +1,60 @@ +#ifndef SORTDIALOG_H +#define SORTDIALOG_H + +#include "db/queryexecutor.h" +#include "common/extactioncontainer.h" +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class SortDialog; +} + +class QTreeWidgetItem; + +class GUI_API_EXPORT SortDialog : public QDialog, public ExtActionContainer +{ + Q_OBJECT + + public: + enum Action + { + MOVE_UP, + MOVE_DOWN + }; + + enum ToolBar + { + }; + + explicit SortDialog(QWidget *parent = 0); + ~SortDialog(); + + void setColumns(const QStringList& columns); + QueryExecutor::SortList getSortOrder() const; + void setSortOrder(const QueryExecutor::SortList& sortOrder); + QToolBar* getToolBar(int toolbar) const; + + protected: + void createActions(); + void setupDefShortcuts(); + + private: + void updateState(QTreeWidgetItem* item); + void updateState(); + void fixItemFlags(QTreeWidgetItem* item); + void rebuildComboForItem(QTreeWidgetItem* item); + void updateSortLabel(); + + Ui::SortDialog *ui = nullptr; + QStringList originalColumns; + + private slots: + void itemChanged(QTreeWidgetItem* item, int column); + void reset(); + void updateButtons(); + void moveCurrentUp(); + void moveCurrentDown(); +}; + +#endif // SORTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.ui new file mode 100644 index 0000000..23bee81 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/sortdialog.ui @@ -0,0 +1,112 @@ + + + SortDialog + + + + 0 + 0 + 457 + 357 + + + + Sort by columns + + + + + + + + + + 2 + 0 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + false + + + + Column + + + + + Order + + + + + + + + + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + + + + + + + buttonBox + accepted() + SortDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SortDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp new file mode 100644 index 0000000..462e57f --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.cpp @@ -0,0 +1,52 @@ +#include "triggercolumnsdialog.h" +#include "ui_triggercolumnsdialog.h" + +#include + +TriggerColumnsDialog::TriggerColumnsDialog(QWidget *parent) : + QDialog(parent, Qt::Popup), + ui(new Ui::TriggerColumnsDialog) +{ + ui->setupUi(this); +} + +TriggerColumnsDialog::~TriggerColumnsDialog() +{ + delete ui; +} + +void TriggerColumnsDialog::addColumn(const QString& name, bool checked) +{ + QCheckBox* cb = new QCheckBox(name); + cb->setChecked(checked); + ui->mainWidget->layout()->addWidget(cb); + checkBoxList << cb; +} + +QStringList TriggerColumnsDialog::getCheckedColumns() const +{ + QStringList columns; + foreach (QCheckBox* cb, checkBoxList) + { + if (cb->isChecked()) + columns << cb->text(); + } + return columns; +} + +void TriggerColumnsDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void TriggerColumnsDialog::showEvent(QShowEvent*) +{ + adjustSize(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.h new file mode 100644 index 0000000..1ba0d69 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.h @@ -0,0 +1,33 @@ +#ifndef TRIGGERCOLUMNSDIALOG_H +#define TRIGGERCOLUMNSDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class TriggerColumnsDialog; +} + +class QCheckBox; + +class GUI_API_EXPORT TriggerColumnsDialog : public QDialog +{ + Q_OBJECT + + public: + explicit TriggerColumnsDialog(QWidget *parent = 0); + ~TriggerColumnsDialog(); + + void addColumn(const QString& name, bool checked); + QStringList getCheckedColumns() const; + + protected: + void changeEvent(QEvent *e); + void showEvent(QShowEvent*); + + private: + QList checkBoxList; + Ui::TriggerColumnsDialog *ui = nullptr; +}; + +#endif // TRIGGERCOLUMNSDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.ui new file mode 100644 index 0000000..4326fca --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggercolumnsdialog.ui @@ -0,0 +1,117 @@ + + + TriggerColumnsDialog + + + + 0 + 0 + 334 + 300 + + + + Dialog + + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 75 + true + + + + Triggering columns: + + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 320 + 239 + + + + + 0 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + buttonBox + accepted() + TriggerColumnsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TriggerColumnsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp new file mode 100644 index 0000000..0707bd3 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp @@ -0,0 +1,413 @@ +#include "triggerdialog.h" +#include "ui_triggerdialog.h" +#include "parser/ast/sqliteselect.h" +#include "services/notifymanager.h" +#include "parser/ast/sqliteexpr.h" +#include "triggercolumnsdialog.h" +#include "common/utils_sql.h" +#include "schemaresolver.h" +#include "parser/parser.h" +#include "iconmanager.h" +#include "db/chainexecutor.h" +#include "dbtree/dbtree.h" +#include "ddlpreviewdialog.h" +#include "uiconfig.h" +#include "services/config.h" +#include "uiutils.h" +#include "services/codeformatter.h" +#include +#include +#include + +TriggerDialog::TriggerDialog(Db* db, QWidget *parent) : + QDialog(parent), + db(db), + ui(new Ui::TriggerDialog) +{ + init(); +} + +TriggerDialog::~TriggerDialog() +{ + delete ui; +} + +void TriggerDialog::setParentTable(const QString& name) +{ + forTable = true; + table = name; + initTrigger(); +} + +void TriggerDialog::setParentView(const QString& name) +{ + forTable = false; + view = name; + initTrigger(); +} + +void TriggerDialog::setTrigger(const QString& name) +{ + trigger = name; + originalTriggerName = name; + existingTrigger = true; + initTrigger(); +} + +void TriggerDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void TriggerDialog::init() +{ + ui->setupUi(this); + limitDialogWidth(this); + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateDdlTab(int))); + connect(ui->actionColumns, SIGNAL(clicked()), this, SLOT(showColumnsDialog())); + + // On object combo + ui->onCombo->setEnabled(false); + connect(ui->onCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(tableChanged(QString))); + + // Action combo + ui->actionCombo->addItems({ + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::DELETE), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::INSERT), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::UPDATE), + SqliteCreateTrigger::Event::typeToString(SqliteCreateTrigger::Event::UPDATE_OF) + }); + connect(ui->actionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState())); + + // Scope combo + ui->scopeCombo->addItems({ + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::null), + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_ROW) + }); + if (db->getDialect() == Dialect::Sqlite2) + { + ui->scopeCombo->addItems({ + SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT) + }); + } + + // Precondition + connect(ui->preconditionCheck, SIGNAL(clicked()), this, SLOT(updateState())); + connect(ui->preconditionEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->preconditionEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->preconditionEdit->setDb(db); + + // Code + connect(ui->codeEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->codeEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->codeEdit->setDb(db); +} + +void TriggerDialog::initTrigger() +{ + // Name edit + ui->nameEdit->setText(trigger); + + if (trigger.isNull()) + { + createTrigger = SqliteCreateTriggerPtr::create(); + createTrigger->event = new SqliteCreateTrigger::Event(); + } + else + { + parseDdl(); + readTrigger(); + } + + // Event combo + if (forTable) + { + ui->whenCombo->addItems({ + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::null), + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::BEFORE), + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::AFTER) + }); + } + else + { + ui->whenCombo->addItems({ + SqliteCreateTrigger::time(SqliteCreateTrigger::Time::INSTEAD_OF) + }); + ui->whenCombo->setEnabled(false); + ui->onLabel->setText(tr("On view:")); + } + + if (!view.isNull() || !table.isNull()) + { + readColumns(); + QString target = getTargetObjectName(); + ui->onCombo->addItem(target); + ui->onCombo->setCurrentText(target); + } + + // Precondition and code edits + setupVirtualSqls(); + + updateState(); +} + +void TriggerDialog::parseDdl() +{ + SchemaResolver resolver(db); + SqliteQueryPtr parsedObject = resolver.getParsedObject(trigger, SchemaResolver::TRIGGER); + if (!parsedObject.dynamicCast()) + { + notifyError(tr("Could not process trigger %1 correctly. Unable to open a trigger dialog.").arg(trigger)); + reject(); + return; + } + + createTrigger = parsedObject.dynamicCast(); + ddl = createTrigger->detokenize(); +} + +void TriggerDialog::readTrigger() +{ + if (!createTrigger) + return; + + forTable = createTrigger->eventTime != SqliteCreateTrigger::Time::INSTEAD_OF; + if (forTable) + table = createTrigger->table; + else + view = createTrigger->table; + + ui->onCombo->addItem(createTrigger->table); + ui->onCombo->setCurrentText(createTrigger->table); + ui->whenCombo->setCurrentText(SqliteCreateTrigger::time(createTrigger->eventTime)); + ui->actionCombo->setCurrentText(SqliteCreateTrigger::Event::typeToString(createTrigger->event->type)); + ui->scopeCombo->setCurrentText(SqliteCreateTrigger::scopeToString(createTrigger->scope)); + if (createTrigger->precondition) + { + ui->preconditionCheck->setChecked(true); + ui->preconditionEdit->setPlainText(createTrigger->precondition->detokenize()); + } + + if (createTrigger->queries.size() > 0) + { + QStringList sqls; + foreach (SqliteQuery* query, createTrigger->queries) + sqls << query->detokenize(); + + ui->codeEdit->setPlainText(sqls.join(";\n")+";"); + } +} + +void TriggerDialog::setupVirtualSqls() +{ + Dialect dialect = db->getDialect(); + static QString preconditionVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 WHEN %3 BEGIN SELECT 1; END;"); + static QString codeVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 BEGIN %3 END;"); + ui->codeEdit->setVirtualSqlCompleteSemicolon(true); + if (!trigger.isNull()) + { + if (createTrigger) // if false, then there was a parsing error in parseDdl(). + { + ui->preconditionEdit->setVirtualSqlExpression( + preconditionVirtSql.arg(wrapObjIfNeeded(trigger, dialect), + wrapObjIfNeeded(createTrigger->table, dialect), + "%1")); + + ui->codeEdit->setVirtualSqlExpression( + codeVirtSql.arg( + wrapObjIfNeeded(trigger, dialect), + wrapObjIfNeeded(createTrigger->table, dialect), + "%1")); + } + } + else if (!table.isNull() || !view.isNull()) + { + ui->preconditionEdit->setVirtualSqlExpression( + preconditionVirtSql.arg("trig", + wrapObjIfNeeded(getTargetObjectName(), dialect), + "%1")); + + ui->codeEdit->setVirtualSqlExpression( + codeVirtSql.arg("trig", + wrapObjIfNeeded(getTargetObjectName(), dialect), + "%1")); + } + else + { + qCritical() << "TriggerDialog is in invalid state. Called initTrigger() but none of trigger/table/view values are set."; + } +} + +void TriggerDialog::readColumns() +{ + SchemaResolver resolver(db); + if (!table.isNull()) + targetColumns = resolver.getTableColumns(table); + else if (!view.isNull()) + targetColumns = resolver.getViewColumns(view); + else + targetColumns.clear(); + + if (createTrigger) + selectedColumns = createTrigger->event->columnNames; +} + +QString TriggerDialog::getTargetObjectName() const +{ + if (!table.isNull()) + return table; + + return view; +} + +void TriggerDialog::rebuildTrigger() +{ + /* + * Trigger is not rebuilt into SqliteCreateTrigger, because it's impossible to parse + * precondition or queries if they are invalid and we still need an invalid queries + * to be presented on the DDL tab. + */ + static const QString tempDdl = QStringLiteral("CREATE TRIGGER %1%2 %3%4 ON %5%6%7 BEGIN %8 END;"); + + Dialect dialect = db->getDialect(); + QString trigName = wrapObjIfNeeded(ui->nameEdit->text(), dialect); + QString when = ui->whenCombo->currentText(); + QString action = ui->actionCombo->currentText(); + QString columns = ""; + QString target = wrapObjIfNeeded(getTargetObjectName(), dialect); + QString scope = ui->scopeCombo->currentText(); + QString precondition = ""; + QString queries = ui->codeEdit->toPlainText(); + + // Columns + SqliteCreateTrigger::Event::Type actionType = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + if (actionType == SqliteCreateTrigger::Event::UPDATE_OF) + { + QStringList colNames; + foreach (const QString& colName, selectedColumns) + colNames << wrapObjIfNeeded(colName, dialect); + + columns = " "+colNames.join(", "); + } + + // Precondition + if (ui->preconditionCheck->isChecked()) + precondition = " WHEN "+ui->preconditionEdit->toPlainText(); + + // Queries + if (!queries.trimmed().endsWith(";")) + queries += ";"; + + // When + if (!when.isNull()) + when.prepend(" "); + + // Scope + if (!scope.isNull()) + scope.prepend(" "); + + ddl = tempDdl.arg(trigName).arg(when).arg(action).arg(columns).arg(target).arg(scope).arg(precondition).arg(queries); +} + +void TriggerDialog::updateState() +{ + SqliteCreateTrigger::Event::Type type = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + ui->actionColumns->setEnabled(type == SqliteCreateTrigger::Event::UPDATE_OF); + ui->preconditionEdit->setEnabled(ui->preconditionCheck->isChecked()); + updateValidation(); +} + +void TriggerDialog::updateValidation() +{ + SqliteCreateTrigger::Event::Type type = SqliteCreateTrigger::Event::stringToType(ui->actionCombo->currentText()); + bool columnsOk = (type != SqliteCreateTrigger::Event::UPDATE_OF || selectedColumns.size() > 0); + + bool preconditionOk = (!ui->preconditionCheck->isChecked() || + (ui->preconditionEdit->isSyntaxChecked() && !ui->preconditionEdit->haveErrors())); + + bool codeOk = (ui->codeEdit->isSyntaxChecked() && !ui->codeEdit->haveErrors()); + + setValidState(ui->preconditionCheck, preconditionOk, tr("Enter a valid condition.")); + setValidState(ui->codeEdit, codeOk, tr("Enter a valid trigger code.")); + ui->actionColumns->setIcon(columnsOk ? ICONS.TRIGGER_COLUMNS : ICONS.TRIGGER_COLUMNS_INVALID); + + QPushButton* okButton = ui->buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(columnsOk && preconditionOk && codeOk); +} + +void TriggerDialog::showColumnsDialog() +{ + TriggerColumnsDialog dialog(this); + foreach (const QString& colName, targetColumns) + dialog.addColumn(colName, selectedColumns.contains(colName, Qt::CaseInsensitive)); + + if (dialog.exec() != QDialog::Accepted) + return; + + QStringList newColumns = dialog.getCheckedColumns(); + selectedColumns = newColumns; + updateValidation(); +} + +void TriggerDialog::updateDdlTab(int tabIdx) +{ + if (tabIdx != 1) + return; + + rebuildTrigger(); + QString formatted = FORMATTER->format("sql", ddl, db); + ui->ddlEdit->setPlainText(formatted); +} + +void TriggerDialog::tableChanged(const QString& newValue) +{ + ui->preconditionEdit->setTriggerContext(newValue); + ui->codeEdit->setTriggerContext(newValue); +} + +void TriggerDialog::accept() +{ + rebuildTrigger(); + + Dialect dialect = db->getDialect(); + + QStringList sqls; + if (existingTrigger) + sqls << QString("DROP TRIGGER %1").arg(wrapObjIfNeeded(originalTriggerName, dialect)); + + sqls << ddl; + + if (!CFG_UI.General.DontShowDdlPreview.get()) + { + DdlPreviewDialog dialog(db, this); + dialog.setDdl(sqls); + if (dialog.exec() != QDialog::Accepted) + return; + } + + ChainExecutor executor; + executor.setDb(db); + executor.setAsync(false); + executor.setQueries(sqls); + executor.exec(); + + if (executor.getSuccessfulExecution()) + { + CFG->addDdlHistory(sqls.join("\n"), db->getName(), db->getPath()); + + QDialog::accept(); + DBTREE->refreshSchema(db); + return; + } + + QMessageBox::critical(this, tr("Error", "trigger dialog"), tr("An error occurred while executing SQL statements:\n%1") + .arg(executor.getErrorsMessages().join(",\n")), QMessageBox::Ok); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h new file mode 100644 index 0000000..d8e7ed4 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.h @@ -0,0 +1,62 @@ +#ifndef TRIGGERDIALOG_H +#define TRIGGERDIALOG_H + +#include "db/db.h" +#include "parser/ast/sqlitecreatetrigger.h" +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class TriggerDialog; +} + +class GUI_API_EXPORT TriggerDialog : public QDialog +{ + Q_OBJECT + + public: + explicit TriggerDialog(Db* db, QWidget *parent = 0); + ~TriggerDialog(); + + void setParentTable(const QString& name); + void setParentView(const QString& name); + void setTrigger(const QString& name); + + protected: + void changeEvent(QEvent *e); + + private: + void init(); + void initTrigger(); + void parseDdl(); + void readTrigger(); + void setupVirtualSqls(); + void readColumns(); + QString getTargetObjectName() const; + void rebuildTrigger(); + + QString originalTriggerName; + QString trigger; + QString table; + QString view; + Db* db = nullptr; + bool forTable = true; + bool existingTrigger = false; + QStringList targetColumns; + QStringList selectedColumns; + QString ddl; + SqliteCreateTriggerPtr createTrigger; + Ui::TriggerDialog *ui = nullptr; + + private slots: + void updateState(); + void updateValidation(); + void showColumnsDialog(); + void updateDdlTab(int tabIdx); + void tableChanged(const QString& newValue); + + public slots: + void accept(); +}; + +#endif // TRIGGERDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.ui new file mode 100644 index 0000000..bf3da0a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.ui @@ -0,0 +1,215 @@ + + + TriggerDialog + + + + 0 + 0 + 490 + 480 + + + + Trigger dialog + + + + + + 0 + + + + Trigger + + + + + + On table: + + + + + + + + + + Action: + + + + + + + + + + + + + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + Pre-condition: + + + + + + + The scope is still not fully supported by the SQLite database. + + + + + + + Trigger name: + + + + + + + When: + + + + + + + List of columns for UPDATE OF action. + + + ... + + + + + + + Scope: + + + + + + + Code: + + + + + + + Trigger statements to be executed. + + + + + + + + 16777215 + 80 + + + + <p>SQL condition that will be evaluated before the actual trigger code. In case the condition returns false, the trigger will not be fired for that row.</p> + + + + + + + + DDL + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + SqlView + QPlainTextEdit +
    sqlview.h
    +
    + + SqlEditor + QPlainTextEdit +
    sqleditor.h
    +
    +
    + + tabWidget + nameEdit + whenCombo + actionCombo + actionColumns + onCombo + scopeCombo + preconditionCheck + preconditionEdit + codeEdit + buttonBox + ddlEdit + + + + + buttonBox + accepted() + TriggerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TriggerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.cpp new file mode 100644 index 0000000..5521245 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.cpp @@ -0,0 +1,31 @@ +#include "versionconvertsummarydialog.h" +#include "ui_versionconvertsummarydialog.h" + +VersionConvertSummaryDialog::VersionConvertSummaryDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::VersionConvertSummaryDialog) +{ + ui->setupUi(this); + + ui->diffTable->setLeftLabel(tr("Before")); + ui->diffTable->setRightLabel(tr("After")); + ui->diffTable->horizontalHeader()->setVisible(true); +} + +VersionConvertSummaryDialog::~VersionConvertSummaryDialog() +{ + delete ui; +} + +void VersionConvertSummaryDialog::setSides(const QList >& data) +{ + ui->diffTable->setSides(data); +} + + +void VersionConvertSummaryDialog::showEvent(QShowEvent* e) +{ + QDialog::showEvent(e); + ui->diffTable->updateSizes(); + +} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.h new file mode 100644 index 0000000..fc63076 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.h @@ -0,0 +1,28 @@ +#ifndef VERSIONCONVERTSUMMARYDIALOG_H +#define VERSIONCONVERTSUMMARYDIALOG_H + +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class VersionConvertSummaryDialog; +} + +class GUI_API_EXPORT VersionConvertSummaryDialog : public QDialog +{ + Q_OBJECT + + public: + explicit VersionConvertSummaryDialog(QWidget *parent = 0); + ~VersionConvertSummaryDialog(); + + void setSides(const QList>& data); + + protected: + void showEvent(QShowEvent* e); + + private: + Ui::VersionConvertSummaryDialog *ui = nullptr; +}; + +#endif // VERSIONCONVERTSUMMARYDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.ui new file mode 100644 index 0000000..a67db1e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/versionconvertsummarydialog.ui @@ -0,0 +1,91 @@ + + + VersionConvertSummaryDialog + + + + 0 + 0 + 600 + 497 + + + + Database version convert + + + + + + Following changes to the SQL statements will be made: + + + + + + + QAbstractItemView::NoSelection + + + false + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + SqlCompareView + QTableWidget +
    sqlcompareview.h
    +
    +
    + + + + buttonBox + accepted() + VersionConvertSummaryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + VersionConvertSummaryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    -- cgit v1.2.3