From 97d9ffddfa7776fff0d791e70b6c9e28685d6084 Mon Sep 17 00:00:00 2001 From: Mik Firestone Date: Mon, 23 May 2016 17:21:08 -0400 Subject: [PATCH 1/4] Bug/Feature #247 -- Database vanished This doesn't fix the core problem. That comes next, because I think I can. This implements automatic, under the cover backups. The OptionDialog has been significantly re-arranged. The user can now select how often (aka, frequency) to backup, and how many old copies to keep. Frequency is defined by the number of times a user closes brewtarget and accepts the changes. Frequency defaults to 4, the number defaults to 10. A frequency of 1 means always backup on close. If the number to keep is -1, it means never delete; 0 means never backup; >0 means that number of backups When brewtarget is closed, we get a counter from the properties file. If that counter % frequency is not 0, we simply store the updated counter and exit. If the number to save is 0, we exit. If we get this far, we pull the backup directory name and the list of existing files from the properties file. If counter % frequency is 0, we generate a filename like bt_database.yyyyMMdd. We then check to see if that file exists in the backup directory. If it does, we go into a loop appending 0001, 0002 ... until we get a filename that doesn't exist. If we get to 10000, we give up and just use bt_database.yyyyMMdd. Once we have the name, we call backupToDir() which already existed. That just copies the file for us. We then append the new name to the list. The final step is to clean up the old copies if we need to. While the size of the list is greater than the number of copies to keep, we pop the first (aka, oldest) file name off the list, and delete it. Using a while loop allows the user to adjust the number of preserved copies down and brewtarget will clean up after itself. Finally, we store the new list of files names back into the properties file and exit. --- src/OptionDialog.cpp | 334 ++++++++++++++++++++++++++++++++++++++----- src/OptionDialog.h | 47 +++++- src/brewtarget.cpp | 13 +- src/brewtarget.h | 2 +- src/database.cpp | 101 ++++++++++++- src/database.h | 4 +- ui/optionsDialog.ui | 281 +++++------------------------------- 7 files changed, 493 insertions(+), 289 deletions(-) diff --git a/src/OptionDialog.cpp b/src/OptionDialog.cpp index 2304f1f8d..46100be6d 100644 --- a/src/OptionDialog.cpp +++ b/src/OptionDialog.cpp @@ -42,7 +42,14 @@ OptionDialog::OptionDialog(QWidget* parent) { int i; + + // I need a lot of control over what is displayed on the DbConfig dialog. + // Maybe designer can do it? No idea. So I did this hybrid model, and I + // think it will end up biting my ... + // anyway. It isn't pretty setupUi(this); + createPostgresElements(); + createSQLiteElements(); if( parent != 0 ) { @@ -141,17 +148,19 @@ OptionDialog::OptionDialog(QWidget* parent) connect( buttonBox, SIGNAL( accepted() ), this, SLOT( saveAndClose() ) ); connect( buttonBox, SIGNAL( rejected() ), this, SLOT( cancel() ) ); - connect( pushButton_dbDirBrowse, SIGNAL( clicked() ), this, SLOT( setDataDir() ) ); - connect( pushButton_dbDirDefault, SIGNAL( clicked() ), this, SLOT( defaultDataDir() ) ); // database panel stuff comboBox_engine->addItem( tr("SQLite (default)"), QVariant(Brewtarget::SQLITE)); comboBox_engine->addItem( tr("PostgreSQL"), QVariant(Brewtarget::PGSQL)); connect( comboBox_engine, SIGNAL( currentIndexChanged(int) ), this, SLOT( setEngine(int) ) ); connect( pushButton_testConnection, SIGNAL( clicked() ), this, SLOT(testConnection())); + + // figure out which database we have + setDbDialog((Brewtarget::DBTypes)Brewtarget::option("dbType", Brewtarget::SQLITE).toInt()); + + // Set the signals connect( checkBox_savePassword, SIGNAL(clicked(bool)), this, SLOT(savePassword(bool))); - // I hope this works connect( btStringEdit_hostname, SIGNAL( textModified() ), this, SLOT(testRequired())); connect( btStringEdit_portnum, SIGNAL( textModified() ), this, SLOT(testRequired())); connect( btStringEdit_schema, SIGNAL( textModified() ), this, SLOT(testRequired())); @@ -159,6 +168,10 @@ OptionDialog::OptionDialog(QWidget* parent) connect( btStringEdit_username, SIGNAL( textModified() ), this, SLOT(testRequired())); connect( btStringEdit_password, SIGNAL( textModified() ), this, SLOT(testRequired())); + connect( pushButton_browseDataDir, SIGNAL( clicked() ), this, SLOT( setDataDir() ) ); + connect( pushButton_defaultDataDir, SIGNAL( clicked() ), this, SLOT( defaultDataDir() ) ); + connect( pushButton_browseBackupDir, SIGNAL( clicked() ), this, SLOT( setBackupDir() ) ); + connect( pushButton_defaultBackupDir, SIGNAL( clicked() ), this, SLOT( defaultBackupDir() ) ); pushButton_testConnection->setEnabled(false); } @@ -167,6 +180,7 @@ void OptionDialog::retranslate() { // Let the Ui take care of its business retranslateUi(this); + retranslateDbDialog(this); // Retranslate the language combobox. // NOTE: the indices MUST correspond to ndxToLangCode. @@ -210,12 +224,24 @@ void OptionDialog::setDataDir() { QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), Brewtarget::getUserDataDir().canonicalPath(), QFileDialog::ShowDirsOnly); if( ! dir.isEmpty() ) - lineEdit_dbDir->setText( dir ); + btStringEdit_dataDir->setText( dir ); +} + +void OptionDialog::setBackupDir() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), Brewtarget::getUserDataDir().canonicalPath(), QFileDialog::ShowDirsOnly); + if( ! dir.isEmpty() ) + btStringEdit_backupDir->setText( dir ); } void OptionDialog::defaultDataDir() { - lineEdit_dbDir->setText( Brewtarget::getConfigDir().canonicalPath() ); + btStringEdit_dataDir->setText( Brewtarget::getConfigDir().canonicalPath() ); +} + +void OptionDialog::defaultBackupDir() +{ + btStringEdit_backupDir->setText( Brewtarget::getConfigDir().canonicalPath() ); } void OptionDialog::saveAndClose() @@ -224,8 +250,7 @@ void OptionDialog::saveAndClose() bool saveDbConfig = true; // TODO:: FIX THIS UI. I am really not sure what the best approach is here. - if ( status == OptionDialog::NEEDSTEST || status == OptionDialog::TESTFAILED ) - { + if ( status == OptionDialog::NEEDSTEST || status == OptionDialog::TESTFAILED ) { QMessageBox::critical(0, tr("Test connection or cancel"), tr("Saving the options without testing the connection can cause brewtarget to not restart. Your changes have been discarded, which is likely really, really crappy UX. Please open a bug explaining exactly how you got to this message.") @@ -265,6 +290,7 @@ void OptionDialog::saveAndClose() else { Brewtarget::removeOption("dbPassword"); } + switch (weightComboBox->itemData(weightComboBox->currentIndex()).toInt(&okay)) { case SI: @@ -361,36 +387,43 @@ void OptionDialog::saveAndClose() Brewtarget::setLanguage( ndxToLangCode[ comboBox_lang->currentIndex() ] ); // Check the new userDataDir. - QString newUserDataDir = lineEdit_dbDir->text(); - - QDir userDirectory(newUserDataDir); - - if( userDirectory != Brewtarget::getUserDataDir() ) - { - // If there are no data files present... - if( ! QFileInfo(userDirectory, "database.sqlite").exists() ) + Brewtarget::DBTypes dbEngine = (Brewtarget::DBTypes)comboBox_engine->currentIndex(); + if ( dbEngine == Brewtarget::SQLITE ) { + QString newUserDataDir = btStringEdit_dataDir->text(); + QDir userDirectory(newUserDataDir); + + // I think this is redundant and could be handled as just a simple db + // transfer using the testPassed loop above. + if( userDirectory != Brewtarget::getUserDataDir() ) { - // ...tell user we will copy old data files to new location. - QMessageBox::information(this, - tr("Copy Data"), - tr("There do not seem to be any data files in this directory, so we will copy your old data here.") - ); - Brewtarget::copyDataFiles(newUserDataDir); + // If there are no data files present... + if( ! QFileInfo(userDirectory, "database.sqlite").exists() ) + { + // ...tell user we will copy old data files to new location. + QMessageBox::information(this, + tr("Copy Data"), + tr("There do not seem to be any data files in this directory, so we will copy your old data here.") + ); + Brewtarget::copyDataFiles(newUserDataDir); + } + + Brewtarget::userDataDir = newUserDataDir; + Brewtarget::setOption("user_data_dir", newUserDataDir); + QMessageBox::information( + this, + tr("Restart"), + tr("Please restart Brewtarget.") + ); } - Brewtarget::userDataDir = newUserDataDir; - Brewtarget::setOption("user_data_dir", newUserDataDir); - QMessageBox::information( - this, - tr("Restart"), - tr("Please restart Brewtarget.") - ); + Brewtarget::setOption("maximum", spinBox_numBackups->value(), "backups"); + Brewtarget::setOption("frequency", spinBox_frequency->value(), "backups"); + Brewtarget::setOption("directory", btStringEdit_backupDir->text(), "backups"); } Brewtarget::setOption("mashHopAdjustment", ibuAdjustmentMashHopDoubleSpinBox->value() / 100); Brewtarget::setOption("firstWortHopAdjustment", ibuAdjustmentFirstWortDoubleSpinBox->value() / 100); - // Make sure the main window updates. if( Brewtarget::mainWindow() ) Brewtarget::mainWindow()->showChanges(); @@ -422,7 +455,12 @@ void OptionDialog::showChanges() ibuFormulaComboBox->setCurrentIndex(ibuFormulaComboBox->findData(Brewtarget::ibuFormula)); // Data directory - lineEdit_dbDir->setText(Brewtarget::getUserDataDir().canonicalPath()); + btStringEdit_dataDir->setText(Brewtarget::getUserDataDir().canonicalPath()); + + // Backup stuff + btStringEdit_backupDir->setText( Brewtarget::option("directory", Brewtarget::getUserDataDir().canonicalPath(), "backups").toString() ); + spinBox_numBackups->setValue( Brewtarget::option("maximum", 10, "backups").toInt() ); + spinBox_frequency->setValue( Brewtarget::option("frequency", 4, "backups").toInt() ); // The IBU modifications. These will all be calculated from a 60 min boil. This is gonna get confusing. double amt = Brewtarget::toDouble(Brewtarget::option("mashHopAdjustment",0).toString(), "OptionDialog::showChanges()"); @@ -435,7 +473,6 @@ void OptionDialog::showChanges() // the default for this field int tmp = (Brewtarget::DBTypes)Brewtarget::option("dbType",Brewtarget::SQLITE).toInt(); comboBox_engine->setCurrentIndex(tmp); - groupBox_dbConfig->setEnabled(tmp != Brewtarget::SQLITE); btStringEdit_hostname->setText(Brewtarget::option("dbHostname","localhost").toString()); btStringEdit_portnum->setText(Brewtarget::option("dbPort","5432").toString()); @@ -449,6 +486,237 @@ void OptionDialog::showChanges() changeColors(); } +void OptionDialog::postgresVisible(bool canSee) +{ + label_hostname->setVisible(canSee); + btStringEdit_hostname->setVisible(canSee); + label_portnum->setVisible(canSee); + btStringEdit_portnum->setVisible(canSee); + label_schema->setVisible(canSee); + btStringEdit_schema->setVisible(canSee); + label_dbName->setVisible(canSee); + btStringEdit_dbname->setVisible(canSee); + label_username->setVisible(canSee); + btStringEdit_username->setVisible(canSee); + label_password->setVisible(canSee); + btStringEdit_password->setVisible(canSee); + checkBox_savePassword->setVisible(canSee); + label_password->setVisible(canSee); +} + +void OptionDialog::sqliteVisible(bool canSee) +{ + label_dataDir->setVisible(canSee); + btStringEdit_dataDir->setVisible(canSee); + + pushButton_browseDataDir->setVisible(canSee); + pushButton_defaultDataDir->setVisible(canSee); + label_backupDir->setVisible(canSee); + btStringEdit_backupDir->setVisible(canSee); + pushButton_browseBackupDir->setVisible(canSee); + pushButton_defaultBackupDir->setVisible(canSee); + + label_numBackups->setVisible(canSee); + spinBox_numBackups->setVisible(canSee); + + label_frequency->setVisible(canSee); + spinBox_frequency->setVisible(canSee); +} + +void OptionDialog::setDbDialog(Brewtarget::DBTypes db) +{ + groupBox_dbConfig->setVisible(false); + + clearLayout(); + if ( db == Brewtarget::PGSQL ) { + postgresVisible(true); + sqliteVisible(false); + + gridLayout->addWidget(label_hostname,0,0); + gridLayout->addWidget(btStringEdit_hostname,0,1); + + gridLayout->addWidget(label_portnum,1,0); + gridLayout->addWidget(btStringEdit_portnum,1,1); + + gridLayout->addWidget(label_schema,2,0); + gridLayout->addWidget(btStringEdit_schema,2,1); + + gridLayout->addWidget(label_dbName,3,0); + gridLayout->addWidget(btStringEdit_dbname,3,1); + + gridLayout->addWidget(label_username,4,0); + gridLayout->addWidget(btStringEdit_username,4,1); + + gridLayout->addWidget(label_password,5,0); + gridLayout->addWidget(btStringEdit_password,5,1); + + gridLayout->addWidget(checkBox_savePassword, 5, 2); + + } + else { + postgresVisible(false); + sqliteVisible(true); + + gridLayout->addWidget(label_dataDir,0,0); + gridLayout->addWidget(btStringEdit_dataDir,0,1,1,-1); + gridLayout->addWidget(pushButton_browseDataDir,1,2); + gridLayout->addWidget(pushButton_defaultDataDir,1,3); + + gridLayout->addWidget(label_backupDir,2,0); + gridLayout->addWidget(btStringEdit_backupDir,2,1,1,-1); + gridLayout->addWidget(pushButton_browseBackupDir,3,2); + gridLayout->addWidget(pushButton_defaultBackupDir,3,3); + + gridLayout->addWidget(label_numBackups,4,0); + gridLayout->addWidget(spinBox_numBackups,4,1); + + gridLayout->addWidget(label_frequency,5,0); + gridLayout->addWidget(spinBox_frequency,5,1); + } + + groupBox_dbConfig->setVisible(true); +} + +void OptionDialog::clearLayout() +{ + QLayoutItem *child; + + while ( (child = gridLayout->takeAt(0)) != 0 ) { + gridLayout->removeItem(child); + } +} + +void OptionDialog::createPostgresElements() +{ + + label_hostname = new QLabel(groupBox_dbConfig); + label_hostname->setObjectName(QStringLiteral("label_hostname")); + + btStringEdit_hostname = new BtStringEdit(groupBox_dbConfig); + btStringEdit_hostname->setObjectName(QStringLiteral("btStringEdit_hostname")); + + label_portnum = new QLabel(groupBox_dbConfig); + label_portnum->setObjectName(QStringLiteral("label_portnum")); + + btStringEdit_portnum = new BtStringEdit(groupBox_dbConfig); + btStringEdit_portnum->setObjectName(QStringLiteral("btStringEdit_portnum")); + + label_schema = new QLabel(groupBox_dbConfig); + label_schema->setObjectName(QStringLiteral("label_schema")); + + btStringEdit_schema = new BtStringEdit(groupBox_dbConfig); + btStringEdit_schema->setObjectName(QStringLiteral("btStringEdit_schema")); + + label_dbName = new QLabel(groupBox_dbConfig); + label_dbName->setObjectName(QStringLiteral("label_dbName")); + + btStringEdit_dbname = new BtStringEdit(groupBox_dbConfig); + btStringEdit_dbname->setObjectName(QStringLiteral("btStringEdit_dbname")); + + label_username = new QLabel(groupBox_dbConfig); + label_username->setObjectName(QStringLiteral("label_username")); + + btStringEdit_username = new BtStringEdit(groupBox_dbConfig); + btStringEdit_username->setObjectName(QStringLiteral("btStringEdit_username")); + + btStringEdit_password = new BtStringEdit(groupBox_dbConfig); + btStringEdit_password->setObjectName(QStringLiteral("btStringEdit_password")); + btStringEdit_password->setEchoMode(QLineEdit::Password); + + checkBox_savePassword = new QCheckBox(groupBox_dbConfig); + checkBox_savePassword->setObjectName(QStringLiteral("checkBox_savePassword")); + + label_password = new QLabel(groupBox_dbConfig); + label_password->setObjectName(QStringLiteral("label_password")); + + postgresVisible(false); +} + +void OptionDialog::createSQLiteElements() +{ + + // Oy vey. Set up the data directory dialog and buttons + label_dataDir = new QLabel(groupBox_dbConfig); + label_dataDir->setObjectName(QStringLiteral("label_dataDir")); + + btStringEdit_dataDir = new BtStringEdit(groupBox_dbConfig); + btStringEdit_dataDir->setObjectName(QStringLiteral("btStringEdit_dataDir")); + + pushButton_browseDataDir = new QPushButton(groupBox_dbConfig); + pushButton_browseDataDir->setObjectName(QStringLiteral("button_browseDataDir")); + + pushButton_defaultDataDir = new QPushButton(groupBox_dbConfig); + pushButton_defaultDataDir->setObjectName(QStringLiteral("button_defaultDataDir")); + + // Set up the backup directory dialog and buttons + label_backupDir = new QLabel(groupBox_dbConfig); + label_backupDir->setObjectName(QStringLiteral("label_backupDir")); + + btStringEdit_backupDir = new BtStringEdit(groupBox_dbConfig); + btStringEdit_backupDir->setObjectName(QStringLiteral("btStringEdit_backupDir")); + + pushButton_browseBackupDir = new QPushButton(groupBox_dbConfig); + pushButton_browseBackupDir->setObjectName(QStringLiteral("button_browseBackupDir")); + + pushButton_defaultBackupDir = new QPushButton(groupBox_dbConfig); + pushButton_defaultBackupDir->setObjectName(QStringLiteral("button_defaultBackupDir")); + + // Set up the two spin boxes + label_numBackups = new QLabel(groupBox_dbConfig); + label_numBackups->setObjectName(QStringLiteral("label_numBackups")); + + spinBox_numBackups = new QSpinBox(groupBox_dbConfig); + spinBox_numBackups->setObjectName(QStringLiteral("spinBox_numBackups")); + spinBox_numBackups->setMinimum(-1); + spinBox_numBackups->setMaximum(9999); + + label_frequency = new QLabel(groupBox_dbConfig); + label_frequency->setObjectName(QStringLiteral("label_frequency")); + + spinBox_frequency = new QSpinBox(groupBox_dbConfig); + spinBox_frequency->setObjectName(QStringLiteral("spinBox_frequency")); + // I couldn't make any semantic difference between 0 and 1. So we start at + // 1 + spinBox_frequency->setMinimum(1); + spinBox_frequency->setMaximum(10); + + sqliteVisible(false); + +} + +void OptionDialog::retranslateDbDialog(QDialog *optionsDialog) +{ + label_hostname->setText(QApplication::translate("optionsDialog", "Hostname", 0)); + label_portnum->setText(QApplication::translate("optionsDialog", "Port", 0)); + label_schema->setText(QApplication::translate("optionsDialog", "Schema", 0)); + label_dbName->setText(QApplication::translate("optionsDialog", "Database", 0)); + label_username->setText(QApplication::translate("optionsDialog", "Username", 0)); + label_password->setText(QApplication::translate("optionsDialog", "Password", 0)); + checkBox_savePassword->setText(QApplication::translate("optionsDialog", "Save password", 0)); + label_dataDir->setText(QApplication::translate("optionsDialog", "Backup Directory", 0)); + pushButton_browseDataDir->setText(QApplication::translate("optionsDialog", "Browse", 0)); + pushButton_defaultDataDir->setText(QApplication::translate("optionsDialog", "Default", 0)); + label_backupDir->setText(QApplication::translate("optionsDialog", "Backup Directory", 0)); + pushButton_browseBackupDir->setText(QApplication::translate("optionsDialog", "Browse", 0)); + pushButton_defaultBackupDir->setText(QApplication::translate("optionsDialog", "Default", 0)); + label_numBackups->setText(QApplication::translate("optionsDialog", "Number of Backups", 0)); + label_frequency->setText(QApplication::translate("optionsDialog", "Frequency of Backups", 0)); + + // set up the tooltips if we are using them +#ifndef QT_NO_TOOLTIP + btStringEdit_hostname->setToolTip(QApplication::translate("optionsDialog", "PostgresSQL's host name or IP address", 0)); + btStringEdit_portnum->setToolTip(QApplication::translate("optionsDialog", "Port the PostgreSQL is listening on", 0)); + btStringEdit_schema->setToolTip(QApplication::translate("optionsDialog", "The schema containing the database", 0)); + btStringEdit_username->setToolTip(QApplication::translate("optionsDialog", "User with create/delete table access", 0)); + btStringEdit_password->setToolTip(QApplication::translate("optionsDialog", "Password for the user", 0)); + btStringEdit_dbname->setToolTip(QApplication::translate("optionsDialog", "The name of the database", 0)); + label_dataDir->setToolTip(QApplication::translate("optionsDialog", "Where your database file is",0)); + label_backupDir->setToolTip(QApplication::translate("optionsDialog", "Where to save your backups",0)); + label_numBackups->setToolTip(QApplication::translate("optionsDialog", "Number of backups to keep", 0)); + label_frequency->setToolTip(QApplication::translate("optionsDialog", "How frequently a backup is made", 0)); +#endif +} + void OptionDialog::changeEvent(QEvent* e) { switch( e->type() ) @@ -467,7 +735,7 @@ void OptionDialog::setEngine(int selected) { Brewtarget::DBTypes newEngine = (Brewtarget::DBTypes)selected; - groupBox_dbConfig->setEnabled(newEngine != Brewtarget::SQLITE); + setDbDialog(newEngine); testRequired(); } @@ -498,7 +766,7 @@ void OptionDialog::testConnection() success = Database::verifyDbConnection(newType,hostname,port,schema,database,username,password); break; default: - hostname = QString("%1/%2").arg(lineEdit_dbDir->text()).arg("database.sqlite"); + hostname = QString("%1/%2").arg(btStringEdit_dataDir->text()).arg("database.sqlite"); success = Database::verifyDbConnection(newType,hostname); } diff --git a/src/OptionDialog.h b/src/OptionDialog.h index b24380265..f96990423 100644 --- a/src/OptionDialog.h +++ b/src/OptionDialog.h @@ -30,6 +30,8 @@ class OptionDialog; #include #include #include +#include +#include "BtLineEdit.h" #include "ui_optionsDialog.h" #include "unit.h" @@ -46,6 +48,45 @@ class OptionDialog : public QDialog, public Ui::optionsDialog //! \brief Default constructor. OptionDialog(QWidget *parent=0); + // UI stuff to make this work as I want + // Postgres things + QLabel *label_hostname; + BtStringEdit *btStringEdit_hostname; + QLabel *label_portnum; + BtStringEdit *btStringEdit_portnum; + QLabel *label_schema; + BtStringEdit *btStringEdit_schema; + QLabel *label_dbName; + BtStringEdit *btStringEdit_dbname; + QLabel *label_username; + BtStringEdit *btStringEdit_username; + QLabel *label_password; + BtStringEdit *btStringEdit_password; + QHBoxLayout *horizontalLayout_7; + QSpacerItem *horizontalSpacer_3; + QCheckBox *checkBox_savePassword; + // SQLite things + QLabel *label_dataDir; + BtStringEdit *btStringEdit_dataDir; + QPushButton *pushButton_browseDataDir; + QPushButton *pushButton_defaultDataDir; + QLabel *label_backupDir; + BtStringEdit *btStringEdit_backupDir; + QPushButton *pushButton_browseBackupDir; + QPushButton *pushButton_defaultBackupDir; + QLabel *label_numBackups; + QSpinBox *spinBox_numBackups; + QLabel *label_frequency; + QSpinBox *spinBox_frequency; + + void createPostgresElements(); + void createSQLiteElements(); + void clearLayout(); + void setDbDialog(Brewtarget::DBTypes db); + void retranslateDbDialog(QDialog *optionsDialog); + void sqliteVisible(bool canSee); + void postgresVisible(bool canSee); + public slots: //! \brief Show the dialog. void show(); @@ -55,8 +96,11 @@ public slots: void cancel(); //! \brief Pop up a dialog to choose the data directory. void setDataDir(); + void setBackupDir(); //! \brief Reset data directory to default. void defaultDataDir(); + void defaultBackupDir(); + //! \brief Enable or disable the configuration panel based on the engine choice void setEngine(int selected); //! \brief test connection to remote databases. This could get ugly @@ -65,7 +109,8 @@ public slots: void testRequired(); //! \brief handle the dialogs for saving passwords void savePassword(bool state); - + + protected: //! \brief Reimplemented from QWidget. diff --git a/src/brewtarget.cpp b/src/brewtarget.cpp index f5eaf3fcb..c77a567b6 100644 --- a/src/brewtarget.cpp +++ b/src/brewtarget.cpp @@ -1403,10 +1403,17 @@ QVariant Brewtarget::option(QString attribute, QVariant default_value, QString s return QSettings().value(name,default_value); } -void Brewtarget::removeOption(QString attribute) +void Brewtarget::removeOption(QString attribute, QString section) { - if ( hasOption(attribute) ) - QSettings().remove(attribute); + QString name; + + if ( section.isNull() ) + name = attribute; + else + name = generateName(attribute,section,NOOP); + + if ( hasOption(name) ) + QSettings().remove(name); } QString Brewtarget::generateName(QString attribute, const QString section, iUnitOps ops) diff --git a/src/brewtarget.h b/src/brewtarget.h index 5167f3a16..8262696cf 100644 --- a/src/brewtarget.h +++ b/src/brewtarget.h @@ -307,7 +307,7 @@ class Brewtarget : public QObject static bool hasOption(QString attribute, const QString section = QString(), iUnitOps ops = NOOP); static void setOption(QString attribute, QVariant value, const QString section = QString(), iUnitOps ops = NOOP); static QVariant option(QString attribute, QVariant default_value = QVariant(), QString section = QString(), iUnitOps = NOOP); - static void removeOption(QString attribute); + static void removeOption(QString attribute, QString section=QString()); static QString generateName(QString attribute, const QString section, iUnitOps ops); diff --git a/src/database.cpp b/src/database.cpp index c9fed013d..f8965e905 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -562,8 +562,9 @@ void Database::unload(bool keepChanges) { // The postgres driver wants nothing to do with this. Core gets dumped if // we try it. Since we don't need to copy things about for postgres... - if ( Brewtarget::dbType() == Brewtarget::SQLITE ) - { + + if ( Brewtarget::dbType() == Brewtarget::SQLITE ) { + QSqlDatabase::database( dbConName, false ).close(); QSqlDatabase::removeDatabase( dbConName ); @@ -572,8 +573,14 @@ void Database::unload(bool keepChanges) { // If load() failed or want to keep the changes, then // just keep the database and don't revert to the backup. - if (dbFile.exists()) dbTempBackupFile.remove(); - return; + if (dbFile.exists()) + dbTempBackupFile.remove(); + + // We only want to make a backup when the user persists the changes. I + // think? + if ( keepChanges ) + automaticBackup(); + return; } // If the user doesn't want to save changes, remove the active database // and restore the backup. @@ -583,6 +590,83 @@ void Database::unload(bool keepChanges) } } +void Database::automaticBackup() +{ + int count = Brewtarget::option("count",0,"backups").toInt() + 1; + int frequency = Brewtarget::option("frequency",4,"backups").toInt(); + int maxBackups = Brewtarget::option("maximum",10,"backups").toInt(); + + // The most common case is update the counter and nothing else + // A frequency of 1 means backup every time. Which this statisfies + if ( count % frequency != 0 ) { + Brewtarget::setOption( "count", count, "backups"); + return; + } + + // If the user has selected 0 max backups, we just return. There's a weird + // case where they have a frequency of 1 and a maxBackup of 0. In that + // case, maxBackup wins + if ( maxBackups == 0 ) { + return; + } + + QString backupDir = Brewtarget::option("directory", Brewtarget::getConfigDir().canonicalPath(),"backups").toString(); + QString listOfFiles = Brewtarget::option("files",QVariant(),"backups").toString(); + QStringList fileNames = listOfFiles.split(",", QString::SkipEmptyParts); + + QString halfName = QString("%1.%2").arg("bt_database").arg(QDate::currentDate().toString("yyyyMMdd")); + QString newName = halfName; + // Unique filenames are a pain in the ass. In the case you open brewtarget + // twice in a day, this loop makes sure we don't over write (or delete) the + // wrong thing + int foobar = 0; + while ( foobar < 10000 && QFile::exists( backupDir + "/" + newName ) ) { + foobar++; + newName = QString("%1_%2").arg(halfName).arg(foobar,4,10,QChar('0')); + if ( foobar > 9999 ) { + Brewtarget::logW( QString("%1 : could not find a unique name in 10000 tries. Overwriting %2").arg(Q_FUNC_INFO).arg(halfName)); + newName = halfName; + } + } + // backup the file first + backupToDir(backupDir,newName); + + // If we have maxBackups == -1, it means never clean. It also means we + // don't track the filenames. + if ( maxBackups == -1 ) { + Brewtarget::removeOption("files","backups"); + return; + } + + fileNames.append(newName); + + // If we have too many backups. This is in a while loop because we need to + // handle the case where a user decides they only want 4 backups, not 10. + // The while loop will clean that up properly. + while ( fileNames.size() > maxBackups ) { + // takeFirst() removes the file from the list, which is important + QString victim = backupDir + "/" + fileNames.takeFirst(); + QFile *file = new QFile(victim); + QFileInfo *fileThing = new QFileInfo(victim); + + // Make sure it exists, and make sure it is a file before we + // try remove it + if ( fileThing->exists() && fileThing->isFile() ) { + // If we can't remove it, give a warning. + if (! file->remove() ) { + Brewtarget::logW( QString("%1 : could not remove %2 (%3).").arg(Q_FUNC_INFO).arg(victim).arg(file->error())); + } + } + } + + // re-encode the list + listOfFiles = fileNames.join(","); + + // finally, reset the counter and save the new list of files + Brewtarget::setOption( "count", 0, "backups"); + Brewtarget::setOption( "files", listOfFiles, "backups"); +} + bool Database::isDirty() { return dirty; @@ -626,7 +710,7 @@ void Database::dropInstance() } -bool Database::backupToDir(QString dir) +bool Database::backupToDir(QString dir,QString filename) { // Make sure the singleton exists. instance(); @@ -635,6 +719,13 @@ bool Database::backupToDir(QString dir) QString prefix = dir + "/"; QString newDbFileName = prefix + "database.sqlite"; + if ( filename.isEmpty() ) { + newDbFileName = prefix + "database.sqlite"; + } + else { + newDbFileName = prefix + filename; + } + // Remove the files if they already exist so that // the copy() operation will succeed. QFile::remove(newDbFileName); diff --git a/src/database.h b/src/database.h index d9fddceb3..131b70fa0 100644 --- a/src/database.h +++ b/src/database.h @@ -101,7 +101,7 @@ class Database : public QObject static bool createBlank(QString const& filename); //! backs up database to 'dir' in chosen directory - static bool backupToDir(QString dir); + static bool backupToDir(QString dir, QString filename=""); //! \brief Reverts database to that of chosen file. static bool restoreFromFile(QString newDbFileStr); @@ -923,7 +923,7 @@ private slots: //! \brief does the heavy lifting to copy the contents from one db to the //next void copyDatabase( Brewtarget::DBTypes oldType, Brewtarget::DBTypes newType, QSqlDatabase oldDb); - + void automaticBackup(); }; diff --git a/ui/optionsDialog.ui b/ui/optionsDialog.ui index 1ede33efd..a6633066d 100644 --- a/ui/optionsDialog.ui +++ b/ui/optionsDialog.ui @@ -9,21 +9,21 @@ 0 0 - 601 - 380 + 481 + 314 Options - + QTabWidget::Rounded - 4 + 3 @@ -218,61 +218,6 @@ - - - Directories - - - - - - - - Database Directory - - - - - - - Where your brewtarget .xml files are. - - - true - - - - - - - Browse - - - - - - - Default - - - - - - - - - Qt::Vertical - - - - 20 - 173 - - - - - - Language @@ -332,190 +277,48 @@ - - - Engines - - - - QFormLayout::ExpandingFieldsGrow - - - - - RDBMS Engine - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Test Connection - - - - - - - - - - false - - - Configuration - - - optionsDialog - - - - QFormLayout::ExpandingFieldsGrow - - - - - Hostname - - - - - - - PostgresSQL's host name or IP address - - - localhost - - - - - - - Port - - - - - - - Port the PostgreSQL is listening on - - - 5432 - - - - - - - Schema - - - - - - - The schema containing the database - - - public - - - - - - - Database - - - - - - - The name of the database - - - brewtarget - - - - - - - Username - - - - - - - User with create/delete table access - - - brewtarget - - - - - - - Password - - - - - - - Password for the user - - - QLineEdit::Password - - - - - + + + + + Engines + + - - - Qt::Horizontal - - - - 40 - 20 - + + + RDBMS Engine - + - + + + + - Save password + Test Connection - - - + + + + + + true + + + Configuration + + + optionsDialog + + + + + @@ -533,18 +336,8 @@ - - - BtStringEdit - QLineEdit -
BtLineEdit.h
-
-
tabWidget - lineEdit_dbDir - pushButton_dbDirBrowse - pushButton_dbDirDefault buttonBox From 916f5bc25fa94a29843fac2e85c658788e771b87 Mon Sep 17 00:00:00 2001 From: mikfire Date: Tue, 24 May 2016 17:29:19 -0400 Subject: [PATCH 2/4] Bug/Feature #247 database VANISHED, part2 This is the other half of the solution. I *think* I've found all the places where we over write the databases when two run at the same time. I've found a few edge cases I don't think we knew about -- like what happens when: there is no temp backup, but the user discards changes. Hint: bye-bye database. I've tried to protect every place I can find. No temp backup is created if we cannot open the database. Database::load() fails if we cannot open the database and run the pragmas. If the load fails, brewtarget bails. I think I need to merge this with the other half, and then set about removing the temporary backups. I like the idea, but there's just no good way to implement it. --- src/MainWindow.cpp | 11 +++++--- src/brewtarget.cpp | 2 +- src/database.cpp | 67 +++++++++++++++++++++++++++++++++------------- src/database.h | 1 + 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 6e8ca420e..fb069d8c9 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -136,8 +136,9 @@ MainWindow::MainWindow(QWidget* parent) QDesktopWidget *desktop = QApplication::desktop(); - // Ensure database initializes. - Database::instance(); + // If the database doesn't load, we bail + if (! Database::instance().loadSuccessful() ) + exit(1); // Set the window title. setWindowTitle( QString("Brewtarget - %1").arg(VERSIONSTRING) ); @@ -1938,7 +1939,11 @@ void MainWindow::closeEvent(QCloseEvent* /*event*/) // Ask the user if they want to save changes, only if the dirty bit has // been thrown - if( Database::instance().isDirty() && + // We should also make sure the backup db still exists -- there's some edge + // cases where it doesn't. + + if( Database::instance().tempBackupExists() && + Database::instance().isDirty() && QMessageBox::question(this, QObject::tr("Save Database Changes"), QObject::tr("Would you like to save the changes you made?"), diff --git a/src/brewtarget.cpp b/src/brewtarget.cpp index f5eaf3fcb..65fca31a9 100644 --- a/src/brewtarget.cpp +++ b/src/brewtarget.cpp @@ -459,7 +459,7 @@ bool Brewtarget::instanceRunning() { if ( kill(pid, 0) == 0 ) { std::cerr << "Brewtarget is already running. PID: " << pid << std::endl; - return true; + return false; } } diff --git a/src/database.cpp b/src/database.cpp index cfbab634f..4c5162b58 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -203,21 +203,29 @@ bool Database::loadSQLite() else { // NOTE: synchronous=off reduces query time by an order of magnitude! - QSqlQuery( "PRAGMA synchronous = off", sqldb); - QSqlQuery( "PRAGMA foreign_keys = on", sqldb); - QSqlQuery( "PRAGMA locking_mode = EXCLUSIVE", sqldb); - // Store temporary tables in memory. - QSqlQuery( "PRAGMA temp_store = MEMORY", sqldb); - - // older sqlite databases may not have a settings table. I think I will - // just check to see if anything is in there. - createFromScratch = sqldb.tables().size() == 0; - - // Associate this db with the current thread. - _threadToConnection.insert(QThread::currentThread(), sqldb.connectionName()); - _threadToConnectionMutex.unlock(); + QSqlQuery pragma(sqldb); + try { + if ( ! pragma.exec( "PRAGMA synchronous = off" ) ) + throw QString("could not disable synchronous writes"); + if ( ! pragma.exec( "PRAGMA foreign_keys = on")) + throw QString("could not enable foreign keys"); + if ( ! pragma.exec( "PRAGMA locking_mode = EXCLUSIVE")) + throw QString("could not enable exclusive locks"); + if ( ! pragma.exec("PRAGMA temp_store = MEMORY") ) + throw QString("could not enable temporary memory"); + + // older sqlite databases may not have a settings table. I think I will + // just check to see if anything is in there. + createFromScratch = sqldb.tables().size() == 0; + + // Associate this db with the current thread. + _threadToConnection.insert(QThread::currentThread(), sqldb.connectionName()); + } + catch(QString e) { + Brewtarget::logE( QString("%1: %2 (%3)").arg(Q_FUNC_INFO).arg(e).arg(pragma.lastError().text())); + dbIsOpen = false; + } } - return dbIsOpen; } @@ -233,7 +241,6 @@ bool Database::loadPgSQL() dbUsername = Brewtarget::option("dbUsername").toString(); - // Yeah, this is a bad idea. Please make this anything other than this before rolling this out if ( Brewtarget::hasOption("dbPassword") ) { dbPassword = Brewtarget::option("dbPassword").toString(); } @@ -274,7 +281,6 @@ bool Database::loadPgSQL() createFromScratch = ! sqldb.tables().contains("settings"); // Associate this db with the current thread. _threadToConnection.insert(QThread::currentThread(), sqldb.connectionName()); - _threadToConnectionMutex.unlock(); } return dbIsOpen; @@ -297,6 +303,7 @@ bool Database::load() dbIsOpen = loadSQLite(); } + _threadToConnectionMutex.unlock(); if ( ! dbIsOpen ) return false; @@ -572,8 +579,9 @@ void Database::unload(bool keepChanges) { // If load() failed or want to keep the changes, then // just keep the database and don't revert to the backup. - if (dbFile.exists()) dbTempBackupFile.remove(); - return; + if (dbFile.exists()) + dbTempBackupFile.remove(); + return; } // If the user doesn't want to save changes, remove the active database // and restore the backup. @@ -664,6 +672,10 @@ bool Database::restoreFromFile(QString newDbFileStr) return success; } +bool Database::tempBackupExists() { + return dbTempBackupFile.exists(); +} + // removeFromRecipe =========================================================== void Database::removeIngredientFromRecipe( Recipe* rec, BeerXMLElement* ing ) { @@ -4700,6 +4712,25 @@ bool Database::cleanupBackupDatabase() // Check if the primary database also exists. if (QFile::exists(dbFileName)) { + { + QSqlDatabase testdb; + + testdb = QSqlDatabase::addDatabase("QSQLITE", "test"); + testdb.setDatabaseName(dbFileName); + + // If we can't test open the db, bail out + if ( ! testdb.open() ) + return false; + + QSqlQuery testConn(testdb); + // if we cannot execute this query, something is wrong and simply + // don't do anything + if ( ! testConn.exec("PRAGMA synchronous = off" ) ) { + Brewtarget::logE( QString("%1 : could not access database. Do you have another instance open?" ).arg(Q_FUNC_INFO) ); + return false; + } + testdb.close(); + } // If it does, prompt the user as to whether they'd like to keep their changes // from the last session (keep the primary DB), or revert back to where they // were before their changes (overwrite the primary DB with the backup). diff --git a/src/database.h b/src/database.h index d9fddceb3..88d7baca9 100644 --- a/src/database.h +++ b/src/database.h @@ -422,6 +422,7 @@ class Database : public QObject bool isConverted(); + bool tempBackupExists(); //! \brief Figures out what databases we are copying to and from, opens what // needs opens and then calls the appropriate workhorse to get it done. void convertDatabase(QString const& Hostname, QString const& DbName, From acf07570341fc92d9a0c328e63faf72ef72dc486 Mon Sep 17 00:00:00 2001 From: Mik Firestone Date: Fri, 27 May 2016 22:08:00 -0400 Subject: [PATCH 3/4] No more dirty bits Which sounds so much more fun. I've removed the temporary backups and all the dirty bit signalling. Writes are permanent now. Wonder how we will get that to the users? --- src/MainWindow.cpp | 36 +------ src/MainWindow.h | 5 - src/OptionDialog.cpp | 9 +- src/brewtarget.cpp | 43 +------- src/database.cpp | 252 +++---------------------------------------- src/database.h | 19 +--- ui/mainWindow.ui | 14 ++- 7 files changed, 32 insertions(+), 346 deletions(-) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index fb069d8c9..ea45c959e 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -419,13 +419,11 @@ MainWindow::MainWindow(QWidget* parent) if ( Brewtarget::dbType() == Brewtarget::PGSQL ) { actionBackup_Database->setEnabled(false); actionRestore_Database->setEnabled(false); - actionSave->setEnabled(false); label_Brewtarget->setToolTip( recipeFormatter->getLabelToolTip()); } else { connect( actionBackup_Database, SIGNAL( triggered() ), this, SLOT( backup() ) ); connect( actionRestore_Database, SIGNAL( triggered() ), this, SLOT( restoreFromBackup() ) ); - connect( actionSave, SIGNAL(triggered()), this, SLOT(save()) ); } // Printing signals/slots. // Refactoring is good. It's like a rye saison fermenting away @@ -494,14 +492,12 @@ MainWindow::MainWindow(QWidget* parent) // No connections from the database yet? Oh FSM, that probably means I'm // doing it wrong again. connect( &(Database::instance()), SIGNAL( deletedSignal(BrewNote*)), this, SLOT( closeBrewNote(BrewNote*))); - connect( &(Database::instance()), SIGNAL( isUnsavedChanged(bool)), this, SLOT( updateUnsavedStatus(bool))); } void MainWindow::setupShortCuts() { actionNewRecipe->setShortcut(QKeySequence::New); actionCopy_Recipe->setShortcut(QKeySequence::Copy); - actionSave->setShortcut(QKeySequence::Save); actionDeleteSelected->setShortcut(QKeySequence::Delete); } @@ -1907,11 +1903,6 @@ void MainWindow::removeMash() } -void MainWindow::save() -{ - Database::instance().saveDatabase(); -} - void MainWindow::closeEvent(QCloseEvent* /*event*/) { Brewtarget::saveSystemOptions(); @@ -1942,21 +1933,7 @@ void MainWindow::closeEvent(QCloseEvent* /*event*/) // We should also make sure the backup db still exists -- there's some edge // cases where it doesn't. - if( Database::instance().tempBackupExists() && - Database::instance().isDirty() && - QMessageBox::question(this, - QObject::tr("Save Database Changes"), - QObject::tr("Would you like to save the changes you made?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::Yes) - == QMessageBox::Yes) - { - Database::instance().unload(true); - } - else - { - Database::instance().unload(false); - } + Database::instance().unload(); } void MainWindow::copyRecipe() @@ -2452,17 +2429,6 @@ void MainWindow::updateStatus(const QString status) { statusBar()->showMessage(status, 3000); } -void MainWindow::updateUnsavedStatus(bool isUnsaved) { - if ( isUnsaved ) { - statusBar()->showMessage(tr("Unsaved Changes")); - actionSave->setIcon(QIcon(SAVEDIRTYPNG)); - } - else { - statusBar()->clearMessage(); - actionSave->setIcon(QIcon(SAVEPNG)); - } -} - void MainWindow::closeBrewNote(BrewNote* b) { Recipe* parent = Database::instance().getParentRecipe(b); diff --git a/src/MainWindow.h b/src/MainWindow.h index a61f5e206..45200b0cd 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -143,8 +143,6 @@ public slots: //! \brief Update the main windows statusbar. void updateStatus(const QString status); - //! \brief Change unsaved status. - void updateUnsavedStatus(bool isUnsaved); //! \brief Close a brewnote tab if we must void closeBrewNote(BrewNote*); @@ -217,9 +215,6 @@ public slots: //! \brief Prints the right thing, depending on the signal sender. void print(); - //! \brief saves the database, which will have some interesting - //implications later - void save(); //! \brief Backup the database. void backup(); //! \brief Restore the database. diff --git a/src/OptionDialog.cpp b/src/OptionDialog.cpp index 46100be6d..961a20247 100644 --- a/src/OptionDialog.cpp +++ b/src/OptionDialog.cpp @@ -686,6 +686,7 @@ void OptionDialog::createSQLiteElements() void OptionDialog::retranslateDbDialog(QDialog *optionsDialog) { + //PostgreSQL stuff label_hostname->setText(QApplication::translate("optionsDialog", "Hostname", 0)); label_portnum->setText(QApplication::translate("optionsDialog", "Port", 0)); label_schema->setText(QApplication::translate("optionsDialog", "Schema", 0)); @@ -693,7 +694,9 @@ void OptionDialog::retranslateDbDialog(QDialog *optionsDialog) label_username->setText(QApplication::translate("optionsDialog", "Username", 0)); label_password->setText(QApplication::translate("optionsDialog", "Password", 0)); checkBox_savePassword->setText(QApplication::translate("optionsDialog", "Save password", 0)); - label_dataDir->setText(QApplication::translate("optionsDialog", "Backup Directory", 0)); + + // SQLite things + label_dataDir->setText(QApplication::translate("optionsDialog", "Data Directory", 0)); pushButton_browseDataDir->setText(QApplication::translate("optionsDialog", "Browse", 0)); pushButton_defaultDataDir->setText(QApplication::translate("optionsDialog", "Default", 0)); label_backupDir->setText(QApplication::translate("optionsDialog", "Backup Directory", 0)); @@ -712,8 +715,8 @@ void OptionDialog::retranslateDbDialog(QDialog *optionsDialog) btStringEdit_dbname->setToolTip(QApplication::translate("optionsDialog", "The name of the database", 0)); label_dataDir->setToolTip(QApplication::translate("optionsDialog", "Where your database file is",0)); label_backupDir->setToolTip(QApplication::translate("optionsDialog", "Where to save your backups",0)); - label_numBackups->setToolTip(QApplication::translate("optionsDialog", "Number of backups to keep", 0)); - label_frequency->setToolTip(QApplication::translate("optionsDialog", "How frequently a backup is made", 0)); + label_numBackups->setToolTip(QApplication::translate("optionsDialog", "Number of backups to keep: -1 means never remove, 0 means never backup", 0)); + label_frequency->setToolTip(QApplication::translate("optionsDialog", "How frequently a backup is made: 1 means always backup", 0)); #endif } diff --git a/src/brewtarget.cpp b/src/brewtarget.cpp index e2de75e68..70886615e 100644 --- a/src/brewtarget.cpp +++ b/src/brewtarget.cpp @@ -87,7 +87,6 @@ QDomDocument* Brewtarget::optionsDoc; QTranslator* Brewtarget::defaultTrans = new QTranslator(); QTranslator* Brewtarget::btTrans = new QTranslator(); bool Brewtarget::userDatabaseDidNotExist = false; -QFile Brewtarget::pidFile; bool Brewtarget::_isInteractive = true; QDateTime Brewtarget::lastDbMergeRequest = QDateTime::fromString("1986-02-24T06:00:00", Qt::ISODate); @@ -414,10 +413,6 @@ bool Brewtarget::initialize(const QString &userDirectory) loadMap(); log.changeDirectory(getUserDataDir()); - // Make sure we're the only instance if we're using SQLite - if ( instanceRunning() && dbType() == Brewtarget::SQLITE) - return false; - // Make sure all the necessary directories and files we need exist before starting. ensureDirectoriesExist(); @@ -440,42 +435,8 @@ bool Brewtarget::initialize(const QString &userDirectory) return false; } -bool Brewtarget::instanceRunning() { - // In Unix, make sure the user isn't running 2 copies. -#if defined(Q_OS_UNIX) - pidFile.setFileName(QString("%1.pid").arg(getUserDataDir().canonicalPath())); - if( pidFile.exists() ) - { - // Read the pid. - qint64 pid; - pidFile.open(QIODevice::ReadOnly); - { - QTextStream pidStream(&pidFile); - pidStream >> pid; - } - pidFile.close(); - - // kill(2) with sig=0 does existence error checking w/o sending any real signal. - if ( kill(pid, 0) == 0 ) - { - std::cerr << "Brewtarget is already running. PID: " << pid << std::endl; - return false; - } - } - - // Open the pidFile, erasing any contents, and write our pid. - pidFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - { - QTextStream pidStream(&pidFile); - pidStream << QCoreApplication::applicationPid(); - } - pidFile.close(); - -#endif - return false; -} - -Brewtarget::DBTypes Brewtarget::dbType() { +Brewtarget::DBTypes Brewtarget::dbType() +{ if ( _dbType == Brewtarget::NODB ) _dbType = (Brewtarget::DBTypes)option("dbType", dbType()).toInt(); return _dbType; diff --git a/src/database.cpp b/src/database.cpp index 3d0f439dd..34b6cfd90 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -85,8 +85,6 @@ QFile Database::dbFile; QString Database::dbFileName; QFile Database::dataDbFile; QString Database::dataDbFileName; -QFile Database::dbTempBackupFile; -QString Database::dbTempBackupFileName; QString Database::dbConName; QHash Database::tableNames; QHash Database::classNameToTable; @@ -104,7 +102,6 @@ Database::Database() _threadToConnectionMutex.lock(); converted = false; - dirty = false; loadWasSuccessful = load(); } @@ -114,7 +111,7 @@ Database::~Database() // If we have not explicitly unloaded, do so now and discard changes. if( QSqlDatabase::database( dbConName, false ).isOpen() ) - unload(false); + unload(); // Delete all the ingredients floating around. qDeleteAll(allBrewNotes); @@ -141,16 +138,9 @@ bool Database::loadSQLite() dbFileName = Brewtarget::getUserDataDir().filePath("database.sqlite"); dataDbFileName = Brewtarget::getDataDir().filePath("default_db.sqlite"); - dbTempBackupFileName = Brewtarget::getUserDataDir().filePath("tempBackupDatabase.sqlite"); - // Set the files. dbFile.setFileName(dbFileName); dataDbFile.setFileName(dataDbFileName); - dbTempBackupFile.setFileName(dbTempBackupFileName); - - // Cleanup the backup database if there was a previous error. - if( !cleanupBackupDatabase() ) - return false; // If user restored the database from a backup, make the backup into the primary. { @@ -180,9 +170,6 @@ bool Database::loadSQLite() Brewtarget::lastDbMergeRequest = QDateTime::currentDateTime(); } - // Create a copy of the database to revert to if the user decides not to make changes. - dbFile.copy(dbTempBackupFileName); - // Open SQLite db. sqldb = QSqlDatabase::addDatabase("QSQLITE"); sqldb.setDatabaseName(dbFileName); @@ -425,11 +412,6 @@ bool Database::load() connect( *n, SIGNAL(changed(QMetaProperty,QVariant)), *m, SLOT(acceptMashStepChange(QMetaProperty,QVariant)) ); } - // The database MUST be saved if we created from scratch. - // It SHOULD be saved if the schema was updated. - dirty = createFromScratch | schemaUpdated; - emit isUnsavedChanged(dirty); - return true; } @@ -495,15 +477,6 @@ void Database::convertFromXml() } } Brewtarget::setOption("converted", QDate().currentDate().toString()); - saveDatabase(); -} - -void Database::saveDatabase() -{ - dbTempBackupFile.remove(); - dbFile.copy(dbTempBackupFileName); - dirty = false; - emit isUnsavedChanged(false); } bool Database::isConverted() @@ -565,7 +538,7 @@ QSqlDatabase Database::sqlDatabase() return sqldb; } -void Database::unload(bool keepChanges) +void Database::unload() { // The postgres driver wants nothing to do with this. Core gets dumped if // we try it. Since we don't need to copy things about for postgres... @@ -576,24 +549,9 @@ void Database::unload(bool keepChanges) QSqlDatabase::removeDatabase( dbConName ); - if (!loadWasSuccessful || keepChanges) - { - // If load() failed or want to keep the changes, then - // just keep the database and don't revert to the backup. - if (dbFile.exists()) - dbTempBackupFile.remove(); - - // We only want to make a backup when the user persists the changes. I - // think? - if ( keepChanges ) - automaticBackup(); - return; - } - // If the user doesn't want to save changes, remove the active database - // and restore the backup. dbFile.close(); - dbFile.remove(); - dbTempBackupFile.rename(dbFileName); + if (loadWasSuccessful) + automaticBackup(); } } @@ -674,11 +632,6 @@ void Database::automaticBackup() Brewtarget::setOption( "files", listOfFiles, "backups"); } -bool Database::isDirty() -{ - return dirty; -} - Database& Database::instance() { @@ -762,10 +715,6 @@ bool Database::restoreFromFile(QString newDbFileStr) return success; } -bool Database::tempBackupExists() { - return dbTempBackupFile.exists(); -} - // removeFromRecipe =========================================================== void Database::removeIngredientFromRecipe( Recipe* rec, BeerXMLElement* ing ) { @@ -834,7 +783,6 @@ void Database::removeIngredientFromRecipe( Recipe* rec, BeerXMLElement* ing ) sqlDatabase().commit(); q.finish(); - makeDirty(); emit rec->changed( rec->metaProperty(propName), QVariant() ); } @@ -866,7 +814,6 @@ void Database::removeFrom( Mash* mash, MashStep* step ) throw; } - makeDirty(); emit mash->mashStepsChanged(); } @@ -929,7 +876,6 @@ void Database::swapMashStepOrder(MashStep* m1, MashStep* m2) q.finish(); - makeDirty(); emit m1->changed( m1->metaProperty("stepNumber") ); emit m2->changed( m2->metaProperty("stepNumber") ); } @@ -964,7 +910,6 @@ void Database::swapInstructionOrder(Instruction* in1, Instruction* in2) q.finish(); - makeDirty(); emit in1->changed( in1->metaProperty("instructionNumber") ); emit in2->changed( in2->metaProperty("instructionNumber") ); } @@ -1037,7 +982,6 @@ void Database::insertInstruction(Instruction* in, int pos) sqlDatabase().commit(); q.finish(); - makeDirty(); emit in->changed( in->metaProperty("instructionNumber"), pos ); } @@ -1165,7 +1109,6 @@ BrewNote* Database::newBrewNote(BrewNote* other, bool signal) emit newBrewNoteSignal(tmp); } - makeDirty(); } return tmp; } @@ -1197,7 +1140,6 @@ BrewNote* Database::newBrewNote(Recipe* parent, bool signal) emit newBrewNoteSignal(tmp); } - makeDirty(); return tmp; } @@ -1211,7 +1153,6 @@ Equipment* Database::newEquipment(Equipment* other) tmp = newIngredient(&allEquipments); if ( tmp ) { - makeDirty(); emit changed( metaProperty("equipments"), QVariant() ); emit newEquipmentSignal(tmp); } @@ -1232,7 +1173,6 @@ Fermentable* Database::newFermentable(Fermentable* other) tmp = newIngredient(&allFermentables); if ( tmp ) { - makeDirty(); emit changed( metaProperty("fermentables"), QVariant() ); emit newFermentableSignal(tmp); } @@ -1253,7 +1193,6 @@ Hop* Database::newHop(Hop* other) tmp = newIngredient(&allHops); if ( tmp ) { - makeDirty(); emit changed( metaProperty("hops"), QVariant() ); emit newHopSignal(tmp); } @@ -1288,7 +1227,6 @@ Instruction* Database::newInstruction(Recipe* rec) // Database's instructions have changed. sqlDatabase().commit(); - makeDirty(); emit changed( metaProperty("instructions"), QVariant() ); return tmp; @@ -1346,7 +1284,6 @@ Mash* Database::newMash(Mash* other, bool displace) if ( other ) sqlDatabase().commit(); - makeDirty(); emit changed( metaProperty("mashs"), QVariant() ); emit newMashSignal(tmp); @@ -1378,7 +1315,6 @@ Mash* Database::newMash(Recipe* parent, bool transact) if ( transact ) sqlDatabase().commit(); - makeDirty(); emit changed( metaProperty("mashs"), QVariant() ); emit newMashSignal(tmp); @@ -1431,7 +1367,6 @@ MashStep* Database::newMashStep(Mash* mash, bool connected) if ( connected ) connect( tmp, SIGNAL(changed(QMetaProperty,QVariant)), mash, SLOT(acceptMashStepChange(QMetaProperty,QVariant)) ); - makeDirty(); emit changed( metaProperty("mashs"), QVariant() ); emit mash->mashStepsChanged(); return tmp; @@ -1447,7 +1382,6 @@ Misc* Database::newMisc(Misc* other) tmp = newIngredient(&allMiscs); if ( tmp ) { - makeDirty(); emit changed( metaProperty("miscs"), QVariant() ); emit newMiscSignal(tmp); } @@ -1478,7 +1412,6 @@ Recipe* Database::newRecipe() } sqlDatabase().commit(); - makeDirty(); emit changed( metaProperty("recipes"), QVariant() ); emit newRecipeSignal(tmp); @@ -1517,7 +1450,6 @@ Recipe* Database::newRecipe(Recipe* other) } sqlDatabase().commit(); - makeDirty(); emit changed( metaProperty("recipes"), QVariant() ); emit newRecipeSignal(tmp); @@ -1540,7 +1472,6 @@ Style* Database::newStyle(Style* other) throw; } - makeDirty(); emit changed( metaProperty("styles"), QVariant() ); emit newStyleSignal(tmp); @@ -1563,7 +1494,6 @@ Water* Database::newWater(Water* other) throw; } - makeDirty(); emit changed( metaProperty("waters"), QVariant() ); emit newWaterSignal(tmp); @@ -1586,8 +1516,6 @@ Yeast* Database::newYeast(Yeast* other) throw; } - - makeDirty(); emit changed( metaProperty("yeasts"), QVariant() ); emit newYeastSignal(tmp); @@ -1609,7 +1537,6 @@ void Database::deleteRecord( Brewtarget::DBTable table, BeerXMLElement* object ) throw; } - makeDirty(); } // NOTE: This really should be in a transaction, but I am going to leave that @@ -1641,7 +1568,6 @@ void Database::duplicateMashSteps(Mash *oldMash, Mash *newMash) throw; } - makeDirty(); emit changed( metaProperty("mashs"), QVariant() ); emit newMash->mashStepsChanged(); @@ -1696,8 +1622,6 @@ void Database::updateEntry( Brewtarget::DBTable table, int key, const char* col_ if ( notify ) emit object->changed(prop,value); - makeDirty(); - } // Inventory functions ======================================================== @@ -1878,10 +1802,9 @@ void Database::addToRecipe( Recipe* rec, Equipment* e, bool noCopy, bool transac } // This is likely illadvised. But if you are telling me to not transact it, - // it is up to you to mark the DB as dirty. This should reduce signals + // it is up to you to commit the changes if ( transact ) { sqlDatabase().commit(); - makeDirty(); } // NOTE: need to disconnect the recipe's old equipment? connect( newEquip, SIGNAL(changed(QMetaProperty,QVariant)), rec, SLOT(acceptEquipChange(QMetaProperty,QVariant)) ); @@ -1914,12 +1837,9 @@ void Database::addToRecipe( Recipe* rec, Fermentable* ferm, bool noCopy, bool tr } // If somebody upstream is doing the transaction, let them call recalcAll - // and makeDirty - if ( transact ) { - makeDirty(); - if (! noCopy ) - rec->recalcAll(); - } + if ( transact && ! noCopy ) + rec->recalcAll(); + } void Database::addToRecipe( Recipe* rec, QListferms, bool transact ) @@ -1947,7 +1867,6 @@ void Database::addToRecipe( Recipe* rec, QListferms, bool transact if ( transact ) { sqlDatabase().commit(); - makeDirty(); rec->recalcAll(); } } @@ -1960,7 +1879,6 @@ void Database::addToRecipe( Recipe* rec, Hop* hop, bool noCopy, bool transact ) connect( newHop, SIGNAL(changed(QMetaProperty,QVariant)), rec, SLOT(acceptHopChange(QMetaProperty,QVariant))); if ( transact ) { rec->recalcIBU(); - makeDirty(); } } catch (QString e) { @@ -1993,7 +1911,6 @@ void Database::addToRecipe( Recipe* rec, QListhops, bool transact ) if ( transact ) { sqlDatabase().commit(); - makeDirty(); rec->recalcIBU(); } } @@ -2028,7 +1945,6 @@ void Database::addToRecipe( Recipe* rec, Mash* m, bool noCopy, bool transact ) if ( transact ) { sqlDatabase().commit(); - makeDirty(); } connect( newMash, SIGNAL(changed(QMetaProperty,QVariant)), rec, SLOT(acceptMashChange(QMetaProperty,QVariant))); emit rec->changed( rec->metaProperty("mash"), BeerXMLElement::qVariantFromPtr(newMash) ); @@ -2046,11 +1962,8 @@ void Database::addToRecipe( Recipe* rec, Misc* m, bool noCopy, bool transact ) throw; } - if ( transact ) { - makeDirty(); - if (! noCopy ) - rec->recalcAll(); - } + if ( transact && ! noCopy ) + rec->recalcAll(); } @@ -2077,7 +1990,6 @@ void Database::addToRecipe( Recipe* rec, QListmiscs, bool transact ) } if ( transact ) { sqlDatabase().commit(); - makeDirty(); rec->recalcAll(); } } @@ -2092,11 +2004,8 @@ void Database::addToRecipe( Recipe* rec, Water* w, bool noCopy, bool transact ) throw; } - if ( transact ) { - makeDirty(); - if (! noCopy ) - rec->recalcAll(); - } + if ( transact && ! noCopy ) + rec->recalcAll(); } void Database::addToRecipe( Recipe* rec, Style* s, bool noCopy, bool transact ) @@ -2127,7 +2036,6 @@ void Database::addToRecipe( Recipe* rec, Style* s, bool noCopy, bool transact ) if ( transact ) { sqlDatabase().commit(); - makeDirty(); } // Emit a changed signal. emit rec->changed( rec->metaProperty("style"), BeerXMLElement::qVariantFromPtr(newStyle) ); @@ -2138,13 +2046,10 @@ void Database::addToRecipe( Recipe* rec, Yeast* y, bool noCopy, bool transact ) try { Yeast* newYeast = addIngredientToRecipe( rec, y, noCopy, &allYeasts, true, transact ); connect( newYeast, SIGNAL(changed(QMetaProperty,QVariant)), rec, SLOT(acceptYeastChange(QMetaProperty,QVariant))); - if ( transact ) { - makeDirty(); - if ( ! noCopy ) - { - rec->recalcOgFg(); - rec->recalcABV_pct(); - } + if ( transact && ! noCopy ) + { + rec->recalcOgFg(); + rec->recalcABV_pct(); } } catch (QString e) { @@ -2176,7 +2081,6 @@ void Database::addToRecipe( Recipe* rec, QListyeasts, bool transact ) if ( transact ) { sqlDatabase().commit(); - makeDirty(); rec->recalcOgFg(); rec->recalcABV_pct(); } @@ -2202,7 +2106,6 @@ void Database::sqlUpdate( Brewtarget::DBTable table, QString const& setClause, Q } q.finish(); - makeDirty(); } void Database::sqlDelete( Brewtarget::DBTable table, QString const& whereClause ) @@ -2223,7 +2126,6 @@ void Database::sqlDelete( Brewtarget::DBTable table, QString const& whereClause } q.finish(); - makeDirty(); } QHash Database::selectAllHash() @@ -2390,7 +2292,6 @@ bool Database::updateSchema(bool* err) *err = true; return false; } - makeDirty(); } sqlDatabase().transaction(); @@ -3832,7 +3733,6 @@ void Database::fromXml(BeerXMLElement* element, QHash const& xm } } - makeDirty(); } // Brewnotes can never be created w/ a recipe, so we will always assume the @@ -4786,116 +4686,6 @@ Yeast* Database::yeastFromXml( QDomNode const& node, Recipe* parent ) return ret; } -/* - * Cleans up the backup database if an error occurred - * during the previous Brewtarget session. - */ -bool Database::cleanupBackupDatabase() -{ - // there are no backup databases in postgresql - if ( Brewtarget::dbType() == Brewtarget::PGSQL ) - return true; - - // Check if the temporary backup database exists. - if (QFile::exists(dbTempBackupFileName)) - { - // Check if the primary database also exists. - if (QFile::exists(dbFileName)) - { - { - QSqlDatabase testdb; - - testdb = QSqlDatabase::addDatabase("QSQLITE", "test"); - testdb.setDatabaseName(dbFileName); - - // If we can't test open the db, bail out - if ( ! testdb.open() ) - return false; - - QSqlQuery testConn(testdb); - // if we cannot execute this query, something is wrong and simply - // don't do anything - if ( ! testConn.exec("PRAGMA synchronous = off" ) ) { - Brewtarget::logE( QString("%1 : could not access database. Do you have another instance open?" ).arg(Q_FUNC_INFO) ); - return false; - } - testdb.close(); - } - // If it does, prompt the user as to whether they'd like to keep their changes - // from the last session (keep the primary DB), or revert back to where they - // were before their changes (overwrite the primary DB with the backup). - QMessageBox messageBox; - messageBox.setIcon(QMessageBox::Question); - messageBox.setWindowTitle(QObject::tr("Multiple Databases Found")); - messageBox.setText(QObject::tr("Multiple databases were found. Do you want to " - "restore the changes you made during your last " - "Brewtarget session, or rollback to before last session's changes?")); - - // Add the Restore and Rollback buttons. - QPushButton *restoreButton = messageBox.addButton(QObject::tr("Restore"), QMessageBox::AcceptRole); - messageBox.addButton(QObject::tr("Rollback"), QMessageBox::RejectRole); - messageBox.setDefaultButton(restoreButton); - - // Display the message box. - messageBox.exec(); - - // Check which button the user clicked. - if (messageBox.clickedButton() == restoreButton) - { - // If they clicked Restore, then simply remove the backup database and keep - // the primary. If that fails, display a message as something likely has - // the file locked, and it needs to be resolved outside Brewtarget. - if (!QFile::remove(dbTempBackupFileName)) - { - QMessageBox::critical(0, - "Database Restore Failure", - QString(QObject::tr("Failed to remove the temporary backup database. " - "Navigate to '%1' and remove " - "'tempBackupDatabase.sqlite'.")).arg(Brewtarget::getUserDataDir().canonicalPath())); - - return false; - } - } - else - { - // If they clicked Rollback, replace the primary DB with the temporary backup - // database. If that fails, display a message as something likely has the - // file locked, and it needs to be cleaned up outside Brewtarget. - QFile::remove(dbFileName); - if (!QFile::rename(dbTempBackupFileName, dbFileName)) - { - QMessageBox::critical(0, - "Database Rollback Failure", - QString(QObject::tr("Failed to rollback to the backup database. " - "Navigate to '%1', remove 'database.sqlite' if it exists, " - "and rename 'tempBackupDatabase.sqlite' to " - "'database.sqlite'.")).arg(Brewtarget::getUserDataDir().canonicalPath())); - - return false; - } - } - } - else - { - // If the primary DB doesn't exist, then restore the backup database as the - // primary. If that fails, display a message as something likely has the - // file locked, and it needs to be cleaned up outside Brewtarget. - if (!QFile::rename(dbTempBackupFileName, dbFileName)) - { - QMessageBox::critical(0, - QObject::tr("Database Restore Failure"), - QString(QObject::tr("Failed to restore the backup database. Navigate to '%1' " - "and rename 'tempBackupDatabase.sqlite' to " - "'database.sqlite'.").arg(Brewtarget::getUserDataDir().canonicalPath()))); - - return false; - } - } - } - - return true; -} - //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QList Database::makeTableParams() @@ -5159,7 +4949,6 @@ void Database::updateDatabase(QString const& filename) // If we, by some miracle, get here, commit sqlDatabase().commit(); // I think - makeDirty(); } catch (QString e) { Brewtarget::logE(QString("%1 %2").arg(Q_FUNC_INFO).arg(e)); @@ -5169,15 +4958,6 @@ void Database::updateDatabase(QString const& filename) } } -void Database::makeDirty() -{ - // databases are updated automagically for postgres - if ( Brewtarget::dbType() != Brewtarget::PGSQL ) { - dirty = true; - emit isUnsavedChanged(true); - } -} - bool Database::verifyDbConnection(Brewtarget::DBTypes testDb, QString const& hostname, int portnum, QString const& schema, QString const& database, QString const& username, QString const& password) { diff --git a/src/database.h b/src/database.h index dc994f15d..0456b5cd7 100644 --- a/src/database.h +++ b/src/database.h @@ -95,7 +95,7 @@ class Database : public QObject //! Call this to delete the internal instance. static void dropInstance(); //! \brief Should be called when we are about to close down. - void unload(bool keepChanges = true); + void unload(); //! \brief Create a blank database in the given file static bool createBlank(QString const& filename); @@ -113,7 +113,6 @@ class Database : public QObject QString const& username="brewtarget", QString const& password="brewtarget"); bool loadSuccessful(); - bool isDirty(); /*! update an entry, and call the notification when complete. * NOTE: This cannot be simplified without a bit more work. The inventory @@ -416,13 +415,10 @@ class Database : public QObject * database file. */ void updateDatabase(QString const& filename); - void saveDatabase(); void convertFromXml(); - bool isConverted(); - bool tempBackupExists(); //! \brief Figures out what databases we are copying to and from, opens what // needs opens and then calls the appropriate workhorse to get it done. void convertDatabase(QString const& Hostname, QString const& DbName, @@ -456,8 +452,6 @@ class Database : public QObject // MashSteps need signals too void newMashStepSignal(MashStep*); - // Emits a signal when the dirty status changes - void isUnsavedChanged(bool); private slots: //! Load database from file. @@ -472,8 +466,6 @@ private slots: static QString dbFileName; static QFile dataDbFile; static QString dataDbFileName; - static QFile dbTempBackupFile; - static QString dbTempBackupFileName; static QString dbConName; // And these are for Postgres databases -- are these really required? Are @@ -505,7 +497,6 @@ private slots: // Instance variables. bool loadWasSuccessful; bool converted; - bool dirty; bool createFromScratch; bool schemaUpdated; @@ -888,20 +879,12 @@ private slots: int getQualifiedMiscUseIndex(QString use, Misc* misc); int getQualifiedHopUseIndex(QString use, Hop* hop); - // Cleans up the backup database if it was leftover from an error. - bool cleanupBackupDatabase(); - static QList makeTableParams(); // Returns true if the schema gets updated, false otherwise. // If err != 0, set it to true if an error occurs, false otherwise. bool updateSchema(bool* err = 0); - /*! - * \brief Register that the DB was modified. - */ - void makeDirty(); - // May St. Stevens intercede on my behalf. // //! \brief opens an SQLite db for transfer diff --git a/ui/mainWindow.ui b/ui/mainWindow.ui index c9935b396..1b2f23d66 100644 --- a/ui/mainWindow.ui +++ b/ui/mainWindow.ui @@ -234,7 +234,7 @@ 0 0 636 - 412 + 386 @@ -837,15 +837,15 @@ + + 0 + 4 5 - - 0 - @@ -1097,7 +1097,7 @@ 0 0 636 - 323 + 337 @@ -1649,7 +1649,7 @@ 0 0 1050 - 21 + 19 @@ -1695,7 +1695,6 @@ - @@ -1758,7 +1757,6 @@ false - From 46a3b7660a76b64e71db1bbf2d176ca223855025 Mon Sep 17 00:00:00 2001 From: Mik Firestone Date: Sat, 28 May 2016 00:31:03 -0400 Subject: [PATCH 4/4] Option dialog doesn't look too bad now Added one single default button, moved the "browse" buttons along side the text input and rebalanced the screens a little. I think it looks much better. The single default button restores all fields in the displayed database dialog to their default values. This is true for both SQLite and PostgreSQL dialogs. Also fixed a typo/leftover from the last commit --- src/OptionDialog.cpp | 81 ++++++++++++++++++------------------- src/OptionDialog.h | 5 +-- src/brewtarget.cpp | 3 -- ui/optionsDialog.ui | 96 ++++++++++++++++++++++++++++---------------- 4 files changed, 101 insertions(+), 84 deletions(-) diff --git a/src/OptionDialog.cpp b/src/OptionDialog.cpp index 961a20247..7a34f0a72 100644 --- a/src/OptionDialog.cpp +++ b/src/OptionDialog.cpp @@ -169,9 +169,8 @@ OptionDialog::OptionDialog(QWidget* parent) connect( btStringEdit_password, SIGNAL( textModified() ), this, SLOT(testRequired())); connect( pushButton_browseDataDir, SIGNAL( clicked() ), this, SLOT( setDataDir() ) ); - connect( pushButton_defaultDataDir, SIGNAL( clicked() ), this, SLOT( defaultDataDir() ) ); connect( pushButton_browseBackupDir, SIGNAL( clicked() ), this, SLOT( setBackupDir() ) ); - connect( pushButton_defaultBackupDir, SIGNAL( clicked() ), this, SLOT( defaultBackupDir() ) ); + connect( pushButton_resetToDefault, SIGNAL( clicked() ), this, SLOT( resetToDefault() ) ); pushButton_testConnection->setEnabled(false); } @@ -234,14 +233,25 @@ void OptionDialog::setBackupDir() btStringEdit_backupDir->setText( dir ); } -void OptionDialog::defaultDataDir() +void OptionDialog::resetToDefault() { - btStringEdit_dataDir->setText( Brewtarget::getConfigDir().canonicalPath() ); -} -void OptionDialog::defaultBackupDir() -{ - btStringEdit_backupDir->setText( Brewtarget::getConfigDir().canonicalPath() ); + Brewtarget::DBTypes engine = (Brewtarget::DBTypes)comboBox_engine->currentIndex(); + if ( engine == Brewtarget::PGSQL ) { + btStringEdit_hostname->setText(QString("localhost")); + btStringEdit_portnum->setText(QString("5432")); + btStringEdit_schema->setText(QString("public")); + btStringEdit_dbname->setText(QString("brewtarget")); + btStringEdit_username->setText(QString("brewtarget")); + btStringEdit_password->setText(QString("")); + checkBox_savePassword->setChecked(false); + } + else { + btStringEdit_dataDir->setText( Brewtarget::getConfigDir().canonicalPath() ); + btStringEdit_backupDir->setText( Brewtarget::getConfigDir().canonicalPath() ); + spinBox_frequency->setValue(4); + spinBox_numBackups->setValue(10); + } } void OptionDialog::saveAndClose() @@ -510,11 +520,9 @@ void OptionDialog::sqliteVisible(bool canSee) btStringEdit_dataDir->setVisible(canSee); pushButton_browseDataDir->setVisible(canSee); - pushButton_defaultDataDir->setVisible(canSee); label_backupDir->setVisible(canSee); btStringEdit_backupDir->setVisible(canSee); pushButton_browseBackupDir->setVisible(canSee); - pushButton_defaultBackupDir->setVisible(canSee); label_numBackups->setVisible(canSee); spinBox_numBackups->setVisible(canSee); @@ -533,24 +541,24 @@ void OptionDialog::setDbDialog(Brewtarget::DBTypes db) sqliteVisible(false); gridLayout->addWidget(label_hostname,0,0); - gridLayout->addWidget(btStringEdit_hostname,0,1); + gridLayout->addWidget(btStringEdit_hostname,0,1,1,2); - gridLayout->addWidget(label_portnum,1,0); - gridLayout->addWidget(btStringEdit_portnum,1,1); + gridLayout->addWidget(label_portnum,0,3); + gridLayout->addWidget(btStringEdit_portnum,0,4); - gridLayout->addWidget(label_schema,2,0); - gridLayout->addWidget(btStringEdit_schema,2,1); + gridLayout->addWidget(label_schema,1,0); + gridLayout->addWidget(btStringEdit_schema,1,1); - gridLayout->addWidget(label_dbName,3,0); - gridLayout->addWidget(btStringEdit_dbname,3,1); + gridLayout->addWidget(label_dbName,2,0); + gridLayout->addWidget(btStringEdit_dbname,2,1); - gridLayout->addWidget(label_username,4,0); - gridLayout->addWidget(btStringEdit_username,4,1); + gridLayout->addWidget(label_username,3,0); + gridLayout->addWidget(btStringEdit_username,3,1); - gridLayout->addWidget(label_password,5,0); - gridLayout->addWidget(btStringEdit_password,5,1); + gridLayout->addWidget(label_password,4,0); + gridLayout->addWidget(btStringEdit_password,4,1); - gridLayout->addWidget(checkBox_savePassword, 5, 2); + gridLayout->addWidget(checkBox_savePassword, 4, 4); } else { @@ -558,22 +566,19 @@ void OptionDialog::setDbDialog(Brewtarget::DBTypes db) sqliteVisible(true); gridLayout->addWidget(label_dataDir,0,0); - gridLayout->addWidget(btStringEdit_dataDir,0,1,1,-1); - gridLayout->addWidget(pushButton_browseDataDir,1,2); - gridLayout->addWidget(pushButton_defaultDataDir,1,3); + gridLayout->addWidget(btStringEdit_dataDir,0,1,1,2); + gridLayout->addWidget(pushButton_browseDataDir,0,3); - gridLayout->addWidget(label_backupDir,2,0); - gridLayout->addWidget(btStringEdit_backupDir,2,1,1,-1); - gridLayout->addWidget(pushButton_browseBackupDir,3,2); - gridLayout->addWidget(pushButton_defaultBackupDir,3,3); + gridLayout->addWidget(label_backupDir,1,0); + gridLayout->addWidget(btStringEdit_backupDir,1,1,1,2); + gridLayout->addWidget(pushButton_browseBackupDir,1,3); - gridLayout->addWidget(label_numBackups,4,0); - gridLayout->addWidget(spinBox_numBackups,4,1); + gridLayout->addWidget(label_numBackups,3,0); + gridLayout->addWidget(spinBox_numBackups,3,1); - gridLayout->addWidget(label_frequency,5,0); - gridLayout->addWidget(spinBox_frequency,5,1); + gridLayout->addWidget(label_frequency,4,0); + gridLayout->addWidget(spinBox_frequency,4,1); } - groupBox_dbConfig->setVisible(true); } @@ -645,9 +650,6 @@ void OptionDialog::createSQLiteElements() pushButton_browseDataDir = new QPushButton(groupBox_dbConfig); pushButton_browseDataDir->setObjectName(QStringLiteral("button_browseDataDir")); - pushButton_defaultDataDir = new QPushButton(groupBox_dbConfig); - pushButton_defaultDataDir->setObjectName(QStringLiteral("button_defaultDataDir")); - // Set up the backup directory dialog and buttons label_backupDir = new QLabel(groupBox_dbConfig); label_backupDir->setObjectName(QStringLiteral("label_backupDir")); @@ -658,9 +660,6 @@ void OptionDialog::createSQLiteElements() pushButton_browseBackupDir = new QPushButton(groupBox_dbConfig); pushButton_browseBackupDir->setObjectName(QStringLiteral("button_browseBackupDir")); - pushButton_defaultBackupDir = new QPushButton(groupBox_dbConfig); - pushButton_defaultBackupDir->setObjectName(QStringLiteral("button_defaultBackupDir")); - // Set up the two spin boxes label_numBackups = new QLabel(groupBox_dbConfig); label_numBackups->setObjectName(QStringLiteral("label_numBackups")); @@ -698,10 +697,8 @@ void OptionDialog::retranslateDbDialog(QDialog *optionsDialog) // SQLite things label_dataDir->setText(QApplication::translate("optionsDialog", "Data Directory", 0)); pushButton_browseDataDir->setText(QApplication::translate("optionsDialog", "Browse", 0)); - pushButton_defaultDataDir->setText(QApplication::translate("optionsDialog", "Default", 0)); label_backupDir->setText(QApplication::translate("optionsDialog", "Backup Directory", 0)); pushButton_browseBackupDir->setText(QApplication::translate("optionsDialog", "Browse", 0)); - pushButton_defaultBackupDir->setText(QApplication::translate("optionsDialog", "Default", 0)); label_numBackups->setText(QApplication::translate("optionsDialog", "Number of Backups", 0)); label_frequency->setText(QApplication::translate("optionsDialog", "Frequency of Backups", 0)); diff --git a/src/OptionDialog.h b/src/OptionDialog.h index f96990423..e9de0dc40 100644 --- a/src/OptionDialog.h +++ b/src/OptionDialog.h @@ -69,11 +69,9 @@ class OptionDialog : public QDialog, public Ui::optionsDialog QLabel *label_dataDir; BtStringEdit *btStringEdit_dataDir; QPushButton *pushButton_browseDataDir; - QPushButton *pushButton_defaultDataDir; QLabel *label_backupDir; BtStringEdit *btStringEdit_backupDir; QPushButton *pushButton_browseBackupDir; - QPushButton *pushButton_defaultBackupDir; QLabel *label_numBackups; QSpinBox *spinBox_numBackups; QLabel *label_frequency; @@ -98,8 +96,7 @@ public slots: void setDataDir(); void setBackupDir(); //! \brief Reset data directory to default. - void defaultDataDir(); - void defaultBackupDir(); + void resetToDefault(); //! \brief Enable or disable the configuration panel based on the engine choice void setEngine(int selected); diff --git a/src/brewtarget.cpp b/src/brewtarget.cpp index 70886615e..7d35155bb 100644 --- a/src/brewtarget.cpp +++ b/src/brewtarget.cpp @@ -493,9 +493,6 @@ void Brewtarget::cleanup() delete _mainWindow; Database::dropInstance(); -#if defined(Q_OS_LINUX) - pidFile.remove(); -#endif } diff --git a/ui/optionsDialog.ui b/ui/optionsDialog.ui index a6633066d..ca04c1acb 100644 --- a/ui/optionsDialog.ui +++ b/ui/optionsDialog.ui @@ -275,47 +275,73 @@ Databases - + - + + + Engines + + + + + + RDBMS Engine + + + + + + + + + + Test Connection + + + + + + + + + + true + + + + 0 + 0 + + + + Configuration + + + optionsDialog + + + + + + - - - Engines + + + Qt::Horizontal - - - - - RDBMS Engine - - - - - - - - - - Test Connection - - - - - + + + 40 + 20 + + + - - - true - - - Configuration - - - optionsDialog + + + Restore defaults -