// databasesettingspage.cpp — Database maintenance page in settings #include "databasesettingspage.h" #include "../db/db_manager.h" #include #include #include #include #include #include #include DatabaseSettingsPage::DatabaseSettingsPage(DbManager* db, QWidget* parent) : QWidget(parent) , m_db(db) { auto* layout = new QVBoxLayout(this); layout->setSpacing(10); layout->setContentsMargins(0, 0, 0, 0); auto* title = new QLabel("Database Maintenance", this); title->setStyleSheet("font-weight: bold; font-size: 13px;"); layout->addWidget(title); // --- DB info --- auto* infoLayout = new QFormLayout(); infoLayout->setSpacing(4); QString dbPath = "—"; QString dbSize = "—"; QString walSize = "—"; if (m_db && m_db->m_db) { const char* fname = sqlite3_db_filename(m_db->m_db, "main"); if (fname && fname[0]) { dbPath = QString::fromUtf8(fname); QFileInfo fi(dbPath); if (fi.exists()) { qint64 sz = fi.size(); if (sz < 1024) dbSize = QString("%1 B").arg(sz); else if (sz < 1024 * 1024) dbSize = QString("%1 KB").arg(sz / 1024); else dbSize = QString("%1 MB").arg(sz / (1024.0 * 1024.0), 0, 'f', 2); } QFileInfo walFi(dbPath + "-wal"); if (walFi.exists()) { qint64 wsz = walFi.size(); if (wsz < 1024) walSize = QString("%1 B").arg(wsz); else if (wsz < 1024 * 1024) walSize = QString("%1 KB").arg(wsz / 1024); else walSize = QString("%1 MB").arg(wsz / (1024.0 * 1024.0), 0, 'f', 2); } else { walSize = "n/a"; } } } m_filePathLabel = new QLabel(dbPath, this); m_filePathLabel->setStyleSheet("font-size: 11px; color: gray;"); m_filePathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); infoLayout->addRow("File:", m_filePathLabel); m_fileSizeLabel = new QLabel(dbSize, this); m_fileSizeLabel->setStyleSheet("font-size: 11px;"); infoLayout->addRow("Size:", m_fileSizeLabel); m_walSizeLabel = new QLabel(walSize, this); m_walSizeLabel->setStyleSheet("font-size: 11px;"); infoLayout->addRow("WAL:", m_walSizeLabel); layout->addLayout(infoLayout); // --- Separator --- auto* sep = new QFrame(this); sep->setFrameShape(QFrame::HLine); sep->setFrameShadow(QFrame::Sunken); layout->addWidget(sep); // --- Buttons --- auto* btnLayout = new QVBoxLayout(); btnLayout->setSpacing(6); auto* quickRow = new QHBoxLayout(); m_quickBtn = new QPushButton("Quick Check", this); m_quickBtn->setStyleSheet( "QPushButton { border: 1px solid palette(mid); border-radius: 4px;" " padding: 5px 14px; font-size: 12px; background: palette(button); }" "QPushButton:hover { background: palette(light); }"); connect(m_quickBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onQuickCheck); quickRow->addWidget(m_quickBtn); auto* quickDesc = new QLabel("Scans structure without verifying indexes (fast)", this); quickDesc->setStyleSheet("color: gray; font-size: 11px;"); quickRow->addWidget(quickDesc, 1); btnLayout->addLayout(quickRow); auto* fullRow = new QHBoxLayout(); m_fullBtn = new QPushButton("Full Check", this); m_fullBtn->setStyleSheet(m_quickBtn->styleSheet()); connect(m_fullBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onFullCheck); fullRow->addWidget(m_fullBtn); auto* fullDesc = new QLabel("Verifies all data structures and indexes (thorough)", this); fullDesc->setStyleSheet("color: gray; font-size: 11px;"); fullRow->addWidget(fullDesc, 1); btnLayout->addLayout(fullRow); auto* optRow = new QHBoxLayout(); m_optimizeBtn = new QPushButton("Optimize", this); m_optimizeBtn->setStyleSheet(m_quickBtn->styleSheet()); connect(m_optimizeBtn, &QPushButton::clicked, this, &DatabaseSettingsPage::onOptimize); optRow->addWidget(m_optimizeBtn); auto* optDesc = new QLabel("Update query planner statistics for better performance", this); optDesc->setStyleSheet("color: gray; font-size: 11px;"); optRow->addWidget(optDesc, 1); btnLayout->addLayout(optRow); layout->addLayout(btnLayout); // --- Status --- m_statusLabel = new QLabel("", this); m_statusLabel->setStyleSheet("font-size: 12px; margin-top: 4px;"); layout->addWidget(m_statusLabel); // --- Results --- m_resultText = new QPlainTextEdit(this); m_resultText->setReadOnly(true); m_resultText->setMaximumHeight(120); m_resultText->setStyleSheet( "QPlainTextEdit { font-family: monospace; font-size: 11px;" " background: palette(base); border: 1px solid palette(mid); border-radius: 4px;" " padding: 4px; }"); m_resultText->setPlaceholderText("Run a check to see results..."); layout->addWidget(m_resultText); layout->addStretch(); if (!m_db || !m_db->m_db) { m_statusLabel->setText("Database not connected"); m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); m_quickBtn->setEnabled(false); m_fullBtn->setEnabled(false); m_optimizeBtn->setEnabled(false); qWarning("DatabaseSettingsPage: DB not ready m_db=%p m_db->m_db=%p", (void*)m_db, m_db ? (void*)m_db->m_db : nullptr); } } void DatabaseSettingsPage::refreshStatus() { if (!m_db || !m_db->m_db) { m_statusLabel->setText("Database not connected"); m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); m_quickBtn->setEnabled(false); m_fullBtn->setEnabled(false); m_optimizeBtn->setEnabled(false); return; } m_quickBtn->setEnabled(true); m_fullBtn->setEnabled(true); m_optimizeBtn->setEnabled(true); const char* fname = sqlite3_db_filename(m_db->m_db, "main"); if (fname && fname[0]) { QString dbPath = QString::fromUtf8(fname); m_filePathLabel->setText(dbPath); QFileInfo fi(dbPath); if (fi.exists()) { qint64 sz = fi.size(); if (sz < 1024) m_fileSizeLabel->setText(QString("%1 B").arg(sz)); else if (sz < 1024 * 1024) m_fileSizeLabel->setText(QString("%1 KB").arg(sz / 1024)); else m_fileSizeLabel->setText(QString("%1 MB").arg(sz / (1024.0 * 1024.0), 0, 'f', 2)); QFileInfo walFi(dbPath + "-wal"); if (walFi.exists()) { qint64 wsz = walFi.size(); if (wsz < 1024) m_walSizeLabel->setText(QString("%1 B").arg(wsz)); else if (wsz < 1024 * 1024) m_walSizeLabel->setText(QString("%1 KB").arg(wsz / 1024)); else m_walSizeLabel->setText(QString("%1 MB").arg(wsz / (1024.0 * 1024.0), 0, 'f', 2)); } else { m_walSizeLabel->setText("n/a"); } } } } void DatabaseSettingsPage::setBusy(bool busy) { m_quickBtn->setEnabled(!busy); m_fullBtn->setEnabled(!busy); m_optimizeBtn->setEnabled(!busy); if (busy) { m_statusLabel->setText("Checking..."); m_statusLabel->setStyleSheet("font-size: 12px; margin-top: 4px; color: gray;"); } } void DatabaseSettingsPage::onQuickCheck() { if (!m_db || !m_db->m_db) { m_statusLabel->setText("Database not open"); m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); return; } setBusy(true); QTimer::singleShot(50, this, [this]() { IntegrityResult r = m_db->integrityCheckQuick(); showResult("Quick Check", r); }); } void DatabaseSettingsPage::onFullCheck() { if (!m_db || !m_db->m_db) { m_statusLabel->setText("Database not open"); m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); return; } setBusy(true); QTimer::singleShot(50, this, [this]() { IntegrityResult r = m_db->integrityCheckFull(); showResult("Full Check", r); }); } void DatabaseSettingsPage::onOptimize() { if (!m_db || !m_db->m_db) { m_statusLabel->setText("Database not open"); m_statusLabel->setStyleSheet("font-size: 12px; color: red;"); return; } setBusy(true); QTimer::singleShot(50, this, [this]() { IntegrityResult r = m_db->integrityOptimize(); showResult("Optimize", r); }); } void DatabaseSettingsPage::showResult(const QString& checkName, const IntegrityResult& result) { m_quickBtn->setEnabled(true); m_fullBtn->setEnabled(true); m_optimizeBtn->setEnabled(true); if (result.ok) { m_statusLabel->setText(QString("%1: OK (%2 ms)") .arg(checkName).arg(result.elapsedMs)); m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: green;"); } else { m_statusLabel->setText(QString("%1: FAILED (%2 ms)") .arg(checkName).arg(result.elapsedMs)); m_statusLabel->setStyleSheet("font-size: 12px; font-weight: bold; color: red;"); } if (result.errors.isEmpty()) { m_resultText->setPlainText(result.message.isEmpty() ? "ok" : result.message); } else { QStringList lines; lines.append(result.message); lines.append(result.errors); m_resultText->setPlainText(lines.join('\n')); } }