bool DatabaseTabWidget::saveDatabase(Database* db)
{
    DatabaseManagerStruct& dbStruct = m_dbList[db];

    if (dbStruct.saveToFilename) {
        QSaveFile saveFile(dbStruct.canonicalFilePath);
        if (saveFile.open(QIODevice::WriteOnly)) {
            m_writer.writeDatabase(&saveFile, db);
            if (m_writer.hasError()) {
                MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                     + m_writer.errorString());
                return false;
            }
            if (!saveFile.commit()) {
                MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                     + saveFile.errorString());
                return false;
            }
        }
        else {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + saveFile.errorString());
            return false;
        }

        dbStruct.modified = false;
        updateTabName(db);
        return true;
    }
    else {
        return saveDatabaseAs(db);
    }
}
void DatabaseTabWidget::saveDatabaseAs(Database* db)
{
    DatabaseManagerStruct& dbStruct = m_dbList[db];
    QString oldFileName;
    if (dbStruct.saveToFilename) {
        oldFileName = dbStruct.filePath;
    }
    QString fileName = fileDialog()->getSaveFileName(this, tr("Save database as"),
                                                     oldFileName, tr("KeePass 2 Database").append(" (*.kdbx)"));
    if (!fileName.isEmpty()) {
        bool result = false;

        QSaveFile saveFile(fileName);
        if (saveFile.open(QIODevice::WriteOnly)) {
            m_writer.writeDatabase(&saveFile, db);
            result = saveFile.commit();
        }

        if (result) {
            dbStruct.modified = false;
            dbStruct.saveToFilename = true;
            QFileInfo fileInfo(fileName);
            dbStruct.filePath = fileInfo.absoluteFilePath();
            dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
            dbStruct.fileName = fileInfo.fileName();
            dbStruct.dbWidget->updateFilename(dbStruct.filePath);
            updateTabName(db);
            updateLastDatabases(dbStruct.filePath);
        }
        else {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + saveFile.errorString());
        }
    }
}
void DatabaseTabWidget::saveDatabase(Database* db)
{
    DatabaseManagerStruct& dbStruct = m_dbList[db];

    if (dbStruct.saveToFilename) {
        bool result = false;

        QSaveFile saveFile(dbStruct.filePath);
        if (saveFile.open(QIODevice::WriteOnly)) {
            m_writer.writeDatabase(&saveFile, db);
            result = saveFile.commit();
        }

        if (result) {
            dbStruct.modified = false;
            updateTabName(db);
        }
        else {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + saveFile.errorString());
        }
    }
    else {
        saveDatabaseAs(db);
    }
}
void DatabaseTabWidget::updateTabNameFromDbWidgetSender()
{
    Q_ASSERT(qobject_cast<DatabaseWidget*>(sender()));
    Q_ASSERT(databaseFromDatabaseWidget(qobject_cast<DatabaseWidget*>(sender())));

    DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
    updateTabName(databaseFromDatabaseWidget(dbWidget));
}
void DatabaseTabWidget::changeDatabase(Database* newDb)
{
    Q_ASSERT(sender());
    Q_ASSERT(!m_dbList.contains(newDb));

    DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(sender());
    Database* oldDb = databaseFromDatabaseWidget(dbWidget);
    DatabaseManagerStruct dbStruct = m_dbList[oldDb];
    m_dbList.remove(oldDb);
    m_dbList.insert(newDb, dbStruct);

    updateTabName(newDb);
    connectDatabase(newDb, oldDb);
}
void DatabaseTabWidget::insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct)
{
    m_dbList.insert(db, dbStruct);

    addTab(dbStruct.dbWidget, "");
    toggleTabbar();
    updateTabName(db);
    int index = databaseIndex(db);
    setCurrentIndex(index);
    connectDatabase(db);
    connect(dbStruct.dbWidget, SIGNAL(closeRequest()), SLOT(closeDatabaseFromSender()));
    connect(dbStruct.dbWidget, SIGNAL(databaseChanged(Database*)), SLOT(changeDatabase(Database*)));
    connect(dbStruct.dbWidget, SIGNAL(unlockedDatabase()), SLOT(updateTabNameFromDbWidgetSender()));
}
void DatabaseTabWidget::lockDatabases()
{
    QHashIterator<Database*, DatabaseManagerStruct> i(m_dbList);
    while (i.hasNext()) {
        i.next();
        DatabaseWidget::Mode mode = i.value().dbWidget->currentMode();

        if ((mode == DatabaseWidget::ViewMode || mode == DatabaseWidget::EditMode)
                && i.value().dbWidget->dbHasKey()) {
            i.value().dbWidget->lock();
            updateTabName(i.key());
        }
    }
}
void DatabaseTabWidget::modified()
{
    Q_ASSERT(qobject_cast<Database*>(sender()));

    Database* db = static_cast<Database*>(sender());
    DatabaseManagerStruct& dbStruct = m_dbList[db];

    if (config()->get("AutoSaveAfterEveryChange").toBool() && dbStruct.saveToFilename) {
        saveDatabase(db);
        return;
    }

    if (!dbStruct.modified) {
        dbStruct.modified = true;
        updateTabName(db);
    }
}
void DatabaseTabWidget::lockDatabases()
{
    clipboard()->clearCopiedText();

    for (int i = 0; i < count(); i++) {
        DatabaseWidget* dbWidget = static_cast<DatabaseWidget*>(widget(i));
        Database* db = databaseFromDatabaseWidget(dbWidget);

        DatabaseWidget::Mode mode = dbWidget->currentMode();

        if ((mode != DatabaseWidget::ViewMode && mode != DatabaseWidget::EditMode)
                || !dbWidget->dbHasKey()) {
            continue;
        }

        // show the correct tab widget before we are asking questions about it
        setCurrentWidget(dbWidget);

        if (mode == DatabaseWidget::EditMode && dbWidget->isEditWidgetModified()) {
            QMessageBox::StandardButton result =
                MessageBox::question(
                    this, tr("Lock database"),
                    tr("Can't lock the database as you are currently editing it.\nPlease press cancel to finish your changes or discard them."),
                    QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
            if (result == QMessageBox::Cancel) {
                continue;
            }
        }


        if (m_dbList[db].modified && !m_dbList[db].saveToFilename) {
            QMessageBox::StandardButton result =
                MessageBox::question(
                    this, tr("Lock database"),
                    tr("This database has never been saved.\nYou can save the database or stop locking it."),
                    QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Cancel);
            if (result == QMessageBox::Save) {
                if (!saveDatabase(db)) {
                    continue;
                }
            }
            else if (result == QMessageBox::Cancel) {
                continue;
            }
        }
        else if (m_dbList[db].modified) {
            QMessageBox::StandardButton result =
                MessageBox::question(
                    this, tr("Lock database"),
                    tr("This database has been modified.\nDo you want to save the database before locking it?\nOtherwise your changes are lost."),
                    QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel);
            if (result == QMessageBox::Save) {
                if (!saveDatabase(db)) {
                    continue;
                }
            }
            else if (result == QMessageBox::Discard) {
                m_dbList[db].modified = false;
            }
            else if (result == QMessageBox::Cancel) {
                continue;
            }
        }

        dbWidget->lock();
        // database has changed so we can't use the db variable anymore
        updateTabName(dbWidget->database());
    }
}
void DatabaseTabWidget::updateTabNameFromDbSender()
{
    Q_ASSERT(qobject_cast<Database*>(sender()));

    updateTabName(static_cast<Database*>(sender()));
}
bool DatabaseTabWidget::saveDatabaseAs(Database* db)
{
    DatabaseManagerStruct& dbStruct = m_dbList[db];
    QString oldFileName;
    if (dbStruct.saveToFilename) {
        oldFileName = dbStruct.filePath;
    }
    else {
        oldFileName = tr("New database").append(".kdbx");
    }
    QString fileName = fileDialog()->getSaveFileName(this, tr("Save database as"),
                                                     oldFileName, tr("KeePass 2 Database").append(" (*.kdbx)"),
                                                     nullptr, 0, "kdbx");
    if (!fileName.isEmpty()) {
        QFileInfo fileInfo(fileName);
        QString lockFilePath;
        if (fileInfo.exists()) {
            // returns empty string when file doesn't exist
            lockFilePath = fileInfo.canonicalPath();
        }
        else {
            lockFilePath = fileInfo.absolutePath();
        }
        QString lockFileName = QString("%1/.%2.lock").arg(lockFilePath, fileInfo.fileName());
        QScopedPointer<QLockFile> lockFile(new QLockFile(lockFileName));
        lockFile->setStaleLockTime(0);
        if (!lockFile->tryLock()) {
            // for now silently ignore if we can't create a lock file
            // due to lack of permissions
            if (lockFile->error() != QLockFile::PermissionError) {
                QMessageBox::StandardButton result = MessageBox::question(this, tr("Save database as"),
                    tr("The database you are trying to save as is locked by another instance of KeePassX.\n"
                       "Do you want to save it anyway?"),
                    QMessageBox::Yes | QMessageBox::No);

                if (result == QMessageBox::No) {
                    return false;
                }
                else {
                    // take over the lock file if possible
                    if (lockFile->removeStaleLockFile()) {
                        lockFile->tryLock();
                    }
                }
            }
        }

        QSaveFile saveFile(fileName);
        if (!saveFile.open(QIODevice::WriteOnly)) {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + saveFile.errorString());
            return false;
        }

        m_writer.writeDatabase(&saveFile, db);
        if (m_writer.hasError()) {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + m_writer.errorString());
            return false;
        }
        if (!saveFile.commit()) {
            MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n"
                                 + saveFile.errorString());
            return false;
        }

        dbStruct.modified = false;
        dbStruct.saveToFilename = true;
        dbStruct.readOnly = false;
        dbStruct.filePath = fileInfo.absoluteFilePath();
        dbStruct.canonicalFilePath = fileInfo.canonicalFilePath();
        dbStruct.fileName = fileInfo.fileName();
        dbStruct.dbWidget->updateFilename(dbStruct.filePath);
        delete dbStruct.lockFile;
        dbStruct.lockFile = lockFile.take();
        updateTabName(db);
        updateLastDatabases(dbStruct.filePath);
        return true;
    }
    else {
        return false;
    }
}