diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,457 +1,444 @@ #include "mainwindow.h" #include "psettings.h" #include "sqlerrordlg.h" #include "updater.h" #include #include #include #ifdef Q_OS_WIN32 #include #endif #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) , _actionEventType(QEvent::User) , _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(KActivityIndicator::Gray); _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())); //получить незадействованный тип события _actionEventType = QEvent::registerEventType(); } MainWindow::~MainWindow() { delete _updater; } //--------------------------------------------------------------------------- void MainWindow::showEvent(QShowEvent* a_event) { QDialog::showEvent(a_event); qApp->postEvent(this, new QEvent(QEvent::Type(_actionEventType))); } //--------------------------------------------------------------------------- bool MainWindow::event(QEvent* a_event) { if (a_event->type() == _actionEventType) { startExecution(); a_event->accept(); return true; } return QWidget::event(a_event); } //--------------------------------------------------------------------------- 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(pset); logon(pset); 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) { 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)); _exitCode = (before & 0xFFFF) | ((after & 0xFFFF) << 16); closeDialog(); //при успешном завершении, то окно закрывается автоматически } else { _btnClose->show(); _btnSave->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано в результате ошибки." "
База данных не изменилась.")); _exitCode = -1; } } //--------------------------------------------------------------------------- int MainWindow::executeAction() { ProgramSettings pset; processArguments(pset); 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; } void MainWindow::processArguments(ProgramSettings& a_pset) { QString USERNAME_PARAM("username"); QString PASSWORD_PARAM("password"); QString DATABASE_PARAM("database"); QString DROPDB_PARAM("dropdb"); QString HOST_PARAM("host"); QString PORT_PARAM("port"); QString FILE_PARAM("file"); //заполнить параметры default значениями QHash params; params.insert(USERNAME_PARAM, QString()); params.insert(PASSWORD_PARAM, QString()); params.insert(DATABASE_PARAM, QString()); params.insert(DROPDB_PARAM, QString()); params.insert(HOST_PARAM, QString()); params.insert(PORT_PARAM, QString()); params.insert(FILE_PARAM, QString()); //чтение аргументов QString key; QStringList arguments = QApplication::arguments(); auto end = arguments.cend(); auto begin = arguments.cbegin(); for (auto it = begin + 1; it != end; ++it) //пропускаем первый параметр - имя программы { QString curArg = *it; - if (curArg.startsWith("--")) - { //обработка длинного параметра - curArg.remove(0, 2); - QMutableHashIterator option(params); - while (option.hasNext()) - { - option.next(); - key = option.key(); - if (curArg.startsWith(key) && curArg.at(key.size()) == '=') - option.setValue(curArg.right(curArg.size() - key.size() - 1)); - } - key.clear(); - } - else if (curArg.at(0) == QChar('-')) + if (curArg.at(0) == QChar('-')) { //обработка короткого параметра key.clear(); switch (curArg.at(1).toLatin1()) { case 'U': key = USERNAME_PARAM; break; case 'd': key = DATABASE_PARAM; break; case 'f': key = FILE_PARAM; break; case 'h': key = HOST_PARAM; break; case 'p': key = PORT_PARAM; break; case 'W': key = PASSWORD_PARAM; break; case '0': params[ DROPDB_PARAM ] = '1'; break; } } else { //обработка одиночного параметра if (key.isEmpty()) key = FILE_PARAM; params[ key ] = curArg; key.clear(); } } //записать прочитанные значения в структуру ProgramSettings a_pset.username = params.value(USERNAME_PARAM); a_pset.password = params.value(PASSWORD_PARAM); a_pset.database = params.value(DATABASE_PARAM); a_pset.host = params.value(HOST_PARAM); a_pset.port = params.value(PORT_PARAM); a_pset.controlFile = params.value(FILE_PARAM); a_pset.dropdb = !params.value(DROPDB_PARAM).isEmpty(); } //--------------------------------------------------------------------------- void MainWindow::logon(ProgramSettings& a_pset) { if (!a_pset.host.isEmpty() || !a_pset.username.isEmpty()) return; #if defined(Q_OS_WIN) KLogon* logon = KLogon::create(); if (!logon) return; QString defaultDSN; HKEY k; DWORD cb = 0; QString valuename("System Directory"); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\irs\\b03"), 0, KEY_QUERY_VALUE, &k) == ERROR_SUCCESS && RegQueryValueEx(k, LPCTSTR(valuename.constData()), 0, 0, 0, &cb) == ERROR_SUCCESS && cb > 0) { QString systemdir; systemdir.resize((cb / sizeof(QChar)) - 1); RegQueryValueEx(k, LPCTSTR(valuename.constData()), 0, 0, LPBYTE(systemdir.data()), &cb); RegCloseKey(k); KSettings systemini(systemdir + "/system.ini"); systemini.beginGroup("Database"); defaultDSN = systemini.value("DefaultDSN").toString(); } QString username = QString::fromLocal8Bit(qgetenv("USERNAME")); logon->setParent(winId()); logon->setUsername(username); if (!defaultDSN.isNull()) logon->setDSN(defaultDSN); if (!logon->execute()) emit message(QStringLiteral("Отказ от ввода имени и пароля пользователя")); else { a_pset.host = logon->host(); a_pset.username = logon->username(); a_pset.password = logon->password(); } delete logon; #else QString username = QString::fromLocal8Bit(getenv("USER")); #endif } //--------------------------------------------------------------------------- void MainWindow::logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username) { message(QStringLiteral("Подключение к базе данных
" "  Сервер: %1
  База данных: %2
" "  Пользователь: %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; } //--------------------------------------------------------------------------- void MainWindow::message(const QString& a_message) { QString message = a_message; _protocol->textCursor().insertHtml(message.replace(QChar('\n'), "
").append("
")); _protocol->moveCursor(QTextCursor::End); } //--------------------------------------------------------------------------- void MainWindow::closeDialog() { QDialog::done(_exitCode); } //--------------------------------------------------------------------------- void MainWindow::saveProtocol() { QString filename = QFileDialog::getSaveFileName(this, QStringLiteral("Сохранить протокол в файл…"), QString(), QStringLiteral("Текстовые файлы (*.txt);;Все файлы (*.*)")); if (!filename.isEmpty()) { QFile outf(filename); if (outf.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream os(&outf); os.setGenerateByteOrderMark(true); os.setCodec("UTF-8"); os << _protocol->toPlainText(); } } } //!главное окно приложения