diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,403 +1,404 @@ #include "exitcode.h" #include "mainwindow.h" #include "psettings.h" #include "updater.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) , _exitCode(0) , _errorCount(0) , _updater(new DatabaseUpdater) { setWindowTitle(QStringLiteral("Создание/обновление базы данных")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setSizeGripEnabled(true); QHBoxLayout* mainLayout = new QHBoxLayout; QVBoxLayout* leftLayout = new QVBoxLayout; QVBoxLayout* rightLayout = new QVBoxLayout; QHBoxLayout* bottomRightLayout = new QHBoxLayout; leftLayout->setContentsMargins(0, 2 * style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), 0); QLabel* picture = new QLabel; picture->setPixmap(QPixmap(":/picture.png")); //кнопки, скрытые до завершения процесса _btnSave = new QPushButton(QStringLiteral("Сохранить")); _btnClose = new QPushButton(QStringLiteral("Закрыть")); _btnSave->setVisible(false); _btnClose->setVisible(false); _btnClose->setDefault(true); leftLayout->addWidget(picture); leftLayout->addStretch(); leftLayout->addWidget(_btnSave); leftLayout->addWidget(_btnClose); QLabel* label = new QLabel(QStringLiteral("Протокол работы")); rightLayout->addWidget(label); _protocol = new QTextEdit(); _protocol->setReadOnly(true); label->setBuddy(_protocol); rightLayout->addWidget(_protocol); //индикаторы выполнения _progress = new QProgressBar; _progress->setTextVisible(false); _ai = new KActivityIndicator; _ai->setVisible(false); bottomRightLayout->addWidget(_progress); bottomRightLayout->addWidget(_ai); rightLayout->addLayout(bottomRightLayout); mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout); //нажатие Esc не соединяется со слотом, чтобы окно не закрывалось new QShortcut(QKeySequence(Qt::Key_Escape), this); //соединить сигналы со слотами connect(_btnSave, SIGNAL(clicked()), this, SLOT(saveProtocol())); connect(_btnClose, SIGNAL(clicked()), this, SLOT(closeDialog())); QTimer::singleShot(0, this, SLOT(startExecution())); } MainWindow::~MainWindow() { delete _updater; } //--------------------------------------------------------------------------- void MainWindow::closeEvent(QCloseEvent* a_event) { if (_futureWatcher.isRunning()) a_event->ignore(); else a_event->accept(); } //--------------------------------------------------------------------------- void MainWindow::startExecution() { if (_futureWatcher.isRunning()) return; ProgramSettings pset = processArguments(); if (!logon(pset)) { _btnClose->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано, база данных не изменилась.")); _exitCode = -1; return; } _logFile = pset.logFile; connect(_updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int))); connect(_updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&))); connect(_updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&))); connect(_updater, SIGNAL(sqlError(const QString&, const QString&, const QString&)), this, SLOT(sqlError(const QString&, const QString&, const QString&))); connect(_updater, SIGNAL(logConnectionParameters(const QString&, const QString&, const QString&)), this, SLOT(logConnectionParameters(const QString&, const QString&, const QString&))); connect(&_futureWatcher, SIGNAL(finished()), this, SLOT(finishExecution())); _ai->setVisible(true); _ai->start(); //int rc = _updater.run( pset ); QFuture rc = QtConcurrent::run(_updater, &DatabaseUpdater::run, pset); _futureWatcher.setFuture(rc); } //--------------------------------------------------------------------------- void MainWindow::finishExecution() { _ai->stop(); _ai->setVisible(false); //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 if (_errorCount == 0) { ExitCode rc(_updater->revisionBefore(), _updater->revisionAfter()); message(rc.message()); _exitCode = rc.exitCode(); closeDialog(); //при успешном завершении, то окно закрывается автоматически } else { _btnClose->show(); _btnSave->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано в результате ошибки.\nБаза данных не изменилась.")); _exitCode = -1; } if (!_logFile.isEmpty()) saveProtocol(_logFile); } //--------------------------------------------------------------------------- int MainWindow::executeAction() { ProgramSettings pset = processArguments(); logon(pset); _ai->start(); DatabaseUpdater updater; connect(&updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int))); connect(&updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&))); connect(&updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&))); connect(&updater, SIGNAL(sqlError(const QString&, const QString&, const QString&)), this, SLOT(sqlError(const QString&, const QString&, const QString&))); connect(&updater, SIGNAL(logConnectionParameters(const QString&, const QString&, const QString&)), this, SLOT(logConnectionParameters(const QString&, const QString&, const QString&))); int rc = updater.run(pset); //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 if (_errorCount == 0) { int before = updater.revisionBefore(); int after = updater.revisionAfter(); if (before == after) message(QStringLiteral("База данных в актуальном состоянии.")); else if (before == 0) message(QStringLiteral("База данных создана и обновлена до версии %1.").arg(after)); else message(QStringLiteral("База данных версии %1 обновлена до версии %2.").arg(before).arg(after)); rc = (before & 0xFFFF) | ((after & 0xFFFF) << 16); } else { _btnClose->show(); _btnSave->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано в результате ошибки." "
База данных не изменилась.")); rc = -1; } return rc; } //--------------------------------------------------------------------------- bool MainWindow::logon(ProgramSettings& a_pset) { - if (!a_pset.host.isEmpty() || !a_pset.username.isEmpty()) + if (!a_pset.host.isEmpty() && !a_pset.username.isEmpty()) return true; - KLogon* logon = KLogon::create(); + std::unique_ptr logon(KLogon::create()); if (!logon) return false; KConfig config; QHash aliases = loadAliases(config.programDataPath()); QString username = QString::fromLocal8Bit(qgetenv("USERNAME")); if (aliases.contains(username)) username = aliases[username]; logon->setParent(winId()); logon->setUsername(username); emit message(QStringLiteral("Авторизация подключения к серверу баз данных **%1**") .arg(config.databaseHost())); if (!logon->execute()) { emit message(QStringLiteral("Отказ от ввода имени и пароля пользователя")); return false; } else { - a_pset.host = config.databaseHost(); - a_pset.username = logon->username(); + if (a_pset.host.isEmpty()) + a_pset.host = config.databaseHost(); + if(a_pset.username.isEmpty()) + a_pset.username = logon->username(); a_pset.password = logon->password(); } - delete logon; return true; } //--------------------------------------------------------------------------- QHash MainWindow::loadAliases(const QString& a_path) { QHash result; QString filename = a_path + QChar('/') + QStringLiteral("aliases.conf"); QFile inf(filename); if (inf.open(QIODevice::ReadOnly)) { QTextStream is(&inf); is.setCodec(QTextCodec::codecForName("UTF-8")); while ( !is.atEnd() ) { QStringList chunks = is.readLine().split(QLatin1Char(':')); if (chunks.size() == 2) result[chunks.at(0).trimmed()] = chunks.at(1).trimmed(); } } return result; } //--------------------------------------------------------------------------- void MainWindow::logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username) { message(QStringLiteral("Подключение к базе данных\n" "\tСервер: **%1**\n\tБаза данных: **%2**\n" "\tПользователь: **%3**") .arg(a_host, a_database, a_username)); } //--------------------------------------------------------------------------- void MainWindow::sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command) { QString errorHtml; if (!a_commandDescription.isEmpty()) errorHtml.append(QStringLiteral("Операция:
")).append(a_commandDescription).append("
"); QString dbError = a_dbError; errorHtml.append(QStringLiteral("Сообщение об ошибке:
")).append(dbError.trimmed().replace('\n', "
")).append("
"); if (!a_command.isEmpty()) errorHtml.append(QStringLiteral("Текст команды:
")).append(a_command).append("
"); errorHtmlToLog(errorHtml); } //--------------------------------------------------------------------------- void MainWindow::error(const QString& a_error) { QString errorHtml = a_error; errorHtmlToLog(errorHtml.replace('<', "<").replace('>', ">").replace('\n', "
")); } //--------------------------------------------------------------------------- void MainWindow::errorHtmlToLog(const QString& a_errorHtml) { QString errorHtml = a_errorHtml; QTextCursor cursor = _protocol->textCursor(); cursor.insertHtml(QStringLiteral("Ошибка
")); cursor.insertHtml(errorHtml); cursor.insertHtml(QStringLiteral("
")); _protocol->moveCursor(QTextCursor::End); ++_errorCount; } QString MainWindow::toHtml(const QString& a_text) { QString result = a_text; static const QString BOLD = QStringLiteral("**"); static const QString HTML_BOLD_BEGIN = QStringLiteral(""); static const QString HTML_BOLD_END = QStringLiteral(""); static const QString HTML_BR = QStringLiteral("
"); static const QString HTML_NBSP = QStringLiteral("  "); static const QChar NL('\n'); static const QChar TAB('\t'); bool begin = true; int pos = result.indexOf(BOLD); while (pos >= 0) { result.replace(pos, BOLD.size(), begin ? HTML_BOLD_BEGIN : HTML_BOLD_END); begin = !begin; pos = result.indexOf(BOLD, pos + 1); } if (result.contains(NL)) result.replace(NL, HTML_BR); if (result.contains(TAB)) result.replace(TAB, HTML_NBSP); return result; } //--------------------------------------------------------------------------- void MainWindow::message(const QString& a_message) { QString message = a_message; _protocol->textCursor().insertHtml(toHtml(message).append(QStringLiteral("
"))); _protocol->moveCursor(QTextCursor::End); } //--------------------------------------------------------------------------- void MainWindow::closeDialog() { QDialog::done(_exitCode); } //--------------------------------------------------------------------------- void MainWindow::saveProtocol(const QString& a_fileName) { QString filename = a_fileName; if (filename.isEmpty()) filename = QFileDialog::getSaveFileName(this, QStringLiteral("Сохранить протокол в файл…"), QString(), QStringLiteral("Текстовые файлы (*.txt);;Все файлы (*.*)")); //если имя файла содержит символы %, то оно считается строкой формата для функции strftime, //и выполняется подстановка текущего времени if (filename.contains('%')) { char buffer[1024]; time_t ctime = QDateTime::currentSecsSinceEpoch(); strftime(buffer, sizeof(buffer), filename.toUtf8().constData(), localtime(&ctime)); filename = QString::fromUtf8(buffer); } if (!filename.isEmpty()) { QFile outf(filename); if (outf.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream os(&outf); os << _protocol->toPlainText().replace('\n', "\r\n"); } } } //!главное окно приложения